## Purpose 按原始需求(`111.md` §14.6、§14.7、§17.5)补齐代理分销与提现链路的三项留痕与展示:注册审批材料带出将同步的业务员、提现申请记录当次资料资格版本与校验结果、店铺投影返回下级代理数量。 ## MODIFIED Requirements ### Requirement: 提现冻结与企业微信终审 提现申请 SHALL 先校验申请店铺存在有效资料资格,再冻结可提现余额、金额、手续费、收款信息和可选发票快照,并创建企业微信审批。余额不足、资格无效或非本人代理时,系统 MUST NOT 创建提现申请、审批实例或任何冻结。发票为申请级材料,若上传 MUST 按当时有效合同主体校验并冻结至该次审批快照。本地 MUST NOT 提供人工通过或驳回终审。审批 MUST 使用业务类型 `commission_withdrawal_approval`。 提现申请 SHALL 记录本次提现所依据的有效资料资格版本标识与当次校验结果,校验结果至少包含校验时间、是否通过,以及未通过时的稳定原因(资格不存在、已失效、已停用或资料未通过审批)。校验通过时,该记录 MUST 随每次提交或重提冻结进当次审批尝试快照,并在提现详情可查询(此处「审批材料」指本地不可变审批尝试快照,本能力 MUST NOT 要求新增企业微信模板控件),详情返回的资格校验结果与快照一致:通过时未通过原因为空字符串;上线前产生、无资格校验留痕的历史尝试按「无留痕」返回(版本标识为 0、校验时间为空、是否通过为 0、未通过原因为空字符串)。校验未通过时,系统 MUST 按既有资料资格校验的拒绝语义处理:MUST NOT 创建提现申请、审批实例与审批尝试,因此该情形 MUST NOT 产生带未通过原因的审批尝试快照,未通过稳定原因 MUST 记录在该次拒绝的结果与审计事实中。两种情形均 MUST NOT 改写历史尝试的校验结果。该记录 MUST NOT 改变既有冻结、扣减、释放、驳回重提与通过后撤销的任何语义。 每次提交或重提 MUST 新增一条不可变的审批尝试记录,冻结该次金额、手续费、实际到账、收款信息与申请级发票快照,并为其创建独立的审批实例;提现申请行只保存最新审批实例标识用于列表投影,MUST NOT 作为历史事实来源。企业微信通过即视为已到账:系统 MUST 保持 `WithdrawalStatusApproved`(2)并在同一事务内仅一次从冻结余额扣减,同时写入到账时间;MUST NOT 使用已到账状态值 4。 企业微信驳回时,系统 MUST 仅一次释放本次尝试的冻结余额并记录释放时间;`cancelled` 与 `deleted` MUST 按驳回同等处理。驳回后代理可修改金额、收款信息和本次发票重新提交,每次新建审批尝试记录与审批实例;资料资格保持有效。企业微信对已通过申请撤销时,系统 MUST NOT 回滚已到账金额、MUST NOT 重新冻结,MUST 保持通过状态并写入正交的异常标记与原因供详情展示与人工处理,MUST NOT 自动重提。 重复、乱序或结果未知的回调 MUST NOT 造成重复扣减、重复释放或第二笔冻结;结果未知时申请保持在途并由既有查询恢复机制收敛。 #### Scenario: 资格无效或余额不足不创建提现 - **WHEN** 代理店铺不存在有效资料资格、资格已失效,或可提现余额不足 - **THEN** 系统拒绝创建提现申请,不创建审批实例且不冻结任何余额 #### Scenario: 提现审批通过 - **WHEN** 企业微信对一笔待审提现返回最终通过且该结果首次被消费 - **THEN** 系统仅一次从冻结余额扣减,申请保持已通过状态并记录到账时间 #### Scenario: 提现审批驳回 - **WHEN** 企业微信最终驳回一笔提现申请 - **THEN** 系统仅一次释放该次尝试的冻结余额并记录释放时间,保留审批快照,并允许在资格仍有效时修改后重提 #### Scenario: 驳回后重提 - **WHEN** 代理修改金额、收款信息或本次发票后重新提交被驳回的提现申请 - **THEN** 系统新增审批尝试记录并创建新的审批实例,历史尝试、快照与审批结果不被覆盖 #### Scenario: 企微取消或删除 - **WHEN** 企业微信对待审提现返回取消或删除 - **THEN** 系统按驳回同等处理,仅一次释放冻结余额并记录释放时间 #### Scenario: 通过后撤销 - **WHEN** 企业微信对已通过的提现申请返回通过后撤销 - **THEN** 系统不回滚已到账金额、不重新冻结、不自动重提,仅写入异常标记与原因供详情展示 #### Scenario: 重复回调不重复扣减 - **WHEN** 同一审批终态被重复投递或乱序到达 - **THEN** 系统至多扣减或释放一次,不产生第二笔冻结 #### Scenario: 资料校验结果可追溯 - **WHEN** 代理在有效资料资格下提交提现申请,随后该资格被替换 - **THEN** 该次提现尝试仍返回提交时冻结的资格版本标识与校验结果,不被新版本改写 #### Scenario: 资格失效时的校验原因 - **GIVEN** 代理店铺资料资格已被超级管理员作废 - **WHEN** 代理发起提现并被拒绝 - **THEN** 拒绝结果与审计记录包含稳定原因(资格已失效),且不创建提现申请、审批实例或冻结 ## ADDED Requirements ### Requirement: 注册审批材料带出业务员 代理注册审批材料 SHALL 在既有字段之外带出「业务员」,取上级代理店铺当前业务员的名称快照,用于审批人在企业微信侧确认将同步给新代理的初始业务员。上级店铺当前无业务员时该字段 MUST 以空值提交并在材料中标记为无,MUST NOT 以提交人、上级店铺主账号或其它账号填充。该字段 MUST 为只读快照:审批通过时写入新店铺的初始业务员仍按既有规则取上级业务员当时值,MUST NOT 因审批材料快照与通过时值不同而改写通过结果或回滚。企业微信模板控件缺失导致该字段无法提交时 MUST 明确失败并提示需配置控件,MUST NOT 静默丢弃。 #### Scenario: 审批材料展示将同步的业务员 - **GIVEN** 上级代理店铺存在启用的业务员 - **WHEN** 创建代理注册审批实例 - **THEN** 审批材料包含该业务员名称快照 #### Scenario: 上级无业务员 - **GIVEN** 上级代理店铺当前没有启用业务员 - **WHEN** 创建代理注册审批实例 - **THEN** 业务员字段以空值提交并标记为无,不使用其它账号填充 #### Scenario: 快照与实际同步值不同 - **GIVEN** 审批材料创建后上级店铺业务员发生变更 - **WHEN** 企业微信最终通过该注册申请 - **THEN** 新店铺初始业务员按通过时上级业务员写入,历史审批材料快照不被改写 ### Requirement: 店铺下级代理数量投影 店铺列表与详情 SHALL 返回该店铺的直接下级代理数量,口径为未删除且上级店铺标识等于该店铺的店铺数。数量 MUST 一次批量聚合完成,MUST NOT 逐店查询放大查询次数。数量 MUST 受既有店铺数据范围约束:范围外店铺按不可见处理。该字段 MUST 只读,MUST NOT 影响上下级关系、佣金关系、通知范围或任何既有写入语义。 #### Scenario: 展示下级数量 - **GIVEN** 某代理店铺存在三家直接下级店铺 - **WHEN** 查询该店铺详情或列表 - **THEN** 下级代理数量返回三 #### Scenario: 无下级返回零 - **WHEN** 查询一家没有任何直接下级的店铺 - **THEN** 下级代理数量返回零而非空值 #### Scenario: 数据范围外不泄露 - **WHEN** 平台用户查询其数据范围外店铺 - **THEN** 系统不返回该店铺及其下级数量