Files
wecom_it_smart_desk/docs/01-产品文档/06-审批与待办/PRD-REQ-审批-001-Todo集成-v1.0.md
T
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

165 lines
14 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.
# PRD: 代办事项系统集成真实数据源
> **子系统**: 06-审批与待办
> **模块**: 代办集成
> **版本**: v1.0
---
## 项目信息
- **Language**: 中文
- **Programming Language**: 后端 FastAPI + SQLAlchemy + PostgreSQL + Redis | 前端 Vue3 + Element Plus + Pinia
- **Project Name**: `todo_integration_real_data`
- **原始需求复述**: 将坐席端"代办事项"面板从 mock 数据切换为真实数据源。数据来源包括企微审批待处理审批单(`getapprovalinfo` API)和 ITSM 运维平台"我的代办"工单(ITSM API + 企微 SSO 免登录)。移除所有 mock 数据和 device 类型,不保留 mock fallback。
- **API 变更说明**: 原始需求引用的 `getapprovaldata``/cgi-bin/oa/getapprovaldata`)已于 2019 年废弃返回 404,实际使用新接口 `getapprovalinfo``/cgi-bin/oa/getapprovalinfo`),分页参数从 `cursor`(int) 变为 `new_cursor`(string),返回从 `data` 数组变为 `sp_no_list` 字符串数组。
- **Token 变更说明**: 原始设计使用 `ApprovalTokenManager`(审批应用 Secret),但服务器出口 IP `218.75.34.87` 未在审批应用可信 IP 白名单中(errcode=60020)。实际改用 `TokenManager`IT 支持应用 Secret,IP 已在白名单)。
---
## 1. 产品目标
1. **数据真实性**:将坐席端代办面板从 20 条硬编码 mock 数据切换为两个真实数据源(企微审批 + ITSM 工单),确保坐席看到的是真实待处理工作。
2. **统一聚合**:后端聚合企微审批和 ITSM 工单两个异构数据源为统一代办列表 API,保持前端交互体验不变。
3. **彻底去 mock**:移除所有 mock 数据(后端 `MOCK_TODO_ITEMS`、前端 `mockTodoListData`)和 device 类型相关代码,确保系统数据真实性,不留 fallback。
---
## 2. 用户故事
1. **作为坐席**,我希望在代办面板看到我真实的待处理企微审批单和 ITSM 工单,而不是 mock 数据,以便我了解真实的工作待办。
2. **作为坐席**,我希望点击代办条目能查看对应的审批单/工单详情,以便快速了解任务内容并决定处理优先级。
3. **作为坐席**,我希望代办列表能按类型(全部/审批/工单)筛选,以便高效分类处理不同来源的待办。
4. **作为坐席**,我希望代办列表能反映最新的待处理状态(支持手动刷新),以便不遗漏新分配的审批和工单。
5. **作为坐席**,我希望代办条目中能清晰区分审批单和工单的类型标签和优先级,以便快速识别紧急事项。
---
## 3. 需求池
### P0 — Must Have
| ID | 需求描述 | 验收标准 |
|---|---|---|
| REQ-001 ✅ | **后端实现企微审批待处理列表查询**:集成企微 `getapprovalinfo` APIPOST `https://qyapi.weixin.qq.com/cgi-bin/oa/getapprovalinfo`),查询当前登录坐席的待处理审批单列表。使用 `TokenManager`IT 支持应用 Secret)获取 access_token,通过 `filters` 参数按审批状态(sp_status=1 审批中)过滤,返回 sp_no_list 后调用 `getapprovaldetail` 补充详情,在代码层按 18 个模板 ID 过滤,再通过 `_extract_current_approver` 按当前审批人过滤。 | ① 调用 `getapprovalinfo` 成功返回审批单号列表;② 仅返回 sp_status=1(审批中)的审批单;③ 支持分页(new_cursor + new_next_cursor);④ token 获取复用 Redis 缓存机制(`wecom:access_token`);⑤ 异常时记日志并返回空列表(不阻塞另一个数据源) |
| REQ-002 ⏳ | **后端实现 ITSM 代办工单列表查询**:集成 ITSM 平台 API`https://devops.dc.servyou-it.com/ITSM/workflow/allTickets/umiAllticketsTodo`),通过企微 SSO 免登录认证查询当前坐席的代办工单列表。**待办:需抓包获取 API 接口规范 + 申请 app_id/app_secret**。 | ① 成功调用 ITSM API 返回代办工单列表;② SSO 认证无需坐席手动登录;③ 返回工单包含标题、优先级、状态、创建时间等关键字段;④ 网络异常时记日志并返回空列表(不阻塞另一个数据源) |
| REQ-003 ✅ | **后端聚合两个数据源为统一代办列表 API**:重写 `GET /api/todo-items` 端点,并行查询企微审批和 ITSM 工单两个数据源,将结果映射为统一的 `TodoItemResponse` 格式返回。移除 `MOCK_TODO_ITEMS` 硬编码数据。 | ① API 返回的 items 仅包含 type=approval 和 type=ticket 两种类型;② 两个数据源并行查询(asyncio.gather),任一失败不影响另一个返回;③ 返回数据按优先级排序(urgent→high→normal);④ 响应时间 < 3 秒(两个外部 API 并行);⑤ 不再返回任何 mock 数据 |
| REQ-004 ✅ | **前端移除 mock fallback 逻辑**:修改 `stores/todo.ts`,移除 `import { mockTodoListData } from '@/mock/data'` 和 catch 块中的 mock fallback 逻辑。API 调用失败时显示空列表 + 错误提示,不回退 mock。 | ① 代码中不再 import mockTodoListData;② API 失败时 todoList 为空数组;③ 控制台输出错误日志;④ 开发环境和生产环境行为一致(无 DEV 特殊分支) |
| REQ-005 ✅ | **移除 device 类型相关代码**:后端从 `VALID_TODO_TYPES` 移除 "device"schema/model);前端从 `typeLabel``typeLabelMap``TaskDetailView` 的 DeviceDetail 分支、TodoPanel 的 type-device 样式中移除 device 相关代码。`DeviceDetail.vue` 组件可保留文件但不再被引用。 | ① 后端 `VALID_TODO_TYPES` 不再包含 "device";② 前端 `typeLabel` 不再包含 device 映射;③ `TaskDetailView.vue` 不再渲染 DeviceDetail 分支;④ TodoPanel 中无 type-device 样式或该样式不生效;⑤ API 返回数据中无 type=device 的条目 |
### P1 — Should Have
| ID | 需求描述 | 验收标准 |
|---|---|---|
| REQ-006 ✅ | **代办列表支持按类型筛选**:在 `TodoPanel.vue` 标题行增加"全部 / 审批 / 工单"三个筛选 Tab,点击后过滤展示对应类型的代办条目。后端 API 增加 `type` 查询参数支持服务端过滤。 | ① 默认显示"全部";② 点击"审批"仅显示 type=approval 的条目;③ 点击"工单"仅显示 type=ticket 的条目;④ 筛选状态有视觉高亮 |
| REQ-007 ⏳ | **点击代办条目展示对应详情**:复用现有 `TaskDetailView``TicketDetail` / `ApprovalDetail` 子视图。点击审批条目展示审批详情,点击工单条目展示工单详情。详情数据来自后端 `GET /api/todo-items/{id}` 端点(需支持按 ID 查询单条代办详情,可能需要缓存或重新查询外部 API)。 | ① 点击审批条目 → 中间栏切换为 task 视图,展示 ApprovalDetail;② 点击工单条目 → 展示 TicketDetail;③ 详情数据正确展示标题、优先级、描述等字段;④ 返回按钮正常回到会话视图 |
| REQ-008 ✅ | **代办列表支持手动刷新**:在 TodoPanel 标题行增加刷新按钮,点击后重新调用 `fetchTodoList()` 获取最新数据。 | ① 刷新按钮可见且有刷新图标;② 点击后触发 API 重新查询;③ 刷新过程中按钮显示 loading 状态;④ 刷新完成后列表更新 |
### P2 — Nice to Have
| ID | 需求描述 | 验收标准 |
|---|---|---|
| REQ-009 ⏳ | **代办列表自动定时刷新**TodoPanel 挂载后每 60 秒自动刷新一次代办列表,组件卸载时清除定时器。 | ① 每 60 秒自动调用 fetchTodoList;② 组件卸载时 clearInterval;③ 不与手动刷新冲突 |
| REQ-010 ⏳ | **代办条目支持跳转到原系统操作**:在详情页底部增加"在企微审批中打开" / "在 ITSM 中打开"按钮,点击后在新窗口打开对应的审批单/工单原始页面。 | ① 审批详情页有"在企微审批中打开"按钮,跳转到企微审批 URL;② 工单详情页有"在 ITSM 中打开"按钮,跳转到 ITSM 工单 URL |
---
## 4. UI 设计稿描述
### 4.1 TodoPanel(左栏待办面板)变化
**标题行变化**
- 现有:`📋 待办事项` + 紧急数量徽章
- 变更后:`📋 待办事项` + 紧急数量徽章 + **类型筛选 Tab**(全部/审批/工单)+ **刷新按钮**(🔄 图标)
- 布局:标题文字左对齐,筛选 Tab 紧跟其后,刷新按钮最右侧
**待办条目变化**
- 每条代办仍保持现有布局:优先级圆点 + 标题文本 + 类型标签 + 时间 + 上报人头像
- 类型标签仅保留两种:`工单`(蓝色,type=ticket)、`审批`(紫色,type=approval
- **移除** `设备`(橙色,type=device)标签
- 数据来源从 mock 变为真实 API 返回
**空状态变化**
- 现有:`暂无待办`
- 变更后:API 加载中显示 loading 动画;加载完成无数据时显示`暂无待办`API 失败时显示`加载失败,点击重试`
**底部坐席统计**
- 保持不变(仍使用 `getAgentStats()` mock 数据,不在本次改造范围内)
### 4.2 TaskDetailView(中间栏任务详情)变化
**顶部标题栏**
- 类型标签映射移除 device`📋 运维工单` / `📝 审批单`(移除 `🖥 设备异常`
- 优先级标签保持不变
**内容区**
- type=ticket → 渲染 `TicketDetail` 子视图(保持不变,但数据来自 ITSM 真实工单)
- type=approval → 渲染 `ApprovalDetail` 子视图(保持不变,但数据来自企微真实审批单)
- **移除** type=device → `DeviceDetail` 分支
- 未知类型 fallback 保持不变
### 4.3 ApprovalDetail / TicketDetail 子视图
- 整体布局保持不变(卡片式信息展示 + 操作按钮区)
- 数据字段需适配真实 API 返回结构:
- 审批详情:`description` 字段需映射企微审批返回的申请人、审批类型、表单内容等
- 工单详情:`description` 字段需映射 ITSM 返回的工单标题、类型、上报人、描述等
- 操作按钮(审批通过/拒绝、接单/结单等)的具体行为取决于待确认问题 #4 的决策
---
## 5. 待确认问题
### 5.1 ITSM API 相关(阻塞 REQ-002)— ⏳ 待申请授权
| # | 问题 | 说明 | 状态 |
|---|------|------|------|
| Q1 | **ITSM API 的完整接口规范** | 需抓包获取请求方法/参数/返回格式 | ⏳ 待 agent-browser 抓包 |
| Q2 | **ITSM SSO 认证的具体方式** | 后端调用 ITSM API 的认证链路待确认 | ⏳ 待抓包确认 |
| Q2.1 | **ITSM app_id/app_secret 申请** | openapi 签名认证需要 app_id 和 app_secret(签名:params 按 key 升序 → 拼接 value → quote_plus → SHA1 → 大写 hex | ⏳ 待向 ITSM 平台申请 |
### 5.2 企微审批相关(影响 REQ-001)— ✅ 已确认
| # | 问题 | 确认结果 |
|---|------|---------|
| Q3 | 过滤条件与时间范围 | ✅ API 层只传 `sp_status=1`(每个 filter key 只能一次),代码层按 18 个模板 ID 过滤。时间范围 7 天(企微 API 最大 31 天,建议改为 30 天) |
| Q4 | "待处理"定义 | ✅ `sp_status=1`=审批中,需二次过滤当前审批人。企微 API 实际字段:`sp_record[].sp_status`(非 `status`)、审批人在 `sp_record[].details[].approver.userid`(非 `approver[].userid` |
### 5.3 交互与数据策略相关(影响 REQ-007、REQ-003
| # | 问题 | 说明 | 状态 |
|---|------|------|------|
| Q5 | **代办状态更新的交互方式** | 现有 `ApprovalDetail` 有"审批通过/拒绝/转交"按钮,`TicketDetail` 有"接单/处理/结单/转派"按钮。如需在服务台内操作,需对接企微审批 API 和 ITSM 工单 API 的状态变更接口 | ✅ 已确认:仅展示+跳转,不在服务台内操作 |
| Q6 | **代办详情查询策略** | 列表查询时是否缓存每条代办的详情数据(Redis)?还是点击时实时查询外部 API? | ✅ 已确认:Redis 缓存 45s,手动刷新跳过缓存 |
| Q7 | **数据刷新策略** | 每次打开/刷新代办面板都实时查询两个外部 API?还是后端缓存? | ✅ 已确认:初始+60s自动=用缓存,手动刷新=`_force=1`跳过缓存 |
| Q8 | **ITSM 工单优先级映射** | ITSM 返回的优先级字段如何映射到 urgent/high/normal | ⏳ 待 ITSM API 返回结构确认 |
---
## 附录:现有代码资产清单
### 可复用资产
| 资产 | 位置 | 复用方式 |
|---|---|---|
| `TokenManager` | `backend/app/utils/token_manager.py` | ✅ 已复用,获取企微 access_tokenIT 支持应用 Secret |
| `get_approval_detail()` | `backend/app/api/approval.py` | ✅ 已复用,获取审批单详情 |
| `_extract_current_approver()` | `backend/app/api/approval.py` | ✅ 已复用,过滤当前审批人(字段名已修正) |
| `_APPROVAL_TEMPLATE_IDS` | `backend/app/services/todo_source_service.py` | ✅ 已实现,18 个模板 ID 代码层过滤 |
| `TodoItem` Model | `backend/app/models/todo_item.py` | 可选复用(如需持久化缓存代办数据) |
| `TodoItemResponse` Schema | `backend/app/schemas/todo_item.py` | ✅ 已复用,已移除 device 类型 |
| 前端 `TodoPanel.vue` | `frontend-agent/src/components/conversation/TodoPanel.vue` | ✅ 已改造 |
| 前端 `TaskDetailView.vue` | `frontend-agent/src/components/chat/TaskDetailView.vue` | ✅ 已改造 |
| 前端 `TicketDetail.vue` / `ApprovalDetail.vue` | `frontend-agent/src/components/chat/task/` | ✅ 已改造 |
### 需删除/废弃资产
| 资产 | 位置 | 处理方式 |
|---|---|---|
| `MOCK_TODO_ITEMS` | `backend/app/api/todo_items.py` | ✅ 已删除 |
| `mockTodoListData` | `frontend-agent/src/mock/data.ts` | ✅ 已删除 |
| mock fallback 逻辑 | `frontend-agent/src/stores/todo.ts` L66-72 | ✅ 已删除 |
| `DeviceDetail.vue` | `frontend-agent/src/components/chat/task/DeviceDetail.vue` | 保留文件但不再被引用 |
| device 类型样式 | `TodoPanel.vue` L299-304 | ✅ 已删除 |
| device 类型映射 | `TodoPanel.vue` L110, `TaskDetailView.vue` L105 | ✅ 已删除 |