12 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-p0 | 03 | execute | 2 |
|
|
true |
|
|
Purpose: C 端"强充绑套餐"功能当前完全断裂——充值成功后无任何代码触发自动购包任务;即使购包完成,佣金链也不会触发。本 Plan 打通完整链路:充值回调 → 自动购包任务入队 → 执行购包 → 佣金计算任务入队。
Output:
- recharge/service.go: 注入 queueClient,HandlePaymentCallback 触发自动购包入队
- auto_purchase.go: 注入 asynqClient,ProcessTask 事务成功后触发佣金入队
- pkg/queue/handler.go: 注册 AutoPurchaseHandler
- bootstrap/services.go: recharge.New() 传入 queueClient
<execution_context> @$HOME/.config/opencode/get-shit-done/workflows/execute-plan.md @$HOME/.config/opencode/get-shit-done/templates/summary.md </execution_context>
@.planning/phases/01-p0/01-01-SUMMARY.md @.planning/phases/01-p0/01-CONTEXT.md修复规格书(方案 A-4,第 301-575 行)
@.sisyphus/plans/修正业务-完整方案.md
// internal/service/recharge/service.go(当前,无 queueClient)
type Service struct {
db *gorm.DB
// ... 其他字段
}
func New(db *gorm.DB, /* ... */) *Service {
return &Service{db: db, /* ... */}
}
const (
TaskTypeAutoPurchaseAfterRecharge = "task:auto_purchase_after_recharge"
TaskTypeCommission = "..." // 已存在,从 pkg/constants/constants.go 确认
QueueDefault = "..."
QueueLow = "..." // 佣金任务用低优先级队列
)
在 Service struct 新增字段:
type Service struct {
db *gorm.DB
// ... 其余字段不变 ...
queueClient *queue.Client // 新增
}
在 New() 新增参数(位于最后):
func New(
db *gorm.DB,
// ... 其余参数 ...
queueClient *queue.Client, // 新增
) *Service {
return &Service{
// ... 其余字段 ...
queueClient: queueClient,
}
}
确认 import 中有 "pkgpath/pkg/queue"(按实际包路径补充)。
步骤 2:在 HandlePaymentCallback 充值成功后添加自动购包入队(第 272 行附近)
在事务成功返回、记录充值成功日志之前,添加:
// 充值成功后,如有关联套餐则触发自动购包(强充绑套餐功能)
linkedIDs := recharge.LinkedPackageIDs
if len(linkedIDs) > 0 && string(linkedIDs) != "[]" && string(linkedIDs) != "null" {
if s.queueClient != nil {
taskPayload := task.AutoPurchasePayload{RechargeRecordID: recharge.ID}
if err := s.queueClient.EnqueueTask(ctx, constants.TaskTypeAutoPurchaseAfterRecharge, taskPayload,
asynq.Queue(constants.QueueDefault),
asynq.MaxRetry(3),
); err != nil {
s.logger.Error("自动购包任务入队失败",
zap.Uint("recharge_id", recharge.ID),
zap.Error(err))
// 不影响充值成功,仅记录日志
}
}
}
步骤 3:修改 internal/bootstrap/services.go
找到 recharge.New(...) 调用位置(执行 rg "recharge.New\|recharge\.New" internal/bootstrap/),在参数列表末尾补充 deps.QueueClient:
rechargeSvc := recharge.New(
deps.DB,
// ... 其他已有参数 ...
deps.QueueClient, // 新增
)
QueueClient 已在 dependencies.go:24 定义,无需新增字段。
完成后 go build ./...,提交 commit(per D-05)。
go build ./... && rg "queueClient" internal/service/recharge/service.go | grep -c "queueClient" | grep -v "^0$" && echo "PASS: queueClient 已注入" || echo "FAIL"
- recharge.Service struct 含 queueClient 字段
- HandlePaymentCallback 在充值成功、LinkedPackageIDs 非空时触发入队
- bootstrap/services.go 的 recharge.New() 传入 deps.QueueClient
- go build ./... 编译通过
在 AutoPurchaseHandler struct 新增字段:
type AutoPurchaseHandler struct {
// ... 已有字段 ...
asynqClient *asynq.Client // 新增,用于事务后触发佣金任务
}
在 NewAutoPurchaseHandler 函数签名中新增 asynqClient *asynq.Client 参数(位于 logger 参数之前),并在返回时赋值 asynqClient: asynqClient。
⚠️ 重要: 不要盲目照搬规格书的 nil 列表。必须按实际签名决定在哪里插入 asynqClient。
步骤 2:修改 ProcessTask,事务成功后触发佣金(CRITICAL-05)
在 ProcessTask() 函数中:
① 在事务前声明 var createdOrderID uint
② 在事务内创建 order 后赋值:createdOrderID = order.ID
③ 事务提交成功 return nil 后,添加佣金任务入队:
// 事务提交成功后,触发佣金计算(不在事务内,防止任务提交后事务回滚的数据一致性问题)
if h.asynqClient != nil && createdOrderID > 0 {
payloadBytes, err := sonic.Marshal(map[string]any{"order_id": createdOrderID})
if err != nil {
h.logger.Warn("佣金任务载荷序列化失败",
zap.Uint("order_id", createdOrderID),
zap.Error(err))
} else {
commissionTask := asynq.NewTask(constants.TaskTypeCommission, payloadBytes,
asynq.Queue(constants.QueueLow),
asynq.MaxRetry(3),
)
if _, err := h.asynqClient.EnqueueContext(ctx, commissionTask); err != nil {
h.logger.Warn("自动购包后提交佣金任务失败",
zap.Uint("order_id", createdOrderID),
zap.Error(err))
}
}
}
注意:使用 sonic.Marshal(项目规范,不用 encoding/json)。
步骤 3:修改 pkg/queue/handler.go 注册 AutoPurchaseHandler
新增方法 registerAutoPurchaseHandler(),并在 RegisterHandlers() 中调用:
func (h *Handler) registerAutoPurchaseHandler() {
autoPurchaseHandler := task.NewAutoPurchaseHandler(
// 按 auto_purchase.go 实际签名传参
// 在合适位置传 h.asynqClient
// 其他 store 参数若已有传参规律则沿用;若为 nil 则查看其他 handler 的注册方式
)
h.mux.HandleFunc(constants.TaskTypeAutoPurchaseAfterRecharge, autoPurchaseHandler.ProcessTask)
h.logger.Info("注册自动购包任务处理器", zap.String("task_type", constants.TaskTypeAutoPurchaseAfterRecharge))
}
在 RegisterHandlers() 中调用:h.registerAutoPurchaseHandler()
参考现有 handler 注册:先读 pkg/queue/handler.go 中其他 register 方法(如 registerCommissionHandler)确认 store 参数传递规律。
完成后 go build ./...,提交 commit(per D-05)。
go build ./... && rg "TaskTypeAutoPurchaseAfterRecharge" pkg/queue/handler.go | grep "HandleFunc" && echo "PASS: AutoPurchaseHandler 已注册" || echo "FAIL: 未注册"
- AutoPurchaseHandler struct 含 asynqClient 字段
- ProcessTask 事务成功后入队 TaskTypeCommission(不在事务内)
- pkg/queue/handler.go 注册了 AutoPurchaseHandler
- RegisterHandlers() 调用了 registerAutoPurchaseHandler()
- go build ./... 编译通过
# 1. 编译验证
go build ./...
# 2. 确认入队代码存在
rg "TaskTypeAutoPurchaseAfterRecharge" internal/service/recharge/service.go
rg "TaskTypeCommission" internal/task/auto_purchase.go
rg "registerAutoPurchaseHandler\|TaskTypeAutoPurchaseAfterRecharge" pkg/queue/handler.go
# 3. 确认 bootstrap 传入了 queueClient
rg "recharge.New\|recharge\.New" internal/bootstrap/services.go
人工验收(参见修正业务-完整方案.md A-4 验收清单):
- DBHub 验证顺序:tb_asset_recharge_record(auto_purchase_status=success) → tb_order(自动购包订单) → tb_package_usage(套餐激活) → tb_commission_record(佣金记录)
<success_criteria>
go build ./...编译通过,无错误- recharge.Service.HandlePaymentCallback 在 LinkedPackageIDs 非空时触发自动购包入队
- AutoPurchaseHandler.ProcessTask 事务成功后触发佣金入队(事务外)
- AutoPurchaseHandler 在 RegisterHandlers 中注册
- bootstrap/services.go recharge.New() 传入 queueClient
- 两个独立 commit 已提交(CRITICAL-04 一个,CRITICAL-05 一个) </success_criteria>