diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index e15a96f..7fe92ef 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -12,7 +12,7 @@ - [x] **CRITICAL-01**: 实名状态常量统一 — `parseRealnameStatus` 写入值 2 改为常量 1,Model 注释统一,全链路使用 `constants.RealNameStatusVerified` - [x] **CRITICAL-02**: C 端实名校验修复 — `client_order/service.go` 实名判断改用 `constants.RealNameStatusVerified`(依赖 CRITICAL-01) -- [ ] **CRITICAL-03**: 实名激活任务断链修复 — `PollingHandler` 注入 `asynq.Client`,删除 RPush 降级方案,改用 `asynq.Client.EnqueueContext()` +- [x] **CRITICAL-03**: 实名激活任务断链修复 — `PollingHandler` 注入 `asynq.Client`,删除 RPush 降级方案,改用 `asynq.Client.EnqueueContext()` - [ ] **CRITICAL-04**: 充值回调后自动购包触发 — `recharge/service.go` 在 HandlePaymentCallback 成功后入队 `TaskTypeAutoPurchaseAfterRecharge`;注册 `AutoPurchaseHandler` - [ ] **CRITICAL-05**: 自动购包佣金链补全 — `AutoPurchaseHandler.ProcessTask()` 事务提交成功后入队 `TaskTypeCommission` - [x] **CRITICAL-06**: 代购订单金额修正 — 场景 5(代理代购下级)`sellerCostPrice` 全部 6 处逐一审查,确保使用 `operatorCostPrice`(非 `buyerCostPrice`) @@ -122,7 +122,7 @@ |--------|-------|--------| | CRITICAL-01 | Phase 1 | Complete | | CRITICAL-02 | Phase 1 | Complete | -| CRITICAL-03 | Phase 1 | Pending | +| CRITICAL-03 | Phase 1 | Complete | | CRITICAL-04 | Phase 1 | Pending | | CRITICAL-05 | Phase 1 | Pending | | CRITICAL-06 | Phase 1 | Complete | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 31c19ae..28e6404 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -35,7 +35,7 @@ Plans: - [x] 01-01-PLAN.md — CRITICAL-01/02:实名状态常量统一 + C 端实名校验修复(Wave 1) -- [ ] 01-02-PLAN.md — CRITICAL-03:实名激活任务断链(PollingHandler 注入 asynqClient)(Wave 2) +- [x] 01-02-PLAN.md — CRITICAL-03:实名激活任务断链(PollingHandler 注入 asynqClient)(Wave 2) - [ ] 01-03-PLAN.md — CRITICAL-04/05:充值回调触发自动购包 + 佣金链补全(Wave 2,parallel) - [x] 01-04-PLAN.md — CRITICAL-06:代购订单 sellerCostPrice 6 处语义审查修正(Wave 1,parallel) - [x] 01-05-PLAN.md — CRITICAL-07/08:设备导入 IMEI + 提现冻结并发校验(Wave 1,parallel) @@ -118,7 +118,7 @@ Plans: | Phase | Plans Complete | Status | Completed | |-------|----------------|--------|-----------| -| 1. P0 紧急修复 | 2/5 | In Progress| | +| 1. P0 紧急修复 | 4/5 | In Progress| | | 2. 权限/财务/实名/轮询修复 | 0/1 | Not started | - | | 3. 设备体系完善 | 0/1 | Not started | - | | 4. 退款 + 支付 + 运营修复 | 0/1 | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index 6066ba6..3c424b8 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,12 +3,12 @@ gsd_state_version: 1.0 milestone: v1.0 milestone_name: milestone status: unknown -last_updated: "2026-03-27T15:25:28.566Z" +last_updated: "2026-03-27T15:30:07.725Z" progress: total_phases: 6 completed_phases: 0 total_plans: 5 - completed_plans: 3 + completed_plans: 4 --- # Project State — 君鸿卡管系统 @@ -22,7 +22,7 @@ progress: ## Current Position Phase: 01 (p0) — EXECUTING -Plan: 2 of 5 +Plan: 3 of 5 | Field | Value | |-------|-------| | **Current Phase** | 1 — P0 紧急修复 | @@ -74,6 +74,7 @@ Progress: [ 1 ]──[ 2 ]──[ 3 ]──[ 4 ]──[ 5 ]──[ 6 ] | Phase 01-p0 P05 | 8 | 2 tasks | 3 files | | Phase 01-p0 P01 | 15 | 2 tasks | 7 files | | Phase 01-p0 P04 | 4min | 1 tasks | 1 files | +| Phase 01-p0 P02 | 3min | 1 tasks | 2 files | ## Accumulated Context diff --git a/.planning/phases/01-p0/01-02-SUMMARY.md b/.planning/phases/01-p0/01-02-SUMMARY.md new file mode 100644 index 0000000..dbcedaf --- /dev/null +++ b/.planning/phases/01-p0/01-02-SUMMARY.md @@ -0,0 +1,131 @@ +--- +phase: 01-p0 +plan: 02 +subsystem: task/polling +tags: [bug-fix, asynq, realname-activation, critical] +one_liner: "修复 PollingHandler 缺失 asynq.Client 导致实名激活任务进入 RPush 降级而非 Asynq 队列的断链问题" +dependency_graph: + requires: + - 01-01 (CRITICAL-02 实名状态常量统一) + provides: + - CRITICAL-03: PollingHandler 正确通过 Asynq 入队实名激活任务 + affects: + - internal/task/polling_handler.go + - pkg/queue/handler.go +tech_stack: + added: [] + patterns: + - "依赖注入:通过构造函数参数传入 asynq.Client" + - "asynq.Client.EnqueueContext 正式入队" +key_files: + modified: + - internal/task/polling_handler.go + - pkg/queue/handler.go +decisions: + - "删除 RPush 降级方案及 _ = task 废弃代码,直接使用 EnqueueContext 入队" + - "asynqClient 参数位置:redis 之后、gatewayClient 之前,与其他 Handler 构造函数风格一致" +metrics: + duration: "3min" + completed: "2026-03-27" + tasks: 1 + files: 2 +requirements: + - CRITICAL-03 +--- + +# Phase 01 Plan 02: PollingHandler 注入 asynq.Client 修复实名激活断链 Summary + +## Objective + +修复 CRITICAL-03:`triggerFirstRealnameActivation` 因缺少 `asynq.Client` 注入,降级使用 Redis RPush 将任务写入无消费者的列表,导致首次实名后套餐激活任务永远不被消费。 + +## What Was Built + +### 修改文件 + +**`internal/task/polling_handler.go`** + +1. `PollingHandler` struct 新增 `asynqClient *asynq.Client` 字段 +2. `NewPollingHandler` 构造函数新增 `asynqClient *asynq.Client` 参数(位置:redis 之后,gatewayClient 之前) +3. `triggerFirstRealnameActivation` 改为 `h.asynqClient.EnqueueContext(ctx, task)`,删除 RPush 降级代码和 `_ = task` + +**`pkg/queue/handler.go`** + +`registerPollingHandlers` 调用 `task.NewPollingHandler` 时补充 `h.asynqClient` 参数(Handler struct 已有此字段,直接传入) + +### 关键代码对比 + +**修改前(降级代码):** + +```go +// polling_handler.go +type PollingHandler struct { + db *gorm.DB + redis *redis.Client + // asynqClient 缺失 + gatewayClient *gateway.Client + ... +} + +// triggerFirstRealnameActivation 内 +task := asynq.NewTask(...) +activationKey := constants.RedisPollingManualQueueKey(constants.TaskTypePackageFirstActivation) +h.redis.RPush(ctx, activationKey, string(payloadBytes)) // 写入无消费者的列表 +_ = task // task 被丢弃 +``` + +**修改后(正确入队):** + +```go +// polling_handler.go +type PollingHandler struct { + db *gorm.DB + redis *redis.Client + asynqClient *asynq.Client // 新增,用于提交 Asynq 任务 + gatewayClient *gateway.Client + ... +} + +// triggerFirstRealnameActivation 内 +task := asynq.NewTask(...) +if _, err := h.asynqClient.EnqueueContext(ctx, task); err != nil { + h.logger.Warn("提交首次实名激活任务失败", ...) + continue +} +``` + +## Verification Results + +``` +✅ go build ./... — 编译通过 +✅ rg "_ = task" internal/task/polling_handler.go → PASS: _ = task 已删除 +✅ rg "asynqClient.EnqueueContext" internal/task/polling_handler.go → PASS: EnqueueContext 已存在 +✅ registerPollingHandlers 传入 h.asynqClient 参数 +``` + +## Commits + +| Task | Commit | Files | +|------|--------|-------| +| Task 1: PollingHandler 注入 asynqClient(CRITICAL-03)| `20b4b6d` | internal/task/polling_handler.go, pkg/queue/handler.go | + +## Deviations from Plan + +None - 计划执行完全按规格书执行,无偏差。 + +## CRITICAL-03 完成状态 + +✅ **CRITICAL-03 已修复** + +- PollingHandler struct 含 asynqClient 字段 ✅ +- NewPollingHandler 接受 asynqClient 参数 ✅ +- triggerFirstRealnameActivation 使用 EnqueueContext,不再 RPush ✅ +- `_ = task` 已删除 ✅ +- pkg/queue/handler.go 传入 h.asynqClient ✅ +- go build ./... 编译通过 ✅ + +## Self-Check: PASSED + +- `internal/task/polling_handler.go` — FOUND ✅ +- `pkg/queue/handler.go` — FOUND ✅ +- Commit `20b4b6d` — FOUND ✅