## 1. 分销与注册 - [x] 1.1 为 `tb_shop` 新增全局唯一、不可修改的随机 `distribution_code`(新成对迁移 + 条件唯一索引,编号按实施时最大编号顺延);在 `internal/application/shop/create.go` 建店事务内生成并处理唯一冲突重试;同步 `scripts/migration/lib/shop_sql.py` 使批量导入店铺同样生成分销码;不提供人工指定或编辑入口。 - [x] 1.2 新增 `tb_agent_distribution_registration`(分销码、上级店铺、手机号、密码哈希、状态、审批实例与时间戳)及模型、状态常量、审计资源常量与 registry 条目;不创建 `status=2` 的店铺或账号;同一手机号驳回后再次扫码为新记录。 - [x] 1.3 实现公开接口 `POST /api/c/v1/agent-distribution-registrations`:经 `internal/routes.Register` 注册,且在 `internal/routes/personal.go` 的 `Use()` 之前;复用既有验证码校验、消费与限流;锁定上级店铺并校验启用,无效码、停用上级、验证码无效或已消费统一返回“分销码不可用”且不落库;新 Handler 同步 `pkg/openapi/handlers.go`、`cmd/api/docs.go`、`cmd/gendocs/main.go` 与 bootstrap 装配。 - [x] 1.4 注册 `agent_distribution_approval` 决策消费者:通过时同一事务建启用店铺、代理主账号、所需钱包、上级层级与上级业务员快照,标记注册记录并写审计,提交后清理上级店铺下级缓存;驳回仅标记不建实体;条件更新保证重复回调不重复建账号、层级或钱包。 - [x] 1.5 新增 `tb_withdrawal_qualification`(主体类型、签约主体代码、法人身份证号、合同与身份证正反面附件、可选营业执照/门头照/发票及其主体代码、状态与失效原因、审批实例)与模型、常量;附件每项单对象键,总数不超过企微 6 附件上限。 - [x] 1.6 实现资格提交、替换、作废与停用失效:`POST /api/admin/shops/:shop_id/withdrawal-qualifications`(仅本人代理店铺)、超管作废 `POST /api/admin/withdrawal-qualifications/:id/void`(原因必填)、代理停用联动失效;替换合同或身份证时同一事务新增版本并使旧有效版本失效;注册 `withdrawal_qualification_approval` 消费者,驳回不影响既有有效版本。 - [x] 1.7 为分销码生成、注册通过/驳回、资格提交/替换/作废/停用失效补齐事务内审计;日志与审计不记录密码、完整证件号或附件内容。 ## 2. 提现 - [x] 2.1 新增 `tb_commission_withdrawal_request_attempt`(`request_id`、`attempt_no`、金额/手续费/费率/实际到账快照、收款信息、申请级发票快照、提交人、审批实例、释放时间)及唯一约束与索引;为 `tb_commission_withdrawal_request` 追加 `latest_attempt_id`、`latest_approval_instance_id`、通过时间与异常标记/原因列。 - [x] 2.2 改造 `CreateWithdrawalRequest`:增加有效资格校验;事务内锁定佣金钱包、冻结金额、写尝试记录、创建审批实例(`business_id` = 尝试记录主键)、回填 `latest_*`、写钱包流水与审计;余额不足、资格无效或非本人代理均不创建申请或冻结。 - [x] 2.3 实现驳回后重提:仅已驳回申请可由本人代理修改金额、收款信息与本次发票;事务内先释放旧未结算尝试的冻结,再按新金额冻结并新增尝试记录与新审批实例,历史快照与审批结果不被覆盖,不产生第二笔冻结。 - [x] 2.4 注册 `commission_withdrawal_approval` 终态消费者:通过仅一次从冻结余额扣减、保持 `WithdrawalStatusApproved`=2 并写 `paid_at`;驳回与 `cancelled`/`deleted` 仅一次释放本次尝试冻结并写释放时间;`revoked_after_approved` 不回滚、不重新冻结、不自动重提,写正交异常标记与原因供详情展示;重复、乱序与未知结果由既有交付租约与查询恢复收敛。 - [x] 2.5 本地人工终审边界:`internal/service/commission_withdrawal` 的通过/驳回在申请已关联审批实例时拒绝并返回状态冲突,不新增任何配置开关;`approval_instance_id` 为空的存量申请保持既有兼容处理;错误交全局 ErrorHandler。 - [x] 2.6 注册资格、提现申请与提现详情的后台路由及 OpenAPI,保障自身店铺与数据范围校验;公开接口不得承载资格、提现或后台查询操作。 ## 3. 审批业务类型注册点 - [x] 3.1 `pkg/constants/approval.go`:新增 `agent_distribution_approval`、`withdrawal_qualification_approval`、`commission_withdrawal_approval` 三个业务类型常量与各场景业务字段常量,注释写明 `business_id` 取值语义。 - [x] 3.2 `internal/model/dto/wecom_scene_dto.go`:两处 `business_type` enum 与中文 description 同步新增三个类型(ENG-DTO-001)。 - [x] 3.3 `internal/application/wecom/scene.go`:`sceneBusinessFields` 白名单、`validApprovalBusinessType`、业务类型中文名三处新增。 - [x] 3.4 新成对迁移扩展 `chk_wecom_approval_scene_business` 纳入三个新类型;`down` 在存在新类型场景行时拒绝破坏性回滚并报明确异常;不修改既有迁移,编号按实施时最大编号顺延。 - [x] 3.5 `cmd/worker/main.go`:新增三个决策 handler 的依赖装配并注册到 `BusinessDecisionHandler` dispatcher(缺注册时按既有规则返回服务不可用)。 - [x] 3.6 `internal/infrastructure/audit/approval.go` 的 `approvalBusinessResource` 新增三个业务类型的资源映射(缺映射会 fail-closed);`pkg/constants/audit.go` 与 `internal/infrastructure/audit/registry.go` 新增分销注册记录、资格资料版本、提现审批尝试记录三个审计资源(提现单资源已存在,无需新增)。 - [ ] 3.7 运行期前置:上线前由超级管理员经 `PUT /api/admin/wecom/scenes/{business_type}` 为三个业务类型配置启用场景与模板控件映射;迁移不写入模板,未配置时相应提交失败关闭。**待维护者执行**:配置 `agent_distribution_approval`、`withdrawal_qualification_approval`、`commission_withdrawal_approval` 的启用场景与控件映射(资格 6 个附件控件对应 `file_list`,必填合同/法人身份证正反面、可选营业执照/门头照/发票);验收证据为 `GET /api/admin/wecom/scenes` 返回三类型启用记录及 `control_mapping`,字段清单经 `GET /api/admin/wecom/scenes/{business_type}/fields` 核对。本项为运行期配置数据,不是代码交付物,故未勾选。 ## 4. 验证 - [x] 4.1 按 ENG-TEST-001 在 `junhong_cmp_test` PostgreSQL + Redis DB 6 验证:迁移 `up/down/up`;从本地工作区以显式 `DB_*` 参数执行 `scripts/migrate.sh`;仅创建、删除本 Change 自己的 fixture,禁止重置整库。验证:以显式 `DB_*`(`junhong_cmp_test`)执行 `./scripts/migrate.sh up`(214/215/216/217 全部成功)→ `version` 为 217 且 `dirty=false` → `down 3` → 再 `up` 回 217;`down` 守卫按设计拒绝破坏性回滚(存在 `tb_agent_distribution_registration` 行时 000214 拒绝、存在新类型场景行时 000216 拒绝),因此整库回退只能到 `000214`,发布说明写明该边界。验证期间只创建、删除本 Change 自己的 fixture(`shop_code LIKE 'AUG26008%'`、三张新表、13 个新增动作码审计与资源行),复核残留:新表与审计残留均为 0,`tb_shop` 无空分销码且全局唯一;未重置整库。 - [x] 4.2 受控验证本地状态机:以 Redis 预置验证码(DB 6)、`approval.Port.Prepare` 与受控 `TerminalDecisionEvent` 驱动业务消费者,验证分销码唯一与停用门禁、注册通过/驳回与重复回调、资格替换与作废、提现冻结/重提/通过/驳回/取消/删除/通过后撤销、本地终审拒绝与存量兼容、待审提现释放接缝;不调用企业微信、短信、对象存储或支付网络。验证:临时受控脚手架(Redis DB 6 显式覆盖,`.env.local` 的 DB 7 不适用)驱动真实业务消费者,结果 **227 项通过 / 0 项失败**,分 18 组:分销码唯一/随机/不可修改/冲突重试与非事务句柄守卫;公开注册验证码无效与已消费、无效码、停用上级统一拒绝且不落库;注册通过建店+账号+钱包+层级+业务员快照且新码≠上级码并可续开下级,重复回调不重复建实体,驳回不建实体且同手机号可再注册;资格提交/替换失效/驳回不改写历史/超管作废/停用与删店联动失效;提现冻结/通过(状态保持 2 且写 `paid_at`,不使用 4)/驳回/cancelled/deleted/重提单笔冻结/通过后撤销不回滚;释放接缝先释放后扣减、重复释放不重复入账;本地终审拒绝新申请与存量兼容;退款佣金回扣 × 在途企微提现(修复前复现 8 项失败);统一加锁序下 6 轮并发无死锁;负向场景拒绝审计与 14 个动作码审计真实落库、恰一个主要资源、失败/拒绝审计不再被吞(`auditfailure` 日志 0 行);数据范围与脱敏投影。脚手架为一次性产物,按清理要求已删除,未在仓库留下测试入口(自动化测试按项目决策为 N/A)。 - [x] 4.3 运行 `gofmt -w`、`go build ./cmd/api ./cmd/worker`、`go run cmd/gendocs/main.go`、`openspec validate add-agent-distribution-withdrawal-qualification --strict`、`openspec doctor --json` 与 `./scripts/context-health.sh`;自动化测试按项目决策为 N/A。验证:`gofmt -l` 对全部变更 Go 文件输出为空;`go build ./cmd/api ./cmd/worker` 无错误;`go vet ./internal/... ./pkg/...` 无输出;`go run cmd/gendocs/main.go` 重新生成 `docs/admin-openapi.yaml`,与工作区文件逐字节一致,新增路由与 6 值业务类型枚举齐全,既有路由未被改写或删除;`./scripts/context-health.sh` 输出「Context 健康检查通过」;`openspec validate add-agent-distribution-withdrawal-qualification --strict` 输出 `Change 'add-agent-distribution-withdrawal-qualification' is valid`;`openspec doctor --json` 为 `"healthy": true`。