实现 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
19 KiB
技术验证 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 企微审批的两套接口体系(背景,官方文档)
企业微信的审批能力在代码与文档中存在两套独立体系,需分别核查:
- 「审批应用」体系(企业微信「审批」应用自带的审批流)
- 回调事件:
sys_approval_change - 服务端接口:
gettemplatedetail、applyevent、getapprovaldetail、getapprovaldata、批量获取审批编号。
- 回调事件:
- 「审批流程引擎」体系(自建应用内嵌审批,走 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=xxxURL Scheme 仅支持 Windows / Mac 唤起客户端打开"个人聊天窗口",不支持跳转审批详情页(官方 Scheme 文档:https://developer.work.weixin.qq.com/document/path/94345)。- 坐席工作台是独立的 PC Web 应用(非企微内嵌 H5),因此上述原生表单能力不可用。(依据类型:官方文档 + 推断,推断部分为"坐席工作台非企微内嵌"——该事实以 PRD 上下文与项目前端独立部署形态为据)
1.4 降级跳转方案(推荐)
既然服务端无代审批接口,审批操作闭环采用**"跳转 + 状态回写"降级**:
- 前端审批详情页提供"在企微审批中打开"链接,跳转至企微审批管理后台/客户端由审批人本人在原系统操作。
- 代码实证:
src/frontend-agent/src/components/chat/task/ApprovalDetail.vue:78-86已使用该跳转模式(https://app.work.weixin.qq.com/wework_admin/approval_v3#/?sp_id=...)。
- 代码实证:
- 操作后状态由原系统回调回写服务台:
- 代码实证:
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):
# 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 分级降级方案
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[前端按接口返回分派 成功/失败/冲突 三态]
降级层级:
- Level 0(立即可执行,不阻塞):审批全量降级跳转;工单在 ITSM 写接口未到位前同样降级跳转。前端按钮接真实"跳转链接"而非 mock toast。
- Level 1(需补开发):审批跳转后通过
approval_webhook.py已具备的回调 → WS 推送实现状态最终一致(需补全approval.py:902回写逻辑)。 - 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(外部) |
依赖顺序图:
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 审批降级跳转 + 状态回写(目标态)
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 接口未到位时)
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 负责 (回写机制待确认)
文档结束。本验证所有"推断"与"待确认"项均已明确标注,未将推断作为既成事实陈述。