feat(agent/backend): 坐席端审批线降级跳转 + 回调最终一致回写
实现 Phase 0 审批线 T01+T02(依据 PRD-REQ-坐席-011 + U-1 技术验证结论)。
前端(T01):
- TaskDetailView.handleAction 移除 mock toast,审批类仅 console.info
- ApprovalDetail 通过/拒绝/转交 + 打开按钮接线为企微审批深链真实跳转(<a target="_blank">)
- useWebSocket 新增 todo_status_changed 实时刷新分支
后端(T02):
- approval.py 新增 writeback_approval_todo_status 主入口 + /approval/callback 接线
- approval_webhook.py 新增 _writeback_agent_todo 复用回调→WS 推送通道
- 以 approval:{sp_no} 为关联键,缓存就地改写 + 7天快照 + WS 推送,最终一致
测试:src/backend/tests/test_approval_todo_writeback.py(51 例全绿)
文档:PRD-REQ-坐席-011 v0.1、技术验证-U-1 v1.0
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
# PRD — 坐席端统一工作队列重构
|
||||
|
||||
> **REQ编号**: REQ-坐席-011
|
||||
> **版本**: v0.1(方案草案 / 沟通确认阶段)
|
||||
> **状态**: 🟡 待评审 — 含未决项,不得据此排期开发
|
||||
> **优先级**: P1(其中 Phase 0 为 P0)
|
||||
> **日期**: 2026-08-08
|
||||
> **作者**: 宋献
|
||||
> **关联**: REQ-坐席-004(任务详情视图切换)、REQ-坐席-009(会话状态Tab筛选)、任务说明书 #132
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景
|
||||
|
||||
### 1.1 提案
|
||||
|
||||
取消坐席工作台「待办事项」独立面板,将审批单与工单纳入左栏,与咨询会话统一按优先级和分类排列。
|
||||
|
||||
### 1.2 提案原始论据与核查结论
|
||||
|
||||
| # | 原始论据 | 核查结论 | 依据 |
|
||||
|---|---|---|---|
|
||||
| ① | 咨询/审批/工单本质都是待处理的单个事件 | ✅ **成立** | 与 Zendesk / ServiceNow Agent Workspace 的单一工作队列范式一致 |
|
||||
| ② | 均关联对应员工(左栏) | ❌ **当前不成立** | `TodoItemData`(`src/frontend-agent/src/api/todo.ts:17`)顶层无 employee 字段 |
|
||||
| ③ | 均需辅助处理功能(右栏) | ⚠️ **当前未实现,且不应简单共用** | `AiAssistantPanel.vue:100` 仅依赖 `conversationId`,不感知 `workspaceView` |
|
||||
|
||||
**对 ② 的补充**:前端待办头像为伪造实现 —— `todoAvatarText()`(`TodoPanel.vue:224-233`)从 `title` 按 `" - "` 切分后取**部门名最后一个字**,颜色取 `title` 的 hash。后端 `applicant` 仅有 userid,埋在 `description` JSON 内未暴露到顶层(`src/backend/app/services/todo_source_service.py:485`)。
|
||||
|
||||
**对 ③ 的补充**:会话辅助信息(知识推荐 / 话术 / 排查步骤)服务于「对话」;审批辅助信息(申请人历史 / 同类通过率 / 合规校验)服务于「决策」。二者不同源、不同构。统一三栏布局 ≠ 统一右栏内容,右栏必须按工作项类型分派渲染。
|
||||
|
||||
### 1.3 立项主理由(重新论证)
|
||||
|
||||
原提案表述为「布局更合理、处理更高效」,论证力度不足。本 PRD 采用以下主理由:
|
||||
|
||||
> **待办面板置于右栏属于信息架构语义错位。** 右栏的定义是「当前会话的上下文辅助面板」,而待办是**不隶属于任何会话的全局工作队列**。将全局队列置入上下文面板,破坏了右栏的语义一致性,并导致坐席的工作入口分裂为两处。
|
||||
|
||||
### 1.4 与 #132 的关系(须在评审中说明)
|
||||
|
||||
| 项 | 内容 |
|
||||
|---|---|
|
||||
| #132 做了什么 | 2026-08-02~08-03,待办面板由**左栏底部**迁至**右栏底部** |
|
||||
| #132 的决策依据 | 任务说明书仅记载「与 v1.1 原型保持一致」,验收项含「左栏会话列表不被压缩」。**无信息架构层面论证** |
|
||||
| 本次是否为返工 | **否**。#132 是面板位移;本次是数据模型与列表融合,属架构升级 |
|
||||
| 需规避 | 若仅将面板移回左栏(见 §5 方案 B),将原地重演 #132 的左栏空间竞争问题,构成第三次搬迁 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 已确认决策
|
||||
|
||||
以下三项经沟通确认,作为本方案的设计约束。
|
||||
|
||||
| # | 决策项 | 结论 | 推导出的约束 |
|
||||
|---|---|---|---|
|
||||
| **D-1** | 左栏列表主键模型 | **以「事」为主键** | 每个咨询/审批/工单各占一条;员工信息作为条目属性展示,不作聚合维度 |
|
||||
| **D-2** | 实施顺序 | **先闭环、后合并** | Phase 0(操作闭环)为 Phase 2(布局合并)的硬前置,顺序不可调换 |
|
||||
| **D-3** | 待办归属范围 | **本人指派 + 组内未分配** | 引入「认领」动作与并发控制 —— **当前系统完全没有此能力,属新增需求** |
|
||||
|
||||
### 2.1 D-3 的成本提示
|
||||
|
||||
D-3 不是筛选条件的调整,而是新增一条状态机路径。当前 `todo-items` 接口按 `assigned_agent_id` 过滤,不存在无主池概念。落地需新增:
|
||||
|
||||
- 后端:未分配待办的查询口径(组边界定义见 §6 未决项 U-3)
|
||||
- 后端:`claim`(认领)动作 + 幂等与乐观锁(防并发抢单)
|
||||
- 前端:认领按钮、认领中态、认领失败(已被他人认领)的提示
|
||||
- 数据:认领操作的审计留痕
|
||||
|
||||
---
|
||||
|
||||
## 3. 阻塞项(P0)
|
||||
|
||||
### 3.1 任务详情操作按钮全部为 Mock
|
||||
|
||||
```js
|
||||
// src/frontend-agent/src/components/chat/TaskDetailView.vue:117
|
||||
function handleAction(action: string): void {
|
||||
ElMessage.success(`操作成功:${action}`) // 仅 toast,不调用任何接口
|
||||
}
|
||||
```
|
||||
|
||||
REQ-坐席-004 §2.4 定义的全部操作 —— 工单(接单 / 开始处理 / 结单 / 转派)、审批(通过 / 拒绝 / 转交)—— **均不生效**。
|
||||
|
||||
**风险定级:P0,阻塞布局合并。**
|
||||
|
||||
理由:当前待办面板位于右栏底部 260px 区域,坐席误操作的暴露面有限。一旦将待办提升至左栏主队列首屏,等同于把不可用功能放置于最高可见度位置。坐席点击「审批通过」后收到绿色成功提示,而企业微信侧该单仍处于挂起状态 —— 属于会造成真实业务后果的错误反馈。
|
||||
|
||||
---
|
||||
|
||||
## 4. 其余工程前置条件
|
||||
|
||||
| 编号 | 前置项 | 现状 | 不处理的后果 |
|
||||
|---|---|---|---|
|
||||
| **B-1** | 待办缺员工身份字段 | 顶层无 employee_id/name/department;`applicant` 仅 userid 且未暴露 | 混排后同列表内会话条目为真人头像+姓名+部门,待办条目为伪头像+部门残字,身份密度断裂,无法按人扫视 |
|
||||
| **B-2** | 待办无实时推送 | 会话走 WS(`useWebSocket.ts` 12 类事件);待办为 60s 轮询(`TodoPanel.vue:143`),后端**无任何 todo WS 事件** | 紧急审批最长滞后 60s 才浮升;同列表内会话实时跳动而待办静止,坐席对排序失去信任 |
|
||||
| **B-3** | 优先级不可比 | 会话为 `urgency_score` 1–5 叠加 6 档加权(置顶 −10000 / 代办 −5000 / 招手 −2000 / 需介入 −1500 / 情绪 −1000 / VIP −800);待办仅 `urgent/high/normal` 三档,且**同档内无二级排序**(`todo_aggregator_service.py:134` 无时间兜底) | 混排结果不可解释,坐席无法预期条目位置 |
|
||||
| **B-4** | SLA 语义冲突 | 会话等待成本为「用户实时干等」(秒级);审批为「当日处理完毕」(小时级) | 纯优先级排序会使 urgent 审批将 `serving` 状态会话挤出首屏。**漏回一条实时会话的代价显著高于晚 30 分钟处理一单审批**,纯优先级模型会系统性放大该错误 |
|
||||
|
||||
**B-4 的设计要求**:统一排序权重**不得**仅取优先级,必须引入「实时性/等待可感知度」维度。建议排序键为 `f(优先级, SLA剩余时间, 对端是否在线等待)`,其中「对端在线等待」应具备最高权重档位。具体系数见 §6 未决项 U-2。
|
||||
|
||||
---
|
||||
|
||||
## 5. 方案选型
|
||||
|
||||
| 方案 | 做法 | 优势 | 劣势 | 采纳 |
|
||||
|---|---|---|---|---|
|
||||
| **A** 完全融合 | 单列表跨类型混排 | 真正的单一队列 | 四项前置全欠;实时会话被挤压;排序不可解释 | ❌ 不作为首个形态 |
|
||||
| **B** 移回左栏保持分区 | 左栏上会话、下待办,可折叠 | 改动最小(≈0.5d),立即消除右栏语义错位 | 仍为两个列表;重演 #132 左栏空间竞争,构成第三次搬迁 | ❌ 不单独实施 |
|
||||
| **C** 统一容器 + Tab 分层 | 左栏 Tab 增加类型层「会话 / 待办 / 全部」,「全部」下混排 | 兼顾专注模式与全局视图;可灰度、可回退 | Tab 层级加深一层 | ✅ **首个落地形态** |
|
||||
| **D** WorkItem 统一模型 | 后端抽象 WorkItem,会话/工单/审批为其子类型,共享优先级、SLA、关联人、状态机 | 架构最干净;未来接入设备告警、巡检任务零边际成本 | 后端工作量高一个数量级 | ✅ **目标态** |
|
||||
|
||||
**采纳路径:以 D 为目标态,C 为首个落地形态,Phase 0 为硬前置。**
|
||||
|
||||
---
|
||||
|
||||
## 6. 实施路线
|
||||
|
||||
> 工时为粗估,用于排序参考,不作为承诺。
|
||||
|
||||
### Phase 0 — 操作闭环(P0,阻塞后续全部阶段)
|
||||
|
||||
> **U-1 已验证结论(2026-08-08)**:企微审批**不可服务端闭环**(官方文档证实无代审批接口,PC Web 无 JS-SDK 能力),审批动作必须降级为「跳转企微原系统 + 回调状态回写」;ITSM 工单因操作类 OpenAPI 尚未落地,**暂不可判定服务端闭环**,需先降级跳转,待 T05 外部依赖到位后升级。详见 `docs/02-技术文档/技术架构/技术验证-U-1-审批与工单操作闭环可行性-v1.0.md`。
|
||||
|
||||
| 任务 | 说明 | 降级层级 |
|
||||
|---|---|---|
|
||||
| 审批操作 → 降级跳转 + 回写 | 通过 / 拒绝 / 转交 → 点击「在企微审批中打开」跳转原系统,由 `approval_webhook.py` 接收 `sys_approval_change` 回调 → 补全 `approval.py:902` 状态回写 → WS 推送 | Level 0 立即可做;Level 1 需补回调回写 |
|
||||
| 工单操作 → 降级跳转(暂) | 接单 / 开始处理 / 结单 / 转派 → 点击「在 ITSM 中打开」跳转原系统;ITSM 写接口到位前不承诺服务端闭环(受 T05 外部阻塞)。**⚠️ 注意:该跳转依赖工单可见,而当前 ITSM 读列表亦未实现(见 U-1.2),故本行在读链路打通前无实际可操作对象** | Level 0(暂,受 U-1.2 前置) |
|
||||
| 失败态处理 | 移除无条件 `ElMessage.success`,改为「跳转成功提示 + 状态回写后刷新」按三态分派;仍禁止以 toast 作为验收依据 | — |
|
||||
|
||||
**验收标准**:操作后外部系统(ITSM / 企微审批)**状态真实变更**,且前端反馈与外部状态**最终一致**(通过回调 / webhook 回写达成)。禁止以 toast 成功作为验收依据。审批 / 工单在降级跳转模式下,以「跳转成功 + 原系统状态通过回调回写并刷新」作为验收闭环,不要求前端内嵌直接操作。
|
||||
|
||||
### Phase 1 — 数据层归一(后端)
|
||||
|
||||
| 任务 | 对应前置项 |
|
||||
|---|---|
|
||||
| `TodoItem` 顶层暴露 `employee_id` / `employee_name` / `department`(由 applicant userid 反查企微通讯录) | B-1 |
|
||||
| 新增 `sla_due_at` 字段 | B-4 |
|
||||
| 定义 WorkItem 统一排序权重模型 | B-3 / B-4 |
|
||||
| 新增 todo WS 事件:`todo_created` / `todo_updated` / `todo_claimed` / `todo_resolved` | B-2 |
|
||||
| 未分配待办查询口径 + `claim` 动作(幂等 + 乐观锁) | D-3 |
|
||||
|
||||
### Phase 2 — 左栏统一容器(前端,方案 C)
|
||||
|
||||
| 任务 |
|
||||
|---|
|
||||
| 左栏 Tab 分层:类型层(会话 / 待办 / 全部)+ 状态层(沿用 REQ-009 的四态) |
|
||||
| 定义统一 `ListItem` 类型(含 `kind` 判别式),条目组件支持会话态与任务态两种渲染 |
|
||||
| 移除 `AiAssistantPanel.vue` 中的 `<TodoPanel />` 挂载 |
|
||||
| **右栏按 `workspaceView` 分派渲染**(修复现存缺陷:任务详情下右栏仍显示上一会话的排查建议) |
|
||||
| 修复 `selectConversation` 不重置 `workspaceView` 的问题 |
|
||||
| 视图切换时保留输入框草稿与滚动位置(当前 `v-if/v-else` 互斥卸载会丢失,`ReplyBox.vue:307` 的 `inputText` 为组件局部 ref) |
|
||||
|
||||
### Phase 3 — 混排与观察
|
||||
|
||||
| 任务 |
|
||||
|---|
|
||||
| 「全部」Tab 下按归一化权重混排 |
|
||||
| 灰度发布 + 2 周数据观察(观测指标见下) |
|
||||
| 依据数据决定是否推进 D(后端 WorkItem 模型) |
|
||||
|
||||
**观测指标**:会话首响时长(是否因混排而劣化)、待办平均处理时长、坐席 Tab 切换频次、认领冲突率。
|
||||
|
||||
---
|
||||
|
||||
## 7. 未决项
|
||||
|
||||
| 编号 | 未决项 | 影响 | 需谁决策 |
|
||||
|---|---|---|---|
|
||||
| **U-1** | 审批操作能否在坐席 PC Web 端完成 | **已验证(2026-08-08)**:企微审批**不可服务端闭环**——官方文档证实无代审批接口,PC Web 亦无 JS-SDK 原生表单能力。结论:审批动作须降级为「跳转企微原系统 + `sys_approval_change` 回调状态回写」。该结论**不阻塞** Phase 0 启动(降级跳转可立即落地)。验证依据见 `技术验证-U-1-审批与工单操作闭环可行性-v1.0.md` | 已闭环(技术) |
|
||||
| **U-1.1** | ITSM 写接口到位时间 | 工单「接单 / 开始处理 / 结单 / 转派」能否服务端闭环,取决于 ITSM 平台方提供的操作类 OpenAPI 文档、`app_id`/`secret` 写权限、测试账号(对应 T05)。当前项目内 ITSM 仅实现只读查询(`itsm_service.py` 全文件无写操作),无写接口实证。该项是**工单服务端闭环的外部阻塞项**,未到位前工单同样降级跳转 | 平台方 / 外部协调 |
|
||||
| **U-1.2** 🔴 | **ITSM 工单当前在坐席端完全不可见(读链路即断)** | **代码实证(2026-08-08 主理人复核补录)**:`ITSMService.get_todo_list()`(`src/backend/app/services/itsm_service.py:113-130`)**无条件 `return []`**,仅打印告警「ITSM 代办列表 API 尚未实现」。`TodoAggregatorService`(`todo_aggregator_service.py:110-127`)并发聚合审批与 ITSM 两源,ITSM 分支恒为空 → **待办列表中工单数量恒为 0,现有待办全部是企微审批单**。且因无列表即无 `process_instance_id`,已实现的 `get_todo_detail` / `workitem/detail`(只读)**实际也无从调用**。<br>**影响**:此项**比 U-1.1(写接口缺失)更前置**——写接口的前提是先能读到工单。在读链路打通前,「工单纳入统一队列」在数据层无内容可纳入,Phase 2 的工单部分实为空跑 | 平台方 / 外部协调(与 U-1.1 合并索取) |
|
||||
| **U-2** | 统一排序权重的具体系数 | 决定混排结果是否可解释、是否会挤压实时会话 | 产品 + 坐席试用反馈 |
|
||||
| **U-3** | 「组内未分配」的组边界定义 | 全体 IT 支持组?还是按技能/区域路由后的子集?直接影响列表长度 | 产品 |
|
||||
| **U-4** | 认领并发冲突的交互 | 两名坐席同时认领同一单时的提示与落败方引导 | 产品 |
|
||||
| **U-5** | 列表长度上限与虚拟滚动 | 合并 + 组内未分配后列表显著变长,260px 宽左栏的承载能力 | 前端 |
|
||||
| **U-6** | 待办条目的未读/变更标识 | 会话有 `is_todo` 等标志,待办无未读概念,混排后视觉规则需统一 | 产品 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 风险登记
|
||||
|
||||
| 编号 | 风险 | 等级 | 缓解措施 |
|
||||
|---|---|---|---|
|
||||
| R-1 | 实时会话被审批挤出首屏,首响时长劣化 | 🔴 高 | 排序模型引入「对端在线等待」最高权重档;Phase 3 灰度观测首响指标 |
|
||||
| R-2 | 认领并发抢单导致重复处理 | 🟡 中 | 后端乐观锁 + 幂等;前端落败态明确提示 |
|
||||
| R-3 | 轮询向 WS 迁移期间的双通道数据不一致 | 🟡 中 | 迁移期保留轮询作为兜底,以 WS 事件为主、轮询做对账 |
|
||||
| R-4 | 第三次布局搬迁引发团队对决策稳定性的质疑 | 🟢 低 | 评审时明确说明本次为架构升级而非位移返工(见 §1.4) |
|
||||
| R-5 | 工单闭环因 ITSM 写接口缺失受阻,导致 Phase 0 工单部分停滞 | 🟡 中 | U-1 已验证:审批走降级跳转**不阻塞** Phase 0;工单闭环的外部阻塞已独立为 U-1.1 / T05,审批与工单降级跳转可先行落地,不拖累整体路线 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 明确不在本次范围
|
||||
|
||||
- 会话侧数据模型改造(`urgency_score` 与加权标志保持不变)
|
||||
- H5 员工端任何改动
|
||||
- 设备异常类型(REQ-004 §2.3 曾定义,现已从 `TodoPanel` 移除,本次不恢复)
|
||||
- 坐席在线统计数据源(当前取自 `src/mock/data.ts`,属独立技术债)
|
||||
|
||||
---
|
||||
|
||||
## 10. 变更记录
|
||||
|
||||
| 日期 | 版本 | 变更内容 | 变更人 |
|
||||
|---|---|---|---|
|
||||
| 2026-08-08 | v0.1 | 创建方案草案;固化 D-1/D-2/D-3 三项决策;完成原始论据核查与现状事实核实 | 宋献 |
|
||||
| 2026-08-08 | v0.1 | 据 U-1 技术验证结论(架构师高见远)修订 §6 Phase 0 与 §7 U-1:审批明确为降级跳转 + 回写(U-1 已验证);新增 U-1.1 ITSM 写接口外部阻塞项;同步下调 R-5 风险表述 | 宋献 |
|
||||
| 2026-08-08 | v0.1 | 主理人复核补录 **U-1.2**:代码实证 `ITSMService.get_todo_list()` 无条件返回空列表,ITSM 工单在坐席端读链路即断、当前待办全为企微审批单。该项前置于 U-1.1,一并标注于 §6 Phase 0 工单行 | 齐活林(交付总监) |
|
||||
@@ -0,0 +1,296 @@
|
||||
# 技术验证 U-1:坐席 PC Web 端审批与工单操作闭环可行性
|
||||
|
||||
> **文档版本**:v1.0
|
||||
> **验证人**:高见远(架构师)
|
||||
> **验证日期**:2026-08-08
|
||||
> **关联 PRD**:`PRD-REQ-坐席-011-统一工作队列重构-v0.1.md`(U-1,§6 Phase 0,§7)
|
||||
> **任务性质**:技术可行性验证(**未修改任何业务源码**)
|
||||
> **代码核查范围**:仅 `src/` 下活跃代码(`frontend-agent/`、`backend/`),忽略根目录废弃的 `frontend/`、`backend_bak/`。
|
||||
|
||||
---
|
||||
|
||||
## 0. 结论先行(可行性判定矩阵)
|
||||
|
||||
**核心结论**:坐席在 PC Web 端**无法**通过服务端接口直接完成审批单的「通过 / 拒绝 / 转交」动作;工单的「接单 / 开始处理 / 结单 / 转派」动作因 ITSM 操作类 OpenAPI 尚未落地,**暂不可判定可行**,需向平台方索取文档与权限。两者均**不能**在 Phase 0 内实现"服务端真实闭环",必须分级降级。
|
||||
|
||||
| 对象 | 动作 | 可行性判定 | 依据类型 | 关键约束 |
|
||||
|------|------|-----------|---------|---------|
|
||||
| 企微审批 | 通过(同意) | **需降级跳转** | 官方文档 | 企微无"代审批人执行同意"的服务端接口 |
|
||||
| 企微审批 | 拒绝 | **需降级跳转** | 官方文档 | 同上 |
|
||||
| 企微审批 | 转交 | **需降级跳转** | 官方文档 | 同上 |
|
||||
| ITSM 工单 | 接单 | **待外部确认** | 代码实证 + 待确认 | 操作类 OpenAPI 未实现、未文档化 |
|
||||
| ITSM 工单 | 开始处理 | **待外部确认** | 代码实证 + 待确认 | 同上 |
|
||||
| ITSM 工单 | 结单 | **待外部确认** | 代码实证 + 待确认 | 同上 |
|
||||
| ITSM 工单 | 转派 | **待外部确认** | 代码实证 + 待确认 | 同上 |
|
||||
|
||||
**判定值枚举说明**:
|
||||
|
||||
- `可服务端闭环`:服务端 API 可代替坐席真实生效,无需跳转原系统。
|
||||
- `需降级跳转`:服务端无代操作能力,必须跳转原系统(企微客户端 / ITSM Web)由坐席本人操作。
|
||||
- `待外部确认`:能力是否存在取决于外部平台方提供的接口/权限,项目内暂无实证。
|
||||
- `不可行`:经核查确认任何路径均无法达成。
|
||||
|
||||
**对 Phase 0 的整体影响(一句话)**:Phase 0 的"审批操作接真实接口"**无法满足**"操作后外部系统状态真实变更"的验收标准,必须改为"降级跳转 +(可选)webhook 回写";工单部分**阻塞于外部依赖**,须等 ITSM 文档/权限到位方可进入闭环开发,否则同样降级为跳转。
|
||||
|
||||
---
|
||||
|
||||
## 1. V-1 企微审批:服务端代审批能力核查
|
||||
|
||||
### 1.1 企微审批的两套接口体系(背景,官方文档)
|
||||
|
||||
企业微信的审批能力在代码与文档中存在**两套独立体系**,需分别核查:
|
||||
|
||||
1. **「审批应用」体系**(企业微信「审批」应用自带的审批流)
|
||||
- 回调事件:`sys_approval_change`
|
||||
- 服务端接口:`gettemplatedetail`、`applyevent`、`getapprovaldetail`、`getapprovaldata`、批量获取审批编号。
|
||||
2. **「审批流程引擎」体系**(自建应用内嵌审批,走 JS-SDK)
|
||||
- 回调事件:`open_approval_change`
|
||||
- 前端能力:`wx.invoke('thirdPartyOpenPage', {oaType:'10001'|'10002'})`
|
||||
- 需 `wx.agentConfig`(应用身份) + 企微客户端环境。
|
||||
|
||||
**核查结论**:无论哪套体系,**服务端均无"代替审批人执行同意/拒绝/转交"的接口**。(依据类型:官方文档)
|
||||
|
||||
### 1.2 官方服务端接口清单(代码实证 + 官方文档)
|
||||
|
||||
项目内 `src/backend/app/api/approval.py` 已实现/封装的企微审批服务端调用,经逐行核对**全部为"提交/查询/回调",无一为"代审批动作"**:
|
||||
|
||||
| 函数 | 行号 | 对应企微 API | 性质 |
|
||||
|------|------|-------------|------|
|
||||
| `get_approval_token` | `approval.py:392` | 获取 access_token | 鉴权 |
|
||||
| `get_template_detail` | `approval.py:401` | `oa/gettemplatedetail` | 查询模板 |
|
||||
| `submit_approval_api` | `approval.py:423` | `oa/applyevent` | **提交申请**(非审批) |
|
||||
| `get_approval_detail` | `approval.py:472` | `oa/getapprovaldetail` | 查询详情 |
|
||||
| `get_approval_data` | `approval.py:505` | `oa/getapprovaldata` | 查询列表 |
|
||||
| `/approval/jump` | `approval.py:840` | — | 生成跳转链接 |
|
||||
| `/approval/submit` | `approval.py:861` | `oa/applyevent` | 提交申请 |
|
||||
| `/approval/callback` | `approval.py:902` | `sys_approval_change` | 状态变化**回调**(当前仅 log,状态未回写业务) |
|
||||
|
||||
**官方文档佐证**:企微「审批应用」服务端 API 文档(https://developer.work.weixin.qq.com/document/path/91854)列出的全部接口即上述 5 类(模板详情、提交申请、状态变化回调、批量获取编号、获取详情),**不含任何"审批/驳回/转交"动作接口**。(依据类型:官方文档)
|
||||
|
||||
### 1.3 为什么 PC Web 端也无法用 JS-SDK 兜底
|
||||
|
||||
PRD U-1 提到现有交互依赖 `wx.invoke('thirdPartyOpenPage', {oaType:'10001'})` 原生表单。核对该能力约束:
|
||||
|
||||
- `wx.config` / `wx.agentConfig` 与 `wx.invoke` **仅在企业微信客户端内嵌的 H5 中生效**,普通 PC 浏览器调用无效(官方 JS-SDK 文档:https://developer.work.weixin.qq.com/document/path/94345;社区多源佐证)。
|
||||
- `wxwork://launch?launch_code=xxx` URL Scheme **仅支持 Windows / Mac 唤起客户端打开"个人聊天窗口"**,不支持跳转审批详情页(官方 Scheme 文档:https://developer.work.weixin.qq.com/document/path/94345)。
|
||||
- 坐席工作台是独立的 **PC Web 应用**(非企微内嵌 H5),因此上述原生表单能力**不可用**。(依据类型:官方文档 + 推断,推断部分为"坐席工作台非企微内嵌"——该事实以 PRD 上下文与项目前端独立部署形态为据)
|
||||
|
||||
### 1.4 降级跳转方案(推荐)
|
||||
|
||||
既然服务端无代审批接口,审批操作闭环采用**"跳转 + 状态回写"降级**:
|
||||
|
||||
1. 前端审批详情页提供"在企微审批中打开"链接,跳转至企微审批管理后台/客户端由审批人本人在原系统操作。
|
||||
- 代码实证:`src/frontend-agent/src/components/chat/task/ApprovalDetail.vue:78-86` 已使用该跳转模式(`https://app.work.weixin.qq.com/wework_admin/approval_v3#/?sp_id=...`)。
|
||||
2. 操作后状态由**原系统回调**回写服务台:
|
||||
- 代码实证:`src/backend/app/api/approval_webhook.py` 已具备接收企微审批状态变化并向前端 WebSocket 推送的能力(`sys_approval_change` → 状态变化 → WS 推送)。
|
||||
- **缺口(待确认/待补全)**:`approval.py:902` 的 `/approval/callback` 当前仅 `logger`,未将 `status_change_event`(同意=2/驳回=3/转审=4)回写业务状态;需在 Phase 0 补一段"回调 → 更新本地待办状态 → WS 通知"。
|
||||
|
||||
> ⚠️ 严格说,降级跳转方案**不满足** PRD Phase 0 验收标准"操作后外部系统状态真实变更且前端反馈一致"中的"前端直接闭环"——因为动作发生在原系统。但它是 U-1 不可行前提下的**唯一可行路径**,且状态可通过 webhook 回写实现"最终一致"。(依据类型:推断)
|
||||
|
||||
---
|
||||
|
||||
## 2. V-2 ITSM 工单:操作类 OpenAPI 核查
|
||||
|
||||
### 2.1 项目内 ITSM 调用现状(代码实证)
|
||||
|
||||
`src/backend/app/services/itsm_service.py` 全文件 261 行,**仅实现只读查询,无任何写操作**:
|
||||
|
||||
| 方法 | 行号 | 性质 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `get_todo_list` | `itsm_service.py:113` | 读(占位) | 返回空列表 + 日志告警,API 待实现 |
|
||||
| `get_todo_detail` | `itsm_service.py:132` | 读 | 调 `workitem/detail` |
|
||||
| `_do_post` | `itsm_service.py:167` | 通用 POST | 带 `ITSMSigner` 签名发送,可复用于写 |
|
||||
| `_get_workitem_detail` | `itsm_service.py:196` | 读 | `POST /openapi/v1/process/workitem/detail` |
|
||||
| `_map_to_todo_item` | `itsm_service.py:217` | 映射 | 详情 → 统一 `TodoItemData` |
|
||||
|
||||
**关键事实**:已知的唯一 ITSM 端点 `POST /openapi/v1/process/workitem/detail` 是**只读详情接口**(代码实证 `itsm_service.py:27,196-215`)。接单 / 开始处理 / 结单 / 转派等**写操作端点路径、请求体 schema、成功/错误码在项目内完全不存在**。(依据类型:代码实证)
|
||||
|
||||
### 2.2 签名机制可复用(利好)
|
||||
|
||||
`src/backend/app/utils/itsm_signer.py` 的 `ITSMSigner.compute_signature(app_id, timestamp, app_secret, biz_data)` 是**纯静态工具**:SHA1(`appSecret`+`appId`+`timestamp`+`bizData` 升序拼接 → `quote_plus` → SHA1 → 大写 hex)。该签名**不区分读写**,一旦获得写操作端点与请求体,可直接复用 `ITSMService._do_post`(`itsm_service.py:167`)发起写请求,无需新增鉴权逻辑。(依据类型:代码实证 + 推断,推断部分为"写操作可走同一签名/同一 `_do_post`"——基于签名与端点解耦的现状合理推断,但需 ITSM 平台方确认写接口是否复用同一套签名)
|
||||
|
||||
### 2.3 操作类 API 缺失的外部依赖(待确认清单)
|
||||
|
||||
工单动作是否可服务端闭环,**取决于 ITSM 平台方提供的接口与权限**,项目内无实证。需向平台方索取:
|
||||
|
||||
| 待确认项 | 说明 | 当前项目状态 |
|
||||
|---------|------|-------------|
|
||||
| ITSM 操作类 OpenAPI 文档 | 接单/开始处理/结单/转派 的端点、方法、请求体、响应码 | PRD-审批-001 Q1「ITSM API 完整接口规范」⏳待抓包 |
|
||||
| `ITSM_APP_ID` / `ITSM_APP_SECRET` | 写操作所需的应用凭证 | PRD-审批-001 Q2.1 ⏳待申请;`docker-compose.yml` 未配置 |
|
||||
| 操作类权限开通 | 当前 app_id 是否具备写权限 | 未知,需平台方确认 |
|
||||
| 测试账号 / 测试工单 | 用于闭环联调 | 未提供 |
|
||||
| 写操作成功/冲突语义 | 例如重复接单是否幂等、并发转派冲突码 | 未知 |
|
||||
|
||||
> 注:PRD-审批-001 已明确 Q5「代办状态更新交互」✅已确认:仅展示 + 跳转,不在服务台内直接操作。这与本验证"工单动作待外部确认"不冲突——Q5 是**产品决策**(先不内嵌操作),本验证是**技术可行性**(若要做内嵌,接口是否存在)。
|
||||
|
||||
### 2.4 结论
|
||||
|
||||
工单 4 动作**全部"待外部确认"**。在当前无任何操作类接口实证的前提下,**不能承诺服务端闭环**;若 ITSM 平台方提供写接口且权限到位,则因签名可复用,开发成本较低(主要工作量在补全 `ITSMService` 写方法 + 前端动作按钮接真实接口)。(依据类型:代码实证 + 待确认)
|
||||
|
||||
---
|
||||
|
||||
## 3. V-3 权限与身份模型
|
||||
|
||||
### 3.1 现状:操作以"应用身份"发起
|
||||
|
||||
| 系统 | 当前调用身份 | 代码实证 |
|
||||
|------|------------|---------|
|
||||
| 企微审批(读/提交) | 应用 access_token(IT 支持应用 Secret) | `approval.py:392` `get_approval_token` |
|
||||
| ITSM(读) | app_id + SHA1 签名(应用级) | `itsm_service.py:106-107,180` |
|
||||
|
||||
服务端调用均使用**应用身份**,不携带坐席个人身份令牌。(依据类型:代码实证)
|
||||
|
||||
### 3.2 工单:坐席个人身份如何传递(待确认)
|
||||
|
||||
`ITSMService._get_workitem_detail` 在请求体中传入 `executor`(坐席 userid):
|
||||
|
||||
```python
|
||||
# itsm_service.py:196-215
|
||||
body = {
|
||||
"process_instance_id": process_instance_id,
|
||||
"executor": executor, # = self.agent_userid(itsm_service.py:103)
|
||||
}
|
||||
```
|
||||
|
||||
即服务台**主动声明**执行人为当前坐席 userid。(依据类型:代码实证)
|
||||
|
||||
但 ITSM 是否据此将" executor"认作**真实操作人并写入审计日志**,取决于 ITSM 侧实现——当前仅详情查询用到该字段,写操作未实现,**无法验证**。(依据类型:待确认)
|
||||
|
||||
### 3.3 审批:个人身份不可绕过(不可行)
|
||||
|
||||
企微审批的"同意/拒绝/转交"依法规与产品逻辑必须由**审批人本人在客户端**操作,服务端无代审批接口(见 §1.2)。因此 PC Web 代审批在**身份与合规层面不可行**,只能由审批人本人跳转原系统操作。(依据类型:官方文档 + 推断)
|
||||
|
||||
### 3.4 审计追溯结论
|
||||
|
||||
- **降级跳转方案下**:动作发生在原系统(企微/ITSM),审计由对方负责。服务台仅能记录"跳转动作"事件,**无法闭环确认结果**,需依赖 webhook / 回调回写状态(见 §1.4、§4.2)。
|
||||
- **若合规要求"个人身份可追溯"**:ITSM 侧需确认是否支持 impersonation 或坐席级令牌;企微审批侧 PC Web 代审批不可行,此路不通。(依据类型:推断 + 待确认)
|
||||
|
||||
---
|
||||
|
||||
## 4. V-4 结论与方案
|
||||
|
||||
### 4.1 可行性判定汇总(同 §0 矩阵,附依据)
|
||||
|
||||
- 审批 3 动作:`需降级跳转`(官方文档实证:无服务端代审批接口 + PC Web 无 JS-SDK 能力)。
|
||||
- 工单 4 动作:`待外部确认`(代码实证:仅只读;待平台方提供写接口/权限/账号)。
|
||||
|
||||
### 4.2 分级降级方案
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[坐席在 PC Web 服务台点击操作] --> B{动作类型}
|
||||
B -->|审批:通过/拒绝/转交| C[降级:跳转企微审批原系统]
|
||||
B -->|工单:接单/处理/结单/转派| D{ITSM 写接口是否到位?}
|
||||
D -->|否| E[降级:跳转 ITSM Web 处理]
|
||||
D -->|是| F[服务端调用 ITSM OpenAPI 真实闭环]
|
||||
C --> G[企微 sys_approval_change 回调]
|
||||
G --> H[/approval/callback 回写状态 + WS 推送/]
|
||||
E --> I[ITSM 状态变化]
|
||||
I --> J[待确认:ITSM 是否提供状态回调/Webhook]
|
||||
F --> K[前端按接口返回分派 成功/失败/冲突 三态]
|
||||
```
|
||||
|
||||
**降级层级**:
|
||||
|
||||
1. **Level 0(立即可执行,不阻塞)**:审批全量降级跳转;工单在 ITSM 写接口未到位前同样降级跳转。前端按钮接真实"跳转链接"而非 mock toast。
|
||||
2. **Level 1(需补开发)**:审批跳转后通过 `approval_webhook.py` 已具备的回调 → WS 推送实现**状态最终一致**(需补全 `approval.py:902` 回写逻辑)。
|
||||
3. **Level 2(依赖外部)**:ITSM 写接口到位后,升级为服务端真实闭环,移除跳转降级。
|
||||
|
||||
### 4.3 对 PRD Phase 0 的影响与修订建议
|
||||
|
||||
PRD §6 Phase 0 原表:
|
||||
|
||||
| 原任务 | 原说明 | 修订后(基于本验证) |
|
||||
|--------|--------|---------------------|
|
||||
| 工单操作接真实接口 | 接单/开始处理/结单/转派 → ITSM API | **降级为跳转 ITSM**,并标注"服务端闭环待 ITSM 接口到位后升级"(受外部依赖阻塞) |
|
||||
| 审批操作接真实接口 | 通过/拒绝/转交 → 企微审批(受 U-1 约束,方案未定) | **明确为"降级跳转 + webhook 回写"**,U-1 判定为"不可服务端闭环" |
|
||||
| 失败态处理 | 移除无条件 `ElMessage.success` | 维持;跳转方案下改为"跳转成功提示 + 状态回写后刷新",仍禁止以 toast 作为验收依据 |
|
||||
|
||||
**对 Phase 0 阻塞关系的影响**:
|
||||
|
||||
- 审批部分:**不阻塞** Phase 0 启动——降级跳转方案可立即落地,原系统回调回写可并行开发。
|
||||
- 工单部分:**受外部依赖阻塞**——若坚持"服务端真实闭环"则必须等 ITSM 文档/权限;若接受降级跳转则与审批同步落地。
|
||||
- 建议 PRD 将 U-1 结论由"待验证"改为"**已验证:审批不可服务端闭环,须降级跳转**",并新增 U-1.1「ITSM 写接口到位时间」作为工单闭环的外部阻塞项。
|
||||
|
||||
### 4.4 有序任务分解(供 Engineer 实施)
|
||||
|
||||
> 以下任务**仅含配置/前端/回调补完**,**不含任何工单写操作实现**(因接口待确认)。工单闭环任务在外部依赖到位后单独追加。
|
||||
|
||||
| 任务 ID | 任务名 | 涉及文件(相对路径) | 依赖 | 优先级 |
|
||||
|---------|--------|---------------------|------|--------|
|
||||
| T01 | 审批/工单详情页动作改为真实跳转链接(移除 mock) | `src/frontend-agent/src/components/chat/task/ApprovalDetail.vue`、`TicketDetail.vue`、`src/frontend-agent/src/components/chat/TaskDetailView.vue`(`handleAction` 不再仅 toast) | — | P0 |
|
||||
| T02 | 补全企微审批回调状态回写(sys_approval_change → 本地状态 → WS) | `src/backend/app/api/approval.py`(`/approval/callback` 现状 `approval.py:902`)、`src/backend/app/api/approval_webhook.py` | T01 | P0 |
|
||||
| T03 | ITSM 工单详情页跳转链接接入(wecom ITSM Web 深链) | `src/frontend-agent/src/components/chat/task/TicketDetail.vue`、`src/backend/app/services/itsm_service.py`(补充 `itsm_web_url` 构造) | T01 | P1 |
|
||||
| T04 | 失败/冲突三态反馈(移除无条件 success) | `TaskDetailView.vue`、各 Detail 子组件、`src/backend/app/api/todo_items.py`(`PUT /{id}/status` 现状 `todo_items.py:158` display_only 保持不变,仅前端反馈改造) | T01 | P1 |
|
||||
| T05 | 外部依赖跟进:向 ITSM 平台方索取写接口文档 + 申请 app_id/secret + 测试账号(阻塞工单闭环) | `docs/01-产品文档/06-审批与待办/PRD-REQ-审批-001-ITSM工单跳转-v1.0.md`(更新 Q1/Q2.1)、`docker-compose.yml`(补 ITSM_APP_ID/SECRET) | — | P0(外部) |
|
||||
|
||||
**依赖顺序图**:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
T01[T01 跳转链接改造] --> T02[T02 审批回调回写]
|
||||
T01 --> T03[T03 ITSM 跳转接入]
|
||||
T01 --> T04[T04 三态反馈]
|
||||
T05[T05 ITSM 外部依赖] -.阻塞.-> T03
|
||||
```
|
||||
|
||||
> 注:T05 为**外部协调任务**(非代码),其完成是工单服务端闭环(未来追加的 T06+)的前置,但不阻塞审批降级与跳转类任务。
|
||||
|
||||
---
|
||||
|
||||
## 5. 证据清单与方法说明
|
||||
|
||||
| 依据类型 | 含义 | 本文使用处 |
|
||||
|---------|------|-----------|
|
||||
| 官方文档 | 企业微信/ITSM 官方接口文档,附链接 | §1.2、§1.3、§1.4 |
|
||||
| 代码实证 | 项目 `src/` 内源码,附 `文件:行号` | §1.2、§2.1、§3.1、§3.2 |
|
||||
| 推断 | 基于上述事实的合理推论,**非证实事实** | §1.4、§2.2、§3.4、§4.2 |
|
||||
| 待确认 | 需外部平台方/产品提供信息方可定论 | §2.3、§3.2、§3.4、T05 |
|
||||
|
||||
**核查边界声明**:
|
||||
|
||||
- 未运行任何代码、未修改任何业务源码,仅静态阅读与官方文档交叉验证。
|
||||
- 企微官方文档链接为验证时引用的权威来源;若文档版本更新导致接口增减,需重新核对。
|
||||
- ITSM 操作类接口结论为"待外部确认",不表示"不可行";结论仅在"项目内当前无任何写接口实证"前提下成立。
|
||||
|
||||
---
|
||||
|
||||
## 6. 附:关键调用链路时序图(降级闭环)
|
||||
|
||||
### 6.1 审批降级跳转 + 状态回写(目标态)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant A as 坐席(PC Web)
|
||||
participant F as 前端服务台
|
||||
participant B as 后端
|
||||
participant W as 企微审批(原系统)
|
||||
participant H as approval_webhook/WS
|
||||
|
||||
A->>F: 点击"在企微审批中打开"
|
||||
F->>W: 跳转 approval_v3#/?sp_id=... (新标签页)
|
||||
Note over A,W: 审批人在企微客户端/管理后台执行 同意/拒绝/转交
|
||||
W-->>B: sys_approval_change 回调 (status_change_event: 2/3/4)
|
||||
B->>B: /approval/callback 回写本地待办状态 (待补全 approval.py:902)
|
||||
B->>H: WebSocket 推送状态变更
|
||||
H->>F: 待办状态刷新
|
||||
F-->>A: 前端状态与企微一致
|
||||
```
|
||||
|
||||
### 6.2 工单跳转(ITSM 接口未到位时)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant A as 坐席(PC Web)
|
||||
participant F as 前端服务台
|
||||
participant I as ITSM Web(原系统)
|
||||
|
||||
A->>F: 点击"在 ITSM 中打开"
|
||||
F->>I: 跳转 ITSM workitem 详情深链 (新标签页)
|
||||
Note over A,I: 坐席在 ITSM 内执行 接单/处理/结单/转派
|
||||
Note over I: 状态变化由 ITSM 负责 (回写机制待确认)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*文档结束。本验证所有"推断"与"待确认"项均已明确标注,未将推断作为既成事实陈述。*
|
||||
Reference in New Issue
Block a user