Files
kami_backend/internal/logic/jd_cookie/order_query.go
danial 2253dc739a feat(jd-cookie):优化订单创建逻辑与状态管理- 新增订单状态 OrderStatusJDOrderFailed用于标识京东订单获取失败
- 新增订单变更类型 OrderChangeTypeJDOrderFailed 用于记录下单失败事件
- 调整订单创建逻辑,支持失败订单重试机制
- 新增 RecordOrderHistoryReq 结构体统一记录订单变更历史参数
- 修改数据库表结构,优化字段类型和索引
- 更新订单创建逻辑,分离本地订单与京东订单创建流程- 增加失败订单重新创建京东订单的处理逻辑
- 调整订单状态检查逻辑,支持更多状态处理
-优化订单历史记录方式,增加备注信息支持
- 更新数据库字符集为 utf8mb4_unicode_ci 提升兼容性
2025-10-18 23:41:31 +08:00

289 lines
8.5 KiB
Go

package jd_cookie
import (
"context"
v1 "kami/api/jd_cookie/v1"
"kami/internal/consts"
"kami/internal/dao"
"kami/internal/model"
"kami/internal/model/entity"
"kami/utility/config"
"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"
"github.com/gogf/gf/v2/util/gconv"
)
// GetPaymentUrl 获取支付链接
func (s *sJdCookie) GetPaymentUrl(ctx context.Context, userOrderId, orderId string) (result *model.PaymentResult, err error) {
if orderId == "" {
return nil, gerror.New("订单号不能为空")
}
// 获取订单信息
order, err := s.getOrderByOrderId(ctx, orderId)
if err != nil {
return nil, gerror.Wrap(err, "查询订单失败")
}
if order == nil {
return nil, gerror.New(consts.ErrCodeOrderNotFound)
}
// 检查订单状态,如果是失败状态,尝试重新创建京东订单
orderStatus := consts.OrderStatus(order.Status)
if orderStatus == consts.OrderStatusJDOrderFailed {
glog.Info(ctx, "检测到失败订单,尝试重新创建京东订单", g.Map{
"orderId": orderId,
"userOrderId": userOrderId,
"status": orderStatus,
})
// 尝试重新创建京东订单
retryResult, retryErr := s.retryCreateJdOrderForFailedOrder(ctx, order, &model.CreateOrderReq{
UserOrderId: userOrderId,
Amount: gconv.Float64(order.Amount),
Category: consts.RedeemOrderCardCategory(order.Category),
})
if retryErr != nil {
// 重试失败,返回错误
return nil, retryErr
}
// 重试成功,返回新的支付信息
return &model.PaymentResult{
WxPayUrl: retryResult.WxPayUrl,
JdOrderId: retryResult.JdOrderId,
OrderId: retryResult.OrderId,
}, nil
}
// 获取关联的京东订单
jdOrder, err := s.getJdOrderByJdOrderId(ctx, order.JdOrderId)
if err != nil {
return nil, gerror.Wrap(err, "查询京东订单失败")
}
if jdOrder == nil {
return nil, gerror.New(consts.ErrCodeJdOrderNotFound)
}
jdOrderId := jdOrder.JdOrderId
wxPayUrl := jdOrder.WxPayUrl
// 检查支付链接是否有效
if jdOrder.WxPayExpireAt != nil && gtime.Now().After(jdOrder.WxPayExpireAt) {
// 刷新京东订单
newWxPayUrl, isCkFailed, refreshErr := s.refreshPaymentUrl(ctx, &model.RefreshPaymentUrlReq{
JdOrderId: jdOrder.JdOrderId,
PayId: jdOrder.PayId,
CookieId: jdOrder.CookieId,
})
if isCkFailed {
s.handleCookieFailure(ctx, userOrderId, jdOrder.CookieId, jdOrder.JdOrderId, isCkFailed, refreshErr.Error())
}
if refreshErr != nil {
// 刷新失败,标记旧订单为失效
_ = s.UpdateJdOrderStatus(ctx, jdOrder.JdOrderId, consts.JdOrderStatusExpired, jdOrder.WxPayUrl, refreshErr.Error())
_ = s.RecordCookieHistory(ctx, &model.RecordCookieHistoryReq{
CookieId: jdOrder.CookieId,
ChangeType: consts.CookieChangeTypeRefreshFail,
StatusBefore: consts.JdCookieStatusUnknown,
StatusAfter: consts.JdCookieStatusExpired,
OrderId: orderId,
UserOrderId: userOrderId,
FailureCount: 0,
Remark: "刷新支付链接失败",
})
// 解绑旧京东订单
_ = s.updateJdOrderId(ctx, jdOrder.JdOrderId, "")
// 创建新的京东订单(带重试机制)
retryRes, createErr := s.createNewJdOrderWithRetry(ctx, &model.CreateNewJdOrderWithRetryReq{
OrderId: orderId,
UserOrderId: userOrderId,
Amount: gconv.Float64(order.Amount),
Category: consts.RedeemOrderCardCategory(order.Category),
})
if createErr != nil {
return nil, gerror.Wrap(createErr, "刷新失败且创建新订单失败")
}
// 更新订单关联的京东订单ID
_ = s.updateOrderJdOrderId(ctx, orderId, retryRes.JdOrderId)
// 京东订单创建成功,更新本地订单状态为待支付,并关联京东订单
err = s.updateOrderSuccess(ctx, orderId, retryRes.JdOrderId)
if err != nil {
glog.Error(ctx, "更新订单状态失败", g.Map{
"orderId": orderId,
"jdOrderId": jdOrderId,
"error": err,
})
return nil, gerror.Wrap(err, "更新订单信息失败")
}
// 更新京东订单的当前关联订单ID
_ = s.updateJdOrderId(ctx, retryRes.JdOrderId, orderId)
// 记录订单重新绑定历史
go func() {
_ = s.RecordOrderHistory(ctx, &model.RecordOrderHistoryReq{
OrderId: orderId,
ChangeType: consts.OrderChangeTypeRebind,
JdOrderId: retryRes.JdOrderId,
Remark: "",
})
// 记录Cookie使用历史
_ = s.RecordCookieHistory(ctx, &model.RecordCookieHistoryReq{
CookieId: retryRes.CookieId,
UserOrderId: userOrderId,
ChangeType: consts.CookieChangeTypeUse,
StatusBefore: consts.JdCookieStatusNormal,
StatusAfter: consts.JdCookieStatusNormal,
OrderId: orderId,
FailureCount: 0,
})
}()
// 返回新的支付信息
jdOrderId = retryRes.JdOrderId
wxPayUrl = retryRes.WxPayUrl
} else {
// 刷新成功,更新支付链接
wxPayUrl = newWxPayUrl
_ = s.updateJdOrderPaymentUrl(ctx, jdOrderId, wxPayUrl)
}
}
// 更新订单最后请求时间
_ = s.updateOrderLastRequest(ctx, orderId)
return &model.PaymentResult{
WxPayUrl: wxPayUrl,
JdOrderId: jdOrderId,
OrderId: order.OrderId,
}, nil
}
// GetOrder 获取单个订单
func (s *sJdCookie) GetOrder(ctx context.Context, orderId string) (order *v1.OrderInfo, err error) {
if orderId == "" {
return nil, gerror.New("订单号不能为空")
}
orderEntity, err := s.getOrderByOrderId(ctx, orderId)
if err != nil {
return nil, gerror.Wrap(err, "查询订单失败")
}
if orderEntity == nil {
return nil, gerror.New(consts.ErrCodeOrderNotFound)
}
order = &v1.OrderInfo{
Id: orderEntity.Id,
OrderId: orderEntity.OrderId,
UserOrderId: orderEntity.UserOrderId,
Amount: gconv.Float64(orderEntity.Amount),
Category: orderEntity.Category,
JdOrderId: orderEntity.JdOrderId,
Status: consts.OrderStatus(orderEntity.Status),
LastRequest: orderEntity.LastRequestAt,
CreatedAt: orderEntity.CreatedAt,
UpdatedAt: orderEntity.UpdatedAt,
DeletedAt: orderEntity.DeletedAt,
}
return
}
// GetOrderStatus 查询订单状态
func (s *sJdCookie) GetOrderStatus(ctx context.Context, orderId string) (order *v1.OrderInfo, err error) {
if orderId == "" {
return nil, gerror.New("订单号不能为空")
}
orderEntity, err := s.getOrderByOrderId(ctx, orderId)
if err != nil {
return nil, gerror.Wrap(err, "查询订单失败")
}
if orderEntity == nil {
return nil, gerror.New(consts.ErrCodeOrderNotFound)
}
order = &v1.OrderInfo{
Id: orderEntity.Id,
OrderId: orderEntity.OrderId,
UserOrderId: orderEntity.UserOrderId,
Amount: gconv.Float64(orderEntity.Amount),
Category: orderEntity.Category,
JdOrderId: orderEntity.JdOrderId,
Status: consts.OrderStatus(orderEntity.Status),
LastRequest: orderEntity.LastRequestAt,
CreatedAt: orderEntity.CreatedAt,
UpdatedAt: orderEntity.UpdatedAt,
DeletedAt: orderEntity.DeletedAt,
}
return
}
// ListOrder 订单列表查询
func (s *sJdCookie) ListOrder(ctx context.Context, page, size int, status consts.OrderStatus, startTime, endTime string) (list []*v1.OrderInfo, total int, err error) {
if page <= 0 {
page = 1
}
if size <= 0 {
size = 20
}
m := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1())
// 构建查询条件
if status > 0 {
m = m.Where(dao.V1JdCookieOrder.Columns().Status, int(status))
}
if startTime != "" {
m = m.WhereGTE(dao.V1JdCookieOrder.Columns().CreatedAt, startTime)
}
if endTime != "" {
m = m.WhereLTE(dao.V1JdCookieOrder.Columns().CreatedAt, endTime)
}
// 查询总数
total, err = m.Count()
if err != nil {
return nil, 0, gerror.Wrap(err, "查询总数失败")
}
// 查询列表数据
var orders []*entity.V1JdCookieOrder
err = m.Page(page, size).OrderDesc(dao.V1JdCookieOrder.Columns().CreatedAt).Scan(&orders)
if err != nil {
return nil, 0, gerror.Wrap(err, "查询订单列表失败")
}
// 转换为响应格式
list = make([]*v1.OrderInfo, 0, len(orders))
for _, orderEntity := range orders {
info := &v1.OrderInfo{
Id: orderEntity.Id,
OrderId: orderEntity.OrderId,
UserOrderId: orderEntity.UserOrderId,
Amount: gconv.Float64(orderEntity.Amount),
Category: orderEntity.Category,
JdOrderId: orderEntity.JdOrderId,
Status: consts.OrderStatus(orderEntity.Status),
LastRequest: orderEntity.LastRequestAt,
CreatedAt: orderEntity.CreatedAt,
UpdatedAt: orderEntity.UpdatedAt,
DeletedAt: orderEntity.DeletedAt,
}
list = append(list, info)
}
return
}