chan 1 год назад
Родитель
Сommit
317a8327ec

+ 138 - 58
netflix-service/src/main/java/com/cyksj/service/recharge/executor/MultiChannelRechargeExecutor.java

@@ -1,5 +1,6 @@
 package com.cyksj.service.recharge.executor;
 
+import com.cyksj.model.entity.GptInternalTask;
 import com.cyksj.model.entity.GptRechargeCardKey;
 import com.cyksj.model.entity.GptRechargeChannel;
 import com.cyksj.service.recharge.ChannelCardKeyManager;
@@ -28,81 +29,137 @@ public class MultiChannelRechargeExecutor {
     private final InternalTaskManager taskManager;
     
     /**
-     * 执行多渠道充值任务
+     * 执行多渠道充值任务(递归重试,支持多渠道)
      */
     public void executeRechargeTask(String internalTaskId, String accessToken, Long orderId, String orderNo) {
-        log.info("开始执行多渠道充值任务: {}", internalTaskId);
+        executeMultiChannelRetryWithSafety(internalTaskId, accessToken, orderId, orderNo, 0);
+    }
+    
+    /**
+     * 执行多渠道充值任务的递归重试方法(带安全机制)
+     * @param recursionDepth 递归深度,防止无限递归
+     */
+    private void executeMultiChannelRetryWithSafety(String internalTaskId, String accessToken, 
+            Long orderId, String orderNo, int recursionDepth) {
         
-        boolean taskCompleted = false;
+        // 安全机制1:递归深度限制
+        int maxRecursionDepth = 20; // 最大递归深度
+        if (recursionDepth > maxRecursionDepth) {
+            log.error("递归深度超限,停止重试: {} - 深度: {}", internalTaskId, recursionDepth);
+            taskManager.updateTaskStatus(internalTaskId, "failed", null, "递归深度超限");
+            return;
+        }
         
-        while (!taskCompleted) {
-            GptRechargeChannel channel = channelManager.getNextAvailableChannel(internalTaskId);
+        log.info("开始执行多渠道充值任务: {} (递归深度: {})", internalTaskId, recursionDepth);
+        
+        try {
+            // 检查任务是否已经完成或超时
+            GptInternalTask internalTask = taskManager.getInternalTask(internalTaskId);
+            if (internalTask == null) {
+                log.warn("内部任务不存在: {}", internalTaskId);
+                return;
+            }
             
+            // 检查任务状态
+            if ("completed".equals(internalTask.getTaskStatus()) || "failed".equals(internalTask.getTaskStatus())) {
+                log.info("任务已完成,跳过执行: {} - {}", internalTaskId, internalTask.getTaskStatus());
+                return;
+            }
+            
+            // 安全机制2:重试次数限制
+            int maxRetryCount = 10; // 最大重试次数
+            if (internalTask.getTotalRetryCount() != null && internalTask.getTotalRetryCount() >= maxRetryCount) {
+                log.warn("任务重试次数超限,标记为失败: {} - 重试次数: {}", internalTaskId, internalTask.getTotalRetryCount());
+                taskManager.updateTaskStatus(internalTaskId, "failed", null, "重试次数超限");
+                return;
+            }
+            
+            // 安全机制3:任务过期保护(超过5分钟自动失败)
+            long taskAge = System.currentTimeMillis() - internalTask.getCreatedTime().getTime();
+            if (taskAge > 300000) { // 5分钟
+                log.warn("任务已过期,标记为失败: {} - 创建时间: {}", internalTaskId, internalTask.getCreatedTime());
+                taskManager.updateTaskStatus(internalTaskId, "failed", null, "任务已过期");
+                return;
+            }
+            
+            // 获取下一个可用渠道
+            GptRechargeChannel channel = channelManager.getNextAvailableChannel(internalTaskId);
             if (channel == null) {
                 // 所有渠道都尝试过了,任务失败
                 log.warn("所有渠道都尝试失败,任务ID: {}", internalTaskId);
                 taskManager.updateTaskStatus(internalTaskId, "failed", null, "所有渠道均充值失败");
-                taskCompleted = true;
-                continue;
+                return;
             }
             
-            log.info("尝试使用渠道: {} 执行充值任务: {}", channel.getChannelName(), internalTaskId);
+            log.info("尝试使用渠道: {} 执行充值任务: {} (递归深度: {})", channel.getChannelName(), internalTaskId, recursionDepth);
             taskManager.incrementRetryCount(internalTaskId);
             
-            try {
-                // 检查渠道是否有可用卡密
-                if (!cardKeyManager.hasAvailableCardKey(channel.getChannelCode())) {
-                    log.warn("渠道 {} 无可用卡密,跳过该渠道", channel.getChannelName());
-                    taskManager.logChannelExecution(internalTaskId, channel.getChannelCode(), 
-                        null, null, "failed", "该渠道无可用卡密", true, null);
-                    continue;
-                }
-                
-                // 获取渠道对应的卡密
-                GptRechargeCardKey cardKey = cardKeyManager.getAvailableCardKey(
-                    channel.getChannelCode(), orderId, orderNo);
-                
-                if (cardKey == null) {
-                    log.warn("获取渠道 {} 卡密失败,可能被其他线程抢占", channel.getChannelName());
-                    taskManager.logChannelExecution(internalTaskId, channel.getChannelCode(), 
-                        null, null, "failed", "获取卡密失败", true, null);
-                    continue;
-                }
-                
-                // 获取渠道实现并执行充值
-                String externalTaskId = executeChannelRecharge(channel, cardKey.getCardKey(), accessToken);
-                
-                if (externalTaskId != null) {
-                    // 任务提交成功
-                    log.info("渠道 {} 充值任务提交成功,外部任务ID: {}", channel.getChannelName(), externalTaskId);
-                    taskManager.updateTaskStatus(internalTaskId, "processing", channel.getChannelCode(), null);
-                    taskManager.logChannelExecution(internalTaskId, channel.getChannelCode(), 
-                        externalTaskId, cardKey.getCardKey(), "success", null, false, null);
-                    taskCompleted = true;
-                } else {
-                    // 任务提交失败,释放卡密,尝试下一个渠道
-                    log.warn("渠道 {} 任务提交失败", channel.getChannelName());
-                    cardKeyManager.releaseCardKey(cardKey.getCardKey());
-                    taskManager.logChannelExecution(internalTaskId, channel.getChannelCode(), 
-                        null, cardKey.getCardKey(), "failed", "任务提交失败", true, null);
-                }
-                
-            } catch (Exception e) {
-                // 检查是否为不可重试错误
-                boolean isRetryable = isRetryableError(e.getMessage());
+            // 检查渠道是否有可用卡密
+            if (!cardKeyManager.hasAvailableCardKey(channel.getChannelCode())) {
+                log.warn("渠道 {} 无可用卡密,尝试下一个渠道", channel.getChannelName());
+                taskManager.logChannelExecution(internalTaskId, channel.getChannelCode(), 
+                    null, null, "failed", "该渠道无可用卡密", true, null);
                 
-                log.error("渠道 {} 执行失败: {}, 是否可重试: {}", 
-                    channel.getChannelName(), e.getMessage(), isRetryable, e);
+                // 添加适当延迟后递归重试下一个渠道
+                addRetryDelay(recursionDepth);
+                executeMultiChannelRetryWithSafety(internalTaskId, accessToken, orderId, orderNo, recursionDepth + 1);
+                return;
+            }
+            
+            // 获取渠道对应的卡密
+            GptRechargeCardKey cardKey = cardKeyManager.getAvailableCardKey(
+                channel.getChannelCode(), orderId != null ? orderId : 0L, orderNo != null ? orderNo : "RETRY");
+            
+            if (cardKey == null) {
+                log.warn("获取渠道 {} 卡密失败,尝试下一个渠道", channel.getChannelName());
+                taskManager.logChannelExecution(internalTaskId, channel.getChannelCode(), 
+                    null, null, "failed", "获取卡密失败", true, null);
                 
+                // 添加适当延迟后递归重试下一个渠道
+                addRetryDelay(recursionDepth);
+                executeMultiChannelRetryWithSafety(internalTaskId, accessToken, orderId, orderNo, recursionDepth + 1);
+                return;
+            }
+            
+            // 获取渠道实现并执行充值
+            String externalTaskId = executeChannelRecharge(channel, cardKey.getCardKey(), accessToken);
+            
+            if (externalTaskId != null) {
+                // 任务提交成功
+                log.info("渠道 {} 充值任务提交成功,外部任务ID: {}", channel.getChannelName(), externalTaskId);
+                taskManager.updateTaskStatus(internalTaskId, "processing", channel.getChannelCode(), null);
                 taskManager.logChannelExecution(internalTaskId, channel.getChannelCode(), 
-                    null, null, "failed", e.getMessage(), isRetryable, null);
+                    externalTaskId, cardKey.getCardKey(), "success", null, false, null);
+            } else {
+                // 任务提交失败,释放卡密并尝试下一个渠道
+                log.warn("渠道 {} 任务提交失败,释放卡密并尝试下一个渠道", channel.getChannelName());
+                cardKeyManager.releaseCardKey(cardKey.getCardKey());
+                taskManager.logChannelExecution(internalTaskId, channel.getChannelCode(), 
+                    null, cardKey.getCardKey(), "failed", "任务提交失败", true, null);
                 
-                if (!isRetryable) {
-                    // 不可重试错误,直接失败
-                    taskManager.updateTaskStatus(internalTaskId, "failed", channel.getChannelCode(), e.getMessage());
-                    taskCompleted = true;
-                }
-                // 如果是可重试错误,继续尝试下一个渠道
+                // 添加适当延迟后递归重试下一个渠道
+                addRetryDelay(recursionDepth);
+                executeMultiChannelRetryWithSafety(internalTaskId, accessToken, orderId, orderNo, recursionDepth + 1);
+            }
+            
+        } catch (Exception e) {
+            // 检查是否为不可重试错误
+            boolean isRetryable = isRetryableError(e.getMessage());
+            
+            log.error("执行多渠道充值任务异常: {}, 是否可重试: {} (递归深度: {})", 
+                e.getMessage(), isRetryable, recursionDepth, e);
+            
+            if (!isRetryable) {
+                // 不可重试错误,直接失败
+                taskManager.updateTaskStatus(internalTaskId, "failed", null, e.getMessage());
+            } else {
+                // 可重试错误,记录错误并尝试下一个渠道
+                taskManager.logChannelExecution(internalTaskId, "UNKNOWN", 
+                    null, null, "failed", e.getMessage(), true, null);
+                
+                // 添加适当延迟后递归重试下一个渠道
+                addRetryDelay(recursionDepth);
+                executeMultiChannelRetryWithSafety(internalTaskId, accessToken, orderId, orderNo, recursionDepth + 1);
             }
         }
     }
@@ -166,4 +223,27 @@ public class MultiChannelRechargeExecutor {
         // 其他情况都认为可以重试
         return true;
     }
+    
+    /**
+     * 添加智能重试延迟
+     * @param recursionDepth 当前递归深度
+     */
+    private void addRetryDelay(int recursionDepth) {
+        try {
+            // 基础延迟:1秒
+            long baseDelay = 1000;
+            
+            // 递增延迟:每增加一层递归,增加500ms延迟,最大5秒
+            long incrementalDelay = Math.min(recursionDepth * 500L, 4000L);
+            
+            long totalDelay = baseDelay + incrementalDelay;
+            
+            log.info("递归深度: {}, 延迟 {}ms 后重试下一个渠道", recursionDepth, totalDelay);
+            Thread.sleep(totalDelay);
+            
+        } catch (InterruptedException e) {
+            log.warn("重试延迟被中断: {}", e.getMessage());
+            Thread.currentThread().interrupt();
+        }
+    }
 }

+ 54 - 3
netflix-service/src/main/java/com/cyksj/service/recharge/impl/GptProxyRechargeServiceImpl.java

@@ -244,14 +244,65 @@ public class GptProxyRechargeServiceImpl implements GptProxyRechargeService {
 		String result = externalResult.getResult();
 		
 		if ("completed".equals(status)) {
+			// 充值成功,更新为完成状态
 			taskManager.updateTaskStatus(internalTaskId, "completed", null, "充值成功");
+			// 更新关联的GroupsRelation状态
+			resetGroupsRelationRechargeStatusByInternalTask(internalTaskId, status, result);
 		} else if ("failed".equals(status) || "unknown".equals(status)) {
-			taskManager.updateTaskStatus(internalTaskId, "failed", null, result);
+			// 外部任务失败,需要判断是否可以重试其他渠道
+			boolean isRetryableError = isRetryableError(result);
+			
+			if (!isRetryableError) {
+				// 不可重试错误(如账号已是会员),直接标记为最终失败
+				log.info("检测到不可重试错误,停止尝试其他渠道: {}", result);
+				taskManager.updateTaskStatus(internalTaskId, "failed", null, result);
+				resetGroupsRelationRechargeStatusByInternalTask(internalTaskId, status, result);
+			} else {
+				// 可重试错误,检查是否还有其他渠道可以尝试
+				GptRechargeChannel nextChannel = channelManager.getNextAvailableChannel(internalTaskId);
+				if (nextChannel == null) {
+					// 没有更多渠道可以尝试,标记为最终失败
+					log.info("所有渠道都已尝试,任务最终失败: {}", result);
+					taskManager.updateTaskStatus(internalTaskId, "failed", null, "所有渠道均充值失败: " + result);
+					resetGroupsRelationRechargeStatusByInternalTask(internalTaskId, status, result);
+				} else {
+					// 还有其他渠道可以尝试,将任务状态重置为pending,等待定时任务重试
+					log.info("当前渠道失败但可重试,将任务重置为pending等待重试: {}", nextChannel.getChannelName());
+					// 记录当前渠道的失败日志
+					GptTaskExecutionLog lastLog = taskManager.getLastExecutionLog(internalTaskId);
+					if (lastLog != null) {
+						taskManager.logChannelExecution(internalTaskId, lastLog.getChannelCode(), 
+							lastLog.getExternalTaskId(), lastLog.getCardKey(), "failed", result, true, null);
+					}
+					
+					// 重置任务状态为pending,让定时任务处理重试
+					taskManager.updateTaskStatus(internalTaskId, "pending", null, "等待重试其他渠道");
+				}
+			}
 		}
 		// processing状态不更新,继续等待
+	}
+	
+	/**
+	 * 检查是否为不可重试错误
+	 */
+	private boolean isRetryableError(String errorMessage) {
+		if (errorMessage == null) {
+			return true;
+		}
+		
+		String lowerErrorMessage = errorMessage.toLowerCase();
+		// 包含"会员"关键词就认为是已经是会员,不可重试
+		if (lowerErrorMessage.contains("会员") || 
+			lowerErrorMessage.contains("member") || 
+			lowerErrorMessage.contains("premium") || 
+			lowerErrorMessage.contains("subscribed") || 
+			lowerErrorMessage.contains("订阅")) {
+			return false;
+		}
 		
-		// 更新关联的GroupsRelation状态
-		resetGroupsRelationRechargeStatusByInternalTask(internalTaskId, status, result);
+		// 其他情况都认为可以重试
+		return true;
 	}
 	
 	/**