Files
wecom_it_smart_desk/docs/system_design.md
T
Simon bea288e414 feat: 2026-07-11 全量更新 - 代办集成+会议室预定+知识迭代修复+UI统一+Bug修复
== 已部署上线 (9项) ==
- 代办事项真实数据源集成 (企微审批API 8bug修复链)
- H5/坐席端 Logo样式统一+绿色背景
- 视频引导页修复 (localStorage key v2)
- 坐席端 v9 Vue版本修复 (ElMessage._context)
- 截图按钮 v10 修复 (getDisplayMedia user gesture)
- 扫码样式恢复+H5扫码登录跳转修复
- H5截图快捷键提示

== 代码完成待部署 (3项) ==
- 知识迭代3Bug修复 (#8 POST端点/#7 MERGE幂等/#6 过期检查)
- 会议室预定-小鱼易联终端 (40文件, 40/40测试通过)
- IT资产升级审批推送 (asset_service.py)

== 需求文档 (2项) ==
- 坐席端AI辅助消息框-PRD (4项新功能确认)
- 坐席端布局优化建议 v2.0 (7天计划)

== 新增文档 ==
- 日报-2026-07-11.md
- 知识迭代Bug修复报告-20260711.md
- 会议室预定-部署指南.md
- CHANGELOG.md 更新

== 测试 ==
- test_todo_integration.py: 40/40
- test_meetingroom.py: 40/40
- test_bugfix_ki_suggestions.py: 21/21
2026-07-11 23:13:10 +08:00

572 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 系统设计文档 — 代办事项真实数据源集成
> 项目:企微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_id``itsm_app_secret``itsm_base_url` 配置项
6. **更新 `schemas/todo_item.py`**`VALID_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. 数据结构和接口(类图)
```mermaid
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 代办列表查询流程
```mermaid
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 代办详情查询流程
```mermaid
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 签名认证流程
```mermaid
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()`:调 `getapprovaldata`sp_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
- 缓存 key`todo: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条硬编码数据)
- 删除 `TodoItemResponse``TodoItemListResponse`(移至 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_userid`header 或 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.vue`P0+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.vue`P0):
- 移除 `import DeviceDetail from './task/DeviceDetail.vue'`
- 移除 `v-else-if="todoItem.type === 'device'"` 条件渲染块
- `typeLabelMap` 移除 `device: '🖥 设备异常'`
- 移除 `.tdv-type-device` CSS 样式
3. `DeviceDetail.vue`P0):
- 文件头注释标记为"已废弃 — v1.0 移除 device 类型"
- 保留文件但不再被任何组件引用
4. `TicketDetail.vue`P1):
- 适配真实 ITSM 工单数据结构(description 字段映射调整)
- 底部操作按钮改为"在 ITSM 中打开"跳转链接(P2
5. `ApprovalDetail.vue`P1):
- 适配真实企微审批数据结构(description 字段映射调整)
- 底部操作按钮改为"在企微审批中打开"跳转链接(P2)
---
### 8. 共享知识(跨文件约定)
#### 8.1 统一数据映射规则
**企微审批 → TodoItemData 映射**
```python
{
"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 映射**
```python
{
"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_type``all` / `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. 任务依赖图
```mermaid
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
```