--- phase: 01-p0 plan: 03 subsystem: 充值回调链路 / 自动购包任务 tags: [bug-fix, queue, asynq, recharge, commission, auto-purchase] dependency_graph: requires: [01-01] provides: [充值回调→自动购包入队, 自动购包→佣金任务入队, AutoPurchaseHandler注册] affects: [internal/service/recharge, internal/task, pkg/queue] tech_stack: added: [] patterns: [asynq任务入队, 事务外触发异步任务, 依赖注入] key_files: created: [] modified: - internal/service/recharge/service.go - internal/bootstrap/services.go - internal/task/auto_purchase.go - pkg/queue/handler.go decisions: - "佣金任务在事务提交成功后入队(事务外),防止任务提交后事务回滚引发数据一致性问题" - "自动购包入队失败不影响充值成功,仅记录 Error 日志" - "AutoPurchaseHandler 的 AssetRechargeStore / AssetWalletTransactionStore 传 nil,由 NewAutoPurchaseHandler 内部按需初始化" metrics: duration: "~8 分钟" completed_date: "2026-03-27" tasks: 2 files_changed: 4 --- # Phase 01 Plan 03: 充值回调触发自动购包 + 佣金链补全 Summary 打通完整链路:充值回调 → 自动购包任务入队 → 执行购包 → 佣金计算任务入队(CRITICAL-04 + CRITICAL-05)。 ## Tasks Completed | Task | Name | Commit | Files | |------|------|--------|-------| | 1 | recharge.Service 注入 queueClient,触发自动购包(CRITICAL-04 Part 1) | 9a4d87a | service/recharge/service.go, bootstrap/services.go | | 2 | AutoPurchaseHandler 注入 asynqClient + 注册 + 触发佣金(CRITICAL-04 Part 2 + CRITICAL-05) | 030425b | task/auto_purchase.go, pkg/queue/handler.go | ## Changes Summary ### Task 1 — CRITICAL-04 Part 1 **`internal/service/recharge/service.go`** - `Service` struct 新增字段:`queueClient *queue.Client` - `New()` 函数末尾新增参数:`queueClient *queue.Client` - `HandlePaymentCallback` 事务成功后,检查 `LinkedPackageIDs` 非空时,调用 `queueClient.EnqueueTask(TaskTypeAutoPurchaseAfterRecharge)` - 入队失败不影响充值成功,仅记录 Error 日志 **`internal/bootstrap/services.go`** - `rechargeSvc.New(...)` 末尾追加 `deps.QueueClient` 参数 ### Task 2 — CRITICAL-04 Part 2 + CRITICAL-05 **`internal/task/auto_purchase.go`** - `AutoPurchaseHandler` struct 新增字段:`asynqClient *asynq.Client` - `NewAutoPurchaseHandler()` 在 `redisClient` 参数之后新增 `asynqClient *asynq.Client` - `ProcessTask` 事务前声明 `var createdOrderID uint`,事务内 `tx.Create(order)` 成功后赋值 - 事务提交成功后(事务外)入队 `TaskTypeCommission`(QueueLow,MaxRetry=3) - 使用 `sonic.Marshal` 序列化(符合项目规范) - 入队失败仅 Warn 日志,不影响购包结果 **`pkg/queue/handler.go`** - `RegisterHandlers()` 末尾调用 `h.registerAutoPurchaseHandler()` - 新增方法 `registerAutoPurchaseHandler()`: - 调用 `task.NewAutoPurchaseHandler(...)` 传入 `h.asynqClient` - `h.mux.HandleFunc(TaskTypeAutoPurchaseAfterRecharge, ...)` ## 关键代码路径 ``` 个人客户充值微信支付回调 └─ recharge.HandlePaymentCallback() ├─ 事务:更新状态、增加余额、触发一次性佣金 └─ 事务成功后: └─ queueClient.EnqueueTask(TaskTypeAutoPurchaseAfterRecharge, {recharge_id}) └─ Worker: AutoPurchaseHandler.ProcessTask() ├─ 扣款、创建订单、激活套餐(事务内) └─ 事务成功后: └─ asynqClient.EnqueueContext(TaskTypeCommission, {order_id}) └─ Worker: CommissionCalculationHandler.HandleCommissionCalculation() ``` ## Verification Results ```bash # 编译验证 go build ./... → 通过,无错误 # 入队代码存在 rg "TaskTypeAutoPurchaseAfterRecharge" internal/service/recharge/service.go → s.queueClient.EnqueueTask(ctx, constants.TaskTypeAutoPurchaseAfterRecharge, ...) rg "TaskTypeCommission" internal/task/auto_purchase.go → asynq.NewTask(constants.TaskTypeCommission, ...) # handler 注册 rg "registerAutoPurchaseHandler|TaskTypeAutoPurchaseAfterRecharge" pkg/queue/handler.go → h.registerAutoPurchaseHandler() → h.mux.HandleFunc(constants.TaskTypeAutoPurchaseAfterRecharge, ...) # bootstrap 传入 queueClient rg "rechargeSvc.New" internal/bootstrap/services.go → rechargeSvc.New(..., deps.QueueClient) ``` ## CRITICAL Requirements Status | 需求 | 状态 | 说明 | |------|------|------| | CRITICAL-04 | ✅ 完成 | 充值回调后自动购包入队 + AutoPurchaseHandler 注册 | | CRITICAL-05 | ✅ 完成 | 自动购包成功后佣金链触发(事务外入队) | ## Deviations from Plan None — 按计划执行,无偏差。 ## Known Stubs None — 所有代码路径均已实现,无占位符。 ## Self-Check: PASSED - [x] `internal/service/recharge/service.go` 存在 ✓ - [x] `internal/bootstrap/services.go` 存在 ✓ - [x] `internal/task/auto_purchase.go` 存在 ✓ - [x] `pkg/queue/handler.go` 存在 ✓ - [x] commit 9a4d87a 存在 ✓ - [x] commit 030425b 存在 ✓ - [x] `go build ./...` 通过 ✓