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

## 概述

本次重构针对京东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 订单创建重试机制

**改进前**：

```go
// 旧逻辑：获取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, "京东下单失败")
}
```

**改进后**：

```go
// 新逻辑：支持多次重试，自动切换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 京东订单复用优化

**改进前**：

```go
// 复用失败直接创建新订单
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, "刷新支付链接失败")
        }
    }
}
```

**改进后**：

```go
// 复用失败后，自动切换到创建新订单，并记录相关历史
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` 中需要添加的常量（如果还未添加）：

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

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

### 3. 改进细节

#### 3.1 日志记录优化

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

```go
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 错误处理改进

```go
// 明确的错误信息
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失效、京东接口异常等场景。
