Files
Simon facc04aa65 chore: docs 结构整改 + compose 双目录对齐(合并重建提交)
本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交
(5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。

一、docs 结构整改(整改 #14)
根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入,
随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录
树并存于 docs/,共 791 文件、双分类体系冲突。

修复动作:
- b2 同名异主题文件改名迁移保全 9 个
- C 类 39 个孤立文件按主题正确归类
- A/B1 类 222 个重复文件删除(新结构已有内容副本)
- 9 个旧独有空目录删除
- 270 处内部引用按 verified 映射改写
- 整改记录 #14 登记于 04-运维文档/部署运维

结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。
残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。

二、compose 双目录对齐(消除踩坑 A)
- docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist
  改为 src/frontend-*/dist(h5 / agent / admin / terminal)
- docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/
- 效果:本地 docker compose up 不再把根目录 stale dist 挂回,
  与线上一致,分叉隐患消除(已 docker compose config 校验通过)

防复发铁律:
- 重构须提交;仓库修复须 git stash -u 或先 commit
- 新结构须 git add 并提交,避免再次 untracked 复活
- H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
2026-08-07 22:31:32 +08:00

24 KiB
Raw Permalink Blame History

系统设计文档 — 代办事项真实数据源集成

项目:企微IT智能服务台 — 代办面板去mock改造 版本:v1.0 日期:2026-07-11


Part A: 系统设计

1. 实现方案

1.1 核心技术挑战

挑战 方案
两个异构数据源(企微审批 + ITSM 工单)聚合为统一列表 引入 TodoAggregatorService,通过抽象接口 TodoSourceService 统一两个数据源,asyncio.gather 并行查询,return_exceptions=True 容错
企微审批需 getapprovaldata → getapprovaldetail 二次过滤当前审批人,性能开销大 先用 getapprovaldata 按 sp_status=1 + 18 个模板批量获取 sp_no_list,再 asyncio.gather 并发调用 getapprovaldetail,最后用 _extract_current_approver 过滤
ITSM 列表 API 尚未获取,签名认证方式特殊 设计 ITSMService 为可插拔实现,签名计算抽为独立工具 itsm_signer.py,列表方法暂返回空列表 + 日志告警,待 API 到位后填充
Redis 缓存需区分不同坐席 缓存 key 设计:todo:cache:{agent_userid}:{type_filter}TTL 45 秒
彻底去 mock(后端 MOCK_TODO_ITEMS + 前端 mockTodoListData + device 类型) 全量删除 mock 数据,schema 中 VALID_TODO_TYPES 移除 device,前端移除 DeviceDetail 引用

1.2 框架与库选择

用途 说明
httpx 异步 HTTP 客户端 已用于 approval.py,复用
redis.asyncio Redis 异步缓存 已用于 token 管理,复用
asyncio 并发查询两个数据源 Python 标准库,asyncio.gather(return_exceptions=True)
hashlib ITSM SHA1 签名 Python 标准库
urllib.parse.quote_plus ITSM 签名 URL 编码 Python 标准库

结论:无需新增任何第三方依赖。

1.3 架构模式

采用 Service Layer + 策略模式

┌─────────────────────────────────────────────────────────┐
│                    API Layer (todo_items.py)              │
│              GET /api/todo-items                          │
│              GET /api/todo-items/{id}                     │
└──────────────────────┬──────────────────────────────────┘
                       │ 调用
┌──────────────────────▼──────────────────────────────────┐
│              TodoAggregatorService                        │
│  ┌─ Redis 缓存检查 ─────────────────────────────────┐   │
│  │  命中 → 直接返回                                  │   │
│  │  未命中 → asyncio.gather 并行查询                  │   │
│  └─────────────────────────────────────────────────┘   │
│         ┌──────────────┬──────────────┐               │
│         │              │              │               │
│  ┌──────▼──────┐ ┌────▼─────────┐    │               │
│  │ApprovalTodo │ │ ITSMService  │    │               │
│  │  Service    │ │ (abstract)   │    │               │
│  └──────┬──────┘ └────┬─────────┘    │               │
│         │              │              │               │
│  ┌──────▼──────┐ ┌────▼─────────┐    │               │
│  │ 企微审批API  │ │ ITSM API     │    │               │
│  │ getapproval  │ │ (待实现)      │    │               │
│  │   data/detail│ │              │    │               │
│  └─────────────┘ └──────────────┘    │               │
└──────────────────────────────────────┘

1.4 后端改造方案

  1. 新增 Service 层3 个文件):

    • TodoSourceService(抽象基类)+ ApprovalTodoService(企微审批实现)
    • ITSMService(ITSM 工单实现,列表方法待 API 到位)
    • TodoAggregatorService(聚合服务,缓存 + 并行查询 + 容错)
  2. 新增 ITSM 签名工具itsm_signer.py,独立封装 SHA1 签名计算

  3. 重写 todo_items.py:删除 MOCK_TODO_ITEMS,调用 TodoAggregatorService 获取真实数据

  4. 补充 approval.py:新增 get_approval_data() 函数调用企微 getapprovaldata API

  5. 更新 config.py:新增 itsm_app_iditsm_app_secretitsm_base_url 配置项

  6. 更新 schemas/todo_item.pyVALID_TODO_TYPES 移除 device

1.5 前端改造方案

  1. api/todo.ts:类型定义移除 device,新增 type 查询参数
  2. stores/todo.ts:删除 mockTodoListData import 和 catch 块 mock fallback
  3. mock/data.ts:删除 mockTodoListData 导出
  4. TodoPanel.vue:新增类型筛选 Tab(全部/审批/工单)+ 手动刷新按钮 + 移除 device 类型样式
  5. TaskDetailView.vue:移除 DeviceDetail 引用
  6. DeviceDetail.vue:标记为废弃(保留文件但不再引用)

2. 文件列表

后端

文件路径 操作 说明
backend/app/config.py 修改 新增 ITSM 配置项
backend/app/schemas/todo_item.py 修改 VALID_TODO_TYPES 移除 device
backend/app/models/todo_item.py 修改 type 注释移除 device
backend/app/services/todo_source_service.py 新建 抽象基类 + ApprovalTodoService
backend/app/services/itsm_service.py 新建 ITSM Service(签名认证 + 可插拔列表)
backend/app/services/todo_aggregator_service.py 新建 聚合服务(缓存 + 并行 + 容错)
backend/app/utils/itsm_signer.py 新建 ITSM SHA1 签名工具
backend/app/api/todo_items.py 修改 删除 mock,重写为真实数据聚合
backend/app/api/approval.py 修改 新增 get_approval_data()

前端

文件路径 操作 说明
frontend-agent/src/api/todo.ts 修改 移除 device 类型,新增 type 参数
frontend-agent/src/stores/todo.ts 修改 移除 mock fallback
frontend-agent/src/mock/data.ts 修改 删除 mockTodoListData
frontend-agent/src/components/conversation/TodoPanel.vue 修改 新增 Tab + 刷新 + 移除 device
frontend-agent/src/components/chat/TaskDetailView.vue 修改 移除 DeviceDetail 引用
frontend-agent/src/components/chat/task/DeviceDetail.vue 修改 废弃标记
frontend-agent/src/components/chat/task/TicketDetail.vue 修改 适配真实 ITSM 数据结构
frontend-agent/src/components/chat/task/ApprovalDetail.vue 修改 适配真实企微审批数据结构

3. 数据结构和接口(类图)

classDiagram
    class TodoSourceService {
        <<abstract>>
        +agent_userid: str
        +redis: aioredis.Redis
        +get_todo_list() List~TodoItemData~*
        +get_todo_detail(item_id: str) TodoItemData*
    }

    class ApprovalTodoService {
        -redis: aioredis.Redis
        -agent_userid: str
        +get_todo_list() List~TodoItemData~
        +get_todo_detail(sp_no: str) TodoItemData
        -_fetch_approval_sp_no_list() List~str~
        -_fetch_approval_details(sp_no_list: List~str~) List~dict~
        -_filter_by_current_approver(details: List~dict~) List~dict~
        -_map_to_todo_item(detail: dict) TodoItemData
    }

    class ITSMService {
        -base_url: str
        -app_id: str
        -app_secret: str
        -redis: aioredis.Redis
        -agent_userid: str
        +get_todo_list() List~TodoItemData~
        +get_todo_detail(workitem_id: str) TodoItemData
        -_do_post(url: str, body: dict) dict
        -_get_workitem_detail(process_instance_id: int, executor: str) dict
    }

    class ITSMSigner {
        +compute_signature(app_id: str, timestamp: str, app_secret: str, biz_data: dict) str
        -_get_signature_compatible(params: dict) str
    }

    class TodoAggregatorService {
        -redis: aioredis.Redis
        -cache_ttl: int
        +get_todo_list(agent_userid: str, todo_type: Optional~str~) dict
        +get_todo_detail(agent_userid: str, item_id: str, todo_type: str) dict
        -_get_from_cache(agent_userid: str, todo_type: Optional~str~) Optional~dict~
        -_set_to_cache(agent_userid: str, todo_type: Optional~str~, data: dict) void
        -_invalidate_cache(agent_userid: str) void
    }

    class TodoItemData {
        +id: str
        +type: str
        +title: str
        +priority: str
        +description: dict
        +status: str
        +assigned_agent_id: Optional~str~
        +corp_id: str
        +created_at: str
        +updated_at: str
    }

    TodoSourceService <|-- ApprovalTodoService
    TodoSourceService <|-- ITSMService
    TodoAggregatorService o-- ApprovalTodoService : creates
    TodoAggregatorService o-- ITSMService : creates
    ITSMService --> ITSMSigner : uses
    TodoSourceService ..> TodoItemData : returns

4. 程序调用流程(时序图)

4.1 代办列表查询流程

sequenceDiagram
    participant FE as 前端 TodoPanel
    participant API as GET /api/todo-items
    participant Agg as TodoAggregatorService
    participant Cache as Redis Cache
    participant Appr as ApprovalTodoService
    participant Wecom as 企微审批API
    participant ITSM as ITSMService
    participant ITSM_API as ITSM OpenAPI

    FE->>API: GET /api/todo-items?type=approval
    API->>Agg: get_todo_list(agent_userid, type="approval")
    Agg->>Cache: GET todo:cache:{userid}:approval
    alt 缓存命中
        Cache-->>Agg: cached_data
        Agg-->>API: {items, total, cached:true}
    else 缓存未命中
        Agg->>Agg: asyncio.gather(approval_svc, itsm_svc, return_exceptions=True)
        par 并行查询审批
            Agg->>Appr: get_todo_list()
            Appr->>Wecom: getapprovaldata(sp_status=1, templates=18)
            Wecom-->>Appr: sp_no_list
            Appr->>Appr: asyncio.gather(getapprovaldetail × N)
            loop 每个sp_no并发获取详情
                Appr->>Wecom: getapprovaldetail(sp_no)
                Wecom-->>Appr: approval_detail
            end
            Appr->>Appr: _filter_by_current_approver(details)
            Appr->>Appr: _map_to_todo_item(detail)
            Appr-->>Agg: List[TodoItemData]
        and 并行查询工单
            Agg->>ITSM: get_todo_list()
            Note over ITSM: ITSM列表API待实现<br/>当前返回空列表+日志告警
            ITSM-->>Agg: List[TodoItemData] (空)
        end
        Agg->>Agg: merge + sort by priority
        Agg->>Cache: SET todo:cache:{userid}:approval TTL=45s
        Agg-->>API: {items, total, cached:false}
    end
    API-->>FE: {code:0, data:{items, total}}

4.2 代办详情查询流程

sequenceDiagram
    participant FE as 前端 TaskDetailView
    participant API as GET /api/todo-items/{id}
    participant Agg as TodoAggregatorService
    participant Appr as ApprovalTodoService
    participant ITSM as ITSMService
    participant Wecom as 企微审批API
    participant ITSM_API as ITSM OpenAPI

    FE->>API: GET /api/todo-items/approval:{sp_no}
    API->>Agg: get_todo_detail(agent_userid, item_id, todo_type="approval")
    
    alt type == "approval"
        Agg->>Appr: get_todo_detail(sp_no)
        Appr->>Wecom: getapprovaldetail(sp_no)
        Wecom-->>Appr: approval_detail
        Appr->>Appr: _map_to_todo_item(detail)
        Appr-->>Agg: TodoItemData
    else type == "ticket"
        Agg->>ITSM: get_todo_detail(workitem_id)
        ITSM->>ITSM_API: POST /openapi/v1/process/workitem/detail
        ITSM_API-->>ITSM: {code:20000, data:{workitem_detail}}
        ITSM->>ITSM: _map_to_todo_item(detail)
        ITSM-->>Agg: TodoItemData
    end
    
    Agg-->>API: TodoItemData
    API-->>FE: {code:0, data:{item}}

4.3 ITSM 签名认证流程

sequenceDiagram
    participant Svc as ITSMService
    participant Signer as ITSMSigner
    participant API as ITSM OpenAPI

    Svc->>Svc: timestamp = str(int(time.time()*1000))
    Svc->>Signer: compute_signature(app_id, timestamp, app_secret, biz_data)
    Signer->>Signer: sign_params = {appId, timestamp, appSecret, bizData}
    Signer->>Signer: sort by key ASC
    Signer->>Signer: concat all values → canonicalized_str
    Signer->>Signer: quote_plus(canonicalized_str)
    Signer->>Signer: sha1(q2).hexdigest().upper()
    Signer-->>Svc: sign_str
    Svc->>API: POST url, headers={appId, timestamp, sign}, json=body
    API-->>Svc: {code:20000, data:{...}}

5. 待明确事项

# 问题 当前假设 影响
1 ITSM 代办列表 API 端点和请求/响应格式 设计为抽象接口,列表方法暂返回空列表 待用户抓包获取后实现,不影响架构
2 ITSM app_id 和 app_secret 需在 config.py 中新增配置项占位 部署时通过环境变量注入
3 企微 getapprovaldata 单次查询上限(size 参数) 假设每页100条,循环 cursor 分页 如上限更小需增加分页逻辑
4 审批详情并发查询数量较多时的限流 使用 asyncio.Semaphore 限制并发数(默认10 防止企微 API 限流
5 坐席身份标识(agent_userid)从哪里获取 假设从请求 header 或 JWT token 中提取 需确认认证中间件传递方式
6 ITSM 工单的优先级映射规则 假设 ITSM 有自己的优先级字段,需映射到 urgent/high/normal 待 API 确认后调整映射逻辑

Part B: 任务分解

6. 依赖包列表

无新增第三方依赖。 所有所需库已在项目中使用:

  • httpx — 已用于 approval.py 的企微 API 调用
  • redis.asyncio — 已用于 token 缓存管理
  • hashlib — Python 标准库(ITSM SHA1 签名)
  • urllib.parse.quote_plus — Python 标准库(ITSM 签名 URL 编码)
  • asyncio — Python 标准库(并行查询)

7. 任务列表

T01: 后端基础设施与数据模型

依赖:无 优先级P0 文件

  • backend/app/config.py(修改)
  • backend/app/schemas/todo_item.py(修改)
  • backend/app/models/todo_item.py(修改)

描述

  1. config.py 新增 ITSM 配置项:
    • itsm_app_id: str = ""
    • itsm_app_secret: str = ""
    • itsm_base_url: str = "https://devops.dc.servyou-it.com/itsm"(生产)
    • itsm_test_base_url: str = "https://test-devops.dc.servyou-it.com/itsm"(测试)
  2. schemas/todo_item.py
    • VALID_TODO_TYPES{"ticket", "approval", "device"} 改为 {"ticket", "approval"}
    • 更新所有字段描述中的类型注释
  3. models/todo_item.py
    • type 字段注释从 ticket/approval/device 改为 ticket/approval

T02: 后端 Service 层 + ITSM 签名工具

依赖T01 优先级P0 文件

  • backend/app/services/todo_source_service.py(新建)
  • backend/app/services/itsm_service.py(新建)
  • backend/app/services/todo_aggregator_service.py(新建)
  • backend/app/utils/itsm_signer.py(新建)

描述

  1. todo_source_service.py
    • 定义抽象基类 TodoSourceService,含 get_todo_list()get_todo_detail() 抽象方法
    • 实现 ApprovalTodoService
      • get_todo_list():调 getapprovaldatasp_status=1, 18模板)→ 并发 getapprovaldetail_extract_current_approver 过滤 → _map_to_todo_item 映射
      • get_todo_detail(sp_no):调 getapprovaldetail_map_to_todo_item
      • _map_to_todo_item():企微审批详情 → TodoItemData 格式映射
      • 使用 asyncio.Semaphore(10) 限制并发详情查询
  2. itsm_signer.py
    • ITSMSigner.compute_signature(app_id, timestamp, app_secret, biz_data) 静态方法
    • 实现 PRD 中的签名算法:sort → concat → quote_plus → sha1 → upper
  3. itsm_service.py
    • 继承 TodoSourceService
    • _do_post(url, body):签名 + 发送请求(复用 ITSMSigner
    • get_workitem_detail(process_instance_id, executor):调已知详情 API
    • get_todo_list()待 API 到位,当前返回空列表 + logger.warning
    • get_todo_detail(workitem_id):调 get_workitem_detail → 映射
  4. todo_aggregator_service.py
    • get_todo_list(agent_userid, todo_type)Redis 缓存 → asyncio.gather(return_exceptions=True) → 合并排序 → 写缓存
    • get_todo_detail(agent_userid, item_id, todo_type):按类型路由到对应 Service
    • 缓存 keytodo:cache:{agent_userid}:{todo_type or "all"}TTL 45s

T03: 后端 API 改造

依赖T02 优先级P0 文件

  • backend/app/api/todo_items.py(修改)
  • backend/app/api/approval.py(修改)
  • backend/app/api/router.py(确认,无需修改路由注册)

描述

  1. todo_items.py
    • 删除 MOCK_TODO_ITEMS(全部20条硬编码数据)
    • 删除 TodoItemResponseTodoItemListResponse(移至 schemas,复用已有)
    • list_todo_items():新增 type 查询参数,调用 TodoAggregatorService.get_todo_list()
    • get_todo_item():从 item_id 中解析类型前缀(如 approval:{sp_no} / ticket:{workitem_id}),调用 TodoAggregatorService.get_todo_detail()
    • update_todo_item_status():保留但标记为"仅展示,不支持在服务台内操作"(按用户决策,交互方式为跳转原系统)
    • 从请求中获取 agent_useridheader 或 JWT
  2. approval.py
    • 新增 get_approval_data(access_token, starttime, endtime, filters) 异步函数
    • 调用企微 POST /cgi-bin/oa/getapprovaldata API
    • 支持 cursor 分页循环

T04: 前端数据层与 API 改造

依赖T03 优先级P0 文件

  • frontend-agent/src/api/todo.ts(修改)
  • frontend-agent/src/stores/todo.ts(修改)
  • frontend-agent/src/mock/data.ts(修改)

描述

  1. api/todo.ts
    • TodoItemData.type 注释移除 device
    • getTodoItems() 新增 type 参数:type?: 'ticket' | 'approval'
    • 新增 refreshTodoItems() 函数(带 _force=1 参数跳过缓存)
  2. stores/todo.ts
    • 删除 import { mockTodoListData } from '@/mock/data'
    • fetchTodoList() catch 块:删除 mock fallback,改为 todoList.value = [] + 错误日志
    • 新增 activeType ref'all' | 'approval' | 'ticket',传入 API type 参数
    • 新增 refreshList() 方法:强制刷新(调 refreshTodoItems
  3. mock/data.ts
    • 删除 mockTodoListData 常量和导出
    • 删除 mockTodos 数组(5条前端 mock 数据)

T05: 前端组件层改造

依赖T04 优先级P0(移除 device/ P1Tab+详情+刷新)/ P2(定时刷新+跳转) 文件

  • frontend-agent/src/components/conversation/TodoPanel.vue(修改)
  • frontend-agent/src/components/chat/TaskDetailView.vue(修改)
  • frontend-agent/src/components/chat/task/DeviceDetail.vue(修改/废弃)
  • frontend-agent/src/components/chat/task/TicketDetail.vue(修改)
  • frontend-agent/src/components/chat/task/ApprovalDetail.vue(修改)

描述

  1. TodoPanel.vueP0+P1+P2):
    • typeLabel 移除 device: '设备'
    • 新增类型筛选 Tab(全部/审批/工单),切换时更新 todoStore.activeType + 刷新列表
    • 新增刷新按钮(🔄 图标),点击调 todoStore.refreshList()
    • 新增定时刷新(P2):setInterval(fetchTodoList, 60000),组件 onUnmounted 时清除
    • 新增跳转按钮(P2):每条待办条目增加"在原系统中打开"链接(审批跳企微审批URL / 工单跳ITSM URL
    • 移除 .todo-type-tag.type-device CSS 样式
  2. TaskDetailView.vueP0):
    • 移除 import DeviceDetail from './task/DeviceDetail.vue'
    • 移除 v-else-if="todoItem.type === 'device'" 条件渲染块
    • typeLabelMap 移除 device: '🖥 设备异常'
    • 移除 .tdv-type-device CSS 样式
  3. DeviceDetail.vueP0):
    • 文件头注释标记为"已废弃 — v1.0 移除 device 类型"
    • 保留文件但不再被任何组件引用
  4. TicketDetail.vueP1):
    • 适配真实 ITSM 工单数据结构(description 字段映射调整)
    • 底部操作按钮改为"在 ITSM 中打开"跳转链接(P2
  5. ApprovalDetail.vueP1):
    • 适配真实企微审批数据结构(description 字段映射调整)
    • 底部操作按钮改为"在企微审批中打开"跳转链接(P2)

8. 共享知识(跨文件约定)

8.1 统一数据映射规则

企微审批 → TodoItemData 映射

{
    "id": f"approval:{sp_no}",           # 前缀类型 + 原始ID
    "type": "approval",
    "title": sp_name,                      # 审批单名称
    "priority": "high",                    # 审批默认 high(企微无优先级概念)
    "description": {
        "sp_no": sp_no,
        "template_name": template_name,
        "applicant": applyer_userid,
        "apply_time": apply_time,
        "sp_status": sp_status,
        "current_approver": current_approver,
        "template_id": template_id,
    },
    "status": "pending",                   # 审批中统一映射为 pending
    "assigned_agent_id": current_approver,
    "corp_id": corp_id,
    "created_at": apply_time_iso,          # apply_time 时间戳转 ISO
    "updated_at": apply_time_iso,
}

ITSM 工单 → TodoItemData 映射

{
    "id": f"ticket:{process_instance_id}", # 前缀类型 + 原始ID
    "type": "ticket",
    "title": title,
    "priority": itsm_priority_to_todo(priority),  # ITSM 优先级 → urgent/high/normal
    "description": {
        "process_instance_id": process_instance_id,
        "executor": executor,
        "status": itsm_status,
        "creator": creator,
        # ... 其他 ITSM 字段
    },
    "status": "pending",
    "assigned_agent_id": agent_userid,
    "corp_id": "",
    "created_at": created_at_iso,
    "updated_at": updated_at_iso,
}

8.2 ID 格式约定

所有 TodoItem 的 id 字段使用 {type}:{原始ID} 格式:

  • 审批:approval:{sp_no}(如 approval:202607110001
  • 工单:ticket:{process_instance_id}(如 ticket:12345

解析规则:item_id.split(":", 1)[type, original_id]

8.3 缓存 Key 设计

todo:cache:{agent_userid}:{todo_type}
  • agent_userid:坐席企微 userid
  • todo_typeall / approval / ticket
  • TTL45 秒
  • 强制刷新:删除 key 后重新查询

8.4 API 响应格式

统一使用 {code: 0, data: {...}, message: "success"} 格式(复用 success_response)。

8.5 并发控制

  • 企微审批详情并发查询:asyncio.Semaphore(10) 限制
  • 两个数据源并行查询:asyncio.gather(return_exceptions=True)
  • 任一数据源失败不影响另一个:isinstance(result, Exception) 检查后跳过

8.6 前端类型约定

  • type 字段:仅 'ticket' | 'approval'(移除 'device'
  • 前缀格式:前端通过 id.includes('approval:')id.includes('ticket:') 判断类型

9. 任务依赖图

graph TD
    T01[T01: 后端基础设施与数据模型<br/>config.py + schemas + models]
    T02[T02: 后端Service层 + ITSM签名<br/>4个新文件]
    T03[T03: 后端API改造<br/>todo_items.py + approval.py]
    T04[T04: 前端数据层与API<br/>api/todo.ts + store + mock]
    T05[T05: 前端组件层<br/>TodoPanel + TaskDetailView + 3个子视图]

    T01 --> T02
    T02 --> T03
    T03 --> T04
    T04 --> T05

    style T01 fill:#4CAF50,color:#fff
    style T02 fill:#2196F3,color:#fff
    style T03 fill:#2196F3,color:#fff
    style T04 fill:#FF9800,color:#fff
    style T05 fill:#FF9800,color:#fff