文件
hyb-oa/docs/需求-统一审批中心规约.md
T

608 行
64 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# 需求 - 统一审批中心规约
**版本**:1.3
**日期**:2026-05-13
**状态**:模块级开发规约
**上级事实源**:[海音达OA-MVP需求规格说明书](./海音达OA-MVP需求规格说明书.md)
本规约细化 `hyb-oa` 审批中心和企业微信原生审批镜像的产品边界、角色权限、状态模型、异常规则、留痕要求和验收场景。WFOC 起,审批按主办方分层;DBWFOC 后边界进一步收窄:报销、财务、采购、合同和项目类审批继续由 OA 主办;请假、补卡、外出、加班等 HR/考勤员工日常审批优先由企业微信主办,OA 同步按域镜像并保留自建历史留存、人工接管和恢复回写能力。WMSG1 已验证企业微信应用消息和 Deeplink 回流,WAPC1 后审批镜像前置工作进入配置准备清单阶段,WAPC2 已提供请假/补卡只读 POC runner,WAPC2FLIVE 已自动生成请假/补卡 OA-POC 样件并复跑通过。WCBK1 已完成只读回调 URL 验证和真实审批事件确认,结论为 `PASS_CALLBACK_EVENT_CONFIRMED`。WEGOV1 后明确先利用企微成熟审批和考勤能力,同时由 OA 保存关键历史和外部关联键。WACP3R 已自动发现 OA-POC 请假/补卡模板并公网复跑到 `PASS_WITH_FINDINGS`:补偿拉取和详情读取可用,finding 是回调证据文件未持久化。WREAD1F 后已完成请假/补卡真机可读性确认:消息卡片、列表、详情页均可看清,远端报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T133628Z/summary.json` 为 `PASS_READABLE`;但同意按钮单击即通过,需在归档审计、提示或操作规程中处理。WADM1 已完成自建留存/镜像数据模型设计;WARCH1 已实现请假/补卡最小历史留存;WARCH2 已新增正式只读历史查询 API 和 PC/移动历史页。WAPPR1/WRULE1 后固定“单一组织规则,两个执行域”:OA 维护员工主管链、业务审批角色和管理员兜底事实,企业微信执行 HR/考勤审批,OA 只做归档和理论审批人校验;完整镜像、应急接管、恢复回写和 RuleOps 仍不能直接放行。后续审批相关开发必须先从本规约拆分 Trellis task,再进入技术方案和代码实现。
---
## 1. 模块目标与边界
### 1.1 目标
- `hyb-oa` 是财务审批、报销、采购、合同、项目和审计镜像中心。
- HR/考勤员工日常审批优先复用企业微信原生审批;OA 同步审批单、审批历史、附件、状态回调和审计镜像。
- OA 保存企微主办审批的完整外部关联键和必要业务摘要,用于历史查看、补偿拉取、审计和后续恢复。
- PC、移动端和企业微信入口应能区分 OA 主办审批、企微主办审批镜像和 OA 应急接管单据。
- 企业微信消息继续作为 OA 主办审批的提醒和 deeplink;企微主办审批在企业微信内完成,OA 负责镜像和恢复对账。
- 企微不可用时,管理员可人工开启 `oa_hr_emergency_mode_v1`,临时由 OA 接管 HR/考勤应急审批;企微恢复后通过 `wecom_recovery_writeback_v1` 回写和对账。
### 1.2 适用对象
首批及后续默认纳入 OA 主办审批中心的正式审批包括:
- 报销审批。
- 采购审批。
- 费用审批。
- 合同审批、合同变更审批和合同归档相关审批。
- 资产领用、归还、调拨、盘点中需要正式留痕的动作。
- 后续新增财务、采购、合同、项目和高风险业务审批,除非另有明确产品决策,否则默认接入 OA 主办审批中心。
企业微信主办审批第一批包括:
- 请假。
- 补卡。
- 外出。
- 加班。
- 与上述事项关联的假期余额和考勤解释。
OA 中既有请假、补卡、外出、加班能力转为历史只读、镜像展示和应急接管能力;正常情况下不再作为员工日常提交入口。
### 1.3 暂不做范围
- 不接入飞书登录、飞书审批、飞书通知或飞书项目管理。
- 首批不做复杂 BPM 设计器、自由拖拽流程、并行会签、条件分支可视化建模。
- 首批不做跨公司多租户审批市场化配置。
- 首批不做审批动作离线处理;移动端必须在线提交动作。
- 不把企业微信 HR/考勤审批结果直接用于报销付款、财务复核或 AP 台账;财务动作必须在 OA 内完成。
- 不把采购单、合同正文、报价、供应商资料、发票、银行回单或代账资料上传到企业微信原生审批或企业微信附件。
- 不让 AI 直接改正式审批模板;`wecom_ruleops_v1` 只能先草拟和 dry-run,人工确认后才允许写入企微。
---
## 2. 角色与权限
| 角色 | 典型用户 | 可做动作 | 权限边界 |
| --- | --- | --- | --- |
| 申请人 | 普通员工、主管、财务、HR | 创建草稿、提交、查看自己的单据、撤回允许撤回的单据、按退回意见修改后重提 | 只能编辑草稿、已退回、已驳回且规则允许重提的本人单据;当前不允许审批自己的待办;未来如需自审例外,必须另拆规则并强审计 |
| 审批人 | 主管、项目负责人、老板、被转审人 | 查看分配给自己的待办,通过、驳回、退回、转审、查看审批历史 | 只能处理当前分配给自己的任务;不能越权处理他人待办 |
| 财务 | 财务人员、财务主管 | 处理报销复核、付款标记、财务导出、重复发票例外审批中与财务相关的动作 | 不默认拥有系统管理权限;财务例外动作必须留痕 |
| HR/行政 | HR、行政负责人 | 在企业微信处理请假、补卡、外出、加班等 HR/考勤员工日常审批;在 OA 查看镜像、处理应急接管和恢复对账 | 不能绕过审批流直接改业务结果,必要修正必须审计 |
| 老板/总经理 | 公司负责人 | 处理大额、高风险或最终节点审批,查看公司级待办和摘要 | 操作仍需写入审批历史和审计日志 |
| 系统管理员 | 运维或后台系统负责人 | 配置角色、权限、企业微信入口、通知、基础字典、功能开关、应急模式和 RuleOps | `admin` 只代表系统管理权限,不自动获得业务单据审批权;应急模式、回写和规则写入必须有审计 |
权限判定以服务端为准,前端按钮显隐只是体验优化,不作为授权依据。
### 2.1 权限与数据范围矩阵
| 角色 | 可查看范围 | 可审批范围 | 附件下载 | 导出 | 例外处理 |
| --- | --- | --- | --- | --- | --- |
| 申请人 | 本人发起、本人作为实际报销人、抄送给本人的单据 | 当前不可审批自己的待办;未来如需自审例外,必须另拆规则并强审计 | 草稿和本人单据附件;审批中及归档附件按业务权限展示 | 不允许批量导出 | 不允许 |
| 审批人 | 分配给自己的待办、已办和自己审批链上的单据 | 当前分配给自己的任务 | 可查看审批所需附件,下载动作需留痕 | 不允许财务级导出 | 可转审,不可财务例外 |
| 财务 | 报销、费用、付款、发票相关单据和财务导出范围 | 财务复核节点、重复发票例外节点、付款相关节点 | 可查看和下载财务复核所需附件,敏感附件下载留痕 | 可按权限导出财务台账 | 可处理重复发票、付款修正等财务例外 |
| HR/行政 | 人员、部门、请假、补卡、外出、加班等行政范围 | HR/行政复核节点 | 可查看行政审批所需附件 | 可导出行政审批统计,不能导出财务明细 | 可处理审批人停用、规则缺失等管理例外 |
| 老板/总经理 | 公司级摘要、大额或高风险审批、关键业务单据 | 最终审批节点或指定高风险节点 | 可查看决策所需附件,下载留痕 | 可查看公司级摘要,批量导出按配置控制 | 可审批高风险例外,但仍需记录原因 |
| 系统管理员 | 系统配置、日志、权限、集成状态 | 默认不具备业务审批权 | 技术排障需最小化授权并留痕 | 默认不导出业务数据 | 可做账号、规则、通知配置修复;业务数据修正需另走审计流程 |
数据范围以服务端权限和审计日志为准。任何导出、附件下载、例外处理都必须记录操作者、时间、对象和原因。
### 2.2 角色视图与动作裁剪
统一审批中心使用同一套 OA 审批数据、任务和历史记录,但 PC、移动和企业微信入口应按角色裁剪入口、摘要、动作和敏感信息。角色视图只影响展示和操作便利性,不改变服务端权限模型。
| 角色视图 | 重点入口与摘要 | 允许突出动作 | 不应展示或必须隐藏 |
| --- | --- | --- | --- |
| 员工视图 | 新建、草稿、审批中、退回修改、通知、我的申请 | 保存草稿、提交审批、撤回、按退回意见重提、查看本人状态 | 管理配置、全量人员、财务付款、他人待办审批动作 |
| 审批人视图 | 待处理、已处理、审批详情、附件/发票摘要、审批历史 | 通过、驳回、退回、转审、查看审批记录 | 无关员工列表、财务后处理、未分配给自己的审批按钮 |
| 财务视图 | PC 端财务复核、付款状态、导出、重复票据例外;移动端只保留必要复核和查看 | 财务复核、付款标记、财务导出、重复发票例外处理 | 系统管理能力、非财务业务审批权、无审计的敏感附件下载 |
| 管理员视图 | 系统配置、账号、角色、组织、通知渠道、审计日志 | 配置修复、账号启停、角色分配、通知渠道测试、审计查询 | 业务审批按钮;除非同时具备正式业务角色且不是自审,不得处理业务待办 |
| 负责人视图 / CEO 视图 | 高额、高风险、例外待办,公司级摘要,审批效率和风险概览 | 处理分配给自己的最终审批或例外审批,查看高风险摘要 | 本人单据自审、仅凭 `admin` 权限处理业务、无配置依据的全量业务操作 |
移动端也遵循上述角色视图,但不为每个角色做完全不同的页面体系:同一移动 Shell、列表卡片、详情分区、顶部返回和吸底主操作框架下,按权限显示不同内容和动作。前端隐藏无权动作只是体验优化;服务端仍必须拒绝越权、自审和状态非法操作。
### 2.3 正式审批组织与角色映射
正式运行和 R5 真机验收按以下组织口径执行;实现不得把个人姓名硬编码到业务逻辑中,应通过员工、直属上级和角色配置落地。
| 人员 | 正式角色口径 | 配置要求 | 审批边界 |
| --- | --- | --- | --- |
| `majianyong` | 公司负责人 / `ceo` | 不配置直属主管;当前唯一 active `manager` 的直属主管配置为 `majianyong` | 处理 CEO 节点事项;本人申请不得自审,由财务兜底 |
| `wangshunli` | 系统开发 / 系统管理员 | 不配置 `manager`、`ceo`、`finance` 或 `finance_mgr` 业务角色,不进入正常业务汇报链 | 只处理系统配置、账号、部署和运维,不处理正常业务审批 |
| 普通员工 / 专用测试员工 | 普通员工 / `employee` | 配置 active 且具备对应业务审批权的直属主管 | 用于日常业务或正式链路验收;专用测试账号不替代真实员工结论 |
正式员工必须配置直属主管。缺直属主管、主管停用、主管链断裂或审批节点解析到申请人本人时,应阻断提交或阻断审批动作,并提示管理员修正组织或审批规则。不得用 `admin` 权限解释业务审批权,也不得用无条件升级 CEO 掩盖主数据错误。
### 2.4 OAFLOW1 负责人本人报销兜底审批
OAFLOW1 后,OA 主办的报销审批规则增加 `ownerSelfFallbackRole`。当公司负责人本人(当前为 `majianyong`,`ceo`)提交报销且直属主管节点无法解析时,系统使用审批规则上的 active 非本人财务兜底替换第一层 `manager` 节点。`07-28-ceo-finance-fallback` 已部署并原子回填生产 3 条规则,当前目标审批人为现有 `finance`;代码兼容 `finance|finance_mgr`,新建默认 `finance`。规则只作用于新提交报销单,提交时保存 `workflow_steps_snapshot`,后续审批推进只读快照,不因之后修改审批规则而漂移。
执行边界:
- 不给 `majianyong` 配置假直属主管,不用组织主数据造假绕过流程;`wangshunli` 仅为系统管理员,不进入业务审批链。
- 不允许自审;若兜底角色也是申请人本人持有的角色,或没有 active 非本人持有人,提交必须阻断。
- 不支持 `admin` 作为负责人本人报销兜底角色;`admin` 仍只代表系统管理权限,不代表业务审批权。
- 当前首版只覆盖 OA 报销金额规则,不扩展到采购、合同、复杂 BPM 或自由流程。
- 当前允许 `ownerSelfFallbackRole=finance|finance_mgr` 或置空,并以 `finance` 为新建默认值;生产三档规则均已使用 `finance`,无 active 非本人持有人时继续阻断。
预期链路示例:
| 金额规则 | 原链路 | 负责人本人申请快照 |
| --- | --- | --- |
| 小额 | `manager -> finance` | `finance` |
| 中额 | `manager -> finance` | `finance` |
| 大额 | `manager -> ceo -> finance` | `finance`;移除本人 `ceo` 并去重 |
---
## 3. 入口与任务视图
### 3.1 入口
| 入口 | 路径或位置 | 用途 | 要求 |
| --- | --- | --- | --- |
| PC 审批待办 | `/workflow/tasks` | 审批人集中处理待办、已办、抄送或历史任务 | 应在 PC 主导航清晰可达 |
| 移动审批 | `/mobile/approvals` | 手机端处理待办和查看审批历史 | 应适配企业微信 WebView 和普通手机浏览器 |
| 业务详情页 | 报销、请假、采购、合同等详情页 | 从具体单据查看审批流、进度和进入当前审批任务 | 状态、当前节点、历史记录必须与待办一致;不复制审批动作区 |
| 企业微信消息 | deeplink 到 OA 页面或企微原生审批 | OA 主办审批回 OA,HR/考勤企微主办审批在企业微信处理 | 必须区分主办方,不把企微 HR 审批当作财务审批;OA 主办域附件不进入企微 |
| Portal 待办摘要 | `/portal` 或首页卡片 | 提醒当前待处理数量 | 点击后进入 `/workflow/tasks` 或对应详情页 |
审批动作承接位置统一为 `待我处理`、审批详情、移动审批详情或明确的当前任务处理面板。业务台账列表和普通业务详情只显示 `查看处理`、`查看进度`、`补材料` 或进入审批任务的入口,不直接放 `通过`、`驳回`、`退回`、`转审`。审批动作执行前必须展示同一套业务摘要、证据完整性、风险提示、后果说明和必要理由输入;批量审批默认不开放,后续启用必须另行定义后端资格标记、失败明细和审计语义。
### 3.2 任务列表要求
- 待办列表至少展示:业务类型、业务编号、标题或摘要、申请人、金额或天数等关键值、当前节点、到达时间、状态。
- 移动端列表应优先展示业务类型、编号、申请人、状态和主要金额/天数,不展示桌面宽表。
- 已办或历史列表应能查看处理结果和处理时间,但首批不要求复杂筛选。
- 待办数量、单据详情状态、审批历史必须一致。
---
## 4. 审批对象与字段
统一审批中心对所有业务单据抽象以下通用字段,具体业务可扩展自己的字段。
| 字段 | 含义 | 要求 |
| --- | --- | --- |
| `business_type` | 业务类型,如报销、请假、采购、合同 | 必填,用于路由、权限和列表展示 |
| `business_id` | 业务单据主键 | 必填,与业务表关联 |
| `business_no` | 业务编号,如报销单号、采购单号 | 必填,OA 生成或校验唯一 |
| `title` | 审批摘要标题 | 必填,用于待办和通知 |
| `applicant_id` | 申请人 | 必填 |
| `department_id` | 申请人所属部门 | 可空但推荐保留 |
| `project_id` | 关联项目 | 业务需要时填写 |
| `amount` | 金额类摘要 | 报销、采购、费用、合同等需要 |
| `current_status` | 当前业务状态 | 必须与工作流状态同步 |
| `current_task_id` | 当前待办任务 | 审批中必须存在 |
| `submitted_at` | 提交时间 | 提交后必填 |
| `completed_at` | 审批完成时间 | 通过、驳回终止、撤回终止或完成时填写 |
---
## 4.5 审批规则配置矩阵
以下金额和天数是 10 人以下公司试运行建议值,不等于最终公司财务制度。正式上线前可由管理员按公司制度配置,后续实现不得把这些阈值硬编码在业务逻辑中。
| 业务类型 | 试运行阈值 | 默认审批链 | 财务/HR 复核 | 兜底审批人 | 撤回 | 转审 |
| --- | --- | --- | --- | --- | --- | --- |
| 报销/费用 | `<=500` 元 | 直属主管 | 无财务节点,财务可事后抽查 | 老板/总经理 | 审批完成前允许撤回 | 允许转给同级或上级审批人 |
| 报销/费用 | `500-3000` 元 | 直属主管 | 财务复核 | 老板/总经理 | 财务复核前允许撤回 | 允许转给具备审批或财务权限的人 |
| 报销/费用 | `>3000` 元 | 直属主管 -> 老板/总经理 | 财务复核 | 老板/总经理 | 老板审批前允许撤回 | 允许转审但不得绕过老板或财务节点 |
| 采购 | `<=1000` 元 | 直属主管 | 视费用类型抽查 | 老板/总经理 | 审批完成前允许撤回 | 允许转给项目负责人或上级 |
| 采购 | `1000-5000` 元 | 直属主管 -> 老板/总经理 | 视付款方式财务复核 | 老板/总经理 | 老板审批前允许撤回 | 允许转审但不得绕过老板节点 |
| 采购 | `>5000` 元 | 直属主管 -> 老板/总经理 | 财务复核,建议关联合同或附件 | 老板/总经理 | 老板审批前允许撤回 | 允许转审但必须保留原因 |
| 请假 | 由企微模板配置 | 企业微信原生审批 | 按企微模板和公司制度 | OA 应急时按管理员临时规则 | 按企微能力或 OA 应急规则 | 企微正常时在企微处理,OA 只同步镜像 |
| 补卡 | 由企微模板配置 | 企业微信原生审批 | 按企微模板和公司制度 | OA 应急时按管理员临时规则 | 按企微能力或 OA 应急规则 | 企微正常时在企微处理,OA 只同步镜像 |
| 外出 | 由企微模板配置 | 企业微信原生审批 | 按企微模板和公司制度 | OA 应急时按管理员临时规则 | 按企微能力或 OA 应急规则 | 企微正常时在企微处理,OA 只同步镜像 |
| 加班 | 由企微模板配置 | 企业微信原生审批 | 按企微模板和公司制度 | OA 应急时按管理员临时规则 | 按企微能力或 OA 应急规则 | 企微正常时在企微处理,OA 只同步镜像 |
| 合同 | 所有 | 负责人主管 -> 老板/总经理 | 财务按金额或付款条款复核 | 老板/总经理 | 老板审批前允许撤回 | 允许转审但不得绕过老板节点 |
规则匹配优先级为:业务类型 -> 金额或天数阈值 -> 项目/部门特殊规则 -> 默认规则。若找不到审批人、缺直属主管、主管链断裂、审批人停用或节点解析为申请人本人,提交必须阻断并提示管理员修正组织或审批规则,不得生成半截流程。
大额、高风险或例外事项默认进入老板/总经理节点;当前试运行最小集合包括:重复发票例外、预算或费用标准超限、缺必要附件仍需例外处理、涉密或重点项目、合同/采购高金额事项。具体阈值和例外条件必须通过审批规则配置,不得硬编码到个人或前端按钮。
---
## 5. 状态模型
### 5.1 通用状态
| 状态 | 含义 | 允许的主要动作 |
| --- | --- | --- |
| 草稿 | 申请人正在编辑,未进入审批 | 保存、提交、删除草稿 |
| 审批中 | 已提交,存在当前审批任务 | 审批人通过、驳回、退回、转审;申请人在规则允许时撤回 |
| 已通过 | 所有审批节点通过,进入业务后处理 | 财务付款、合同归档、考勤生效等业务动作 |
| 已驳回 | 审批人否决,流程终止或允许复制重提 | 查看历史、按业务规则重新发起 |
| 已退回 | 审批人要求申请人修改 | 申请人修改后重提、撤回 |
| 已撤回 | 申请人撤回,流程终止 | 查看历史、按业务规则复制重提 |
| 已完成 | 审批通过后的业务处理完成 | 查看、归档、导出 |
| 已付款 | 报销或费用类单据已付款 | 查看、导出;普通用户不可修改 |
`已付款` 是报销、费用等财务类业务的完成状态之一;非财务业务可使用 `已完成`。
### 5.2 状态转换规则
- 草稿只能由申请人或授权代填人提交。
- 提交成功后生成工作流实例、当前节点和第一条待办。
- 当前审批人通过后,系统按规则推进到下一节点;无下一节点时进入 `已通过`。
- 驳回默认终止当前流程,是否允许复制重提由业务模块决定。
- 退回回到申请人修改态,重提后应保留同一业务单号和完整历史。
- 撤回只允许在流程未完成且规则允许时发生。
- 转审只改变当前任务处理人,不改变业务单据申请人。
- 已完成、已付款、已归档类状态原则上不允许普通用户修改核心字段。
### 5.3 状态流转表
| 当前状态 | 动作 | 操作者 | 目标状态 | 是否保留单号 | 是否生成新版本 | 是否通知 |
| --- | --- | --- | --- | --- | --- | --- |
| 草稿 | 保存 | 申请人 | 草稿 | 是 | 否 | 否 |
| 草稿 | 提交 | 申请人 | 审批中 | 是 | 是,记录提交版本 | 是,通知首个审批人 |
| 审批中 | 通过但还有下一节点 | 当前审批人 | 审批中 | 是 | 否 | 是,通知下一审批人 |
| 审批中 | 最终通过 | 当前审批人 | 已通过 | 是 | 否 | 是,通知申请人和后处理角色 |
| 审批中 | 驳回 | 当前审批人 | 已驳回 | 是 | 否 | 是,通知申请人 |
| 审批中 | 退回 | 当前审批人 | 已退回 | 是 | 是,等待申请人修改 | 是,通知申请人 |
| 已退回 | 修改后重提 | 申请人 | 审批中 | 是 | 是,记录重提版本 | 是,通知首个审批人 |
| 审批中 | 撤回 | 申请人 | 已撤回 | 是 | 否 | 是,通知当前审批人 |
| 审批中 | 转审 | 当前审批人 | 审批中 | 是 | 否 | 是,通知被转审人 |
| 已通过 | 财务付款或业务归档 | 财务或业务负责人 | 已付款或已完成 | 是 | 否 | 是,通知申请人 |
| 已完成/已付款 | 管理修正 | 授权管理员或财务主管 | 保持已完成/已付款并追加修正记录 | 是 | 是,记录修正版本 | 按敏感级别通知 |
撤回、退回、驳回后的重新发起策略由业务模块决定;报销场景默认保留原单号用于退回重提,驳回后复制新单据时应生成新流程并关联原单据。
---
## 6. 审批动作
| 动作 | 发起人 | 前置条件 | 结果与留痕 |
| --- | --- | --- | --- |
| 保存草稿 | 申请人 | 有创建权限 | 写入业务草稿,不生成审批任务 |
| 提交 | 申请人 | 必填字段、附件、金额、发票等业务校验通过 | 生成工作流实例、待办、提交历史和通知 |
| 通过 | 当前审批人 | 任务未处理且权限匹配 | 当前任务完成,进入下一节点或业务通过 |
| 驳回 | 当前审批人 | 必填驳回原因 | 流程终止或进入业务定义的驳回态 |
| 退回 | 当前审批人 | 必填退回原因 | 任务回到申请人修改态,保留历史 |
| 撤回 | 申请人 | 流程未完成且业务允许撤回 | 流程终止或回到草稿,写入撤回原因 |
| 转审 | 当前审批人 | 目标人具备处理资格,未超过转审限制 | 当前任务转给目标人,记录转审链路和原因 |
| 批量审批 | 审批人 | 多个任务均可被当前人处理 | 每个任务独立校验、独立成功或失败 |
| 查看历史 | 相关人员 | 对单据有查看权限 | 展示提交、审批、退回、转审、通知和关键业务动作 |
批量审批不得绕过单条任务的权限、状态和业务校验。
审批动作按钮使用短文案 `通过 / 驳回 / 退回`,不得在按钮内堆叠长说明。PC 和移动端执行这些动作前必须二次确认,在确认弹窗中解释后果:通过会进入下一审批节点或完成审批并写入历史;驳回会终止当前流程或进入驳回态,申请人通常需要复制或重新发起;退回会回到申请人修改态,保留原单号和审批历史,申请人修改后可重新提交。驳回和退回都必须填写可理解意见。
R7E 后,全站高风险操作沿用同一确认模式:提交审批、撤回、删除、归档、启用/停用、批量处理、财务复核/付款、重复票据例外、敏感数据导出和通知测试/重发都必须使用 OA 内二次确认弹窗;普通保存草稿、筛选、分页、查看、预览和取消编辑不强制确认,避免确认疲劳。PC 端确认由 Playwright 自动化覆盖,企业微信真机只保留消息、免登录、WebView 文件选择器等真实环境差异项。
### 6.1 自审与转审约束
- 自审默认禁止,不因申请人同时拥有 `admin`、`manager`、`ceo`、`finance` 或其他角色而放开。
- 自审识别至少覆盖:工作流提交人、报销实际报销人、报销提交人、请假人、采购申请人、采购提交人、差旅申请人和差旅提交人。
- 批量审批必须逐条执行自审校验;自审任务失败不应影响其他合法任务处理。
- 转审目标必须具备对应业务审批权限,不能是申请人本人,不能是仅具备 `admin` 的系统管理员。
- 转审不能绕过规则定义的必经老板/总经理、财务或 HR/行政节点;需要例外时必须另走明确改派或规则调整流程并留痕。
- 若老板/总经理本人提交的单据触发老板/总经理节点,默认阻断或进入明确配置的非本人兜底审批人;本规约不开放自审例外。
### 6.2 WF8 转审扩展设计
WF8 阶段只做轻量转审扩展,不进入复杂 BPM、审批设计器、并行会签或自由流程编排。转审的定义是“当前待办任务处理人变更”,不是业务单据申请人变更,也不是审批规则永久改写。
成熟 BPM 和任务中心通常把流程定义、候选处理人和当前任务分配分开管理。WF8 沿用这一思路:规则仍决定必经节点,转审只在当前节点内调整 `assignee_id`,并通过历史和通知留痕。
适用范围:
- 首批覆盖报销、请假、采购三类已接入统一审批中心的待办。
- 合同、差旅、资产等后续业务沿用同一原则,但必须另走对应连续 Issue 验收。
- OA 主办审批通过企业微信通知和 deeplink 回 OA;HR/考勤企微主办审批允许在企业微信原生审批中完成转审或流转,OA 同步最终详情。
发起人规则:
- 只有当前 pending 待办的实际可处理人可以发起转审;有效委托场景下,实际处理人必须写入委托相关 payload。
- `admin` 单角色不能代替业务审批人发起转审。
- 已处理、已撤回、已取消、已完成或孤立待办不得转审。
接收人规则:
- 接收人必须是 active 员工。
- 接收人必须具备当前业务类型对应的审批权限:报销使用 `expense.approve`,请假使用 `leave.approve`,采购使用 `procurement.approve`。
- 接收人不能是申请人、实际报销人、提交人或当前动作人本人。
- 仅具备 `admin` 的系统管理员不能作为业务转审接收人。
- 不得通过转审绕过老板/总经理、财务、HR/行政等规则定义的必经节点。
状态和历史规则:
- 转审保持同一个 workflow instance、同一个 workflow task 和同一个当前审批节点,只更新当前任务处理人。
- 转审不改变报销、请假、采购的业务状态,不触发财务付款、预算占用确认、ERP 同步、考勤生效或附件归档。
- 每次转审必须写 `workflow_history`,至少包含原处理人、目标处理人、原因、转审次数和委托上下文。
- 报销既有 `approval_transfers` 可作为兼容审计表继续保留;请假和采购首批以 `workflow_history` 为事实源,后续如需结构化转审表必须另写迁移方案。
- 转审成功后通知目标审批人;通知失败不回滚主业务,但必须可追溯。
并发和重复规则:
- 同一任务最多转审 3 次,超过后阻断并提示选择管理员处理或规则修正。
- 服务端必须在事务内锁定当前任务,防止两个窗口同时转审、审批或退回导致重复结果。
- 转审不得新建并行待办,不得留下原审批人可继续处理的 pending 任务。
- 转审后原审批人再次打开旧页面时,应看到任务已变化或无权处理提示。
三类业务副作用边界:
| 业务 | 转审允许影响 | 不允许影响 |
| --- | --- | --- |
| 报销 | 当前审批人、审批历史、通知目标 | 发票绑定、附件、财务付款状态、重复票据例外、OCR 状态 |
| 请假 | 当前审批人、审批历史、通知目标 | 请假天数、余额预占、团队状态、考勤生效结果 |
| 采购 | 当前审批人、审批历史、通知目标 | 采购明细、附件、预算预检结果、ERP 同步、PO/合同归档 |
回滚和修正路径:
- 转审目标选错但任务仍 pending 时,由当前合法处理人再次转审回正确处理人,并填写修正原因。
- 目标人停用、离职或权限被回收时,不直接改数据库;由管理员按 ORG8 账号与权限治理流程确认后,通过受审计的改派或规则修正路径处理。
- 若代码实现引入回归,优先回滚 WF8 代码补丁并复跑 `server/scripts/regression-workflow-actions.sh`;已产生的历史记录不删除,只追加修正说明。
WF8-3、WF8-4、WF8-5 实现前必须基于本设计给技术方案;实现后必须运行转审单测、相关业务流回归,并启用 workflow reviewer 检查状态机、权限、通知和漏测项。
---
## 7. 异常规则
| 异常 | 处理规则 | 用户提示 |
| --- | --- | --- |
| 无审批人 | 提交时阻断,不生成半截流程;管理员可修正规则后重试 | “当前审批规则未找到审批人,请联系管理员” |
| 自审 | 申请人、实际报销人或提交人命中当前审批人时阻断;管理员身份不能绕过 | “自己的申请不能审批,请等待下一审批人处理” |
| 系统管理员无业务审批角色 | 仅有 `admin` 权限时不能处理业务审批;需配置 `manager`、`ceo`、`finance`、`hr` 等正式业务角色 | “系统管理员不自动获得业务审批权,请配置正式审批角色后处理” |
| 权限不足 | 服务端拒绝,记录必要安全日志 | “无权处理该审批任务” |
| 任务已处理 | 并发点击或重复提交时返回当前最新状态 | “该任务已处理,请刷新查看最新结果” |
| 并发审批 | 以服务端事务和任务版本为准,只允许一个处理结果生效 | “处理失败,任务状态已变化” |
| 转审超限 | 超过次数、转给无权限人员或形成循环时阻断 | “无法转审给该人员,请选择具备审批权限的人” |
| 审批人离职或停用 | 待办不能继续由停用账号处理;管理员按规则改派或转审 | “当前审批人不可用,请联系管理员处理” |
| 业务单据被删除或归档 | 不允许继续处理孤立待办 | “关联单据不可处理,请刷新或联系管理员” |
| 通知发送失败 | 不阻断主业务,进入可重试状态 | 业务页面不报错;管理员可在通知日志查看 |
---
## 8. 留痕与一致性
### 8.1 必须留痕的记录
- 工作流历史:提交、通过、驳回、退回、撤回、转审、批量审批结果。
- 审计日志:权限变更、审批规则变更、管理员改派、敏感附件查看、财务例外动作。
- 通知记录:发送渠道、接收人、发送状态、失败原因、重试次数、deeplink。
- 业务状态记录:业务单据状态与工作流状态变更必须能追溯。
### 8.2 一致性要求
- 审批动作成功后,业务单据状态、当前任务、审批历史必须在同一事务或等效一致性边界内更新。
- 通知失败不回滚审批动作,但必须可重试。
- 同一任务不能出现两个最终处理结果。
- 业务详情页展示的审批历史与 `/workflow/tasks` 的任务状态必须一致。
---
## 9. 企业微信边界
- 企业微信自建应用只作为 `海音达工作台` 手机入口。
- 企业微信个人应用消息是正式审批待办提醒的首选 IM 通道;群机器人和 mock 只允许作为运维或自动化补充证据。
- 企业微信消息只发送脱敏提醒:标题、业务编号、状态、处理入口。
- 企业微信 deeplink 必须指向 OA 审批页或业务详情页。
- 用户点击消息后,在 OA 内查看详情并执行审批动作。
- 企业微信原生审批参与 HR/考勤员工日常审批正式流程,OA 通过 `wecom_attendance_approval_mirror_v1` 同步为可查询、可导出、可审计的镜像记录;该命名承接旧口径 `wecom_native_approval_mirror_v1`。报销、财务、采购、合同和项目类 OA 主办审批仍不得在企业微信原生审批中替代完成。
- 采购单附件、合同正文、报价、供应商资料、发票、银行回单、代账资料和其他高敏附件不进入企业微信原生审批或企业微信附件;企业微信只做提醒、deeplink 和免登录回流。
- 按域镜像是默认规则:企微主办 HR/考勤域镜像到 OA;OA 主办采购、合同、项目、财务和报销域不反向全量镜像到企微。
- WFDG 后,审批相关页面和接口必须区分 `OA 主办`、`企微镜像` 和 `OA 应急接管`;企微镜像不得被误用为 OA 日常新建入口,OA 应急接管单据必须带 `EMERGENCY` 并进入恢复回写队列。
- WAPI/WRRA/WOUA-FIX/WAPC1/WAPC2/WPROC1/WEGOV1 后,审批相关运行时迁移必须先读 [企微优先开发路线图](./企微优先开发路线图.md) 和 [企微 API 能力目录](./企微API能力目录.md),按 `wecom_hr_master_sync_v1` -> `runtime_first_wecom_enablement` -> `WOUA-FIX / WECOM-RUNTIME-READY-V1` -> `WAPC1 审批配置准备` -> `WAPC2 请假/补卡只读 POC` -> `WAPC2F 自动生成 POC 样件` -> `WAPC2FLIVE live 创建与 WAPC2 自动复跑` -> `WCBK1 回调只读 POC` -> `WEGOV1 企微主办治理与自建留存路线修正` -> `WACP3/WACP3R 补偿拉取 POC` -> `WREAD1 审批可读性与误审批防护门禁` -> `WADM1 自建留存/镜像数据模型设计` -> `WARCH1 最小自建历史留存实现` -> `WARCH2 正式只读历史查询` -> `WROAD2+OPS 受控上线与每日巡检` -> `WARCH3 归档稳定化自动巡检` -> `WCHECK1 打卡摘要留存` -> `WEXT1 外出/加班 dry-run` -> `WOPS1 巡检告警降噪` -> `WROAD2-CLOSEOUT 三天验收` -> `WROAD2-BIZ1 三天业务冒烟` -> `WAPPR1 审批规则规范化` -> `WRULE1 审批链校验器 POC` -> `WAUTO1 自动证据汇总` -> `WEXT1LD 外出/加班 live 前 dry-run` -> `WFULLSMOKE1 三天全域最终目标测试` -> `WROUTE2 测试号收口到最小镜像路线锁定` -> `WTCLOSE2 测试号最终收口与证据复核` -> `WTCLOSE2F 远端可信 IP 执行口径修正` -> `WREMOTE1/WFULLSMOKE2 远端收口` -> `WEXT1-FORMAL 正式模板外出/加班测试` -> `四类测试号证据和审批规则门禁通过后问答 WREAL1 小范围真实试用(首批 1 名真实员工 + 1 名审批人/管理员观察)` -> `wecom_attendance_approval_mirror_v1 最小完整镜像` -> `oa_hr_emergency_mode_v1` -> `wecom_recovery_writeback_v1` 的顺序拆单功能 Trellis task;WOUA-FIX 只收口了用户一致性测试账号差异和处理决策,WRV1/WRA1/WMSG1 只验证入口、OAuth/绑定、脱敏消息、deeplink、通讯录、人事助手、打卡只读主链路和真实消息回流。WAPC1 只固化审批配置准备清单;WAPC2 只读模板详情和低风险 `sp_no` 审批详情;WAPC2FLIVE 只生成 OA-POC 低风险测试样件并自动复跑只读 POC;WCBK1 只证明真实审批状态回调可验签、可解密、可脱敏留证;WACP3R 只证明补偿拉取和详情读取可用,并发现回调证据文件未持久化;WEGOV1 只修正混合主办、自建留存和测试治理口径;WREAD1 只证明请假/补卡当前样例可读;WADM1 只完成模型设计;WARCH1 已实现请假/补卡最小历史留存;WARCH2 只提供只读查询 API 和历史页;WEXT1 只完成外出/加班 dry-run;WROAD2-BIZ1 只补充测试账号人工业务冒烟清单;WAPPR1 将申请人任意选择审批人列为真实试用前阻断 finding;WRULE1 只校验已归档审批链;WAUTO1 只做自动证据汇总,WEXT1LD 只做 live 前 dry-run,WFULLSMOKE1 只做全域主路径总控;WROUTE2 只锁定路线,WTCLOSE2/WREMOTE1 只做远端收口和全域 smoke,不授权写企微;WEXT1-FORMAL 人工提交前必须另建合同并再次人工确认。完整企微审批镜像必须等 WREAL1 通过后再规划,应急接管和恢复回写仍后置,不得用 WARCH1/WARCH2 或测试号证据直接放行。
- WRV1/WRA1 runner `npm run check:wecom-runtime-ready` 中的审批模板、测试 `sp_no` 和真实回调检查只允许输出 `NEEDS_CONFIG`;这类缺口不阻断运行优先主链路的 `PASS_WITH_CONFIG_GAPS`,但必须阻断 `wecom_attendance_approval_mirror_v1`、应急恢复回写和 RuleOps。`WECOM_CHECKIN_SYNC_ENABLED` 只控制正式考勤同步批次,不阻断 QA-only 打卡只读能力探测。
- WMSG1 已证明企业微信应用消息可达和 Deeplink 可进入 OA 通知页;该证据只清除通知链路,不清除审批模板、测试 `sp_no` 或真实回调前置。WAPC1 固化配置准备清单后,WAPC2 先提供请假/补卡模板详情和低风险审批详情只读 POC;WPROC1 后优先用 WAPC2F 自动生成 POC 样件,不默认要求人工手动创建模板或测试单。人工介入限于写入批准、密钥/回调配置、企微后台无法 API 化配置、真机确认和风险拍板。
- 后续企微审批同步的 POC 必须输出 `CAN_REUSE_NOW`、`NEEDS_CONFIG`、`NEEDS_API_POC`、`KEEP_OA_PRIMARY`、`NOT_SUPPORTED_BY_API`,缺模板、回调、Secret 或权限时记录 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`。
- 若企业微信返回 `invaliduser`、`unlicenseduser`、不可见范围或权限类错误,业务提交和审批动作不应回滚,但通知记录必须可审计、可重试,并提示管理员修正企业微信应用可见范围或账号绑定。
- 真机验收以企业微信手机端收到应用消息并可点击回流为准;系统级弹窗是否出现受客户端通知权限影响,不作为 OA 通知链路硬门槛。
### 9.1 WAPC1 企微审批配置准备
WAPC1 是进入 `wecom_attendance_approval_mirror_v1` 前的配置准备清单,不是审批镜像实现。
首批企业微信原生审批模板:
- 请假。
- 补卡。
- 外出。
- 加班。
每个模板至少准备:
- 模板 ID。
- 适用范围、可见范围和所属自建应用权限。
- 字段项、字段类型、审批人规则和状态流转边界。
- 企微侧转审、撤回、退回、附件和抄送能力边界。
后续 POC 必须验证:
- `POST /cgi-bin/oa/gettemplatedetail` 可读取模板详情,Secret 固定为 `WECOM_AGENT_SECRET`。
- `POST /cgi-bin/oa/getapprovaldetail` 可读取低风险测试 `sp_no` 的审批详情,测试单不得包含采购、合同、报销、发票、银行回单、高敏附件或真实敏感业务资料。
- 真实回调 URL 可被企业微信访问,Token 与 EncodingAESKey 只配置在部署环境,不写入仓库、文档或 Issue。
- 回调验签、解密、幂等键、企业微信重试语义、失败重试和补偿拉取方案明确。
- 批量获取审批单号作为补偿拉取候选,先标记 `NEEDS_API_POC`。
### 9.2 WAPC2 企微审批只读 POC
WAPC2 是 WAPC1 后的首个审批只读 POC,范围只包括请假和补卡,不是 `wecom_attendance_approval_mirror_v1`。
- 新增 QA-only runner `npm run check:wecom-approval-poc`,默认不写 OA/企微数据,不接真实回调。
- 请假模板使用 `--template=leave:<id>`,补卡模板使用 `--template=missed_punch:<id>`。
- 请假低风险测试单使用 `--sp-no=leave:<sp_no>`,补卡低风险测试单使用 `--sp-no=missed_punch:<sp_no>`。
- `gettemplatedetail` 和 `getapprovaldetail` 均固定使用 `WECOM_AGENT_SECRET`,不得尝试通讯录、打卡或历史 HR assistant Secret。
- 报告只记录脱敏模板 ID/`sp_no` 前缀、字段类型计数、审批节点计数和状态摘要;不得输出表单值、附件、评论、完整 `sp_no` 或原始 payload。
- 缺模板 ID 或测试单号时结论为 `PARTIAL_APPROVAL_POC_NEEDS_CONFIG`;权限、可信 IP、可见范围或 Secret 阻断时结论为 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`。
### 9.3 WAPC2F 自动生成 POC 样件
WAPC2F 是 WAPC2 后的自动化优先补缺阶段,目标是尽量用 QA-only runner 自动创建 OA-POC 请假/补卡测试模板和低风险测试审批单,再自动复跑 WAPC2。它不是正式模板配置、不是真实业务审批,也不是镜像实现。当前已新增 `npm run check:wecom-approval-fixtures` 并完成公网 dry-run,报告 `/app/artifacts/qa-flow/wecom-approval-fixtures-20260519T045001Z/summary.json` 为 `PASS_FIXTURE_DRY_RUN_READY`。WAPC2FLIVE 已在用户批准后修正 `create_template` payload,创建 OA-POC 请假/补卡测试模板和低风险测试审批单,报告 `/app/artifacts/qa-flow/wecom-approval-fixtures-20260519T053400Z/summary.json` 为 `PASS_FIXTURE_CREATED`,并自动复跑 WAPC2 得到 `PASS_APPROVAL_POC`;测试模板和审批记录保留审计,不自动删除。
- 默认申请人固定 `SMOKE002`,审批人固定 `SMOKE101`。
- 预检两者必须 active、已绑定企微、可见、不是同一 OA 员工、不是同一企微 userid。
- 自审硬阻断,不因 `admin`、`manager`、`ceo`、`finance` 或其他角色放开。
- 如果 `SMOKE101` 不可用,不自动换成 `HYD000007` 或其他人员;必须返回配置缺口并用问答方式确认新链路。
- live 创建 OA-POC 样件必须有显式 CLI 参数 `--live --create-templates --submit-test-approvals` 和单次进程环境变量 `WECOM_APPROVAL_FIXTURE_WRITE=1`,默认 dry-run 不获取写入 token、不调用企微写接口;WEXT1-FORMAL 正式模板测试不走该写入路径。
- 报告只保存脱敏模板 ID/`sp_no` 前缀、计数、状态和错误分类,不保存完整 `sp_no`、表单值、附件、评论、完整 userid、Secret、token 或原始 payload。
### 9.3.1 WAPPR1 企微审批规则规范化门禁
WAPPR1 承接 WROAD2 期间发现的补卡申请仍需申请人手动选择审批人风险。企微主办不等于放开自由流;HR/考勤审批进入真实试用前,必须先证明审批人规则自动化、非自审且可审计。
- 测试模板固定为 `SMOKE002 -> SMOKE101`,申请人不得选择自己,`SMOKE101` 不可用时不自动换人。
- 公司不到 10 人时,正式请假、补卡、外出、加班模板采用一层直属主管或指定审批角色自动带出审批人,不允许申请人任意选择审批人。
- 审批人缺失、停用、未绑定企微、不可见、主管链断裂或命中申请人本人时,必须由管理员兜底或阻断提交;若只能手选审批人,则阻断进入真实试用。
- 官方 API 边界:`gettemplatedetail` 只返回模板控件,`getapprovaldetail` 只返回已提交审批单的实际审批节点和审批人,均不能只读证明申请页是否允许手选或修改审批人;`applyevent` 的 `use_template_approver=1` 属于写入型验证,且不适合作为补卡等模板的通用只读门禁。
- WAPPR1 验收必须由三类证据组合:管理员在企微后台确认无“申请人自选”节点;`SMOKE002` 真机提交页确认审批人自动带出且不能任意改人;提交后用 `getapprovaldetail` 校验实际审批人为 `SMOKE101` 且无自审。
- 2026-05-21 人工门禁结论:测试员工直属上级已设置为测试主管,用户真机确认请假、补卡、外出、加班提交页均自动带出测试主管,且申请人不能删除、改人或新增审批人;四类模板均按 A / `PASS_APPROVER_RULES_READY` 记录。
- 结论按模板分别记录:`PASS_APPROVER_RULES_READY`、`PASS_WITH_TEMPLATE_ADJUSTMENTS`、`FAIL_KEEP_OA_PRIMARY_FOR_THIS_TEMPLATE` 或 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`。
- A/B/C/D 人工门禁口径不变:只有 A 可作为规则门禁通过条件;B、C、D 均阻断 WEXT1-FORMAL、WREAL1 和后续 `wecom_attendance_approval_mirror_v1`。
- 如果企微模板能力或后台配置暂时无法做到自动审批人规则,对应模板继续保持测试账号 POC 或 OA 主办,不进入 WREAL1 真实员工试用。
### 9.3.2 WRULE1 企微 HR/考勤审批链校验器 POC
WRULE1 不新造 HR/考勤审批引擎,也不读取企微后台规则配置。它只在 WAPPR1 人工门禁基础上,用 OA 已归档的企微审批详情和 OA 组织事实源自动校验“理论审批人”和“实际审批链”是否一致,防止单规则双域变成两套规则。
- runner 为 `npm run check:wecom-approval-rule-validator`;默认只读 OA DB,不调用企业微信 API,不读取 Secret,不写 OA 或企业微信业务数据。
- 上游审批详情仍由 WARCH1/WROAD2 补偿归档链路提供,固定使用 `WECOM_AGENT_SECRET`,不得靠通讯录、打卡或历史 HR assistant Secret fallback。
- 理论审批人计算顺序为:申请人的有效直属主管、配置的业务审批角色、管理员兜底。直属主管或角色审批人必须 active、已绑定企微且不是申请人本人。
- 管理员兜底只允许作为可解释异常,输出 `PASS_WITH_ADMIN_FALLBACK`;它不能把 `admin` 权限解释成普通业务审批权。
- 实际审批人为申请人本人时输出 `FAIL_SELF_APPROVAL`;实际审批人不在 OA 理论候选内时输出 `FAIL_APPROVER_MISMATCH`;WAPPR1 手工门禁 B/C 时输出 `FAIL_APPLICANT_CAN_CHOOSE`。
- 未传 `--manual-gate=<template>:A/B/C/D` 时输出 `PAUSED_MANUAL_CONFIRMATION`,表示缺少后台人工确认或真机提交页证据,不得把实际审批链匹配误读为模板规则已合规。
- 结论限定为 `PASS_RULE_MATCH`、`PASS_WITH_ADMIN_FALLBACK`、`FAIL_APPLICANT_CAN_CHOOSE`、`FAIL_SELF_APPROVAL`、`FAIL_APPROVER_MISMATCH`、`PAUSED_MANUAL_CONFIRMATION` 或 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`。
- 报告只输出审批类型、计数、脱敏 `sp_no`/template/employee 前缀和 finding 类型,不输出完整 userid、完整 template_id、完整 `sp_no`、手机号、邮箱、token、Secret、表单值、附件、评论或原始 payload。
- `PASS_RULE_MATCH` 或可解释 `PASS_WITH_ADMIN_FALLBACK` 只是进入测试号收口的自动证据之一;四类模板仍必须先由 WAPPR1 人工确认 A,才能进入 WEXT1-FORMAL、WREAL1 和最小完整镜像。
### 9.3.3 WTCLOSE2 测试号最终收口与证据复核
WTCLOSE2 是正式模板外出/加班测试前的最后自动收口。它复跑并汇总 WAUTO1、WFULLSMOKE1、外出/加班 dry-run 和 WRULE1 证据,确认测试号链路是否可解释通过;它不写企业微信、不创建外出/加班模板或审批单、不启用真实员工。
- 企业微信 API 受可信 IP 限制,WTCLOSE2 真实运行必须在部署服务器执行;runner 为 `bash scripts/qa/run-wecom-test-account-final-closeout.sh --remote`,由 SSH 到部署服务器并在 server 容器中生成 `artifacts/qa-flow/wecom-test-account-final-closeout-*/summary.json`。
- 本地直接执行 WTCLOSE2 脚本默认拒绝跑企微链路;本地只允许做 `bash -n`、合同、文档和静态门禁,不得把本地缺 env/DB 输出当作企微业务阻断证据。
- WFULLSMOKE1 在 WTCLOSE2 中必须以 `WFULLSMOKE_ALLOW_PUBLIC_WRITES=0` 运行,不新增 OA 测试业务数据。
- WRULE1 固定传入 `--manual-gate=all:A`,但仍只代表已提交审批链校验,不替代 WAPPR1 人工后台和真机门禁。
- 外出/加班步骤只执行 dry-run,不设置 `WECOM_APPROVAL_FIXTURE_WRITE=1`,不带 `--live`、`--create-templates` 或 `--submit-test-approvals`。
- 结论限定为 `PASS_TEST_ACCOUNT_CLOSEOUT_READY`、`PASS_WITH_FINDINGS`、`PAUSED_MANUAL_CONFIRMATION` 或 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`。
- WTCLOSE2 可解释通过后只能进入 WREMOTE1/WFULLSMOKE2 远端收口和 WEXT1-FORMAL 人工提交问答;真实员工试用和最小完整镜像仍需各自独立合同和人工批准。
### 9.3.4 WREMOTE1/WEXT1-FORMAL 正式模板外出/加班测试
WTCLOSE2F 后,企业微信审批相关业务证据必须来自部署服务器可信 IP。外出/加班下一步不再自动创建 OA-POC 测试模板,也不由系统自动提交审批单;改为使用企业微信后台现有正式模板做真实提交页和审批链验证。
- WREMOTE1 先部署/同步代码并执行 `bash scripts/qa/run-wecom-test-account-final-closeout.sh --remote`;全域 smoke 使用 `WFULLSMOKE_ALLOW_PUBLIC_WRITES=1 bash scripts/qa/run-three-day-full-goal-smoke.sh --remote`。
- WEXT1-FORMAL 人工操作为 `SMOKE002` 在正式外出、加班模板提交低风险测试单,`SMOKE101` 先打开详情页再审批。
- 系统只读校验 runner 为 `WEXT1F_MANUAL_CONFIRMATION=SMOKE002_SUBMITTED_SMOKE101_REVIEWED bash scripts/qa/run-wecom-formal-outing-overtime-evidence.sh --remote`;只能从部署服务器可信 IP 执行。本地直接执行必须拒绝业务运行,缺少人工确认标记时不得跑企微 API。
- 该 runner 显式使用 `--target=outing --target=overtime` 拉取正式模板外出/加班详情,并同时设置 `--filter-creator-employee-no=SMOKE002`;默认请假/补卡巡检范围不得因此扩大,也不得按正式模板 ID 宽泛读取同模板其他员工单据。
- OA 归档写入只允许走既有双开关:`WECOM_ARCHIVE_WRITE=1` + `--persist-to-db`;这只写 OA 自建留存库,不写企业微信。
- 系统只读校验为补偿拉取、审批详情读取、OA 归档读取、WRULE1 审批链校验和隐私扫描;不得调用创建模板、提交审批或其他企微写接口。
- WEXT1F 结论限定为 `PASS_FORMAL_OUTING_OVERTIME_READY`、`PASS_WITH_FINDINGS`、`PAUSED_MANUAL_CONFIRMATION` 或 `SKIP/BLOCKED_BY_EXTERNAL_CONFIG`。
- 若正式模板出现申请人可任意改审批人、自审、审批人不一致、可见范围或可信 IP 阻断,结论记录为 blocking finding,不进入 WREAL1。
- WREAL1 真实员工和审批人信息只通过 `/tmp/hyb-oa-wreal1.env` 临时文件提供,不写入 Trellis task、仓库文档或聊天。
### 9.4 WEGOV1 / WACP3 / WREAD1 / WADM1 / WARCH1 前置门禁
在 `wecom_attendance_approval_mirror_v1` 前必须补齐治理、POC、真机和数据模型门禁:
- `WCBK1 回调只读 POC`:已新增只读 `GET/POST /api/wecom/approval/callback` 和 `npm run check:wecom-approval-callback-poc`。公网 synthetic 验签、解密和解析通过后,部署环境完成回调配置并达到 `PASS_CALLBACK_URL_VERIFIED`;`SMOKE101` 随后处理现有 OA-POC 补卡测试单触发真实事件,复跑结论 `PASS_CALLBACK_EVENT_CONFIRMED`,报告只保存脱敏事件证据。
- `WEGOV1 企微主办治理与自建留存路线修正`:固定企微主办域、OA 主办域、OA 留存域和 OA 应急域;明确企业微信没有完整 sandbox 假设,默认使用受控测试域和自动化 runner。
- `WACP3 补偿拉取 POC`:已新增 `check:wecom-approval-compensation-poc` 和 `getapprovalinfo` 只读封装;WACP3R 公网复跑为 `PASS_WITH_FINDINGS`,已按 `SMOKE002` creator 自动发现请假/补卡 OA-POC 模板并拉取审批详情。当时 finding 为回调证据文件未持久化,已由 WADM1 设计和 WARCH1 DB 回调事件入库承接。
- `WREAD1 审批可读性与误审批防护门禁`:已新增 `check:wecom-approval-readability-gate` 只读 runner,并在 WREAD1F 新样例后完成真机确认。公网人工结果报告 `/app/artifacts/qa-flow/wecom-approval-readability-gate-20260519T133628Z/summary.json` 为 `PASS_READABLE`:请假和补卡消息卡片、审批列表、详情页足够看清;驳回有二次确认,同意单击即通过。后续不要求回退 OA 主办;WARCH1 已在归档审计中保留动作来源和补偿对账,正式历史页/操作规程仍需继续提示该风险。
- `WADM1 自建留存/镜像数据模型设计`:已完成字段、状态机、幂等键、索引、隐私保留、审计和迁移回滚方案设计。
- `WARCH1 最小自建历史留存实现`:已新增请假/补卡审批留存表、回调事件表、补偿 run/item/finding、补偿入库双开关和 QA-only 离线读取 runner;先证明企微不可用时可查看已保存历史,再进入完整镜像增强。
- `WARCH2 正式只读历史查询`:已新增只读查询 API 和 PC/移动历史页;查询只读 OA DB,不调用企微 API,不读取企微 Secret;普通员工、主管和 HR/admin/* 使用分层权限,申请人未映射和 callback-only 记录默认只允许 HR/admin/* 查看;页面不提供审批按钮或待办语义。
### 9.4.1 WADM1 自建审批留存模型
WADM1 只做模型设计。WARCH1 已在单独合同中创建最小迁移和写入路径,范围仍限定请假/补卡历史留存。
企微主办审批进入 OA 自建留存时,建议按以下模型拆分:
| 设计模型 | 职责 | 必要字段 |
| --- | --- | --- |
| `WecomApprovalArchive` | 审批单主表,表示一条企微审批在 OA 的历史事实 | `sp_no`、`template_id`、`template_type`、规范化状态、企微原状态码、申请人 OA 员工、申请人企微 userid、申请/完成/更新时间、最后来源、风险标记 |
| `WecomApprovalParticipant` | 申请人、审批人、抄送人、节点参与人快照 | `sp_no`、角色、节点序号、企微 userid、OA 员工映射、是否当前节点、是否映射失败 |
| `WecomApprovalAction` | 通过、驳回、撤回、转交、系统状态变化等动作历史 | `sp_no`、动作类型、操作者、动作时间、节点序号、意见摘要、来源和幂等键 |
| `WecomApprovalFieldSummary` | 表单字段规范化摘要 | 字段 key、字段名、字段类型、规范化值、敏感级别、是否可展示 |
| `WecomApprovalAttachmentSummary` | 低风险附件摘要和外部引用 | 附件数量、类型、外部引用摘要、是否下载、敏感级别 |
| `WecomApprovalCallbackEvent` | 企微回调事件持久证据 | `approval_archive_id`、完整 `sp_no`、完整 `template_id`、事件类型、规范化状态、接收时间、企微事件时间、处理结果、幂等键、摘要 digest;日志和报告只输出脱敏前缀 |
| `WecomApprovalCompensationRun` / `WecomApprovalCompensationItem` | 补偿拉取运行和逐单详情证据 | 时间窗口、模板过滤、游标、计数、错误分类、`sp_no` 关联、详情拉取状态 |
| `WecomApprovalReconcileFinding` | 回调与补偿对账差异 | 漏回调、重复回调、状态不一致、样例缺失、权限阻断和解释状态 |
状态机统一为 `pending`、`approved`、`rejected`、`revoked`、`terminated`、`changed`、`unknown`、`sync_failed`。同一 `sp_no` 采用幂等 upsert,动作历史只追加不删除;若回调和补偿拉取状态冲突,主表不静默覆盖为成功,而是写入 `WecomApprovalReconcileFinding`。
回调与补偿合并规则:
- DB 业务表必须保存完整 `sp_no`、`template_id`、企微 userid 和 `approval_archive_id`,用于稳定关联和补偿恢复;脱敏只发生在日志、报告、Issue、文档、默认导出和普通 UI 摘要。
- `getapprovaldetail` 的详情快照是业务状态的最终校准来源;回调是低延迟事件来源。补偿拉取成功拉到详情后,可以修正主表状态,但必须保留原回调事件和动作历史。
- 主表维护 `status_observed_at`、`last_source`、`last_detail_digest`。新事件只有在 `observed_at` 晚于当前状态时间,或详情 digest 变化时才能推进主状态;旧的非终态事件不得覆盖较新的终态。
- 终态 `approved`、`rejected`、`revoked`、`terminated` 不被较旧的 `pending` 或 `unknown` 覆盖;若企微详情返回不同终态,记录 `status_mismatch` finding,并以最新详情快照作为待确认状态。
- 回调重试使用同一事件幂等键 upsert 并递增重试计数,不重复写动作历史。补偿拉取同一 `run_id + sp_no` 只能有一条 item,详情 digest 相同则只更新时间和来源,不重复生成动作。
- source 优先级用于解释,不用于盲目覆盖:`getapprovaldetail` 详情快照 > 回调事件 > 手工导入/测试样例。任何跨来源不一致必须留 finding。
请假/补卡 WARCH1 V1 至少应保存:申请类型、申请人、审批人、时间范围或补卡时间、时长、原因摘要、状态、审批历史、来源、外部关联键和同步证据。外出/加班仍需后续样例或处理决策;不可把请假/补卡可读性结论扩展为外出/加班已通过。
同意单击即通过的风险处理不要求回退 OA 主办。WARCH1 已在归档记录中保留动作来源、操作者、时间、状态变化、回调/补偿来源和对账 finding;后续正式历史页和操作规程必须继续突出是否由企微单击同意触发,以及补偿拉取是否与回调一致。
隐私边界:
- OA DB 可保存完整 `sp_no`、`template_id` 和企微 userid,用于补偿、恢复和审计;日志、报告、Issue 和文档只输出脱敏前缀。
- 不保存原始 XML、密文、Token、EncodingAESKey、access token、Secret 或无治理原始 payload。
- 表单字段只保存业务必要的规范化字段;如后续确需保存原始 payload,必须另设加密存储、保留期限和访问审计。
- 普通员工只能看本人历史;审批人看自己处理链路;HR/管理员按范围查看;导出默认脱敏。
### 9.5 WF8 邮件通道决策
WF8 当前正式验收通道为站内通知和企业微信个人应用消息。邮件通道先记录为冻结项,不作为当前 WF8 硬门禁;三渠道验收在本阶段按“两渠道验收 + 邮件冻结说明”处理。
冻结原因:
- 当前 R7E 真实试运行尚未完成,邮件是否必要应由真实使用反馈决定。
- 未确认正式 SMTP 配置、发件域、收件人范围、退信处理和敏感信息脱敏策略前,不应把邮件纳入发布可测结论。
- 审批邮件容易被转发或进入个人邮箱,消息内容必须比企业微信更严格脱敏。
后续解锁条件:
- 用户确认邮件是审批通知正式通道,而不是临时辅助提醒。
- 技术方案写清 SMTP provider、密钥存放、退信处理、失败重试、速率限制和回滚路径。
- 邮件内容只包含脱敏标题、业务编号、状态和 OA deeplink,不包含完整发票、附件、合同正文、报价、银行卡、身份证或 token。
- `Test_Method` 同时覆盖站内、企业微信和邮件;缺 SMTP 或样例收件人时必须标 `SKIP`,不得写成 `PASS`。
---
## 10. 验收场景
| 场景 | 步骤 | 预期结果 |
| --- | --- | --- |
| OA 主办审批通过 | 申请人提交报销、采购或合同事项;审批人在 PC 通过;查看业务详情 | 单据从草稿到审批中再到已通过;历史、当前状态、通知记录一致 |
| 移动端审批 | 申请人提交单据;审批人从 `/mobile/approvals` 打开待办并通过 | 移动端可完成审批;PC 待办同步消失或进入已办 |
| 企业微信通知回 OA | OA 主办待办产生后发送企业微信提醒;审批人点击 deeplink | 打开 OA 对应审批页;审批动作在 OA 完成 |
| 企微 HR 审批镜像 | 员工在企业微信提交请假、补卡、外出或加班;审批人在企业微信处理 | OA 通过 `wecom_attendance_approval_mirror_v1` 同步审批详情、历史、附件和状态 |
| OA 应急 HR 审批 | 管理员人工开启 `oa_hr_emergency_mode_v1` 后,员工在 OA 提交 HR/考勤应急单 | 单据带 `EMERGENCY` 标记,进入恢复回写队列 |
| 企业微信个人应用消息 | 触发待办后向绑定企业微信成员发送个人应用消息 | delivery 为成功且无 `invaliduser`;手机端可收到消息并点击回到 OA 移动审批 |
| 驳回重提 | 审批人填写原因驳回;申请人查看原因后复制或重建申请 | 原流程留痕完整;新流程有新的提交记录 |
| 退回修改 | 审批人退回;申请人修改后重提 | 保留同一业务单号和退回历史;重新进入审批中 |
| 撤回 | 申请人在未完成前撤回 | 流程终止或回到草稿;审批人待办失效 |
| 转审 | 当前审批人填写原因并转给有权限人员 | 原审批人待办关闭;目标审批人产生新待办;历史显示转审链路 |
| 无审批人阻断 | 提交时审批规则找不到审批人 | 提交失败,无半截流程,提示联系管理员 |
| 正式主管链路 | 专用员工提交普通低/中额单据;其 active 直属主管处理待办 | 待办分配给实际直属主管,审批历史与组织关系一致 |
| 公司负责人二审 | 高额、高风险或例外单据进入 `ceo` 节点 | 串行节点完整,`majianyong(ceo)` 处理负责人节点 |
| 主管本人申请 | 当前 `manager` 提交单据 | 主管本人不能自审,按组织链进入 `majianyong(ceo)` 或明确非本人兜底节点 |
| 公司负责人本人申请 | `majianyong` 提交并触发负责人本人兜底 | 不允许自审;active 非本人 `finance` 唯一审批;部署和生产规则回填前应阻断 |
| 系统管理员无业务审批权 | 仅 `admin` 角色账号尝试审批业务单据 | 服务端拒绝,不因 `admin` 权限生成审批历史 |
| 并发处理 | 两个窗口同时处理同一任务 | 只有一个结果生效,另一个提示任务已变化 |
| 通知失败不阻断 | 模拟企业微信通知发送失败 | 审批提交或处理成功,通知记录进入失败可重试 |
---
## 11. 后续开发拆分建议
后续实现时建议按以下 Issue 拆分,不直接跳到大改代码:
- A1:审批任务列表与入口收口,覆盖 PC `/workflow/tasks`、移动 `/mobile/approvals` 和 Portal 待办摘要。
- A2:通用状态转换与并发保护,覆盖提交、通过、驳回、退回、撤回、转审。
- A3:审批权限与当前处理人校验,覆盖越权、自审、停用账号和角色待办。
- A4:审批历史、审计日志和通知记录一致性。
- A5:企业微信通知 deeplink 回 OA 验证,覆盖 OA 主办审批回 OA 和企微主办 HR/考勤审批镜像。
- A6:报销、采购、合同等 OA 主办业务逐个接入统一审批验收;请假、补卡、外出、加班按 `wecom_attendance_approval_mirror_v1` 单独接入镜像。
- A7:审批回归脚本与 Playwright PC/移动/企业微信 UA 验证。
- A8:审批规则配置矩阵落地,覆盖金额阈值、天数阈值、兜底审批人、转审和撤回。
- A9:权限与数据范围矩阵落地,覆盖附件下载、导出、例外处理和系统管理员最小授权。
R6 首轮试运行只验证已有审批主链路稳定性,不新增复杂审批能力。R6 后续审批效率能力已通过 AP6 v1 落地,当前事实源如下:
- 待办聚合:PC 和移动统一展示待办、已办、抄送和通知入口。
- 催办:`POST /api/workflow/tasks/:taskId/remind` 仅允许申请人、提交人或实际归属人催办本人审批中单据的 pending 任务;同一申请人对同一任务 24 小时内最多 1 次;审批人不能催办自己,`admin` 单角色不能代催办。
- 超时提醒:`job:workflow-overdue-reminders` 只扫描 pending 待办;优先使用 `due_date`,没有 `due_date` 时按创建后 24 小时判定;同一任务 24 小时内只发一次超时提醒。
- 审批效率统计:`GET /api/workflow/tasks/summary` 聚合待办总数、超时数、最早待处理时间、平均等待小时数,并按业务类型、节点和角色汇总;`scope=system` 仅 `system.audit.view` 可读,返回聚合数,不返回申请人明细。
- 状态与审计:催办和超时提醒不改变审批状态,不替代正式审批动作;通知失败不回滚业务,但必须进入审批历史、审计日志、站内通知或通知投递记录。
- 后续治理增强:流程配置治理、升级处理、节点 SLA 配置和复杂 BPM 仍需另拆 Issue,不在 AP6 v1 内顺手扩展。
涉及状态机、权限模型、数据库迁移或审批规则调整的 Issue,必须在技术方案中写明回滚策略和回归范围。