# 渠道无关审批核心功能总结

## 本次范围

任务 2.9 建立由业务侧拥有的 Approval Port 和通用审批实例。退款、线下充值及后续审批业务只依赖这套稳定契约，不依赖企业微信或其他渠道的 SDK、DTO、状态码和外部编号字段。

通用实例仅保存业务类型与业务 ID、真实提交人及其快照、`provider`、通用 `external_ref`、标准状态、申请/决策快照、关联 ID 和并发版本。数据库使用 `(business_type, business_id)` 保证一张业务单只有一个审批实例，并使用 `(provider, external_ref)` 的非空部分唯一索引防止同一渠道实例重复绑定。

## 标准决策

渠道 Adapter 只能向业务核心输出以下标准决策：

- `approved`：审批通过。
- `rejected`：审批拒绝。
- `cancelled`：审批撤销。
- `deleted`：审批删除。
- `revoked_after_approved`：审批通过后撤销。

业务消费者不得识别企微、钉钉或其他渠道状态。标准决策到通用终态的映射由审批领域统一维护。

## 事务与扩展边界

业务侧 Port 分为事务前 `Prepare` 和调用方事务内 `CreateInTx`。后续任务 2.12 将实现有效 Adapter、场景和发起身份的失败关闭检查，以及业务单、通用审批实例和提交 Outbox 的原子创建。

本任务不实现审批同步、终态 Outbox、处理租约、通用 Query 或任何渠道 Adapter；这些分别由 2.10～2.12 和 4.W1～4.W14 收口。当前没有外部调用，因此 Integration Log 为 N/A；没有提交后副作用，因此本任务不写 Outbox；通用审批表本身是审批 Domain Ledger。Audit Event 按总台账冻结到 6.5。

## 明确排除

- 不保存企微 Token、`sp_no`、模板 ID、控件 ID 或成员身份。
- 不保存审批节点、审批人、意见、附件副本、会签或或签规则。
- 不建设本地审批流引擎，也不定义退款、充值、钱包或佣金规则。
- 不新增 API、Handler、Worker 或前端页面。

## 通用读取模型

任务 2.10 在通用实例之上增加单条和最多 100 条的批量 Query。Query 只读取当前页实例，并一次性调用业务权限 Adapter 复核当前账号对退款、充值等原业务资源的访问权；无权、资源不存在和引用失效对单条读取统一返回禁止访问。批量读取只返回有权项目，并保持调用方当前页顺序，不逐条查询业务表。

稳定投影包含业务引用与摘要、真实提交人、`provider`、标准状态及中文名称、状态时间和业务处理摘要。处理状态由对应业务 Adapter 提供，不以审批状态冒充退款或入账结果。

渠道扩展 Resolver 是可选接缝，只允许平台或超级管理员在通过原业务权限复核后读取已保存的本地快照，并明确禁止在 Query 请求中实时访问外部审批平台。代理和企业账号不会调用扩展 Resolver，因此不能获得审批节点、审批人、内部意见或渠道附件。

## 回调、轮询与标准决策分发

任务 2.11 提供回调、兜底轮询和受控人工同步共用的 `SyncDecision` 用例。具体渠道 Adapter 必须先读取渠道权威详情并翻译为五类标准决策，再以 `callback`、`polling` 或 `manual` 来源调用同一用例；回调载荷本身不能绕过权威详情直接修改业务状态。

用例在行锁事务内执行状态机校验和乐观锁条件更新，并以 `approval:{instance_id}:{decision}` 作为稳定事件 ID，同事务写标准决策投递事实和公共 Outbox。回调与轮询并发、重复回调或重复轮询只会由先到者写入一次；后到的相同决策正常幂等结束。`revoked_after_approved` 允许在 `approved` 之后形成独立事件，其他互相冲突的终态拒绝覆盖。

Outbox 消费后，分发器按业务类型调用退款、充值等业务消费者。每个“审批实例 + 标准决策”拥有独立处理记录和可过期租约，成功后永久幂等；失败释放租约并保留安全摘要等待重试。业务消费者仍必须以审批实例 ID 和决策作为自身幂等键，且不得导入任何第三方审批 SDK、DTO、状态码或模板字段。

本轮新增和此前公共基础迁移 `000165`～`000170` 已为每个新增表字段或新增列补齐中文数据库备注；不建立外键，关联继续由 Application/Domain 显式维护。

## 原子创建与失败关闭

任务 2.12 实现业务侧 Approval Port。业务用例先在事务外调用 `Prepare`，由具体 Provider Adapter 同时确认 Adapter 已装配、场景可用和真实发起身份可解析；任一条件不满足都会在业务单、审批实例和 Outbox 写入前返回服务不可用。准备结果只有 30 秒有效期，绑定业务类型、真实提交人和关联 ID，且内部字段不能由退款或充值业务包自行构造。

通过前置检查后，业务用例把自己的 GORM 事务传给 `CreateInTx`。该方法在同一事务内创建唯一通用审批实例、让 Provider Adapter 固化渠道专属安全上下文，并写 `approval.submission.requested` Outbox；任何一步失败都向调用方返回错误，调用方必须让包含业务单的整个事务回滚。事务内不调用 Redis、Asynq、对象存储或第三方审批网络。

当前测试环境尚未装配具体审批 Adapter 时使用失败关闭实现，因此退款和线下代充值核心可以围绕 Port 编译，但新提交入口不能被误开放。企微场景、模板和身份上下文由 4.W1～4.W5 实现，不进入通用实例。
