|
@@ -1,5 +1,6 @@
|
|
|
package com.cyksj.service.recharge.executor;
|
|
package com.cyksj.service.recharge.executor;
|
|
|
|
|
|
|
|
|
|
+import com.cyksj.model.entity.GptInternalTask;
|
|
|
import com.cyksj.model.entity.GptRechargeCardKey;
|
|
import com.cyksj.model.entity.GptRechargeCardKey;
|
|
|
import com.cyksj.model.entity.GptRechargeChannel;
|
|
import com.cyksj.model.entity.GptRechargeChannel;
|
|
|
import com.cyksj.service.recharge.ChannelCardKeyManager;
|
|
import com.cyksj.service.recharge.ChannelCardKeyManager;
|
|
@@ -28,81 +29,137 @@ public class MultiChannelRechargeExecutor {
|
|
|
private final InternalTaskManager taskManager;
|
|
private final InternalTaskManager taskManager;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 执行多渠道充值任务
|
|
|
|
|
|
|
+ * 执行多渠道充值任务(递归重试,支持多渠道)
|
|
|
*/
|
|
*/
|
|
|
public void executeRechargeTask(String internalTaskId, String accessToken, Long orderId, String orderNo) {
|
|
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) {
|
|
if (channel == null) {
|
|
|
// 所有渠道都尝试过了,任务失败
|
|
// 所有渠道都尝试过了,任务失败
|
|
|
log.warn("所有渠道都尝试失败,任务ID: {}", internalTaskId);
|
|
log.warn("所有渠道都尝试失败,任务ID: {}", internalTaskId);
|
|
|
taskManager.updateTaskStatus(internalTaskId, "failed", null, "所有渠道均充值失败");
|
|
taskManager.updateTaskStatus(internalTaskId, "failed", null, "所有渠道均充值失败");
|
|
|
- taskCompleted = true;
|
|
|
|
|
- continue;
|
|
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- log.info("尝试使用渠道: {} 执行充值任务: {}", channel.getChannelName(), internalTaskId);
|
|
|
|
|
|
|
+ log.info("尝试使用渠道: {} 执行充值任务: {} (递归深度: {})", channel.getChannelName(), internalTaskId, recursionDepth);
|
|
|
taskManager.incrementRetryCount(internalTaskId);
|
|
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(),
|
|
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;
|
|
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();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|