feat: 实现订单超时自动取消功能,支持钱包余额解冻和 Asynq Scheduler 统一调度
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m58s
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:
@@ -133,6 +133,16 @@ func (s *OrderStore) List(ctx context.Context, opts *store.QueryOptions, filters
|
||||
if v, ok := filters["end_time"]; ok {
|
||||
query = query.Where("created_at <= ?", v)
|
||||
}
|
||||
if v, ok := filters["is_expired"]; ok {
|
||||
isExpired, _ := v.(bool)
|
||||
if isExpired {
|
||||
// 已过期:expires_at 不为空且小于当前时间,且订单仍为待支付状态
|
||||
query = query.Where("expires_at IS NOT NULL AND expires_at <= NOW() AND payment_status = ?", model.PaymentStatusPending)
|
||||
} else {
|
||||
// 未过期:expires_at 为空或 expires_at 大于当前时间
|
||||
query = query.Where("expires_at IS NULL OR expires_at > NOW()")
|
||||
}
|
||||
}
|
||||
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
@@ -156,13 +166,17 @@ func (s *OrderStore) List(ctx context.Context, opts *store.QueryOptions, filters
|
||||
return orders, total, nil
|
||||
}
|
||||
|
||||
func (s *OrderStore) UpdatePaymentStatus(ctx context.Context, id uint, status int, paidAt *time.Time) error {
|
||||
func (s *OrderStore) UpdatePaymentStatus(ctx context.Context, id uint, status int, paidAt *time.Time, expiresAt ...*time.Time) error {
|
||||
updates := map[string]any{
|
||||
"payment_status": status,
|
||||
}
|
||||
if paidAt != nil {
|
||||
updates["paid_at"] = paidAt
|
||||
}
|
||||
// 支持可选的 expiresAt 参数,用于支付成功后清除过期时间或取消时清除过期时间
|
||||
if len(expiresAt) > 0 {
|
||||
updates["expires_at"] = expiresAt[0]
|
||||
}
|
||||
return s.db.WithContext(ctx).Model(&model.Order{}).Where("id = ?", id).Updates(updates).Error
|
||||
}
|
||||
|
||||
@@ -171,3 +185,19 @@ func (s *OrderStore) GenerateOrderNo() string {
|
||||
randomNum := rand.Intn(1000000)
|
||||
return fmt.Sprintf("ORD%s%06d", now.Format("20060102150405"), randomNum)
|
||||
}
|
||||
|
||||
// FindExpiredOrders 查询已超时的待支付订单
|
||||
// 查询条件:expires_at <= NOW() AND payment_status = 1(待支付)
|
||||
// limit 参数限制每次批量处理的数量,避免一次性加载太多数据
|
||||
func (s *OrderStore) FindExpiredOrders(ctx context.Context, limit int) ([]*model.Order, error) {
|
||||
var orders []*model.Order
|
||||
err := s.db.WithContext(ctx).
|
||||
Where("expires_at IS NOT NULL AND expires_at <= NOW() AND payment_status = ?", model.PaymentStatusPending).
|
||||
Order("expires_at ASC").
|
||||
Limit(limit).
|
||||
Find(&orders).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return orders, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user