Files
junhong_cmp_fiber/.planning/phases/01-p0/01-03-PLAN.md

12 KiB
Raw Blame History

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
01-01
internal/service/recharge/service.go
internal/bootstrap/services.go
pkg/queue/handler.go
internal/task/auto_purchase.go
true
CRITICAL-04
CRITICAL-05
truths artifacts key_links
recharge.Service struct 有 queueClient *queue.Client 字段
HandlePaymentCallback 在充值成功后,若 LinkedPackageIDs 非空,入队 TaskTypeAutoPurchaseAfterRecharge
AutoPurchaseHandler struct 有 asynqClient *asynq.Client 字段
AutoPurchaseHandler.ProcessTask 事务提交成功后入队 TaskTypeCommission
pkg/queue/handler.go 注册了 AutoPurchaseHandlerregisterAutoPurchaseHandler 被调用)
internal/bootstrap/services.go 的 recharge.New() 传入 queueClient 参数
path provides contains
internal/service/recharge/service.go queueClient 注入HandlePaymentCallback 触发自动购包 queueClient.EnqueueTask
path provides contains
pkg/queue/handler.go registerAutoPurchaseHandler 方法和注册调用 registerAutoPurchaseHandler
path provides contains
internal/task/auto_purchase.go asynqClient 注入ProcessTask 触发佣金 TaskTypeCommission
path provides contains
internal/bootstrap/services.go recharge.New() 传入 queueClient queueClient
from to via pattern
internal/service/recharge/service.go pkg/queue/handler.go queueClient.EnqueueTask(TaskTypeAutoPurchaseAfterRecharge) TaskTypeAutoPurchaseAfterRecharge
from to via pattern
internal/task/auto_purchase.go asynq.Client h.asynqClient.EnqueueContext(ctx, commissionTask) TaskTypeCommission
修复充值回调后自动购包触发CRITICAL-04和自动购包后佣金链补全CRITICAL-05

Purpose: C 端"强充绑套餐"功能当前完全断裂——充值成功后无任何代码触发自动购包任务;即使购包完成,佣金链也不会触发。本 Plan 打通完整链路:充值回调 → 自动购包任务入队 → 执行购包 → 佣金计算任务入队。

Output:

  • recharge/service.go: 注入 queueClientHandlePaymentCallback 触发自动购包入队
  • auto_purchase.go: 注入 asynqClientProcessTask 事务成功后触发佣金入队
  • 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                          = "..."  // 佣金任务用低优先级队列
)
Task 1: recharge.Service 注入 queueClient触发自动购包CRITICAL-04 Part 1 internal/service/recharge/service.go, internal/bootstrap/services.go **步骤 1先读 `internal/service/recharge/service.go` 确认当前 struct 和 New() 参数列表。**

在 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 ./...,提交 commitper 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 ./... 编译通过

Task 2: AutoPurchaseHandler 注入 asynqClient + 注册 + 触发佣金CRITICAL-04 Part 2 + CRITICAL-05 internal/task/auto_purchase.go, pkg/queue/handler.go **步骤 1先读 `internal/task/auto_purchase.go` 确认 AutoPurchaseHandler struct 和 NewAutoPurchaseHandler 的真实参数列表。**

在 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 ./...,提交 commitper 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>

  1. go build ./... 编译通过,无错误
  2. recharge.Service.HandlePaymentCallback 在 LinkedPackageIDs 非空时触发自动购包入队
  3. AutoPurchaseHandler.ProcessTask 事务成功后触发佣金入队(事务外)
  4. AutoPurchaseHandler 在 RegisterHandlers 中注册
  5. bootstrap/services.go recharge.New() 传入 queueClient
  6. 两个独立 commit 已提交CRITICAL-04 一个CRITICAL-05 一个) </success_criteria>
完成后创建 `.planning/phases/01-p0/01-03-SUMMARY.md`,记录: - 修改文件及改动说明 - 关键代码路径(充值回调 → 入队 → 消费 → 佣金) - 编译验证结果 - CRITICAL-04 和 CRITICAL-05 完成状态