feat(jd-cookie): 引入用户订单号支持并重构订单创建逻辑

- 新增用户订单号字段以区分内部订单号
- 修改订单表结构添加 user_order_id 字段及索引
- 更新 CreateOrder 接口支持用户订单号参数-重构 CreateOrder 和 GetPaymentUrl 方法返回统一结果对象
- 新增模型定义用于封装订单创建与支付结果
- 调整相关逻辑方法签名与调用方式适配新结构- 优化订单创建流程增加内部订单号生成逻辑
- 完善订单查询逻辑确保正确关联用户订单号- 更新控制器层对接新版服务接口- 升级 Cookie 状态及订单状态管理枚举类型使用
This commit is contained in:
danial
2025-10-13 15:00:11 +08:00
parent 5b059bdb84
commit f358aa0745
13 changed files with 254 additions and 148 deletions

View File

@@ -28,16 +28,17 @@ type JdCookieOrderApi struct {
// CreateOrderReq Create Order Request // CreateOrderReq Create Order Request
type CreateOrderReq struct { type CreateOrderReq struct {
g.Meta `path:"/jd-cookie/order/create" method:"post" summary:"Create Order" tags:"JD Order Management"` g.Meta `path:"/jd-cookie/order/create" method:"post" summary:"Create Order" tags:"JD Order Management"`
OrderId string `json:"orderId" v:"required#订单号不能为空" dc:"订单号"` UserOrderId string `json:"userOrderId" v:"required#用户订单号不能为空" dc:"用户订单号"`
Amount float64 `json:"amount" v:"required|min:0.01#订单金额不能为空|订单金额必须大于0" dc:"订单金额"` Amount float64 `json:"amount" v:"required|min:0.01#订单金额不能为空|订单金额必须大于0" dc:"订单金额"`
Category consts.RedeemOrderCardCategory `json:"category" v:"required#商品品类不能为空" dc:"商品品类"` Category consts.RedeemOrderCardCategory `json:"category" v:"required#商品品类不能为空" dc:"商品品类"`
} }
type CreateOrderRes struct { type CreateOrderRes struct {
g.Meta `mime:"application/json"` g.Meta `mime:"application/json"`
WxPayUrl string `json:"wxPayUrl" dc:"微信支付链接"` WxPayUrl string `json:"wxPayUrl" dc:"微信支付链接"`
JdOrderId string `json:"jdOrderId" dc:"京东订单号"` JdOrderId string `json:"jdOrderId" dc:"京东订单号"`
OrderId string `json:"orderId" dc:"内部订单号"`
} }
// GetPaymentUrlReq Get Payment URL Request // GetPaymentUrlReq Get Payment URL Request
@@ -47,8 +48,10 @@ type GetPaymentUrlReq struct {
} }
type GetPaymentUrlRes struct { type GetPaymentUrlRes struct {
g.Meta `mime:"application/json"` g.Meta `mime:"application/json"`
WxPayUrl string `json:"wxPayUrl" dc:"微信支付链接"` WxPayUrl string `json:"wxPayUrl" dc:"微信支付链接"`
JdOrderId string `json:"jdOrderId" dc:"京东订单号"`
OrderId string `json:"orderId" dc:"内部订单号"`
} }
// GetOrderStatusReq Query Order Status Request // GetOrderStatusReq Query Order Status Request
@@ -96,14 +99,24 @@ type OrderInfo struct {
} }
type JdOrderInfo struct { type JdOrderInfo struct {
JdOrderId string `json:"jdOrderId" dc:"京东订单号"` Id int64 `json:"id" dc:"主键ID"`
OrderId string `json:"orderId" dc:"关联的内部订单号"` JdOrderId string `json:"jdOrderId" dc:"京东订单号"`
Amount float64 `json:"amount" dc:"订单金额"` PayId string `json:"payId" dc:"支付ID"`
Category string `json:"category" dc:"商品品类"` Amount float64 `json:"amount" dc:"订单金额"`
Status consts.JdOrderStatus `json:"status" dc:"状态1待支付 2已支付 3已过期 4已取消"` Category string `json:"category" dc:"商品品类"`
PaidAt string `json:"paidAt" dc:"支付时间"` CookieId string `json:"cookieId" dc:"使用的Cookie ID"`
CreatedAt string `json:"createdAt" dc:"创建时间"` Status consts.JdOrderStatus `json:"status" dc:"状态1待支付 2已支付 3已过期 4已取消"`
UpdatedAt string `json:"updatedAt" dc:"更新时间"` WxPayUrl string `json:"wxPayUrl" dc:"微信支付链接"`
WxPayExpireAt *string `json:"wxPayExpireAt" dc:"微信支付链接过期时间"`
OrderExpireAt *string `json:"orderExpireAt" dc:"订单过期时间(默认24小时)"`
CurrentOrderId int64 `json:"currentOrderId" dc:"当前关联的订单ID"`
PaidAt *string `json:"paidAt" dc:"支付完成时间"`
CardNo string `json:"cardNo" dc:"卡号"`
CardPassword string `json:"cardPassword" dc:"卡密"`
CardExtractedAt *string `json:"cardExtractedAt" dc:"卡密提取时间"`
CreatedAt *string `json:"createdAt" dc:"创建时间"`
UpdatedAt *string `json:"updatedAt" dc:"更新时间"`
DeletedAt *string `json:"deletedAt" dc:"删除时间"`
} }
// ListOrderReq Order List Query Request // ListOrderReq Order List Query Request

View File

@@ -11,14 +11,15 @@ import (
// CreateOrder 创建订单 // CreateOrder 创建订单
func (c *ControllerV1) CreateOrder(ctx context.Context, req *v1.CreateOrderReq) (res *v1.CreateOrderRes, err error) { func (c *ControllerV1) CreateOrder(ctx context.Context, req *v1.CreateOrderReq) (res *v1.CreateOrderRes, err error) {
wxPayUrl, jdOrderId, err := service.JdCookie().CreateOrder(ctx, req.OrderId, req.Amount, req.Category) result, err := service.JdCookie().CreateOrder(ctx, req.UserOrderId, req.Amount, req.Category)
if err != nil { if err != nil {
return nil, gerror.WrapCode(gcode.CodeInternalError, err, "创建订单失败") return nil, gerror.WrapCode(gcode.CodeInternalError, err, "创建订单失败")
} }
res = &v1.CreateOrderRes{ res = &v1.CreateOrderRes{
WxPayUrl: wxPayUrl, WxPayUrl: result.WxPayUrl,
JdOrderId: jdOrderId, JdOrderId: result.JdOrderId,
OrderId: result.OrderId,
} }
return return
} }

View File

@@ -18,13 +18,15 @@ func (c *ControllerV1) GetPaymentUrl(ctx context.Context, req *v1.GetPaymentUrlR
return return
} }
wxPayUrl, _, err := service.JdCookie().GetPaymentUrl(ctx, req.OrderId) result, err := service.JdCookie().GetPaymentUrl(ctx, req.OrderId)
if err != nil { if err != nil {
return nil, gerror.WrapCode(gcode.CodeInternalError, err, "获取支付链接失败") return nil, gerror.WrapCode(gcode.CodeInternalError, err, "获取支付链接失败")
} }
res = &v1.GetPaymentUrlRes{ res = &v1.GetPaymentUrlRes{
WxPayUrl: wxPayUrl, WxPayUrl: result.WxPayUrl,
JdOrderId: result.JdOrderId,
OrderId: result.OrderId,
} }
return return
} }

View File

@@ -23,6 +23,7 @@ type V1JdCookieOrderDao struct {
type V1JdCookieOrderColumns struct { type V1JdCookieOrderColumns struct {
Id string // 主键ID Id string // 主键ID
OrderId string // 订单号 OrderId string // 订单号
UserOrderId string // 用户订单号
Amount string // 订单金额 Amount string // 订单金额
Category string // 商品品类 Category string // 商品品类
JdOrderId string // 关联的京东订单号 JdOrderId string // 关联的京东订单号
@@ -38,6 +39,7 @@ type V1JdCookieOrderColumns struct {
var v1JdCookieOrderColumns = V1JdCookieOrderColumns{ var v1JdCookieOrderColumns = V1JdCookieOrderColumns{
Id: "id", Id: "id",
OrderId: "order_id", OrderId: "order_id",
UserOrderId: "user_order_id",
Amount: "amount", Amount: "amount",
Category: "category", Category: "category",
JdOrderId: "jd_order_id", JdOrderId: "jd_order_id",

View File

@@ -3,116 +3,142 @@ package jd_cookie
import ( import (
"context" "context"
"kami/internal/consts" "kami/internal/consts"
"kami/internal/model"
"kami/utility/utils"
"github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/glog" "github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gtime"
) )
// CreateOrder 创建订单 // CreateOrder 创建订单
func (s *sJdCookie) CreateOrder(ctx context.Context, orderId string, amount float64, category consts.RedeemOrderCardCategory) (wxPayUrl, jdOrderId string, err error) { func (s *sJdCookie) CreateOrder(ctx context.Context, userOrderId string, amount float64, category consts.RedeemOrderCardCategory) (result *model.CreateOrderResult, err error) {
_ = s.ReleaseExpiredJdOrders(ctx) _ = s.ReleaseExpiredJdOrders(ctx)
if orderId == "" { if userOrderId == "" {
return "", "", gerror.New("订单号不能为空") return nil, gerror.New("用户订单号不能为空")
} }
if amount <= 0 { if amount <= 0 {
return "", "", gerror.New("订单金额必须大于0") return nil, gerror.New("订单金额必须大于0")
} }
// 检查订单是否已存在 // 检查用户订单是否已存在
existingOrder, err := s.getOrderByOrderId(ctx, orderId) existingOrder, err := s.getOrderByUserOrderId(ctx, userOrderId)
if err != nil { if err != nil {
return "", "", gerror.Wrap(err, "检查订单是否存在失败") return nil, gerror.Wrap(err, "检查用户订单是否存在失败")
} }
// //
if existingOrder != nil { if existingOrder != nil {
// 订单已存在,尝试获取支付链接 // 订单已存在,尝试获取支付链接
return s.GetPaymentUrl(ctx, orderId) paymentResult, err := s.GetPaymentUrl(ctx, existingOrder.OrderId)
}
//
//// 优先尝试复用现有的京东订单
//reusableJdOrder, err := s.findReusableJdOrder(ctx, amount, category)
//if err != nil {
// glog.Warning(ctx, "查找可复用京东订单失败", err)
//}
//
var cookieId string
//var isReused bool = 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, orderId)
// if refreshErr != nil {
// glog.Warning(ctx, "刷新支付链接失败,将创建新订单", g.Map{
// "jdOrderId": jdOrderId,
// "error": refreshErr,
// })
// // 刷新失败,标记为不可复用
// _ = s.UpdateJdOrderStatus(ctx, jdOrderId, int(consts.JdOrderStatusExpired), "刷新支付链接失败")
// _ = s.RecordJdOrderHistory(ctx, jdOrderId, string(consts.JdOrderChangeTypeInvalid), "", reusableJdOrder.WxPayUrl)
//
// // 记录Cookie刷新失败历史
// _ = s.RecordCookieHistory(ctx, cookieId, string(consts.CookieChangeTypeRefreshFail), 0, 0, orderId, 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": orderId,
// "jdOrderId": jdOrderId,
// "cookieId": cookieId,
// })
// }
//}
//
// 如果没有成功复用,创建新的京东订单
if jdOrderId == "" {
jdOrderId, cookieId, wxPayUrl, err = s.createNewJdOrderWithRetry(ctx, orderId, amount, category)
if err != nil { if err != nil {
return "", "", err 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, orderId, amount, category, jdOrderId, wxPayUrl) err = s.createOrderRecord(ctx, internalOrderId, userOrderId, amount, category, jdOrderId, wxPayUrl)
if err != nil { if err != nil {
return "", "", gerror.Wrap(err, "创建订单记录失败") return nil, gerror.Wrap(err, "创建订单记录失败")
} }
// 更新京东订单的当前关联订单ID // 更新京东订单的当前关联订单ID
_ = s.updateJdOrderCurrentOrderId(ctx, jdOrderId, orderId) _ = s.updateJdOrderCurrentOrderId(ctx, jdOrderId, internalOrderId)
// 记录Cookie使用历史 // 记录Cookie使用历史
_ = s.RecordCookieHistory(ctx, cookieId, consts.CookieChangeTypeUse, 0, 0, orderId, 0) _ = s.RecordCookieHistory(ctx, cookieId, consts.CookieChangeTypeUse, 0, 0, internalOrderId, 0)
// 记录订单创建历史 // 记录订单创建历史
_ = s.RecordOrderHistory(ctx, orderId, consts.OrderChangeTypeCreate, jdOrderId) _ = s.RecordOrderHistory(ctx, internalOrderId, consts.OrderChangeTypeCreate, jdOrderId)
return wxPayUrl, jdOrderId, nil return &model.CreateOrderResult{
WxPayUrl: wxPayUrl,
JdOrderId: jdOrderId,
OrderId: internalOrderId,
}, nil
} }
// createNewJdOrderWithRetry 创建新的京东订单(带重试机制) // createNewJdOrderWithRetry 创建新的京东订单(带重试机制)
func (s *sJdCookie) createNewJdOrderWithRetry(ctx context.Context, orderId string, amount float64, category consts.RedeemOrderCardCategory) (jdOrderId, cookieId, wxPayUrl string, err error) { func (s *sJdCookie) createNewJdOrderWithRetry(ctx context.Context, req *model.CreateNewJdOrderWithRetryReq) (res *model.CreateNewJdOrderWithRetryRes, err error) {
var lastErr error var lastErr error
var triedCookies []string // 记录已尝试的Cookie var triedCookies []string // 记录已尝试的Cookie
@@ -122,7 +148,7 @@ func (s *sJdCookie) createNewJdOrderWithRetry(ctx context.Context, orderId strin
availableCookieId, cookieErr := s.GetAvailableCookie(ctx) availableCookieId, cookieErr := s.GetAvailableCookie(ctx)
if cookieErr != nil { if cookieErr != nil {
glog.Warning(ctx, "获取可用Cookie失败", g.Map{ glog.Warning(ctx, "获取可用Cookie失败", g.Map{
"orderId": orderId, "orderId": req.OrderId,
"triedCookies": len(triedCookies), "triedCookies": len(triedCookies),
"error": cookieErr, "error": cookieErr,
}) })
@@ -143,44 +169,48 @@ func (s *sJdCookie) createNewJdOrderWithRetry(ctx context.Context, orderId strin
// 调用京东下单接口 // 调用京东下单接口
var payId string var payId string
jdOrderId, payId, wxPayUrl, lastErr = s.callJdCreateOrder(ctx, availableCookieId, amount, category) jdOrderId, payId, wxPayUrl, lastErr := s.callJdCreateOrder(ctx, availableCookieId, req.Amount, req.Category)
if lastErr != nil { if lastErr != nil {
glog.Warning(ctx, "京东下单失败尝试切换Cookie重试", g.Map{ glog.Warning(ctx, "京东下单失败尝试切换Cookie重试", g.Map{
"orderId": orderId, "orderId": req.OrderId,
"cookieId": availableCookieId, "cookieId": availableCookieId,
"triedCookies": len(triedCookies), "triedCookies": len(triedCookies),
"error": lastErr, "error": lastErr,
}) })
// Cookie失败更新状态 // Cookie失败更新状态
s.handleCookieFailure(ctx, availableCookieId, orderId) s.handleCookieFailure(ctx, availableCookieId, req.OrderId)
// 继续下一次重试 // 继续下一次重试
continue continue
} }
// 下单成功,创建京东订单记录 // 下单成功,创建京东订单记录
err = s.createJdOrderRecord(ctx, jdOrderId, payId, availableCookieId, category, amount, wxPayUrl) err = s.createJdOrderRecord(ctx, jdOrderId, payId, availableCookieId, req.Category, req.Amount, wxPayUrl)
if err != nil { if err != nil {
glog.Error(ctx, "创建京东订单记录失败", g.Map{ glog.Error(ctx, "创建京东订单记录失败", g.Map{
"jdOrderId": jdOrderId, "jdOrderId": jdOrderId,
"error": err, "error": err,
}) })
return "", "", "", gerror.Wrap(err, "创建京东订单记录失败") return nil, gerror.Wrap(err, "创建京东订单记录失败")
} }
// 记录京东订单创建历史 // 记录京东订单创建历史
_ = s.RecordJdOrderHistory(ctx, jdOrderId, consts.JdOrderChangeTypeCreate, orderId, wxPayUrl) _ = s.RecordJdOrderHistory(ctx, jdOrderId, consts.JdOrderChangeTypeCreate, req.OrderId, wxPayUrl)
glog.Info(ctx, "创建京东订单成功", g.Map{ glog.Info(ctx, "创建京东订单成功", g.Map{
"orderId": orderId, "orderId": req.OrderId,
"jdOrderId": jdOrderId, "jdOrderId": jdOrderId,
"cookieId": availableCookieId, "cookieId": availableCookieId,
"triedCookies": len(triedCookies), "triedCookies": len(triedCookies),
}) })
// 返回成功结果 // 返回成功结果
return jdOrderId, availableCookieId, wxPayUrl, nil return &model.CreateNewJdOrderWithRetryRes{
JdOrderId: jdOrderId,
CookieId: availableCookieId,
WxPayUrl: wxPayUrl,
}, nil
} }
// 所有重试都失败了 // 所有重试都失败了
@@ -189,12 +219,12 @@ func (s *sJdCookie) createNewJdOrderWithRetry(ctx context.Context, orderId strin
} }
glog.Error(ctx, "创建京东订单失败所有Cookie均不可用", g.Map{ glog.Error(ctx, "创建京东订单失败所有Cookie均不可用", g.Map{
"orderId": orderId, "orderId": req.OrderId,
"triedCookies": triedCookies, "triedCookies": triedCookies,
"error": lastErr, "error": lastErr,
}) })
return "", "", "", gerror.Wrapf(lastErr, "创建京东订单失败,已尝试%d个Cookie", len(triedCookies)) return nil, gerror.Wrapf(lastErr, "创建京东订单失败,已尝试%d个Cookie", len(triedCookies))
} }
// hasCookieBeenTried 检查Cookie是否已经尝试过 // hasCookieBeenTried 检查Cookie是否已经尝试过

View File

@@ -5,6 +5,7 @@ import (
v1 "kami/api/jd_cookie/v1" v1 "kami/api/jd_cookie/v1"
"kami/internal/consts" "kami/internal/consts"
"kami/internal/dao" "kami/internal/dao"
"kami/internal/model"
"kami/internal/model/entity" "kami/internal/model/entity"
"kami/utility/config" "kami/utility/config"
@@ -14,31 +15,31 @@ import (
) )
// GetPaymentUrl 获取支付链接 // GetPaymentUrl 获取支付链接
func (s *sJdCookie) GetPaymentUrl(ctx context.Context, orderId string) (wxPayUrl, jdOrderId string, err error) { func (s *sJdCookie) GetPaymentUrl(ctx context.Context, orderId string) (result *model.PaymentResult, err error) {
if orderId == "" { if orderId == "" {
return "", "", gerror.New("订单号不能为空") return nil, gerror.New("订单号不能为空")
} }
// 获取订单信息 // 获取订单信息
order, err := s.getOrderByOrderId(ctx, orderId) order, err := s.getOrderByOrderId(ctx, orderId)
if err != nil { if err != nil {
return "", "", gerror.Wrap(err, "查询订单失败") return nil, gerror.Wrap(err, "查询订单失败")
} }
if order == nil { if order == nil {
return "", "", gerror.New(consts.ErrCodeOrderNotFound) return nil, gerror.New(consts.ErrCodeOrderNotFound)
} }
// 获取关联的京东订单 // 获取关联的京东订单
jdOrder, err := s.getJdOrderByJdOrderId(ctx, order.JdOrderId) jdOrder, err := s.getJdOrderByJdOrderId(ctx, order.JdOrderId)
if err != nil { if err != nil {
return "", "", gerror.Wrap(err, "查询京东订单失败") return nil, gerror.Wrap(err, "查询京东订单失败")
} }
if jdOrder == nil { if jdOrder == nil {
return "", "", gerror.New(consts.ErrCodeJdOrderNotFound) return nil, gerror.New(consts.ErrCodeJdOrderNotFound)
} }
jdOrderId = jdOrder.JdOrderId jdOrderId := jdOrder.JdOrderId
wxPayUrl = jdOrder.WxPayUrl wxPayUrl := jdOrder.WxPayUrl
// 检查支付链接是否有效 // 检查支付链接是否有效
if jdOrder.WxPayExpireAt != nil && gtime.Now().After(jdOrder.WxPayExpireAt) { if jdOrder.WxPayExpireAt != nil && gtime.Now().After(jdOrder.WxPayExpireAt) {
@@ -46,7 +47,7 @@ func (s *sJdCookie) GetPaymentUrl(ctx context.Context, orderId string) (wxPayUrl
newWxPayUrl, refreshErr := s.refreshPaymentUrl(ctx, jdOrder.JdOrderId, jdOrder.PayId, jdOrder.CookieId, orderId) newWxPayUrl, refreshErr := s.refreshPaymentUrl(ctx, jdOrder.JdOrderId, jdOrder.PayId, jdOrder.CookieId, orderId)
if refreshErr != nil { if refreshErr != nil {
// 刷新失败,标记旧订单为失效 // 刷新失败,标记旧订单为失效
_ = s.UpdateJdOrderStatus(ctx, jdOrder.JdOrderId, int(consts.JdOrderStatusExpired), "刷新支付链接失败") _ = s.UpdateJdOrderStatus(ctx, jdOrder.JdOrderId, consts.JdOrderStatusExpired, "刷新支付链接失败")
_ = s.RecordJdOrderHistory(ctx, jdOrder.JdOrderId, consts.JdOrderChangeTypeInvalid, orderId, jdOrder.WxPayUrl) _ = s.RecordJdOrderHistory(ctx, jdOrder.JdOrderId, consts.JdOrderChangeTypeInvalid, orderId, jdOrder.WxPayUrl)
_ = s.RecordCookieHistory(ctx, jdOrder.CookieId, consts.CookieChangeTypeRefreshFail, 0, 0, orderId, 0) _ = s.RecordCookieHistory(ctx, jdOrder.CookieId, consts.CookieChangeTypeRefreshFail, 0, 0, orderId, 0)
@@ -54,26 +55,30 @@ func (s *sJdCookie) GetPaymentUrl(ctx context.Context, orderId string) (wxPayUrl
_ = s.updateJdOrderCurrentOrderId(ctx, jdOrder.JdOrderId, "") _ = s.updateJdOrderCurrentOrderId(ctx, jdOrder.JdOrderId, "")
// 创建新的京东订单(带重试机制) // 创建新的京东订单(带重试机制)
newJdOrderId, newCookieId, newWxPayUrl, createErr := s.createNewJdOrderWithRetry(ctx, orderId, gconv.Float64(order.Amount), consts.RedeemOrderCardCategory(order.Category)) retryRes, createErr := s.createNewJdOrderWithRetry(ctx, &model.CreateNewJdOrderWithRetryReq{
OrderId: orderId,
Amount: gconv.Float64(order.Amount),
Category: consts.RedeemOrderCardCategory(order.Category),
})
if createErr != nil { if createErr != nil {
return "", "", gerror.Wrap(createErr, "刷新失败且创建新订单失败") return nil, gerror.Wrap(createErr, "刷新失败且创建新订单失败")
} }
// 更新订单关联的京东订单ID // 更新订单关联的京东订单ID
_ = s.updateOrderJdOrderId(ctx, orderId, newJdOrderId, newWxPayUrl) _ = s.updateOrderJdOrderId(ctx, orderId, retryRes.JdOrderId, retryRes.WxPayUrl)
// 更新京东订单的当前关联订单ID // 更新京东订单的当前关联订单ID
_ = s.updateJdOrderCurrentOrderId(ctx, newJdOrderId, orderId) _ = s.updateJdOrderCurrentOrderId(ctx, retryRes.JdOrderId, orderId)
// 记录Cookie使用历史 // 记录Cookie使用历史
_ = s.RecordCookieHistory(ctx, newCookieId, consts.CookieChangeTypeUse, 0, 0, orderId, 0) _ = s.RecordCookieHistory(ctx, retryRes.CookieId, consts.CookieChangeTypeUse, 0, 0, orderId, 0)
// 记录订单重新绑定历史 // 记录订单重新绑定历史
_ = s.RecordOrderHistory(ctx, orderId, consts.OrderChangeTypeRebind, newJdOrderId) _ = s.RecordOrderHistory(ctx, orderId, consts.OrderChangeTypeRebind, retryRes.JdOrderId)
// 返回新的支付信息 // 返回新的支付信息
jdOrderId = newJdOrderId jdOrderId = retryRes.JdOrderId
wxPayUrl = newWxPayUrl wxPayUrl = retryRes.WxPayUrl
} else { } else {
// 刷新成功,更新支付链接 // 刷新成功,更新支付链接
wxPayUrl = newWxPayUrl wxPayUrl = newWxPayUrl
@@ -84,7 +89,11 @@ func (s *sJdCookie) GetPaymentUrl(ctx context.Context, orderId string) (wxPayUrl
// 更新订单最后请求时间 // 更新订单最后请求时间
_ = s.updateOrderLastRequest(ctx, orderId) _ = s.updateOrderLastRequest(ctx, orderId)
return wxPayUrl, jdOrderId, nil return &model.PaymentResult{
WxPayUrl: wxPayUrl,
JdOrderId: jdOrderId,
OrderId: order.OrderId,
}, nil
} }
// GetOrder 获取单个订单 // GetOrder 获取单个订单

View File

@@ -29,6 +29,14 @@ func (s *sJdCookie) getOrderByOrderId(ctx context.Context, orderId string) (orde
return return
} }
// getOrderByUserOrderId 根据用户订单号查询订单
func (s *sJdCookie) getOrderByUserOrderId(ctx context.Context, userOrderId string) (order *entity.V1JdCookieOrder, err error) {
m := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1())
err = m.Where(dao.V1JdCookieOrder.Columns().UserOrderId, userOrderId).Scan(&order)
err = utils.HandleNoRowsError(err)
return
}
// getJdOrderByJdOrderId 根据京东订单号查询京东订单 // getJdOrderByJdOrderId 根据京东订单号查询京东订单
func (s *sJdCookie) getJdOrderByJdOrderId(ctx context.Context, jdOrderId string) (jdOrder *entity.V1JdCookieJdOrder, err error) { func (s *sJdCookie) getJdOrderByJdOrderId(ctx context.Context, jdOrderId string) (jdOrder *entity.V1JdCookieJdOrder, err error) {
m := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1()) m := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1())
@@ -65,15 +73,16 @@ func (s *sJdCookie) createJdOrderRecord(ctx context.Context, jdOrderId, payId, c
} }
// createOrderRecord 创建订单记录 // createOrderRecord 创建订单记录
func (s *sJdCookie) createOrderRecord(ctx context.Context, orderId string, amount float64, category consts.RedeemOrderCardCategory, jdOrderId, wxPayUrl string) error { func (s *sJdCookie) createOrderRecord(ctx context.Context, internalOrderId, userOrderId string, amount float64, category consts.RedeemOrderCardCategory, jdOrderId, wxPayUrl string) error {
m := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1()) m := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1())
_, err := m.Insert(&do.V1JdCookieOrder{ _, err := m.Insert(&do.V1JdCookieOrder{
OrderId: orderId, OrderId: internalOrderId,
Amount: amount, UserOrderId: userOrderId,
Category: category, Amount: amount,
JdOrderId: jdOrderId, Category: category,
Status: int(consts.OrderStatusPending), JdOrderId: jdOrderId,
WxPayUrl: wxPayUrl, Status: int(consts.OrderStatusPending),
WxPayUrl: wxPayUrl,
}) })
return err return err
} }

View File

@@ -101,7 +101,7 @@ func (s *sJdCookie) unlockExpiredCookies(ctx context.Context) {
// ==================================================================================== // ====================================================================================
// UpdateCookieStatus 更新Cookie状态 // UpdateCookieStatus 更新Cookie状态
func (s *sJdCookie) UpdateCookieStatus(ctx context.Context, cookieId string, status int, failureCount int) (err error) { func (s *sJdCookie) UpdateCookieStatus(ctx context.Context, cookieId string, status consts.JdCookieStatus, failureCount int) (err error) {
if cookieId == "" { if cookieId == "" {
return gerror.New("Cookie ID不能为空") return gerror.New("Cookie ID不能为空")
} }
@@ -119,17 +119,17 @@ func (s *sJdCookie) UpdateCookieStatus(ctx context.Context, cookieId string, sta
} }
updateData := &do.V1JdCookieAccount{ updateData := &do.V1JdCookieAccount{
Status: status, Status: int(status),
FailureCount: failureCount, FailureCount: failureCount,
} }
// 如果是暂停状态,设置暂停时间 // 如果是暂停状态,设置暂停时间
if status == int(consts.JdCookieStatusSuspend) { if status == consts.JdCookieStatusSuspend {
updateData.SuspendUntil = gtime.Now().Add(time.Minute * consts.JdCookieSuspendDuration) updateData.SuspendUntil = gtime.Now().Add(time.Minute * consts.JdCookieSuspendDuration)
} }
// 如果是恢复正常状态,清除暂停时间 // 如果是恢复正常状态,清除暂停时间
if status == int(consts.JdCookieStatusNormal) { if status == consts.JdCookieStatusNormal {
updateData.SuspendUntil = nil updateData.SuspendUntil = nil
} }
@@ -139,20 +139,20 @@ func (s *sJdCookie) UpdateCookieStatus(ctx context.Context, cookieId string, sta
} }
// 记录状态变更历史 // 记录状态变更历史
if oldCookie.Status != status || oldCookie.FailureCount != failureCount { if oldCookie.Status != int(status) || oldCookie.FailureCount != failureCount {
var changeType consts.CookieChangeType var changeType consts.CookieChangeType
switch status { switch status {
case int(consts.JdCookieStatusNormal): case consts.JdCookieStatusNormal:
changeType = consts.CookieChangeTypeResume changeType = consts.CookieChangeTypeResume
case int(consts.JdCookieStatusSuspend): case consts.JdCookieStatusSuspend:
changeType = consts.CookieChangeTypeSuspend changeType = consts.CookieChangeTypeSuspend
case int(consts.JdCookieStatusExpired): case consts.JdCookieStatusExpired:
changeType = consts.CookieChangeTypeFail changeType = consts.CookieChangeTypeFail
default: default:
changeType = consts.CookieChangeTypeUpdate changeType = consts.CookieChangeTypeUpdate
} }
_ = s.RecordCookieHistory(ctx, cookieId, changeType, oldCookie.Status, status, "", failureCount) _ = s.RecordCookieHistory(ctx, cookieId, changeType, oldCookie.Status, int(status), "", failureCount)
} }
return return
@@ -190,7 +190,7 @@ func (s *sJdCookie) CreateJdOrder(ctx context.Context, jdOrderId, payId, cookieI
} }
// UpdateJdOrderStatus 更新京东订单状态 // UpdateJdOrderStatus 更新京东订单状态
func (s *sJdCookie) UpdateJdOrderStatus(ctx context.Context, jdOrderId string, status int, wxPayUrl string) (err error) { func (s *sJdCookie) UpdateJdOrderStatus(ctx context.Context, jdOrderId string, status consts.JdOrderStatus, wxPayUrl string) (err error) {
if jdOrderId == "" { if jdOrderId == "" {
return gerror.New("京东订单号不能为空") return gerror.New("京东订单号不能为空")
} }
@@ -208,7 +208,7 @@ func (s *sJdCookie) UpdateJdOrderStatus(ctx context.Context, jdOrderId string, s
} }
updateData := &do.V1JdCookieJdOrder{ updateData := &do.V1JdCookieJdOrder{
Status: status, Status: int(status),
} }
if wxPayUrl != "" { if wxPayUrl != "" {
@@ -222,14 +222,14 @@ func (s *sJdCookie) UpdateJdOrderStatus(ctx context.Context, jdOrderId string, s
} }
// 记录状态变更历史 // 记录状态变更历史
if oldOrder.Status != status { if oldOrder.Status != int(status) {
var changeType consts.JdOrderChangeType var changeType consts.JdOrderChangeType
switch status { switch status {
case int(consts.JdOrderStatusPaid): case consts.JdOrderStatusPaid:
changeType = consts.JdOrderChangeTypePay changeType = consts.JdOrderChangeTypePay
case int(consts.JdOrderStatusExpired): case consts.JdOrderStatusExpired:
changeType = consts.JdOrderChangeTypeExpire changeType = consts.JdOrderChangeTypeExpire
case int(consts.JdOrderStatusCanceled): case consts.JdOrderStatusCanceled:
changeType = consts.JdOrderChangeTypeInvalid changeType = consts.JdOrderChangeTypeInvalid
default: default:
changeType = consts.JdOrderChangeTypeReplace changeType = consts.JdOrderChangeTypeReplace

View File

@@ -14,6 +14,7 @@ type V1JdCookieOrder struct {
g.Meta `orm:"table:jd_cookie_order, do:true"` g.Meta `orm:"table:jd_cookie_order, do:true"`
Id any // 主键ID Id any // 主键ID
OrderId any // 订单号 OrderId any // 订单号
UserOrderId any // 用户订单号
Amount any // 订单金额 Amount any // 订单金额
Category any // 商品品类 Category any // 商品品类
JdOrderId any // 关联的京东订单号 JdOrderId any // 关联的京东订单号

View File

@@ -13,6 +13,7 @@ import (
type V1JdCookieOrder struct { type V1JdCookieOrder struct {
Id int64 `json:"id" orm:"id" description:"主键ID"` Id int64 `json:"id" orm:"id" description:"主键ID"`
OrderId string `json:"orderId" orm:"order_id" description:"订单号"` OrderId string `json:"orderId" orm:"order_id" description:"订单号"`
UserOrderId string `json:"userOrderId" orm:"user_order_id" description:"用户订单号"`
Amount decimal.Decimal `json:"amount" orm:"amount" description:"订单金额"` Amount decimal.Decimal `json:"amount" orm:"amount" description:"订单金额"`
Category string `json:"category" orm:"category" description:"商品品类"` Category string `json:"category" orm:"category" description:"商品品类"`
JdOrderId string `json:"jdOrderId" orm:"jd_order_id" description:"关联的京东订单号"` JdOrderId string `json:"jdOrderId" orm:"jd_order_id" description:"关联的京东订单号"`

View File

@@ -0,0 +1,35 @@
package model
import "kami/internal/consts"
// ====================================================================================
// JD Cookie 相关模型结构体
// ====================================================================================
// CreateOrderResult 创建订单返回结果
type CreateOrderResult struct {
WxPayUrl string `json:"wxPayUrl" dc:"微信支付链接"`
JdOrderId string `json:"jdOrderId" dc:"京东订单号"`
OrderId string `json:"orderId" dc:"内部订单号"`
}
// PaymentResult 支付结果
type PaymentResult struct {
WxPayUrl string `json:"wxPayUrl" dc:"微信支付链接"`
JdOrderId string `json:"jdOrderId" dc:"京东订单号"`
OrderId string `json:"orderId" dc:"内部订单号"`
}
// CreateNewJdOrderWithRetryReq 创建新的京东订单请求参数
type CreateNewJdOrderWithRetryReq struct {
OrderId string `json:"orderId" dc:"内部订单号"`
Amount float64 `json:"amount" dc:"订单金额"`
Category consts.RedeemOrderCardCategory `json:"category" dc:"卡券类别"`
}
// CreateNewJdOrderWithRetryRes 创建新的京东订单返回结果
type CreateNewJdOrderWithRetryRes struct {
JdOrderId string `json:"jdOrderId" dc:"京东订单号"`
CookieId string `json:"cookieId" dc:"Cookie ID"`
WxPayUrl string `json:"wxPayUrl" dc:"微信支付链接"`
}

View File

@@ -9,6 +9,7 @@ import (
"context" "context"
v1 "kami/api/jd_cookie/v1" v1 "kami/api/jd_cookie/v1"
"kami/internal/consts" "kami/internal/consts"
"kami/internal/model"
"kami/internal/model/entity" "kami/internal/model/entity"
) )
@@ -37,7 +38,7 @@ type (
// GetJdOrderHistoryByOrderId 根据订单ID获取所有关联的京东订单历史 // GetJdOrderHistoryByOrderId 根据订单ID获取所有关联的京东订单历史
GetJdOrderHistoryByOrderId(ctx context.Context, orderId string, page int, size int) (list []*v1.JdOrderHistoryInfo, total int, err error) GetJdOrderHistoryByOrderId(ctx context.Context, orderId string, page int, size int) (list []*v1.JdOrderHistoryInfo, total int, err error)
// CreateOrder 创建订单 // CreateOrder 创建订单
CreateOrder(ctx context.Context, orderId string, amount float64, category consts.RedeemOrderCardCategory) (wxPayUrl string, jdOrderId string, err error) CreateOrder(ctx context.Context, userOrderId string, amount float64, category consts.RedeemOrderCardCategory) (result *model.CreateOrderResult, err error)
// GetJdOrder 获取单个京东订单 // GetJdOrder 获取单个京东订单
GetJdOrder(ctx context.Context, jdOrderId string) (order *v1.JdOrderInfo, err error) GetJdOrder(ctx context.Context, jdOrderId string) (order *v1.JdOrderInfo, err error)
// ListJdOrder 京东订单列表查询 // ListJdOrder 京东订单列表查询
@@ -55,7 +56,7 @@ type (
// ReleaseExpiredJdOrders 释放过期京东订单的关联(使其可以被复用) // ReleaseExpiredJdOrders 释放过期京东订单的关联(使其可以被复用)
ReleaseExpiredJdOrders(ctx context.Context) error ReleaseExpiredJdOrders(ctx context.Context) error
// GetPaymentUrl 获取支付链接 // GetPaymentUrl 获取支付链接
GetPaymentUrl(ctx context.Context, orderId string) (wxPayUrl string, jdOrderId string, err error) GetPaymentUrl(ctx context.Context, orderId string) (result *model.PaymentResult, err error)
// GetOrder 获取单个订单 // GetOrder 获取单个订单
GetOrder(ctx context.Context, orderId string) (order *v1.OrderInfo, err error) GetOrder(ctx context.Context, orderId string) (order *v1.OrderInfo, err error)
// GetOrderStatus 查询订单状态 // GetOrderStatus 查询订单状态

View File

@@ -104,7 +104,8 @@ func main() {
`DROP TABLE IF EXISTS jd_cookie_order`, `DROP TABLE IF EXISTS jd_cookie_order`,
`CREATE TABLE jd_cookie_order ( `CREATE TABLE jd_cookie_order (
id bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', id bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
order_id varchar(64) NOT NULL COMMENT '订单号', order_id varchar(64) NOT NULL COMMENT '内部订单号',
user_order_id varchar(64) DEFAULT NULL COMMENT '用户订单号',
amount decimal(10,2) NOT NULL COMMENT '订单金额', amount decimal(10,2) NOT NULL COMMENT '订单金额',
category varchar(50) NOT NULL COMMENT '商品品类', category varchar(50) NOT NULL COMMENT '商品品类',
jd_order_id varchar(64) DEFAULT NULL COMMENT '关联的京东订单号', jd_order_id varchar(64) DEFAULT NULL COMMENT '关联的京东订单号',
@@ -116,6 +117,7 @@ func main() {
deleted_at datetime DEFAULT NULL COMMENT '删除时间', deleted_at datetime DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (id), PRIMARY KEY (id),
UNIQUE KEY uk_order_id (order_id), UNIQUE KEY uk_order_id (order_id),
KEY idx_user_order_id (user_order_id),
KEY idx_status (status), KEY idx_status (status),
KEY idx_jd_order_id (jd_order_id), KEY idx_jd_order_id (jd_order_id),
KEY idx_last_request (last_request_at), KEY idx_last_request (last_request_at),