Files
kami_backend/internal/logic/jd_cookie/order_create.go
danial f358aa0745 feat(jd-cookie): 引入用户订单号支持并重构订单创建逻辑
- 新增用户订单号字段以区分内部订单号
- 修改订单表结构添加 user_order_id 字段及索引
- 更新 CreateOrder 接口支持用户订单号参数-重构 CreateOrder 和 GetPaymentUrl 方法返回统一结果对象
- 新增模型定义用于封装订单创建与支付结果
- 调整相关逻辑方法签名与调用方式适配新结构- 优化订单创建流程增加内部订单号生成逻辑
- 完善订单查询逻辑确保正确关联用户订单号- 更新控制器层对接新版服务接口- 升级 Cookie 状态及订单状态管理枚举类型使用
2025-10-13 15:00:11 +08:00

239 lines
7.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package jd_cookie
import (
"context"
"kami/internal/consts"
"kami/internal/model"
"kami/utility/utils"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gtime"
)
// CreateOrder 创建订单
func (s *sJdCookie) CreateOrder(ctx context.Context, userOrderId string, amount float64, category consts.RedeemOrderCardCategory) (result *model.CreateOrderResult, err error) {
_ = s.ReleaseExpiredJdOrders(ctx)
if userOrderId == "" {
return nil, gerror.New("用户订单号不能为空")
}
if amount <= 0 {
return nil, gerror.New("订单金额必须大于0")
}
// 检查用户订单是否已存在
existingOrder, err := s.getOrderByUserOrderId(ctx, userOrderId)
if err != nil {
return nil, gerror.Wrap(err, "检查用户订单是否存在失败")
}
//
if existingOrder != nil {
// 订单已存在,尝试获取支付链接
paymentResult, err := s.GetPaymentUrl(ctx, existingOrder.OrderId)
if err != nil {
return nil, err
}
// 转换为CreateOrderResult
return &model.CreateOrderResult{
WxPayUrl: paymentResult.WxPayUrl,
JdOrderId: paymentResult.JdOrderId,
OrderId: paymentResult.OrderId,
}, nil
}
// 优先尝试复用现有的京东订单
reusableJdOrder, err := s.findReusableJdOrder(ctx, amount, category)
if err != nil {
glog.Warning(ctx, "查找可复用京东订单失败", err)
}
// 生成内部订单ID
internalOrderId := "JD_" + utils.GenerateRandomUUID()
var cookieId, jdOrderId, wxPayUrl string
var isReused = false
if reusableJdOrder != nil {
// 尝试使用可复用的京东订单
jdOrderId = reusableJdOrder.JdOrderId
cookieId = reusableJdOrder.CookieId
wxPayUrl = reusableJdOrder.WxPayUrl
// 检查支付链接是否过期
if reusableJdOrder.WxPayExpireAt != nil && gtime.Now().After(reusableJdOrder.WxPayExpireAt) {
// 支付链接已过期,尝试刷新
newWxPayUrl, refreshErr := s.refreshPaymentUrl(ctx, jdOrderId, reusableJdOrder.PayId, cookieId, internalOrderId)
if refreshErr != nil {
glog.Warning(ctx, "刷新支付链接失败,将创建新订单", g.Map{
"jdOrderId": jdOrderId,
"error": refreshErr,
})
// 刷新失败,标记为不可复用
_ = s.UpdateJdOrderStatus(ctx, jdOrderId, consts.JdOrderStatusExpired, "刷新支付链接失败")
_ = s.RecordJdOrderHistory(ctx, jdOrderId, consts.JdOrderChangeTypeInvalid, "", reusableJdOrder.WxPayUrl)
// 记录Cookie刷新失败历史
_ = s.RecordCookieHistory(ctx, cookieId, consts.CookieChangeTypeRefreshFail, 0, 0, internalOrderId, 0)
// 清空,准备创建新订单
jdOrderId = ""
cookieId = ""
wxPayUrl = ""
} else {
wxPayUrl = newWxPayUrl
// 更新京东订单的支付链接和过期时间
_ = s.updateJdOrderPaymentUrl(ctx, jdOrderId, wxPayUrl)
isReused = true
}
} else {
isReused = true
}
if isReused {
glog.Info(ctx, "复用现有京东订单", g.Map{
"orderId": internalOrderId,
"jdOrderId": jdOrderId,
"cookieId": cookieId,
})
}
}
// 如果没有成功复用,创建新的京东订单
if jdOrderId == "" {
retryRes, err := s.createNewJdOrderWithRetry(ctx, &model.CreateNewJdOrderWithRetryReq{
OrderId: internalOrderId,
Amount: amount,
Category: category,
})
if err != nil {
return nil, err
}
jdOrderId = retryRes.JdOrderId
cookieId = retryRes.CookieId
wxPayUrl = retryRes.WxPayUrl
}
// 创建订单记录
err = s.createOrderRecord(ctx, internalOrderId, userOrderId, amount, category, jdOrderId, wxPayUrl)
if err != nil {
return nil, gerror.Wrap(err, "创建订单记录失败")
}
// 更新京东订单的当前关联订单ID
_ = s.updateJdOrderCurrentOrderId(ctx, jdOrderId, internalOrderId)
// 记录Cookie使用历史
_ = s.RecordCookieHistory(ctx, cookieId, consts.CookieChangeTypeUse, 0, 0, internalOrderId, 0)
// 记录订单创建历史
_ = s.RecordOrderHistory(ctx, internalOrderId, consts.OrderChangeTypeCreate, jdOrderId)
return &model.CreateOrderResult{
WxPayUrl: wxPayUrl,
JdOrderId: jdOrderId,
OrderId: internalOrderId,
}, nil
}
// createNewJdOrderWithRetry 创建新的京东订单(带重试机制)
func (s *sJdCookie) createNewJdOrderWithRetry(ctx context.Context, req *model.CreateNewJdOrderWithRetryReq) (res *model.CreateNewJdOrderWithRetryRes, err error) {
var lastErr error
var triedCookies []string // 记录已尝试的Cookie
// 不断尝试直到没有Cookie为止
for {
// 获取可用的Cookie
availableCookieId, cookieErr := s.GetAvailableCookie(ctx)
if cookieErr != nil {
glog.Warning(ctx, "获取可用Cookie失败", g.Map{
"orderId": req.OrderId,
"triedCookies": len(triedCookies),
"error": cookieErr,
})
lastErr = cookieErr
break // 没有可用Cookie停止重试
}
// 检查是否已经尝试过这个Cookie
if s.hasCookieBeenTried(triedCookies, availableCookieId) {
glog.Debug(ctx, "Cookie已被尝试过跳过", g.Map{
"cookieId": availableCookieId,
})
continue
}
// 记录已尝试的Cookie
triedCookies = append(triedCookies, availableCookieId)
// 调用京东下单接口
var payId string
jdOrderId, payId, wxPayUrl, lastErr := s.callJdCreateOrder(ctx, availableCookieId, req.Amount, req.Category)
if lastErr != nil {
glog.Warning(ctx, "京东下单失败尝试切换Cookie重试", g.Map{
"orderId": req.OrderId,
"cookieId": availableCookieId,
"triedCookies": len(triedCookies),
"error": lastErr,
})
// Cookie失败更新状态
s.handleCookieFailure(ctx, availableCookieId, req.OrderId)
// 继续下一次重试
continue
}
// 下单成功,创建京东订单记录
err = s.createJdOrderRecord(ctx, jdOrderId, payId, availableCookieId, req.Category, req.Amount, wxPayUrl)
if err != nil {
glog.Error(ctx, "创建京东订单记录失败", g.Map{
"jdOrderId": jdOrderId,
"error": err,
})
return nil, gerror.Wrap(err, "创建京东订单记录失败")
}
// 记录京东订单创建历史
_ = s.RecordJdOrderHistory(ctx, jdOrderId, consts.JdOrderChangeTypeCreate, req.OrderId, wxPayUrl)
glog.Info(ctx, "创建京东订单成功", g.Map{
"orderId": req.OrderId,
"jdOrderId": jdOrderId,
"cookieId": availableCookieId,
"triedCookies": len(triedCookies),
})
// 返回成功结果
return &model.CreateNewJdOrderWithRetryRes{
JdOrderId: jdOrderId,
CookieId: availableCookieId,
WxPayUrl: wxPayUrl,
}, nil
}
// 所有重试都失败了
if lastErr == nil {
lastErr = gerror.New(consts.ErrCodeCookieNotAvailable)
}
glog.Error(ctx, "创建京东订单失败所有Cookie均不可用", g.Map{
"orderId": req.OrderId,
"triedCookies": triedCookies,
"error": lastErr,
})
return nil, gerror.Wrapf(lastErr, "创建京东订单失败,已尝试%d个Cookie", len(triedCookies))
}
// hasCookieBeenTried 检查Cookie是否已经尝试过
func (s *sJdCookie) hasCookieBeenTried(triedCookies []string, cookieId string) bool {
for _, tried := range triedCookies {
if tried == cookieId {
return true
}
}
return false
}