Files
junhong_cmp_fiber/openspec/changes/archive/2026-08-18-add-legacy-approval-trigger/design.md
2026-08-18 16:30:16 +08:00

3.3 KiB
Raw Blame History

Context

历史记录在 tb_refund_requesttb_agent_recharge_record 中保持待审批但 approval_instance_id 为空。现有新建用例已经在单个事务中创建通用审批实例、企业微信上下文、审批提交 Outbox并回填该关联两个业务表的 approval_instance_id 均有唯一索引,通用审批实例还以业务类型和业务 ID 唯一。

Goals / Non-Goals

Goals:

  • 以最小增量复用现有审批创建和可靠提交链路补发历史记录。
  • 以原创建账号构造企业微信发起人及审批快照。
  • 使并发请求和重复请求均不会形成第二张审批单。

Non-Goals:

  • 不批量扫描或自动补发历史记录。
  • 不改变既有审批终态、企业微信提交重试或人工审批接口。
  • 不新增迁移、重置既有审批关联,或为提交失败创建第二张审批单。

Decisions

在各业务审批创建用例中增加历史记录发起入口

退款和线下代理充值分别新增面向既有记录的 Application 用例入口,复用各自已有的快照构造、提交人校验、通用审批 Prepare/CreateInTx、审计及 DTO 组装逻辑。Handler 只解析路径 ID 并调用服务Service 负责加载完整业务事实和调用 Application。

选择按业务保留两个小入口,而不引入跨退款/充值的通用“历史审批补发器”:二者的资格条件、快照和关联事实不同,现有两个创建用例已是最短复用边界。

以事务内条件更新和既有唯一约束保证一次性

发起前可在事务外执行审批渠道预检;事务内必须重新读取或条件更新业务记录,要求 status=待审批 AND approval_instance_id IS NULL,再创建通用审批及渠道上下文/Outbox并回填 approval_instance_id。任一环节失败回滚,不消耗发起资格;成功提交后,由业务表关联唯一索引和通用审批业务唯一索引共同拒绝并发的第二次创建。

不增加“已尝试”字段:用户确认以成功创建审批实例作为一次性边界,已有唯一关联就是持久化且可恢复的事实源。

发起人和授权语义

企业微信发起人固定为业务记录 Creator,不使用点击接口的账号;该账号不可用时失败关闭。接口沿用各自当前路由组的账号类型授权,不扩大既有退款或代理充值管理入口的访问范围。返回值沿用现有详情 DTO 的审批摘要字段,避免新增响应类型。

Risks / Trade-offs

  • [原创建账号已禁用或未绑定企业微信] → 不创建任何审批事实并返回错误;维护者修复账号/绑定后可再次操作。
  • [两个请求同时发起] → 事务条件和数据库唯一约束确保仅一个提交成功,调用方对另一个请求按冲突处理。
  • [提交 Outbox 后企微调用结果未知] → 沿用已有结果未知恢复流程,禁止通过本接口重建审批。

Migration Plan

  1. 发布 API 与 Worker 均包含该版本的应用代码,确保 Outbox 消费者已注册。
  2. 维护者在生产环境按发布运行说明,通过列表筛选待审批历史记录后逐单调用新接口,并核对返回的审批摘要与审计/Outbox 事实。
  3. 如需回滚,仅停止暴露新路由并回滚应用二进制;已成功创建的审批实例继续由既有 Worker 流程处理,不删除审批关联或重新发起。