feat: 实现订单超时自动取消功能,支持钱包余额解冻和 Asynq Scheduler 统一调度
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m58s

- 新增 expires_at 字段和复合索引,待支付订单 30 分钟超时自动取消
- 实现 cancelOrder/unfreezeWalletForCancel 钱包余额解冻逻辑
- 创建 Asynq 定时任务(order_expire/alert_check/data_cleanup)
- 将原有 time.Ticker 轮询迁移至 Asynq Scheduler 统一调度
- 同步 delta specs 到 main specs 并归档变更
This commit is contained in:
2026-02-28 17:16:15 +08:00
parent 5bb0ff0ddf
commit e661b59bb9
35 changed files with 1157 additions and 314 deletions

View File

@@ -61,6 +61,13 @@ const (
TaskTypePackageFirstActivation = "package:first:activation" // 首次实名激活
TaskTypePackageQueueActivation = "package:queue:activation" // 主套餐排队激活
TaskTypePackageDataReset = "package:data:reset" // 套餐流量重置
// 订单超时任务类型
TaskTypeOrderExpire = "order:expire" // 订单超时自动取消
// 定时任务类型(由 Asynq Scheduler 调度)
TaskTypeAlertCheck = "alert:check" // 告警检查
TaskTypeDataCleanup = "data:cleanup" // 数据清理
)
// 用户状态常量
@@ -150,6 +157,12 @@ const (
OrderStatusCancelled = "cancelled" // 已取消
)
// 订单超时配置常量
const (
OrderExpireTimeout = 30 * time.Minute // 订单超时时间30分钟
OrderExpireBatchSize = 100 // 每次批量处理超时订单的数量上限
)
// 队列配置常量
const (
QueueCritical = "critical" // 关键任务队列

View File

@@ -32,7 +32,7 @@ const (
// 代理钱包交易子类型(当 transaction_type = "deduct" 用于订单支付时)
const (
WalletTransactionSubtypeSelfPurchase = "self_purchase" // 自购
WalletTransactionSubtypeSelfPurchase = "self_purchase" // 自购
WalletTransactionSubtypePurchaseForSubordinate = "purchase_for_subordinate" // 给下级代理购买
)

View File

@@ -68,6 +68,9 @@ func (h *Handler) RegisterHandlers() *asynq.ServeMux {
h.registerCommissionCalculationHandler()
h.registerPollingHandlers()
h.registerPackageActivationHandlers()
h.registerOrderExpireHandler()
h.registerAlertCheckHandler()
h.registerDataCleanupHandler()
h.logger.Info("所有任务处理器注册完成")
return h.mux
@@ -179,6 +182,24 @@ func (h *Handler) registerPackageActivationHandlers() {
h.logger.Info("注册排队激活任务处理器", zap.String("task_type", constants.TaskTypePackageQueueActivation))
}
func (h *Handler) registerOrderExpireHandler() {
orderExpireHandler := task.NewOrderExpireHandler(h.workerResult.Services.OrderExpirer, h.logger)
h.mux.HandleFunc(constants.TaskTypeOrderExpire, orderExpireHandler.HandleOrderExpire)
h.logger.Info("注册订单超时取消任务处理器", zap.String("task_type", constants.TaskTypeOrderExpire))
}
func (h *Handler) registerAlertCheckHandler() {
alertCheckHandler := task.NewAlertCheckHandler(h.workerResult.Services.AlertService, h.logger)
h.mux.HandleFunc(constants.TaskTypeAlertCheck, alertCheckHandler.HandleAlertCheck)
h.logger.Info("注册告警检查任务处理器", zap.String("task_type", constants.TaskTypeAlertCheck))
}
func (h *Handler) registerDataCleanupHandler() {
dataCleanupHandler := task.NewDataCleanupHandler(h.workerResult.Services.CleanupService, h.logger)
h.mux.HandleFunc(constants.TaskTypeDataCleanup, dataCleanupHandler.HandleDataCleanup)
h.logger.Info("注册数据清理任务处理器", zap.String("task_type", constants.TaskTypeDataCleanup))
}
// GetMux 获取 ServeMux用于启动 Worker 服务器)
func (h *Handler) GetMux() *asynq.ServeMux {
return h.mux

View File

@@ -1,6 +1,8 @@
package queue
import (
"context"
"github.com/break/junhong_cmp_fiber/internal/service/commission_calculation"
"github.com/break/junhong_cmp_fiber/internal/service/commission_stats"
packagepkg "github.com/break/junhong_cmp_fiber/internal/service/package"
@@ -8,6 +10,13 @@ import (
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
)
// OrderExpirer 订单超时取消接口
// 解耦 pkg/queue 与 internal/service/order 之间的循环依赖
type OrderExpirer interface {
// CancelExpiredOrders 批量取消已超时的待支付订单,返回取消数量
CancelExpiredOrders(ctx context.Context) (int, error)
}
// WorkerStores Worker 侧所有 Store 的集合
type WorkerStores struct {
IotCardImportTask *postgres.IotCardImportTaskStore
@@ -33,6 +42,7 @@ type WorkerStores struct {
// 新增代理钱包 Store
AgentWallet *postgres.AgentWalletStore
AgentWalletTransaction *postgres.AgentWalletTransactionStore
CardWallet *postgres.CardWalletStore // 卡钱包 Store用于订单取消时解冻
}
// WorkerServices Worker 侧所有 Service 的集合
@@ -44,6 +54,7 @@ type WorkerServices struct {
ResetService *packagepkg.ResetService
AlertService *pollingSvc.AlertService
CleanupService *pollingSvc.CleanupService
OrderExpirer OrderExpirer // 订单超时取消服务(接口类型,避免循环依赖)
}
// WorkerBootstrapResult Worker Bootstrap 结果