## Why 功能 ID:`hotfix-main-package-activation-recovery` 线上已第二次出现原主套餐成功过期、队首待生效套餐仍长期停留在 `status=0` 的故障。只读诊断确认当前孤儿扫描固定取出的 100 条记录全部仍有占位套餐,真实孤儿永远无法进入恢复窗口;同时,过期事务提交前投递 Asynq 会让消费者读到旧套餐仍为生效中并把跳过误判为成功。 ## What Changes - PostgreSQL 在 `LIMIT 100` 前完成每个载体队首选择和 `status IN (1,2)` 占位排除,删除逐条检查的 N+1 查询。 - 旧主套餐过期和加油包失效事务提交成功后,才查询队首套餐并投递现有 `package:queue:activation` Asynq 任务。 - Redis 激活锁冲突返回现有套餐激活冲突错误,让 Asynq 按既有策略重试,不再确认假成功。 - Asynq Handler 仅在实际推进套餐或确认已完成幂等事实时记录成功;占位阻塞、等待实名和锁冲突记录明确原因。 - 不新增 Outbox、数据库迁移、依赖或新任务类型,保持线上现有纯 Asynq 架构。 - 按用户明确要求,不新增、修改或运行自动化测试;使用全量构建、只读 SQL、查询计划和日志核验。 ## Capabilities ### New Capabilities 无。 ### Modified Capabilities - `package-queue-activation`:补充纯 Asynq 过期接续的提交后投递、真实孤儿公平扫描、锁冲突重试和准确成功语义。 ## Impact - **适用范围**:仅当前 `main` 线上代码。 - **架构通道**:旧套餐轮询复杂写用例,沿用 `Polling Handler → GORM transaction → Asynq → Package Activation Service`;不迁移未触碰的套餐模块。 - **代码**:`internal/polling/package_activation_handler.go`、`internal/service/package/activation_service.go`。 - **数据库**:无迁移;仅调整 `tb_package_usage` 查询顺序与过滤。 - **API/前端**:无改动。 - **依赖**:继续使用 GORM、PostgreSQL、Redis、Asynq 和 Zap,不新增依赖。 - **性能**:孤儿扫描由最多 101 次查询收敛为一次候选查询和有限任务投递;使用查询计划确认数据库耗时。 - **审计与可靠性**:Audit Event、Domain Ledger、Integration Log、Outbox 均为 N/A;`tb_package_usage` 是权威状态,现有 Asynq + 周期孤儿扫描负责最终恢复。 - **验证**:不写自动化测试;执行 `gofmt`、静态检查、`go build ./...`、只读 SQL及日志核验。