2026-07-23-oa-approval-transfer-design.md 9.4 KB

OA 审批 · 转交(Transfer)设计

项 内容
状态 已定稿(待实现)
日期 2026-07-23
范围 审批待办「转交」:替换处理人;引擎 / API / 详情 UI / 进度文案 / 已办列表
交互 Demo docs/mockups/oa-approval-transfer-demo.html
关联 加签 add_sign;进度人员行 2026-07-23-oa-or-and-sign-display-design.md;打回终态 2026-07-15-oa-return-copy-new-instance-design.md

1. 背景

当前 OA 审批详情在待办处理人视角提供:同意 / 拒绝 / 打回 / 加签。缺少「把本人待办交给他人处理」的能力;请假、出差等场景只能依赖加签(本人待办仍在)或线下协调。

产品确认增加 转交,语义对齐常见 OA「替换处理人」,而非代理委托。


2. 目标与非目标

目标

  • 当前待办处理人可将本人这一份 pending 审批任务转交给租户内另一有效用户。
  • 转交后:原处理人待办结束(transferred),目标人新增同节点 pending;实例仍 running,节点不自动推进。
  • 进度可追溯(含连续转交 A→B→C);「已处理」列表含转交记录。
  • 全应用可用,不增加设计器开关。

非目标

  • 设计器 allowTransfer 开关。
  • 代理/委托(责任仍算原审批人)。
  • 一次转交多人、转交并同意。
  • 按部门或节点候选池收窄选人。
  • 小程序端、钉钉式独立「转交墙」UI。
  • 改变或签/会签收口规则(仅替换本人任务,sign_mode 不变)。

3. 产品决策(已确认)

项 决定
语义 替换处理人:A 待办 → transferred;B 新建 pending
转交说明 可选(对齐加签说明)
应用开关 无;所有应用一律可转交
选人范围 租户内有效用户;排除本人;排除本节点已有 pending 的用户
再转交 允许连续转交
实现路径 关旧待办 + 建新待办(不用原地改 assignee_user_id)

与加签区别

| | 转交 | 加签 | |--|------|------| | 本人待办 | 结束 | 仍在 | | 选人 | 单选 | 可多选 | | 节点人数 | 替换一人 | 追加一人或多 |


4. 引擎行为(SHALL)

新增 transfer_task(db, task_id, *, tenant_no, user_id, target_user_id, comment=None),对齐 add_sign / return_task 的权限与校验风格。

4.1 前置条件

  1. 任务存在、租户匹配、del_flag=0。
  2. assignee_user_id == user_id(本人待办)。
  3. task_type == "approval" 且 status == "pending"。
  4. 实例 status == "running"。
  5. target_user_id 非空、≠ user_id。
  6. 目标用户:同租户、del_flag=0、status=="1"。
  7. 目标用户不在同实例同 node_key 的 pending 任务 assignee 集合中。

不满足时抛 OaEngineError,文案明确(无权 / 仅待处理可转交 / 用户无效 / 已在待办中等)。

4.2 写入步骤

  1. 当前任务:
    • status = "transferred"
    • comment_text = 说明 trim 后文本,可空
    • update_by / update_time 更新
  2. 同节点新建 OaTask:
    • node_key / node_name / sign_mode / round_no 继承原任务(round_no 与实例 current_round 对齐,同加签)
    • assignee_user_id = target_user_id
    • status = "pending"
    • resolve_source = "transfer"
    • remark 可存说明快照(与加签一致,可选)
    • 公共字段按现有任务创建约定填写
  3. _add_instance_event:event_type="transfer",actor_user_id=user_id,task_id/node_key 指向原任务;comment_text 可用用户填写的说明,或在说明为空时写入简短审计句(如「转交给 {targetUserId}」)。现有事件表无独立 payload 字段;进度展示以 oa_task 行为权威,事件仅审计。
  4. db.commit();返回 get_instance_detail。

4.3 节点聚合与列表

  • _aggregate_node_status:在现有优先级(rejected / returned > pending > approved …)上,将 transferred 与 approved 同等对待——不因存在 transferred 把节点标为终态;同节点仍有 pending 时节点为审批中。
  • 「已处理」任务查询:在现有 approved / rejected / returned 基础上 SHALL 包含 transferred。
  • 或签一人通过取消同节点其余 pending 时:行为不变;已 transferred 的历史行不被改写为 done(与「本人已同意不被覆盖」同一原则:只动仍为 pending 的任务)。

4.4 连续转交

B 持有 pending 后可再次转交 C:重复 4.1–4.2。进度同节点下按任务时间顺序展示多行(已转交 / 待处理)。


5. API(SHALL)

POST /api/admin/oa/tasks/{task_id}/transfer

  • 鉴权:与现有 approve / add-sign 相同(require_admin_or_super_admin + 租户)。
  • Body(Pydantic):
    • targetUserId: str(必填)
    • comment: Optional[str] = None
  • 成功:success_response(instance_detail, message="已转交")
  • 失败:error_response(400, str(e))

前端:web-admin/src/api/oaApproval.ts 增加 fetchTransferOaTask(taskId, { targetUserId, comment? })。


6. 前端 UI(SHALL)

权威交互:docs/mockups/oa-approval-transfer-demo.html(Fresh Mint)。

6.1 详情顶栏

  • 条件:canApprove(存在本人 pending 审批任务)。
  • 按钮顺序建议:同意 → 拒绝 → 打回(若允许)→ 加签(若允许)→ 转交。
  • 不依赖 allowTransfer / allowAddSign;转交始终在 canApprove 时展示。
  • 图标:Leading el-icon(如 Position / Right 一类,与现有按钮规范一致)。

6.2 转交弹窗

  • el-dialog:dialog-form-layout、label-position="top"、无 backdrop blur、modal-class 与现有协议/加签弹窗一致。
  • 字段:
    • 转交给:单选用户(el-select filterable);选项来源同加签用户列表;前端可再过滤本人与本节点 pending 处理人(后端仍须二次校验)。
    • 转交说明:可选 textarea。
  • 底栏:取消 / 确定转交(Close / Check 图标)。

6.3 成功后

刷新实例详情;原用户无 pending → 操作区消失;进度见 §7。


7. 进度与文案(SHALL)

  • 任务状态文案:transferred → 已转交(详情 status 映射、进度人员行、打印同源)。
  • 人员行格式对齐 formatProgressPersonLines:
    • 有转交目标、无说明:{姓名} 转交给{目标姓名} {时间}
    • 有转交目标、有说明:{姓名} 转交给{目标姓名} {说明} {时间}
    • 缺转交目标、无说明:{姓名} 已转交 {时间}(兼容旧数据)
    • 缺转交目标、有说明:{姓名} {说明} {时间}
  • 后端 build_progress_items 的 people[] SHALL 为 transferred 项补齐 transferToUserId / transferToUserName(同节点 resolve_source=transfer 且 create_by=转交人的新任务处理人)。
  • Tone:建议 mute 或独立中性(勿用 danger);cancelled 仍为 danger,避免与转交混淆。
  • ACTED_STATUS(或等价集合)SHALL 包含 transferred,以便走「已处理」拼行分支而非「待处理」。
  • 当前处理人展示:取同节点仍为 pending 的 assignee;转交后更新为新人。
  • 界面「审批进度」与打印 PDF「审批流程」SHALL 使用同一套人员行文案(均经 formatProgressPersonLines)。

8. 测试(SHALL)

backend/tests/test_oa_engine.py(风格对齐加签用例):

  1. 成功转交:原任务 transferred,新任务 pending + resolve_source=transfer。
  2. 说明可空。
  3. 不可转给自己;不可转给本节点已有 pending 用户;无效用户报错。
  4. 非 pending / 非本人 / 实例非 running 报错。
  5. 会签:同节点另一人 pending 不受影响。
  6. 连续转交两次成功。
  7. 已办查询含 transferred(若有直接测 list kind=done 的辅助逻辑则一并覆盖)。

前端:至少手工走通 Demo 与详情页;若已有进度 formatter 单测,补 transferred 一行用例。


9. 改动面清单

层 路径
引擎 backend/app/services/oa_engine.py
API / schema backend/app/api/oa_approval.py(及现有 Oa body schema 文件)
测试 backend/tests/test_oa_engine.py
API 客户端 web-admin/src/api/oaApproval.ts
详情 web-admin/src/views/oa-approval/instances/detail.vue
进度文案 web-admin/src/utils/oa-progress-person-lines.ts
打印(若共用状态映射) web-admin/src/utils/oa-approval-print.ts 等按需
Demo docs/mockups/oa-approval-transfer-demo.html

无需新表;oa_task.status 增加取值 transferred(字符串枚举,无 DDL 强制时以约定为准)。


10. 风险与缓解

风险 缓解
已办列表漏 transferred 与打回修订同一模式,查询显式纳入
与 cancelled/done 混淆 独立状态 + 进度文案「已转交」
聚合把仅含 transferred 的节点判错 有 pending 优先;测会签/或签样例
加签按钮旁误点 Demo 与文案区分;实现后 UI 对照 Demo

11. 验收标准

  1. 待办人可打开转交弹窗,选人后成功;说明可空。
  2. 进度显示原人已转交、新人待处理;当前处理人更新。
  3. 原人「已处理」可见该单;新人「待处理」可见。
  4. 会签只换自己那份;连续转交可用。
  5. 转给自己 / 重复 pending 用户 / 非待办态均失败且有提示。