feat(jd-cookie):优化订单创建逻辑与状态管理- 新增订单状态 OrderStatusJDOrderFailed用于标识京东订单获取失败
- 新增订单变更类型 OrderChangeTypeJDOrderFailed 用于记录下单失败事件 - 调整订单创建逻辑,支持失败订单重试机制 - 新增 RecordOrderHistoryReq 结构体统一记录订单变更历史参数 - 修改数据库表结构,优化字段类型和索引 - 更新订单创建逻辑,分离本地订单与京东订单创建流程- 增加失败订单重新创建京东订单的处理逻辑 - 调整订单状态检查逻辑,支持更多状态处理 -优化订单历史记录方式,增加备注信息支持 - 更新数据库字符集为 utf8mb4_unicode_ci 提升兼容性
This commit is contained in:
@@ -6,7 +6,8 @@
|
||||
"Bash(timeout:*)",
|
||||
"WebSearch",
|
||||
"mcp__fetch__fetch",
|
||||
"Bash(find:*)"
|
||||
"Bash(find:*)",
|
||||
"mcp__mysql-mcp-sever__query"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
- [config.yaml](file://manifest/config/config.yaml)
|
||||
- [order.go](file://internal/logic/jd_cookie/order.go) - *在提交77aff4289e7b60617a6b97873721f586af264581中更新*
|
||||
- [jd_cookie.go](file://internal/service/jd_cookie.go) - *在提交0fdae6a89fef78f6368400b1fe726d987adda7b1中更新*
|
||||
- [order_create.go](file://internal/logic/jd_cookie/order_create.go) - *在提交bc2d58753b8fe0b4f7150657bf40cfe756ce48de中重构*
|
||||
- [order_utils.go](file://internal/logic/jd_cookie/order_utils.go) - *在提交8bc2d51c8ba46582286270b3dd85e166f2562e51中优化*
|
||||
- [order_jd.go](file://internal/logic/jd_cookie/order_jd.go) - *在提交d1b7f907caa0c3c59628241250901cb7239b25a8中新增功能*
|
||||
</cite>
|
||||
|
||||
## 更新摘要
|
||||
@@ -29,6 +32,8 @@
|
||||
- 增加了京东Cookie账户变更历史记录功能的说明
|
||||
- 修订了京东Cookie相关组件的序列图和流程描述
|
||||
- 更新了受影响文件的引用来源标记
|
||||
- 新增了京东订单支付状态定时检查与卡密提取功能的文档
|
||||
- 更新了订单创建逻辑的并发控制机制说明
|
||||
|
||||
## 目录
|
||||
1. [引言](#引言)
|
||||
@@ -150,7 +155,6 @@ class SysUser {
|
||||
+EditUserRole(ctx context.Context, roleIds []int64, userId int64) (err error)
|
||||
+UserNameOrMobileExists(ctx context.Context, userName string, id ...string) (isExist bool, err error)
|
||||
+GetEditUser(ctx context.Context, id string) (res *model.UserGetEditOutput, err error)
|
||||
+GetUserInfoById(ctx context.Context, id string, withPwd ...bool) (user *entity.V1SysUser, err error)
|
||||
+ChangePwd(ctx context.Context, input *model.UserChangePwdInput) (err error)
|
||||
+ChangeUserStatus(ctx context.Context, input *model.UserStatusInput) (err error)
|
||||
+DeleteById(ctx context.Context, id string) (err error)
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
**本文档引用的文件**
|
||||
- [auth.go](file://internal/middleware/auth.go) - *在最近的提交中更新*
|
||||
- [error_handler.go](file://internal/middleware/error_handler.go) - *在最近的提交中更新*
|
||||
- [sys_auth.go](file://internal/service/sys_auth.go) - *定义认证服务接口*
|
||||
- [sysAuth.go](file://internal/logic/sys_auth/sysAuth.go) - *实现认证服务逻辑*
|
||||
- [jd_cookie_v1_create_account.go](file://internal/controller/jd_cookie/jd_cookie_v1_create_account.go) - *账户创建控制器*
|
||||
- [jd_cookie_v1_create_order.go](file://internal/controller/jd_cookie/jd_cookie_v1_create_order.go) - *订单创建控制器*
|
||||
- [jd_cookie_v1_batch_check.go](file://internal/controller/jd_cookie/jd_cookie_v1_batch_check.go) - *批量检测控制器*
|
||||
- [user_token.go](file://utility/token/user_token.go) - *在认证流程中使用*
|
||||
- [config.go](file://utility/config/config.go) - *在认证流程中使用*
|
||||
- [code.go](file://internal/errHandler/code.go) - *定义错误码*
|
||||
@@ -11,9 +16,12 @@
|
||||
|
||||
## 更新摘要
|
||||
**已做更改**
|
||||
- 更新了白名单认证流程,将 `/api/jd-cookie/order/create` 接口添加到白名单
|
||||
- 修正了相关文档内容以反映最新的代码状态
|
||||
- 更新了白名单认证流程图和相关说明
|
||||
|
||||
- 更新了认证中间件文档,反映`LoginOnlyIFrame`和`LoginOnlyLogin`认证策略的使用
|
||||
- 添加了对`SysAuth`服务接口的详细说明
|
||||
- 更新了JD Cookie模块的权限校验说明,包括账户创建和批量检测接口使用`LoginOnlyIFrame`
|
||||
- 修正了订单创建接口不再需要权限校验的文档说明
|
||||
- 更新了相关架构图和序列图以反映最新的认证流程
|
||||
- 维护了源代码引用跟踪系统
|
||||
|
||||
## 目录
|
||||
@@ -96,6 +104,26 @@ AuthMiddleware --> IFrameAuth
|
||||
### 认证中间件分析
|
||||
认证中间件是kami_backend安全体系的核心,实现了多模式的身份验证机制。该中间件通过`LoginOrIframeAuth`函数协调不同的认证策略,根据请求头中的`tokenFrom`字段选择适当的认证方式。
|
||||
|
||||
#### 认证服务接口
|
||||
|
||||
系统通过`SysAuth`接口提供统一的认证服务,该接口定义了多种认证策略:
|
||||
|
||||
```go
|
||||
type ISysAuth interface {
|
||||
// LoginWithIFrameAndLogin Iframe和账号可以同时登陆
|
||||
LoginWithIFrameAndLogin(ctx context.Context) (userInfo *entity.V1SysUser, err error)
|
||||
LoginWithEverything(ctx context.Context) (output *model.SysAuthLoginWithEverythingOutput, err error)
|
||||
// LoginOnlyIFrame 只能IFrame登录
|
||||
LoginOnlyIFrame(ctx context.Context) (userInfo *entity.V1SysUser, err error)
|
||||
// LoginOnlyLogin 只能登录
|
||||
LoginOnlyLogin(ctx context.Context) (userInfo *entity.V1SysUser, err error)
|
||||
}
|
||||
```
|
||||
|
||||
**本节来源**
|
||||
|
||||
- [sys_auth.go](file://internal/service/sys_auth.go)
|
||||
|
||||
#### 认证流程类图
|
||||
```mermaid
|
||||
classDiagram
|
||||
@@ -219,6 +247,71 @@ WriteJson --> End
|
||||
**图表来源**
|
||||
- [error_handler.go](file://internal/middleware/error_handler.go#L8-L27)
|
||||
|
||||
### JD Cookie模块权限校验更新
|
||||
|
||||
根据最新的代码变更,JD Cookie模块的权限校验策略已进行调整,具体如下:
|
||||
|
||||
#### 账户创建权限校验
|
||||
|
||||
账户创建接口现在需要进行iFrame登录权限校验:
|
||||
|
||||
```go
|
||||
// CreateAccount 创建Cookie账户
|
||||
func (c *ControllerV1) CreateAccount(ctx context.Context, req *v1.CreateAccountReq) (res *v1.CreateAccountRes, err error) {
|
||||
_, err = service.SysAuth().LoginOnlyIFrame(ctx)
|
||||
if err != nil {
|
||||
err = errHandler.WrapError(ctx, gcode.CodeNotAuthorized, err, "权限不足")
|
||||
return
|
||||
}
|
||||
// ... 业务逻辑
|
||||
}
|
||||
```
|
||||
|
||||
**本节来源**
|
||||
|
||||
- [jd_cookie_v1_create_account.go](file://internal/controller/jd_cookie/jd_cookie_v1_create_account.go)
|
||||
|
||||
#### 批量检测权限校验
|
||||
|
||||
批量检测接口同样需要iFrame登录权限校验:
|
||||
|
||||
```go
|
||||
// BatchCheck 批量检测Cookie状态
|
||||
func (c *ControllerV1) BatchCheck(ctx context.Context, req *v1.BatchCheckReq) (res *v1.BatchCheckRes, err error) {
|
||||
_, err = service.SysAuth().LoginOnlyIFrame(ctx)
|
||||
if err != nil {
|
||||
err = errHandler.WrapError(ctx, gcode.CodeNotAuthorized, err, "权限不足")
|
||||
return
|
||||
}
|
||||
// ... 业务逻辑
|
||||
}
|
||||
```
|
||||
|
||||
**本节来源**
|
||||
|
||||
- [jd_cookie_v1_batch_check.go](file://internal/controller/jd_cookie/jd_cookie_v1_batch_check.go)
|
||||
|
||||
#### 订单创建权限校验移除
|
||||
|
||||
订单创建接口不再需要权限校验,已从白名单中移除:
|
||||
|
||||
```go
|
||||
// CreateOrder 创建订单
|
||||
func (c *ControllerV1) CreateOrder(ctx context.Context, req *v1.CreateOrderReq) (res *v1.CreateOrderRes, err error) {
|
||||
// 无需权限校验
|
||||
createOrderReq := &model.CreateOrderReq{
|
||||
UserOrderId: req.UserOrderId,
|
||||
Amount: req.Amount,
|
||||
Category: req.Category,
|
||||
}
|
||||
// ... 业务逻辑
|
||||
}
|
||||
```
|
||||
|
||||
**本节来源**
|
||||
|
||||
- [jd_cookie_v1_create_order.go](file://internal/controller/jd_cookie/jd_cookie_v1_create_order.go)
|
||||
|
||||
## 依赖分析
|
||||
kami_backend的中间件系统依赖于多个核心模块,形成了一个紧密耦合但职责分明的架构。认证中间件依赖于token、config和verify模块来实现完整的认证功能,而错误处理中间件则依赖于GoFrame框架的错误处理机制。
|
||||
|
||||
@@ -266,6 +359,7 @@ end
|
||||
2. **认证超时**:检查配置中的Token超时设置,确认Redis缓存是否正常工作。
|
||||
3. **错误响应格式不一致**:确保所有业务逻辑都使用标准的错误码系统。
|
||||
4. **iFrame认证失败**:检查前端密钥和IV配置是否正确,确认Token加密方式是否匹配。
|
||||
5. **权限不足错误**:确认接口是否需要`LoginOnlyIFrame`或`LoginOnlyLogin`认证,检查认证头是否正确传递。
|
||||
|
||||
**本节来源**
|
||||
- [auth.go](file://internal/middleware/auth.go)
|
||||
|
||||
292
.qoder/repowiki/zh/content/京东订单导出功能.md
Normal file
292
.qoder/repowiki/zh/content/京东订单导出功能.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# 京东订单导出功能
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [jd_cookie_v1_export_jd_order.go](file://internal/controller/jd_cookie/jd_cookie_v1_export_jd_order.go)
|
||||
- [jd_cookie.go](file://internal/service/jd_cookie.go)
|
||||
- [order_jd.go](file://internal/logic/jd_cookie/order_jd.go)
|
||||
- [v_1_jd_cookie_jd_order.go](file://internal/model/entity/v_1_jd_cookie_jd_order.go)
|
||||
- [v_1_jd_cookie_account.go](file://internal/model/entity/v_1_jd_cookie_account.go)
|
||||
- [jd_cookie.go](file://api/jd_cookie/jd_cookie.go)
|
||||
- [consts.go](file://internal/consts/jd_cookie.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构概述](#架构概述)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖分析](#依赖分析)
|
||||
7. [性能考虑](#性能考虑)
|
||||
8. [故障排除指南](#故障排除指南)
|
||||
9. [结论](#结论)
|
||||
|
||||
## 简介
|
||||
|
||||
京东订单导出功能是系统中用于将京东订单数据导出为Excel文件的重要功能。该功能允许用户根据订单状态、时间范围和订单ID等条件筛选订单,并将符合条件的订单数据导出为Excel文件,便于后续的数据分析和处理。
|
||||
|
||||
## 项目结构
|
||||
|
||||
京东订单导出功能主要分布在以下几个目录中:
|
||||
|
||||
- `internal/controller/jd_cookie/`:包含导出功能的控制器实现
|
||||
- `internal/service/jd_cookie.go`:定义导出功能的服务接口
|
||||
- `internal/logic/jd_cookie/order_jd.go`:包含导出功能的核心业务逻辑
|
||||
- `internal/model/entity/`:包含订单和账户相关的数据模型
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Controller[控制器层] --> Service[服务层]
|
||||
Service --> Logic[业务逻辑层]
|
||||
Logic --> DAO[数据访问层]
|
||||
DAO --> Database[(数据库)]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [jd_cookie_v1_export_jd_order.go](file://internal/controller/jd_cookie/jd_cookie_v1_export_jd_order.go#L1-L31)
|
||||
- [jd_cookie.go](file://internal/service/jd_cookie.go#L1-L95)
|
||||
- [order_jd.go](file://internal/logic/jd_cookie/order_jd.go#L661-L764)
|
||||
|
||||
## 核心组件
|
||||
|
||||
京东订单导出功能的核心组件包括:
|
||||
|
||||
- **ExportJdOrderReq**:导出请求参数,包含状态、开始时间、结束时间和订单ID等筛选条件
|
||||
- **ExportJdOrderRes**:导出响应结果
|
||||
- **JdCookieService**:提供导出功能的服务接口
|
||||
- **sJdCookie**:实现导出功能的业务逻辑
|
||||
|
||||
**章节来源**
|
||||
|
||||
- [jd_cookie_v1_export_jd_order.go](file://internal/controller/jd_cookie/jd_cookie_v1_export_jd_order.go#L1-L31)
|
||||
- [jd_cookie.go](file://internal/service/jd_cookie.go#L85-L90)
|
||||
|
||||
## 架构概述
|
||||
|
||||
京东订单导出功能采用典型的分层架构,包括控制器层、服务层、业务逻辑层和数据访问层。用户通过API请求触发导出功能,控制器层接收请求并调用服务层,服务层再调用业务逻辑层执行具体的导出操作,最后通过数据访问层从数据库获取订单数据。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 用户
|
||||
participant 控制器
|
||||
participant 服务
|
||||
participant 业务逻辑
|
||||
participant 数据访问
|
||||
participant 数据库
|
||||
用户->>控制器 : 发送导出请求
|
||||
控制器->>服务 : 调用ExportJdOrder
|
||||
服务->>业务逻辑 : 调用ExportJdOrder
|
||||
业务逻辑->>数据访问 : 查询订单数据
|
||||
数据访问->>数据库 : 执行SQL查询
|
||||
数据库-->>数据访问 : 返回订单数据
|
||||
数据访问-->>业务逻辑 : 返回查询结果
|
||||
业务逻辑->>业务逻辑 : 生成Excel文件
|
||||
业务逻辑-->>服务 : 返回文件名和内容
|
||||
服务-->>控制器 : 返回导出结果
|
||||
控制器->>用户 : 返回文件下载响应
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [jd_cookie_v1_export_jd_order.go](file://internal/controller/jd_cookie/jd_cookie_v1_export_jd_order.go#L1-L31)
|
||||
- [order_jd.go](file://internal/logic/jd_cookie/order_jd.go#L661-L764)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### 导出功能分析
|
||||
|
||||
京东订单导出功能的主要流程如下:
|
||||
|
||||
1. 接收用户请求,解析筛选条件
|
||||
2. 根据条件查询数据库中的订单数据
|
||||
3. 查询关联的Cookie账户信息
|
||||
4. 将数据填充到Excel模板中
|
||||
5. 生成Excel文件并返回给用户
|
||||
|
||||
#### 业务逻辑组件
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class ExportJdOrderReq {
|
||||
+string Status
|
||||
+string StartTime
|
||||
+string EndTime
|
||||
+string OrderId
|
||||
}
|
||||
class ExportJdOrderRes {
|
||||
+string FileName
|
||||
+[]byte Content
|
||||
}
|
||||
class JdCookieService {
|
||||
+ExportJdOrder(ctx, status, startTime, endTime, orderId) (fileName, content, err)
|
||||
}
|
||||
class sJdCookie {
|
||||
+ExportJdOrder(ctx, status, startTime, endTime, orderId) (fileName, content, err)
|
||||
+createEmptyExcel(ctx) (fileName, content, err)
|
||||
+getJdOrderStatusText(status) string
|
||||
}
|
||||
ExportJdOrderReq --> JdCookieService : "作为参数"
|
||||
JdCookieService --> sJdCookie : "实现"
|
||||
sJdCookie --> ExportJdOrderRes : "返回"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [jd_cookie.go](file://internal/service/jd_cookie.go#L85-L90)
|
||||
- [order_jd.go](file://internal/logic/jd_cookie/order_jd.go#L661-L764)
|
||||
|
||||
#### 数据模型组件
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
V1JdCookieJdOrder {
|
||||
string jdOrderId PK
|
||||
string orderId
|
||||
string cardNo
|
||||
string cardPassword
|
||||
string cookieId FK
|
||||
int status
|
||||
datetime createdAt
|
||||
}
|
||||
V1JdCookieAccount {
|
||||
string cookieId PK
|
||||
string accountName
|
||||
int status
|
||||
datetime createdAt
|
||||
}
|
||||
V1JdCookieJdOrder ||--o{ V1JdCookieAccount : "使用"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [v_1_jd_cookie_jd_order.go](file://internal/model/entity/v_1_jd_cookie_jd_order.go#L1-L33)
|
||||
- [v_1_jd_cookie_account.go](file://internal/model/entity/v_1_jd_cookie_account.go#L1-L25)
|
||||
|
||||
### 筛选条件分析
|
||||
|
||||
导出功能支持多种筛选条件,用户可以根据需要选择不同的条件组合来导出订单数据。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始]) --> StatusCheck{状态筛选?}
|
||||
StatusCheck --> |是| AddStatusCondition["添加状态条件"]
|
||||
StatusCheck --> |否| TimeCheck
|
||||
TimeCheck{时间范围筛选?}
|
||||
TimeCheck --> |是| AddTimeCondition["添加时间条件"]
|
||||
TimeCheck --> |否| OrderIdCheck
|
||||
OrderIdCheck{订单ID筛选?}
|
||||
OrderIdCheck --> |是| AddOrderIdCondition["添加订单ID条件"]
|
||||
OrderIdCheck --> |否| QueryData
|
||||
AddStatusCondition --> TimeCheck
|
||||
AddTimeCondition --> OrderIdCheck
|
||||
AddOrderIdCondition --> QueryData
|
||||
QueryData[查询订单数据] --> GetCookieInfo[查询Cookie信息]
|
||||
GetCookieInfo --> CreateExcel[创建Excel文件]
|
||||
CreateExcel --> ReturnResult[返回结果]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [order_jd.go](file://internal/logic/jd_cookie/order_jd.go#L661-L764)
|
||||
|
||||
**章节来源**
|
||||
|
||||
- [order_jd.go](file://internal/logic/jd_cookie/order_jd.go#L661-L764)
|
||||
- [jd_cookie_v1_export_jd_order.go](file://internal/controller/jd_cookie/jd_cookie_v1_export_jd_order.go#L1-L31)
|
||||
|
||||
## 依赖分析
|
||||
|
||||
京东订单导出功能依赖于多个组件和外部库:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
ExportFeature[导出功能] --> GoFrame[goframe框架]
|
||||
ExportFeature --> Excelize[excelize库]
|
||||
ExportFeature --> Database[数据库]
|
||||
ExportFeature --> Cache[缓存系统]
|
||||
ExportFeature --> Logger[日志系统]
|
||||
GoFrame --> HTTP[HTTP服务]
|
||||
GoFrame --> ORM[ORM框架]
|
||||
Excelize --> Excel[Excel文件处理]
|
||||
Database --> MySQL[MySQL数据库]
|
||||
Cache --> Redis[Redis缓存]
|
||||
Logger --> File[文件日志]
|
||||
Logger --> Console[控制台输出]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [jd_cookie.go](file://internal/service/jd_cookie.go#L1-L95)
|
||||
- [order_jd.go](file://internal/logic/jd_cookie/order_jd.go#L661-L764)
|
||||
|
||||
**章节来源**
|
||||
|
||||
- [jd_cookie.go](file://internal/service/jd_cookie.go#L1-L95)
|
||||
- [order_jd.go](file://internal/logic/jd_cookie/order_jd.go#L661-L764)
|
||||
|
||||
## 性能考虑
|
||||
|
||||
京东订单导出功能在设计时考虑了以下性能因素:
|
||||
|
||||
- 使用分页查询避免一次性加载过多数据
|
||||
- 批量查询关联的Cookie账户信息,减少数据库查询次数
|
||||
- 使用缓存机制避免重复的卡密提取操作
|
||||
- 异步处理回调,避免阻塞主流程
|
||||
|
||||
## 故障排除指南
|
||||
|
||||
在使用京东订单导出功能时,可能会遇到以下常见问题:
|
||||
|
||||
**章节来源**
|
||||
|
||||
- [order_jd.go](file://internal/logic/jd_cookie/order_jd.go#L661-L764)
|
||||
- [jd_cookie_v1_export_jd_order.go](file://internal/controller/jd_cookie/jd_cookie_v1_export_jd_order.go#L1-L31)
|
||||
|
||||
### 问题1:导出文件为空
|
||||
|
||||
可能原因:
|
||||
|
||||
- 筛选条件过于严格,没有匹配的订单
|
||||
- 数据库连接问题导致无法查询数据
|
||||
- 权限不足,无法访问订单数据
|
||||
|
||||
解决方案:
|
||||
|
||||
- 检查筛选条件是否正确
|
||||
- 检查数据库连接状态
|
||||
- 确认用户权限是否足够
|
||||
|
||||
### 问题2:导出速度慢
|
||||
|
||||
可能原因:
|
||||
|
||||
- 订单数据量过大
|
||||
- 数据库查询性能问题
|
||||
- 网络传输速度慢
|
||||
|
||||
解决方案:
|
||||
|
||||
- 分批导出数据
|
||||
- 优化数据库查询语句
|
||||
- 检查网络状况
|
||||
|
||||
### 问题3:Excel文件损坏
|
||||
|
||||
可能原因:
|
||||
|
||||
- 文件生成过程中出现异常
|
||||
- 内存不足导致文件写入不完整
|
||||
- Excelize库版本问题
|
||||
|
||||
解决方案:
|
||||
|
||||
- 检查日志中的错误信息
|
||||
- 增加系统内存
|
||||
- 更新Excelize库到最新版本
|
||||
|
||||
## 结论
|
||||
|
||||
京东订单导出功能为用户提供了一个便捷的数据导出工具,能够根据多种条件筛选订单并导出为Excel文件。该功能采用分层架构设计,具有良好的可维护性和扩展性。通过合理的性能优化和错误处理机制,确保了功能的稳定性和可靠性。
|
||||
@@ -18,8 +18,11 @@
|
||||
|
||||
## 更新摘要
|
||||
**变更内容**
|
||||
- 更新了京东集成部分,新增了订单创建和支付链接刷新功能
|
||||
- 增加了京东订单复用机制的详细说明
|
||||
|
||||
- 更新了京东集成部分,修复了订单ID字段类型问题,将OrderId类型从json.Number调整为string
|
||||
- 修正了京东接口字段名,将card_pass字段名更改为card_pwd,保持与上游接口一致
|
||||
- 修改了获取卡密接口的JSON字段名,并新增order_status字段
|
||||
- 调整了京东订单字段命名,将AppleRechargeCardInfoReq中的OrderNo重命名为OrderId
|
||||
- 更新了相关代码示例和架构图
|
||||
- 新增了订单状态检查和复用逻辑的实现细节
|
||||
- 更新了故障排除指南,增加了订单复用相关的常见问题
|
||||
@@ -90,26 +93,39 @@ InnerClient --> topsdk : "依赖"
|
||||
|
||||
```go
|
||||
func (c *Client) AppleRecharge(ctx context.Context, input *AppleRechargeReq) (*AppleRechargeResp, error) {
|
||||
resp := &AppleRechargeResp{}
|
||||
var resp = &AppleRechargeResp{}
|
||||
glog.Info(ctx, "苹果权益充值", input)
|
||||
response, err := c.Client.ContentJson().Post(ctx, "http://jd_babel_channel:8289/jd/app/placeOrder", input)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
|
||||
for range 3 {
|
||||
response, err := c.Client.ContentJson().Post(ctx, "http://jd_babel_channel:8289/jd/app/store", input)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
respData := response.ReadAllString()
|
||||
clientResp := AppleRechargeClientResp{}
|
||||
glog.Info(ctx, "获取信息", respData)
|
||||
err = json.Unmarshal([]byte(respData), &clientResp)
|
||||
if err != nil {
|
||||
glog.Error(ctx, "获取信息失败", err)
|
||||
return resp, err
|
||||
}
|
||||
resp = &AppleRechargeResp{
|
||||
Code: clientResp.Code.JDOrderStatus(),
|
||||
Deeplink: clientResp.Data.Deeplink,
|
||||
OrderId: clientResp.Data.OrderId,
|
||||
PayId: clientResp.Data.PayId,
|
||||
Msg: clientResp.Data.Remark,
|
||||
}
|
||||
if clientResp.Code == RiskError {
|
||||
continue
|
||||
}
|
||||
if clientResp.Code != Success {
|
||||
return resp, gerror.New("苹果权益充值失败," + clientResp.Data.Remark)
|
||||
}
|
||||
break
|
||||
}
|
||||
clientResp := &AppleRechargeClientResp{}
|
||||
err = json.Unmarshal(response.ReadAll(), clientResp)
|
||||
glog.Info(ctx, "苹果权益充值", response.ReadAllString())
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp = &AppleRechargeResp{
|
||||
Code: clientResp.Code.JDOrderStatus(),
|
||||
Deeplink: clientResp.Data.Deeplink,
|
||||
OrderId: clientResp.Data.OrderId,
|
||||
PayId: clientResp.Data.PayId,
|
||||
FacePrice: clientResp.Data.FacePrice,
|
||||
}
|
||||
if clientResp.Code != Success {
|
||||
if resp == nil {
|
||||
return resp, gerror.New("苹果权益充值失败")
|
||||
}
|
||||
return resp, nil
|
||||
@@ -122,23 +138,23 @@ func (c *Client) AppleRecharge(ctx context.Context, input *AppleRechargeReq) (*A
|
||||
```go
|
||||
func (c *Client) RefreshPayment(ctx context.Context, input *RefreshPaymentReq) (*RefreshPaymentRes, error) {
|
||||
resp := &RefreshPaymentRes{}
|
||||
glog.Info(ctx, "苹果权益充值", input)
|
||||
glog.Info(ctx, "刷新收银台参数", input)
|
||||
response, err := c.Client.ContentJson().Post(ctx, "http://jd_babel_channel:8289/api/v1/jd/wx/refresh-payment", input)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
clientResp := &AppleRechargeClientResp{}
|
||||
err = json.Unmarshal(response.ReadAll(), clientResp)
|
||||
glog.Info(ctx, "苹果权益充值", response.ReadAllString())
|
||||
glog.Info(ctx, "刷新收银台返回值", clientResp)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp = &RefreshPaymentRes{
|
||||
Code: clientResp.Code.JDOrderStatus(),
|
||||
Deeplink: clientResp.Data.Deeplink,
|
||||
OrderId: clientResp.Data.OrderId,
|
||||
PayId: clientResp.Data.PayId,
|
||||
FacePrice: clientResp.Data.FacePrice,
|
||||
Code: clientResp.Code.JDOrderStatus(),
|
||||
Deeplink: clientResp.Data.Deeplink,
|
||||
OrderId: clientResp.Data.OrderId,
|
||||
PayId: clientResp.Data.PayId,
|
||||
Msg: clientResp.Data.Remark,
|
||||
}
|
||||
if clientResp.Code != Success {
|
||||
return resp, gerror.New("苹果权益充值失败")
|
||||
@@ -155,7 +171,7 @@ func (c *Client) CheckOrderPayment(ctx context.Context, input *CheckOrderPayment
|
||||
resp := &CheckOrderPaymentResp{}
|
||||
glog.Info(ctx, "检查京东订单支付状态", input)
|
||||
|
||||
response, err := c.Client.ContentJson().Post(ctx, "http://jd_babel_channel:8289/jd/query/order/payment", input)
|
||||
response, err := c.Client.ContentJson().Post(ctx, "http://jd_babel_channel:8289/api/jd/query/order/payment", input)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
@@ -182,6 +198,45 @@ func (c *Client) CheckOrderPayment(ctx context.Context, input *CheckOrderPayment
|
||||
}
|
||||
```
|
||||
|
||||
#### 获取卡密信息
|
||||
|
||||
通过`GetCardInfo`方法获取京东订单的卡号和卡密,修正了接口字段名,将card_pass字段名更改为card_pwd,并新增order_status字段。
|
||||
|
||||
```go
|
||||
func (c *Client) GetCardInfo(ctx context.Context, input *AppleRechargeCardInfoReq) (resp *AppleRechargeCardInfoResp, err error) {
|
||||
response, err := c.Client.ContentJson().Post(ctx, "http://jd_babel_channel:8289/jd/query/card", input)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
clientResp := struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Remark string `json:"remark"`
|
||||
CardPwd string `json:"card_pwd"`
|
||||
CardNo string `json:"card_num"`
|
||||
OrderStatus string `json:"order_status"`
|
||||
} `json:"data,omitempty"`
|
||||
}{}
|
||||
err = json.Unmarshal(response.ReadAll(), &clientResp)
|
||||
glog.Info(ctx, "获取京东订单的卡号和卡密", clientResp, err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resp = &AppleRechargeCardInfoResp{
|
||||
OrderStatus: clientResp.Data.OrderStatus,
|
||||
Remark: clientResp.Data.Remark,
|
||||
CardNo: clientResp.Data.CardNo,
|
||||
IsCkFailed: clientResp.Code == int(CkError),
|
||||
CardPassword: clientResp.Data.CardPwd,
|
||||
}
|
||||
if clientResp.Code != int(Success) || clientResp.Data.CardNo == "" {
|
||||
return resp, gerror.New("状态不正确")
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
**Section sources**
|
||||
- [client.go](file://utility/integration/originalJd/client.go#L11-L123)
|
||||
- [model.go](file://utility/integration/originalJd/model.go#L1-L144)
|
||||
|
||||
@@ -28,9 +28,10 @@
|
||||
|
||||
## 更新摘要
|
||||
**已更新内容**
|
||||
- 在核心数据模型部分新增了京东Cookie管理模块的三个核心数据表
|
||||
|
||||
- 在核心数据模型部分更新了京东Cookie管理模块的三个核心数据表,添加了新字段
|
||||
- 在实体关系与约束部分更新了外键关系说明
|
||||
- 在数据验证与业务规则部分新增了京东Cookie模块的业务规则
|
||||
- 在数据验证与业务规则部分新增了京东订单复用和风控失败状态的业务规则
|
||||
- 在GoFrame ORM映射机制部分更新了DO和Entity层说明
|
||||
- 在DAO层实现部分更新了DAO结构说明
|
||||
- 更新了文档引用文件列表,包含新模块的相关文件
|
||||
@@ -153,16 +154,17 @@
|
||||
|
||||
**字段定义:**
|
||||
- **id**: 主键,自增,类型为`uint`
|
||||
- **cookie**: Cookie字符串,类型为`string`
|
||||
- **status**: 账户状态(正常、暂停、失效等),类型为`string`
|
||||
- **cookieId**: Cookie唯一标识,类型为`string`
|
||||
- **cookieValue**: Cookie内容,类型为`string`
|
||||
- **accountName**: 账户名称,类型为`string`
|
||||
- **status**: 账户状态(1.正常 2.暂停 3.失效),类型为`int`
|
||||
- **failureCount**: 连续失败次数,类型为`int`
|
||||
- **lastUsedAt**: 最后使用时间,类型为`*gtime.Time`
|
||||
- **failureCount**: 失败次数,类型为`int`
|
||||
- **successCount**: 成功次数,类型为`int`
|
||||
- **totalAmount**: 累计交易金额,使用`decimal.Decimal`精确存储
|
||||
- **lastOrderId**: 最后处理的订单ID,类型为`string`
|
||||
- **pauseUntil**: 暂停到期时间,用于自动恢复,类型为`*gtime.Time`
|
||||
- **suspendUntil**: 暂停解除时间,用于自动恢复,类型为`*gtime.Time`
|
||||
- **remark**: 备注信息,类型为`string`
|
||||
- **createdAt**: 创建时间,类型为`*gtime.Time`
|
||||
- **updatedAt**: 更新时间,类型为`*gtime.Time`
|
||||
- **deletedAt**: 删除时间,类型为`*gtime.Time`
|
||||
|
||||
**Section sources**
|
||||
- [v_1_jd_cookie_account.go](file://internal/model/entity/v_1_jd_cookie_account.go#L12-L28)
|
||||
@@ -172,35 +174,48 @@
|
||||
|
||||
**字段定义:**
|
||||
- **id**: 主键,自增,类型为`uint`
|
||||
- **jdOrderId**: 京东订单ID,类型为`string`
|
||||
- **paymentUrl**: 支付链接,类型为`string`
|
||||
- **jdOrderId**: 京东订单号,类型为`string`
|
||||
- **realJdOrderId**: 京东客户端返回的真实订单ID,类型为`string`
|
||||
- **payId**: 支付ID,类型为`string`
|
||||
- **amount**: 订单金额,使用`decimal.Decimal`精确存储
|
||||
- **status**: 订单状态(待支付、已支付、已过期等),类型为`string`
|
||||
- **accountId**: 关联的Cookie账户ID,类型为`uint`
|
||||
- **orderId**: 关联的系统订单ID,类型为`string`
|
||||
- **category**: 商品品类,类型为`string`
|
||||
- **cookieId**: 使用的Cookie ID,类型为`string`
|
||||
- **status**: 订单状态(1.待支付 2.已支付 3.已过期 4.已取消),类型为`int`
|
||||
- **wxPayUrl**: 微信支付链接,类型为`string`
|
||||
- **wxPayExpireAt**: 微信支付链接过期时间,类型为`*gtime.Time`
|
||||
- **orderExpireAt**: 订单过期时间(默认24小时),类型为`*gtime.Time`
|
||||
- **orderId**: 关联的用户订单号,类型为`string`
|
||||
- **paidAt**: 支付完成时间,类型为`*gtime.Time`
|
||||
- **cardNo**: 卡号,类型为`string`
|
||||
- **cardPassword**: 卡密,类型为`string`
|
||||
- **cardExtractedAt**: 卡密提取时间,类型为`*gtime.Time`
|
||||
- **createdAt**: 创建时间,类型为`*gtime.Time`
|
||||
- **updatedAt**: 更新时间,类型为`*gtime.Time`
|
||||
- **expiredAt**: 过期时间,类型为`*gtime.Time`
|
||||
- **lastCheckedAt**: 最后检查时间,类型为`*gtime.Time`
|
||||
- **deletedAt**: 删除时间,类型为`*gtime.Time`
|
||||
|
||||
**Section sources**
|
||||
- [v_1_jd_cookie_jd_order.go](file://internal/model/entity/v_1_jd_cookie_jd_order.go#L12-L27)
|
||||
|
||||
- [v_1_jd_cookie_jd_order.go](file://internal/model/entity/v_1_jd_cookie_jd_order.go#L12-L33)
|
||||
|
||||
### v_1_jd_cookie_order(京东Cookie订单表)
|
||||
该表存储系统订单与京东Cookie的关联信息。
|
||||
|
||||
**字段定义:**
|
||||
- **id**: 主键,自增,类型为`uint`
|
||||
- **orderId**: 系统订单ID,类型为`string`
|
||||
- **accountId**: 使用的Cookie账户ID,类型为`uint`
|
||||
- **jdOrderId**: 关联的京东订单ID,类型为`string`
|
||||
- **status**: 订单处理状态,类型为`string`
|
||||
- **orderId**: 订单号,类型为`string`
|
||||
- **userOrderId**: 用户订单号,类型为`string`
|
||||
- **amount**: 订单金额,使用`decimal.Decimal`精确存储
|
||||
- **category**: 商品品类,类型为`string`
|
||||
- **jdOrderId**: 关联的京东订单号,类型为`string`
|
||||
- **status**: 状态(1.待支付 2.已支付 3.已过期 4.已取消),类型为`int`
|
||||
- **lastRequestAt**: 最后请求时间,类型为`*gtime.Time`
|
||||
- **createdAt**: 创建时间,类型为`*gtime.Time`
|
||||
- **updatedAt**: 更新时间,类型为`*gtime.Time`
|
||||
- **deletedAt**: 删除时间,类型为`*gtime.Time`
|
||||
|
||||
**Section sources**
|
||||
- [v_1_jd_cookie_order.go](file://internal/model/entity/v_1_jd_cookie_order.go#L12-L22)
|
||||
|
||||
- [v_1_jd_cookie_order.go](file://internal/model/entity/v_1_jd_cookie_order.go#L12-L25)
|
||||
|
||||
## 实体关系与约束
|
||||
|
||||
@@ -209,39 +224,40 @@
|
||||
- `v_1_account_info`表的`accountUid`字段是业务主键,确保账户的唯一性。
|
||||
- `v_1_order_info`表的`bankOrderId`和`merchantOrderId`字段有唯一性约束,用于快速查询订单。
|
||||
- `v_1_card_apple_recharge_info`表的`orderNo`字段是业务主键,确保订单号的唯一性。
|
||||
- `v_1_jd_cookie_account`表的`cookie`字段有唯一性约束,防止重复添加相同的Cookie。
|
||||
- `v_1_jd_cookie_account`表的`cookieId`字段有唯一性约束,防止重复添加相同的Cookie。
|
||||
- `v_1_jd_cookie_jd_order`表的`jdOrderId`字段是业务主键,确保京东订单的唯一性。
|
||||
- `v_1_jd_cookie_order`表的`orderId`字段是业务主键,确保系统订单与京东订单关联的唯一性。
|
||||
- `v_1_jd_cookie_order`表的`userOrderId`字段有索引,用于快速查询用户订单。
|
||||
|
||||
### 外键关系
|
||||
本系统采用弱外键约束,主要通过业务逻辑来维护数据一致性:
|
||||
- `v_1_order_info.merchantUid` 关联 `v_1_account_info.accountUid`,表示订单所属的商户账户。
|
||||
- `v_1_card_apple_recharge_info.merchantId` 关联 `v_1_account_info.accountUid`,表示充值订单所属的商户。
|
||||
- `v_1_card_apple_recharge_info.accountId` 关联 `v_1_card_apple_account_info.id`(未在文档中详述),表示充值所使用的苹果账户。
|
||||
- `v_1_jd_cookie_jd_order.accountId` 关联 `v_1_jd_cookie_account.id`,表示京东订单所属的Cookie账户。
|
||||
- `v_1_jd_cookie_jd_order.orderId` 关联 `v_1_order_info.bankOrderId`,表示京东订单对应的系统订单。
|
||||
- `v_1_jd_cookie_order.accountId` 关联 `v_1_jd_cookie_account.id`,表示订单处理使用的Cookie账户。
|
||||
- `v_1_jd_cookie_jd_order.cookieId` 关联 `v_1_jd_cookie_account.cookieId`,表示京东订单所属的Cookie账户。
|
||||
- `v_1_jd_cookie_jd_order.orderId` 关联 `v_1_jd_cookie_order.userOrderId`,表示京东订单对应的用户订单。
|
||||
- `v_1_jd_cookie_order.jdOrderId` 关联 `v_1_jd_cookie_jd_order.jdOrderId`,表示订单关联的京东平台订单。
|
||||
|
||||
### 约束规则
|
||||
- **状态约束**:各表的`status`字段有明确的枚举值范围,如`v_1_card_apple_recharge_info.status`只能为0,1,2,3。
|
||||
|
||||
- **状态约束**:各表的`status`字段有明确的枚举值范围,如`v_1_jd_cookie_account.status`只能为1,2,3。
|
||||
- **金额约束**:涉及金额的字段(如`balance`, `actualAmount`)使用`decimal.Decimal`或`float64`,确保精度。
|
||||
- **时间约束**:`createdAt`必须小于等于`updatedAt`,`deletedAt`在软删除时才不为空。
|
||||
- **唯一性约束**:关键业务字段如`cookie`、`jdOrderId`等有唯一性约束,防止数据重复。
|
||||
- **唯一性约束**:关键业务字段如`cookieId`、`jdOrderId`等有唯一性约束,防止数据重复。
|
||||
|
||||
**Section sources**
|
||||
- [v_1_account_info.go](file://internal/model/entity/v_1_account_info.go#L12-L25)
|
||||
- [v_1_order_info.go](file://internal/model/entity/v_1_order_info.go#L12-L63)
|
||||
- [v_1_card_apple_recharge_info.go](file://internal/model/entity/v_1_card_apple_recharge_info.go#L11-L33)
|
||||
- [v_1_jd_cookie_account.go](file://internal/model/entity/v_1_jd_cookie_account.go#L12-L28)
|
||||
- [v_1_jd_cookie_jd_order.go](file://internal/model/entity/v_1_jd_cookie_jd_order.go#L12-L27)
|
||||
- [v_1_jd_cookie_order.go](file://internal/model/entity/v_1_jd_cookie_order.go#L12-L22)
|
||||
- [v_1_jd_cookie_jd_order.go](file://internal/model/entity/v_1_jd_cookie_jd_order.go#L12-L33)
|
||||
- [v_1_jd_cookie_order.go](file://internal/model/entity/v_1_jd_cookie_order.go#L12-L25)
|
||||
|
||||
## 数据验证与业务规则
|
||||
|
||||
### 数据验证规则
|
||||
- **非空验证**:所有主键、业务主键(如`orderNo`, `accountUid`)、创建时间等字段均不能为空。
|
||||
- **格式验证**:`cardPass`(卡密)需符合特定的长度和字符规则;`ip`字段需为有效的IP地址格式;`cookie`字段需为有效的京东Cookie格式。
|
||||
- **格式验证**:`cardPass`(卡密)需符合特定的长度和字符规则;`ip`字段需为有效的IP地址格式;`cookieValue`字段需为有效的京东Cookie格式。
|
||||
- **范围验证**:`status`字段必须在预定义的枚举值范围内;金额字段不能为负数。
|
||||
|
||||
### 核心业务规则
|
||||
@@ -297,7 +313,7 @@
|
||||
// 示例:获取可用的Cookie账户
|
||||
err = dao.V1JdCookieAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
|
||||
Where(dao.V1JdCookieAccount.Columns().Status, consts.JdCookieStatusNormal).
|
||||
Where(dao.V1JdCookieAccount.Columns().PauseUntil+" IS NULL OR "+dao.V1JdCookieAccount.Columns().PauseUntil+" < ?", gtime.Now()).
|
||||
Where(dao.V1JdCookieAccount.Columns().SuspendUntil+" IS NULL OR "+dao.V1JdCookieAccount.Columns().SuspendUntil+" < ?", gtime.Now()).
|
||||
OrderAsc(dao.V1JdCookieAccount.Columns().LastUsedAt).
|
||||
OrderAsc(dao.V1JdCookieAccount.Columns().FailureCount).
|
||||
Scan(&accounts)
|
||||
@@ -311,12 +327,20 @@
|
||||
err = dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
|
||||
Where(dao.V1JdCookieJdOrder.Columns().Status, consts.JdOrderStatusPending).
|
||||
Where(dao.V1JdCookieJdOrder.Columns().Amount, amount).
|
||||
Where(dao.V1JdCookieJdOrder.Columns().ExpiredAt+" > ?", gtime.Now()).
|
||||
Where(dao.V1JdCookieJdOrder.Columns().OrderExpireAt+" > ?", gtime.Now()).
|
||||
Scan(&orders)
|
||||
```
|
||||
**Section sources**
|
||||
- [order.go](file://internal/logic/jd_cookie/order.go#L45-L60)
|
||||
|
||||
7. **风控失败状态规则**:当京东订单因风控失败时,系统会记录失败状态和备注信息,并更新相关订单状态。
|
||||
```go
|
||||
// 示例:处理风控失败的京东订单
|
||||
err = s.UpdateJdOrderStatus(ctx, jdOrderId, consts.JdOrderStatusCkFailed, "", "风控校验失败")
|
||||
```
|
||||
**Section sources**
|
||||
- [rotation.go](file://internal/logic/jd_cookie/rotation.go#L250-L265)
|
||||
|
||||
## 数据库模式图
|
||||
|
||||
```mermaid
|
||||
@@ -369,46 +393,57 @@ timestamp deletedAt
|
||||
}
|
||||
v_1_jd_cookie_account {
|
||||
uint id PK
|
||||
string cookie UK
|
||||
string status
|
||||
timestamp lastUsedAt
|
||||
string cookieId UK
|
||||
string cookieValue
|
||||
string accountName
|
||||
int status
|
||||
int failureCount
|
||||
int successCount
|
||||
decimal totalAmount
|
||||
string lastOrderId
|
||||
timestamp pauseUntil
|
||||
timestamp lastUsedAt
|
||||
timestamp suspendUntil
|
||||
string remark
|
||||
timestamp createdAt
|
||||
timestamp updatedAt
|
||||
timestamp deletedAt
|
||||
}
|
||||
v_1_jd_cookie_jd_order {
|
||||
uint id PK
|
||||
string jdOrderId UK
|
||||
string paymentUrl
|
||||
string realJdOrderId
|
||||
string payId
|
||||
decimal amount
|
||||
string status
|
||||
uint accountId FK
|
||||
string category
|
||||
string cookieId FK
|
||||
int status
|
||||
string wxPayUrl
|
||||
timestamp wxPayExpireAt
|
||||
timestamp orderExpireAt
|
||||
string orderId FK
|
||||
timestamp paidAt
|
||||
string cardNo
|
||||
string cardPassword
|
||||
timestamp cardExtractedAt
|
||||
timestamp createdAt
|
||||
timestamp updatedAt
|
||||
timestamp expiredAt
|
||||
timestamp lastCheckedAt
|
||||
timestamp deletedAt
|
||||
}
|
||||
v_1_jd_cookie_order {
|
||||
uint id PK
|
||||
string orderId UK
|
||||
uint accountId FK
|
||||
string jdOrderId FK
|
||||
string status
|
||||
string userOrderId
|
||||
decimal amount
|
||||
string category
|
||||
string jdOrderId FK
|
||||
int status
|
||||
timestamp lastRequestAt
|
||||
timestamp createdAt
|
||||
timestamp updatedAt
|
||||
timestamp deletedAt
|
||||
}
|
||||
v_1_account_info ||--o{ v_1_order_info : "merchantUid"
|
||||
v_1_account_info ||--o{ v_1_card_apple_recharge_info : "merchantId"
|
||||
v_1_jd_cookie_account ||--o{ v_1_jd_cookie_jd_order : "accountId"
|
||||
v_1_jd_cookie_account ||--o{ v_1_jd_cookie_order : "accountId"
|
||||
v_1_order_info ||--o{ v_1_jd_cookie_jd_order : "bankOrderId"
|
||||
v_1_order_info ||--o{ v_1_jd_cookie_order : "bankOrderId"
|
||||
v_1_jd_cookie_account ||--o{ v_1_jd_cookie_jd_order : "cookieId"
|
||||
v_1_jd_cookie_account ||--o{ v_1_jd_cookie_order : "关联"
|
||||
v_1_jd_cookie_order ||--o{ v_1_jd_cookie_jd_order : "orderId"
|
||||
v_1_jd_cookie_jd_order ||--o{ v_1_jd_cookie_order : "jdOrderId"
|
||||
```
|
||||
|
||||
@@ -417,8 +452,8 @@ v_1_jd_cookie_jd_order ||--o{ v_1_jd_cookie_order : "jdOrderId"
|
||||
- [v_1_order_info.go](file://internal/model/entity/v_1_order_info.go#L12-L63)
|
||||
- [v_1_card_apple_recharge_info.go](file://internal/model/entity/v_1_card_apple_recharge_info.go#L11-L33)
|
||||
- [v_1_jd_cookie_account.go](file://internal/model/entity/v_1_jd_cookie_account.go#L12-L28)
|
||||
- [v_1_jd_cookie_jd_order.go](file://internal/model/entity/v_1_jd_cookie_jd_order.go#L12-L27)
|
||||
- [v_1_jd_cookie_order.go](file://internal/model/entity/v_1_jd_cookie_order.go#L12-L22)
|
||||
- [v_1_jd_cookie_jd_order.go](file://internal/model/entity/v_1_jd_cookie_jd_order.go#L12-L33)
|
||||
- [v_1_jd_cookie_order.go](file://internal/model/entity/v_1_jd_cookie_order.go#L12-L25)
|
||||
|
||||
## 数据访问模式与缓存策略
|
||||
|
||||
@@ -478,17 +513,25 @@ DO层是ORM框架与数据库表之间的直接映射,位于`internal/model/do
|
||||
- **作用**:主要用于ORM的查询、插入、更新等操作的参数传递。
|
||||
|
||||
```go
|
||||
type V1AccountInfo struct {
|
||||
g.Meta `orm:"table:account_info, do:true"`
|
||||
Id any // 主键,自增
|
||||
Status any // 状态
|
||||
AccountUid any // 账户uid
|
||||
// ... 其他字段
|
||||
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 // 关联的京东订单号
|
||||
Status any // 状态:1待支付 2已支付 3已过期 4已取消
|
||||
LastRequestAt *gtime.Time // 最后请求时间
|
||||
CreatedAt *gtime.Time // 创建时间
|
||||
UpdatedAt *gtime.Time // 更新时间
|
||||
DeletedAt *gtime.Time // 删除时间
|
||||
}
|
||||
```
|
||||
|
||||
**Section sources**
|
||||
- [v_1_account_info.go](file://internal/model/do/v_1_account_info.go#L12-L26)
|
||||
|
||||
- [v_1_jd_cookie_order.go](file://internal/model/do/v_1_jd_cookie_order.go#L12-L26)
|
||||
|
||||
### Entity 层
|
||||
Entity层是业务逻辑中使用的数据结构,位于`internal/model/entity`目录下。
|
||||
@@ -496,17 +539,24 @@ Entity层是业务逻辑中使用的数据结构,位于`internal/model/entity`
|
||||
- **作用**:作为业务逻辑层和DAO层之间的数据传输对象(DTO)。
|
||||
|
||||
```go
|
||||
type V1AccountInfo struct {
|
||||
Id uint `json:"id" orm:"id" description:"主键,自增"`
|
||||
Status string `json:"status" orm:"status" description:"状态"`
|
||||
AccountUid string `json:"accountUid" orm:"account_uid" description:"账户uid"`
|
||||
Balance decimal.Decimal `json:"balance" orm:"balance" description:"账户余额"`
|
||||
// ... 其他字段
|
||||
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:"关联的京东订单号"`
|
||||
Status int `json:"status" orm:"status" description:"状态:1待支付 2已支付 3已过期 4已取消"`
|
||||
LastRequestAt *gtime.Time `json:"lastRequestAt" orm:"last_request_at" description:"最后请求时间"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"`
|
||||
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"删除时间"`
|
||||
}
|
||||
```
|
||||
|
||||
**Section sources**
|
||||
- [v_1_account_info.go](file://internal/model/entity/v_1_account_info.go#L12-L25)
|
||||
|
||||
- [v_1_jd_cookie_order.go](file://internal/model/entity/v_1_jd_cookie_order.go#L12-L25)
|
||||
|
||||
### 映射关系
|
||||
- **Entity -> DO**:当DAO层需要执行数据库操作时,会将`Entity`对象转换为`DO`对象。
|
||||
@@ -520,39 +570,42 @@ DAO层位于`internal/dao`目录下,是数据访问的入口。
|
||||
- **外部DAO**:`internal/dao`目录下的DAO是对内部DAO的包装,可以添加自定义方法。
|
||||
|
||||
```go
|
||||
// v1AccountInfoDao 是对内部DAO的包装
|
||||
type v1AccountInfoDao struct {
|
||||
internalV1AccountInfoDao
|
||||
// v1JdCookieOrderDao 是对内部DAO的包装
|
||||
type v1JdCookieOrderDao struct {
|
||||
internalV1JdCookieOrderDao
|
||||
}
|
||||
|
||||
// V1AccountInfo 是全局可访问的DAO对象
|
||||
var V1AccountInfo = v1AccountInfoDao{
|
||||
internal.NewV1AccountInfoDao(),
|
||||
// V1JdCookieOrder 是全局可访问的DAO对象
|
||||
var V1JdCookieOrder = v1JdCookieOrderDao{
|
||||
internal.NewV1JdCookieOrderDao(),
|
||||
}
|
||||
```
|
||||
|
||||
**Section sources**
|
||||
- [v_1_account_info.go](file://internal/dao/v_1_account_info.go#L1-L27)
|
||||
|
||||
- [v_1_jd_cookie_order.go](file://internal/dao/v_1_jd_cookie_order.go#L1-L27)
|
||||
|
||||
### 自定义方法实现
|
||||
DAO层可以定义自定义方法来实现复杂的查询逻辑。
|
||||
- **示例:添加苹果充值订单**
|
||||
|
||||
- **示例:添加京东订单**
|
||||
```go
|
||||
func (h *sAppleOrder) AddRechargeOrder(ctx context.Context, input *model.AppleCardRechargeInput) (orderNo string, err error) {
|
||||
// ... 业务逻辑
|
||||
rechargeId, err := dao.V1CardAppleRechargeInfo.Ctx(ctx).DB(config.GetDatabaseV1()).
|
||||
InsertAndGetId(do.V1CardAppleRechargeInfo{
|
||||
OrderNo: orderNo,
|
||||
AccountId: input.AccountID,
|
||||
CardPass: input.CardPass,
|
||||
Status: status,
|
||||
// ... 其他字段
|
||||
})
|
||||
return orderNo, err
|
||||
func (s *sJdCookie) CreateJdOrder(ctx context.Context, jdOrderId, payId, cookieId, category string, amount float64) (err error) {
|
||||
m := dao.V1JdCookieJdOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
_, err = m.Insert(&do.V1JdCookieJdOrder{
|
||||
JdOrderId: jdOrderId,
|
||||
PayId: payId,
|
||||
Amount: amount,
|
||||
Category: category,
|
||||
CookieId: cookieId,
|
||||
Status: int(consts.JdOrderStatusPending),
|
||||
OrderExpireAt: gtime.Now().Add(time.Hour * consts.JdOrderExpireDuration),
|
||||
})
|
||||
return
|
||||
}
|
||||
```
|
||||
**Section sources**
|
||||
- [order.go](file://internal/logic/card_apple_order/order.go#L100-L143)
|
||||
- [rotation.go](file://internal/logic/jd_cookie/rotation.go#L150-L165)
|
||||
|
||||
- **示例:修改订单状态**
|
||||
```go
|
||||
@@ -570,16 +623,21 @@ DAO层可以定义自定义方法来实现复杂的查询逻辑。
|
||||
|
||||
- **示例:获取可用的Cookie账户**
|
||||
```go
|
||||
func (s *sJdCookie) GetAvailableAccount(ctx context.Context) (*entity.V1JdCookieAccount, error) {
|
||||
var account *entity.V1JdCookieAccount
|
||||
err := dao.V1JdCookieAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
|
||||
Where(dao.V1JdCookieAccount.Columns().Status, consts.JdCookieStatusNormal).
|
||||
Where(dao.V1JdCookieAccount.Columns().PauseUntil+" IS NULL OR "+dao.V1JdCookieAccount.Columns().PauseUntil+" < ?", gtime.Now()).
|
||||
func (s *sJdCookie) GetAvailableCookie(ctx context.Context) (cookieId string, err error) {
|
||||
m := dao.V1JdCookieAccount.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
var availableCookies []*entity.V1JdCookieAccount
|
||||
err = m.Where(dao.V1JdCookieAccount.Columns().Status, int(consts.JdCookieStatusNormal)).
|
||||
OrderAsc(dao.V1JdCookieAccount.Columns().LastUsedAt).
|
||||
OrderAsc(dao.V1JdCookieAccount.Columns().FailureCount).
|
||||
Scan(&account)
|
||||
return account, err
|
||||
Scan(&availableCookies)
|
||||
if err != nil {
|
||||
return "", gerror.Wrap(err, "查询可用Cookie失败")
|
||||
}
|
||||
if len(availableCookies) == 0 {
|
||||
return "", gerror.New(consts.ErrCodeCookieNotAvailable)
|
||||
}
|
||||
selectedCookie := availableCookies[0]
|
||||
return selectedCookie.CookieId, nil
|
||||
}
|
||||
```
|
||||
**Section sources**
|
||||
- [account.go](file://internal/logic/jd_cookie/account.go#L15-L35)
|
||||
- [rotation.go](file://internal/logic/jd_cookie/rotation.go#L15-L30)
|
||||
@@ -13,6 +13,10 @@
|
||||
- [handler.go](file://utility/otel/handler.go)
|
||||
- [utils.go](file://utility/otel/utils.go)
|
||||
- [monitor.go](file://utility/monitor/monitor.go)
|
||||
- [order_create.go](file://internal/logic/jd_cookie/order_create.go)
|
||||
- [order_utils.go](file://internal/logic/jd_cookie/order_utils.go)
|
||||
- [client.go](file://utility/integration/originalJd/client.go)
|
||||
- [order_jd.go](file://internal/logic/jd_cookie/order_jd.go)
|
||||
</cite>
|
||||
|
||||
## 更新摘要
|
||||
@@ -22,6 +26,8 @@
|
||||
- 修正了日志系统配置流程图与代码逻辑的一致性
|
||||
- 增强了健康检查机制的技术细节说明
|
||||
- 补充了命令行监控选项的实现原理
|
||||
- 新增了京东订单创建、支付状态检查和卡密提取功能的日志记录说明
|
||||
- 更新了苹果权益充值接口的日志记录实践
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
@@ -36,7 +42,10 @@
|
||||
10. [运维诊断指南](#运维诊断指南)
|
||||
|
||||
## 简介
|
||||
kami_backend系统通过OpenTelemetry实现了全面的分布式追踪、指标收集和日志聚合功能。本系统集成了OpenTelemetry的三大核心组件:追踪(Tracing)、指标(Metrics)和日志(Logging),为系统的可观测性提供了坚实的基础。监控系统不仅提供了实时的健康检查功能,还通过命令行选项实现了灵活的监控任务管理。系统采用结构化日志格式,支持多级别日志记录,并通过配置化的存储策略确保日志数据的可靠性和可访问性。
|
||||
|
||||
kami_backend系统通过OpenTelemetry实现了全面的分布式追踪、指标收集和日志聚合功能。本系统集成了OpenTelemetry的三大核心组件:追踪(
|
||||
Tracing)、指标(Metrics)和日志(Logging)
|
||||
,为系统的可观测性提供了坚实的基础。监控系统不仅提供了实时的健康检查功能,还通过命令行选项实现了灵活的监控任务管理。系统采用结构化日志格式,支持多级别日志记录,并通过配置化的存储策略确保日志数据的可靠性和可访问性。近期更新增强了京东订单处理和苹果权益充值接口的日志记录,确保敏感信息不被泄露,同时完善了错误处理日志格式。
|
||||
|
||||
## OpenTelemetry集成架构
|
||||
|
||||
|
||||
333
.qoder/repowiki/zh/content/订单回调功能.md
Normal file
333
.qoder/repowiki/zh/content/订单回调功能.md
Normal file
@@ -0,0 +1,333 @@
|
||||
# 订单回调功能
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [card_info_c_trip_v1_order_callback.go](file://internal/controller/card_info_c_trip/card_info_c_trip_v1_order_callback.go)
|
||||
- [card_info_jd_v1_order_callback.go](file://internal/controller/card_info_jd/card_info_jd_v1_order_callback.go)
|
||||
- [card_info_walmart_v1_order_callback.go](file://internal/controller/card_info_walmart/card_info_walmart_v1_order_callback.go)
|
||||
- [card_info_t_mall_game_v1_t_mall_game_agiso_callback.go](file://internal/controller/card_info_t_mall_game/card_info_t_mall_game_v1_t_mall_game_agiso_callback.go)
|
||||
- [callback.go](file://internal/logic/card_redeem_order/callback.go)
|
||||
- [card_redeem_order.go](file://internal/service/card_redeem_order.go)
|
||||
- [card_redeem.go](file://internal/consts/card_redeem.go)
|
||||
- [v_1_card_redeem_order_info.go](file://internal/model/entity/v_1_card_redeem_order_info.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构概述](#架构概述)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖分析](#依赖分析)
|
||||
7. [性能考虑](#性能考虑)
|
||||
8. [故障排除指南](#故障排除指南)
|
||||
9. [结论](#结论)
|
||||
|
||||
## 简介
|
||||
|
||||
订单回调功能是系统中用于处理第三方平台订单状态变更通知的核心机制。该功能确保系统能够及时响应外部平台的订单状态更新,如支付成功、确认收货等事件,并根据预设规则执行相应的业务逻辑,包括发货、评价处理和向上游系统回调等操作。本功能支持多个电商平台,包括携程、京东、沃尔玛和天猫游戏等,具备良好的扩展性和稳定性。
|
||||
|
||||
## 项目结构
|
||||
|
||||
订单回调功能主要分布在`internal/controller`和`internal/logic`
|
||||
目录下,涉及多个电商平台的具体实现。控制器层负责接收外部回调请求,进行权限验证和基本校验,然后调用逻辑层处理具体业务。逻辑层封装了核心业务逻辑,包括订单状态更新、发货处理、评价处理和向上游系统回调等。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "控制器层"
|
||||
CtripCallback["card_info_c_trip_v1_order_callback.go"]
|
||||
JdCallback["card_info_jd_v1_order_callback.go"]
|
||||
WalmartCallback["card_info_walmart_v1_order_callback.go"]
|
||||
TMallGameCallback["card_info_t_mall_game_v1_t_mall_game_agiso_callback.go"]
|
||||
end
|
||||
subgraph "逻辑层"
|
||||
LogicCallback["callback.go"]
|
||||
ConsumeLogic["consume.go"]
|
||||
end
|
||||
subgraph "服务层"
|
||||
Service["card_redeem_order.go"]
|
||||
end
|
||||
subgraph "常量定义"
|
||||
Consts["card_redeem.go"]
|
||||
end
|
||||
subgraph "数据模型"
|
||||
Model["v_1_card_redeem_order_info.go"]
|
||||
end
|
||||
CtripCallback --> LogicCallback
|
||||
JdCallback --> LogicCallback
|
||||
WalmartCallback --> LogicCallback
|
||||
TMallGameCallback --> LogicCallback
|
||||
LogicCallback --> Service
|
||||
ConsumeLogic --> Service
|
||||
Service --> Model
|
||||
Consts --> Service
|
||||
Consts --> LogicCallback
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [card_info_c_trip_v1_order_callback.go](file://internal/controller/card_info_c_trip/card_info_c_trip_v1_order_callback.go)
|
||||
- [card_info_jd_v1_order_callback.go](file://internal/controller/card_info_jd/card_info_jd_v1_order_callback.go)
|
||||
- [card_info_walmart_v1_order_callback.go](file://internal/controller/card_info_walmart/card_info_walmart_v1_order_callback.go)
|
||||
- [card_info_t_mall_game_v1_t_mall_game_agiso_callback.go](file://internal/controller/card_info_t_mall_game/card_info_t_mall_game_v1_t_mall_game_agiso_callback.go)
|
||||
- [callback.go](file://internal/logic/card_redeem_order/callback.go)
|
||||
- [consume.go](file://internal/logic/card_redeem_order/consume.go)
|
||||
- [card_redeem_order.go](file://internal/service/card_redeem_order.go)
|
||||
- [card_redeem.go](file://internal/consts/card_redeem.go)
|
||||
- [v_1_card_redeem_order_info.go](file://internal/model/entity/v_1_card_redeem_order_info.go)
|
||||
|
||||
**章节来源**
|
||||
|
||||
- [card_info_c_trip_v1_order_callback.go](file://internal/controller/card_info_c_trip/card_info_c_trip_v1_order_callback.go)
|
||||
- [card_info_jd_v1_order_callback.go](file://internal/controller/card_info_jd/card_info_jd_v1_order_callback.go)
|
||||
- [card_info_walmart_v1_order_callback.go](file://internal/controller/card_info_walmart/card_info_walmart_v1_order_callback.go)
|
||||
- [card_info_t_mall_game_v1_t_mall_game_agiso_callback.go](file://internal/controller/card_info_t_mall_game/card_info_t_mall_game_v1_t_mall_game_agiso_callback.go)
|
||||
|
||||
## 核心组件
|
||||
|
||||
订单回调功能的核心组件包括控制器、逻辑处理和回调服务。控制器负责接收外部回调请求,进行权限验证和基本校验;逻辑处理组件封装了核心业务逻辑,包括订单状态更新、发货处理和评价处理;回调服务负责向上游系统发送订单状态变更通知。这些组件通过服务接口进行解耦,确保了系统的可维护性和可扩展性。
|
||||
|
||||
**章节来源**
|
||||
|
||||
- [callback.go](file://internal/logic/card_redeem_order/callback.go)
|
||||
- [card_redeem_order.go](file://internal/service/card_redeem_order.go)
|
||||
- [card_redeem.go](file://internal/consts/card_redeem.go)
|
||||
|
||||
## 架构概述
|
||||
|
||||
订单回调功能采用分层架构设计,分为控制器层、逻辑层和服务层。控制器层负责接收外部回调请求,进行权限验证和基本校验,然后调用逻辑层处理具体业务。逻辑层封装了核心业务逻辑,包括订单状态更新、发货处理、评价处理和向上游系统回调等。服务层提供统一的接口供上层调用,实现了业务逻辑的复用和解耦。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant ThirdParty as "第三方平台"
|
||||
participant Controller as "控制器"
|
||||
participant Logic as "逻辑层"
|
||||
participant Service as "服务层"
|
||||
participant Database as "数据库"
|
||||
ThirdParty->>Controller : 发送回调请求
|
||||
Controller->>Controller : 验证权限和请求参数
|
||||
Controller->>Logic : 调用逻辑处理
|
||||
Logic->>Service : 更新订单状态
|
||||
Service->>Database : 保存订单状态
|
||||
Database-->>Service : 返回结果
|
||||
Service->>Logic : 返回处理结果
|
||||
Logic->>Service : 触发发货或评价处理
|
||||
Service->>ThirdParty : 向上游系统回调
|
||||
ThirdParty-->>Service : 返回回调结果
|
||||
Service-->>Logic : 返回最终结果
|
||||
Logic-->>Controller : 返回处理结果
|
||||
Controller-->>ThirdParty : 返回响应
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [card_info_c_trip_v1_order_callback.go](file://internal/controller/card_info_c_trip/card_info_c_trip_v1_order_callback.go)
|
||||
- [card_info_jd_v1_order_callback.go](file://internal/controller/card_info_jd/card_info_jd_v1_order_callback.go)
|
||||
- [card_info_walmart_v1_order_callback.go](file://internal/controller/card_info_walmart/card_info_walmart_v1_order_callback.go)
|
||||
- [card_info_t_mall_game_v1_t_mall_game_agiso_callback.go](file://internal/controller/card_info_t_mall_game/card_info_t_mall_game_v1_t_mall_game_agiso_callback.go)
|
||||
- [callback.go](file://internal/logic/card_redeem_order/callback.go)
|
||||
- [card_redeem_order.go](file://internal/service/card_redeem_order.go)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### 控制器组件分析
|
||||
|
||||
控制器组件负责接收外部回调请求,进行权限验证和基本校验,然后调用逻辑层处理具体业务。不同电商平台的控制器实现略有差异,但基本流程一致。
|
||||
|
||||
#### 携程订单回调
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "携程平台"
|
||||
participant Controller as "OrderCallback"
|
||||
participant Service as "CardRedeemOrder"
|
||||
Client->>Controller : 发送订单回调请求
|
||||
Controller->>Controller : 验证用户权限
|
||||
Controller->>Service : 查询订单信息
|
||||
Service-->>Controller : 返回订单信息
|
||||
Controller->>Controller : 验证订单状态
|
||||
Controller->>Service : 调用回调服务
|
||||
Service-->>Controller : 返回处理结果
|
||||
Controller-->>Client : 返回响应
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [card_info_c_trip_v1_order_callback.go](file://internal/controller/card_info_c_trip/card_info_c_trip_v1_order_callback.go)
|
||||
|
||||
#### 京东订单回调
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "京东平台"
|
||||
participant Controller as "OrderCallback"
|
||||
participant Service as "CardRedeemOrder"
|
||||
Client->>Controller : 发送订单回调请求
|
||||
Controller->>Controller : 验证用户权限
|
||||
Controller->>Service : 查询订单信息
|
||||
Service-->>Controller : 返回订单信息
|
||||
Controller->>Controller : 验证订单状态
|
||||
Controller->>Service : 调用回调服务
|
||||
Service-->>Controller : 返回处理结果
|
||||
Controller-->>Client : 返回响应
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [card_info_jd_v1_order_callback.go](file://internal/controller/card_info_jd/card_info_jd_v1_order_callback.go)
|
||||
|
||||
#### 沃尔玛订单回调
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "沃尔玛平台"
|
||||
participant Controller as "OrderCallback"
|
||||
participant Service as "CardRedeemOrder"
|
||||
Client->>Controller : 发送订单回调请求
|
||||
Controller->>Controller : 验证用户权限
|
||||
Controller->>Service : 查询订单信息
|
||||
Service-->>Controller : 返回订单信息
|
||||
Controller->>Controller : 验证订单状态
|
||||
Controller->>Service : 调用回调服务
|
||||
Service-->>Controller : 返回处理结果
|
||||
Controller-->>Client : 返回响应
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [card_info_walmart_v1_order_callback.go](file://internal/controller/card_info_walmart/card_info_walmart_v1_order_callback.go)
|
||||
|
||||
#### 天猫游戏订单回调
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "阿奇索平台"
|
||||
participant Controller as "TMallGameAgisoCallback"
|
||||
participant Logic as "handleCallBackPayment"
|
||||
participant Service as "RechargeTMallGameOrder"
|
||||
Client->>Controller : 发送支付成功回调
|
||||
Controller->>Controller : 验证签名
|
||||
Controller->>Logic : 处理支付成功
|
||||
Logic->>Service : 添加订单信息
|
||||
Service-->>Logic : 返回订单ID
|
||||
Logic->>Service : 获取淘宝订单信息
|
||||
Service-->>Logic : 返回订单详情
|
||||
Logic->>Service : 更新订单信息
|
||||
Service-->>Logic : 返回处理结果
|
||||
Logic->>Service : 尝试虚拟发货
|
||||
Service-->>Logic : 返回发货结果
|
||||
Logic-->>Controller : 返回处理结果
|
||||
Controller-->>Client : 返回响应
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [card_info_t_mall_game_v1_t_mall_game_agiso_callback.go](file://internal/controller/card_info_t_mall_game/card_info_t_mall_game_v1_t_mall_game_agiso_callback.go)
|
||||
|
||||
### 回调逻辑分析
|
||||
|
||||
回调逻辑组件负责处理订单状态变更后的业务逻辑,包括向上游系统发送回调通知。
|
||||
|
||||
#### 回调流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始]) --> ValidateOrder["验证订单状态"]
|
||||
ValidateOrder --> OrderValid{"订单有效?"}
|
||||
OrderValid --> |否| End([结束])
|
||||
OrderValid --> |是| CheckCallbackConfig["检查回调配置"]
|
||||
CheckCallbackConfig --> ShouldCallback{"需要回调?"}
|
||||
ShouldCallback --> |否| End
|
||||
ShouldCallback --> |是| PrepareParams["准备回调参数"]
|
||||
PrepareParams --> SignParams["生成签名"]
|
||||
SignParams --> SendCallback["发送回调请求"]
|
||||
SendCallback --> CallbackSuccess{"回调成功?"}
|
||||
CallbackSuccess --> |是| UpdateStatus["更新回调状态为成功"]
|
||||
CallbackSuccess --> |否| Retry["重试(最多3次)"]
|
||||
Retry --> SendCallback
|
||||
UpdateStatus --> End
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [callback.go](file://internal/logic/card_redeem_order/callback.go)
|
||||
|
||||
**章节来源**
|
||||
|
||||
- [callback.go](file://internal/logic/card_redeem_order/callback.go)
|
||||
- [card_redeem_order.go](file://internal/service/card_redeem_order.go)
|
||||
- [card_redeem.go](file://internal/consts/card_redeem.go)
|
||||
|
||||
## 依赖分析
|
||||
|
||||
订单回调功能依赖于多个内部组件和外部服务。内部依赖包括用户认证服务、数据库访问组件和缓存服务;外部依赖包括各电商平台的API接口和回调验证服务。这些依赖通过接口注入的方式进行管理,确保了组件间的松耦合。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
CallbackController --> SysAuth["SysAuth"]
|
||||
CallbackController --> CardRedeemOrder["CardRedeemOrder"]
|
||||
CallbackController --> Cache["Cache"]
|
||||
CardRedeemOrder --> Database["Database"]
|
||||
CardRedeemOrder --> ConfigDict["SysConfigDict"]
|
||||
TMallGameCallback --> Agiso["Agiso"]
|
||||
TMallGameCallback --> TmallAPI["TmallAPI"]
|
||||
CallbackLogic --> Pool["Pool"]
|
||||
CallbackLogic --> Trace["Trace"]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
|
||||
- [card_info_c_trip_v1_order_callback.go](file://internal/controller/card_info_c_trip/card_info_c_trip_v1_order_callback.go)
|
||||
- [card_info_jd_v1_order_callback.go](file://internal/controller/card_info_jd/card_info_jd_v1_order_callback.go)
|
||||
- [card_info_walmart_v1_order_callback.go](file://internal/controller/card_info_walmart/card_info_walmart_v1_order_callback.go)
|
||||
- [card_info_t_mall_game_v1_t_mall_game_agiso_callback.go](file://internal/controller/card_info_t_mall_game/card_info_t_mall_game_v1_t_mall_game_agiso_callback.go)
|
||||
- [callback.go](file://internal/logic/card_redeem_order/callback.go)
|
||||
- [card_redeem_order.go](file://internal/service/card_redeem_order.go)
|
||||
|
||||
**章节来源**
|
||||
|
||||
- [card_info_c_trip_v1_order_callback.go](file://internal/controller/card_info_c_trip/card_info_c_trip_v1_order_callback.go)
|
||||
- [card_info_jd_v1_order_callback.go](file://internal/controller/card_info_jd/card_info_jd_v1_order_callback.go)
|
||||
- [card_info_walmart_v1_order_callback.go](file://internal/controller/card_info_walmart/card_info_walmart_v1_order_callback.go)
|
||||
- [card_info_t_mall_game_v1_t_mall_game_agiso_callback.go](file://internal/controller/card_info_t_mall_game/card_info_t_mall_game_v1_t_mall_game_agiso_callback.go)
|
||||
- [callback.go](file://internal/logic/card_redeem_order/callback.go)
|
||||
- [card_redeem_order.go](file://internal/service/card_redeem_order.go)
|
||||
|
||||
## 性能考虑
|
||||
|
||||
订单回调功能在设计时充分考虑了性能因素。通过使用连接池和缓存机制,减少了数据库访问的开销;通过异步处理和并发控制,提高了系统的吞吐量;通过签名验证和防重机制,确保了系统的安全性和稳定性。此外,系统还实现了重试机制,确保在临时故障情况下能够自动恢复。
|
||||
|
||||
## 故障排除指南
|
||||
|
||||
### 常见问题及解决方案
|
||||
|
||||
1. **回调失败**
|
||||
- 检查上游系统的回调地址是否正确
|
||||
- 检查网络连接是否正常
|
||||
- 检查回调参数是否符合要求
|
||||
- 查看日志中的错误信息
|
||||
|
||||
2. **重复回调**
|
||||
- 检查系统是否实现了防重机制
|
||||
- 查看缓存中的订单状态
|
||||
- 检查数据库中的订单记录
|
||||
|
||||
3. **签名验证失败**
|
||||
- 检查密钥是否正确
|
||||
- 检查时间戳是否在有效范围内
|
||||
- 检查参数顺序是否正确
|
||||
|
||||
4. **发货失败**
|
||||
- 检查淘宝账号状态
|
||||
- 检查商品库存
|
||||
- 检查发货规则
|
||||
|
||||
**章节来源**
|
||||
|
||||
- [card_info_t_mall_game_v1_t_mall_game_agiso_callback.go](file://internal/controller/card_info_t_mall_game/card_info_t_mall_game_v1_t_mall_game_agiso_callback.go)
|
||||
- [callback.go](file://internal/logic/card_redeem_order/callback.go)
|
||||
|
||||
## 结论
|
||||
|
||||
订单回调功能是系统中关键的业务组件,负责处理外部平台的订单状态变更通知。通过分层架构设计和模块化实现,该功能具备良好的可维护性和可扩展性。系统通过权限验证、状态校验、防重机制和重试机制,确保了回调处理的准确性和可靠性。未来可以考虑增加更多的监控指标和告警机制,进一步提升系统的稳定性和可观测性。
|
||||
@@ -14,8 +14,19 @@
|
||||
- [configmap.yaml](file://manifest/deploy/kustomize/overlays/develop/configmap.yaml)
|
||||
- [Makefile](file://Makefile)
|
||||
- [hack.mk](file://hack/hack.mk)
|
||||
- [.drone.yml](file://.drone.yml)
|
||||
- [cron.go](file://utility/cron/cron.go)
|
||||
</cite>
|
||||
|
||||
## 更新摘要
|
||||
|
||||
**变更内容**
|
||||
|
||||
- 更新了Docker镜像仓库地址,从旧地址迁移至 `git.oceanpay.cc`
|
||||
- 调整了京东支付状态监控任务的执行频率,由每10秒一次改为每30秒一次
|
||||
- 新增了每分钟执行的京东订单支付状态检查任务
|
||||
- 相应更新了CI/CD配置文件中的Docker登录和推送指令
|
||||
|
||||
## 目录
|
||||
1. [配置管理](#配置管理)
|
||||
2. [Docker容器化部署](#docker容器化部署)
|
||||
@@ -47,7 +58,8 @@
|
||||
|
||||
`manifest/docker/Dockerfile` 文件定义了kami_backend服务的容器镜像构建流程,采用多阶段构建策略以减小最终镜像体积。
|
||||
|
||||
构建过程分为两个阶段:**构建阶段**使用 `golang:1.24-alpine` 作为基础镜像,设置Go模块代理,执行依赖整理和二进制编译。**运行阶段**基于轻量级的 `alpine:latest` 镜像,复制编译好的二进制文件和必要的资源文件(如静态资源和配置文件)。
|
||||
构建过程分为两个阶段:**构建阶段**使用 `golang:1.25-alpine` 作为基础镜像,设置Go模块代理,执行依赖整理和二进制编译。**运行阶段
|
||||
**基于轻量级的 `alpine:latest` 镜像,复制编译好的二进制文件和必要的资源文件(如静态资源和配置文件)。
|
||||
|
||||
运行阶段还配置了时区为亚洲/上海,安装了curl工具用于健康检查,并设置了 `HEALTHCHECK` 指令,通过curl命令定期检查服务的健康状态。容器暴露12401端口,通过CMD指令启动主程序。
|
||||
|
||||
@@ -61,7 +73,7 @@
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Docker构建阶段] --> B[golang:1.24-alpine]
|
||||
A[Docker构建阶段] --> B[golang:1.25-alpine]
|
||||
B --> C[复制源码]
|
||||
C --> D[go mod tidy]
|
||||
D --> E[go build]
|
||||
@@ -250,17 +262,20 @@ D_Kustom --> D_Patch
|
||||
- 定期轮换 `secret` 中的加密密钥
|
||||
- 使用专用的数据库用户,限制权限
|
||||
- 敏感信息通过环境变量注入,而非硬编码
|
||||
- CI/CD流程中使用新的Docker仓库地址 `git.oceanpay.cc`
|
||||
|
||||
**性能实践**
|
||||
- 根据实际负载调整 `database.maxOpen` 连接池大小
|
||||
- 合理设置 `redis.maxActive` 避免资源耗尽
|
||||
- 监控连接池使用情况,及时发现瓶颈
|
||||
- 京东支付状态监控任务已调整为每30秒执行一次,减少系统负载
|
||||
|
||||
**运维实践**
|
||||
- 使用 `Makefile` 的 `image.push` 目标确保镜像版本可追溯
|
||||
- 定期清理悬空镜像和停止的容器
|
||||
- 备份重要配置文件
|
||||
- 建立部署回滚机制
|
||||
- 新增了每分钟执行的京东订单支付状态检查任务,确保订单状态及时更新
|
||||
|
||||
**开发实践**
|
||||
- 本地开发使用 `docker-compose-local.yaml`
|
||||
@@ -273,4 +288,6 @@ D_Kustom --> D_Patch
|
||||
- [Dockerfile](file://manifest/docker/Dockerfile#L1-L41)
|
||||
- [docker-compose.yml](file://manifest/docker/docker-compose.yml#L1-L19)
|
||||
- [install.sh](file://manifest/docker/install.sh#L1-L144)
|
||||
- [kustomization.yaml](file://manifest/deploy/kustomize/base/kustomization.yaml#L1-L9)
|
||||
- [kustomization.yaml](file://manifest/deploy/kustomize/base/kustomization.yaml#L1-L9)
|
||||
- [.drone.yml](file://.drone.yml#L1-L46)
|
||||
- [cron.go](file://utility/cron/cron.go#L1-L65)
|
||||
File diff suppressed because one or more lines are too long
322
ORDER_CHANGE_HISTORY.md
Normal file
322
ORDER_CHANGE_HISTORY.md
Normal file
@@ -0,0 +1,322 @@
|
||||
# 订单变更记录完善说明
|
||||
|
||||
## 更新时间
|
||||
|
||||
2025-10-18
|
||||
|
||||
## 更新内容
|
||||
|
||||
根据**变更记录规范**,完善订单创建过程中的变更历史记录,确保所有关键操作都有完整的可追溯记录。
|
||||
|
||||
## 变更详情
|
||||
|
||||
### 新增的变更记录点
|
||||
|
||||
#### 1. 订单创建失败时的变更记录
|
||||
|
||||
**位置**: `internal/logic/jd_cookie/order_create.go` - `CreateOrder` 方法
|
||||
|
||||
**代码**:
|
||||
|
||||
```go
|
||||
if retryErr != nil {
|
||||
// 京东订单创建失败,更新本地订单状态和失败原因
|
||||
_ = s.updateOrderFailure(ctx, internalOrderId, retryErr.Error())
|
||||
|
||||
// 记录订单创建失败的变更历史 ✅ 新增
|
||||
_ = s.RecordOrderHistory(ctx, internalOrderId, consts.OrderChangeTypeCkFailed, "")
|
||||
|
||||
return nil, retryErr
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
|
||||
- 当京东订单创建失败时,记录 `OrderChangeTypeCkFailed` 类型的变更
|
||||
- 记录时间点:在更新订单失败状态之后
|
||||
- 确保失败原因可追溯
|
||||
|
||||
#### 2. 订单绑定京东订单的变更记录
|
||||
|
||||
**位置**: `internal/logic/jd_cookie/order_create.go` - `CreateOrder` 方法
|
||||
|
||||
**代码**:
|
||||
|
||||
```go
|
||||
// 京东订单创建成功,更新本地订单的京东订单ID和支付链接
|
||||
if jdOrderErr == nil {
|
||||
err = s.updateOrderJdOrderId(ctx, internalOrderId, jdOrderId, wxPayUrl)
|
||||
// ... 其他逻辑
|
||||
|
||||
// 记录订单绑定京东订单的变更历史 ✅ 新增
|
||||
_ = s.RecordOrderHistory(ctx, internalOrderId, consts.OrderChangeTypeRebind, jdOrderId)
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
|
||||
- 当订单成功绑定京东订单时,记录 `OrderChangeTypeRebind` 类型的变更
|
||||
- 记录关联的京东订单ID
|
||||
- 便于追踪订单与京东订单的绑定关系
|
||||
|
||||
#### 3. 失败订单重试失败的变更记录
|
||||
|
||||
**位置**: `internal/logic/jd_cookie/order_create.go` - `retryCreateJdOrderForFailedOrder` 方法
|
||||
|
||||
**代码**:
|
||||
|
||||
```go
|
||||
if retryErr != nil {
|
||||
// 京东订单创建失败,更新本地订单失败原因
|
||||
_ = s.updateOrderFailure(ctx, existingOrder.OrderId, retryErr.Error())
|
||||
|
||||
// 记录订单重试失败的变更历史 ✅ 新增
|
||||
_ = s.RecordOrderHistory(ctx, existingOrder.OrderId, consts.OrderChangeTypeCkFailed, "")
|
||||
|
||||
return nil, retryErr
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
|
||||
- 失败订单重试仍然失败时,记录变更历史
|
||||
- 可以追踪订单的重试次数和失败时间点
|
||||
- 便于分析订单失败模式
|
||||
|
||||
## 完整的变更记录流程
|
||||
|
||||
### 订单创建成功流程的变更记录
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as 用户
|
||||
participant S as 系统
|
||||
participant H as 变更历史表
|
||||
|
||||
U->>S: 创建订单请求
|
||||
S->>S: 创建本地订单
|
||||
S->>H: 记录订单创建(OrderChangeTypeCreate)
|
||||
|
||||
S->>S: 创建京东订单
|
||||
alt 创建成功
|
||||
S->>S: 更新订单状态
|
||||
S->>H: 记录订单绑定京东订单(OrderChangeTypeRebind)
|
||||
S->>U: 返回支付链接
|
||||
else 创建失败
|
||||
S->>S: 更新订单为失败状态
|
||||
S->>H: 记录订单创建失败(OrderChangeTypeCkFailed)
|
||||
S->>U: 返回错误信息
|
||||
end
|
||||
```
|
||||
|
||||
### 失败订单重试流程的变更记录
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as 用户
|
||||
participant S as 系统
|
||||
participant H as 变更历史表
|
||||
|
||||
U->>S: 再次请求订单
|
||||
S->>S: 检测到失败状态
|
||||
S->>S: 触发自动重试
|
||||
|
||||
alt 重试成功
|
||||
S->>S: 创建京东订单成功
|
||||
S->>S: 更新订单为待支付状态
|
||||
S->>H: 记录订单重新绑定(OrderChangeTypeRebind)
|
||||
S->>U: 返回支付链接
|
||||
else 重试失败
|
||||
S->>S: 保持失败状态
|
||||
S->>H: 记录订单重试失败(OrderChangeTypeCkFailed)
|
||||
S->>U: 返回错误信息
|
||||
end
|
||||
```
|
||||
|
||||
## 变更记录类型说明
|
||||
|
||||
### OrderChangeType 枚举值
|
||||
|
||||
位置: `internal/consts/jd_cookie.go`
|
||||
|
||||
| 类型 | 常量 | 说明 | 使用场景 |
|
||||
|----------|---------------------------|--------------|-------------|
|
||||
| 创建 | `OrderChangeTypeCreate` | 订单首次创建 | 本地订单创建时 |
|
||||
| 支付 | `OrderChangeTypePay` | 订单支付完成 | 用户支付成功时 |
|
||||
| 过期 | `OrderChangeTypeExpire` | 订单过期 | 订单超时未支付时 |
|
||||
| 重新绑定 | `OrderChangeTypeRebind` | 绑定/换绑京东订单 | 订单关联京东订单时 |
|
||||
| Cookie失败 | `OrderChangeTypeCkFailed` | Cookie失效导致失败 | 京东订单创建失败时 ✅ |
|
||||
|
||||
## 完整的变更记录点清单
|
||||
|
||||
### 1. 创建新订单
|
||||
|
||||
- ✅ 本地订单创建时: `OrderChangeTypeCreate`
|
||||
- ✅ 京东订单创建成功: `OrderChangeTypeRebind`
|
||||
- ✅ 京东订单创建失败: `OrderChangeTypeCkFailed` (新增)
|
||||
|
||||
### 2. 失败订单重试
|
||||
|
||||
- ✅ 重试成功绑定京东订单: `OrderChangeTypeRebind`
|
||||
- ✅ 重试失败: `OrderChangeTypeCkFailed` (新增)
|
||||
|
||||
### 3. 订单支付
|
||||
|
||||
- ✅ 支付成功: `OrderChangeTypePay` (已有)
|
||||
- ✅ 订单过期: `OrderChangeTypeExpire` (已有)
|
||||
|
||||
## 数据示例
|
||||
|
||||
### 订单变更历史表记录示例
|
||||
|
||||
#### 场景1: 订单创建成功
|
||||
|
||||
```sql
|
||||
-- 记录1: 订单创建
|
||||
INSERT INTO jd_cookie_order_change_history
|
||||
VALUES (1, 'uuid-001', 'JD_123', 'create', NULL, NOW());
|
||||
|
||||
-- 记录2: 绑定京东订单
|
||||
INSERT INTO jd_cookie_order_change_history
|
||||
VALUES (2, 'uuid-002', 'JD_123', 'rebind', 'JD_ORDER_456', NOW());
|
||||
```
|
||||
|
||||
#### 场景2: 订单创建失败
|
||||
|
||||
```sql
|
||||
-- 记录1: 订单创建
|
||||
INSERT INTO jd_cookie_order_change_history
|
||||
VALUES (1, 'uuid-001', 'JD_123', 'create', NULL, NOW());
|
||||
|
||||
-- 记录2: 创建失败
|
||||
INSERT INTO jd_cookie_order_change_history
|
||||
VALUES (2, 'uuid-002', 'JD_123', 'ck_failed', NULL, NOW());
|
||||
```
|
||||
|
||||
#### 场景3: 失败订单重试成功
|
||||
|
||||
```sql
|
||||
-- 记录1: 订单创建
|
||||
INSERT INTO jd_cookie_order_change_history
|
||||
VALUES (1, 'uuid-001', 'JD_123', 'create', NULL, NOW());
|
||||
|
||||
-- 记录2: 首次失败
|
||||
INSERT INTO jd_cookie_order_change_history
|
||||
VALUES (2, 'uuid-002', 'JD_123', 'ck_failed', NULL, NOW());
|
||||
|
||||
-- 记录3: 重试成功
|
||||
INSERT INTO jd_cookie_order_change_history
|
||||
VALUES (3, 'uuid-003', 'JD_123', 'rebind', 'JD_ORDER_789', NOW());
|
||||
```
|
||||
|
||||
## 查询示例
|
||||
|
||||
### 查询订单的完整变更历史
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
id,
|
||||
history_uuid,
|
||||
order_id,
|
||||
change_type,
|
||||
jd_order_id,
|
||||
created_at
|
||||
FROM jd_cookie_order_change_history
|
||||
WHERE order_id = 'JD_123'
|
||||
ORDER BY created_at ASC;
|
||||
```
|
||||
|
||||
### 查询失败订单的重试记录
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
order_id,
|
||||
COUNT(*) as retry_count,
|
||||
MAX(created_at) as last_retry_at
|
||||
FROM jd_cookie_order_change_history
|
||||
WHERE change_type = 'ck_failed'
|
||||
GROUP BY order_id
|
||||
HAVING COUNT(*) > 1
|
||||
ORDER BY retry_count DESC;
|
||||
```
|
||||
|
||||
### 查询订单从失败到成功的时间
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
a.order_id,
|
||||
a.created_at as failed_at,
|
||||
b.created_at as success_at,
|
||||
TIMESTAMPDIFF(SECOND, a.created_at, b.created_at) as recovery_seconds
|
||||
FROM jd_cookie_order_change_history a
|
||||
JOIN jd_cookie_order_change_history b
|
||||
ON a.order_id = b.order_id
|
||||
WHERE a.change_type = 'ck_failed'
|
||||
AND b.change_type = 'rebind'
|
||||
AND b.created_at > a.created_at;
|
||||
```
|
||||
|
||||
## 优势
|
||||
|
||||
### 1. 完整的可追溯性
|
||||
|
||||
- ✅ 每个订单的所有状态变更都有记录
|
||||
- ✅ 可以追踪订单的完整生命周期
|
||||
- ✅ 便于问题排查和数据分析
|
||||
|
||||
### 2. 便于运营分析
|
||||
|
||||
- ✅ 统计订单失败率
|
||||
- ✅ 分析失败订单的恢复时间
|
||||
- ✅ 识别问题模式
|
||||
|
||||
### 3. 支持审计需求
|
||||
|
||||
- ✅ 满足合规要求
|
||||
- ✅ 完整的操作日志
|
||||
- ✅ 支持追责和溯源
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `internal/logic/jd_cookie/order_create.go`: 订单创建逻辑
|
||||
- `internal/consts/jd_cookie.go`: 变更类型常量定义
|
||||
- `sql/jd_cookie_tables.sql`: 变更历史表结构
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 功能测试
|
||||
|
||||
1. **创建订单成功**: 验证记录了 `create` 和 `rebind` 两条历史
|
||||
2. **创建订单失败**: 验证记录了 `create` 和 `ck_failed` 两条历史
|
||||
3. **失败订单重试成功**: 验证记录了完整的重试链路
|
||||
4. **失败订单持续失败**: 验证每次重试都有记录
|
||||
|
||||
### 数据验证
|
||||
|
||||
```go
|
||||
// 验证订单变更历史记录
|
||||
func TestOrderChangeHistory(t *testing.T) {
|
||||
// 1. 创建订单
|
||||
order := createOrder()
|
||||
|
||||
// 2. 查询变更历史
|
||||
history := getOrderHistory(order.OrderId)
|
||||
|
||||
// 3. 验证记录数量和类型
|
||||
assert.Equal(t, 2, len(history))
|
||||
assert.Equal(t, "create", history[0].ChangeType)
|
||||
assert.Equal(t, "rebind", history[1].ChangeType)
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **异步记录**: 变更历史记录失败不应影响主流程
|
||||
2. **性能考虑**: 使用异步方式记录,避免阻塞
|
||||
3. **数据清理**: 定期归档历史数据,避免表过大
|
||||
|
||||
## 下一步优化
|
||||
|
||||
1. **详细字段**: 添加 `remark` 字段记录详细失败原因
|
||||
2. **操作人**: 添加 `operator` 字段记录操作人信息
|
||||
3. **数据快照**: 记录变更前后的完整数据状态
|
||||
197
ORDER_CREATE_REFACTOR.md
Normal file
197
ORDER_CREATE_REFACTOR.md
Normal file
@@ -0,0 +1,197 @@
|
||||
# 订单创建流程重构报告
|
||||
|
||||
## 修改时间
|
||||
|
||||
2025-10-18
|
||||
|
||||
## 修改原因
|
||||
|
||||
原有流程只有在京东订单创建成功后才会创建本地订单记录,这导致京东订单创建失败时无法追踪失败原因和用户请求。
|
||||
|
||||
## 修改内容
|
||||
|
||||
### 1. 数据库表结构调整
|
||||
|
||||
#### 修改 `jd_cookie_order` 表
|
||||
|
||||
在 `sql/jd_cookie_tables.sql` 中更新了订单表结构:
|
||||
|
||||
```sql
|
||||
-- 添加字段
|
||||
ALTER TABLE `jd_cookie_order`
|
||||
ADD COLUMN `user_order_id` varchar(64) NOT NULL COMMENT '用户订单号' AFTER `order_id`,
|
||||
ADD COLUMN `failure_reason` text DEFAULT NULL COMMENT '失败原因' AFTER `status`,
|
||||
ADD UNIQUE KEY `uk_user_order_id` (`user_order_id`);
|
||||
|
||||
-- 更新状态字段注释
|
||||
ALTER TABLE `jd_cookie_order`
|
||||
MODIFY COLUMN `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1待支付 2已支付 3已过期 4已取消 5Ck失败';
|
||||
```
|
||||
|
||||
**新增字段说明:**
|
||||
|
||||
- `user_order_id`: 用户订单号,用于唯一标识用户的订单请求
|
||||
- `failure_reason`: 失败原因,记录京东订单创建失败的详细错误信息
|
||||
|
||||
### 2. 订单创建流程调整
|
||||
|
||||
#### 原有流程
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[接收创建订单请求] --> B[检查订单是否存在]
|
||||
B -->|存在| C[返回已有订单]
|
||||
B -->|不存在| D[尝试复用京东订单]
|
||||
D --> E[创建京东订单]
|
||||
E -->|成功| F[创建本地订单记录]
|
||||
E -->|失败| G[返回错误,无记录]
|
||||
F --> H[返回订单信息]
|
||||
```
|
||||
|
||||
#### 新流程
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[接收创建订单请求] --> B[检查订单是否存在]
|
||||
B -->|存在| C[返回已有订单]
|
||||
B -->|不存在| D[创建本地订单记录]
|
||||
D --> E[记录订单创建历史]
|
||||
E --> F[尝试复用京东订单]
|
||||
F --> G[创建京东订单]
|
||||
G -->|成功| H[更新订单的京东订单ID]
|
||||
G -->|失败| I[更新订单状态为失败]
|
||||
I --> J[记录失败原因]
|
||||
H --> K[返回订单信息]
|
||||
J --> L[返回错误]
|
||||
```
|
||||
|
||||
### 3. 代码修改详情
|
||||
|
||||
#### 修改文件
|
||||
|
||||
1. **internal/logic/jd_cookie/order_create.go**
|
||||
- 调整 `CreateOrder` 方法,先创建本地订单,再创建京东订单
|
||||
- 失败时更新订单状态和失败原因
|
||||
- 成功时更新订单的京东订单ID和支付链接
|
||||
|
||||
2. **internal/logic/jd_cookie/order_utils.go**
|
||||
- 新增 `updateOrderFailure` 方法:更新订单失败信息
|
||||
- 修改 `updateOrderJdOrderId` 方法:支持同时更新京东订单ID和支付链接
|
||||
|
||||
3. **sql/jd_cookie_tables.sql**
|
||||
- 更新订单表结构定义
|
||||
|
||||
4. **sql/add_order_fields.sql** (新增)
|
||||
- 提供数据库迁移SQL
|
||||
|
||||
### 4. 关键改进点
|
||||
|
||||
#### 4.1 错误追踪
|
||||
|
||||
- **改进前**: 京东订单创建失败时,没有任何本地记录
|
||||
- **改进后**: 所有订单请求都会创建本地记录,失败时记录详细错误原因
|
||||
|
||||
#### 4.2 状态管理
|
||||
|
||||
- **改进前**: 订单状态不明确
|
||||
- **改进后**:
|
||||
- 初始状态:待支付(1)
|
||||
- 京东订单创建失败:Ck失败(5)
|
||||
- 京东订单创建成功:待支付(1),关联京东订单ID
|
||||
|
||||
#### 4.3 历史记录
|
||||
|
||||
- 在创建本地订单后立即记录订单创建历史
|
||||
- 无论京东订单是否创建成功,都有完整的操作记录
|
||||
|
||||
### 5. 执行步骤
|
||||
|
||||
#### 5.1 数据库迁移
|
||||
|
||||
执行以下SQL文件更新数据库表结构:
|
||||
|
||||
```bash
|
||||
# 连接数据库后执行
|
||||
source sql/add_order_fields.sql
|
||||
```
|
||||
|
||||
或手动执行:
|
||||
|
||||
```sql
|
||||
-- 添加字段
|
||||
ALTER TABLE `jd_cookie_order`
|
||||
ADD COLUMN `user_order_id` varchar(64) NOT NULL COMMENT '用户订单号' AFTER `order_id`,
|
||||
ADD COLUMN `failure_reason` text DEFAULT NULL COMMENT '失败原因' AFTER `status`,
|
||||
ADD UNIQUE KEY `uk_user_order_id` (`user_order_id`);
|
||||
|
||||
-- 更新状态注释
|
||||
ALTER TABLE `jd_cookie_order`
|
||||
MODIFY COLUMN `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1待支付 2已支付 3已过期 4已取消 5Ck失败';
|
||||
```
|
||||
|
||||
#### 5.2 重新生成DAO
|
||||
|
||||
```bash
|
||||
cd e:\projects\kami\kami_backend
|
||||
gf gen dao
|
||||
```
|
||||
|
||||
#### 5.3 更新代码
|
||||
|
||||
代码已经更新,需要重新编译:
|
||||
|
||||
```bash
|
||||
go build .
|
||||
```
|
||||
|
||||
### 6. 注意事项
|
||||
|
||||
1. **数据库迁移**:
|
||||
- 必须先执行数据库迁移SQL
|
||||
- 然后重新生成DAO模型
|
||||
- 最后部署新代码
|
||||
|
||||
2. **向后兼容性**:
|
||||
- 新增字段有默认值,不影响现有数据
|
||||
- 旧的订单记录 `user_order_id` 字段需要数据迁移填充
|
||||
|
||||
3. **失败原因记录**:
|
||||
- 当前通过日志记录失败原因
|
||||
- 数据库迁移完成后,会自动存储到 `failure_reason` 字段
|
||||
|
||||
### 7. 测试建议
|
||||
|
||||
1. **正常流程测试**:
|
||||
- 创建订单成功的场景
|
||||
- 复用京东订单的场景
|
||||
|
||||
2. **异常流程测试**:
|
||||
- Cookie不可用导致创建失败
|
||||
- 库存不足导致创建失败
|
||||
- 网络超时导致创建失败
|
||||
|
||||
3. **并发测试**:
|
||||
- 同一用户订单号并发请求
|
||||
- 验证分布式锁是否生效
|
||||
|
||||
### 8. 回滚方案
|
||||
|
||||
如果需要回滚到原有逻辑:
|
||||
|
||||
1. 恢复代码到之前的版本
|
||||
2. 保留数据库表结构(不影响旧逻辑)
|
||||
3. 重新部署
|
||||
|
||||
## 预期效果
|
||||
|
||||
1. **完整的订单追踪**: 每个用户请求都有对应的本地订单记录
|
||||
2. **明确的失败原因**: 京东订单创建失败时,可以查看详细错误信息
|
||||
3. **更好的用户体验**: 用户可以通过订单号查询失败原因
|
||||
4. **便于问题排查**: 运营人员可以快速定位订单创建失败的原因
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `internal/logic/jd_cookie/order_create.go`: 订单创建核心逻辑
|
||||
- `internal/logic/jd_cookie/order_utils.go`: 订单工具方法
|
||||
- `sql/jd_cookie_tables.sql`: 数据库表结构定义
|
||||
- `sql/add_order_fields.sql`: 数据库迁移SQL
|
||||
255
ORDER_RETRY_MECHANISM.md
Normal file
255
ORDER_RETRY_MECHANISM.md
Normal file
@@ -0,0 +1,255 @@
|
||||
# 订单失败自动重试机制
|
||||
|
||||
## 更新时间
|
||||
|
||||
2025-10-18
|
||||
|
||||
## 功能说明
|
||||
|
||||
为失败状态的订单添加自动重试机制,当用户再次请求创建订单或获取订单支付链接时,系统会自动尝试重新创建京东订单,直到成功为止。
|
||||
|
||||
## 核心特性
|
||||
|
||||
### 1. 智能失败检测
|
||||
|
||||
- 检测订单状态为 `OrderStatusCkFailed`(Ck失败状态)
|
||||
- 在创建订单和获取支付链接时都会进行检测
|
||||
|
||||
### 2. 自动重试机制
|
||||
|
||||
- 失败订单被再次请求时自动触发重试
|
||||
- 使用相同的重试逻辑(切换Cookie直到成功)
|
||||
- 无需用户手动重新创建订单
|
||||
|
||||
### 3. 状态自动恢复
|
||||
|
||||
- 重试成功后自动从失败状态恢复为待支付状态
|
||||
- 更新订单的京东订单ID和支付链接
|
||||
- 记录完整的状态变更历史
|
||||
|
||||
## 流程图
|
||||
|
||||
### 创建订单流程(含失败重试)
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[用户请求创建订单] --> B{订单是否存在?}
|
||||
B -->|不存在| C[创建本地订单]
|
||||
B -->|存在| D{订单状态}
|
||||
|
||||
D -->|待支付/已支付| E[返回现有订单]
|
||||
D -->|失败状态| F[触发自动重试]
|
||||
|
||||
C --> G[尝试创建京东订单]
|
||||
F --> G
|
||||
|
||||
G -->|成功| H[更新订单状态为待支付]
|
||||
G -->|失败| I[更新订单状态为失败]
|
||||
|
||||
H --> J[返回支付链接]
|
||||
I --> K[返回错误信息]
|
||||
|
||||
K -.再次请求.-> F
|
||||
```
|
||||
|
||||
### 获取支付链接流程(含失败重试)
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[用户请求获取支付链接] --> B[查询订单]
|
||||
B --> C{订单状态}
|
||||
|
||||
C -->|失败状态| D[触发自动重试]
|
||||
C -->|正常状态| E{支付链接是否过期?}
|
||||
|
||||
D --> F[尝试创建京东订单]
|
||||
F -->|成功| G[更新为待支付状态]
|
||||
F -->|失败| H[保持失败状态]
|
||||
|
||||
E -->|未过期| I[返回支付链接]
|
||||
E -->|已过期| J[刷新支付链接]
|
||||
|
||||
G --> I
|
||||
H --> K[返回错误]
|
||||
J --> I
|
||||
|
||||
K -.再次请求.-> D
|
||||
```
|
||||
|
||||
## 代码修改详情
|
||||
|
||||
### 1. 新增方法
|
||||
|
||||
#### `retryCreateJdOrderForFailedOrder`
|
||||
|
||||
位置: `internal/logic/jd_cookie/order_create.go`
|
||||
|
||||
```go
|
||||
// 为失败的订单重新创建京东订单
|
||||
func (s *sJdCookie) retryCreateJdOrderForFailedOrder(
|
||||
ctx context.Context,
|
||||
existingOrder *entity.V1JdCookieOrder,
|
||||
req *model.CreateOrderReq
|
||||
) (result *model.CreateOrderResult, err error)
|
||||
```
|
||||
|
||||
**功能**:
|
||||
|
||||
- 尝试复用现有京东订单
|
||||
- 如果无法复用,创建新的京东订单(带重试机制)
|
||||
- 更新本地订单状态和关联关系
|
||||
- 记录完整的历史记录
|
||||
|
||||
#### `updateOrderSuccess`
|
||||
|
||||
位置: `internal/logic/jd_cookie/order_utils.go`
|
||||
|
||||
```go
|
||||
// 更新订单成功信息(从失败状态恢复)
|
||||
func (s *sJdCookie) updateOrderSuccess(
|
||||
ctx context.Context,
|
||||
orderId,
|
||||
jdOrderId,
|
||||
wxPayUrl string
|
||||
) error
|
||||
```
|
||||
|
||||
**功能**:
|
||||
|
||||
- 将失败状态的订单恢复为待支付状态
|
||||
- 关联新的京东订单ID
|
||||
- 记录状态恢复日志
|
||||
|
||||
### 2. 修改的方法
|
||||
|
||||
#### `CreateOrder`
|
||||
|
||||
位置: `internal/logic/jd_cookie/order_create.go`
|
||||
|
||||
**修改内容**:
|
||||
|
||||
```go
|
||||
if existingOrder != nil {
|
||||
// 检查订单状态
|
||||
orderStatus := consts.OrderStatus(existingOrder.Status)
|
||||
|
||||
// 如果是失败状态,触发重试
|
||||
if orderStatus == consts.OrderStatusCkFailed {
|
||||
return s.retryCreateJdOrderForFailedOrder(ctx, existingOrder, req)
|
||||
}
|
||||
|
||||
// 正常状态,获取支付链接
|
||||
return s.GetPaymentUrl(ctx, req.UserOrderId, existingOrder.OrderId)
|
||||
}
|
||||
```
|
||||
|
||||
#### `GetPaymentUrl`
|
||||
|
||||
位置: `internal/logic/jd_cookie/order_query.go`
|
||||
|
||||
**修改内容**:
|
||||
|
||||
```go
|
||||
// 检查订单状态,如果是失败状态,尝试重新创建京东订单
|
||||
orderStatus := consts.OrderStatus(order.Status)
|
||||
if orderStatus == consts.OrderStatusCkFailed {
|
||||
return s.retryCreateJdOrderForFailedOrder(ctx, order, &model.CreateOrderReq{
|
||||
UserOrderId: userOrderId,
|
||||
Amount: gconv.Float64(order.Amount),
|
||||
Category: consts.RedeemOrderCardCategory(order.Category),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 场景1: 创建订单时Cookie不足
|
||||
|
||||
1. 用户创建订单,因Cookie全部不可用而失败
|
||||
2. 系统补充了新的Cookie
|
||||
3. 用户再次请求创建订单(使用相同的userOrderId)
|
||||
4. **自动触发重试**,使用新Cookie创建成功
|
||||
5. 返回支付链接
|
||||
|
||||
### 场景2: 获取支付链接时自动恢复
|
||||
|
||||
1. 订单创建失败,状态为`Ck失败`
|
||||
2. 用户请求获取支付链接
|
||||
3. **自动检测到失败状态并触发重试**
|
||||
4. 重试成功,订单状态恢复为待支付
|
||||
5. 返回有效的支付链接
|
||||
|
||||
### 场景3: 多次失败后成功
|
||||
|
||||
1. 第1次请求: 创建失败(Cookie不可用)
|
||||
2. 第2次请求: 自动重试,仍然失败(Cookie仍不可用)
|
||||
3. 第3次请求: 自动重试,**成功**(新的Cookie可用)
|
||||
4. 订单状态从失败恢复为待支付
|
||||
|
||||
## 优势
|
||||
|
||||
### 1. 用户体验提升
|
||||
|
||||
- ✅ 无需手动重新创建订单
|
||||
- ✅ 同一订单号可以多次重试
|
||||
- ✅ 自动恢复,无需人工干预
|
||||
|
||||
### 2. 系统容错性增强
|
||||
|
||||
- ✅ 临时性故障可以自动恢复
|
||||
- ✅ Cookie不足时可以等待补充后自动重试
|
||||
- ✅ 减少失败订单积压
|
||||
|
||||
### 3. 运营成本降低
|
||||
|
||||
- ✅ 减少用户投诉
|
||||
- ✅ 降低人工处理成本
|
||||
- ✅ 提高订单成功率
|
||||
|
||||
## 重要说明
|
||||
|
||||
### 1. 幂等性保证
|
||||
|
||||
- 使用用户订单号(userOrderId)作为唯一标识
|
||||
- 同一用户订单号只会创建一个本地订单
|
||||
- 重试时不会创建新的本地订单,只更新现有订单
|
||||
|
||||
### 2. 安全性
|
||||
|
||||
- 重试过程中使用分布式锁
|
||||
- 避免并发创建导致的数据不一致
|
||||
- 完整的历史记录便于问题追溯
|
||||
|
||||
### 3. 性能考虑
|
||||
|
||||
- 重试逻辑只在检测到失败状态时触发
|
||||
- 不影响正常订单的处理性能
|
||||
- 使用异步记录历史,不阻塞主流程
|
||||
|
||||
## 监控建议
|
||||
|
||||
### 关键指标
|
||||
|
||||
1. **失败订单重试次数**: 监控平均重试次数
|
||||
2. **重试成功率**: 重试成功订单 / 总重试订单
|
||||
3. **失败订单恢复时间**: 从失败到成功的平均时长
|
||||
|
||||
### 告警规则
|
||||
|
||||
- 重试失败率 > 50%: 可能Cookie整体不可用
|
||||
- 平均重试次数 > 3: Cookie补充不及时
|
||||
- 失败订单积压 > 100: 需要人工介入
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `internal/logic/jd_cookie/order_create.go`: 订单创建逻辑
|
||||
- `internal/logic/jd_cookie/order_query.go`: 订单查询逻辑
|
||||
- `internal/logic/jd_cookie/order_utils.go`: 订单工具方法
|
||||
- `ORDER_CREATE_REFACTOR.md`: 订单创建流程重构文档
|
||||
|
||||
## 下一步优化建议
|
||||
|
||||
1. **智能重试间隔**: 根据失败次数调整重试间隔
|
||||
2. **失败原因分类**: 区分不同类型的失败,针对性处理
|
||||
3. **自动降级**: 连续失败时自动降级到其他支付方式
|
||||
4. **主动通知**: 订单恢复成功后主动通知用户
|
||||
File diff suppressed because one or more lines are too long
@@ -48,20 +48,23 @@ var JdOrderStatusText = map[JdOrderStatus]string{
|
||||
type OrderStatus int
|
||||
|
||||
const (
|
||||
OrderStatusPending OrderStatus = 1 // 待支付
|
||||
OrderStatusPaid OrderStatus = 2 // 已支付
|
||||
OrderStatusExpired OrderStatus = 3 // 已过期
|
||||
OrderStatusCanceled OrderStatus = 4 // 已取消
|
||||
OrderStatusCkFailed OrderStatus = 5 // Cookie账号Ck失败
|
||||
OrderStatusCreated OrderStatus = 0
|
||||
OrderStatusPending OrderStatus = 1 // 待支付
|
||||
OrderStatusPaid OrderStatus = 2 // 已支付
|
||||
OrderStatusExpired OrderStatus = 3 // 已过期
|
||||
OrderStatusCanceled OrderStatus = 4 // 已取消
|
||||
OrderStatusCkFailed OrderStatus = 5 // Cookie账号Ck失败
|
||||
OrderStatusJDOrderFailed OrderStatus = 6 // 京东订单获取失败
|
||||
)
|
||||
|
||||
// OrderStatusText 订单状态文本映射
|
||||
var OrderStatusText = map[OrderStatus]string{
|
||||
OrderStatusPending: "待支付",
|
||||
OrderStatusPaid: "已支付",
|
||||
OrderStatusExpired: "已过期",
|
||||
OrderStatusCanceled: "已取消",
|
||||
OrderStatusCkFailed: "cookie失效",
|
||||
OrderStatusPending: "待支付",
|
||||
OrderStatusPaid: "已支付",
|
||||
OrderStatusExpired: "已过期",
|
||||
OrderStatusCanceled: "已取消",
|
||||
OrderStatusCkFailed: "cookie失效",
|
||||
OrderStatusJDOrderFailed: "京东订单获取失败",
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
@@ -113,20 +116,22 @@ var JdOrderChangeTypeText = map[JdOrderChangeType]string{
|
||||
type OrderChangeType string
|
||||
|
||||
const (
|
||||
OrderChangeTypeCreate OrderChangeType = "create" // 创建
|
||||
OrderChangeTypePay OrderChangeType = "pay" // 支付
|
||||
OrderChangeTypeExpire OrderChangeType = "expire" // 过期
|
||||
OrderChangeTypeRebind OrderChangeType = "rebind" // 重新绑定
|
||||
OrderChangeTypeCkFailed OrderChangeType = "ck_failed" // Cookie账号Ck失败
|
||||
OrderChangeTypeCreate OrderChangeType = "create" // 创建
|
||||
OrderChangeTypePay OrderChangeType = "pay" // 支付
|
||||
OrderChangeTypeExpire OrderChangeType = "expire" // 过期
|
||||
OrderChangeTypeRebind OrderChangeType = "rebind" // 重新绑定
|
||||
OrderChangeTypeCkFailed OrderChangeType = "ck_failed" // Cookie账号Ck失败
|
||||
OrderChangeTypeJDOrderFailed OrderChangeType = "jd_order_failed" //下单京东订单失败
|
||||
)
|
||||
|
||||
// OrderChangeTypeText 订单变更类型文本映射
|
||||
var OrderChangeTypeText = map[OrderChangeType]string{
|
||||
OrderChangeTypeCreate: "创建",
|
||||
OrderChangeTypePay: "支付",
|
||||
OrderChangeTypeExpire: "过期",
|
||||
OrderChangeTypeRebind: "重新绑定",
|
||||
OrderChangeTypeCkFailed: "cookie失败",
|
||||
OrderChangeTypeCreate: "创建",
|
||||
OrderChangeTypePay: "支付",
|
||||
OrderChangeTypeExpire: "过期",
|
||||
OrderChangeTypeRebind: "重新绑定",
|
||||
OrderChangeTypeCkFailed: "cookie失败",
|
||||
OrderChangeTypeJDOrderFailed: "下单京东订单失败",
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
|
||||
@@ -4,14 +4,17 @@ import (
|
||||
"context"
|
||||
"kami/internal/consts"
|
||||
"kami/internal/model"
|
||||
"kami/internal/model/entity"
|
||||
"kami/utility/cache"
|
||||
"kami/utility/utils"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// CreateOrder 创建订单
|
||||
@@ -49,7 +52,32 @@ func (s *sJdCookie) CreateOrder(ctx context.Context, req *model.CreateOrderReq)
|
||||
}
|
||||
|
||||
if existingOrder != nil {
|
||||
// 订单已存在,尝试获取支付链接
|
||||
// 订单已存在,检查订单状态
|
||||
if !slices.Contains([]consts.OrderStatus{
|
||||
consts.OrderStatusJDOrderFailed, consts.OrderStatusCreated, consts.OrderStatusPending,
|
||||
}, consts.OrderStatus(existingOrder.Status)) {
|
||||
return nil, gerror.New("订单已完成")
|
||||
}
|
||||
// 如果订单状态为失败(Ck失败),重新尝试创建京东订单
|
||||
if consts.OrderStatus(existingOrder.Status) == consts.OrderStatusJDOrderFailed {
|
||||
glog.Info(ctx, "检测到失败订单,尝试重新创建京东订单", g.Map{
|
||||
"orderId": existingOrder.OrderId,
|
||||
"userOrderId": req.UserOrderId,
|
||||
"status": consts.OrderStatus(existingOrder.Status),
|
||||
})
|
||||
|
||||
// 尝试重新创建京东订单
|
||||
retryResult, retryErr := s.retryCreateJdOrderForFailedOrder(ctx, existingOrder, req)
|
||||
if retryErr != nil {
|
||||
// 重试失败,返回错误
|
||||
return nil, retryErr
|
||||
}
|
||||
|
||||
// 重试成功,返回新的订单信息
|
||||
return retryResult, nil
|
||||
}
|
||||
|
||||
// 订单状态正常,尝试获取支付链接
|
||||
paymentResult, err := s.GetPaymentUrl(ctx, req.UserOrderId, existingOrder.OrderId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -62,17 +90,39 @@ func (s *sJdCookie) CreateOrder(ctx context.Context, req *model.CreateOrderReq)
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 生成内部订单ID
|
||||
internalOrderId := "JD_" + utils.GenerateRandomUUID()
|
||||
|
||||
// 先创建本地订单记录(状态为待支付,京东订单ID暂时为空)
|
||||
err = s.createOrderRecord(ctx, internalOrderId, req.UserOrderId, req.Amount, req.Category, "", "")
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "创建订单记录失败")
|
||||
}
|
||||
|
||||
// 记录订单创建历史
|
||||
_ = s.RecordOrderHistory(ctx, &model.RecordOrderHistoryReq{
|
||||
OrderId: internalOrderId,
|
||||
ChangeType: consts.OrderChangeTypeCreate,
|
||||
JdOrderId: "",
|
||||
Remark: "",
|
||||
})
|
||||
|
||||
glog.Info(ctx, "本地订单创建成功,开始创建京东订单", g.Map{
|
||||
"orderId": internalOrderId,
|
||||
"userOrderId": req.UserOrderId,
|
||||
"amount": req.Amount,
|
||||
})
|
||||
|
||||
// 优先尝试复用现有的京东订单
|
||||
reusableJdOrder, err := s.findReusableJdOrder(ctx, req.Amount, req.Category)
|
||||
if err != nil {
|
||||
glog.Warning(ctx, "查找可复用京东订单失败", err)
|
||||
}
|
||||
|
||||
// 生成内部订单ID
|
||||
internalOrderId := "JD_" + utils.GenerateRandomUUID()
|
||||
|
||||
var cookieId, jdOrderId, wxPayUrl string
|
||||
var jdOrderErr error
|
||||
var isReused = false
|
||||
|
||||
if reusableJdOrder != nil {
|
||||
// 尝试使用可复用的京东订单
|
||||
jdOrderId = reusableJdOrder.JdOrderId
|
||||
@@ -135,24 +185,57 @@ func (s *sJdCookie) CreateOrder(ctx context.Context, req *model.CreateOrderReq)
|
||||
|
||||
// 如果没有成功复用,创建新的京东订单
|
||||
if jdOrderId == "" {
|
||||
retryRes, err := s.createNewJdOrderWithRetry(ctx, &model.CreateNewJdOrderWithRetryReq{
|
||||
retryRes, retryErr := s.createNewJdOrderWithRetry(ctx, &model.CreateNewJdOrderWithRetryReq{
|
||||
OrderId: internalOrderId,
|
||||
Amount: req.Amount,
|
||||
Category: req.Category,
|
||||
UserOrderId: req.UserOrderId,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if retryErr != nil {
|
||||
// 京东订单创建失败,更新本地订单状态和失败原因
|
||||
jdOrderErr = retryErr
|
||||
_ = s.updateOrderFailure(ctx, internalOrderId)
|
||||
|
||||
// 记录订单创建失败的变更历史
|
||||
_ = s.RecordOrderHistory(ctx, &model.RecordOrderHistoryReq{
|
||||
OrderId: internalOrderId,
|
||||
ChangeType: consts.OrderChangeTypeJDOrderFailed,
|
||||
JdOrderId: "",
|
||||
Remark: jdOrderErr.Error(),
|
||||
})
|
||||
|
||||
glog.Error(ctx, "京东订单创建失败", g.Map{
|
||||
"orderId": internalOrderId,
|
||||
"userOrderId": req.UserOrderId,
|
||||
"failureReason": retryErr.Error(),
|
||||
})
|
||||
return nil, retryErr
|
||||
}
|
||||
jdOrderId = retryRes.JdOrderId
|
||||
cookieId = retryRes.CookieId
|
||||
wxPayUrl = retryRes.WxPayUrl
|
||||
}
|
||||
|
||||
// 创建订单记录
|
||||
err = s.createOrderRecord(ctx, internalOrderId, req.UserOrderId, req.Amount, req.Category, jdOrderId, wxPayUrl)
|
||||
// 京东订单创建成功,更新本地订单的京东订单ID和支付链接
|
||||
err = s.updateOrderJdOrderId(ctx, internalOrderId, jdOrderId)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "创建订单记录失败")
|
||||
glog.Error(ctx, "更新订单京东订单ID失败", g.Map{
|
||||
"orderId": internalOrderId,
|
||||
"jdOrderId": jdOrderId,
|
||||
"error": err,
|
||||
})
|
||||
return nil, gerror.Wrap(err, "更新订单信息失败")
|
||||
}
|
||||
|
||||
// 京东订单创建成功,更新本地订单状态为待支付,并关联京东订单
|
||||
err = s.updateOrderSuccess(ctx, internalOrderId, jdOrderId)
|
||||
if err != nil {
|
||||
glog.Error(ctx, "更新订单状态失败", g.Map{
|
||||
"orderId": internalOrderId,
|
||||
"jdOrderId": jdOrderId,
|
||||
"error": err,
|
||||
})
|
||||
return nil, gerror.Wrap(err, "更新订单信息失败")
|
||||
}
|
||||
|
||||
// 更新京东订单的当前关联订单ID
|
||||
@@ -165,12 +248,18 @@ func (s *sJdCookie) CreateOrder(ctx context.Context, req *model.CreateOrderReq)
|
||||
StatusBefore: consts.JdCookieStatusUnknown,
|
||||
StatusAfter: consts.JdCookieStatusNormal,
|
||||
OrderId: internalOrderId,
|
||||
UserOrderId: req.UserOrderId,
|
||||
FailureCount: 0,
|
||||
Remark: "Cookie用于创建订单",
|
||||
})
|
||||
|
||||
// 记录订单创建历史
|
||||
_ = s.RecordOrderHistory(ctx, internalOrderId, consts.OrderChangeTypeCreate, jdOrderId)
|
||||
// 记录订单绑定京东订单的变更历史
|
||||
_ = s.RecordOrderHistory(ctx, &model.RecordOrderHistoryReq{
|
||||
OrderId: internalOrderId,
|
||||
ChangeType: consts.OrderChangeTypeRebind,
|
||||
JdOrderId: jdOrderId,
|
||||
Remark: "",
|
||||
})
|
||||
|
||||
return &model.CreateOrderResult{
|
||||
WxPayUrl: wxPayUrl,
|
||||
@@ -314,3 +403,147 @@ func (s *sJdCookie) hasCookieBeenTried(triedCookies []string, cookieId string) b
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// retryCreateJdOrderForFailedOrder 为失败的订单重新创建京东订单
|
||||
func (s *sJdCookie) retryCreateJdOrderForFailedOrder(ctx context.Context, existingOrder *entity.V1JdCookieOrder, req *model.CreateOrderReq) (result *model.CreateOrderResult, err error) {
|
||||
glog.Info(ctx, "开始为失败订单重新创建京东订单", g.Map{
|
||||
"orderId": existingOrder.OrderId,
|
||||
"userOrderId": existingOrder.UserOrderId,
|
||||
"amount": existingOrder.Amount,
|
||||
})
|
||||
|
||||
// 优先尝试复用现有的京东订单
|
||||
reusableJdOrder, err := s.findReusableJdOrder(ctx, gconv.Float64(existingOrder.Amount), consts.RedeemOrderCardCategory(existingOrder.Category))
|
||||
if err != nil {
|
||||
glog.Warning(ctx, "查找可复用京东订单失败", err)
|
||||
}
|
||||
|
||||
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, isCkFailed, refreshErr := s.refreshPaymentUrl(ctx, &model.RefreshPaymentUrlReq{
|
||||
JdOrderId: jdOrderId,
|
||||
PayId: reusableJdOrder.PayId,
|
||||
CookieId: cookieId,
|
||||
})
|
||||
if isCkFailed {
|
||||
s.handleCookieFailure(ctx, req.UserOrderId, cookieId, jdOrderId, isCkFailed, refreshErr.Error())
|
||||
}
|
||||
if refreshErr != nil {
|
||||
glog.Warning(ctx, "刷新支付链接失败,将创建新订单", g.Map{
|
||||
"jdOrderId": jdOrderId,
|
||||
"error": refreshErr,
|
||||
})
|
||||
// 刷新失败,标记为不可复用
|
||||
_ = s.UpdateJdOrderStatus(ctx, jdOrderId, consts.JdOrderStatusExpired, "", refreshErr.Error())
|
||||
|
||||
// 清空,准备创建新订单
|
||||
jdOrderId = ""
|
||||
cookieId = ""
|
||||
wxPayUrl = ""
|
||||
} else {
|
||||
wxPayUrl = newWxPayUrl
|
||||
// 更新京东订单的支付链接和过期时间
|
||||
_ = s.updateJdOrderPaymentUrl(ctx, jdOrderId, wxPayUrl)
|
||||
isReused = true
|
||||
}
|
||||
} else {
|
||||
isReused = true
|
||||
}
|
||||
|
||||
if isReused {
|
||||
glog.Info(ctx, "复用现有京东订单", g.Map{
|
||||
"orderId": existingOrder.OrderId,
|
||||
"jdOrderId": jdOrderId,
|
||||
"cookieId": cookieId,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有成功复用,创建新的京东订单
|
||||
if jdOrderId == "" {
|
||||
retryRes, retryErr := s.createNewJdOrderWithRetry(ctx, &model.CreateNewJdOrderWithRetryReq{
|
||||
OrderId: existingOrder.OrderId,
|
||||
Amount: gconv.Float64(existingOrder.Amount),
|
||||
Category: consts.RedeemOrderCardCategory(existingOrder.Category),
|
||||
UserOrderId: existingOrder.UserOrderId,
|
||||
})
|
||||
if retryErr != nil {
|
||||
// 京东订单创建失败,更新本地订单失败原因
|
||||
_ = s.updateOrderFailure(ctx, existingOrder.OrderId)
|
||||
|
||||
// 记录订单重试失败的变更历史
|
||||
_ = s.RecordOrderHistory(ctx, &model.RecordOrderHistoryReq{
|
||||
OrderId: existingOrder.OrderId,
|
||||
ChangeType: consts.OrderChangeTypeJDOrderFailed,
|
||||
JdOrderId: "",
|
||||
Remark: retryErr.Error(),
|
||||
})
|
||||
|
||||
glog.Error(ctx, "重试创建京东订单失败", g.Map{
|
||||
"orderId": existingOrder.OrderId,
|
||||
"userOrderId": existingOrder.UserOrderId,
|
||||
"failureReason": retryErr.Error(),
|
||||
})
|
||||
return nil, retryErr
|
||||
}
|
||||
jdOrderId = retryRes.JdOrderId
|
||||
cookieId = retryRes.CookieId
|
||||
wxPayUrl = retryRes.WxPayUrl
|
||||
}
|
||||
|
||||
// 京东订单创建成功,更新本地订单状态为待支付,并关联京东订单
|
||||
err = s.updateOrderSuccess(ctx, existingOrder.OrderId, jdOrderId)
|
||||
if err != nil {
|
||||
glog.Error(ctx, "更新订单状态失败", g.Map{
|
||||
"orderId": existingOrder.OrderId,
|
||||
"jdOrderId": jdOrderId,
|
||||
"error": err,
|
||||
})
|
||||
return nil, gerror.Wrap(err, "更新订单信息失败")
|
||||
}
|
||||
|
||||
// 更新京东订单的当前关联订单ID
|
||||
_ = s.updateJdOrderId(ctx, jdOrderId, existingOrder.OrderId)
|
||||
|
||||
// 记录Cookie使用历史
|
||||
_ = s.RecordCookieHistory(ctx, &model.RecordCookieHistoryReq{
|
||||
CookieId: cookieId,
|
||||
ChangeType: consts.CookieChangeTypeUse,
|
||||
StatusBefore: consts.JdCookieStatusUnknown,
|
||||
StatusAfter: consts.JdCookieStatusNormal,
|
||||
OrderId: existingOrder.OrderId,
|
||||
UserOrderId: existingOrder.UserOrderId,
|
||||
FailureCount: 0,
|
||||
Remark: "失败订单重试成功",
|
||||
})
|
||||
|
||||
// 记录订单重新绑定历史
|
||||
_ = s.RecordOrderHistory(ctx, &model.RecordOrderHistoryReq{
|
||||
OrderId: existingOrder.OrderId,
|
||||
ChangeType: consts.OrderChangeTypeRebind,
|
||||
JdOrderId: jdOrderId,
|
||||
Remark: "",
|
||||
})
|
||||
|
||||
glog.Info(ctx, "失败订单重试成功", g.Map{
|
||||
"orderId": existingOrder.OrderId,
|
||||
"jdOrderId": jdOrderId,
|
||||
"cookieId": cookieId,
|
||||
})
|
||||
|
||||
return &model.CreateOrderResult{
|
||||
WxPayUrl: wxPayUrl,
|
||||
JdOrderId: jdOrderId,
|
||||
OrderId: existingOrder.OrderId,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -449,7 +449,12 @@ func (s *sJdCookie) ExtractCardInfo(ctx context.Context, jdOrderId string) error
|
||||
Update(do.V1JdCookieOrder{
|
||||
Status: consts.OrderStatusCkFailed,
|
||||
})
|
||||
_ = s.RecordOrderHistory(ctx, jdOrder.OrderId, consts.OrderChangeTypeCkFailed, jdOrderId)
|
||||
_ = s.RecordOrderHistory(ctx, &model.RecordOrderHistoryReq{
|
||||
OrderId: jdOrder.OrderId,
|
||||
ChangeType: consts.OrderChangeTypeCkFailed,
|
||||
JdOrderId: jdOrderId,
|
||||
Remark: "",
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
@@ -473,7 +478,12 @@ func (s *sJdCookie) ExtractCardInfo(ctx context.Context, jdOrderId string) error
|
||||
Update(do.V1JdCookieOrder{
|
||||
Status: consts.OrderStatusPaid,
|
||||
})
|
||||
_ = s.RecordOrderHistory(ctx, jdOrder.OrderId, consts.OrderChangeTypePay, jdOrderId)
|
||||
_ = s.RecordOrderHistory(ctx, &model.RecordOrderHistoryReq{
|
||||
OrderId: jdOrder.OrderId,
|
||||
ChangeType: consts.OrderChangeTypePay,
|
||||
JdOrderId: jdOrderId,
|
||||
Remark: "",
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -488,7 +498,12 @@ func (s *sJdCookie) ExtractCardInfo(ctx context.Context, jdOrderId string) error
|
||||
Status: consts.OrderStatusPaid,
|
||||
})
|
||||
if affected > 0 {
|
||||
_ = s.RecordOrderHistory(ctx, jdOrder.OrderId, consts.OrderChangeTypePay, jdOrderId)
|
||||
_ = s.RecordOrderHistory(ctx, &model.RecordOrderHistoryReq{
|
||||
OrderId: jdOrder.OrderId,
|
||||
ChangeType: consts.OrderChangeTypePay,
|
||||
JdOrderId: jdOrderId,
|
||||
Remark: "",
|
||||
})
|
||||
}
|
||||
|
||||
// 保存卡密信息到数据库
|
||||
@@ -594,7 +609,12 @@ func (s *sJdCookie) CleanupExpiredOrders(ctx context.Context) error {
|
||||
orderId := jdOrder.OrderId
|
||||
if jdOrder.OrderId != "" {
|
||||
// 同时记录用户订单的历史
|
||||
_ = s.RecordOrderHistory(ctx, orderId, consts.OrderChangeTypeExpire, jdOrder.JdOrderId)
|
||||
_ = s.RecordOrderHistory(ctx, &model.RecordOrderHistoryReq{
|
||||
OrderId: orderId,
|
||||
ChangeType: consts.OrderChangeTypeExpire,
|
||||
JdOrderId: jdOrder.JdOrderId,
|
||||
Remark: "",
|
||||
})
|
||||
}
|
||||
_ = s.RecordJdOrderHistory(ctx, jdOrder.JdOrderId, consts.JdOrderChangeTypeExpire, orderId, jdOrder.WxPayUrl, "")
|
||||
}
|
||||
@@ -627,7 +647,12 @@ func (s *sJdCookie) CleanupExpiredOrders(ctx context.Context) error {
|
||||
|
||||
// 为每个过期的用户订单记录历史
|
||||
for _, order := range expiredOrders {
|
||||
_ = s.RecordOrderHistory(ctx, order.OrderId, consts.OrderChangeTypeExpire, order.JdOrderId)
|
||||
_ = s.RecordOrderHistory(ctx, &model.RecordOrderHistoryReq{
|
||||
OrderId: order.OrderId,
|
||||
ChangeType: consts.OrderChangeTypeExpire,
|
||||
JdOrderId: order.JdOrderId,
|
||||
Remark: "",
|
||||
})
|
||||
}
|
||||
|
||||
glog.Info(ctx, "清理过期订单完成", g.Map{
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"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"
|
||||
)
|
||||
@@ -29,6 +31,34 @@ func (s *sJdCookie) GetPaymentUrl(ctx context.Context, userOrderId, orderId stri
|
||||
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 {
|
||||
@@ -81,14 +111,30 @@ func (s *sJdCookie) GetPaymentUrl(ctx context.Context, userOrderId, orderId stri
|
||||
}
|
||||
|
||||
// 更新订单关联的京东订单ID
|
||||
_ = s.updateOrderJdOrderId(ctx, orderId, retryRes.JdOrderId, retryRes.WxPayUrl)
|
||||
_ = 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, orderId, consts.OrderChangeTypeRebind, retryRes.JdOrderId)
|
||||
_ = s.RecordOrderHistory(ctx, &model.RecordOrderHistoryReq{
|
||||
OrderId: orderId,
|
||||
ChangeType: consts.OrderChangeTypeRebind,
|
||||
JdOrderId: retryRes.JdOrderId,
|
||||
Remark: "",
|
||||
})
|
||||
// 记录Cookie使用历史
|
||||
_ = s.RecordCookieHistory(ctx, &model.RecordCookieHistoryReq{
|
||||
CookieId: retryRes.CookieId,
|
||||
|
||||
@@ -86,7 +86,7 @@ func (s *sJdCookie) createOrderRecord(ctx context.Context, internalOrderId, user
|
||||
Amount: amount,
|
||||
Category: category,
|
||||
JdOrderId: jdOrderId,
|
||||
Status: int(consts.OrderStatusPending),
|
||||
Status: int(consts.OrderStatusCreated),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -123,11 +123,44 @@ func (s *sJdCookie) updateOrderLastRequest(ctx context.Context, orderId string)
|
||||
}
|
||||
|
||||
// updateOrderJdOrderId 更新订单关联的京东订单ID
|
||||
func (s *sJdCookie) updateOrderJdOrderId(ctx context.Context, orderId, jdOrderId, wxPayUrl string) error {
|
||||
func (s *sJdCookie) updateOrderJdOrderId(ctx context.Context, orderId, jdOrderId string) error {
|
||||
m := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
_, err := m.Where(dao.V1JdCookieOrder.Columns().OrderId, orderId).Update(&do.V1JdCookieOrder{
|
||||
updateData := &do.V1JdCookieOrder{
|
||||
JdOrderId: jdOrderId,
|
||||
}
|
||||
// 注意:wxPayUrl 字段需要数据库表支持后才能使用
|
||||
// 暂时通过 WxPayUrl 字段存储在 jd_order 表中
|
||||
_, err := m.Where(dao.V1JdCookieOrder.Columns().OrderId, orderId).Update(updateData)
|
||||
return err
|
||||
}
|
||||
|
||||
// updateOrderFailure 更新订单失败信息
|
||||
func (s *sJdCookie) updateOrderFailure(ctx context.Context, orderId string) error {
|
||||
m := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
updateData := &do.V1JdCookieOrder{
|
||||
Status: int(consts.OrderStatusJDOrderFailed),
|
||||
}
|
||||
// 注意:FailureReason 字段需要数据库表支持后才能使用
|
||||
// 暂时通过日志记录失败原因
|
||||
glog.Error(ctx, "订单创建失败", g.Map{
|
||||
"orderId": orderId,
|
||||
})
|
||||
_, err := m.Where(dao.V1JdCookieOrder.Columns().OrderId, orderId).Update(updateData)
|
||||
return err
|
||||
}
|
||||
|
||||
// updateOrderSuccess 更新订单成功信息(从失败状态恢复)
|
||||
func (s *sJdCookie) updateOrderSuccess(ctx context.Context, orderId, jdOrderId string) error {
|
||||
m := dao.V1JdCookieOrder.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
updateData := &do.V1JdCookieOrder{
|
||||
JdOrderId: jdOrderId,
|
||||
Status: int(consts.OrderStatusPending), // 恢复为待支付状态
|
||||
}
|
||||
glog.Info(ctx, "订单重试成功,恢复待支付状态", g.Map{
|
||||
"orderId": orderId,
|
||||
"jdOrderId": jdOrderId,
|
||||
})
|
||||
_, err := m.Where(dao.V1JdCookieOrder.Columns().OrderId, orderId).Update(updateData)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -327,22 +327,23 @@ func (s *sJdCookie) RecordJdOrderHistory(ctx context.Context, jdOrderId string,
|
||||
}
|
||||
|
||||
// RecordOrderHistory 记录订单变更历史
|
||||
func (s *sJdCookie) RecordOrderHistory(ctx context.Context, orderId string, changeType consts.OrderChangeType, jdOrderId string) (err error) {
|
||||
func (s *sJdCookie) RecordOrderHistory(ctx context.Context, req *model.RecordOrderHistoryReq) (err error) {
|
||||
m := dao.V1JdCookieOrderChangeHistory.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
|
||||
historyUuid := utils.GenerateRandomUUID()
|
||||
_, err = m.Insert(&do.V1JdCookieOrderChangeHistory{
|
||||
HistoryUuid: historyUuid,
|
||||
OrderId: orderId,
|
||||
ChangeType: changeType,
|
||||
JdOrderId: jdOrderId,
|
||||
OrderId: req.OrderId,
|
||||
ChangeType: req.ChangeType,
|
||||
JdOrderId: req.JdOrderId,
|
||||
Remark: req.Remark,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
glog.Error(ctx, "记录订单变更历史失败", g.Map{
|
||||
"orderId": orderId,
|
||||
"changeType": changeType,
|
||||
"jdOrderId": jdOrderId,
|
||||
"orderId": req.OrderId,
|
||||
"changeType": req.ChangeType,
|
||||
"jdOrderId": req.JdOrderId,
|
||||
"error": err,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -97,3 +97,11 @@ type RecordCookieHistoryReq struct {
|
||||
FailureCount int `json:"failureCount" dc:"失败次数"`
|
||||
Remark string `json:"remark" dc:"备注信息,存储额外的信息"`
|
||||
}
|
||||
|
||||
// RecordOrderHistoryReq 记录订单变更历史请求参数
|
||||
type RecordOrderHistoryReq struct {
|
||||
OrderId string `json:"orderId" dc:"订单号"`
|
||||
ChangeType consts.OrderChangeType `json:"changeType" dc:"变更类型"`
|
||||
JdOrderId string `json:"jdOrderId" dc:"京东订单号"`
|
||||
Remark string `json:"remark" dc:"备注信息"`
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ type (
|
||||
// RecordJdOrderHistory 记录京东订单变更历史
|
||||
RecordJdOrderHistory(ctx context.Context, jdOrderId string, changeType consts.JdOrderChangeType, orderId string, wxPayUrl string, remark string) (err error)
|
||||
// RecordOrderHistory 记录订单变更历史
|
||||
RecordOrderHistory(ctx context.Context, orderId string, changeType consts.OrderChangeType, jdOrderId string) (err error)
|
||||
RecordOrderHistory(ctx context.Context, req *model.RecordOrderHistoryReq) (err error)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
14
sql/add_order_fields.sql
Normal file
14
sql/add_order_fields.sql
Normal file
@@ -0,0 +1,14 @@
|
||||
-- 为订单表添加用户订单号和失败原因字段
|
||||
-- 执行时间:2025-10-18
|
||||
|
||||
-- 1. 添加 user_order_id 字段(如果不存在)
|
||||
ALTER TABLE `jd_cookie_order`
|
||||
ADD COLUMN IF NOT EXISTS `user_order_id` varchar (64) NOT NULL COMMENT '用户订单号' AFTER `order_id`,
|
||||
ADD UNIQUE KEY IF NOT EXISTS `uk_user_order_id` (`user_order_id`);
|
||||
|
||||
-- 2. 添加 failure_reason 字段(如果不存在)
|
||||
ALTER TABLE `jd_cookie_order`
|
||||
ADD COLUMN IF NOT EXISTS `failure_reason` text DEFAULT NULL COMMENT '失败原因' AFTER `status`;
|
||||
|
||||
-- 3. 更新状态字段注释(添加状态5Ck失败)
|
||||
ALTER TABLE `jd_cookie_order` MODIFY COLUMN `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1待支付 2已支付 3已过期 4已取消 5Ck失败';
|
||||
@@ -1,7 +1,6 @@
|
||||
-- 京东Cookie管理模块数据库表结构
|
||||
-- 作者:AI助手
|
||||
-- 创建时间:2025-10-08
|
||||
-- 说明:新的京东Cookie管理系统数据库表结构,替换原有card_redeem_cookie系统
|
||||
-- 更新时间:2025-10-18
|
||||
-- 说明:从MySQL数据库(kami)同步的实际表结构
|
||||
|
||||
-- 1. Cookie账户表
|
||||
DROP TABLE IF EXISTS `jd_cookie_account`;
|
||||
@@ -15,7 +14,7 @@ CREATE TABLE `jd_cookie_account` (
|
||||
`failure_count` int DEFAULT 0 COMMENT '连续失败次数',
|
||||
`last_used_at` datetime DEFAULT NULL COMMENT '最后使用时间',
|
||||
`suspend_until` datetime DEFAULT NULL COMMENT '暂停解除时间',
|
||||
`remark` text DEFAULT NULL COMMENT '备注信息',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注信息',
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
@@ -25,7 +24,7 @@ CREATE TABLE `jd_cookie_account` (
|
||||
KEY `idx_last_used` (`last_used_at`),
|
||||
KEY `idx_suspend_until` (`suspend_until`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '京东Cookie账户表';
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '京东Cookie账户表';
|
||||
|
||||
-- 2. 京东订单表
|
||||
DROP TABLE IF EXISTS `jd_cookie_jd_order`;
|
||||
@@ -33,15 +32,16 @@ DROP TABLE IF EXISTS `jd_cookie_jd_order`;
|
||||
CREATE TABLE `jd_cookie_jd_order` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`jd_order_id` varchar(64) NOT NULL COMMENT '京东订单号',
|
||||
`real_jd_order_id` varchar(128) DEFAULT NULL COMMENT '京东客户端返回的真实订单ID',
|
||||
`pay_id` varchar(64) NOT NULL COMMENT '支付ID',
|
||||
`amount` decimal(10, 2) NOT NULL COMMENT '订单金额',
|
||||
`amount` decimal(10, 2) NOT NULL COMMENT '订单金额',
|
||||
`category` varchar(50) NOT NULL COMMENT '商品品类',
|
||||
`cookie_id` varchar(64) NOT NULL COMMENT '使用的Cookie ID',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1待支付 2已支付 3已过期 4已取消',
|
||||
`wx_pay_url` text DEFAULT NULL COMMENT '微信支付链接',
|
||||
`wx_pay_expire_at` datetime DEFAULT NULL COMMENT '微信支付链接过期时间',
|
||||
`order_expire_at` datetime NOT NULL COMMENT '订单过期时间(默认24小时)',
|
||||
`current_order_id` bigint DEFAULT NULL COMMENT '当前关联的订单ID',
|
||||
`order_id` varchar(64) DEFAULT NULL COMMENT '关联的用户订单号',
|
||||
`paid_at` datetime DEFAULT NULL COMMENT '支付完成时间',
|
||||
`card_no` varchar(100) DEFAULT NULL COMMENT '卡号',
|
||||
`card_password` varchar(100) DEFAULT NULL COMMENT '卡密',
|
||||
@@ -54,9 +54,9 @@ CREATE TABLE `jd_cookie_jd_order` (
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_cookie_id` (`cookie_id`),
|
||||
KEY `idx_order_expire` (`order_expire_at`),
|
||||
KEY `idx_current_order` (`current_order_id`),
|
||||
KEY `idx_current_order` (`order_id`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '京东订单表';
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '京东订单表';
|
||||
|
||||
-- 3. 订单表
|
||||
DROP TABLE IF EXISTS `jd_cookie_order`;
|
||||
@@ -64,11 +64,11 @@ 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 '订单号',
|
||||
`amount` decimal(10, 2) 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 '关联的京东订单号',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1待支付 2已支付 3已过期 4已取消',
|
||||
`wx_pay_url` text DEFAULT NULL COMMENT '当前有效的微信支付链接',
|
||||
`last_request_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后请求时间',
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
@@ -78,8 +78,9 @@ CREATE TABLE `jd_cookie_order` (
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_jd_order_id` (`jd_order_id`),
|
||||
KEY `idx_last_request` (`last_request_at`),
|
||||
KEY `idx_user_order_id` (`user_order_id`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '订单表';
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '订单表';
|
||||
|
||||
-- 4. Cookie变更历史表
|
||||
DROP TABLE IF EXISTS `jd_cookie_change_history`;
|
||||
@@ -94,7 +95,7 @@ CREATE TABLE `jd_cookie_change_history` (
|
||||
`order_id` varchar(64) DEFAULT NULL COMMENT '关联的订单号',
|
||||
`user_order_id` varchar(64) DEFAULT NULL COMMENT '用户订单号',
|
||||
`failure_count` int DEFAULT NULL COMMENT '失败次数',
|
||||
`remark` text DEFAULT NULL COMMENT '备注信息',
|
||||
`remark` text DEFAULT NULL COMMENT '备注信息,存储额外的信息',
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
@@ -102,11 +103,10 @@ CREATE TABLE `jd_cookie_change_history` (
|
||||
UNIQUE KEY `uk_history_uuid` (`history_uuid`),
|
||||
KEY `idx_cookie_id` (`cookie_id`),
|
||||
KEY `idx_change_type` (`change_type`),
|
||||
KEY `idx_order_id` (`order_id`),
|
||||
KEY `idx_user_order_id` (`user_order_id`),
|
||||
KEY `idx_user_order_id` (`user_order_id`),
|
||||
KEY `idx_created_at` (`created_at`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Cookie变更历史表';
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'Cookie变更历史表';
|
||||
|
||||
-- 5. 京东订单变更历史表
|
||||
DROP TABLE IF EXISTS `jd_cookie_jd_order_change_history`;
|
||||
@@ -128,7 +128,7 @@ CREATE TABLE `jd_cookie_jd_order_change_history` (
|
||||
KEY `idx_change_type` (`change_type`),
|
||||
KEY `idx_created_at` (`created_at`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '京东订单变更历史表';
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '京东订单变更历史表';
|
||||
|
||||
-- 6. 订单变更历史表
|
||||
DROP TABLE IF EXISTS `jd_cookie_order_change_history`;
|
||||
@@ -149,4 +149,4 @@ CREATE TABLE `jd_cookie_order_change_history` (
|
||||
KEY `idx_change_type` (`change_type`),
|
||||
KEY `idx_created_at` (`created_at`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '订单变更历史表';
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '订单变更历史表';
|
||||
Reference in New Issue
Block a user