Files
break 575d056f54
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
feat(代理分销提现): 落地扫码注册、提现资料资格与企微终审提现
AUG26-008。

- 迁移 000214–000217:tb_shop 全局唯一且不可修改的随机分销码(含存量回填)、
  tb_agent_distribution_registration 待审批注册记录、tb_withdrawal_qualification 资料版本、
  tb_commission_withdrawal_request_attempt 审批尝试记录,以及提现申请的 latest_*/异常标记列;
  不修改既有迁移,down 在存在本 Change 业务事实或新类型场景行时拒绝破坏性回滚。
- 公开接口 POST /api/c/v1/agent-distribution-registrations:无认证,复用既有短信验证码校验、
  消费与限流;无效分销码、停用上级、验证码无效或已消费统一返回「分销码不可用」且不落库,
  审批通过前不创建店铺、账号或钱包。
- 审批通过才在同一事务内建启用店铺、代理主账号、钱包、上级层级与业务员快照,驳回不建实体,
  重复回调不重复建实体,提交后清理上级下级缓存。
- 提现资料资格按不可变版本保存,替换合同或法人身份证即新增版本并同事务失效旧有效版本;
  超管作废原因必填;代理停用与店铺删除联动失效。
- 提现每次提交或重提新增不可变审批尝试记录并冻结金额;企业微信通过仅一次从冻结扣减、
  保持状态 2 并写 paid_at(不使用状态 4),驳回/cancelled/deleted 仅一次释放,
  通过后撤销不回滚、不重新冻结、只写正交异常标记;加锁顺序统一为申请→尝试→钱包。
- 本地人工终审对已关联审批实例的申请返回状态冲突,approval_instance_id 为空的存量申请保持既有行为,
  不新增任何配置开关。
- 补齐审批业务类型注册点全集:业务类型与场景字段常量、场景 DTO 两处枚举与中文描述、
  场景字段白名单/合法类型/中文名、数据库 CHECK、Worker 决策消费者与装配、审批审计资源映射,
  以及三个新审计资源与 13 个审计动作;失败/拒绝审计改为必达。
- 新增后台路由与 OpenAPI:资格提交/查询/作废、提现申请/重提/详情、店铺详情返回只读分销码。
- 归档本 Change:主 Spec 新增 agent-distribution-withdrawal 能力(5 个 Requirement)。

验证(junhong_cmp_test + Redis DB 6,显式 DB_*,未重置整库):
- 迁移 up → version 217 且 dirty=false → down 3 → up 回 217,fixture 复核残留为 0。
- 受控状态机脚手架 227 项通过 / 0 项失败,覆盖 18 组场景(幂等与乱序回调、资金冻结/释放/重提、
  退款回扣 × 在途提现并发、负向场景拒绝审计与 14 个动作码审计真实落库)。
- gofmt 空、go build/go vet 通过、gendocs 与工作区逐字节一致、context-health 通过、
  openspec validate --strict 通过、doctor healthy;自动化测试按项目决策为 N/A。

运行期前置(未完成,非代码交付物):由超管经 PUT /api/admin/wecom/scenes/{business_type} 为
agent_distribution_approval、withdrawal_qualification_approval、commission_withdrawal_approval
配置启用场景与模板控件映射;未配置时相应提交失败关闭。
2026-09-14 09:45:13 +08:00

9.9 KiB
Raw Blame History

1. 分销与注册

  • 1.1 为 tb_shop 新增全局唯一、不可修改的随机 distribution_code(新成对迁移 + 条件唯一索引,编号按实施时最大编号顺延);在 internal/application/shop/create.go 建店事务内生成并处理唯一冲突重试;同步 scripts/migration/lib/shop_sql.py 使批量导入店铺同样生成分销码;不提供人工指定或编辑入口。
  • 1.2 新增 tb_agent_distribution_registration(分销码、上级店铺、手机号、密码哈希、状态、审批实例与时间戳)及模型、状态常量、审计资源常量与 registry 条目;不创建 status=2 的店铺或账号;同一手机号驳回后再次扫码为新记录。
  • 1.3 实现公开接口 POST /api/c/v1/agent-distribution-registrations:经 internal/routes.Register 注册,且在 internal/routes/personal.goUse() 之前;复用既有验证码校验、消费与限流;锁定上级店铺并校验启用,无效码、停用上级、验证码无效或已消费统一返回“分销码不可用”且不落库;新 Handler 同步 pkg/openapi/handlers.gocmd/api/docs.gocmd/gendocs/main.go 与 bootstrap 装配。
  • 1.4 注册 agent_distribution_approval 决策消费者:通过时同一事务建启用店铺、代理主账号、所需钱包、上级层级与上级业务员快照,标记注册记录并写审计,提交后清理上级店铺下级缓存;驳回仅标记不建实体;条件更新保证重复回调不重复建账号、层级或钱包。
  • 1.5 新增 tb_withdrawal_qualification(主体类型、签约主体代码、法人身份证号、合同与身份证正反面附件、可选营业执照/门头照/发票及其主体代码、状态与失效原因、审批实例)与模型、常量;附件每项单对象键,总数不超过企微 6 附件上限。
  • 1.6 实现资格提交、替换、作废与停用失效:POST /api/admin/shops/:shop_id/withdrawal-qualifications(仅本人代理店铺)、超管作废 POST /api/admin/withdrawal-qualifications/:id/void(原因必填)、代理停用联动失效;替换合同或身份证时同一事务新增版本并使旧有效版本失效;注册 withdrawal_qualification_approval 消费者,驳回不影响既有有效版本。
  • 1.7 为分销码生成、注册通过/驳回、资格提交/替换/作废/停用失效补齐事务内审计;日志与审计不记录密码、完整证件号或附件内容。

2. 提现

  • 2.1 新增 tb_commission_withdrawal_request_attemptrequest_idattempt_no、金额/手续费/费率/实际到账快照、收款信息、申请级发票快照、提交人、审批实例、释放时间)及唯一约束与索引;为 tb_commission_withdrawal_request 追加 latest_attempt_idlatest_approval_instance_id、通过时间与异常标记/原因列。
  • 2.2 改造 CreateWithdrawalRequest:增加有效资格校验;事务内锁定佣金钱包、冻结金额、写尝试记录、创建审批实例(business_id = 尝试记录主键)、回填 latest_*、写钱包流水与审计;余额不足、资格无效或非本人代理均不创建申请或冻结。
  • 2.3 实现驳回后重提:仅已驳回申请可由本人代理修改金额、收款信息与本次发票;事务内先释放旧未结算尝试的冻结,再按新金额冻结并新增尝试记录与新审批实例,历史快照与审批结果不被覆盖,不产生第二笔冻结。
  • 2.4 注册 commission_withdrawal_approval 终态消费者:通过仅一次从冻结余额扣减、保持 WithdrawalStatusApproved=2 并写 paid_at;驳回与 cancelled/deleted 仅一次释放本次尝试冻结并写释放时间;revoked_after_approved 不回滚、不重新冻结、不自动重提,写正交异常标记与原因供详情展示;重复、乱序与未知结果由既有交付租约与查询恢复收敛。
  • 2.5 本地人工终审边界:internal/service/commission_withdrawal 的通过/驳回在申请已关联审批实例时拒绝并返回状态冲突,不新增任何配置开关;approval_instance_id 为空的存量申请保持既有兼容处理;错误交全局 ErrorHandler。
  • 2.6 注册资格、提现申请与提现详情的后台路由及 OpenAPI保障自身店铺与数据范围校验公开接口不得承载资格、提现或后台查询操作。

3. 审批业务类型注册点

  • 3.1 pkg/constants/approval.go:新增 agent_distribution_approvalwithdrawal_qualification_approvalcommission_withdrawal_approval 三个业务类型常量与各场景业务字段常量,注释写明 business_id 取值语义。
  • 3.2 internal/model/dto/wecom_scene_dto.go:两处 business_type enum 与中文 description 同步新增三个类型ENG-DTO-001
  • 3.3 internal/application/wecom/scene.gosceneBusinessFields 白名单、validApprovalBusinessType、业务类型中文名三处新增。
  • 3.4 新成对迁移扩展 chk_wecom_approval_scene_business 纳入三个新类型;down 在存在新类型场景行时拒绝破坏性回滚并报明确异常;不修改既有迁移,编号按实施时最大编号顺延。
  • 3.5 cmd/worker/main.go:新增三个决策 handler 的依赖装配并注册到 BusinessDecisionHandler dispatcher缺注册时按既有规则返回服务不可用
  • 3.6 internal/infrastructure/audit/approval.goapprovalBusinessResource 新增三个业务类型的资源映射(缺映射会 fail-closedpkg/constants/audit.gointernal/infrastructure/audit/registry.go 新增分销注册记录、资格资料版本、提现审批尝试记录三个审计资源(提现单资源已存在,无需新增)。
  • 3.7 运行期前置:上线前由超级管理员经 PUT /api/admin/wecom/scenes/{business_type} 为三个业务类型配置启用场景与模板控件映射;迁移不写入模板,未配置时相应提交失败关闭。待维护者执行:配置 agent_distribution_approvalwithdrawal_qualification_approvalcommission_withdrawal_approval 的启用场景与控件映射(资格 6 个附件控件对应 file_list,必填合同/法人身份证正反面、可选营业执照/门头照/发票);验收证据为 GET /api/admin/wecom/scenes 返回三类型启用记录及 control_mapping,字段清单经 GET /api/admin/wecom/scenes/{business_type}/fields 核对。本项为运行期配置数据,不是代码交付物,故未勾选。

4. 验证

  • 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 up214/215/216/217 全部成功)→ version 为 217 且 dirty=falsedown 3 → 再 up 回 217down 守卫按设计拒绝破坏性回滚(存在 tb_agent_distribution_registration 行时 000214 拒绝、存在新类型场景行时 000216 拒绝),因此整库回退只能到 000214,发布说明写明该边界。验证期间只创建、删除本 Change 自己的 fixtureshop_code LIKE 'AUG26008%'、三张新表、13 个新增动作码审计与资源行),复核残留:新表与审计残留均为 0tb_shop 无空分销码且全局唯一;未重置整库。
  • 4.2 受控验证本地状态机:以 Redis 预置验证码DB 6approval.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
  • 4.3 运行 gofmt -wgo build ./cmd/api ./cmd/workergo run cmd/gendocs/main.goopenspec validate add-agent-distribution-withdrawal-qualification --strictopenspec 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 validopenspec doctor --json"healthy": true