From f358aa0745eebab14e4848cc7b7093d63ab7ca0c Mon Sep 17 00:00:00 2001 From: danial Date: Mon, 13 Oct 2025 15:00:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(jd-cookie):=20=E5=BC=95=E5=85=A5=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E8=AE=A2=E5=8D=95=E5=8F=B7=E6=94=AF=E6=8C=81=E5=B9=B6?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E8=AE=A2=E5=8D=95=E5=88=9B=E5=BB=BA=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增用户订单号字段以区分内部订单号 - 修改订单表结构添加 user_order_id 字段及索引 - 更新 CreateOrder 接口支持用户订单号参数-重构 CreateOrder 和 GetPaymentUrl 方法返回统一结果对象 - 新增模型定义用于封装订单创建与支付结果 - 调整相关逻辑方法签名与调用方式适配新结构- 优化订单创建流程增加内部订单号生成逻辑 - 完善订单查询逻辑确保正确关联用户订单号- 更新控制器层对接新版服务接口- 升级 Cookie 状态及订单状态管理枚举类型使用 --- api/jd_cookie/v1/order.go | 41 ++-- .../jd_cookie/jd_cookie_v1_create_order.go | 7 +- .../jd_cookie/jd_cookie_v1_get_payment_url.go | 6 +- internal/dao/internal/v_1_jd_cookie_order.go | 2 + internal/logic/jd_cookie/order_create.go | 202 ++++++++++-------- internal/logic/jd_cookie/order_query.go | 45 ++-- internal/logic/jd_cookie/order_utils.go | 23 +- internal/logic/jd_cookie/rotation.go | 30 +-- internal/model/do/v_1_jd_cookie_order.go | 1 + internal/model/entity/v_1_jd_cookie_order.go | 1 + internal/model/jd_cookie.go | 35 +++ internal/service/jd_cookie.go | 5 +- tools/create_tables.go | 4 +- 13 files changed, 254 insertions(+), 148 deletions(-) create mode 100644 internal/model/jd_cookie.go diff --git a/api/jd_cookie/v1/order.go b/api/jd_cookie/v1/order.go index bec09ffd..7e5e3acf 100644 --- a/api/jd_cookie/v1/order.go +++ b/api/jd_cookie/v1/order.go @@ -28,16 +28,17 @@ type JdCookieOrderApi struct { // CreateOrderReq Create Order Request type CreateOrderReq struct { - g.Meta `path:"/jd-cookie/order/create" method:"post" summary:"Create Order" tags:"JD Order Management"` - OrderId string `json:"orderId" v:"required#订单号不能为空" dc:"订单号"` - Amount float64 `json:"amount" v:"required|min:0.01#订单金额不能为空|订单金额必须大于0" dc:"订单金额"` - Category consts.RedeemOrderCardCategory `json:"category" v:"required#商品品类不能为空" dc:"商品品类"` + g.Meta `path:"/jd-cookie/order/create" method:"post" summary:"Create Order" tags:"JD Order Management"` + UserOrderId string `json:"userOrderId" v:"required#用户订单号不能为空" dc:"用户订单号"` + Amount float64 `json:"amount" v:"required|min:0.01#订单金额不能为空|订单金额必须大于0" dc:"订单金额"` + Category consts.RedeemOrderCardCategory `json:"category" v:"required#商品品类不能为空" dc:"商品品类"` } type CreateOrderRes struct { g.Meta `mime:"application/json"` WxPayUrl string `json:"wxPayUrl" dc:"微信支付链接"` JdOrderId string `json:"jdOrderId" dc:"京东订单号"` + OrderId string `json:"orderId" dc:"内部订单号"` } // GetPaymentUrlReq Get Payment URL Request @@ -47,8 +48,10 @@ type GetPaymentUrlReq struct { } type GetPaymentUrlRes struct { - g.Meta `mime:"application/json"` - WxPayUrl string `json:"wxPayUrl" dc:"微信支付链接"` + g.Meta `mime:"application/json"` + WxPayUrl string `json:"wxPayUrl" dc:"微信支付链接"` + JdOrderId string `json:"jdOrderId" dc:"京东订单号"` + OrderId string `json:"orderId" dc:"内部订单号"` } // GetOrderStatusReq Query Order Status Request @@ -96,14 +99,24 @@ type OrderInfo struct { } type JdOrderInfo struct { - JdOrderId string `json:"jdOrderId" dc:"京东订单号"` - OrderId string `json:"orderId" dc:"关联的内部订单号"` - Amount float64 `json:"amount" dc:"订单金额"` - Category string `json:"category" dc:"商品品类"` - Status consts.JdOrderStatus `json:"status" dc:"状态:1待支付 2已支付 3已过期 4已取消"` - PaidAt string `json:"paidAt" dc:"支付时间"` - CreatedAt string `json:"createdAt" dc:"创建时间"` - UpdatedAt string `json:"updatedAt" dc:"更新时间"` + Id int64 `json:"id" dc:"主键ID"` + JdOrderId string `json:"jdOrderId" dc:"京东订单号"` + PayId string `json:"payId" dc:"支付ID"` + Amount float64 `json:"amount" dc:"订单金额"` + Category string `json:"category" dc:"商品品类"` + CookieId string `json:"cookieId" dc:"使用的Cookie ID"` + Status consts.JdOrderStatus `json:"status" dc:"状态:1待支付 2已支付 3已过期 4已取消"` + 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 diff --git a/internal/controller/jd_cookie/jd_cookie_v1_create_order.go b/internal/controller/jd_cookie/jd_cookie_v1_create_order.go index 3ca5ef7d..cb4801f7 100644 --- a/internal/controller/jd_cookie/jd_cookie_v1_create_order.go +++ b/internal/controller/jd_cookie/jd_cookie_v1_create_order.go @@ -11,14 +11,15 @@ import ( // CreateOrder 创建订单 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 { return nil, gerror.WrapCode(gcode.CodeInternalError, err, "创建订单失败") } res = &v1.CreateOrderRes{ - WxPayUrl: wxPayUrl, - JdOrderId: jdOrderId, + WxPayUrl: result.WxPayUrl, + JdOrderId: result.JdOrderId, + OrderId: result.OrderId, } return } diff --git a/internal/controller/jd_cookie/jd_cookie_v1_get_payment_url.go b/internal/controller/jd_cookie/jd_cookie_v1_get_payment_url.go index 2bf22447..6ee69d6c 100644 --- a/internal/controller/jd_cookie/jd_cookie_v1_get_payment_url.go +++ b/internal/controller/jd_cookie/jd_cookie_v1_get_payment_url.go @@ -18,13 +18,15 @@ func (c *ControllerV1) GetPaymentUrl(ctx context.Context, req *v1.GetPaymentUrlR return } - wxPayUrl, _, err := service.JdCookie().GetPaymentUrl(ctx, req.OrderId) + result, err := service.JdCookie().GetPaymentUrl(ctx, req.OrderId) if err != nil { return nil, gerror.WrapCode(gcode.CodeInternalError, err, "获取支付链接失败") } res = &v1.GetPaymentUrlRes{ - WxPayUrl: wxPayUrl, + WxPayUrl: result.WxPayUrl, + JdOrderId: result.JdOrderId, + OrderId: result.OrderId, } return } diff --git a/internal/dao/internal/v_1_jd_cookie_order.go b/internal/dao/internal/v_1_jd_cookie_order.go index 1158121f..33adc148 100644 --- a/internal/dao/internal/v_1_jd_cookie_order.go +++ b/internal/dao/internal/v_1_jd_cookie_order.go @@ -23,6 +23,7 @@ type V1JdCookieOrderDao struct { type V1JdCookieOrderColumns struct { Id string // 主键ID OrderId string // 订单号 + UserOrderId string // 用户订单号 Amount string // 订单金额 Category string // 商品品类 JdOrderId string // 关联的京东订单号 @@ -38,6 +39,7 @@ type V1JdCookieOrderColumns struct { var v1JdCookieOrderColumns = V1JdCookieOrderColumns{ Id: "id", OrderId: "order_id", + UserOrderId: "user_order_id", Amount: "amount", Category: "category", JdOrderId: "jd_order_id", diff --git a/internal/logic/jd_cookie/order_create.go b/internal/logic/jd_cookie/order_create.go index 5b7ee68a..66ed3f0e 100644 --- a/internal/logic/jd_cookie/order_create.go +++ b/internal/logic/jd_cookie/order_create.go @@ -3,116 +3,142 @@ 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, 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) - if orderId == "" { - return "", "", gerror.New("订单号不能为空") + if userOrderId == "" { + return nil, gerror.New("用户订单号不能为空") } 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 { - return "", "", gerror.Wrap(err, "检查订单是否存在失败") + return nil, gerror.Wrap(err, "检查用户订单是否存在失败") } // if existingOrder != nil { // 订单已存在,尝试获取支付链接 - return s.GetPaymentUrl(ctx, 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) + paymentResult, err := s.GetPaymentUrl(ctx, existingOrder.OrderId) 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 { - return "", "", gerror.Wrap(err, "创建订单记录失败") + return nil, gerror.Wrap(err, "创建订单记录失败") } // 更新京东订单的当前关联订单ID - _ = s.updateJdOrderCurrentOrderId(ctx, jdOrderId, orderId) + _ = s.updateJdOrderCurrentOrderId(ctx, jdOrderId, internalOrderId) // 记录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 创建新的京东订单(带重试机制) -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 triedCookies []string // 记录已尝试的Cookie @@ -122,7 +148,7 @@ func (s *sJdCookie) createNewJdOrderWithRetry(ctx context.Context, orderId strin availableCookieId, cookieErr := s.GetAvailableCookie(ctx) if cookieErr != nil { glog.Warning(ctx, "获取可用Cookie失败", g.Map{ - "orderId": orderId, + "orderId": req.OrderId, "triedCookies": len(triedCookies), "error": cookieErr, }) @@ -143,44 +169,48 @@ func (s *sJdCookie) createNewJdOrderWithRetry(ctx context.Context, orderId strin // 调用京东下单接口 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 { glog.Warning(ctx, "京东下单失败,尝试切换Cookie重试", g.Map{ - "orderId": orderId, + "orderId": req.OrderId, "cookieId": availableCookieId, "triedCookies": len(triedCookies), "error": lastErr, }) // Cookie失败,更新状态 - s.handleCookieFailure(ctx, availableCookieId, orderId) + s.handleCookieFailure(ctx, availableCookieId, req.OrderId) // 继续下一次重试 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 { glog.Error(ctx, "创建京东订单记录失败", g.Map{ "jdOrderId": jdOrderId, "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{ - "orderId": orderId, + "orderId": req.OrderId, "jdOrderId": jdOrderId, "cookieId": availableCookieId, "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{ - "orderId": orderId, + "orderId": req.OrderId, "triedCookies": triedCookies, "error": lastErr, }) - return "", "", "", gerror.Wrapf(lastErr, "创建京东订单失败,已尝试%d个Cookie", len(triedCookies)) + return nil, gerror.Wrapf(lastErr, "创建京东订单失败,已尝试%d个Cookie", len(triedCookies)) } // hasCookieBeenTried 检查Cookie是否已经尝试过 diff --git a/internal/logic/jd_cookie/order_query.go b/internal/logic/jd_cookie/order_query.go index 8edef984..d10a55b1 100644 --- a/internal/logic/jd_cookie/order_query.go +++ b/internal/logic/jd_cookie/order_query.go @@ -5,6 +5,7 @@ import ( v1 "kami/api/jd_cookie/v1" "kami/internal/consts" "kami/internal/dao" + "kami/internal/model" "kami/internal/model/entity" "kami/utility/config" @@ -14,31 +15,31 @@ import ( ) // 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 == "" { - return "", "", gerror.New("订单号不能为空") + return nil, gerror.New("订单号不能为空") } // 获取订单信息 order, err := s.getOrderByOrderId(ctx, orderId) if err != nil { - return "", "", gerror.Wrap(err, "查询订单失败") + return nil, gerror.Wrap(err, "查询订单失败") } if order == nil { - return "", "", gerror.New(consts.ErrCodeOrderNotFound) + return nil, gerror.New(consts.ErrCodeOrderNotFound) } // 获取关联的京东订单 jdOrder, err := s.getJdOrderByJdOrderId(ctx, order.JdOrderId) if err != nil { - return "", "", gerror.Wrap(err, "查询京东订单失败") + return nil, gerror.Wrap(err, "查询京东订单失败") } if jdOrder == nil { - return "", "", gerror.New(consts.ErrCodeJdOrderNotFound) + return nil, gerror.New(consts.ErrCodeJdOrderNotFound) } - jdOrderId = jdOrder.JdOrderId - wxPayUrl = jdOrder.WxPayUrl + jdOrderId := jdOrder.JdOrderId + wxPayUrl := jdOrder.WxPayUrl // 检查支付链接是否有效 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) 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.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, "") // 创建新的京东订单(带重试机制) - 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 { - return "", "", gerror.Wrap(createErr, "刷新失败且创建新订单失败") + return nil, gerror.Wrap(createErr, "刷新失败且创建新订单失败") } // 更新订单关联的京东订单ID - _ = s.updateOrderJdOrderId(ctx, orderId, newJdOrderId, newWxPayUrl) + _ = s.updateOrderJdOrderId(ctx, orderId, retryRes.JdOrderId, retryRes.WxPayUrl) // 更新京东订单的当前关联订单ID - _ = s.updateJdOrderCurrentOrderId(ctx, newJdOrderId, orderId) + _ = s.updateJdOrderCurrentOrderId(ctx, retryRes.JdOrderId, orderId) // 记录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 - wxPayUrl = newWxPayUrl + jdOrderId = retryRes.JdOrderId + wxPayUrl = retryRes.WxPayUrl } else { // 刷新成功,更新支付链接 wxPayUrl = newWxPayUrl @@ -84,7 +89,11 @@ func (s *sJdCookie) GetPaymentUrl(ctx context.Context, orderId string) (wxPayUrl // 更新订单最后请求时间 _ = s.updateOrderLastRequest(ctx, orderId) - return wxPayUrl, jdOrderId, nil + return &model.PaymentResult{ + WxPayUrl: wxPayUrl, + JdOrderId: jdOrderId, + OrderId: order.OrderId, + }, nil } // GetOrder 获取单个订单 diff --git a/internal/logic/jd_cookie/order_utils.go b/internal/logic/jd_cookie/order_utils.go index b1ded373..3aa444ad 100644 --- a/internal/logic/jd_cookie/order_utils.go +++ b/internal/logic/jd_cookie/order_utils.go @@ -29,6 +29,14 @@ func (s *sJdCookie) getOrderByOrderId(ctx context.Context, orderId string) (orde 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 根据京东订单号查询京东订单 func (s *sJdCookie) getJdOrderByJdOrderId(ctx context.Context, jdOrderId string) (jdOrder *entity.V1JdCookieJdOrder, err error) { m := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1()) @@ -65,15 +73,16 @@ func (s *sJdCookie) createJdOrderRecord(ctx context.Context, jdOrderId, payId, c } // 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()) _, err := m.Insert(&do.V1JdCookieOrder{ - OrderId: orderId, - Amount: amount, - Category: category, - JdOrderId: jdOrderId, - Status: int(consts.OrderStatusPending), - WxPayUrl: wxPayUrl, + OrderId: internalOrderId, + UserOrderId: userOrderId, + Amount: amount, + Category: category, + JdOrderId: jdOrderId, + Status: int(consts.OrderStatusPending), + WxPayUrl: wxPayUrl, }) return err } diff --git a/internal/logic/jd_cookie/rotation.go b/internal/logic/jd_cookie/rotation.go index 3fccf165..e9daab9d 100644 --- a/internal/logic/jd_cookie/rotation.go +++ b/internal/logic/jd_cookie/rotation.go @@ -101,7 +101,7 @@ func (s *sJdCookie) unlockExpiredCookies(ctx context.Context) { // ==================================================================================== // 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 == "" { return gerror.New("Cookie ID不能为空") } @@ -119,17 +119,17 @@ func (s *sJdCookie) UpdateCookieStatus(ctx context.Context, cookieId string, sta } updateData := &do.V1JdCookieAccount{ - Status: status, + Status: int(status), FailureCount: failureCount, } // 如果是暂停状态,设置暂停时间 - if status == int(consts.JdCookieStatusSuspend) { + if status == consts.JdCookieStatusSuspend { updateData.SuspendUntil = gtime.Now().Add(time.Minute * consts.JdCookieSuspendDuration) } // 如果是恢复正常状态,清除暂停时间 - if status == int(consts.JdCookieStatusNormal) { + if status == consts.JdCookieStatusNormal { 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 switch status { - case int(consts.JdCookieStatusNormal): + case consts.JdCookieStatusNormal: changeType = consts.CookieChangeTypeResume - case int(consts.JdCookieStatusSuspend): + case consts.JdCookieStatusSuspend: changeType = consts.CookieChangeTypeSuspend - case int(consts.JdCookieStatusExpired): + case consts.JdCookieStatusExpired: changeType = consts.CookieChangeTypeFail default: changeType = consts.CookieChangeTypeUpdate } - _ = s.RecordCookieHistory(ctx, cookieId, changeType, oldCookie.Status, status, "", failureCount) + _ = s.RecordCookieHistory(ctx, cookieId, changeType, oldCookie.Status, int(status), "", failureCount) } return @@ -190,7 +190,7 @@ func (s *sJdCookie) CreateJdOrder(ctx context.Context, jdOrderId, payId, cookieI } // 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 == "" { return gerror.New("京东订单号不能为空") } @@ -208,7 +208,7 @@ func (s *sJdCookie) UpdateJdOrderStatus(ctx context.Context, jdOrderId string, s } updateData := &do.V1JdCookieJdOrder{ - Status: status, + Status: int(status), } 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 switch status { - case int(consts.JdOrderStatusPaid): + case consts.JdOrderStatusPaid: changeType = consts.JdOrderChangeTypePay - case int(consts.JdOrderStatusExpired): + case consts.JdOrderStatusExpired: changeType = consts.JdOrderChangeTypeExpire - case int(consts.JdOrderStatusCanceled): + case consts.JdOrderStatusCanceled: changeType = consts.JdOrderChangeTypeInvalid default: changeType = consts.JdOrderChangeTypeReplace diff --git a/internal/model/do/v_1_jd_cookie_order.go b/internal/model/do/v_1_jd_cookie_order.go index 5c99a679..b7fce756 100644 --- a/internal/model/do/v_1_jd_cookie_order.go +++ b/internal/model/do/v_1_jd_cookie_order.go @@ -14,6 +14,7 @@ type V1JdCookieOrder struct { g.Meta `orm:"table:jd_cookie_order, do:true"` Id any // 主键ID OrderId any // 订单号 + UserOrderId any // 用户订单号 Amount any // 订单金额 Category any // 商品品类 JdOrderId any // 关联的京东订单号 diff --git a/internal/model/entity/v_1_jd_cookie_order.go b/internal/model/entity/v_1_jd_cookie_order.go index 0ba240b8..8734711a 100644 --- a/internal/model/entity/v_1_jd_cookie_order.go +++ b/internal/model/entity/v_1_jd_cookie_order.go @@ -13,6 +13,7 @@ import ( type V1JdCookieOrder struct { Id int64 `json:"id" orm:"id" description:"主键ID"` 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:"订单金额"` Category string `json:"category" orm:"category" description:"商品品类"` JdOrderId string `json:"jdOrderId" orm:"jd_order_id" description:"关联的京东订单号"` diff --git a/internal/model/jd_cookie.go b/internal/model/jd_cookie.go new file mode 100644 index 00000000..65b1f6d0 --- /dev/null +++ b/internal/model/jd_cookie.go @@ -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:"微信支付链接"` +} diff --git a/internal/service/jd_cookie.go b/internal/service/jd_cookie.go index 443b7543..f90b312b 100644 --- a/internal/service/jd_cookie.go +++ b/internal/service/jd_cookie.go @@ -9,6 +9,7 @@ import ( "context" v1 "kami/api/jd_cookie/v1" "kami/internal/consts" + "kami/internal/model" "kami/internal/model/entity" ) @@ -37,7 +38,7 @@ type ( // GetJdOrderHistoryByOrderId 根据订单ID获取所有关联的京东订单历史 GetJdOrderHistoryByOrderId(ctx context.Context, orderId string, page int, size int) (list []*v1.JdOrderHistoryInfo, total int, err error) // 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(ctx context.Context, jdOrderId string) (order *v1.JdOrderInfo, err error) // ListJdOrder 京东订单列表查询 @@ -55,7 +56,7 @@ type ( // ReleaseExpiredJdOrders 释放过期京东订单的关联(使其可以被复用) ReleaseExpiredJdOrders(ctx context.Context) error // 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(ctx context.Context, orderId string) (order *v1.OrderInfo, err error) // GetOrderStatus 查询订单状态 diff --git a/tools/create_tables.go b/tools/create_tables.go index 16ef5494..9fae6b94 100644 --- a/tools/create_tables.go +++ b/tools/create_tables.go @@ -104,7 +104,8 @@ func main() { `DROP TABLE IF EXISTS jd_cookie_order`, `CREATE TABLE jd_cookie_order ( 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 '订单金额', category varchar(50) NOT NULL COMMENT '商品品类', jd_order_id varchar(64) DEFAULT NULL COMMENT '关联的京东订单号', @@ -116,6 +117,7 @@ func main() { deleted_at datetime DEFAULT NULL COMMENT '删除时间', PRIMARY KEY (id), UNIQUE KEY uk_order_id (order_id), + KEY idx_user_order_id (user_order_id), KEY idx_status (status), KEY idx_jd_order_id (jd_order_id), KEY idx_last_request (last_request_at),