ソースを参照

refactor(消费服务): 请求交易与上传交易验证优化

1.请求交易时的数据有效性与交易流程验证的并行验证流程优化
2.由于验证流程和处理逻辑是一样,只是每次验证的方法的个数不一样,因此封装了通用的验证流程
autumnal_wind 10 ヶ月 前
コミット
077fb73ecf
14 ファイル変更558 行追加380 行削除
  1. 0 1
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java
  2. 2 1
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/TradeStatusEnum.java
  3. 3 1
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/service/impl/PtUserAccountServiceImpl.java
  4. 6 7
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/BaseBusiness.java
  5. 12 8
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/ConsumeBusiness.java
  6. 33 16
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/CardConsumeValidation.java
  7. 70 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/CheckError.java
  8. 183 273
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/CommonCheck.java
  9. 57 71
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/ConsumeRequestCheck.java
  10. 1 1
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/ConsumeUploadCheck.java
  11. 15 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/IValidationTask.java
  12. 155 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/ParallelTaskExecutor.java
  13. 21 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/ValidationTaskDes.java
  14. 0 1
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/impl/XfCardLimitedServiceImpl.java

+ 0 - 1
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java

@@ -58,7 +58,6 @@ public class ThreadPoolConfig {
                 }
             };
         });
-
         executor.initialize(); // 主动初始化线程池
         return executor;
     }

+ 2 - 1
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/TradeStatusEnum.java

@@ -32,7 +32,8 @@ public enum TradeStatusEnum {
     SysError("系统错误", (byte)0x63),
     MidasApiError("米大师接口调用失败", (byte)0x64),
     NoDish("无订餐数据", (byte)0x70),
-    VALIDATION_TIMEOUT("交易超时",(byte)0x80);
+    VALIDATION_TIMEOUT("交易超时",(byte)0x80),
+    TimeoutError("业务处理超时",(byte)0x81);
 
     private String name; // 名称
     private Byte value; // 值

+ 3 - 1
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/service/impl/PtUserAccountServiceImpl.java

@@ -150,7 +150,9 @@ public class PtUserAccountServiceImpl implements IPtUserAccountService {
     public List<PtUserAccountVo> queryList(PtUserAccountBo bo) {
         LambdaQueryWrapper<PtUserAccount> lqw = buildQueryWrapper(bo);
         List<PtUserAccountVo> accountVoList = baseMapper.selectVoList(lqw);
-        addOtherInfoForList(accountVoList);
+        if (CollectionUtil.isNotEmpty(accountVoList)) {
+            addOtherInfoForList(accountVoList);
+        }
         return accountVoList;
     }
 

+ 6 - 7
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/BaseBusiness.java

@@ -271,11 +271,11 @@ public class BaseBusiness {
     @Transactional(rollbackFor = ConsumeException.class)
     public R<ErrorInfo> postConsumeRecord(ConsumptionBo bo, RemoteUserAccountVo userAccountVo, RemoteCardVo cardVo,
                                           List<PtBagVo> bagVos, XfTermVo termVo, RemoteMealTypeVo mealTypeVo, String remark) {
-        XfConsumeDetailVo consumeDetailVo = consumeDetailService.queryVoByOriginalId(bo.getOriginalId());
-        if (ObjectUtil.isNotEmpty(consumeDetailVo)) {
-            // 认为是重复上传,不再写入明细
-            return R.ok();
-        }
+        // XfConsumeDetailVo consumeDetailVo = consumeDetailService.queryVoByOriginalId(bo.getOriginalId());
+        // if (ObjectUtil.isNotEmpty(consumeDetailVo)) {
+        //     // 认为是重复上传,不再写入明细
+        //     return R.ok();
+        // }
         List<XfConsumeDetailBo> detailBos = new ArrayList<>();
         for (PtBagVo bagVo : bagVos) {
             XfConsumeDetailBo detailBo = this.createConsumeDetailBo(bo, userAccountVo, cardVo, bagVo, termVo, mealTypeVo, remark);
@@ -296,7 +296,6 @@ public class BaseBusiness {
             throw new ConsumeException("更新设备日统计表失败");
         }
         // 4.更新钱包余额
-
         log.warn("[上传交易]-[更新钱包余额]-[{}]", JSONUtil.toJsonStr(JSONUtil.toJsonStr(bagVos)));
         bagService.updateConsumeBalance(bagVos);
 
@@ -431,7 +430,7 @@ public class BaseBusiness {
      */
     public void completeUploadRecord(ConsumptionBo bo, RemoteUserAccountVo accountVo) {
         // 消费记录上传完成,还有一些后续工作,不需要知道处理结果,采用异步任务提交
-        threadPoolTaskExecutor.execute(() -> sendConsumeToKafka(bo, accountVo));
+        // threadPoolTaskExecutor.execute(() -> sendConsumeToKafka(bo, accountVo));
         //taskExecutor.submit(() -> sendCloudConsume(bo));
     }
 

+ 12 - 8
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/ConsumeBusiness.java

@@ -68,10 +68,11 @@ public class ConsumeBusiness {
         AllowConsumeValidationContext ctx = AllowConsumeValidationContext.create(bo);
         R<ErrorInfo> result = commonCheck.consumeValidation(ctx);
         if (R.isError(result)) {
-            log.error("[请求交易]-[记录有效性验证失败]-[{}]-[{}]", JSONUtil.toJsonStr(result.getData()), JSONUtil.toJsonStr(bo));
+            log.error("[请求交易]-[记录有效性验证失败]-[数据:{}]-[耗时:{} ms]-[错误:{}]", JSONUtil.toJsonStr(bo),
+                System.currentTimeMillis() - startTime, JSONUtil.toJsonStr(result.getData()));
             return result;
         }
-        log.info("[请求交易]-[记录有效性验证完成]-[耗时: {} ms]-[{}]", System.currentTimeMillis() - startTime, JSONUtil.toJsonStr(bo));
+        log.info("[请求交易]-[记录有效性验证完成]-[数据:{}]-[耗时: {} ms]", JSONUtil.toJsonStr(bo), System.currentTimeMillis() - startTime);
 
         RemoteUserAccountVo userAccountVo = ctx.getUserAccountVo();
         RemoteCardVo userCardVo = ctx.getUserCardVo();
@@ -82,15 +83,17 @@ public class ConsumeBusiness {
         startTime = System.currentTimeMillis();
         result = requestCheck.checkConsume(bo, userAccountVo, userCardVo, termVo, mapCardLimited, cardLimitedVo);
         if (R.isError(result)) {
-            log.error("[请求交易]-[交易流程验证失败]-[{}]-[{}]", JSONUtil.toJsonStr(result.getData()), JSONUtil.toJsonStr(bo));
+            log.error("[请求交易]-[交易流程验证失败]-[数据:{}]-[耗时:{} ms]-[错误:{}]", JSONUtil.toJsonStr(bo),
+                System.currentTimeMillis() - startTime, JSONUtil.toJsonStr(result.getData()));
             return result;
         }
-        log.warn("[请求交易]-[交易流程验证完成]-[耗时: {} ms]-[{}]", System.currentTimeMillis() - startTime, JSONUtil.toJsonStr(bo));
+        log.info("[请求交易]-[交易流程验证完成]-[数据:{}]-[耗时: {} ms]", JSONUtil.toJsonStr(bo), System.currentTimeMillis() - startTime);
 
         startTime = System.currentTimeMillis();
         result = requestCheck.completeConsumeRequest(bo, userAccountVo, userCardVo, termVo, mapCardLimited, cardLimitedVo);
         if (R.isError(result)) {
-            log.error("[请求交易]-[消费原始记录失败]-[{}]-[{}]", JSONUtil.toJsonStr(result.getData()), JSONUtil.toJsonStr(bo));
+            log.error("[请求交易]-[生成原始消费记录失败]-[数据:{}]-[耗时:{} ms]-[错误:{}]", JSONUtil.toJsonStr(bo),
+                System.currentTimeMillis() - startTime, JSONUtil.toJsonStr(result.getData()));
             return result;
         }
         log.info("[请求交易]-[生成原始消费记录完成]-[耗时: {} ms]-[{}]", System.currentTimeMillis() - startTime, JSONUtil.toJsonStr(bo));
@@ -111,7 +114,8 @@ public class ConsumeBusiness {
         AllowConsumeValidationContext ctx = AllowConsumeValidationContext.create(bo);
         R<ErrorInfo> result = commonCheck.consumeValidation(ctx);
         if (R.isError(result)) {
-            log.error("[上传交易]-[记录有效性验证失败]-[{}]-[{}]", JSONUtil.toJsonStr(result.getData()), JSONUtil.toJsonStr(bo));
+            log.error("[上传交易]-[记录有效性验证失败]-[数据:{}]-[耗时:{} ms]-[错误:{}]", JSONUtil.toJsonStr(bo),
+                System.currentTimeMillis() - startTime, JSONUtil.toJsonStr(result.getData()));
             return result;
         }
         log.info("[上传交易]-[记录有效性验证完成]-[耗时: {} ms]-[{}]", System.currentTimeMillis() - startTime, JSONUtil.toJsonStr(bo));
@@ -183,7 +187,7 @@ public class ConsumeBusiness {
      */
     @Async
     public void postOrderAsync(ConsumptionBo bo, String mac, String xfPwd) {
-        R<ErrorInfo> result=null;
+        R<ErrorInfo> result = null;
         for (int i = 0; i < 3; i++) {
             result = SpringUtils.getAopProxy(this).postOrder(bo, mac, xfPwd);
             if (R.isSuccess(result)) {
@@ -191,7 +195,7 @@ public class ConsumeBusiness {
             }
         }
         if (R.isError(result)) {
-            log.info("[上传交易异步处理尝试3次失败,需要手工处理]-[{}]-[{}]", JSONUtil.toJsonStr(bo),JSONUtil.toJsonStr(result.getData()));
+            log.info("[上传交易异步处理尝试3次失败,需要手工处理]-[{}]-[{}]", JSONUtil.toJsonStr(bo), JSONUtil.toJsonStr(result.getData()));
         }
         log.info("[上传交易异步处理完成]");
     }

+ 33 - 16
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/CardConsumeValidation.java

@@ -44,20 +44,33 @@ public class CardConsumeValidation {
     private final CommonCheck commonCheck;
     private final ValidationParam validationParam;
     private final ThreadPoolTaskExecutor taskExecutor;
+    private final ParallelTaskExecutor taskExecutor1;
 
     public R<ErrorInfo> cardValidation(ConsumptionBo bo, XfTermVo termVo, RemoteCardVo userCardVo,
                                        RemoteMealTypeVo mealTypeVo,
                                        XfCardLimitedVo cardLimitedVo, Map<String, Boolean> mapCardLimited) {
 
         // public R<ErrorInfo> cardValidation(CardConsumeValidationContext validationContext) {
+        long startTime = System.currentTimeMillis();
         // 1.初始化验证上下文
         CardConsumeValidationContext validationContext = CardConsumeValidationContext.create(bo, termVo, userCardVo, mealTypeVo.getTypeId());
 
         // 2. 执行异步验证链
-        R<ErrorInfo> result = executeCardValidationChain(validationContext);
+        // R<ErrorInfo> result = executeCardValidationChain(validationContext);
         // if (R.isSuccess(result)) {
         //     dealCardQuota(validationContext);
         // }
+        List<ValidationTaskDes<R<ErrorInfo>>> tasks = List.of(
+            new ValidationTaskDes<>("折扣验证", () -> dealCardDiscount(validationContext)),
+            new ValidationTaskDes<>("限次验证", () -> dealCardLimited(validationContext))
+        );
+        //
+        R<ErrorInfo> result = taskExecutor1.executeParallelTasks(tasks, 5, TimeUnit.SECONDS);
+        // R<ErrorInfo> result = executeTermValidationChain(ctx);
+        if (R.isSuccess(result)) {
+            // 限次验证和折扣计算完成,进行限额验证
+            result = dealCardQuota(validationContext);
+        }
         // 3.不管验证是否成功,获取验证后的折扣金额及卡验证限制数据
         bo.setConsumeMoney(validationContext.discountMoney);
         RemoteVoConvert.INSTANCE.copyXfCardLimitedVo(cardLimitedVo, validationContext.getCardLimitedVo());
@@ -65,6 +78,8 @@ public class CardConsumeValidation {
         mapCardLimited.put("hasQuota", validationContext.getHasQuota());
         mapCardLimited.put("hasLimited", validationContext.getHasLimited());
 
+        log.info("[请求交易]-[卡片消费验证完成]-[耗时:{} ms]",System.currentTimeMillis()-startTime);
+
         return result;
     }
 
@@ -93,14 +108,14 @@ public class CardConsumeValidation {
             Thread.currentThread().interrupt();
             // 取消所有任务
             futures.forEach(f -> f.cancel(true));
-            return commonCheck.createError(TradeStatusEnum.SysError);
+            return CheckError.createError(TradeStatusEnum.SysError);
         }
 
         // 循环取消所有任务
         futures.forEach(f -> f.cancel(true));
         if (!finallyResult) {
             log.error("executeCardValidationChain- 折扣和限次校验10s超时");
-            return commonCheck.createError(TradeStatusEnum.SysError);
+            return CheckError.createError(TradeStatusEnum.SysError);
         }
 
         R<ErrorInfo> errorInfoR = firstError.get();
@@ -116,7 +131,7 @@ public class CardConsumeValidation {
             return dealCardQuota(ctx);
         } catch (Exception e) {
             log.error("{}执行异常", getTaskName(2), e);
-            return commonCheck.createError(TradeStatusEnum.DayLimitMoney, "日限额验证失败");
+            return CheckError.createError(TradeStatusEnum.DayLimitMoney, "日限额验证失败");
         }finally {
             log.info("{}结束,耗时:{} ms", getTaskName(2), System.currentTimeMillis() - starTime);
         }
@@ -142,7 +157,7 @@ public class CardConsumeValidation {
 //                    }
 //                } catch (ExecutionException e) {
 //                    log.error("{}执行异常", getTaskName(taskIndex), e);
-//                    if (firstError.compareAndSet(null, commonCheck.createError(TradeStatusEnum.SysError))) {
+//                    if (firstError.compareAndSet(null, CheckError.createError(TradeStatusEnum.SysError))) {
 //                        futures.forEach(f -> f.cancel(true));
 //                    }
 //                } finally {
@@ -162,7 +177,7 @@ public class CardConsumeValidation {
 //                return dealCardQuota(ctx);
 //            } catch (Exception e) {
 //                log.error("{}执行异常", getTaskName(2), e);
-//                return commonCheck.createError(TradeStatusEnum.DayLimitMoney, "日限额验证失败");
+//                return CheckError.createError(TradeStatusEnum.DayLimitMoney, "日限额验证失败");
 //            } finally {
 //                log.info("{}结束,耗时:{} ms", getTaskName(2), System.currentTimeMillis() - starTime);
 //            }
@@ -170,7 +185,7 @@ public class CardConsumeValidation {
 //        } catch (InterruptedException e) {
 //            Thread.currentThread().interrupt(); // 保留中断状态
 //            log.error("验证过程被中断", e);
-//            return commonCheck.createError(TradeStatusEnum.SysError);
+//            return CheckError.createError(TradeStatusEnum.SysError);
 //        }
 
         // 使用CountDownLatch跟踪任务完成
@@ -189,7 +204,7 @@ public class CardConsumeValidation {
         //             }
         //         } catch (Exception e) {
         //             log.error("系统错误1", e);
-        //             if (firstError.compareAndSet(null, commonCheck.createError(TradeStatusEnum.SysError))) {
+        //             if (firstError.compareAndSet(null, CheckError.createError(TradeStatusEnum.SysError))) {
         //                 taskExecutor.getThreadPoolExecutor().getQueue().clear();
         //             }
         //         } finally {
@@ -201,7 +216,7 @@ public class CardConsumeValidation {
         // try {
         //     // 等待所有任务完成或超时
         //     //if (!latch.await(VALIDATION_TIMEOUT, TimeUnit.MILLISECONDS)) {
-        //     //    return commonCheck.createError(TradeStatusEnum.VALIDATION_TIMEOUT);
+        //     //    return CheckError.createError(TradeStatusEnum.VALIDATION_TIMEOUT);
         //     //}
         //     latch.await();
         //     // 返回第一个发现的错误,如果没有错误则返回成功
@@ -214,7 +229,7 @@ public class CardConsumeValidation {
         // } catch (InterruptedException e) {
         //     Thread.currentThread().interrupt();
         //     log.error("系统错误", e);
-        //     return commonCheck.createError(TradeStatusEnum.SysError);
+        //     return CheckError.createError(TradeStatusEnum.SysError);
         // }
     }
 
@@ -272,7 +287,8 @@ public class CardConsumeValidation {
     }
 
     // region 卡片折扣处理
-    private R<ErrorInfo> dealCardDiscount(CardConsumeValidationContext ctx) {
+    private R<ErrorInfo> dealCardDiscount(CardConsumeValidationContext ctx) throws InterruptedException {
+        Thread.sleep(1);
         if (!validationParam.getRATE_CONSUME()) {
             log.warn("全局折扣功能未启用,跳过折扣验证");
             return R.ok();
@@ -345,7 +361,8 @@ public class CardConsumeValidation {
     // endregion
 
     //region 卡片限次处理
-    private R<ErrorInfo> dealCardLimited(CardConsumeValidationContext ctx) {
+    private R<ErrorInfo> dealCardLimited(CardConsumeValidationContext ctx) throws InterruptedException {
+        Thread.sleep(1);
         if (!validationParam.getXC_CONSUME()) {
             log.warn("全局限次功能未启用,跳过限次验证");
             return R.ok();
@@ -396,7 +413,7 @@ public class CardConsumeValidation {
     private R<ErrorInfo> checkDailyLimit(RemoteLimitedVo limitedVo, Long currentDayCount) {
         Long dayLimit = limitedVo.getDailyCount();
         if (dayLimit > 0 && currentDayCount >= dayLimit) {
-            return commonCheck.createError(TradeStatusEnum.DayLimitTimes, "卡类日次数限制");
+            return CheckError.createError(TradeStatusEnum.DayLimitTimes, "卡类日次数限制");
         }
         return null;
     }
@@ -429,7 +446,7 @@ public class CardConsumeValidation {
 
         if (currentMealCount >= mealLimit) {
             String mealName = MEAL_TYPE_NAMES.getOrDefault(mealType, "未知餐次");
-            return commonCheck.createError(TradeStatusEnum.MealLimitTimes, String.format("卡类%s次数限制", mealName));
+            return CheckError.createError(TradeStatusEnum.MealLimitTimes, String.format("卡类%s次数限制", mealName));
         }
         return R.ok();
     }
@@ -489,7 +506,7 @@ public class CardConsumeValidation {
     private R<ErrorInfo> checkDailyQuota(RemoteQuotaVo quotaVo, BigDecimal dayMoney, BigDecimal consumeMoney) {
         BigDecimal dayQuotaMoney = quotaVo.getDailyMoney();
         if (dayQuotaMoney.compareTo(BigDecimal.ZERO) > 0 && dayQuotaMoney.compareTo(dayMoney.add(consumeMoney)) < 0) {
-            return commonCheck.createError(TradeStatusEnum.DayLimitMoney, "卡类日限制额度");
+            return CheckError.createError(TradeStatusEnum.DayLimitMoney, "卡类日限制额度");
         }
         return null;
     }
@@ -523,7 +540,7 @@ public class CardConsumeValidation {
         if (mealQuota.compareTo(BigDecimal.ZERO) > 0) {
             if (mealQuota.compareTo(mealMoney.add(consumeMoney)) < 0) {
                 String mealName = MEAL_TYPE_NAMES.getOrDefault(mealType, "未知餐次");
-                return commonCheck.createError(TradeStatusEnum.MealLimitMoney, String.format("卡类%s额度限制", mealName));
+                return CheckError.createError(TradeStatusEnum.MealLimitMoney, String.format("卡类%s额度限制", mealName));
             }
         }
         return R.ok();

+ 70 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/CheckError.java

@@ -0,0 +1,70 @@
+package org.dromara.server.consume.check;
+
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.domain.model.ErrorInfo;
+import org.dromara.common.core.enums.TradeStatusEnum;
+
+/**
+ * 所有的验证错误方法封装
+ * <p>
+ * [功能说明]
+ * ${1:在此简要描述文件核心功能}
+ *
+ * @author luoyibo
+ * @date 2025-07-13
+ * @since JDK17
+ */
+@Slf4j
+public class CheckError {
+
+    /**
+     * 创建错误响应对象
+     *
+     * @param status  交易状态
+     * @param message 错误消息
+     * @return 封装好的错误响应
+     */
+    public static R<ErrorInfo> createError(TradeStatusEnum status, String message) {
+        return R.fail(new ErrorInfo(
+            400,
+            status.toString(),
+            message,
+            status.getName()));
+    }
+    public static R<ErrorInfo> createError(TradeStatusEnum status) {
+        return R.fail(new ErrorInfo(
+            400,
+            status.toString(),
+            status.getName(),
+            status.getName()));
+    }
+    public static R<ErrorInfo> createTimeoutError() {
+        log.error("验证任务整体超时");
+        return createError(TradeStatusEnum.TimeoutError);
+    }
+
+    public static R<ErrorInfo> createExecutionError(String taskName, Throwable cause) {
+        log.error("{}执行异常: {}", taskName, cause.getMessage(), cause);
+        return createError(TradeStatusEnum.SysError);
+    }
+
+    public static R<ErrorInfo> createInterruptionError() {
+        log.error("验证过程被中断");
+        return createError(TradeStatusEnum.SysError);
+    }
+
+    /**
+     * 创建错误响应对象
+     *
+     * @param code   错误码
+     * @param type   错误类型
+     * @param msg    错误消息
+     * @param detail 错误详情
+     * @return 封装好的错误响应
+     */
+    public static R<ErrorInfo> createErrorResponse(int code, String type, String msg, String detail) {
+        ErrorInfo errorInfo = new ErrorInfo(code, type, msg, detail);
+        return R.fail(errorInfo);
+    }
+}

+ 183 - 273
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/CommonCheck.java

@@ -6,7 +6,6 @@ import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.time.DateFormatUtils;
 import org.dromara.backstage.api.domain.vo.RemoteCardVo;
 import org.dromara.backstage.api.domain.vo.RemoteMealTypeVo;
 import org.dromara.backstage.api.domain.vo.RemoteUserAccountVo;
@@ -16,15 +15,12 @@ import org.dromara.common.core.constant.Constants;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.domain.model.ErrorInfo;
 import org.dromara.common.core.enums.CardStatusEnum;
-import org.dromara.common.core.enums.TradeStatusEnum;
 import org.dromara.common.core.enums.UserAccountStatusEnum;
-import org.dromara.common.json.utils.JsonUtils;
 import org.dromara.common.redis.utils.RedisUtils;
 import org.dromara.server.common.constant.ConsumeConstants;
 import org.dromara.server.common.domain.consume.bo.ConsumptionBo;
 import org.dromara.server.common.util.CardDateUtils;
 import org.dromara.server.consume.business.*;
-import org.dromara.server.consume.cache.CardCacheManager;
 import org.dromara.server.consume.domain.vo.XfCardLimitedVo;
 import org.dromara.server.consume.domain.vo.XfTermVo;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@@ -32,14 +28,11 @@ import org.springframework.stereotype.Service;
 
 import java.math.BigDecimal;
 import java.text.MessageFormat;
-import java.time.Duration;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.util.*;
 import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Supplier;
 
 /**
  * 消费业务通用验证类
@@ -54,26 +47,39 @@ import java.util.function.Supplier;
 @Service
 @RequiredArgsConstructor
 public class CommonCheck {
-    // 异步线程执行超时时间,以毫秒为单位
-    private static final long VALIDATION_TIMEOUT = 500;
+    // 多任务执行总体超时时间,以毫秒为单位
+    private static final long TIMEOUT_TOTAL = 3000;
 
     private final BaseBusiness baseBusiness;
     private final EmployeeBusiness employeeBusiness;
     private final CardBusiness cardBusiness;
-    private final ThreadPoolTaskExecutor taskExecutor;
+    private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
     private final TermBusiness termBusiness;
+    private final ParallelTaskExecutor taskExecutor;
 
-    //region 账户、卡片与设备验证
+    //region 账户、卡片与设备的有效性验证
 
     /**
      * 账户、设备有效性验证
+     * <p>
+     * 1.验证传入的参数是否满足要求:机号大于0,卡流水号、物理卡号、人员流水号、人员编号必有一个
+     * 2.根据机号验证是否是系统中的设备
+     * 3.根据卡流水号或物理卡号验证是否是系统中的卡片、卡片的状态是否正常,卡正常后还要根据卡查询账户,验证账户是否有效
+     * 4.根据人员流水号或人员编号验证是否是系统中的账户、账户的状态是否正常,如果账户正常则根据账户获取正常的主卡
      *
      * @param ctx 验证上下文
      * @return 校验结果,包含错误信息或成功标识
      */
     public R<ErrorInfo> consumeValidation(AllowConsumeValidationContext ctx) {
-        // 1. 执行异步验证链
-        R<ErrorInfo> result = executeTermValidationChain(ctx);
+        // 使用线程池并行验证
+        List<ValidationTaskDes<R<ErrorInfo>>> tasks = List.of(
+            new ValidationTaskDes<>("参数有效性验证", () -> checkParam(ctx)),
+            new ValidationTaskDes<>("设备有效性验证", () -> checkTerm(ctx)),
+            new ValidationTaskDes<>("账户有效性验证", () -> checkUserAccount(ctx))
+        );
+        //
+        R<ErrorInfo> result = taskExecutor.executeParallelTasks(tasks, TIMEOUT_TOTAL, TimeUnit.MILLISECONDS);
+        // R<ErrorInfo> result = executeTermValidationChain(ctx);
         if (R.isError(result)) {
             return result;
         }
@@ -87,196 +93,142 @@ public class CommonCheck {
      * @return 校验结果,包含错误信息或成功标识
      */
     private R<ErrorInfo> executeTermValidationChain(AllowConsumeValidationContext ctx) {
-        // 创建CountDownLatch 有1个任务返回错误时就唤醒主线程 // 使用CountDownLatch跟踪任务完成
-        CountDownLatch latch = new CountDownLatch(1);
-        // 用于存储第一个错误结果
-        AtomicReference<R<ErrorInfo>> firstError = new AtomicReference<>(null);
-        // 创建验证任务列表
-        List<Callable<R<ErrorInfo>>> validationTasks = getTermValidCallables(ctx, firstError, latch);
-
-        // 所有任务的结果集合
-        List<Future<R<ErrorInfo>>> futures = new ArrayList<>();
-        // 提交所有任务
-        for (Callable<R<ErrorInfo>> task : validationTasks) {
-            futures.add(taskExecutor.submit(task));
-        }
-
-        long starTime = System.currentTimeMillis();
-        // 最终执行的结果,true 为正常执行结束及latch减至了0;false 为超时
-        boolean finallyResult;
+        // 1. 使用LinkedHashMap保持任务顺序(便于调试)
+        final Map<String, Callable<R<ErrorInfo>>> validationTasks  = new LinkedHashMap<>();
+        validationTasks.put("参数有效性验证", () -> checkParam(ctx));
+        validationTasks.put("设备有效性验证", () -> checkTerm(ctx));
+        validationTasks.put("账户有效性验证", () -> checkUserAccount(ctx));
+
+        // 2. 快速失败标志和获取任务完成结果
+        final AtomicBoolean hasError = new AtomicBoolean(false);
+        final ExecutorCompletionService<R<ErrorInfo>> completionService = new ExecutorCompletionService<>(threadPoolTaskExecutor);
+
+        // 3. 使用IdentityHashMap优化Future查找
+        final Map<Future<R<ErrorInfo>>, String> futureToName = new IdentityHashMap<>();
+        final List<Future<R<ErrorInfo>>> allFutures = new ArrayList<>();
+        // 4. 提取常量
+        final int totalTasks = validationTasks.size();
+        final long deadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(TIMEOUT_TOTAL);
+        // 5. 提交任务(优化日志封装)
+        validationTasks.forEach((taskName, task) -> {
+            Callable<R<ErrorInfo>> loggedTask = createLoggedTask(taskName, task, hasError);
+            Future<R<ErrorInfo>> future = completionService.submit(loggedTask);
+            futureToName.put(future, taskName);
+            allFutures.add(future);
+        });
+        R<ErrorInfo> firstError = null;
+        int completedTasks = 0;
         try {
-            // 阻塞主线程,等待执行结果; latch 为0时,会唤醒主线程,最多阻塞10s
-            finallyResult = latch.await(10, TimeUnit.SECONDS);
+            while (completedTasks < totalTasks && !hasError.get()) {
+                // 5. 使用纳秒级精确时间计算
+                long remainingNanos = deadline - System.nanoTime();
+                if (remainingNanos <= 0) {
+                    // 设置错误标志
+                    hasError.set(true);
+                    break;
+                }
+
+                Future<R<ErrorInfo>> future = completionService.poll(remainingNanos, TimeUnit.NANOSECONDS);
+                if (future == null) {
+                    firstError = CheckError.createTimeoutError();
+                    // 设置错误标志
+                    hasError.set(true);
+                    break;
+                }
+
+                completedTasks++;
+                String taskName = futureToName.get(future);
+
+                try {
+                    R<ErrorInfo> result = future.get();
+                    if (R.isError(result)) {
+                        firstError = result;
+                        log.info("{}验证失败: {}", taskName, result);
+                        // 设置错误标志并立即取消其他任务
+                        hasError.set(true);
+                        cancelPendingFutures(allFutures);
+                    }
+                } catch (ExecutionException e) {
+                    firstError = CheckError.createExecutionError(taskName, e.getCause());
+                    hasError.set(true);
+                    cancelPendingFutures(allFutures);
+                } catch (CancellationException e) {
+                    log.info("{}已提前终止", taskName);
+                    completedTasks--; // 调整计数器
+                }
+            }
         } catch (InterruptedException e) {
-            log.error("executeTermValidationChain- main 被中断 :{}", e.getMessage());
             Thread.currentThread().interrupt();
-            // 取消所有任务
-            futures.forEach(f -> f.cancel(true));
-            return createError(TradeStatusEnum.SysError);
+            firstError = CheckError.createInterruptionError();
+            hasError.set(true);
+            cancelPendingFutures(allFutures);
+        } finally {
+            // 6. 确保所有任务最终被清理
+            if (firstError == null && completedTasks < totalTasks) {
+                cancelPendingFutures(allFutures);
+            }
         }
 
-        // 循环取消所有任务
-        futures.forEach(f -> f.cancel(true));
-        if (!finallyResult) {
-            log.error("executeTermValidationChain- 设备、账户、参数校验10s超时");
-            return createError(TradeStatusEnum.SysError);
-        }
+        return firstError != null ? firstError : R.ok();
+    }
 
-        R<ErrorInfo> errorInfoR = firstError.get();
-        log.info("executeTermValidationChain-  设备、账户、参数校验耗时:{}ms", System.currentTimeMillis() - starTime);
-        if (errorInfoR != null && R.isError(errorInfoR)) {
-            return errorInfoR;
-        }else {
-            return R.ok();
+    /**
+     * 取消所有未完成的任务。
+     *
+     * @param futures 任务的 Future 对象列表
+     */
+    private void cancelPendingFutures(List<Future<R<ErrorInfo>>> futures) {
+        for (Future<R<ErrorInfo>> f : futures) {
+            if (!f.isDone()) {
+                f.cancel(true);
+            }
         }
-
-//        int taskIndex = 0;
-//        try {
-//            for (Future<R<ErrorInfo>> future : futures) {
-//                long starTime = System.currentTimeMillis();
-//                if (firstError.get() != null) {
-//                    future.cancel(true); // 取消剩余任务
-//                    continue;
-//                }
-//
-//                try {
-//                    // R<ErrorInfo> result = future.get(VALIDATION_TIMEOUT, TimeUnit.MILLISECONDS);
-//                    R<ErrorInfo> result = future.get();
-//                    // 发现错误,立即取消其他任务
-//                    if (result != null && R.isError(result)) {
-//                        if (firstError.compareAndSet(null, result)) {
-//                            futures.forEach(f -> f.cancel(true));
-//                        }
-//                    }
-//                } catch (ExecutionException e) {
-//                    log.error("{}验证执行异常", getTaskName(taskIndex), e);
-//                    if (firstError.compareAndSet(null, createError(TradeStatusEnum.SysError))) {
-//                        futures.forEach(f -> f.cancel(true));
-//                    }
-//                } finally {
-//                    log.info("{}结束,耗时:{} ms", getTaskName(taskIndex), System.currentTimeMillis() - starTime);
-//                    taskIndex++;
-//                }
-//            }
-//            return firstError.get() != null ? firstError.get() : R.ok();
-//
-//        } catch (InterruptedException e) {
-//            Thread.currentThread().interrupt(); // 保留中断状态
-//            log.error("验证过程被中断", e);
-//            return createError(TradeStatusEnum.SysError);
-//        }
-
-        // // 提交所有验证任务
-        // for (int i = 0, j = validationTasks.size(); i < j; i++) {
-        //     long starTime = System.currentTimeMillis();
-        //     final int taskIndex = i;
-        //     final Supplier<R<ErrorInfo>> task = validationTasks.get(taskIndex);
-        //     taskExecutor.execute(() -> {
-        //         if (cancelled.get()) {
-        //             latch.countDown();
-        //             return;
-        //         }
-        //
-        //         try {
-        //             R<ErrorInfo> result = task.get();
-        //             // 如果发现错误且尚未设置错误结果
-        //             if (result != null && R.isError(result) &&
-        //                 firstError.compareAndSet(null, result)) {
-        //                 // 取消其他任务(通过中断)
-        //                 taskExecutor.getThreadPoolExecutor().getQueue().clear();
-        //             }
-        //         } catch (Exception e) {
-        //             if (firstError.compareAndSet(null, createError(TradeStatusEnum.SysError))) {
-        //                 taskExecutor.getThreadPoolExecutor().getQueue().clear();
-        //             }
-        //         } finally {
-        //             log.info("{}完成,耗时:{} ms",getTaskName(taskIndex),System.currentTimeMillis()-starTime);
-        //             latch.countDown();
-        //         }
-        //     });
-        // }
-        //
-        // try {
-        //     // 等待所有任务完成或超时
-        //     if (!latch.await(VALIDATION_TIMEOUT, TimeUnit.MILLISECONDS)) {
-        //         return createError(TradeStatusEnum.VALIDATION_TIMEOUT);
-        //     }
-        //
-        //     // 返回第一个发现的错误,如果没有错误则返回成功
-        //     return firstError.get() != null ? firstError.get() : R.ok();
-        // } catch (InterruptedException e) {
-        //     Thread.currentThread().interrupt();
-        //     log.error("error:{}", e.getMessage(), e);
-        //     return createError(TradeStatusEnum.SysError);
-        // }
     }
 
     /**
-     * 获取所有终端的验证任务
+     * 创建一个带有日志记录的任务。
      *
-     * @param ctx
-     * @return
+     * @param taskName 任务名称
+     * @param task 需要执行的任务
+     * @param hasError 用于检查是否有其他任务已经失败的标志
+     * @return 带有日志记录的 Callable 任务
      */
-    private List<Callable<R<ErrorInfo>>> getTermValidCallables(AllowConsumeValidationContext ctx,
-                                                               AtomicReference<R<ErrorInfo>> firstError, CountDownLatch latch) {
-        List<Callable<R<ErrorInfo>>> validationTasks = new ArrayList<>();
-
-        // 校验参数任务
-        validationTasks.add(() -> {
-            R<ErrorInfo> errorInfoR = firstError.get();
-            if (errorInfoR != null && R.isError(errorInfoR)) {
-                return null;
+    private Callable<R<ErrorInfo>> createLoggedTask(String taskName, Callable<R<ErrorInfo>> task, AtomicBoolean hasError) {
+        return () -> {
+            // 快速失败检查
+            if (hasError.get()) {
+                log.warn("{}-跳过执行(已有失败)", taskName);
+                throw new CancellationException("任务已取消");
             }
-            R<ErrorInfo> infoR = checkParam(ctx);
-            if (infoR != null && R.isError(infoR)) {
-                // 设置错误结果
-                firstError.set(infoR);
-                latch.countDown();
-            }
-            return infoR;
-        });
-        //消费设备校验
-        validationTasks.add(() -> {
-            R<ErrorInfo> errorInfoR = firstError.get();
-            if (errorInfoR != null && R.isError(errorInfoR)) {
-                return null;
-            }
-            R<ErrorInfo> infoR = checkTerm(ctx);
-            if (infoR != null && R.isError(infoR)) {
-                // 存储错误结果
-                firstError.set(infoR);
-                latch.countDown();
-            }
-            return infoR;
-        });
-        //用户账户校验
-        validationTasks.add(() -> {
-            R<ErrorInfo> errorInfoR = firstError.get();
-            if (errorInfoR != null && R.isError(errorInfoR)) {
-                return null;
+            // 主动检查中断状态
+            if (Thread.currentThread().isInterrupted()) {
+                log.info("{}-被中断", taskName);
+                throw new CancellationException("线程已中断");
             }
-            R<ErrorInfo> infoR = checkUserAccount(ctx);
-            if (infoR != null && R.isError(infoR)) {
-                // 存储错误结果
-                firstError.set(infoR);
-                latch.countDown();
+            final long startTime = System.currentTimeMillis();
+            log.info("{}-开始执行", taskName);
+
+            try {
+                R<ErrorInfo> result = task.call();
+
+                if (Thread.currentThread().isInterrupted()) {
+                    throw new InterruptedException("任务执行中被取消");
+                }
+                return result;
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                log.info("{}-执行中被取消", taskName);
+                throw new CancellationException("线程已中断");
+            } finally {
+                final long duration = System.currentTimeMillis() - startTime;
+                log.info("{}-结束-耗时:{}ms", taskName, duration);
             }
-            return infoR;
-        });
-        return validationTasks;
-    }
-
-    private String getTaskName(int taskIndex) {
-        return switch (taskIndex) {
-            case 0 -> "参数验证";
-            case 1 -> "终端验证";
-            case 2 -> "账户验证";
-            default -> "未知任务";
         };
     }
 
-    // region 消费参数校验
+    // endregion
 
+    // region 消费参数校验
     /**
      * 消费参数校验
      * <p>1.必须有设备机号或mac地址,如果是机号则机号>0</p>
@@ -288,15 +240,15 @@ public class CommonCheck {
     public R<ErrorInfo> checkParam(AllowConsumeValidationContext ctx) {
         // 1. 校验设备标识参数
         if (isTerminalInvalid(ctx)) {
-            return createErrorResponse(1, ApiErrorTypeConstants.PARAM_ERROR,
+            return CheckError.createErrorResponse(1, ApiErrorTypeConstants.PARAM_ERROR,
                 "设备机号不正确", "设备机号必须大于零或MAC地址不能为空!");
         }
 
         // 2. 校验用户标识参数
         if (isUserIdentifierInvalid(ctx)) {
-            return createErrorResponse(1, ApiErrorTypeConstants.PARAM_ERROR,
+            return CheckError.createErrorResponse(1, ApiErrorTypeConstants.PARAM_ERROR,
                 "交易人员标识不满足",
-                "必须提供 [CardNo | FactoryId | userNo | userNumb]  中至少1项来标识交易用户");
+                "必须提供 [cardID | factoryFixID | employeeID | employeeStrID]  中至少1项来标识交易用户");
         }
 
         // 3. 所有参数校验通过
@@ -338,14 +290,16 @@ public class CommonCheck {
      * @param ctx 消费有效性校验上下文
      * @return 检查结果
      */
-    public R<ErrorInfo> checkTerm(AllowConsumeValidationContext ctx) {
+    public R<ErrorInfo> checkTerm(AllowConsumeValidationContext ctx) throws InterruptedException {
         String msgInfo = ObjectUtil.isEmpty(ctx.getTermMac()) ? ctx.getTermNo().toString() : ctx.getTermMac();
         String checkParam = ObjectUtil.isNotEmpty(ctx.getTermMac()) ? ctx.getTermMac() : ctx.getTermNo().toString();
         Integer checkMode = ObjectUtil.isNotEmpty(ctx.getTermMac()) ? ConsumeConstants.TERM_MAC : ConsumeConstants.TERM_NO;
+        // 延时1毫秒以便捕获中断响应
+        Thread.sleep(1);
         XfTermVo termVo = termBusiness.getTermFromCache(checkParam, checkMode);
 
         if (ObjectUtil.isEmpty(termVo)) {
-            return createErrorResponse(400, ApiErrorTypeConstants.OBJECT_NOT_EXISTS,
+            return CheckError.createErrorResponse(400, ApiErrorTypeConstants.OBJECT_NOT_EXISTS,
                 "设备不存在",
                 MessageFormat.format("机号或MAC为[{0}]的设备不存在,不允许交易", msgInfo));
         }
@@ -368,12 +322,14 @@ public class CommonCheck {
      * @param ctx 消费有效性校验上下文,包含消费相关的基础信息(如用户流水号、消费金额等)
      * @return 如果校验失败,则返回包含错误信息的 R 对象;如果校验成功,则返回表示成功的 R 对象
      */
-    public R<ErrorInfo> checkUserAccount(AllowConsumeValidationContext ctx) {
+    public R<ErrorInfo> checkUserAccount(AllowConsumeValidationContext ctx) throws InterruptedException {
         long cardNo = ObjectUtil.isEmpty(ctx.getCardNo()) ? 0 : ctx.getCardNo();
         long userNo = ObjectUtil.isEmpty(ctx.getUserNo()) ? 0 : ctx.getUserNo();
         long factoryId = ObjectUtil.isEmpty(ctx.getFactoryId()) ? 0 : ctx.getFactoryId();
         String userNumb = ObjectUtil.isEmpty(ctx.getUserNumb()) ? null : ctx.getUserNumb();
 
+        // 延时1毫秒以便捕获中断响应
+        Thread.sleep(1);
         // 如果卡流水号>0验证卡信息
         if (cardNo > 0) {
             return checkCardNo(ctx);
@@ -407,7 +363,7 @@ public class CommonCheck {
         Long factoryId = ctx.getFactoryId();
         Long cardFactoryId = ctx.getUserCardVo().getFactoryId();
         if (factoryId.compareTo(0L) > 0 && !cardFactoryId.equals(factoryId)) {
-            return createErrorResponse(400, ApiErrorTypeConstants.PARAM_ERROR,
+            return CheckError.createErrorResponse(400, ApiErrorTypeConstants.PARAM_ERROR,
                 "卡片不正确",
                 MessageFormat.format("物理卡号不一致,不允许交易。传入值[{0}],系统值[{1}]", factoryId,
                     cardFactoryId));
@@ -450,7 +406,6 @@ public class CommonCheck {
         return R.ok();
     }
 
-
     /**
      * 根据用户流水号校验账户是否可以消费。
      *
@@ -469,7 +424,7 @@ public class CommonCheck {
     }
 
     /**
-     * 根据用户编号校验账户是否可以消费。
+     * 根据用户编号校验账户是否可以消费。
      *
      * @param ctx 消费有效性校验上下文,包含消费相关的基础信息(如用户流水号、消费金额等)
      * @return 如果校验失败,则返回包含错误信息的 R 对象;如果校验成功,则返回表示成功的 R 对象
@@ -483,9 +438,14 @@ public class CommonCheck {
         }
         // 校验卡片信息
         return checkCardAfterUser(ctx);
-
     }
 
+    /**
+     * 在用户检查之后验证卡片。
+     *
+     * @param ctx 验证上下文
+     * @return 验证结果
+     */
     private R<ErrorInfo> checkCardAfterUser(AllowConsumeValidationContext ctx) {
         R<ErrorInfo> result;
         Long userId = ctx.getUserAccountVo().getUserId();
@@ -514,31 +474,31 @@ public class CommonCheck {
         RemoteUserAccountVo accountVo = employeeBusiness.getAccountFromCache(checkParam, checkMode);
         // 账户不存在,不允许交易
         if (accountVo == null) {
-            return createErrorResponse(400, ApiErrorTypeConstants.NOT_FOUND,
+            return CheckError.createErrorResponse(400, ApiErrorTypeConstants.NOT_FOUND,
                 "账户不存在",
                 MessageFormat.format("流水号或Id为[{0}]的账户不存在,不允许交易", checkParam));
         }
         // 账户被冻结,不允许交易
         if (ObjectUtil.equals(accountVo.getFreezeStatus(), Constants.SYS_YES)) {
-            return createErrorResponse(400, ApiErrorTypeConstants.BAD_REQUEST,
+            return CheckError.createErrorResponse(400, ApiErrorTypeConstants.BAD_REQUEST,
                 "账户被冻结",
                 MessageFormat.format("流水号或Id为[{0}]的账户被冻结,不允许交易", checkParam));
         }
         // 账户状态不正常,不允许交易
         if (ObjectUtil.equals(accountVo.getStatus(), Constants.SYS_NO)) {
-            return createErrorResponse(400, ApiErrorTypeConstants.NOT_FOUND,
+            return CheckError.createErrorResponse(400, ApiErrorTypeConstants.NOT_FOUND,
                 "账户状态不正常",
                 MessageFormat.format("流水号或Id为[{0}]的账户状态不正常,不允许交易", checkParam));
         }
         // 未开户,不允许交易
         if (!ObjectUtil.equals(accountVo.getAccountStatus(), UserAccountStatusEnum.IS_OPEN.code().toString())) {
-            return createErrorResponse(400, ApiErrorTypeConstants.NOT_FOUND,
+            return CheckError.createErrorResponse(400, ApiErrorTypeConstants.NOT_FOUND,
                 "账户尚未开户",
                 MessageFormat.format("流水号或Id为[{0}]的账户尚未开户,不允许交易", checkParam));
         }
         // 账户已过有效期,不允许消费
         if (accountVo.getLifespan().getTime() < System.currentTimeMillis()) {
-            return createErrorResponse(400, ApiErrorTypeConstants.BAD_REQUEST,
+            return CheckError.createErrorResponse(400, ApiErrorTypeConstants.BAD_REQUEST,
                 "账户过期",
                 MessageFormat.format("流水号或Id为[{0}]的账户已过期,不允许交易", checkParam));
         }
@@ -562,13 +522,13 @@ public class CommonCheck {
         RemoteCardVo cardVo = cardBusiness.getCardFromCache(checkParam, checkMode);
         // 卡片不存在,不允许消费
         if (cardVo == null) {
-            return createErrorResponse(400, ApiErrorTypeConstants.CARD_NOT_EXISTS,
+            return CheckError.createErrorResponse(400, ApiErrorTypeConstants.CARD_NOT_EXISTS,
                 "卡片不存在",
                 MessageFormat.format("流水号或用户Id或物理卡号为[{0}]的卡片不存在,不允许交易", checkParam));
         }
         // 卡片状态不正常,不允许消费
         if (!String.valueOf(CardStatusEnum.NORMAL.code()).equals(cardVo.getStatus())) {
-            return createErrorResponse(400, ApiErrorTypeConstants.CARD_STATUS_NOT_NORMAL,
+            return CheckError.createErrorResponse(400, ApiErrorTypeConstants.CARD_STATUS_NOT_NORMAL,
                 "卡片状态不正确",
                 MessageFormat.format("流水号或用户Id或物理卡号为[{0}]的卡片状态不正确,不允许交易", checkParam));
 
@@ -587,43 +547,31 @@ public class CommonCheck {
     }
 
     /**
-     * 创建错误响应对象
+     * 设置校验后的账户与卡片数据。
+     * <p>
+     * 该方法用于在校验完成后,将相关的账户信息和卡片信息设置到业务对象中,以便后续的业务逻辑使用。
      *
-     * @param code   错误码
-     * @param type   错误类型
-     * @param msg    错误消息
-     * @param detail 错误详情
-     * @return 封装好的错误响应
+     * @param ctx 消费有效性校验上下文,包含消费相关的基础信息
      */
-    public R<ErrorInfo> createErrorResponse(int code, String type, String msg, String detail) {
-        ErrorInfo errorInfo = new ErrorInfo(code, type, msg, detail);
-        return R.fail(errorInfo);
-    }
+    private void setCheckAfterAccountData(AllowConsumeValidationContext ctx) {
+        RemoteUserAccountVo accountVo = ctx.getUserAccountVo();
+        RemoteCardVo cardVo = ctx.getUserCardVo();
+        // 重置部部分卡片信息
+        ctx.getBo().setCardNo(cardVo.getCardNo());
+        ctx.getBo().setFactoryId(cardVo.getFactoryId());
+        ctx.getBo().setCardTypeName(cardVo.getCardTypeName());
 
-    /**
-     * 创建错误响应对象
-     *
-     * @param status 交易状态
-     * @return 封装好的错误响应
-     */
-    public R<ErrorInfo> createError(TradeStatusEnum status) {
-        return createError(status, status.getName());
+        // 重置部分账户信息
+        ctx.getBo().setUserId(accountVo.getUserId());
+        ctx.getBo().setRealName(StrUtil.isEmpty(accountVo.getRealName()) ? "----" : accountVo.getRealName());
+        ctx.getBo().setUserNo(accountVo.getUserNo());
+        ctx.getBo().setUserNumb(accountVo.getUserNumb());
+        ctx.getBo().setTenantId(accountVo.getTenantId());
+        ctx.getBo().setExpireDate(accountVo.getLifespan());
+        ctx.getBo().setDeptName(accountVo.getDeptName());
     }
 
-    /**
-     * 创建错误响应对象
-     *
-     * @param status  交易状态
-     * @param message 错误消息
-     * @return 封装好的错误响应
-     */
-    public R<ErrorInfo> createError(TradeStatusEnum status, String message) {
-        return R.fail(new ErrorInfo(
-            400,
-            status.toString(),
-            message,
-            status.getName()));
-    }
+    // endregion
 
     /**
      * 根据消费日期获取对应的餐次类型。
@@ -667,48 +615,10 @@ public class CommonCheck {
         Long mealType = bo.getMealType();
         Date consumeDate = bo.getConsumeDate();
         BigDecimal consumeMoney = bo.getConsumeMoney();
-        String currentDateStr = DateFormatUtils.format(new Date(), "yyyy-MM-dd");
-        String consumeDateStr = DateFormatUtils.format(consumeDate, "yyyy-MM-dd");
-        Long userId = bo.getUserId();
-        BigDecimal balance = bo.getBalance();
-
-        //// 将当笔原始消费记录标识放入缓存,1天过期
-        //Set<String> originalId = Collections.singleton(bo.getOriginalId());
-        //RedisUtils.setCacheSet(CacheNames.XF_ORIGINAL_ID, originalId);
-        //RedisUtils.expire(CacheNames.XF_ORIGINAL_ID, Duration.ofHours(5));
 
-        // if (ObjectUtil.equals(currentDateStr, consumeDateStr)) {
         // 重置卡天当日消费数据
-        taskExecutor.execute(() -> baseBusiness.resetCardConsumeInfo(userCardVo, mealType, consumeMoney, consumeDate));
+        threadPoolTaskExecutor.execute(() -> baseBusiness.resetCardConsumeInfo(userCardVo, mealType, consumeMoney, consumeDate));
         // 重置卡片当日限制数据
-        taskExecutor.execute(() -> baseBusiness.restCardLimitedInfo(mapCardLimited, cardLimitedVo, consumeMoney));
-        // 重置人员当日总卡余
-        // taskExecutor.submit(() -> baseBusiness.resetUserBalance(userId, balance));
-        // }
-    }
-
-    /**
-     * 设置校验后的账户与卡片数据。
-     * <p>
-     * 该方法用于在校验完成后,将相关的账户信息和卡片信息设置到业务对象中,以便后续的业务逻辑使用。
-     *
-     * @param ctx 消费有效性校验上下文,包含消费相关的基础信息
-     */
-    private void setCheckAfterAccountData(AllowConsumeValidationContext ctx) {
-        RemoteUserAccountVo accountVo = ctx.getUserAccountVo();
-        RemoteCardVo cardVo = ctx.getUserCardVo();
-        // 重置部部分卡片信息
-        ctx.getBo().setCardNo(cardVo.getCardNo());
-        ctx.getBo().setFactoryId(cardVo.getFactoryId());
-        ctx.getBo().setCardTypeName(cardVo.getCardTypeName());
-
-        // 重置部分账户信息
-        ctx.getBo().setUserId(accountVo.getUserId());
-        ctx.getBo().setRealName(StrUtil.isEmpty(accountVo.getRealName()) ? "----" : accountVo.getRealName());
-        ctx.getBo().setUserNo(accountVo.getUserNo());
-        ctx.getBo().setUserNumb(accountVo.getUserNumb());
-        ctx.getBo().setTenantId(accountVo.getTenantId());
-        ctx.getBo().setExpireDate(accountVo.getLifespan());
-        ctx.getBo().setDeptName(accountVo.getDeptName());
+        threadPoolTaskExecutor.execute(() -> baseBusiness.restCardLimitedInfo(mapCardLimited, cardLimitedVo, consumeMoney));
     }
 }

+ 57 - 71
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/ConsumeRequestCheck.java

@@ -1,5 +1,7 @@
 package org.dromara.server.consume.check;
+import java.util.Date;
 
+import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
 import lombok.RequiredArgsConstructor;
@@ -56,6 +58,7 @@ public class ConsumeRequestCheck {
     private final CardConsumeValidation cardConsumevalidation;
     private final BaseBusiness baseBusiness;
     private final IPtBagService bagService;
+    private final ParallelTaskExecutor taskExecutor;
 
     public R<ErrorInfo> checkConsume(ConsumptionBo bo, RemoteUserAccountVo userAccountVo,
                                      RemoteCardVo userCardVo, XfTermVo useTermVo,
@@ -69,23 +72,22 @@ public class ConsumeRequestCheck {
             bo.setConsumeDate(consumeDate);
         }
         // 检查是否重复请求
-        long startTime = System.currentTimeMillis();
-        log.info("检查重复原始消费记录");
         R<ErrorInfo> result = checkRepeatOriginalId(bo);
         if (R.isError(result)) {
             return result;
         }
-        log.info("检查重复原始消费记录,耗时:{} ms ", System.currentTimeMillis()-startTime);
         // 获取餐类
+        long startTime = System.currentTimeMillis();
         RemoteMealTypeVo remoteMealTypeVo = commonCheck.getMealType(consumeDate);
+        log.info("[请求交易]-[获取餐类当前餐类]-[耗时:{} ms] ", System.currentTimeMillis()-startTime);
         if (ObjectUtil.isEmpty(remoteMealTypeVo)) {
-            return commonCheck.createErrorResponse(400, ApiErrorTypeConstants.NOT_FOUND, "不在交易时段",
+            return CheckError.createErrorResponse(400, ApiErrorTypeConstants.NOT_FOUND, "不在交易时段",
                 MessageFormat.format("非营业时段,不允许交易。消费时间[{0}]",
                     DateUtil.format(consumeDate, "HH:mm:ss")));
         }
         // 设置消费记录的当前餐类
         bo.setMealType(Long.valueOf(remoteMealTypeVo.getTypeId()));
-        log.info("获取餐类,耗时:{} ms ", System.currentTimeMillis()-startTime);
+
         // 如果消费记录标识为消费机消费则需要进行限次、限额和折扣验证
         XfCardLimitedVo xfCardLimitedVo = new XfCardLimitedVo();
         int statusFlag = bo.getStatusFlag();
@@ -93,28 +95,34 @@ public class ConsumeRequestCheck {
             || statusFlag == Integer.parseInt(ConsumeRecordTypeEnum.XFJXF_4.code())) {
 
             // 设备是否可以消费验证
-            long startTime1 = System.currentTimeMillis();
             result = checkTermLimitedAndOther(bo, useTermVo, userCardVo, remoteMealTypeVo);
-            log.info("检查是否设备是否可以消费,耗时:{} ms ", System.currentTimeMillis()-startTime1);
             if (R.isError(result)) {
                 return obtainResult(result);
             }
             // 卡片是否可以消费验证
-            long startTime2 = System.currentTimeMillis();
             result = cardConsumevalidation.cardValidation(bo, useTermVo, userCardVo, remoteMealTypeVo, xfCardLimitedVo, mapCardLimited);
-            log.info("检查是卡片是否可以消费,耗时:{} ms ", System.currentTimeMillis()-startTime2);
             if (R.isError(result)) {
                 return obtainResult(result);
             }
         }
-        log.info("检查是否可以消费,耗时:{} ms ", System.currentTimeMillis()-startTime);
         // 余额校验,余额不足不能交易,如果有折扣,则消费金额是以折扣金额为准的,所以余额验证放在最后
         result = checkWalletBalance(bo);
         if (R.isError(result)) {
             return result;
         }
-        log.info("检查钱包,耗时:{} ms ", System.currentTimeMillis()-startTime);
         RemoteVoConvert.INSTANCE.copyXfCardLimitedVo(cardLimitedVo, xfCardLimitedVo);
+        // cardLimitedVo
+        // cardLimitedVo.setLimitedId(xfCardLimitedVo.getLimitedId());
+        // cardLimitedVo.setCardNo(xfCardLimitedVo.getCardNo());
+        // cardLimitedVo.setDayCount(xfCardLimitedVo.getDayCount());
+        // cardLimitedVo.setDayMoney(xfCardLimitedVo.getDayMoney());
+        // cardLimitedVo.setMealCount(xfCardLimitedVo.getMealCount());
+        // cardLimitedVo.setMealMoney(xfCardLimitedVo.getMealMoney());
+        // cardLimitedVo.setDayDiscountCount(xfCardLimitedVo.getDayDiscountCount());
+        // cardLimitedVo.setMealDiscountCount(xfCardLimitedVo.getMealDiscountCount());
+        // cardLimitedVo.setLastPay(xfCardLimitedVo.getLastPay());
+        // cardLimitedVo.setLastMeal(xfCardLimitedVo.getLastMeal());
+        // BeanUtil.copyProperties(xfCardLimitedVo, cardLimitedVo);
         return R.ok();
     }
 
@@ -155,20 +163,22 @@ public class ConsumeRequestCheck {
      * @return 如果校验失败,则返回包含错误信息的 R 对象;如果校验成功,则返回表示成功的 R 对象
      */
     private R<ErrorInfo> checkWalletBalance(ConsumptionBo bo) {
-        String userIdStr = bo.getUserId().toString();
+        long startTime = System.currentTimeMillis();
         BigDecimal consumeMoney = bo.getConsumeMoney();
         BigDecimal totalBalance = bagService.getUserTotalBalance(bo.getUserId());
         if (ObjectUtil.isEmpty(totalBalance)) {
             totalBalance = BigDecimal.ZERO;
         }
         if (consumeMoney.compareTo(totalBalance) > 0) {
-            return commonCheck.createErrorResponse(400, ApiErrorTypeConstants.CONSUME_CHECK_FAIL, "账户余额不足",
+            log.info("[请求交易]-[钱包余额验证完成]-[耗时:{} ms]",System.currentTimeMillis()-startTime);
+            return CheckError.createErrorResponse(400, ApiErrorTypeConstants.CONSUME_CHECK_FAIL, "账户余额不足",
                 MessageFormat.format("总余额[{0}],消费金额[{1}]", totalBalance, consumeMoney));
         }
         // 计算扣费后的余额
         BigDecimal balance = totalBalance.subtract(consumeMoney);
         bo.setBalance(balance);
         baseBusiness.resetUserBalance(bo.getUserId(), balance);
+        log.info("[请求交易]-[钱包余额验证完成]-[耗时:{} ms]",System.currentTimeMillis()-startTime);
         return R.ok();
     }
 
@@ -183,40 +193,50 @@ public class ConsumeRequestCheck {
      * @return 如果原始消费记录ID重复,返回包含错误信息的响应对象;否则返回成功的响应对象
      */
     private R<ErrorInfo> checkRepeatOriginalId(ConsumptionBo bo) {
-
+        long startTime = System.currentTimeMillis();
         String originalId = bo.getOriginalId();
         if (ObjectUtil.isEmpty(originalId)) {
             originalId = RecordIdUtils.getRecordId(bo.getConsumeDate(), bo.getTermNo().intValue(),
                 bo.getTermRecordId().intValue(), bo.getUserNo().intValue(), 0);
         }
-        //String originalIdCache = RedisUtils.getCacheObject(CacheNames.XF_ORIGINAL_ID+ ":"+ originalId);
-        //if (originalIdCache != null && originalIdCache.equals(originalId)) {
-        if (RedisUtils.hasKey(CacheNames.XF_ORIGINAL_ID+ ":"+ originalId)) {
-            return commonCheck.createErrorResponse(400, ApiErrorTypeConstants.CONSUME_CHECK_FAIL, "原始消费记录存在",
+
+        String keyName = String.format("%s:%s", CacheNames.XF_ORIGINAL_ID, originalId);
+        if (RedisUtils.hasKey(keyName)) {
+            log.info("[请求交易]-[检查是否重复请求]-[耗时:{} ms] ", System.currentTimeMillis()-startTime);
+            return CheckError.createErrorResponse(400, ApiErrorTypeConstants.CONSUME_CHECK_FAIL, "原始消费记录存在",
                 MessageFormat.format("原始消费记录已存在:{0}", originalId));
         }
         bo.setOriginalId(originalId);
 
-        // 将当笔原始消费记录标识放入缓存,4小时过期
-        RedisUtils.setCacheObject(CacheNames.XF_ORIGINAL_ID+ ":"+ originalId, "",Duration.ofHours(1));
-        //RedisUtils.expire(CacheNames.XF_ORIGINAL_ID, Duration.ofHours(4));
-
+        // 将当笔原始消费记录标识放入缓存,1小时过期
+        RedisUtils.setCacheObject(keyName, "",Duration.ofHours(1));
+        log.info("[请求交易]-[检查是否重复请求]-[耗时:{} ms] ", System.currentTimeMillis()-startTime);
         return R.ok();
     }
 
     private R<ErrorInfo> checkTermLimitedAndOther(ConsumptionBo bo, XfTermVo termVo, RemoteCardVo userCardVo, RemoteMealTypeVo mealTypeVo) {
-        log.info("检查是否设备是否可以消费开始");
-        // 1. 前置检查与数据初始化
+        // 1. 如果机号为0并且餐类为0,直接返回成功
         if (isSpecialScenario(termVo, mealTypeVo)) return R.ok();
+
+        // 2.初始化卡片的消费信息
         long startTime = System.currentTimeMillis();
         initializeCardData(userCardVo, mealTypeVo);
-        log.info("初始化日卡数据,耗时:{} ms",System.currentTimeMillis()-startTime);
         // 2. 准备验证上下文
         TermConsumeValidationContext validationContext = TermConsumeValidationContext.create(bo, termVo, userCardVo, mealTypeVo);
-        log.info("准备验证上下文,耗时:{} ms",System.currentTimeMillis()-startTime);
-        // 3. 执行异步验证链
-        return executeTermValidationChain(validationContext);
 
+        // 3. 执行异步验证链
+        List<ValidationTaskDes<R<ErrorInfo>>> tasks = List.of(
+            new ValidationTaskDes<>("设备消费间隔验证", () -> validateSwipeInterval(validationContext)),
+            new ValidationTaskDes<>("设备单次金额验证", () -> validateSingleLimit(validationContext)),
+            new ValidationTaskDes<>("设备卡片有效验证", () -> validateCardValidity(validationContext)),
+            new ValidationTaskDes<>("设备消费卡类验证", () -> validateCardType(validationContext)),
+            new ValidationTaskDes<>("设备每餐限制验证", () -> validateMealLimit(validationContext)),
+            new ValidationTaskDes<>("设备每日限制验证", () -> validateDailyLimit(validationContext))
+        );
+        R<ErrorInfo>result =  taskExecutor.executeParallelTasks(tasks, 5, TimeUnit.SECONDS);
+        log.info("[请求交易]-[设备消费验证完成]-[耗时:{} ms]",System.currentTimeMillis()-startTime);
+
+        return result;
     }
 
     /**
@@ -305,14 +325,14 @@ public class ConsumeRequestCheck {
             Thread.currentThread().interrupt();
             // 取消所有任务
             futures.forEach(f -> f.cancel(true));
-            return createError(TradeStatusEnum.SysError);
+            return CheckError.createError(TradeStatusEnum.SysError);
         }
 
         // 循环取消所有任务
         futures.forEach(f -> f.cancel(true));
         if (!finallyResult) {
             log.error("executeTermValidationChain- 执行消费终端校验链10s超时");
-            return createError(TradeStatusEnum.SysError);
+            return CheckError.createError(TradeStatusEnum.SysError);
         }
 
         R<ErrorInfo> errorInfoR = firstError.get();
@@ -509,34 +529,6 @@ public class ConsumeRequestCheck {
         return validationTasks;
     }
 
-    //validationTasks.add(() -> validateSwipeInterval(context));
-    //validationTasks.add(() -> validateSingleLimit(context));
-    //validationTasks.add(() -> validateCardValidity(context));
-    //validationTasks.add(() -> validateCardType(context));
-    //validationTasks.add(() -> validateMealLimit(context));
-    //validationTasks.add(() -> validateDailyLimit(context));
-    private String getTaskName(int taskIndex) {
-        return switch (taskIndex) {
-            case 0 -> "消费间隔验证";
-            case 1 -> "单次验证";
-            case 2 -> "卡验证";
-            case 3 -> "卡类验证";
-            case 4 -> "餐类验证";
-            case 5 -> "日限制验证";
-            default -> "未知任务";
-        };
-    }
-    public R<ErrorInfo> createError(TradeStatusEnum status) {
-        return createError(status, status.getName());
-    }
-    public R<ErrorInfo> createError(TradeStatusEnum status, String message) {
-        return R.fail(new ErrorInfo(
-            400,
-            status.toString(),
-            message,
-            status.getName()));
-    }
-
     private R<ErrorInfo> validateSwipeInterval(TermConsumeValidationContext ctx) {
         if (ctx.getTermSwipeInterval() <= 0) return null;
 
@@ -545,44 +537,38 @@ public class ConsumeRequestCheck {
         long intervalMin = (currentSec - lastPaySec) / 60;
 
         return intervalMin < ctx.getTermSwipeInterval() ?
-            commonCheck.createError(TradeStatusEnum.TimeInterval) : null;
+            CheckError.createError(TradeStatusEnum.TimeInterval) : null;
     }
-
     private R<ErrorInfo> validateSingleLimit(TermConsumeValidationContext ctx) {
         if (ctx.getTermSingleMoney().compareTo(BigDecimal.ZERO) <= 0) return null;
         return ctx.getConsumeValue().compareTo(ctx.getTermSingleMoney()) > 0 ?
-            commonCheck.createError(TradeStatusEnum.OnceBigMoney) : null;
+            CheckError.createError(TradeStatusEnum.OnceBigMoney) : null;
     }
-
     private R<ErrorInfo> validateCardValidity(TermConsumeValidationContext ctx) {
         if (ctx.getFactoryId() == 0 || !ctx.isTermUseValidity()) return null;
         return ctx.getCurrentTime().isAfter(ctx.getExpiryTime()) ?
-            commonCheck.createError(TradeStatusEnum.CardValidDate) : null;
+            CheckError.createError(TradeStatusEnum.CardValidDate) : null;
     }
-
     private R<ErrorInfo> validateCardType(TermConsumeValidationContext ctx) {
         int mask = 1 << (ctx.getCardType() - 1);
-        log.info ("卡类是否消费验证");
         return (mask & ctx.getTermCardType()) == 0 ?
-            commonCheck.createError(TradeStatusEnum.CardTypeLimit) : null;
+            CheckError.createError(TradeStatusEnum.CardTypeLimit) : null;
     }
-
     private R<ErrorInfo> validateMealLimit(TermConsumeValidationContext ctx) {
         return ctx.getTermMealCount() > 0 && ctx.getMealCount() >= ctx.getTermMealCount() ?
-            commonCheck.createError(TradeStatusEnum.MealLimitTimes) : null;
+            CheckError.createError(TradeStatusEnum.MealLimitTimes) : null;
     }
-
     private R<ErrorInfo> validateDailyLimit(TermConsumeValidationContext ctx) {
         // 日限次验证
         if (ctx.getTermDayCount() > 0 && ctx.getDayCount() >= ctx.getTermDayCount()) {
-            return commonCheck.createError(TradeStatusEnum.DayLimitTimes);
+            return CheckError.createError(TradeStatusEnum.DayLimitTimes);
         }
 
         // 日限额验证
         if (ctx.getTermDayMoney().compareTo(BigDecimal.ZERO) > 0) {
             BigDecimal total = ctx.getDayValue().add(ctx.getConsumeValue());
             if (total.compareTo(ctx.getTermDayMoney()) > 0) {
-                return commonCheck.createError(TradeStatusEnum.DayLimitMoney);
+                return CheckError.createError(TradeStatusEnum.DayLimitMoney);
             }
         }
         return null;

+ 1 - 1
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/ConsumeUploadCheck.java

@@ -69,7 +69,7 @@ public class ConsumeUploadCheck {
         String detailIdSet = RedisUtils.getCacheMapValue(CacheNames.XF_DETAIL_ID,detailId);
 
         if (detailIdSet!=null && detailIdSet.equals(detailId)) {
-            return commonCheck.createErrorResponse(400, ApiErrorTypeConstants.CONSUME_CHECK_FAIL, "消费记录已上传",
+            return CheckError.createErrorResponse(400, ApiErrorTypeConstants.CONSUME_CHECK_FAIL, "消费记录已上传",
                 MessageFormat.format("消费记录已上传:{0}", detailId));
         }
 

+ 15 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/IValidationTask.java

@@ -0,0 +1,15 @@
+package org.dromara.server.consume.check;
+
+/**
+ * 通用消费校验任务接口
+ * <p>
+ * [功能说明]
+ * ${1:在此简要描述文件核心功能}
+ *
+ * @author luoyibo
+ * @date 2025-07-13
+ * @since JDK17
+ */
+public interface IValidationTask<T> {
+    T execute() throws Exception;
+}

+ 155 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/ParallelTaskExecutor.java

@@ -0,0 +1,155 @@
+package org.dromara.server.consume.check;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.domain.model.ErrorInfo;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * 并行任务执行器
+ * <p>
+ * [功能说明]
+ * ${1:在此简要描述文件核心功能}
+ *
+ * @author luoyibo
+ * @date 2025-07-13
+ * @since JDK17
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ParallelTaskExecutor {
+    private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
+
+    public <T> R<ErrorInfo> executeParallelTasks(
+        List<ValidationTaskDes<T>> taskDescriptors,
+        long timeout,
+        TimeUnit unit
+    ) {
+        final int totalTasks = taskDescriptors.size();
+        final AtomicBoolean hasError = new AtomicBoolean(false);
+        final AtomicReference<R<ErrorInfo>> firstError = new AtomicReference<>();
+        final long deadline = System.nanoTime() + unit.toNanos(timeout);
+        final ExecutorCompletionService<T> completionService = new ExecutorCompletionService<>(threadPoolTaskExecutor);
+
+
+        // 用于跟踪任务名称
+        Map<Future<T>, String> futureToName = new IdentityHashMap<>();
+        List<Future<?>> allFutures = new ArrayList<>();
+
+        // 提交任务
+        for (ValidationTaskDes<T> descriptor : taskDescriptors) {
+            String taskName = descriptor.getName();
+            Callable<T> task = createLoggedTask(taskName, descriptor.getTask(), hasError);
+            Future<T> future = completionService.submit(task);
+            futureToName.put(future, taskName);
+            allFutures.add(future);
+        }
+
+        try {
+            int completedTasks = 0;
+            while (completedTasks < totalTasks && !hasError.get()) {
+                long remainingNanos = deadline - System.nanoTime();
+                if (remainingNanos <= 0) {
+                    if (firstError.compareAndSet(null, CheckError.createTimeoutError())) {
+                        cancelPendingFutures(allFutures);
+                    }
+                    break;
+                }
+
+                Future<T> future = completionService.poll(remainingNanos, TimeUnit.NANOSECONDS);
+                if (future == null) {
+                    if (firstError.compareAndSet(null, CheckError.createTimeoutError())) {
+                        cancelPendingFutures(allFutures);
+                    }
+                    break;
+                }
+
+                completedTasks++;
+                String taskName = futureToName.get(future);
+
+                try {
+                    T result = future.get();
+                    if (result instanceof R && ((R<?>) result).getCode()!=200) {
+                        @SuppressWarnings("unchecked")
+                        R<ErrorInfo> errorResult = (R<ErrorInfo>) result;
+                        if (firstError.compareAndSet(null, errorResult)) {
+                            cancelPendingFutures(allFutures);
+                        }
+                    }
+                } catch (ExecutionException e) {
+                    if (firstError.compareAndSet(null, CheckError.createExecutionError(taskName, e.getCause()))) {
+                        cancelPendingFutures(allFutures);
+                    }
+                } catch (CancellationException e) {
+                    // 忽略已取消的任务
+                }
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            if (firstError.compareAndSet(null, CheckError.createInterruptionError())) {
+                cancelPendingFutures(allFutures);
+            }
+        } finally {
+            if (firstError.get() == null && allFutures.stream().anyMatch(f -> !f.isDone())) {
+                cancelPendingFutures(allFutures);
+            }
+        }
+
+        return firstError.get() != null ? firstError.get() : R.ok();
+    }
+
+    /**
+     * 创建带日志和中断检查的任务
+     */
+    private <T> Callable<T> createLoggedTask(String taskName, IValidationTask<T> task, AtomicBoolean hasError) {
+        return () -> {
+            if (hasError.get()) {
+                throw new CancellationException("任务已取消");
+            }
+
+            if (Thread.currentThread().isInterrupted()) {
+                throw new CancellationException("线程已中断");
+            }
+
+            final long startTime = System.currentTimeMillis();
+            log.info("{}开始执行", taskName);
+
+            try {
+                T result = task.execute();
+
+                // 检查执行过程中是否被中断
+                if (Thread.currentThread().isInterrupted()) {
+                    throw new InterruptedException("任务执行中被取消");
+                }
+
+                return result;
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                log.warn("{}执行中被取消", taskName);
+                throw new CancellationException("线程已中断");
+            } finally {
+                long duration = System.currentTimeMillis() - startTime;
+                log.info("{}结束,耗时:{}ms", taskName, duration);
+            }
+        };
+    }
+
+    private void cancelPendingFutures(List<Future<?>> futures) {
+        futures.forEach(f -> {
+            if (!f.isDone()) {
+                f.cancel(true);
+            }
+        });
+    }
+}

+ 21 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/check/ValidationTaskDes.java

@@ -0,0 +1,21 @@
+package org.dromara.server.consume.check;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * 任务描述器,封装任务名称和实际的任务逻辑
+ * <p>
+ * [功能说明]
+ * ${1:在此简要描述文件核心功能}
+ *
+ * @author luoyibo
+ * @date 2025-07-13
+ * @since JDK17
+ */
+@Data
+@AllArgsConstructor
+public class ValidationTaskDes <T>{
+    private final String name;
+    private final IValidationTask<T> task;
+}

+ 0 - 1
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/impl/XfCardLimitedServiceImpl.java

@@ -334,7 +334,6 @@ public class XfCardLimitedServiceImpl implements IXfCardLimitedService {
         } else {
             baseMapper.insert(entity);
         }
-
     }
 
     @Override