v1.0
This commit is contained in:
232
退款时代理处理逻辑分析.md
Normal file
232
退款时代理处理逻辑分析.md
Normal file
@@ -0,0 +1,232 @@
|
||||
# 退款时代理处理逻辑分析
|
||||
|
||||
## 当前流程分析
|
||||
|
||||
### 1. 订单支付成功后的流程
|
||||
|
||||
```
|
||||
支付成功
|
||||
↓
|
||||
支付回调(支付宝/微信)
|
||||
↓
|
||||
更新订单状态为 "paid"
|
||||
↓
|
||||
发送异步任务 SendQueryTask(order.Id)
|
||||
↓
|
||||
PaySuccessNotifyUserHandler.ProcessTask
|
||||
├─ 创建查询记录(query表,状态为 "pending")
|
||||
├─ 生成授权书
|
||||
├─ 调用API请求服务(第164行)← 可能失败
|
||||
├─ 如果API成功:
|
||||
│ ├─ 更新查询状态为 "success"
|
||||
│ └─ 发送代理处理任务 SendAgentProcessTask(第192行)
|
||||
└─ 如果API失败:
|
||||
└─ handleError → 退款 → 更新订单状态为 "refunded"
|
||||
```
|
||||
|
||||
### 2. 代理订单创建时机
|
||||
|
||||
**在支付时创建**(`paymentlogic.go:316-327`):
|
||||
- 代理订单在用户支付时创建
|
||||
- `ProcessStatus = 0`(待处理)
|
||||
- 此时还没有发放佣金和返佣
|
||||
|
||||
### 3. 代理处理任务执行时机
|
||||
|
||||
**只在查询成功后发送**(`paySuccessNotify.go:192`):
|
||||
```go
|
||||
// 报告生成成功后,发送代理处理异步任务(不阻塞报告流程)
|
||||
if asyncErr := l.svcCtx.AsynqService.SendAgentProcessTask(order.Id); asyncErr != nil {
|
||||
// 代理处理任务发送失败,只记录日志,不影响报告流程
|
||||
logx.Errorf("发送代理处理任务失败,订单ID: %d, 错误: %v", order.Id, asyncErr)
|
||||
}
|
||||
```
|
||||
|
||||
### 4. API调用失败时的处理(第164-167行)
|
||||
|
||||
```go
|
||||
combinedResponse, err := l.svcCtx.ApiRequestService.ProcessRequests(decryptData, product.Id)
|
||||
if err != nil {
|
||||
return l.handleError(ctx, err, order, query)
|
||||
}
|
||||
```
|
||||
|
||||
**handleError 的处理**(第206-257行):
|
||||
1. 删除Redis缓存
|
||||
2. 更新查询状态为 `failed`
|
||||
3. **退款**
|
||||
4. 更新订单状态为 `refunded`
|
||||
|
||||
**关键点**:此时代理处理任务**还没有发送**(因为查询还没成功)
|
||||
|
||||
---
|
||||
|
||||
## 问题分析
|
||||
|
||||
### ✅ 情况1:API调用失败,退款(正常情况)
|
||||
|
||||
**流程**:
|
||||
1. 支付成功,创建代理订单(`ProcessStatus = 0`)
|
||||
2. 调用API失败(第164-167行)
|
||||
3. 进入 `handleError`,退款
|
||||
4. 更新订单状态为 `refunded`
|
||||
5. **代理处理任务还没发送**(查询未成功)
|
||||
|
||||
**代理状态**:
|
||||
- ✅ 代理订单 `ProcessStatus = 0`(未处理)
|
||||
- ✅ 代理**没有收到**佣金
|
||||
- ✅ 代理**没有收到**返佣
|
||||
- ✅ **处理正确**
|
||||
|
||||
### ⚠️ 情况2:查询成功但订单被退款(边界情况)
|
||||
|
||||
**可能的场景**:
|
||||
1. 支付成功,创建代理订单(`ProcessStatus = 0`)
|
||||
2. 调用API成功,查询状态更新为 `success`
|
||||
3. 发送代理处理任务(第192行)
|
||||
4. **但在代理处理任务执行前**,订单被退款(比如管理员手动退款)
|
||||
|
||||
**代理处理任务的保护机制**(`agentProcess.go:43-46`):
|
||||
```go
|
||||
// 检查订单状态
|
||||
if order.Status != "paid" {
|
||||
logx.Infof("代理处理任务跳过,订单未支付: orderID=%d, status=%s", payload.OrderID, order.Status)
|
||||
return nil // 订单未支付,不处理,不重试
|
||||
}
|
||||
```
|
||||
|
||||
**代理状态**:
|
||||
- ✅ 如果订单状态是 `refunded`,代理处理任务会跳过
|
||||
- ✅ 代理订单 `ProcessStatus` 仍然是 0
|
||||
- ✅ 代理**没有收到**佣金和返佣
|
||||
- ✅ **处理正确**
|
||||
|
||||
### ⚠️ 情况3:代理已处理但订单被退款(需要处理)
|
||||
|
||||
**可能的场景**:
|
||||
1. 支付成功,创建代理订单(`ProcessStatus = 0`)
|
||||
2. 调用API成功,查询状态更新为 `success`
|
||||
3. 发送代理处理任务
|
||||
4. 代理处理任务执行,发放佣金和返佣(`ProcessStatus = 1`)
|
||||
5. **之后**订单被退款(比如管理员手动退款)
|
||||
|
||||
**当前问题**:
|
||||
- ❌ **代理已经收到佣金和返佣**
|
||||
- ❌ **没有撤销代理收益的逻辑**
|
||||
- ❌ **退款回调中也没有处理代理订单的逻辑**
|
||||
|
||||
---
|
||||
|
||||
## 发现的问题
|
||||
|
||||
### 问题1:退款回调中缺少代理订单处理
|
||||
|
||||
**当前退款回调逻辑**(`wechatpayrefundcallbacklogic.go` 和 `alipayrefundcallbacklogic.go`):
|
||||
- ✅ 只更新订单状态和退款记录
|
||||
- ❌ **没有检查代理订单**
|
||||
- ❌ **没有撤销代理收益**
|
||||
|
||||
### 问题2:管理员手动退款时缺少代理订单处理
|
||||
|
||||
**当前管理员退款逻辑**(`adminrefundorderlogic.go`):
|
||||
- ✅ 创建退款记录,更新订单状态
|
||||
- ❌ **没有检查代理订单**
|
||||
- ❌ **没有撤销代理收益**
|
||||
|
||||
---
|
||||
|
||||
## 建议的解决方案
|
||||
|
||||
### 方案1:在退款回调中处理代理订单(推荐)
|
||||
|
||||
在退款成功回调中,检查代理订单并撤销收益:
|
||||
|
||||
```go
|
||||
// 在 handleQueryOrderRefund 中添加代理订单处理
|
||||
if status == refunddomestic.STATUS_SUCCESS {
|
||||
// 更新订单状态
|
||||
order.Status = orderStatus
|
||||
// ...
|
||||
|
||||
// 检查并处理代理订单
|
||||
agentOrder, err := l.svcCtx.AgentOrderModel.FindOneByOrderId(ctx, order.Id)
|
||||
if err == nil && agentOrder.ProcessStatus == 1 {
|
||||
// 代理订单已处理,需要撤销收益
|
||||
err = l.svcCtx.AgentService.CancelAgentCommission(ctx, order.Id)
|
||||
if err != nil {
|
||||
logx.Errorf("撤销代理收益失败,订单ID: %d, 错误: %v", order.Id, err)
|
||||
// 不阻断退款流程,只记录日志
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 方案2:在管理员退款时处理代理订单
|
||||
|
||||
在 `AdminRefundOrderLogic` 中添加代理订单检查和处理。
|
||||
|
||||
### 方案3:在代理处理任务中增加订单状态检查(已有保护)
|
||||
|
||||
当前已有保护机制(`agentProcess.go:43-46`),如果订单状态不是 `paid`,会跳过处理。
|
||||
|
||||
---
|
||||
|
||||
## 当前处理是否正确?
|
||||
|
||||
### ✅ 对于 API 调用失败的情况
|
||||
|
||||
**完全正确**:
|
||||
- 如果API调用失败(第164-167行),会进入退款流程
|
||||
- 此时代理处理任务还没发送(因为查询未成功)
|
||||
- 代理订单 `ProcessStatus = 0`,代理没有收到收益
|
||||
- **处理正确,无需修改**
|
||||
|
||||
### ⚠️ 对于已处理代理订单的退款情况
|
||||
|
||||
**存在问题**:
|
||||
- 如果代理订单已经处理(`ProcessStatus = 1`),代理已收到佣金和返佣
|
||||
- 此时订单退款,**没有撤销代理收益的逻辑**
|
||||
- 这会导致:
|
||||
- 用户收到退款
|
||||
- 但代理仍然保留佣金和返佣
|
||||
- **资金不一致**
|
||||
|
||||
---
|
||||
|
||||
## 建议修改
|
||||
|
||||
### 1. 在退款回调中添加代理订单检查
|
||||
|
||||
### 2. 在管理员退款中添加代理订单检查
|
||||
|
||||
### 3. 创建撤销代理收益的方法
|
||||
|
||||
```go
|
||||
// CancelAgentCommission 撤销代理收益(订单退款时调用)
|
||||
func (s *AgentService) CancelAgentCommission(ctx context.Context, orderId int64) error {
|
||||
// 1. 查找代理订单
|
||||
// 2. 检查是否已处理
|
||||
// 3. 撤销佣金(从钱包扣除)
|
||||
// 4. 撤销返佣(从上级钱包扣除)
|
||||
// 5. 更新代理订单状态
|
||||
// 6. 创建撤销记录
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
### 当前情况(API调用失败退款)
|
||||
|
||||
✅ **处理正确**:
|
||||
- API调用失败时,代理处理任务还没发送
|
||||
- 代理订单未处理,代理没有收益
|
||||
- **无需修改**
|
||||
|
||||
### 需要补充的场景
|
||||
|
||||
⚠️ **需要处理**:
|
||||
- 代理订单已处理(`ProcessStatus = 1`)后订单退款的情况
|
||||
- 需要在退款回调和管理员退款中添加撤销代理收益的逻辑
|
||||
|
||||
Reference in New Issue
Block a user