# 退款时代理处理逻辑分析 ## 当前流程分析 ### 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`)后订单退款的情况 - 需要在退款回调和管理员退款中添加撤销代理收益的逻辑