Files
kami_backend/docs/JD_ORDER_CREATE_REFACTOR_REPORT.md
danial e6ccd423b7 refactor(otel): 简化OTel配置与错误处理
- 移除不必要的配置字段和复杂错误类型
- 简化trace和log初始化逻辑,保留核心功能
- 使用标准Go错误替代自定义错误结构
- 启用默认批处理和消息丢弃机制- 保留gzip压缩和自动重连功能- 更新相关文档路径引用
- 添加OTel简化增强实现说明文档
2025-11-09 01:09:50 +08:00

11 KiB
Raw Permalink Blame History

京东订单创建逻辑重构报告

概述

本次重构针对京东Cookie管理模块的订单创建逻辑主要完成以下目标

  1. 添加重试机制解决Cookie失效和京东接口调用失败的问题
  2. 优化文件结构:将庞大的 order.go 文件拆分为多个职责明确的文件
  3. 增强容错能力:提升系统的稳定性和可靠性

重构内容

1. 文件重构

原有文件结构

internal/logic/jd_cookie/
├── account.go      # Cookie账户管理
├── history.go      # 历史记录管理
├── index.go        # 服务注册
├── order.go        # 订单管理700+ 行)
├── order_test.go   # 测试文件
└── rotation.go     # Cookie轮询

新文件结构

internal/logic/jd_cookie/
├── account.go          # Cookie账户管理
├── history.go          # 历史记录管理
├── index.go            # 服务注册
├── order_create.go     # 订单创建逻辑(带重试机制)
├── order_query.go      # 订单查询逻辑
├── order_jd.go         # 京东订单管理逻辑
├── order_utils.go      # 订单工具方法
├── order_test.go       # 测试文件
└── rotation.go         # Cookie轮询

文件职责划分

文件 职责 核心方法
order_create.go 订单创建与重试逻辑 CreateOrder, createNewJdOrderWithRetry
order_query.go 订单查询与列表 GetOrder, ListOrder, GetPaymentUrl
order_jd.go 京东订单管理与检查 GetJdOrder, ListJdOrder, CheckJdOrderPayment, ExtractCardInfo
order_utils.go 通用辅助方法 数据库查询、更新、京东接口调用等

2. 核心改进

2.1 订单创建重试机制

改进前

// 旧逻辑获取Cookie失败或下单失败直接返回错误
cookieId, err := s.GetAvailableCookie(ctx)
if err != nil {
    return "", "", "", gerror.Wrap(err, "获取可用Cookie失败")
}

jdOrderId, payId, wxPayUrl, err = s.callJdCreateOrder(ctx, cookieId, amount, category)
if err != nil {
    s.handleCookieFailure(ctx, cookieId, orderId)
    return "", "", "", gerror.Wrap(err, "京东下单失败")
}

改进后

// 新逻辑支持多次重试自动切换Cookie
func (s *sJdCookie) createNewJdOrderWithRetry(ctx context.Context, orderId string, amount float64, category consts.RedeemOrderCardCategory) (jdOrderId, cookieId, wxPayUrl string, err error) {
    const maxRetryCount = 10 // 最大重试次数
    var lastErr error
    var triedCookies []string // 记录已尝试的Cookie

    for retryCount := 0; retryCount < maxRetryCount; retryCount++ {
        // 获取可用的Cookie
        availableCookieId, cookieErr := s.GetAvailableCookie(ctx)
        if cookieErr != nil {
            lastErr = cookieErr
            break // 没有可用Cookie停止重试
        }

        // 检查是否已经尝试过这个Cookie
        if s.hasCookieBeenTried(triedCookies, availableCookieId) {
            continue
        }

        // 记录已尝试的Cookie
        triedCookies = append(triedCookies, availableCookieId)

        // 调用京东下单接口
        var payId string
        jdOrderId, payId, wxPayUrl, lastErr = s.callJdCreateOrder(ctx, availableCookieId, amount, category)
        if lastErr != nil {
            // Cookie失败更新状态
            s.handleCookieFailure(ctx, availableCookieId, orderId)
            continue // 继续下一次重试
        }

        // 下单成功,创建京东订单记录
        err = s.createJdOrderRecord(ctx, jdOrderId, payId, availableCookieId, category, amount, wxPayUrl)
        if err != nil {
            return "", "", "", gerror.Wrap(err, "创建京东订单记录失败")
        }

        // 返回成功结果
        return jdOrderId, availableCookieId, wxPayUrl, nil
    }

    // 所有重试都失败了
    if lastErr == nil {
        lastErr = gerror.New(consts.ErrCodeCookieNotAvailable)
    }

    return "", "", "", gerror.Wrapf(lastErr, "创建京东订单失败,已尝试%d个Cookie", len(triedCookies))
}

重试机制的优势

  1. 自动容错单个Cookie失败不会导致整个订单创建失败
  2. Cookie轮换自动切换到下一个可用Cookie
  3. 避免重复记录已尝试的Cookie避免重复尝试
  4. 详细日志:记录每次重试的详细信息,便于排查问题

2.2 京东订单复用优化

改进前

// 复用失败直接创建新订单
if reusableJdOrder != nil {
    // 使用可复用的京东订单
    jdOrderId = reusableJdOrder.JdOrderId
    cookieId = reusableJdOrder.CookieId
    wxPayUrl = reusableJdOrder.WxPayUrl
    
    // 刷新支付链接
    if expired {
        wxPayUrl, err = s.refreshPaymentUrl(ctx, jdOrderId, payId, cookieId)
        if err != nil {
            return "", "", "", gerror.Wrap(err, "刷新支付链接失败")
        }
    }
}

改进后

// 复用失败后,自动切换到创建新订单,并记录相关历史
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)
        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 jdOrderId == "" {
    jdOrderId, cookieId, wxPayUrl, err = s.createNewJdOrderWithRetry(ctx, orderId, amount, category)
    if err != nil {
        return "", "", "", err
    }
}

优化的优势

  1. 降级处理:复用失败时自动降级到创建新订单
  2. 完整记录:记录刷新失败、订单失效等所有状态变更
  3. Cookie管理刷新失败时记录Cookie的失败历史
  4. 用户体验:保证订单创建成功率,不因单个环节失败而影响整体流程

2.3 新增的变更类型常量

internal/consts/jd_cookie.go 中需要添加的常量(如果还未添加):

// CookieChangeType Cookie变更类型
const (
    CookieChangeTypeRefreshFail CookieChangeType = "refresh_fail" // 刷新失败
    CookieChangeTypeReplaced    CookieChangeType = "replaced"     // 被替换
)

// JdOrderChangeType 京东订单变更类型
const (
    JdOrderChangeTypeInvalid JdOrderChangeType = "invalid" // 失效
)

3. 改进细节

3.1 日志记录优化

每个关键步骤都添加了详细的日志记录:

glog.Warning(ctx, "刷新支付链接失败,将创建新订单", g.Map{
    "jdOrderId": jdOrderId,
    "error":     refreshErr,
})

glog.Info(ctx, "创建京东订单成功", g.Map{
    "orderId":    orderId,
    "jdOrderId":  jdOrderId,
    "cookieId":   availableCookieId,
    "retryCount": retryCount,
})

glog.Error(ctx, "创建京东订单失败所有Cookie均不可用", g.Map{
    "orderId":      orderId,
    "triedCookies": triedCookies,
    "error":        lastErr,
})

3.2 历史记录完整性

所有状态变更都会记录到历史表:

  1. Cookie历史:使用、失败、刷新失败、被替换
  2. 京东订单历史:创建、绑定、失效、替换
  3. 用户订单历史:创建、绑定、过期

3.3 错误处理改进

// 明确的错误信息
return "", "", "", gerror.Wrapf(lastErr, "创建京东订单失败,已尝试%d个Cookie", len(triedCookies))

// 保留最后一次错误
if lastErr == nil {
    lastErr = gerror.New(consts.ErrCodeCookieNotAvailable)
}

测试建议

单元测试

  1. 测试重试机制

    • 模拟多个Cookie失败的场景
    • 验证重试次数是否正确
    • 验证是否避免重复尝试相同Cookie
  2. 测试订单复用

    • 测试支付链接刷新成功
    • 测试支付链接刷新失败后创建新订单
    • 测试历史记录是否完整
  3. 测试Cookie切换

    • 验证Cookie失败后状态更新
    • 验证自动切换到下一个Cookie

集成测试

  1. 端到端测试

    • 创建订单 → 支付 → 提取卡密
    • 复用订单 → 刷新链接 → 支付
  2. 压力测试

    • 并发创建订单
    • 验证Cookie分配是否均衡
    • 验证系统稳定性

性能影响

优化点

  1. 减少数据库查询:复用订单时减少不必要的查询
  2. 批量操作:历史记录使用异步批量写入
  3. 缓存利用充分利用Redis缓存减少数据库压力

注意事项

  1. 重试次数当前设置为10次可根据实际情况调整
  2. 超时控制:建议添加整体超时控制,避免长时间阻塞
  3. 并发控制:注意订单创建的并发控制,避免竞争条件

向后兼容性

  1. API接口不变CreateOrder 方法签名保持不变
  2. 数据结构不变:不需要修改数据库表结构
  3. 配置项不变:使用现有的常量配置

部署建议

  1. 灰度发布:先在小流量环境测试

  2. 监控指标

    • 订单创建成功率
    • Cookie使用分布
    • 重试次数统计
    • 平均响应时间
  3. 回滚方案:保留旧版本代码,必要时快速回滚

未来优化方向

  1. 动态重试次数根据Cookie池大小动态调整重试次数
  2. 智能Cookie选择根据历史成功率优先选择可靠的Cookie
  3. 异步处理:订单创建改为异步处理,提升响应速度
  4. 熔断机制单个Cookie连续失败达到阈值后自动熔断

总结

本次重构主要解决了以下问题:

  1. Cookie失败重试自动切换Cookie提升订单创建成功率
  2. 订单复用优化:刷新失败后自动降级到创建新订单
  3. 代码结构优化:文件职责清晰,便于维护和扩展
  4. 历史记录完整:所有状态变更都有详细记录
  5. 日志体系完善:关键步骤都有日志记录,便于排查问题

重构后的代码更加健壮、可维护能够更好地应对Cookie失效、京东接口异常等场景。