Constraint: 在线热修前必须保存当前迭代分支全部有效代码进展 Confidence: medium Scope-risk: broad Directive: 后续修改需保持审计事件与业务事务边界一致 Tested: git diff --cached --check Not-tested: 未运行全量测试,提交用于切换分支前保存既有工作
3.5 KiB
3.5 KiB
Why
功能 ID:feature-505-package-activation-recovery
生产环境已第二次出现“原主套餐已过期、队首待生效主套餐仍停留在 status=0”的问题。只读数据证明旧孤儿扫描每轮固定读取的 100 条记录全部仍有占位主套餐,真实孤儿因此永久无法进入恢复窗口;七月迭代分支需要按其现有同步接续能力和卡观测 Outbox 架构消除同类缺陷,避免未来覆盖发布后继续复现。
What Changes
- 将孤儿恢复改为由 PostgreSQL 在限流前完成“每个载体只选队首套餐”和“排除仍有
status IN (1,2)占位主套餐”的筛选,避免固定前 100 条造成永久饥饿,并删除逐条占位检查的 N+1 查询。 - 七月分支的过期接续不再投递
package:queue:activationAsynq 任务:旧套餐过期事务提交后,直接调用现有ActivateNextPendingMainPackage推进队首套餐。 - 孤儿恢复同样直接调用
ActivateNextPendingMainPackage,复用载体级 Redis 锁、状态幂等、购买条款快照和激活事务。 - 套餐激活事务继续原子写入现有
card.observation.series.requestedOutbox,由七月分支公共 Outbox 可靠驱动后续实名、流量和网络观测;不新增第二种套餐激活事件。 - 收紧结果日志:只有实际推进套餐状态时记录激活成功,锁冲突、占位阻塞、等待实名和幂等跳过分别记录稳定原因。
- 按用户明确要求,本 Change 不新增、修改或运行自动化测试;使用全量构建、只读 SQL、查询计划和运行日志完成验证。
Capabilities
New Capabilities
无。
Modified Capabilities
package-queue-activation:将七月迭代的过期接续与孤儿恢复收口为同步应用调用,并补充真实孤儿公平扫描和既有卡观测 Outbox 一致性要求。
Impact
- 适用分支:仅
Iteration/7-11,按当前分支的同步接续与卡观测 Outbox 架构独立实施。 - 架构通道:主通道为套餐生命周期复杂写,沿用
Polling Handler → Package Activation Service → GORM transaction;辅助通道为 PostgreSQL 候选查询、Redis 载体锁和既有卡观测 Outbox。完整边界只覆盖“旧套餐过期后接续队首套餐及孤儿恢复”,不迁移购买、实名、流量、退款和停复机用例。 - 主要代码:
internal/polling/package_activation_handler.go、必要时最小调整internal/service/package/activation_service.go的结果日志;复用现有internal/infrastructure/cardobservation/series_event.go和公共 Outbox 装配。 - 数据库:不新增表、字段、索引或迁移;只调整现有
tb_package_usage查询和调用顺序。 - API/前端:无接口、DTO、路由和前端改动。
- 依赖:不新增依赖,继续使用 GORM、PostgreSQL、Redis、Zap 及七月分支已有 Outbox。
- 性能:孤儿扫描从“最多 100 条候选 + 最多 100 次占位查询”收敛为一次数据库候选查询和最多 100 次有业务意义的同步激活调用;使用
EXPLAIN (ANALYZE, BUFFERS)验证查询成本。 - 审计与可靠性:Audit Event N/A(系统自动生命周期推进);
tb_package_usage是状态权威事实;Integration Log N/A(本修复不新增外呼);复用现有card.observation.series.requestedOutbox,不新增事件类型。 - 验证:不写自动化测试;执行
gofmt、现有静态检查、go build ./...、只读 SQL、查询计划和日志核验。