Forráskód Böngészése

fix: 消费服务
1.增加脱机消费处理机制

luo.yibo@datuai.com 1 éve
szülő
commit
2a5fdfbb2e

+ 2 - 0
ruoyi-server/ruoyi-server-common/src/main/java/org/dromara/server/common/domain/consume/bo/ConsumptionBo.java

@@ -1,6 +1,7 @@
 package org.dromara.server.common.domain.consume.bo;
 
 import lombok.Data;
+
 import java.math.BigDecimal;
 import java.util.Date;
 
@@ -88,6 +89,7 @@ public class ConsumptionBo {
      * 记录Id
      */
     private Long recordId;
+
     /**
      * 消费记录标志位?
      */

+ 10 - 4
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/BaseBusiness.java

@@ -80,14 +80,20 @@ public class BaseBusiness {
      */
     public R<ErrorInfo> createOriginalOrder(ConsumptionBo consumeBo, RemoteUserAccountVo accountVo,
                                             XfConsumeDetailOriginalVo originalVo) {
-        String originalId = RecordIdUtils.getRecordId(consumeBo.getConsumeDate(), consumeBo.getTermNo().shortValue(),
-                                                      consumeBo.getTermRecordId().shortValue(),
-                                                      accountVo.getUserNo().intValue(), 0);
+        String originalId = consumeBo.getOriginalId();
+        if (ObjectUtil.isEmpty(originalId)) {
+            originalId = RecordIdUtils.getRecordId(consumeBo.getConsumeDate(), consumeBo.getTermNo().shortValue(),
+                                                   consumeBo.getTermRecordId().shortValue(),
+                                                   accountVo.getUserNo().intValue(), 0);
+        }
 
         XfConsumeDetailOriginalBo originalBo = new XfConsumeDetailOriginalBo();
         BeanUtil.copyProperties(consumeBo, originalBo);
+        if (originalBo.getRecordId() == 0) {
+            originalBo.setRecordId(null);
+        }
         originalBo.setOriginalId(originalId);
-        originalBo.setDataFlag(0L);
+        originalBo.setDataFlag(consumeBo.getRecordStatus());
         originalBo.setAnalysisFlag(0L);
         originalBo.setCardValue(consumeBo.getBalance());
         originalBo.setConsumeBalance(consumeBo.getBalance());

+ 210 - 83
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/CheckBusiness.java

@@ -1,6 +1,7 @@
 package org.dromara.server.consume.business;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
@@ -36,10 +37,7 @@ import java.math.RoundingMode;
 import java.text.MessageFormat;
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
 
 /**
  * name: CheckBusiness
@@ -80,8 +78,45 @@ public class CheckBusiness {
     private final TokenManager tokenManager;
     private final IXfCardLimitedService cardLimitedService;
     private final DefaultConfig defaultConfig;
+    private final BaseBusiness baseBusiness;
     // private final SyncRemoteXfTermService syncRemoteXfTermService;
 
+    /**
+     * 计算折扣金额
+     *
+     * @param cardLimitedVo    卡片限制信息
+     * @param bo               消费记录
+     * @param remoteDiscountVo 折扣信息
+     * @return 折扣金额
+     */
+    @NotNull
+    private static BigDecimal getDisCountMoney(XfCardLimitedVo cardLimitedVo, ConsumptionBo bo, RemoteDiscountVo remoteDiscountVo) {
+        int rateCount = remoteDiscountVo.getRateType().equals(
+            "1") ? cardLimitedVo.getDayDiscountCount().intValue() : cardLimitedVo.getMealDiscountCount().intValue();
+        BigDecimal disCountMoney;
+        BigDecimal consumeMoney = bo.getConsumeMoney();
+        // 折扣率1
+        BigDecimal oneRate = remoteDiscountVo.getOneRate();
+        // 折扣率2
+        BigDecimal twoRate = remoteDiscountVo.getTwoRate();
+        // 折扣率3
+        BigDecimal threeRate = remoteDiscountVo.getThreeRate();
+        // 折扣率4
+        BigDecimal fourRate = remoteDiscountVo.getFourRate();
+        if (rateCount == 0) {
+            disCountMoney = consumeMoney.multiply(oneRate.divide(new BigDecimal("100.0"), 2, RoundingMode.HALF_UP)).setScale(2, RoundingMode.HALF_UP);
+        } else if (rateCount == 1) {// 第二次
+            disCountMoney = consumeMoney.multiply(twoRate.divide(new BigDecimal("100.0"), 2, RoundingMode.HALF_UP)).setScale(2, RoundingMode.HALF_UP);
+        } else if (rateCount == 2) {// 第三次
+            disCountMoney = consumeMoney.multiply(threeRate.divide(new BigDecimal("100.0"), 2, RoundingMode.HALF_UP)).setScale(2,
+                                                                                                                               RoundingMode.HALF_UP);
+        } else { // 第四次及以上
+            disCountMoney = consumeMoney.multiply(fourRate.divide(new BigDecimal("100.0"), 2, RoundingMode.HALF_UP)).setScale(2,
+                                                                                                                              RoundingMode.HALF_UP);
+        }
+        return disCountMoney;
+    }
+
     /**
      * 消费记录参数检检查
      * 1.检查设备编号,设备编号必须大于0
@@ -100,7 +135,7 @@ public class CheckBusiness {
         // 检查交易人员标识
         if (bo.getCardNo() <= 0 && bo.getFactoryId() == 0 && bo.getUserNo() <= 0 && StrUtil.isEmpty(bo.getUserNumb())) {
             errorInfo = new ErrorInfo(1, ApiErrorTypeConstants.PARAM_ERROR, "交易人员标识不满足",
-                "必须提供 [CardNo | FactoryId | userNo | userNumb] 中至少1项来标识交易用户");
+                                      "必须提供 [CardNo | FactoryId | userNo | userNumb] 中至少1项来标识交易用户");
             return R.fail(errorInfo);
         }
         return R.ok();
@@ -119,7 +154,7 @@ public class CheckBusiness {
         TermToken token = tokenManager.getTermToken().get(String.valueOf(termNo));
         if (ObjectUtil.isEmpty(token)) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.INVALID_TOKEN, "Token不存在",
-                MessageFormat.format("没有找到机号为[{0}]的设备的Token!", termNo));
+                                      MessageFormat.format("没有找到机号为[{0}]的设备的Token!", termNo));
             return R.fail(errorInfo);
         }
         if (ObjectUtil.isNotEmpty(mac)) {
@@ -174,7 +209,7 @@ public class CheckBusiness {
         RemoteXfTermVo remoteXfTermVo = remoteXfTermService.queryByNo(termNo, tenantId);
         if (ObjectUtil.isEmpty(remoteXfTermVo)) {
             ErrorInfo errorInfo = new ErrorInfo(400, "", "设备不存在",
-                MessageFormat.format("机号为[{0}]的设备不存在,不允许交易", termNo));
+                                                MessageFormat.format("机号为[{0}]的设备不存在,不允许交易", termNo));
 
             return R.fail(errorInfo);
         }
@@ -199,22 +234,22 @@ public class CheckBusiness {
         // 1.消费账户状态验证,验证账户是否已开户、是否冻结、状态是否正常、是否过有效期
         if ("Y".equals(userAccountVo.getFreezeStatus())) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.NOT_FOUND, "账户已被冻结",
-                MessageFormat.format("流水号为[{0}]的账户已被冻结,不允许交易", userAccountVo.getUserNo()));
+                                      MessageFormat.format("流水号为[{0}]的账户已被冻结,不允许交易", userAccountVo.getUserNo()));
             return R.fail(errorInfo);
         }
         if ("N".equals(userAccountVo.getStatus())) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.NOT_FOUND, "账户状态不正常",
-                MessageFormat.format("流水号为[{0}]的账户状态不正常,不允许交易", userAccountVo.getUserNo()));
+                                      MessageFormat.format("流水号为[{0}]的账户状态不正常,不允许交易", userAccountVo.getUserNo()));
             return R.fail(errorInfo);
         }
         if (!ObjectUtil.equal(userAccountVo.getAccountStatus(), UserAccountStatusEnum.IS_OPEN.code().toString())) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.NOT_FOUND, "账户尚未开户",
-                MessageFormat.format("流水号为[{0}]的账户尚未开户,不允许交易", userAccountVo.getUserNo()));
+                                      MessageFormat.format("流水号为[{0}]的账户尚未开户,不允许交易", userAccountVo.getUserNo()));
             return R.fail(errorInfo);
         }
         if (userAccountVo.getLifespan() != null && bo.getConsumeDate().getTime() > userAccountVo.getLifespan().getTime()) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.NOT_FOUND, "账户已过有效期",
-                MessageFormat.format("流水号为[{0}]的账户已过有效期,不允许交易", userAccountVo.getUserNo()));
+                                      MessageFormat.format("流水号为[{0}]的账户已过有效期,不允许交易", userAccountVo.getUserNo()));
             return R.fail(errorInfo);
         }
         // 2.餐类验证
@@ -244,7 +279,7 @@ public class CheckBusiness {
         if (R.isError(result)) {
             return result;
         }
-        result = checkOriginalRecord(bo);
+        result = checkOriginalRecord(bo, userAccountVo);
         if (R.isError(result)) {
             return result;
         }
@@ -281,6 +316,7 @@ public class CheckBusiness {
         bo.setRealName(StrUtil.isEmpty(accountVo.getRealName()) ? "----" : accountVo.getRealName());
         bo.setUserNo(accountVo.getUserNo());
         bo.setUserNumb(accountVo.getUserNumb());
+        bo.setTenantId(accountVo.getTenantId());
     }
 
     /**
@@ -298,13 +334,13 @@ public class CheckBusiness {
         RemoteCardVo cardVo = remoteCardService.queryCardByCardNo(cardNo);
         if (ObjectUtil.isEmpty(cardVo)) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片不存在",
-                MessageFormat.format("流水号为[{0}]的卡片不存在,不允许交易", cardNo));
+                                      MessageFormat.format("流水号为[{0}]的卡片不存在,不允许交易", cardNo));
 
             return R.fail(errorInfo);
         }
         if (!String.valueOf(CardStatusEnum.NORMAL.code()).equals(cardVo.getStatus())) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片状态不正确",
-                MessageFormat.format("流水号为[{0}]的卡片状态不正确,不允许交易", cardNo));
+                                      MessageFormat.format("流水号为[{0}]的卡片状态不正确,不允许交易", cardNo));
 
             return R.fail(errorInfo);
         }
@@ -338,12 +374,12 @@ public class CheckBusiness {
         RemoteCardVo cardVo = remoteCardService.queryCardByFactoryId(factoryId);
         if (ObjectUtil.isEmpty(cardVo)) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片不存在",
-                MessageFormat.format("物理卡号为[{0}]的卡片不存在,不允许交易", factoryId));
+                                      MessageFormat.format("物理卡号为[{0}]的卡片不存在,不允许交易", factoryId));
             return R.fail(errorInfo);
         }
         if (!String.valueOf(CardStatusEnum.NORMAL.code()).equals(cardVo.getStatus())) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片状态不正确",
-                MessageFormat.format("物理卡号为[{0}]的卡片卡片状态不正确,不允许交易", factoryId));
+                                      MessageFormat.format("物理卡号为[{0}]的卡片卡片状态不正确,不允许交易", factoryId));
             return R.fail(errorInfo);
         }
         RemoteUserAccountVo accountVo = remoteUserAccountService.getUserAccountVoById(cardVo.getUserId());
@@ -369,21 +405,21 @@ public class CheckBusiness {
         RemoteUserAccountVo accountVo = remoteUserAccountService.getUserAccountVoByUserNo(userNo);
         if (ObjectUtil.isEmpty(accountVo)) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "人员不存在",
-                MessageFormat.format("流水号为[{0}]的人员不存在,不允许交易", userNo));
+                                      MessageFormat.format("流水号为[{0}]的人员不存在,不允许交易", userNo));
 
             return R.fail(errorInfo);
         }
         RemoteCardVo cardVo = remoteCardService.queryMainCardByUserId(accountVo.getUserId());
         if (ObjectUtil.isEmpty(cardVo)) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片不存在",
-                MessageFormat.format("没有流水号为[{0}]人员的卡片信息,不允许交易", userNo));
+                                      MessageFormat.format("没有流水号为[{0}]人员的卡片信息,不允许交易", userNo));
 
             return R.fail(errorInfo);
         }
         // 实体卡时需要验证卡状态
         if (!String.valueOf(CardStatusEnum.NORMAL.code()).equals(cardVo.getStatus()) && cardVo.getFactoryId() > 0) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片状态不正确",
-                MessageFormat.format("流水号为[{0}]的人员卡片状态不正确,不允许交易", userNo));
+                                      MessageFormat.format("流水号为[{0}]的人员卡片状态不正确,不允许交易", userNo));
 
             return R.fail(errorInfo);
         }
@@ -411,21 +447,21 @@ public class CheckBusiness {
 
         if (ObjectUtil.isEmpty(accountVo)) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "人员不存在",
-                MessageFormat.format("编号为[{0}]的人员不存在,不允许交易", userNumb));
+                                      MessageFormat.format("编号为[{0}]的人员不存在,不允许交易", userNumb));
 
             return R.fail(errorInfo);
         }
         RemoteCardVo cardVo = remoteCardService.queryMainCardByUserId(accountVo.getUserId());
         if (ObjectUtil.isEmpty(cardVo)) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片不存在",
-                MessageFormat.format("没有编号为[{0}]的人员的卡片信息,不允许交易", userNumb));
+                                      MessageFormat.format("没有编号为[{0}]的人员的卡片信息,不允许交易", userNumb));
 
             return R.fail(errorInfo);
         }
         // 实体卡时需要验证卡状态
         if (!String.valueOf(CardStatusEnum.NORMAL.code()).equals(cardVo.getStatus()) && cardVo.getFactoryId() > 0) {
             errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片状态不正确",
-                MessageFormat.format("编号为[{0}]的人员卡片状态不正确,不允许交易", userNumb));
+                                      MessageFormat.format("编号为[{0}]的人员卡片状态不正确,不允许交易", userNumb));
 
             return R.fail(errorInfo);
         }
@@ -439,23 +475,68 @@ public class CheckBusiness {
         return R.ok();
     }
 
-    private R<ErrorInfo> checkOriginalRecord(ConsumptionBo bo) {
+    private R<ErrorInfo> checkOriginalRecord(ConsumptionBo bo, RemoteUserAccountVo userAccountVo) {
         String originalId = RecordIdUtils.getRecordId(bo.getConsumeDate(), bo.getTermNo().shortValue(),
-            bo.getTermRecordId().shortValue(), bo.getUserNo().intValue(), 0);
-        XfConsumeDetailOriginalVo consumeDetailOriginalVo = consumeDetailOriginalService.queryById(originalId);
-        if (ObjectUtil.isEmpty(consumeDetailOriginalVo)) {
-            // TODO 正常应该是进行记录类型和脱机消费的检查,此处先按错误处理
-            log.error("原始消费记录不存在-[{}]", JSONUtil.parse(bo));
-            return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.NOT_FOUND, "原始消费记录不存在",
-                MessageFormat.format("标识为[{0}]的原始消费记录不存在", bo.getRecordId())));
-        }
-        XfConsumeDetailVo consumeDetailVo = consumeDetailService.queryVoByOriginalId(originalId);
+                                                      bo.getTermRecordId().shortValue(), bo.getUserNo().intValue(), 0);
+        //  补偿性措施:防止消费时间错乱问题(在线模式)
+        int recordType = bo.getRecordStatus().intValue();
+        long uniqueRecordId = bo.getRecordId();
+        bo.setOriginalId(originalId);
+        XfConsumeDetailOriginalVo originalVo;
+        if (!((recordType == 106 || recordType == 108 || recordType == 110 || recordType == 111))) {
+            originalVo = consumeDetailOriginalService.queryById(originalId);
+            if (ObjectUtil.isEmpty(originalVo)) {
+                boolean checkFlag = false;
+                if (uniqueRecordId > 0) {
+                    // 根据自增的记录Id查询
+                    originalVo = consumeDetailOriginalService.queryByRecordId(uniqueRecordId);
+                    if (ObjectUtil.isNotEmpty(originalVo)) {
+                        // 如果根据自增Id找到原始记录,以查找的数据重置消费对象的消费日期和原始记录Id
+                        bo.setConsumeDate(originalVo.getConsumeDate());
+                        bo.setOriginalId(originalVo.getOriginalId());
+                        originalId = originalVo.getOriginalId();
+                        checkFlag = true;
+                    }
+                }
+                if (!checkFlag) {
+                    // 根据卡流水号、机号、机器流水号和消费金额查询
+                    originalVo = consumeDetailOriginalService.queryByConsumeMoney(bo.getCardNo(), bo.getTermNo(), bo.getTermRecordId(),
+                                                                                  bo.getConsumeMoney());
+                    if (ObjectUtil.isNotEmpty(originalVo)) {
+                        bo.setConsumeDate(originalVo.getConsumeDate());
+                        bo.setRecordId(originalVo.getRecordId());
+                        bo.setOriginalId(originalVo.getOriginalId());
+                        // originalId = originalVo.getOriginalId();
+                    } else {
+                        bo.setRecordStatus(108L);
+                        // 判断脱机消费
+                        R<ErrorInfo> result = doOfflineRecord(bo, originalId, userAccountVo);
+                        if (R.isError(result)) {
+                            String msg = MessageFormat.format("处理记录号为[{0}]的脱机消费数据失败", bo.getRecordId());
+                            return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.RECORD_IS_EXISTS, "处理脱机消费数据失败", msg));
+                        }
+                        // 重置这项数据
+                        bo.setConsumeDate(originalVo.getConsumeDate());
+                        bo.setRecordId(originalVo.getRecordId());
+                        bo.setOriginalId(originalVo.getOriginalId());
+                        // originalId = originalVo.getOriginalId();
+                    }
+                }
+            }
+        } else {
+            R<ErrorInfo> result = doOfflineRecord(bo, originalId, userAccountVo);
+            if (R.isError(result)) {
+                return result;
+            }
+        }
+        // XfConsumeDetailVo consumeDetailVo = consumeDetailService.queryVoByOriginalId(originalId);
+        XfConsumeDetailVo consumeDetailVo = consumeDetailService.queryVoByOriginalId(bo.getOriginalId());
         if (ObjectUtil.isNotEmpty(consumeDetailVo)) {
             // 认为是重复上传,不再入账
             return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.RECORD_IS_EXISTS, "原始消费记录已处理",
-                MessageFormat.format("标识为[{0}]的原始消费记录已处理", bo.getRecordId())));
+                                        MessageFormat.format("标识为[{0}]的原始消费记录已处理", bo.getRecordId())));
         }
-        bo.setOriginalId(originalId);
+        // bo.setOriginalId(originalId);
         return R.ok();
     }
 
@@ -468,7 +549,7 @@ public class CheckBusiness {
         Long userId = userAccountVo.getUserId();
         // 可能会在处理过程中更改实际的消费金额,因此先取出来
         BigDecimal consumeMoney = bo.getConsumeMoney();
-        //扣费的过程金额
+        // 扣费的过程金额
         BigDecimal doMoney = bo.getConsumeMoney();
         // 计算后实际需要扣费的钱包,会小于或等于指定的扣费钱包数
         List<PtBagVo> doBagVos = new ArrayList<>();
@@ -483,7 +564,7 @@ public class CheckBusiness {
         // 如果扣费钱包总余额<消费金额,则不允许消费
         if (consumeMoney.compareTo(totalBalance) > 0) {
             return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.CONSUME_CHECK_FAIL, "钱包余额不足",
-                MessageFormat.format("余额不足,总余额[{0}],消费金额[{1}]", totalBalance, consumeMoney)));
+                                        MessageFormat.format("余额不足,总余额[{0}],消费金额[{1}]", totalBalance, consumeMoney)));
         }
         for (String bagCode : bagCodes) {
             // 1.查询对应的钱包
@@ -495,17 +576,17 @@ public class CheckBusiness {
             // 2.比较扣费金额
             BigDecimal balance = bagVo.getBalance();
             log.info("钱包代码:{},钱包余额:{},消费金额:{}", bagCode, balance, doMoney);
-            if (doMoney.compareTo(BigDecimal.ZERO)==0) {
-                //如果是消费0元,设置为第一个钱包扣费
+            if (doMoney.compareTo(BigDecimal.ZERO) == 0) {
+                // 如果是消费0元,设置为第一个钱包扣费
                 bagVo.setReceiptMoney(doMoney);
                 bagVo.setBalance(balance.subtract(doMoney));
                 sb.append(BagNameEnum.getMessage(Integer.parseInt(bagCode)));
                 doBagVos.add(bagVo);
                 break;
             } else {
-                //如果消费金额>0,则可能会需要多钱包扣费
+                // 如果消费金额>0,则可能会需要多钱包扣费
                 if (balance.compareTo(BigDecimal.ZERO) > 0) {
-                    //钱包有余额则扣费
+                    // 钱包有余额则扣费
                     if (balance.compareTo(doMoney) >= 0) {
                         // 如果钱包金额>=扣费金额,设置扣费结果并中断循环
                         bagVo.setReceiptMoney(doMoney);
@@ -575,7 +656,7 @@ public class CheckBusiness {
 
         // 设备消费间隔验证
         if ((currentLocalDt.toEpochSecond(ZoneOffset.of("+8"))
-            - lastPayLocalDt.toEpochSecond(ZoneOffset.of("+8"))) / 60 < termSwipeInterval && termSwipeInterval > 0) {// 消费间隔
+                 - lastPayLocalDt.toEpochSecond(ZoneOffset.of("+8"))) / 60 < termSwipeInterval && termSwipeInterval > 0) {// 消费间隔
             return R.fail(new ErrorInfo(400, TradeStatusEnum.TimeInterval.toString(), "超过设备单次限额", TradeStatusEnum.TimeInterval.getName()));
         }
         // 设备单次限额验证
@@ -729,28 +810,28 @@ public class CheckBusiness {
                 if (ObjectUtil.equals(mealType, "1")) {
                     if (cardLimitedVo.getMealCount().compareTo(oneCount) >= 0) {
                         return R.fail(new ErrorInfo(400, TradeStatusEnum.MealLimitTimes.toString(), "卡类早餐限制次数",
-                            TradeStatusEnum.MealLimitTimes.getName()));
+                                                    TradeStatusEnum.MealLimitTimes.getName()));
                     }
                 }
                 // 午餐限次
                 if (ObjectUtil.equals(mealType, "2")) {
                     if (cardLimitedVo.getMealCount().compareTo(twoCount) >= 0) {
                         return R.fail(new ErrorInfo(400, TradeStatusEnum.MealLimitTimes.toString(), "卡类午餐限制次数",
-                            TradeStatusEnum.MealLimitTimes.getName()));
+                                                    TradeStatusEnum.MealLimitTimes.getName()));
                     }
                 }
                 // 晚餐限次
                 if (ObjectUtil.equals(mealType, "3")) {
                     if (cardLimitedVo.getMealCount().compareTo(threeCount) >= 0) {
                         return R.fail(new ErrorInfo(400, TradeStatusEnum.MealLimitTimes.toString(), "卡类晚餐限制次数",
-                            TradeStatusEnum.MealLimitTimes.getName()));
+                                                    TradeStatusEnum.MealLimitTimes.getName()));
                     }
                 }
                 // 宵夜限次
                 if (ObjectUtil.equals(mealType, "4")) {
                     if (cardLimitedVo.getMealCount().compareTo(fourCount) >= 0) {
                         return R.fail(new ErrorInfo(400, TradeStatusEnum.MealLimitTimes.toString(), "卡类夜宵限制次数",
-                            TradeStatusEnum.MealLimitTimes.getName()));
+                                                    TradeStatusEnum.MealLimitTimes.getName()));
                     }
                 }
             }
@@ -803,7 +884,7 @@ public class CheckBusiness {
         BigDecimal fourMoney = remoteQuotaVo.getFourMoney();
 
         if (dayMoney.compareTo(BigDecimal.ZERO) > 0 || oneMoney.compareTo(BigDecimal.ZERO) > 0 || twoMoney.compareTo(BigDecimal.ZERO) > 0
-            || threeMoney.compareTo(BigDecimal.ZERO) > 0 || fourMoney.compareTo(BigDecimal.ZERO) > 0) {
+                || threeMoney.compareTo(BigDecimal.ZERO) > 0 || fourMoney.compareTo(BigDecimal.ZERO) > 0) {
             if (dayMoney.compareTo(BigDecimal.ZERO) > 0) {
                 BigDecimal mealQuotaMoney = cardLimitedVo.getMealMoney();
                 // 日限额
@@ -815,28 +896,28 @@ public class CheckBusiness {
                 if (ObjectUtil.equals(mealType, "1") && oneMoney.compareTo(BigDecimal.ZERO) > 0) {
                     if (oneMoney.compareTo(mealQuotaMoney.add(disCountMoney)) < 0) {
                         return R.fail(new ErrorInfo(400, TradeStatusEnum.MealLimitMoney.toString(), "卡类早餐限制额度",
-                            TradeStatusEnum.MealLimitMoney.getName()));
+                                                    TradeStatusEnum.MealLimitMoney.getName()));
                     }
                 }
                 // 午餐限额
                 if (ObjectUtil.equals(mealType, "2") && twoMoney.compareTo(BigDecimal.ZERO) > 0) {
                     if (twoMoney.compareTo(mealQuotaMoney.add(disCountMoney)) < 0) {
                         return R.fail(new ErrorInfo(400, TradeStatusEnum.MealLimitMoney.toString(), "卡类午餐限制额度",
-                            TradeStatusEnum.MealLimitMoney.getName()));
+                                                    TradeStatusEnum.MealLimitMoney.getName()));
                     }
                 }
                 // 晚餐限额
                 if (ObjectUtil.equals(mealType, "3") && threeMoney.compareTo(BigDecimal.ZERO) > 0) {
                     if (threeMoney.compareTo(mealQuotaMoney.add(disCountMoney)) < 0) {
                         return R.fail(new ErrorInfo(400, TradeStatusEnum.MealLimitMoney.toString(), "卡类晚餐限制额度",
-                            TradeStatusEnum.MealLimitMoney.getName()));
+                                                    TradeStatusEnum.MealLimitMoney.getName()));
                     }
                 }
                 // 宵夜限额
                 if (ObjectUtil.equals(mealType, "4") && threeMoney.compareTo(BigDecimal.ZERO) > 0) {
                     if (fourMoney.compareTo(mealQuotaMoney.add(disCountMoney)) < 0) {
                         return R.fail(new ErrorInfo(400, TradeStatusEnum.MealLimitMoney.toString(), "卡类夜宵限制额度",
-                            TradeStatusEnum.MealLimitMoney.getName()));
+                                                    TradeStatusEnum.MealLimitMoney.getName()));
                     }
                 }
             }
@@ -865,7 +946,8 @@ public class CheckBusiness {
             // 当前设备没有设置折扣
             return R.ok();
         }
-        RemoteDiscountVo remoteDiscountVo = remoteDisCountService.queryDisCountByCardType(userCardVo.getCardType().intValue(), mealTypeVo.getTypeId());
+        RemoteDiscountVo remoteDiscountVo = remoteDisCountService.queryDisCountByCardType(userCardVo.getCardType().intValue(),
+                                                                                          mealTypeVo.getTypeId());
         if (ObjectUtil.isEmpty(remoteDiscountVo)) {
             // 当前卡类没有折扣信息
             return R.ok();
@@ -941,7 +1023,8 @@ public class CheckBusiness {
                 // 当前设备也有折扣
                 if (ObjectUtil.isNotEmpty(disCountTermId) && disCountTermId > 0) {
                     // 查询卡类的折扣信息
-                    RemoteDiscountVo remoteDisCountVo = remoteDisCountService.queryDisCountByCardType(userCardVo.getCardType().intValue(), mealTypeVo.getTypeId());
+                    RemoteDiscountVo remoteDisCountVo = remoteDisCountService.queryDisCountByCardType(userCardVo.getCardType().intValue(),
+                                                                                                      mealTypeVo.getTypeId());
                     // 有卡类的折扣信息并已启用
                     if (ObjectUtil.isNotEmpty(remoteDisCountVo) && ObjectUtil.equals(remoteDisCountVo.getStatus(), "1")) {
                         // 更新卡类的折扣信息
@@ -993,40 +1076,84 @@ public class CheckBusiness {
     }
 
     /**
-     * 计算折扣金额
+     * 处理脱机消费记录.
      *
-     * @param cardLimitedVo    卡片限制信息
-     * @param bo               消费记录
-     * @param remoteDiscountVo 折扣信息
-     * @return 折扣金额
+     * @param bo            消费记录对象
+     * @param originalId    原始记录Id
+     * @param userAccountVo 用户账户信息
+     * @return a Result object containing ErrorInfo if there is an error, otherwise null
      */
-    @NotNull
-    private static BigDecimal getDisCountMoney(XfCardLimitedVo cardLimitedVo, ConsumptionBo bo, RemoteDiscountVo remoteDiscountVo) {
-        int rateCount = remoteDiscountVo.getRateType().equals(
-            "1") ? cardLimitedVo.getDayDiscountCount().intValue() : cardLimitedVo.getMealDiscountCount().intValue();
-        BigDecimal disCountMoney;
-        BigDecimal consumeMoney = bo.getConsumeMoney();
-        // 折扣率1
-        BigDecimal oneRate = remoteDiscountVo.getOneRate();
-        // 折扣率2
-        BigDecimal twoRate = remoteDiscountVo.getTwoRate();
-        // 折扣率3
-        BigDecimal threeRate = remoteDiscountVo.getThreeRate();
-        // 折扣率4
-        BigDecimal fourRate = remoteDiscountVo.getFourRate();
-        if (rateCount == 0) {
-            disCountMoney = consumeMoney.multiply(oneRate.divide(new BigDecimal("100.0"), 2, RoundingMode.HALF_UP)).setScale(2, RoundingMode.HALF_UP);
-        } else if (rateCount == 1) {// 第二次
-            disCountMoney = consumeMoney.multiply(twoRate.divide(new BigDecimal("100.0"), 2, RoundingMode.HALF_UP)).setScale(2, RoundingMode.HALF_UP);
-        } else if (rateCount == 2) {// 第三次
-            disCountMoney = consumeMoney.multiply(threeRate.divide(new BigDecimal("100.0"), 2, RoundingMode.HALF_UP)).setScale(2,
-                RoundingMode.HALF_UP);
-        } else { // 第四次及以上
-            disCountMoney = consumeMoney.multiply(fourRate.divide(new BigDecimal("100.0"), 2, RoundingMode.HALF_UP)).setScale(2,
-                RoundingMode.HALF_UP);
+    private R<ErrorInfo> doOfflineRecord(ConsumptionBo bo, String originalId, RemoteUserAccountVo userAccountVo) {
+        try {
+            XfConsumeDetailOriginalVo vo;
+            Date currentDate = DateUtil.date();
+            int recordStatus = bo.getRecordStatus().intValue();
+            Long RecordId = bo.getRecordId();
+            if (RecordId == 0 && (recordStatus == 106 || recordStatus == 108 || recordStatus == 110 || recordStatus == 111)) {
+                if (recordStatus == 110 || recordStatus == 111) {
+                    vo = consumeDetailOriginalService.queryByConsumeMoney(bo.getCardNo(), bo.getTermNo(), bo.getTermRecordId(),
+                                                                          bo.getOperatorMoney());
+                    if (ObjectUtil.isNotEmpty(vo)) {
+                        resetBoByOfflineResult(bo, vo);
+                    }
+                } else {
+                    vo = consumeDetailOriginalService.queryByConsumeDate(bo.getCardNo(), bo.getTermNo(), bo.getTermRecordId(), bo.getConsumeDate());
+                    if (ObjectUtil.isNotEmpty(vo)) {
+                        resetBoByOfflineResult(bo, vo);
+                    } else {
+                        vo = consumeDetailOriginalService.queryById(originalId);
+                        resetBoByOfflineResult(bo, vo);
+                    }
+                }
+                if (bo.getRecordId() == 0) {
+                    if (bo.getConsumeDate().getTime() > currentDate.getTime()) {
+                        bo.setConsumeDate(currentDate);
+                        originalId = RecordIdUtils.getRecordId(bo.getConsumeDate(), bo.getTermNo().shortValue(),
+                                                               bo.getTermRecordId().shortValue(), bo.getUserNo().intValue(), 0);
+                        bo.setOriginalId(originalId);
+                    }
+                    vo = new XfConsumeDetailOriginalVo();
+                    R<ErrorInfo> result = baseBusiness.createOriginalOrder(bo, userAccountVo, vo);
+                    if (R.isError(result)) {
+                        return result;
+                    }
+                    bo.setRecordId(vo.getRecordId());
+                    bo.setOriginalId(vo.getOriginalId());
+                }
+            } else {
+                consumeDetailOriginalService.updateRecordStatusByOrginId(recordStatus, originalId);
+            }
+            return R.ok();
+        } catch (Exception e) {
+            log.error("[处理脱机记录错误]-[{}]-[{}]", e.getMessage(), Arrays.toString(e.getStackTrace()));
+            return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.RECORD_IS_EXISTS, "处理脱机记录错误",
+                                        MessageFormat.format("错误消息:{0}", e.getMessage())));
         }
-        return disCountMoney;
     }
 
-
+    /**
+     * Resets the ConsumptionBo based on the offline result from XfConsumeDetailOriginalVo.
+     * If the provided vo is not null, it updates the bo's recordId and originalId with those from the vo.
+     * Additionally, if the dataFlag in the vo is 0, indicating a specific condition related to online transactions,
+     * it adjusts the recordStatus of the bo by adding 256 (to mark it as an online transaction) and updates this
+     * new status in the database for the given originalId.
+     *
+     * @param bo The ConsumptionBo object to be reset.
+     * @param vo The XfConsumeDetailOriginalVo containing the offline result information.
+     */
+    private void resetBoByOfflineResult(ConsumptionBo bo, XfConsumeDetailOriginalVo vo) {
+        if (ObjectUtil.isNotEmpty(vo)) {
+            bo.setRecordId(vo.getRecordId());
+            bo.setOriginalId(vo.getOriginalId());
+            if (vo.getDataFlag() == 0) {
+                /*
+                  如果库中原来的原始记录的dataFlag为零,则说明是在线交易时收到一次数据,然而应答之后又没有收到上传的记录,设备却写入了
+                  此种记录将采集时得到的DataFlag加上高字节的在线标识256后,得到正确DataFlag,并将之更新到库中
+                 */
+                Long recordStatus = bo.getRecordStatus() + 256;
+                bo.setRecordStatus(recordStatus);
+                consumeDetailOriginalService.updateRecordStatusByOrginId(recordStatus.intValue(), bo.getOriginalId());
+            }
+        }
+    }
 }

+ 16 - 17
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/controller/v1/ConsumeController.java

@@ -25,9 +25,7 @@ import org.springframework.http.ResponseEntity;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
-import java.math.BigDecimal;
 import java.util.Objects;
-import java.util.concurrent.ScheduledExecutorService;
 
 /**
  * name: ConsumeController
@@ -47,7 +45,6 @@ import java.util.concurrent.ScheduledExecutorService;
 @RequestMapping("/v1/Consumes")
 public class ConsumeController {
     private final RecordConvertStrategyContent recordConvertStrategy;
-    private final ScheduledExecutorService scheduledExecutorService;
     private final ConsumeBusiness consumeBusiness;
     private final BaseBusiness baseBusiness;
     private final DefaultConfig defaultConfig;
@@ -65,7 +62,7 @@ public class ConsumeController {
     public Object consumeOriginalVirtualCard(@RequestBody Object record, @RequestParam(name = "mac", required = false) java.lang.String mac,
                                              @RequestParam(name = "qrcode") java.lang.String qrcode,
                                              @RequestParam(name = "xfPwd", required = false) java.lang.String xfPwd) {
-        log.info("[二维码请求消费]-[{}]",JSONUtil.parseObj(record));
+        log.info("[二维码请求消费]-[{}]", JSONUtil.parseObj(record));
         JSONObject yc = JSONUtil.parseObj(record);
         long userNo = ObjectUtil.isEmpty(yc.get("employeeID")) ? 0L : Long.parseLong(yc.get("employeeID").toString());
         Object redisQrCode = RedisUtils.getCacheMapValue(CacheNames.SCHOOL_CODE, Long.toString(userNo));
@@ -89,8 +86,9 @@ public class ConsumeController {
      * @return 请求消费结果
      */
     @PostMapping("/ConsumeOriginal")
-    public Object consumeOriginal(@RequestBody Object record, @RequestParam(name = "mac", required = false) java.lang.String mac,@RequestParam(name = "xfPwd", required = false) java.lang.String xfPwd) {
-        log.info("[刷卡请求消费]-[{}]",JSONUtil.parseObj(record));
+    public Object consumeOriginal(@RequestBody Object record, @RequestParam(name = "mac", required = false) java.lang.String mac,
+                                  @RequestParam(name = "xfPwd", required = false) java.lang.String xfPwd) {
+        log.info("[刷卡请求消费]-[{}]", JSONUtil.parseObj(record));
         return doRecordData(record, "requestConsume", mac, xfPwd);
     }
 
@@ -103,12 +101,11 @@ public class ConsumeController {
      */
     @PostMapping("/Consume")
     public Object uploadRecord(@RequestBody Object record, @RequestParam(name = "mac", required = false) java.lang.String mac) {
-        log.info("[上传交易记录]-[{}]",JSONUtil.parseObj(record));
-        String consumeMoney = JSONUtil.parseObj(record).get("consumeValue").toString();
+        log.info("[消费机上传消费记录]-[{}]", JSONUtil.parseObj(record));
         Object object = doRecordData(record, "uploadRecord", mac, "");
         JSONObject jsonObject = JSONUtil.parseObj(object);
 
-        sendConsumeToCloud(jsonObject, record,consumeMoney);
+        sendConsumeToCloud(jsonObject, record);
 
         return object;
     }
@@ -140,12 +137,14 @@ public class ConsumeController {
 
     /**
      * 原始消费对账,将有原始消费记录但没有消费明细的消费记录写入消费明细
+     *
      * @return 对账结果
      */
     @PostMapping("/consume/original/reconciliation/{consumeDate}")
-    public R<ErrorInfo> originalReconciliation(@PathVariable("consumeDate") java.lang.String consumeDate){
+    public R<ErrorInfo> originalReconciliation(@PathVariable("consumeDate") java.lang.String consumeDate) {
         return consumeBusiness.originalReconciliation(consumeDate);
     }
+
     /**
      * 消费业务初步处理
      *
@@ -166,9 +165,10 @@ public class ConsumeController {
 
         R<ErrorInfo> errorInfo;
         if (Objects.equals(type, "requestConsume")) {
-            if (ObjectUtil.equals(bo.getCreditType(), CreditTypeEnum.TERM_CONSUME.code())) {
-                // 如果是消费机请求消费,将消费时间设置为当前时间,以防消费时时钟不对造成实际消费时间不正确
-                //bo.setConsumeDate(DateUtil.date());
+            int recordType = bo.getRecordStatus().intValue();
+            if (ObjectUtil.equals(bo.getCreditType(), CreditTypeEnum.TERM_CONSUME.code()) && recordType == 364) {
+                // 如果是消费机请求消费,而且是正常消费记录将消费时间设置为当前时间,以防消费时时钟不对造成实际消费时间不正确
+                bo.setConsumeDate(DateUtil.date());
             }
             errorInfo = consumeBusiness.createOrder(bo, mac, xfPwd);
         } else if (Objects.equals(type, "uploadRecord")) {
@@ -186,13 +186,12 @@ public class ConsumeController {
         return recordConvertStrategy.reConvert(bo, "YC");
     }
 
-    private void sendConsumeToCloud(JSONObject jsonObject, Object record,String consumeMoney) {
-        if(ObjectUtil.isEmpty(jsonObject.get("body"))){
-            //发送消息
+    private void sendConsumeToCloud(JSONObject jsonObject, Object record) {
+        if (ObjectUtil.isEmpty(jsonObject.get("body"))) {
+            // 发送消息
             if (ObjectUtil.equals(defaultConfig.getLocationFlag(), DefaultConstants.LOCAL_FLAG)) {
                 ConsumptionBo bo = recordConvertStrategy.convert(record, "YC");
                 bo.setConsumeDate(jsonObject.getDate("consumeDate"));
-                bo.setConsumeMoney(new BigDecimal(consumeMoney));
                 ThreadUtil.execAsync(() -> baseBusiness.sendCloudConsume(bo));
             }
         }

+ 1 - 1
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/convert/strategy/impl/YcRecordConvertStrategyImpl.java

@@ -50,7 +50,7 @@ public class YcRecordConvertStrategyImpl implements IRecordConvertStrategy {
             }
             consumptionBo.setTermNo(ObjectUtil.isEmpty(yc.get("termID")) ? 0 : Long.parseLong(yc.get("termID").toString()));
             consumptionBo.setTermRecordId(ObjectUtil.isEmpty(yc.get("termRecordID")) ? 0 : Long.parseLong(yc.get("termRecordID").toString()));
-            consumptionBo.setRecordId(ObjectUtil.isEmpty(yc.get("oriRecordID")) ? null : Long.parseLong(yc.get("oriRecordID").toString()));
+            consumptionBo.setRecordId(ObjectUtil.isEmpty(yc.get("oriRecordID")) ? 0 : Long.parseLong(yc.get("oriRecordID").toString()));
             consumptionBo.setRecordStatus(ObjectUtil.isEmpty(yc.get("posRecordState")) ? 0 : Long.parseLong(yc.get("posRecordState").toString()));
             consumptionBo.setOperatorId(ObjectUtil.isEmpty(yc.get("operatorID")) ? 0 : Long.parseLong(yc.get("operatorID").toString()));
             //StatusFlag

+ 40 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/IConsumeDetailOriginalService.java

@@ -4,6 +4,7 @@ import org.dromara.server.common.domain.consume.bo.ConsumptionBo;
 import org.dromara.server.consume.domain.bo.XfConsumeDetailOriginalBo;
 import org.dromara.server.consume.domain.vo.XfConsumeDetailOriginalVo;
 
+import java.math.BigDecimal;
 import java.util.Date;
 import java.util.List;
 
@@ -53,4 +54,43 @@ public interface IConsumeDetailOriginalService {
      * @return 消费记录
      */
     List<ConsumptionBo> selectReconciliationData(Date consumeDate);
+
+    /**
+     * 根据记录ID查询原始消费记录
+     *
+     * @param recordId 记录ID
+     * @return 原始消费记录视图对象
+     */
+    XfConsumeDetailOriginalVo queryByRecordId(Long recordId);
+
+    /**
+     * 根据卡号、设备机号和消费金额查询原始消费记录
+     *
+     * @param cardNo       卡流水号
+     * @param termNo       设备机号
+     * @param termRecordId 机器流水号
+     * @param consumeMoney 消费金额
+     * @return 原始消费记录视图对象
+     */
+    XfConsumeDetailOriginalVo queryByConsumeMoney(Long cardNo, Long termNo, Long termRecordId, BigDecimal consumeMoney);
+
+    /**
+     * 根据原始ID更新记录状态。
+     *
+     * @param dataFlag 要更新的记录状态
+     * @param originId 原始ID
+     * @return 如果更新成功返回true,否则返回false
+     */
+    Integer updateRecordStatusByOrginId(Integer dataFlag, String originId);
+
+    /**
+     * 根据卡号、设备机号、机器流水号和消费日期查询原始消费记录。
+     *
+     * @param cardNo       卡流水号
+     * @param termNo       设备机号
+     * @param termRecordId 机器流水号
+     * @param consumeDate  消费日期
+     * @return 原始消费记录视图对象,如果未找到匹配的记录则返回null
+     */
+    XfConsumeDetailOriginalVo queryByConsumeDate(Long cardNo, Long termNo, Long termRecordId, Date consumeDate);
 }

+ 64 - 1
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/impl/ConsumeDetailOriginalServiceImpl.java

@@ -1,6 +1,7 @@
 package org.dromara.server.consume.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.utils.MapstructUtils;
@@ -13,6 +14,7 @@ import org.dromara.server.consume.mapper.ConsumeDetailOriginalMapper;
 import org.dromara.server.consume.service.IConsumeDetailOriginalService;
 import org.springframework.stereotype.Service;
 
+import java.math.BigDecimal;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
@@ -36,7 +38,7 @@ public class ConsumeDetailOriginalServiceImpl implements IConsumeDetailOriginalS
      * @return 原始消费记录
      */
     @Override
-    public XfConsumeDetailOriginalVo queryById(String originalId){
+    public XfConsumeDetailOriginalVo queryById(String originalId) {
         return baseMapper.selectVoById(originalId);
     }
 
@@ -125,4 +127,65 @@ public class ConsumeDetailOriginalServiceImpl implements IConsumeDetailOriginalS
     public List<ConsumptionBo> selectReconciliationData(Date consumeDate) {
         return baseMapper.selectReconciliationData(consumeDate);
     }
+
+    /**
+     * 根据记录ID查询原始消费记录
+     *
+     * @param recordId 记录ID
+     * @return 原始消费记录视图对象
+     */
+    @Override
+    public XfConsumeDetailOriginalVo queryByRecordId(Long recordId) {
+        return baseMapper.selectVoOne(new LambdaQueryWrapper<XfConsumeDetailOriginal>().eq(XfConsumeDetailOriginal::getRecordId, recordId));
+    }
+
+    /**
+     * 根据卡号、设备机号和消费金额查询原始消费记录
+     *
+     * @param cardNo       卡流水号
+     * @param termNo       设备机号
+     * @param termRecordId 机器流水号
+     * @param consumeMoney 消费金额
+     * @return 原始消费记录视图对象
+     */
+    @Override
+    public XfConsumeDetailOriginalVo queryByConsumeMoney(Long cardNo, Long termNo, Long termRecordId, BigDecimal consumeMoney) {
+        return baseMapper.selectVoOne(new LambdaQueryWrapper<XfConsumeDetailOriginal>().eq(XfConsumeDetailOriginal::getCardNo, cardNo)
+                                          .eq(XfConsumeDetailOriginal::getTermNo, termNo)
+                                          .eq(XfConsumeDetailOriginal::getTermRecordId, termRecordId)
+                                          .eq(XfConsumeDetailOriginal::getConsumeMoney, consumeMoney));
+    }
+
+    /**
+     * 根据原始ID更新记录状态。
+     *
+     * @param dataFlag 要更新的记录状态
+     * @param originId 原始ID
+     * @return 如果更新成功返回true,否则返回false
+     */
+    @Override
+    public Integer updateRecordStatusByOrginId(Integer dataFlag, String originId) {
+        LambdaUpdateWrapper<XfConsumeDetailOriginal> luw = new LambdaUpdateWrapper<XfConsumeDetailOriginal>()
+                                                               .set(XfConsumeDetailOriginal::getDataFlag, dataFlag)
+                                                               .eq(XfConsumeDetailOriginal::getOriginalId, originId)
+                                                               .eq(XfConsumeDetailOriginal::getDataFlag, 0);
+        return baseMapper.update(null, luw);
+    }
+
+    /**
+     * 根据卡号、设备机号、机器流水号和消费日期查询原始消费记录。
+     *
+     * @param cardNo       卡流水号
+     * @param termNo       设备机号
+     * @param termRecordId 机器流水号
+     * @param consumeDate  消费日期
+     * @return 原始消费记录视图对象,如果未找到匹配的记录则返回null
+     */
+    @Override
+    public XfConsumeDetailOriginalVo queryByConsumeDate(Long cardNo, Long termNo, Long termRecordId, Date consumeDate) {
+        return baseMapper.selectVoOne(new LambdaQueryWrapper<XfConsumeDetailOriginal>().eq(XfConsumeDetailOriginal::getCardNo, cardNo)
+                                          .eq(XfConsumeDetailOriginal::getTermNo, termNo)
+                                          .eq(XfConsumeDetailOriginal::getTermRecordId, termRecordId)
+                                          .eq(XfConsumeDetailOriginal::getConsumeDate, consumeDate));
+    }
 }