Преглед на файлове

feature: 消费对接
1.设备卡片餐限次验证

luo.yibo@datuai.com преди 1 година
родител
ревизия
7afed05690

+ 13 - 1
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/RemoteCardService.java

@@ -1,6 +1,5 @@
 package org.dromara.backstage.api;
 
-import org.dromara.backstage.api.domain.bo.RemoteCardBo;
 import org.dromara.backstage.api.domain.vo.RemoteCardVo;
 
 /**
@@ -43,4 +42,17 @@ public interface RemoteCardService {
      * @return 主卡信息
      */
     RemoteCardVo queryMainCardByUserId(Long userId);
+
+    /**
+     * 初始化卡片的餐消费信息
+     * @param cardNo 卡流水号
+     * @param mealType 餐类
+     */
+    void initCardMealData(Long cardNo, String mealType);
+
+    /**
+     * 初始化卡片日消费数据
+     * @param cardNo 卡流水号
+     */
+    void initCardDayData(Long cardNo);
 }

+ 30 - 3
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/cardCenter/dubbo/RemoteCardServiceImpl.java

@@ -24,6 +24,7 @@ import org.springframework.stereotype.Service;
 @DubboService
 public class RemoteCardServiceImpl implements RemoteCardService {
     private final IPtCardService cardService;
+
     @Override
     public String selectAccountCardByIds(String userIds) {
         return cardService.selectAccountCardByIds(userIds);
@@ -32,26 +33,52 @@ public class RemoteCardServiceImpl implements RemoteCardService {
     @Override
     public RemoteCardVo queryCardByCardNo(Long cardNo) {
         PtCardVo vo = cardService.queryCardByCardNo(cardNo);
-        return MapstructUtils.convert(vo,RemoteCardVo.class);
+        return MapstructUtils.convert(vo, RemoteCardVo.class);
     }
+
     /**
      * 根据物理卡号获取卡片信息
+     *
      * @param factoryId 物理卡号
      * @return 卡片信息
      */
     @Override
     public RemoteCardVo queryCardByFactoryId(Long factoryId) {
         PtCardVo vo = cardService.queryCardByFactoryId(factoryId);
-        return MapstructUtils.convert(vo,RemoteCardVo.class);
+        return MapstructUtils.convert(vo, RemoteCardVo.class);
     }
+
     /**
      * 根据人员Id查询主卡信息
+     *
      * @param userId 人员Id
      * @return 主卡信息
      */
     @Override
     public RemoteCardVo queryMainCardByUserId(Long userId) {
         PtCardVo vo = cardService.queryMainCardByUserId(userId);
-        return MapstructUtils.convert(vo,RemoteCardVo.class);
+        return MapstructUtils.convert(vo, RemoteCardVo.class);
+    }
+
+    /**
+     * 初始化卡片的餐消费信息
+     *
+     * @param cardNo   卡流水号
+     * @param mealType 餐类
+     */
+    @Override
+    public void initCardMealData(Long cardNo, String mealType) {
+        cardService.initCardMealData(cardNo, mealType);
+    }
+
+    /**
+     * 初始化卡片日消费数据
+     *
+     * @param cardNo 卡流水号
+     */
+    @Override
+    public void initCardDayData(Long cardNo) {
+        cardService.initCardDayData(cardNo);
     }
 }
+

+ 13 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/cardCenter/service/IPtCardService.java

@@ -173,4 +173,17 @@ public interface IPtCardService {
      * @return 回收结果
      */
     Boolean recycleCardByBo(PtCardBo bo);
+
+    /**
+     * 初始化卡片的餐消费信息
+     * @param cardNo 卡流水号
+     * @param mealType 餐类
+     */
+    void initCardMealData(Long cardNo, String mealType);
+
+    /**
+     * 初始化卡片日消费数据
+     * @param cardNo 卡流水号
+     */
+    void initCardDayData(Long cardNo);
 }

+ 17 - 1
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/cardCenter/service/impl/PtCardServiceImpl.java

@@ -265,7 +265,6 @@ public class PtCardServiceImpl implements IPtCardService {
             .eq(PtCard::getCardId, cardId)) > 0;
     }
 
-
     /**
      * 计算管理费
      * 考虑账户可能没有卡数据就充值,此时只能从账户中获取账户的卡类,从而得到管理费的计算方式
@@ -449,4 +448,21 @@ public class PtCardServiceImpl implements IPtCardService {
 
         return baseMapper.update(null,lpw)>0;
     }
+    /**
+     * 初始化卡片的餐消费信息
+     * @param cardNo 卡流水号
+     * @param mealType 餐类
+     */
+    @Override
+    public void initCardMealData(Long cardNo, String mealType) {
+
+    }
+    /**
+     * 初始化卡片日消费数据
+     * @param cardNo 卡流水号
+     */
+    @Override
+    public void initCardDayData(Long cardNo) {
+
+    }
 }

+ 75 - 21
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/CheckBusiness.java

@@ -22,12 +22,14 @@ import org.dromara.common.core.enums.TradeStatusEnum;
 import org.dromara.common.core.enums.UserAccountStatusEnum;
 import org.dromara.common.core.utils.RecordIdUtils;
 import org.dromara.server.common.domain.consume.bo.ConsumptionBo;
+import org.dromara.server.consume.cache.TokenManager;
 import org.dromara.server.consume.domain.vo.PtBagVo;
 import org.dromara.server.consume.domain.vo.XfConsumeDetailOriginalVo;
 import org.dromara.server.consume.domain.vo.XfConsumeDetailVo;
 import org.dromara.server.consume.domain.vo.XfTermVo;
-import org.dromara.server.consume.service.IPtBagService;
+import org.dromara.server.consume.domain.vo.yc.TermToken;
 import org.dromara.server.consume.service.IConsumeDetailOriginalService;
+import org.dromara.server.consume.service.IPtBagService;
 import org.dromara.server.consume.service.IXfConsumeDetailService;
 import org.dromara.server.consume.service.IXfTermService;
 import org.jetbrains.annotations.NotNull;
@@ -35,6 +37,8 @@ import org.springframework.stereotype.Service;
 
 import java.math.BigDecimal;
 import java.text.MessageFormat;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -66,6 +70,7 @@ public class CheckBusiness {
     private final IConsumeDetailOriginalService consumeDetailOriginalService;
     private final IXfConsumeDetailService consumeDetailService;
     private final IPtBagService bagService;
+    private final TokenManager tokenManager;
 
     /**
      * 消费记录参数检检查
@@ -79,41 +84,52 @@ public class CheckBusiness {
         ErrorInfo errorInfo;
         // 检查设备机号
         if (ObjectUtil.isEmpty(bo.getTermNo()) || bo.getTermNo() == 0) {
-            errorInfo = new ErrorInfo(1, "", "设备机号不正确", "设备机号必须大于零!");
+            errorInfo = new ErrorInfo(1, ApiErrorTypeConstants.PARAM_ERROR, "设备机号不正确", "设备机号必须大于零!");
             return R.fail(errorInfo);
         }
         // 检查交易人员标识
         if (bo.getCardNo() <= 0 && bo.getFactoryId() == 0 && bo.getUserNo() <= 0 && StrUtil.isEmpty(bo.getUserNumb())) {
-            errorInfo = new ErrorInfo(1, "", "交易人员标识不满足",
+            errorInfo = new ErrorInfo(1, ApiErrorTypeConstants.PARAM_ERROR, "交易人员标识不满足",
                                       "必须提供 [CardNo | FactoryId | userNo | userNumb] 中至少1项来标识交易用户");
             return R.fail(errorInfo);
         }
         return R.ok();
     }
 
+    /**
+     * 检查校验码
+     * @param bo 消费信息
+     * @param mac 校验码
+     * @return 检查结果
+     */
+    public R<ErrorInfo> checkMac(ConsumptionBo bo, String mac) {
+        ErrorInfo errorInfo;
+        Long termNo = bo.getTermNo();
+        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));
+            return R.fail(errorInfo);
+        }
+        if (ObjectUtil.isNotEmpty(mac)) {
+            // TODO 2024-12-25 luoyibo 如果mac不为空,需要校验
+            return R.ok();
+        }
+        return R.ok();
+    }
     /**
      * 消费记录人员、卡片检查,全部通过后获取到消费账户、卡片及设备的视图信息
      *
      * @param bo            消费记录
      * @param userAccountVo 消费账户
      * @param userCardVo    消费卡片
-     * @param useTermVo     消费设备
      * @return 验证结果
      */
-    public R<ErrorInfo> checkUser(ConsumptionBo bo, RemoteUserAccountVo userAccountVo, RemoteCardVo userCardVo,
-                                  XfTermVo useTermVo) {
-        long termNo = ObjectUtil.isEmpty(bo.getTermNo()) ? 0 : bo.getTermNo();
+    public R<ErrorInfo> checkUser(ConsumptionBo bo, RemoteUserAccountVo userAccountVo, RemoteCardVo userCardVo) {
         long cardNo = ObjectUtil.isEmpty(bo.getCardNo()) ? 0 : bo.getCardNo();
         long factoryId = ObjectUtil.isEmpty(bo.getFactoryId()) ? 0 : bo.getFactoryId();
         long userNo = ObjectUtil.isEmpty(bo.getUserNo()) ? 0 : bo.getUserNo();
         String userNumb = bo.getUserNumb() == null ? null : bo.getUserNumb();
-        // 获取设备信息
-        if (termNo > 0) {
-            R<ErrorInfo> result = checkTerm(bo, useTermVo);
-            if (R.isError(result)) {
-                return result;
-            }
-        }
         // 卡流水号检查
         if (cardNo > 0) {
             return checkCardNo(bo, userAccountVo, userCardVo);
@@ -182,8 +198,8 @@ public class CheckBusiness {
         if (ObjectUtil.isEmpty(mealType)) {
             return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.NOT_FOUND, "不在交易时段", "不在交易时段"));
         }
-        // 3.折扣\限额\限次验证
-        result = checkLimitDeal(bo, userAccountVo, useTermVo, userCardVo, mealType);
+        // 3.设备限制验证
+        result = checkTermLimitDeal(bo, userAccountVo, useTermVo, userCardVo, mealType);
         if (R.isError(result)) {
             return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.NOT_FOUND, "消费限制判断存在问题", JSONUtil.toJsonStr(result.getData())));
         }
@@ -427,8 +443,10 @@ public class CheckBusiness {
         return R.ok();
     }
 
-    public R<ErrorInfo> checkLimitDeal(ConsumptionBo bo, RemoteUserAccountVo userAccountVo, XfTermVo termVo, RemoteCardVo userCardVo,RemoteMealTypeVo mealTypeVo) {
+    public R<ErrorInfo> checkTermLimitDeal(ConsumptionBo bo, RemoteUserAccountVo userAccountVo, XfTermVo termVo, RemoteCardVo userCardVo,RemoteMealTypeVo mealTypeVo) {
         Long termNo = termVo.getTermNo();
+        BigDecimal consumeValue = bo.getConsumeMoney();
+        Long factoryFixId = userCardVo.getFactoryId();
         String mealTypeId = mealTypeVo.getTypeId();
         if (termNo == 0 && ObjectUtil.equal("0", mealTypeId)) {
             return R.ok();
@@ -442,20 +460,56 @@ public class CheckBusiness {
         // 每天最大消费金额
         BigDecimal termDayMoney = termVo.getDayMoney() == null ? BigDecimal.ZERO : termVo.getDayMoney();
         // 每餐最大消费次数
-        Integer termMealCount = termVo.getMealCount() == null ? 0 : termVo.getMealCount();
+        int termMealCount = termVo.getMealCount() == null ? 0 : termVo.getMealCount();
         // 单次最大消费金额
         BigDecimal termSingleMoney = termVo.getSingleMoney() == null ? BigDecimal.ZERO : termVo.getSingleMoney();
         // 二次可用最大时间间隔
-        Integer termSwipeInterval = termVo.getSwipeInterval() == null ? 0 : termVo.getSwipeInterval();
+        int termSwipeInterval = termVo.getSwipeInterval() == null ? 0 : termVo.getSwipeInterval();
         // 设备是否启用了卡有效
-        Boolean termUseValidity = Objects.equals(termVo.getTermValidity(), "0") ? Boolean.FALSE : Boolean.TRUE;
+        boolean termUseValidity = Objects.equals(termVo.getTermValidity(), "0") ? Boolean.FALSE : Boolean.TRUE;
+
+        LocalDateTime currentLocalDt = LocalDateTime.now();
+        LocalDateTime expiryLocalDt = LocalDateTime.now();
+
+        // 最后消费时间
+        LocalDateTime lastPayLocalDt = LocalDateTime.ofInstant(userCardVo.getLastPay().toInstant(), ZoneOffset.of("+8"));
+        // 有效期时间
+        expiryLocalDt = LocalDateTime.ofInstant(userCardVo.getLifespan().toInstant(), ZoneOffset.of("+8"));
 
+        // 设备消费间隔验证
+        if ((currentLocalDt.toEpochSecond(ZoneOffset.of("+8"))
+                 - lastPayLocalDt.toEpochSecond(ZoneOffset.of("+8"))) / 60 < termSwipeInterval && termSwipeInterval > 0) {// 消费间隔
+            return R.fail(new ErrorInfo(400, TradeStatusEnum.TimeInterval.toString(), "超过设备单次限额", TradeStatusEnum.TimeInterval.getName()));
+        }
+        // 设备单次限额验证
+        if (termSingleMoney.compareTo(consumeValue) < 0 && termSingleMoney.compareTo(BigDecimal.ZERO) > 0) {// 限制金额
+            return R.fail(new ErrorInfo(400, TradeStatusEnum.OnceBigMoney.toString(), "超过设备单次限额", TradeStatusEnum.OnceBigMoney.getName()));
+        }
+        // 设备启用了卡有效时进行卡有效期验证
+        if (factoryFixId != 0 && termUseValidity && currentLocalDt.isAfter(expiryLocalDt)) {
+            return R.fail(new ErrorInfo(400, TradeStatusEnum.CardValidDate.toString(), "卡片已超过失效日期", TradeStatusEnum.CardValidDate.getName()));
+        }
         // 消费卡类限制验证
         int offsetTypeId = (int) Math.pow(2, (cardTypeId - 1));
         int temp = offsetTypeId & termCardTypeId;
         if (temp != offsetTypeId) {
-            return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.NOT_FOUND, "卡类限制", TradeStatusEnum.CardTypeLimit.getName()));
+            return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.NOT_FOUND, "设备卡类限制", TradeStatusEnum.CardTypeLimit.getName()));
+        }
+        String lastMeal = userCardVo.getLastMeal().toString();
+        int mealCount = userCardVo.getMealCount().intValue();
+        BigDecimal mealValue = userCardVo.getMealTotal();
+        if (!ObjectUtil.equals(lastMeal,mealTypeId)
+                || !currentLocalDt.toLocalDate().isEqual(lastPayLocalDt.toLocalDate())) {
+            remoteCardService.initCardMealData(userCardVo.getCardNo(), mealTypeId);
+            lastMeal = mealTypeId;
+            mealCount = 0;
+            mealValue = BigDecimal.ZERO;
+        } else {
+            if (termMealCount != 0 && mealCount >= termMealCount) {
+                return R.fail(new ErrorInfo(400, TradeStatusEnum.MealLimitTimes.toString(), "餐限次", TradeStatusEnum.MealLimitTimes.getName()));
+            }
         }
+
         return R.ok();
     }
 

+ 24 - 13
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/ConsumeBusiness.java

@@ -10,7 +10,6 @@ import org.dromara.backstage.api.domain.vo.RemoteUserAccountVo;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.domain.model.ErrorInfo;
 import org.dromara.server.common.domain.consume.bo.ConsumptionBo;
-import org.dromara.server.consume.convert.strategy.RecordConvertStrategyContent;
 import org.dromara.server.consume.domain.vo.PtBagVo;
 import org.dromara.server.consume.domain.vo.XfConsumeDetailOriginalVo;
 import org.dromara.server.consume.domain.vo.XfTermVo;
@@ -36,30 +35,40 @@ import java.util.List;
 public class ConsumeBusiness {
     private final CheckBusiness checkBusiness;
     private final BaseBusiness baseBusiness;
-    private final RecordConvertStrategyContent recordConvertStrategy;
 
     /**
      * 请求消费
      *
-     * @param bo 消费记录
+     * @param bo    消费记录
+     * @param mac
+     * @param xfPwd
      * @return 请求结果
      */
-    public R<ErrorInfo> createOrder(ConsumptionBo bo) {
+    public R<ErrorInfo> createOrder(ConsumptionBo bo, String mac, String xfPwd) {
+        log.info("[请求交易]-[开始]-[{}]", JSONUtil.toJsonStr(bo));
         R<ErrorInfo> result = checkBusiness.checkParam(bo);
         if (R.isError(result)) {
             log.error("[请求交易]-[参数验证失败]-[{}]", JSONUtil.toJsonStr(result.getData()));
             return result;
         }
-
+        result = checkBusiness.checkMac(bo, mac);
+        if (R.isError(result)) {
+            log.error("[请求交易]-[mac校验失败]-[{}]", JSONUtil.toJsonStr(result.getData()));
+            return result;
+        }
         RemoteUserAccountVo userAccountVo = new RemoteUserAccountVo();
         RemoteCardVo userCardVo = new RemoteCardVo();
-        XfTermVo termVo = new XfTermVo();
-        result = checkBusiness.checkUser(bo, userAccountVo, userCardVo, termVo);
+        result = checkBusiness.checkUser(bo, userAccountVo, userCardVo);
         if (R.isError(result)) {
             log.error("[请求交易]-[用户信息验证失败]-[{}]", JSONUtil.toJsonStr(result.getData()));
             return result;
         }
-
+        XfTermVo termVo = new XfTermVo();
+        result = checkBusiness.checkTerm(bo, termVo);
+        if (R.isError(result)) {
+            log.error("[请求交易]-[设备验证失败]-[{}]", result.getData());
+            return result;
+        }
         result = checkBusiness.checkConsume(bo, userAccountVo, userCardVo, termVo);
         if (R.isError(result)) {
             log.error("[请求交易]-[交易验证失败]-[{}]", JSONUtil.toJsonStr(result.getData()));
@@ -81,10 +90,12 @@ public class ConsumeBusiness {
     /**
      * 上传消费记录
      *
-     * @param bo 消费记录
+     * @param bo    消费记录
+     * @param mac
+     * @param xfPwd
      * @return 上传结果
      */
-    public R<ErrorInfo> postOrder(ConsumptionBo bo) {
+    public R<ErrorInfo> postOrder(ConsumptionBo bo, String mac, String xfPwd) {
         R<ErrorInfo> result = checkBusiness.checkParam(bo);
         if (R.isError(result)) {
             log.error("[上传交易]-[参数验证失败]-[{}]", JSONUtil.toJsonStr(result.getData()));
@@ -115,13 +126,13 @@ public class ConsumeBusiness {
         return R.ok();
     }
 
-    public R<ErrorInfo> fullOrder(ConsumptionBo bo) {
-        R<ErrorInfo> result = this.createOrder(bo);
+    public R<ErrorInfo> fullOrder(ConsumptionBo bo, String mac, String xfPwd) {
+        R<ErrorInfo> result = this.createOrder(bo, "", "");
         if(!R.isSuccess(result)) {
             log.error("[请求交易]-[请求交易处理失败]-[{}]", JSONUtil.toJsonStr(result.getData()));
             return result;
         }
-        result = this.postOrder(bo);
+        result = this.postOrder(bo, "", "");
         if(!R.isSuccess(result)) {
             log.error("[交易上传]-[交易上传处理失败]-[{}]", JSONUtil.toJsonStr(result.getData()));
             return result;

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

@@ -109,7 +109,7 @@ public class ConsumeController {
      */
     @PostMapping("/Consume/kafka")
     public R<ErrorInfo> consumeKafka(@RequestBody ConsumptionBo bo) {
-        return consumeBusiness.fullOrder(bo);
+        return consumeBusiness.fullOrder(bo, "", "");
     }
 
     /**
@@ -133,11 +133,11 @@ public class ConsumeController {
 
         R<ErrorInfo> errorInfo;
         if (Objects.equals(type, "requestConsume")) {
-            errorInfo = consumeBusiness.createOrder(bo);
+            errorInfo = consumeBusiness.createOrder(bo, mac, xfPwd);
         } else if (Objects.equals(type, "uploadRecord")) {
-            errorInfo = consumeBusiness.postOrder(bo);
+            errorInfo = consumeBusiness.postOrder(bo, mac, xfPwd);
         } else {
-            errorInfo = consumeBusiness.fullOrder(bo);
+            errorInfo = consumeBusiness.fullOrder(bo, mac, xfPwd);
         }
         if (R.isError(errorInfo)) {
             ErrorResult result = new ErrorResult();

+ 1 - 1
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/dubbo/RemoteConsumeServiceImpl.java

@@ -26,6 +26,6 @@ public class RemoteConsumeServiceImpl implements RemoteConsumeService {
     private final ConsumeBusiness consumeBusiness;
     @Override
     public R<ErrorInfo> dealKafkaConsumeData(RemoteConsumeBo bo) {
-        return consumeBusiness.fullOrder(BeanUtil.copyProperties(bo, ConsumptionBo.class));
+        return consumeBusiness.fullOrder(BeanUtil.copyProperties(bo, ConsumptionBo.class), "", "");
     }
 }