- 新增用户订单号字段以区分内部订单号 - 修改订单表结构添加 user_order_id 字段及索引 - 更新 CreateOrder 接口支持用户订单号参数-重构 CreateOrder 和 GetPaymentUrl 方法返回统一结果对象 - 新增模型定义用于封装订单创建与支付结果 - 调整相关逻辑方法签名与调用方式适配新结构- 优化订单创建流程增加内部订单号生成逻辑 - 完善订单查询逻辑确保正确关联用户订单号- 更新控制器层对接新版服务接口- 升级 Cookie 状态及订单状态管理枚举类型使用
239 lines
7.1 KiB
Go
239 lines
7.1 KiB
Go
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
|
||
}
|