Files
junhong_cmp_fiber/openspec/changes/archive/2026-09-17-add-asset-wallet-auto-renewal/tasks.md
break d52be16802
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 10m15s
feat(资产钱包自动续费): 新增全局配置、每日扫描续购与可靠复机
- 新增单行配置表 tb_asset_auto_renewal_config 与尝试记录表 tb_asset_auto_renewal_attempt(迁移 000229/000230)
- 每日按上海自然日扫描,窗口内以同一资产钱包可用余额续购当前主套餐,资金/订单/套餐/审计同一事务闭合
- 唯一键保证每资产每日至多一次尝试,占位中断由后续扫描收敛,当日不重试
- 四类失败原因向客户与店铺各投递每日至多一条站内通知,并注册通知类型与个人客户白名单
- 续费成功后按条件经 Outbox 可靠投递复机,新增恢复扫描只查询回填,不使用即发即弃调用
- 配置读写仅超级管理员与平台账号,保存记录操作者、前后值快照并登记统一审计
- tasks 7.1–7.15 全部验证通过(本机隔离 PostgreSQL/Redis,零外部渠道调用)
2026-09-17 16:39:26 +08:00

69 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 1. 配置基座
- [x] 1.1 新增单行自动续费配置表成对迁移:主键恒为 1、开关、范围全部/指定)、指定套餐集合、到期前天数(上限 90、配置版本、创建人与更新人与时间指定范围集合非空、全部范围集合为空迁移预置一行且默认关闭
- [x] 1.2 新增配置模型与读写 Store读取单行、保存时以单行事务锁串行化并在保存事务内自增配置版本
- [x] 1.3 实现配置读写接口与 DTO范围枚举校验、指定范围只能选择当前可售主套餐、保存写入操作者与前后值快照
- [x] 1.4 注册路由与路由组级门禁(仅超级管理员与平台账号),并在应用层复核账号类型;代理、企业、个人客户无入口
- [x] 1.5 新增审计动作、操作与资源常量并在统一审计注册表登记定义(含操作者、来源、主资源与事务要求)
- [x] 1.6 同步 `cmd/api/docs.go``cmd/gendocs/main.go` 占位并运行 `go run cmd/gendocs/main.go` 核对路由与文档一致
- [x] 1.7 落实变更语义:配置变更只影响后续扫描,尝试记录只保留触发时配置版本快照、不做重算;关闭开关后当日不再创建尝试或订单
## 2. 尝试记录表
- [x] 2.1 新增尝试记录表成对迁移:唯一键(资产类型、资产 ID、触发日期、状态与原因字段、复机状态与外部交互号字段、资金与订单字段、配置与窗口快照字段、客户与店铺快照字段、操作者与跨日尝试次数字段
- [x] 2.2 新增模型与常量:尝试状态(处理中/成功/失败/跳过)、失败原因(余额不足/不可续费/订单失败/复机失败)、跳过原因(人工已完成续购/人工订单在途)、复机状态(未评估/跳过/已投递/成功/失败/未知),字面量与规格一致
- [x] 2.3 实现占位写入:扫描开始时以独立短事务写入当次尝试,唯一冲突即视为当日已尝试并跳过该资产
- [x] 2.4 实现终态写入:成功在续费事务内更新;失败与跳过在独立短事务条件更新,不复用已回滚事务的连接或事务
- [x] 2.5 实现单资产失败隔离:用例内捕获失败、落终态并继续处理其余资产,仅扫描级失败返回错误交既有任务重试
## 3. 候选与资格
- [x] 3.1 新增按窗口参数化的候选查询,复用既有最终到期推算口径(含待生效主套餐顺延),不改变既有临期列表行为
- [x] 3.2 实现资格判定:独立卡与设备、最终到期剩余天数为闭区间 0 至配置天数、按上海自然日比较、只接受明确推算结果、已过期不处理
- [x] 3.3 实现当前主套餐与续购对象取法:与既有临期口径同序取第一条主套餐,续购对象为其套餐商品
- [x] 3.4 实现范围判定:全部主套餐,或当前主套餐商品在指定集合内(运行时不因后来下架而拒绝,交由续费豁免判定)
- [x] 3.5 实现跳过判定:不存在待生效主套餐为资格前置;人工已完成续购与存在未关闭(待支付)个人资产钱包主套餐订单时跳过并记跳过原因,不发通知
- [x] 3.6 复用应用层购买校验与价格策略取得可续费判定与执行时续费价(含续费豁免下架、生效零售价与成本价比较),禁止依赖 handler 层续费价实现
## 4. 执行事务
- [x] 4.1 新增每日扫描任务与调度注册(上海时区每日一次),审计上下文固定为计划任务与 Worker 来源
- [x] 4.2 执行事务内先锁资产钱包行、后锁资产载体行,锁后重读最终到期、当前主套餐、待生效主套餐、可售续费价与可用余额
- [x] 4.3 同一事务内闭合:按可用余额扣款、续购订单与明细、已支付支付记录、钱包流水、套餐使用记录(按既有规则排队)、佣金与卡观测 Outbox、统一审计、尝试记录置成功
- [x] 4.4 实现失败路径:不建订单、不扣款,按失败原因落终态并投递通知;订单失败随事务回滚后在独立短事务落失败事实
- [x] 4.5 实现条件复机:按可复机判定投递复机 Outbox条件不成立记录复机跳过且不投递、不通知
- [x] 4.6 边界遵守:不重构既有充值后自动购包实现与人工锁路径,不接线未使用的资产钱包锁常量,不统一人工路径的 Redis 锁命名空间,事务体在自有用例内实现
## 5. 通知
- [x] 5.1 注册受控通知类型与注册表定义:类别 expiry、级别 warning、接收人含账号与个人客户、允许引用资源类型、模板字段
- [x] 5.2 将新类型加入个人客户通知类型白名单的两处(通知列表与未读数范围),确保 H5 可见
- [x] 5.3 事件必带业务到期时间(类别为 expiry 时缺失会因参数非法投递失败)
- [x] 5.4 实现双接收人与幂等键:个人客户与资产所属店铺、键内嵌资产类型与资产 ID、上海自然日、原因与接收人业务员为空不阻断续费与尝试记录资产无店铺只发客户通知
- [x] 5.5 落实每日至多一条:同一资产同一自然日同一原因同一接收人只一条;复机失败沿用该次尝试的日期键,同一尝试只通知一次、不跨日新增
## 6. 复机可靠投递、恢复扫描与终态收敛
- [x] 6.1 新增导出的可复机判定与执行入口(返回成功/失败/未知分类),判定规则复用既有单一来源,不复制规则、不直接调用对跳过也返回成功的既有入口
- [x] 6.2 新增复机 Outbox 事件类型与消费者:条件认领后执行,回写尝试记录的复机状态、外部交互号与失败原因
- [x] 6.3 新增恢复扫描周期任务:只查询回填未知结果,不重复发起复机调用;仅最终失败按通知契约投递
- [x] 6.4 实现终态收敛:后续扫描先把触发日期早于当日且非终态的尝试记录收敛为中断或未知,且当日不重试
## 7. 验证
- [x] 7.1 在 `junhong_cmp_test` 与测试 Redis 库执行迁移 up/down/up只清理本 Change 自己创建的 fixture不重置测试库
- [x] 7.2 关闭总开关后扫描不产生新尝试与订单,既有记录保留
- [x] 7.3 窗口闭区间上下界与上海零点边界:剩余天数为 0 与等于配置天数进入执行,大于配置天数不处理
- [x] 7.4 已存在待生效主套餐时跳过(同时覆盖人工已完成续购与不叠加周期)
- [x] 7.5 已有当日尝试(成功/失败/跳过)时唯一冲突跳过,当日不重复尝试
- [x] 7.6 可用余额不足(含冻结占用后)记余额不足,客户与业务员各一条通知
- [x] 7.7 商品被禁用、渠道下架且不满足续费豁免、价格异常、钱包状态异常均记不可续费且不建订单不扣款
- [x] 7.8 人工成功续购后自动重读跳过,不产生第二笔订单
- [x] 7.9 人工待支付订单存在时跳过且不发送通知
- [x] 7.10 成功路径订单、支付记录、钱包流水与套餐使用记录在同一事务提交后同时可见,续费价等于同渠道人工续费价
- [x] 7.11 复机条件不成立时为跳过、无通知,续费事实不受影响
- [x] 7.12 复机本地回写失败记未知并保留外部交互号,恢复扫描回填;仅确认失败才通知
- [x] 7.13 占位后中断由次日扫描收敛为中断或未知,且当日不重试
- [x] 7.14 并发交叉验证:同一订单不重复扣款,同周期不产生两笔已支付续购
- [x] 7.15 运行 `gofmt -w``go build ./cmd/api ./cmd/worker``go run cmd/gendocs/main.go``openspec validate add-asset-wallet-auto-renewal --strict``openspec doctor --json`;自动化测试按项目决策为 N/A