Procházet zdrojové kódy

限额、限次、折扣数据双向同步功能开发

baiyun před 1 rokem
rodič
revize
eff5da9dab
55 změnil soubory, kde provedl 2465 přidání a 225 odebrání
  1. 9 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/RemoteMealTypeService.java
  2. 19 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/RemoteOperatorService.java
  3. 2 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/RemotePtAccountService.java
  4. 3 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/RemotePtRoomService.java
  5. 39 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/RemoteXfLimitedService.java
  6. 39 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/RemoteXfQuotaService.java
  7. 67 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/bo/RemoteXfLimitedBo.java
  8. 33 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/bo/RemoteXfLimitedTermBo.java
  9. 67 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/bo/RemoteXfQuotaBo.java
  10. 32 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/bo/RemoteXfQuotaTermBo.java
  11. 2 135
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/bo/RemoteXfTermParamBo.java
  12. 54 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/vo/RemoteMealTypeVo.java
  13. 67 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/vo/RemoteOperatorVo.java
  14. 72 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/vo/RemotePtAccountVo.java
  15. 79 0
      ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/vo/RemotePtRoomVo.java
  16. 30 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/ApiErrorTypeConstants.java
  17. 23 3
      ruoyi-common/ruoyi-common-message/src/main/java/org/dromara/common/message/kafka/constant/MessageEventTypeConstants.java
  18. 19 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/domain/convert/RemoteMealTypeVoConvert.java
  19. 19 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/domain/convert/RemoteOperatorVoConvert.java
  20. 17 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/domain/convert/RemotePtAccountVoConvert.java
  21. 17 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/domain/convert/RemotePtRoomVoConvert.java
  22. 20 7
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/dubbo/RemoteMealTypeServiceImpl.java
  23. 35 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/dubbo/RemoteOperatorServiceImpl.java
  24. 9 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/dubbo/RemotePtAccountServiceImpl.java
  25. 13 5
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/dubbo/RemotePtRoomServiceImpl.java
  26. 0 1
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/service/impl/PtRoomServiceImpl.java
  27. 1 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/business/payments/PayOrderBusiness.java
  28. 54 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/consumption/dubbo/RemoteXfLimitedServiceImpl.java
  29. 52 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/consumption/dubbo/RemoteXfQuotaServiceImpl.java
  30. 9 5
      ruoyi-server/ruoyi-server-base/src/main/java/org/dromara/server/base/service/yktOperation/SyncRemotePtUserAccountService.java
  31. 76 0
      ruoyi-server/ruoyi-server-base/src/main/java/org/dromara/server/base/service/yktOperation/SyncRemoteXfLimitedService.java
  32. 76 0
      ruoyi-server/ruoyi-server-base/src/main/java/org/dromara/server/base/service/yktOperation/SyncRemoteXfQuotaService.java
  33. 2 0
      ruoyi-server/ruoyi-server-base/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  34. 6 1
      ruoyi-server/ruoyi-server-common/src/main/java/org/dromara/server/common/constant/DefaultConstants.java
  35. 7 1
      ruoyi-server/ruoyi-server-common/src/main/java/org/dromara/server/common/domain/consume/bo/ConsumptionBo.java
  36. 26 0
      ruoyi-server/ruoyi-server-common/src/main/java/org/dromara/server/common/util/CardDateUtils.java
  37. 189 5
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/BaseBusiness.java
  38. 212 49
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/CheckBusiness.java
  39. 43 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/ConsumeBusiness.java
  40. 24 10
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/controller/v1/ConsumeController.java
  41. 2 1
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/convert/strategy/impl/YcRecordConvertStrategyImpl.java
  42. 80 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/domain/PtBag.java
  43. 92 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/domain/bo/PtBagBo.java
  44. 82 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/domain/vo/PtBagVo.java
  45. 15 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/mapper/PtBagMapper.java
  46. 110 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/IPtBagService.java
  47. 4 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/IXfConsumeDetailService.java
  48. 380 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/impl/PtBagServiceImpl.java
  49. 18 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/impl/XfConsumeDetailServiceImpl.java
  50. 20 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/impl/XfTermServiceImpl.java
  51. 1 1
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/impl/XfUserTotalServiceImpl.java
  52. 25 0
      ruoyi-server/ruoyi-server-consume/src/main/resources/mapper/consume/PtBagMapper.xml
  53. 1 1
      ruoyi-server/ruoyi-server-mqdata/src/main/java/org/dromara/server/mq/event/kafka/impl/yktOperation/PtXfDiscountEventStrategyImpl.java
  54. 36 0
      ruoyi-server/ruoyi-server-mqdata/src/main/java/org/dromara/server/mq/event/kafka/impl/yktOperation/PtXfLimitedEventStrategyImpl.java
  55. 36 0
      ruoyi-server/ruoyi-server-mqdata/src/main/java/org/dromara/server/mq/event/kafka/impl/yktOperation/PtXfQuotaEventStrategyImpl.java

+ 9 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/RemoteMealTypeService.java

@@ -1,8 +1,11 @@
 package org.dromara.backstage.api;
 
 import org.dromara.backstage.api.domain.bo.RemotePtMealTypeBo;
+import org.dromara.backstage.api.domain.bo.RemotePtParameterBo;
+import org.dromara.backstage.api.domain.vo.RemoteMealTypeVo;
 
 import java.util.Collection;
+import java.util.Date;
 
 /**
  * 餐类服务
@@ -18,4 +21,10 @@ public interface RemoteMealTypeService {
 
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) throws Exception;
 
+    /**
+     * 根据就餐获取对应的餐类信息
+     * @param mealTime 就餐时间
+     * @return 餐类信息
+     */
+    RemoteMealTypeVo queryMealTypeVoByTime(Date mealTime);
 }

+ 19 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/RemoteOperatorService.java

@@ -0,0 +1,19 @@
+package org.dromara.backstage.api;
+
+import org.dromara.backstage.api.domain.vo.RemoteOperatorVo;
+
+/**
+ * 餐类服务
+ *
+ * @author bing
+ */
+public interface RemoteOperatorService {
+
+    /**
+     * 根据营业员Id获取营业员信息
+     *
+     * @param operatorId 营业员Id
+     * @return 餐类信息
+     */
+    RemoteOperatorVo getVoById(Long operatorId);
+}

+ 2 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/RemotePtAccountService.java

@@ -1,6 +1,7 @@
 package org.dromara.backstage.api;
 
 import org.dromara.backstage.api.domain.bo.RemotePtAccountBo;
+import org.dromara.backstage.api.domain.vo.RemotePtAccountVo;
 
 import java.util.Collection;
 
@@ -20,4 +21,5 @@ public interface RemotePtAccountService {
 
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) throws Exception;
 
+    RemotePtAccountVo selectVoById(Long id) ;
 }

+ 3 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/RemotePtRoomService.java

@@ -2,6 +2,7 @@ package org.dromara.backstage.api;
 
 import org.dromara.backstage.api.domain.bo.RemotePtRoomBatchSetBo;
 import org.dromara.backstage.api.domain.bo.RemotePtRoomBo;
+import org.dromara.backstage.api.domain.vo.RemotePtRoomVo;
 
 import java.util.Collection;
 
@@ -17,4 +18,6 @@ public interface RemotePtRoomService {
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) throws Exception;
 
     Boolean batchSet(RemotePtRoomBatchSetBo bo);
+
+    RemotePtRoomVo selectVoById(Long id) ;
 }

+ 39 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/RemoteXfLimitedService.java

@@ -0,0 +1,39 @@
+package org.dromara.backstage.api;
+
+import org.dromara.backstage.api.domain.bo.RemoteXfLimitedBo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 消费限额服务
+ *
+ * @author Lion Li
+ */
+public interface RemoteXfLimitedService {
+
+    /**
+     * 新增消费限额
+     */
+    Boolean insertByBo(RemoteXfLimitedBo bo) throws Exception;
+
+    /**
+     * 修改消费限额
+     */
+    Boolean updateByBo(RemoteXfLimitedBo bo) throws Exception;
+
+    /**
+     * 删除消费限额
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) throws Exception;
+
+    /**
+     * 新增限额设备
+     */
+    Boolean addLimitedTerm(List<Long> ids);
+
+    /**
+     * 删除限额设备
+     */
+    Boolean deleteLimitedTerm(List<Long> ids);
+}

+ 39 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/RemoteXfQuotaService.java

@@ -0,0 +1,39 @@
+package org.dromara.backstage.api;
+
+import org.dromara.backstage.api.domain.bo.RemoteXfQuotaBo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 消费限额服务
+ *
+ * @author Lion Li
+ */
+public interface RemoteXfQuotaService {
+
+    /**
+     * 新增消费限额
+     */
+    Boolean insertByBo(RemoteXfQuotaBo bo) throws Exception;
+
+    /**
+     * 修改消费限额
+     */
+    Boolean updateByBo(RemoteXfQuotaBo bo) throws Exception;
+
+    /**
+     * 删除消费限额
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) throws Exception;
+
+    /**
+     * 新增限额设备
+     */
+    Boolean addQuotaTerm(List<Long> ids);
+
+    /**
+     * 删除限额设备
+     */
+    Boolean deleteQuotaTerm(List<Long> ids);
+}

+ 67 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/bo/RemoteXfLimitedBo.java

@@ -0,0 +1,67 @@
+package org.dromara.backstage.api.domain.bo;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 限次管理业务对象
+ *
+ * @author bing
+ * @date 2024-08-15
+ */
+@Data
+@NoArgsConstructor
+public class RemoteXfLimitedBo implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 限次Id,主键
+     */
+    private Long limitedId;
+
+    /**
+     * 卡类Id
+     */
+    private Long cardType;
+
+    /**
+     * 每日次数
+     */
+    private Long dailyCount;
+
+    /**
+     * 早餐次数
+     */
+    private Long oneCount;
+
+    /**
+     * 午餐次数
+     */
+    private Long twoCount;
+
+    /**
+     * 晚餐次数
+     */
+    private Long threeCount;
+
+    /**
+     * 宵夜次数
+     */
+    private Long fourCount;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 启用状态,0-未启用 1-启用
+     */
+    private String status;
+
+
+}

+ 33 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/bo/RemoteXfLimitedTermBo.java

@@ -0,0 +1,33 @@
+package org.dromara.backstage.api.domain.bo;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 限次设备绑定业务对象
+ *
+ * @author bing
+ * @date 2024-08-15
+ */
+@Data
+@NoArgsConstructor
+public class RemoteXfLimitedTermBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 限次Id,主键
+     */
+    private Long limitedTermId;
+
+    /**
+     * 设备Id
+     */
+    private Long termId;
+
+
+}

+ 67 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/bo/RemoteXfQuotaBo.java

@@ -0,0 +1,67 @@
+package org.dromara.backstage.api.domain.bo;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 限额管理业务对象
+ *
+ * @author bing
+ * @date 2024-08-14
+ */
+@Data
+@NoArgsConstructor
+public class RemoteXfQuotaBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+    /**
+     * 限额Id,主键
+     */
+    private Long quotaId;
+
+    /**
+     * 卡类Id
+     */
+    private Long cardType;
+
+    /**
+     * 每日金额
+     */
+    private Long dailyMoney;
+
+    /**
+     * 早餐金额
+     */
+    private Long oneMoney;
+
+    /**
+     * 午餐金额
+     */
+    private Long twoMoney;
+
+    /**
+     * 晚餐金额
+     */
+    private Long threeMoney;
+
+    /**
+     * 宵夜金额
+     */
+    private Long fourMoney;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 启用状态,0-未启用 1-启用
+     */
+    private String status;
+
+
+}

+ 32 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/bo/RemoteXfQuotaTermBo.java

@@ -0,0 +1,32 @@
+package org.dromara.backstage.api.domain.bo;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 限额设备绑定业务对象
+ *
+ * @author bing
+ * @date 2024-08-14
+ */
+@Data
+@NoArgsConstructor
+public class RemoteXfQuotaTermBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+    /**
+     * 限额Id,主键
+     */
+    private Long quotaTermId;
+
+    /**
+     * 设备Id
+     */
+    private Long termId;
+
+
+}

+ 2 - 135
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/bo/RemoteXfTermParamBo.java

@@ -3,6 +3,7 @@ package org.dromara.backstage.api.domain.bo;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import java.io.Serial;
 import java.io.Serializable;
 import java.math.BigDecimal;
 
@@ -16,133 +17,19 @@ import java.math.BigDecimal;
 @NoArgsConstructor
 public class RemoteXfTermParamBo implements Serializable {
 
+    @Serial
     private static final long serialVersionUID = 1L;
     /**
      * 设备Id,主键
      */
     private Long termId;
 
-    /**
-     * 设备机号
-     */
-//    private Long termNo;
-
-    /**
-     * 设备名称
-     */
-//    private String termName;
-
-    /**
-     * 所属餐厅Id
-     */
-//    private Long roomId;
-
-    /**
-     * 结算账户Id
-     */
-//    private Long accountId;
-
-    /**
-     * 设备类型,见XF_TermType字典类型
-     */
-//    private String termType;
-
-    /**
-     * 设备二维码
-     */
-//    private String qrCode;
-
-    /**
-     * 工作站Id
-     */
-//    private Long stationId;
-
-    /**
-     * 扣费类型,见XF_ConsumeType字典类型
-     */
-//    private String consumeType;
-
-    /**
-     * 是否自动下载参数,见sys_yew_no字典项
-     */
-//    private String autoDown;
-
-    /**
-     * 应用系统,见YYXT数据字典类别
-     */
-//    private String useType;
 
     /**
      * 允许使用的卡类
      */
-//    @NotNull(message = "允许使用的卡类不能为空")
     private Long cardType;
 
-    /**
-     * 设备IP
-     */
-//    private String termIp;
-
-    /**
-     * 设备MAC地址
-     */
-//    private String termMac;
-
-    /**
-     * 设备通讯端口
-     */
-//    private Long commPort;
-
-    /**
-     * 子网掩码
-     */
-//    private String mask;
-
-    /**
-     * 服务器IP
-     */
-//    private String serverIp;
-
-    /**
-     * 服务器端口
-     */
-//    private Long serverPort;
-
-    /**
-     * 网关IP
-     */
-//    private String gatewayIp;
-
-    /**
-     * 心跳间隔,以秒为单位
-     */
-//    private Long beatInterval;
-
-    /**
-     * 通讯超时时间,以100毫秒为单位
-     */
-//    private Long timeout;
-
-    /**
-     * 允许脱机时间
-     */
-//    private Long offlineTime;
-
-    /**
-     * 高级参数
-     */
-//    private String advParam;
-
-    /**
-     * 设备参数
-     */
-//    private String posParam;
-
-    /**
-     * 费率参数
-     */
-//    private String rateParam;
-
     /**
      * 消费工作模式,见XF_WorkMode字典类别
      */
@@ -303,26 +190,6 @@ public class RemoteXfTermParamBo implements Serializable {
      */
     private String termValidity;
 
-    /**
-     * 设备记录流水号
-     */
-//    private Long recordId;
-
-    /**
-     * 数据上传时间
-     */
-//    private Date uploadTime;
-
-    /**
-     * 黑名单下载时间
-     */
-//    private Date blackDownTime;
-
-    /**
-     * 最后校时时间
-     */
-//    private Date lastCheck;
-
     /**
      * 重启时间
      */

+ 54 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/vo/RemoteMealTypeVo.java

@@ -0,0 +1,54 @@
+package org.dromara.backstage.api.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 营业时段视图对象 t_pt_mealType
+ *
+ * @author Yz
+ * @date 2024-08-05
+ */
+@Data
+@ExcelIgnoreUnannotated
+public class RemoteMealTypeVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 营业时段id
+     */
+    private Long mealId;
+
+    /**
+     * 餐类Id
+     */
+    private String typeId;
+    /**
+     * 餐类名称
+     */
+    private String mealName;
+
+    /**
+     * 开始时间
+     */
+    private String beginTime;
+
+    /**
+     * 结束时间
+     */
+    private String endTime;
+
+    /**
+     * 类别说明
+     */
+    private String mealNotes;
+
+
+}

+ 67 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/vo/RemoteOperatorVo.java

@@ -0,0 +1,67 @@
+package org.dromara.backstage.api.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 营业员视图对象 t_pt_operator
+ *
+ * @author bing
+ * @date 2024-08-01
+ */
+@Data
+@ExcelIgnoreUnannotated
+public class RemoteOperatorVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 营业员Id,主键
+     */
+    private Long operatorId;
+
+    /**
+     * 所属结算账户Id
+     */
+    private Long accountId;
+
+    private String accountName;
+
+    /**
+     * 营业员名称
+     */
+    private String operatorName;
+
+    /**
+     * 卡流水号,默认已有最大流水号+1
+     */
+    private Long cardNumber;
+
+    /**
+     * 物理卡号,卡片出厂的唯一id
+     */
+    private String factoryId;
+
+    /**
+     * 营业员卡密码,会和硬件对接
+     */
+    private String cardPwd;
+
+    /**
+     * 营业员卡有效期,会和硬件对接
+     */
+    private Date lifespan;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 72 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/vo/RemotePtAccountVo.java

@@ -0,0 +1,72 @@
+package org.dromara.backstage.api.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 结算账户视图对象 t_pt_account
+ *
+ * @author bing
+ * @date 2024-07-31
+ */
+@Data
+@ExcelIgnoreUnannotated
+public class RemotePtAccountVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Long accountId;
+
+    /**
+     * 结算账户名称
+     */
+    private String accountName;
+
+    /**
+     * 结算账户代码
+     */
+    private String accountCode;
+
+    /**
+     * 联系电话
+     */
+    private String telephone;
+
+    /**
+     * 银行卡号
+     */
+    private String bankNumber;
+
+    /**
+     * 账户状态,见sys_normal_disable字典类别
+     */
+    private String status;
+
+    /**
+     * 管理员姓名
+     */
+    private String adminName;
+
+    /**
+     * 证件编号
+     */
+    private String idNumber;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+
+}

+ 79 - 0
ruoyi-api/ruoyi-api-backstage/src/main/java/org/dromara/backstage/api/domain/vo/RemotePtRoomVo.java

@@ -0,0 +1,79 @@
+package org.dromara.backstage.api.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 房间定义视图对象 t_pt_room
+ *
+ * @author bing
+ * @date 2024-08-09
+ */
+@Data
+@ExcelIgnoreUnannotated
+public class RemotePtRoomVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 房间Id,主键
+     */
+    private Long roomId;
+
+    /**
+     * 所属区域id
+     */
+//    @ExcelProperty(value = "所属区域id")
+    private Long areaId;
+
+    private String areaName;
+
+    /**
+     * 房间编码
+     */
+    private String roomCode;
+
+    /**
+     * 房间名称
+     */
+    private String roomName;
+
+    /**
+     * 房间类型,见FJLX字典类型
+     */
+    private String roomType;
+
+    /**
+     * 门牌号1
+     */
+    private String codeOne;
+
+    /**
+     * 门牌号2
+     */
+    private String codeTwo;
+
+    /**
+     * 门牌号3
+     */
+    private String codeThree;
+
+    /**
+     * 门牌号4
+     */
+    private String codeFour;
+
+    /**
+     * 门牌号5
+     */
+    private String codeFive;
+
+
+
+
+}

+ 30 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/ApiErrorTypeConstants.java

@@ -0,0 +1,30 @@
+package org.dromara.common.core.constant;
+
+/**
+ * API接口调用错误类型枚举
+ */
+public interface ApiErrorTypeConstants {
+    String EXCEPTION = "EXCEPTION";
+     String BAD_REQUEST = "BAD_REQUEST";
+     String NOT_FOUND = "NOT_FOUND";
+     String PARAM_ERROR = "PARAM_ERROR";
+     String CONSUME_CHECK_FAIL = "CONSUME_CHECK_FAIL";
+     String CONSUME_FAIL = "CONSUME_FAIL";
+     String REFUND_FAIL = "REFUND_FAIL";
+     String CONDITION_DISSATISFY = "CONDITION_DISSATISFY";
+     String INVALID_SIGN = "INVALID_SIGN";
+     String INVALID_TOKEN = "INVALID_TOKEN";
+     String INVALID_MAC = "INVALID_MAC";
+     String AUTHENTICATION_FAIL = "AUTHENTICATION_FAIL";
+     String SQLDB_COMMIT_FAIL = "SQLDB_COMMIT_FAIL";
+     String REMOTE_OPEN_DOOR_FAIL = "REMOTE_OPEN_DOOR_FAIL";
+     String RECORD_IS_EXISTS = "RECORD_IS_EXISTS";
+     String RECORD_IS_DEALED = "RECORD_IS_DEALED";
+     String OBJECT_NOT_EXISTS = "OBJECT_NOT_EXISTS";
+     String CARD_NOT_EXISTS = "CARD_NOT_EXISTS";
+     String CARD_BAGS_NOT_EXISTS = "CARD_BAGS_NOT_EXISTS";
+     String CARD_STATUS_NOT_NORMAL = "CARD_STATUS_NOT_NORMAL";
+     String CREDIT_BACK_ID_NOT_EXISTS = "CREDIT_BACK_ID_NOT_EXISTS";
+     String DEAL_NOT_EXISTS = "DEAL_NOT_EXISTS";
+     String OPERATION_TOO_FREQUENT = "OPERATION_TOO_FREQUENT";
+}

+ 23 - 3
ruoyi-common/ruoyi-common-message/src/main/java/org/dromara/common/message/kafka/constant/MessageEventTypeConstants.java

@@ -90,6 +90,14 @@ public class MessageEventTypeConstants {
      * 折扣管理能模块标识
      */
     public static final String XF_DISCOUNT_SENDER = "YKT_114";
+    /**
+     * 折扣管理能模块标识
+     */
+    public static final String XF_QUOTA_SENDER = "YKT_116";
+    /**
+     * 折扣管理能模块标识
+     */
+    public static final String XF_LIMITED_SENDER = "YKT_118";
     /**
      * 补助设置能模块标识
      */
@@ -183,14 +191,26 @@ public class MessageEventTypeConstants {
     /** 新增折扣设备 */
     public static final String	DISCOUNT_TERM_ADD = "YKT_114_DISCOUNT_TERM_ADD";
     /** 删除折扣设备 */
-    public static final String	DISCOUNT_TERM_DEL = "YKT_114_DISCOUNT_TERM_DEL";
+    public static final String	DISCOUNT_TERM_REMOVE = "YKT_114_DISCOUNT_TERM_REMOVE";
+    /** 限额规则新增 */
     public static final String	xfQuota_ADD = "YKT_116_ADD";
+    /** 限额规则修改 */
     public static final String	xfQuota_EDIT = "YKT_116_EDIT";
+    /** 限额规则删除 */
     public static final String	xfQuota_REMOVE = "YKT_116_REMOVE";
-    public static final String	xfQuotaterm_EDIT = "YKT_117_EDIT";
+    /** 限额设备新增 */
+    public static final String	QUOTA_TERM_ADD = "YKT_116_QUOTA_TERM_ADD";
+    /** 限额设备删除 */
+    public static final String	QUOTA_TERM_REMOVE = "YKT_116_QUOTA_TERM_REMOVE";
+    /** 限次规则新增 */
     public static final String	xfLimited_ADD = "YKT_118_ADD";
+    /** 限次规则修改 */
     public static final String	xfLimited_EDIT = "YKT_118_EDIT";
+    /** 限次规则删除 */
     public static final String	xfLimited_REMOVE = "YKT_118_REMOVE";
-    public static final String	xfLimitedterm_EDIT = "YKT_119_EDIT";
+    /** 限次设备新增 */
+    public static final String	LIMITED_TERM_ADD = "YKT_118_LIMITED_TERM_ADD";
+    /** 限次设备删除 */
+    public static final String	LIMITED_TERM_REMOVE = "YKT_118_LIMITED_TERM_REMOVE";
 
 }

+ 19 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/domain/convert/RemoteMealTypeVoConvert.java

@@ -0,0 +1,19 @@
+package org.dromara.backstage.basics.domain.convert;
+
+import io.github.linpeilie.BaseMapper;
+import org.dromara.backstage.api.domain.bo.RemotePtSchoolBo;
+import org.dromara.backstage.api.domain.vo.RemoteMealTypeVo;
+import org.dromara.backstage.basics.domain.bo.PtSchoolBo;
+import org.dromara.backstage.basics.domain.vo.PtMealTypeVo;
+import org.mapstruct.Mapper;
+import org.mapstruct.MappingConstants;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * 餐类信息转换器
+ * @author bing
+ */
+@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface RemoteMealTypeVoConvert extends BaseMapper<PtMealTypeVo, RemoteMealTypeVo> {
+
+}

+ 19 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/domain/convert/RemoteOperatorVoConvert.java

@@ -0,0 +1,19 @@
+package org.dromara.backstage.basics.domain.convert;
+
+import io.github.linpeilie.BaseMapper;
+import org.dromara.backstage.api.domain.vo.RemoteMealTypeVo;
+import org.dromara.backstage.api.domain.vo.RemoteOperatorVo;
+import org.dromara.backstage.basics.domain.vo.PtMealTypeVo;
+import org.dromara.backstage.basics.domain.vo.PtOperatorVo;
+import org.mapstruct.Mapper;
+import org.mapstruct.MappingConstants;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * 餐类信息转换器
+ * @author bing
+ */
+@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface RemoteOperatorVoConvert extends BaseMapper<PtOperatorVo, RemoteOperatorVo> {
+
+}

+ 17 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/domain/convert/RemotePtAccountVoConvert.java

@@ -0,0 +1,17 @@
+package org.dromara.backstage.basics.domain.convert;
+
+import io.github.linpeilie.BaseMapper;
+import org.dromara.backstage.api.domain.vo.RemotePtAccountVo;
+import org.dromara.backstage.basics.domain.vo.PtAccountVo;
+import org.mapstruct.Mapper;
+import org.mapstruct.MappingConstants;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * 餐类信息转换器
+ * @author bing
+ */
+@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface RemotePtAccountVoConvert extends BaseMapper<PtAccountVo, RemotePtAccountVo> {
+
+}

+ 17 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/domain/convert/RemotePtRoomVoConvert.java

@@ -0,0 +1,17 @@
+package org.dromara.backstage.basics.domain.convert;
+
+import io.github.linpeilie.BaseMapper;
+import org.dromara.backstage.api.domain.vo.RemotePtRoomVo;
+import org.dromara.backstage.basics.domain.vo.PtRoomVo;
+import org.mapstruct.Mapper;
+import org.mapstruct.MappingConstants;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * 餐类信息转换器
+ * @author bing
+ */
+@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface RemotePtRoomVoConvert extends BaseMapper<PtRoomVo, RemotePtRoomVo> {
+
+}

+ 20 - 7
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/dubbo/RemoteMealTypeServiceImpl.java

@@ -2,41 +2,54 @@ package org.dromara.backstage.basics.dubbo;
 
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DateUtil;
 import lombok.RequiredArgsConstructor;
 import org.apache.dubbo.config.annotation.DubboService;
 import org.dromara.backstage.api.RemoteMealTypeService;
 import org.dromara.backstage.api.domain.bo.RemotePtMealTypeBo;
+import org.dromara.backstage.api.domain.vo.RemoteMealTypeVo;
 import org.dromara.backstage.basics.domain.bo.PtMealTypeBo;
+import org.dromara.backstage.basics.domain.vo.PtMealTypeVo;
 import org.dromara.backstage.basics.service.IPtMealtypeService;
+import org.dromara.common.core.utils.MapstructUtils;
 import org.springframework.stereotype.Service;
 
 import java.util.Collection;
+import java.util.Date;
 
 @RequiredArgsConstructor
 @Service
 @DubboService
 public class RemoteMealTypeServiceImpl implements RemoteMealTypeService {
 
-    private final IPtMealtypeService mealtypeService;
+    private final IPtMealtypeService mealTypeService;
     @Override
     public String selectMealTypeNameByIds(String ids) {
-        return mealtypeService.selectMealTypeNameByIds(ids);
+        return mealTypeService.selectMealTypeNameByIds(ids);
     }
 
     @Override
     public Boolean insertByBo(RemotePtMealTypeBo bo) throws Exception {
-        return mealtypeService.insertByBo(BeanUtil.toBean(bo, PtMealTypeBo.class));
+        return mealTypeService.insertByBo(BeanUtil.toBean(bo, PtMealTypeBo.class));
     }
 
     @Override
     public Boolean updateByBo(RemotePtMealTypeBo bo) throws Exception {
-        return mealtypeService.updateByBo(BeanUtil.toBean(bo, PtMealTypeBo.class));
+        return mealTypeService.updateByBo(BeanUtil.toBean(bo, PtMealTypeBo.class));
     }
 
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) throws Exception {
-        return mealtypeService.deleteWithValidByIds(ids,isValid);
+        return mealTypeService.deleteWithValidByIds(ids,isValid);
+    }
+    /**
+     * 根据就餐获取对应的餐类信息
+     * @param mealTime 就餐时间
+     * @return 餐类信息
+     */
+    @Override
+    public RemoteMealTypeVo queryMealTypeVoByTime(Date mealTime) {
+        PtMealTypeVo vo = mealTypeService.queryVoByTime(DateUtil.format(mealTime, "HH:mm:ss"));
+        return MapstructUtils.convert(vo, RemoteMealTypeVo.class);
     }
-
-
 }

+ 35 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/dubbo/RemoteOperatorServiceImpl.java

@@ -0,0 +1,35 @@
+package org.dromara.backstage.basics.dubbo;
+
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DateUtil;
+import lombok.RequiredArgsConstructor;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.dromara.backstage.api.RemoteMealTypeService;
+import org.dromara.backstage.api.RemoteOperatorService;
+import org.dromara.backstage.api.domain.bo.RemotePtMealTypeBo;
+import org.dromara.backstage.api.domain.vo.RemoteMealTypeVo;
+import org.dromara.backstage.api.domain.vo.RemoteOperatorVo;
+import org.dromara.backstage.basics.domain.bo.PtMealTypeBo;
+import org.dromara.backstage.basics.domain.vo.PtMealTypeVo;
+import org.dromara.backstage.basics.domain.vo.PtOperatorVo;
+import org.dromara.backstage.basics.service.IPtMealtypeService;
+import org.dromara.backstage.basics.service.IPtOperatorService;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.Date;
+
+@RequiredArgsConstructor
+@Service
+@DubboService
+public class RemoteOperatorServiceImpl implements RemoteOperatorService {
+    private final IPtOperatorService ptOperatorService;
+
+    @Override
+    public RemoteOperatorVo getVoById(Long operatorId) {
+        PtOperatorVo vo = ptOperatorService.queryById(operatorId);
+        return MapstructUtils.convert(vo, RemoteOperatorVo.class);
+    }
+}

+ 9 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/dubbo/RemotePtAccountServiceImpl.java

@@ -5,8 +5,11 @@ import lombok.RequiredArgsConstructor;
 import org.apache.dubbo.config.annotation.DubboService;
 import org.dromara.backstage.api.RemotePtAccountService;
 import org.dromara.backstage.api.domain.bo.RemotePtAccountBo;
+import org.dromara.backstage.api.domain.vo.RemotePtAccountVo;
 import org.dromara.backstage.basics.domain.bo.PtAccountBo;
+import org.dromara.backstage.basics.domain.vo.PtAccountVo;
 import org.dromara.backstage.basics.service.IPtAccountService;
+import org.dromara.common.core.utils.MapstructUtils;
 import org.springframework.stereotype.Service;
 
 import java.util.Collection;
@@ -37,4 +40,10 @@ public class RemotePtAccountServiceImpl implements RemotePtAccountService {
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) throws Exception {
         return accountService.deleteWithValidByIds(ids, isValid);
     }
+
+    @Override
+    public RemotePtAccountVo selectVoById(Long id) {
+        PtAccountVo vo = accountService.queryById(id);
+        return MapstructUtils.convert(vo,RemotePtAccountVo.class);
+    }
 }

+ 13 - 5
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/dubbo/RemotePtRoomServiceImpl.java

@@ -6,9 +6,12 @@ import org.apache.dubbo.config.annotation.DubboService;
 import org.dromara.backstage.api.RemotePtRoomService;
 import org.dromara.backstage.api.domain.bo.RemotePtRoomBatchSetBo;
 import org.dromara.backstage.api.domain.bo.RemotePtRoomBo;
+import org.dromara.backstage.api.domain.vo.RemotePtRoomVo;
 import org.dromara.backstage.basics.domain.bo.PtRoomBatchSetBo;
 import org.dromara.backstage.basics.domain.bo.PtRoomBo;
+import org.dromara.backstage.basics.domain.vo.PtRoomVo;
 import org.dromara.backstage.basics.service.IPtRoomService;
+import org.dromara.common.core.utils.MapstructUtils;
 import org.springframework.stereotype.Service;
 
 import java.util.Collection;
@@ -21,25 +24,30 @@ import java.util.Collection;
 @DubboService
 public class RemotePtRoomServiceImpl implements RemotePtRoomService {
 
-    private final  IPtRoomService iPtRoomService;
+    private final  IPtRoomService roomService;
     @Override
     public Boolean insertByBo(RemotePtRoomBo bo) throws Exception {
-        return iPtRoomService.insertByBo(BeanUtil.toBean(bo, PtRoomBo.class));
+        return roomService.insertByBo(BeanUtil.toBean(bo, PtRoomBo.class));
     }
 
     @Override
     public Boolean updateByBo(RemotePtRoomBo bo) throws Exception {
-        return iPtRoomService.updateByBo(BeanUtil.toBean(bo, PtRoomBo.class));
+        return roomService.updateByBo(BeanUtil.toBean(bo, PtRoomBo.class));
     }
 
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) throws Exception {
-        return iPtRoomService.deleteWithValidByIds(ids,isValid);
+        return roomService.deleteWithValidByIds(ids,isValid);
     }
 
     @Override
     public Boolean batchSet(RemotePtRoomBatchSetBo bo) {
-        return iPtRoomService.batchSet(BeanUtil.toBean(bo, PtRoomBatchSetBo.class));
+        return roomService.batchSet(BeanUtil.toBean(bo, PtRoomBatchSetBo.class));
     }
 
+    @Override
+    public RemotePtRoomVo selectVoById(Long id) {
+        PtRoomVo vo = roomService.queryById(id);
+        return MapstructUtils.convert(vo,RemotePtRoomVo.class);
+    }
 }

+ 0 - 1
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/basics/service/impl/PtRoomServiceImpl.java

@@ -1,6 +1,5 @@
 package org.dromara.backstage.basics.service.impl;
 
-import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.map.MapUtil;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;

+ 1 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/business/payments/PayOrderBusiness.java

@@ -233,6 +233,7 @@ public class PayOrderBusiness {
             return R.fail(e.getMessage(),bo);
         }
     }
+
     /**
      * 将要入账的补助信息转成收支订单业务对象
      * @param item 补助信息

+ 54 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/consumption/dubbo/RemoteXfLimitedServiceImpl.java

@@ -0,0 +1,54 @@
+package org.dromara.backstage.consumption.dubbo;
+
+import cn.hutool.core.bean.BeanUtil;
+import lombok.RequiredArgsConstructor;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.dromara.backstage.api.RemoteXfLimitedService;
+import org.dromara.backstage.api.domain.bo.RemoteXfLimitedBo;
+import org.dromara.backstage.consumption.domain.bo.XfLimitedBo;
+import org.dromara.backstage.consumption.service.IXfLimitedService;
+import org.dromara.backstage.consumption.service.IXfLimitedtermService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Hz
+ * {@code @date} 2024/11/4
+ * @description
+ */
+
+@RequiredArgsConstructor
+@Service
+@DubboService
+public class RemoteXfLimitedServiceImpl implements RemoteXfLimitedService {
+
+    private final IXfLimitedService limitedService;
+    private final IXfLimitedtermService limitedTermService;
+
+    @Override
+    public Boolean insertByBo(RemoteXfLimitedBo bo) throws Exception {
+        return limitedService.insertByBo(BeanUtil.copyProperties(bo, XfLimitedBo.class));
+    }
+
+    @Override
+    public Boolean updateByBo(RemoteXfLimitedBo bo) throws Exception {
+        return limitedService.updateByBo(BeanUtil.copyProperties(bo, XfLimitedBo.class));
+    }
+
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) throws Exception {
+        return limitedService.deleteWithValidByIds(ids,isValid);
+    }
+
+    @Override
+    public Boolean addLimitedTerm(List<Long> ids) {
+        return limitedTermService.insertByTermIds(ids);
+    }
+
+    @Override
+    public Boolean deleteLimitedTerm(List<Long> ids) {
+        return limitedTermService.deleteWithValidByIds(ids,true);
+    }
+}

+ 52 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/consumption/dubbo/RemoteXfQuotaServiceImpl.java

@@ -0,0 +1,52 @@
+package org.dromara.backstage.consumption.dubbo;
+
+import cn.hutool.core.bean.BeanUtil;
+import lombok.RequiredArgsConstructor;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.dromara.backstage.api.RemoteXfQuotaService;
+import org.dromara.backstage.api.domain.bo.RemoteXfQuotaBo;
+import org.dromara.backstage.consumption.domain.bo.XfQuotaBo;
+import org.dromara.backstage.consumption.service.IXfQuotaService;
+import org.dromara.backstage.consumption.service.IXfQuotatermService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Hz
+ * {@code @date} 2024/11/4
+ * @description
+ */
+
+@RequiredArgsConstructor
+@Service
+@DubboService
+public class RemoteXfQuotaServiceImpl implements RemoteXfQuotaService {
+    private final IXfQuotaService xfQuotaService;
+    private final IXfQuotatermService xfQuotatermService;
+    @Override
+    public Boolean insertByBo(RemoteXfQuotaBo bo) throws Exception {
+        return xfQuotaService.insertByBo(BeanUtil.copyProperties(bo, XfQuotaBo.class));
+    }
+
+    @Override
+    public Boolean updateByBo(RemoteXfQuotaBo bo) throws Exception {
+        return xfQuotaService.updateByBo(BeanUtil.copyProperties(bo, XfQuotaBo.class));
+    }
+
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) throws Exception {
+        return xfQuotaService.deleteWithValidByIds(ids,isValid);
+    }
+
+    @Override
+    public Boolean addQuotaTerm(List<Long> ids) {
+        return xfQuotatermService.insertByTermIds(ids);
+    }
+
+    @Override
+    public Boolean deleteQuotaTerm(List<Long> ids) {
+        return xfQuotatermService.deleteWithValidByIds(ids,true);
+    }
+}

+ 9 - 5
ruoyi-server/ruoyi-server-base/src/main/java/org/dromara/server/base/service/yktOperation/SyncRemotePtUserAccountService.java

@@ -6,12 +6,12 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.dromara.backstage.api.RemoteUserAccountService;
-import org.dromara.backstage.api.domain.bo.RemoteUserAccountBo;
+import org.dromara.common.core.domain.R;
 import org.dromara.common.core.exception.ServiceException;
+import org.dromara.system.api.RemoteUserService;
+import org.dromara.system.api.domain.bo.RemoteUserBo;
 import org.springframework.stereotype.Service;
 
-import java.util.Arrays;
-
 /**
  * 结算账户同步
  */
@@ -22,6 +22,8 @@ public class SyncRemotePtUserAccountService {
 
     @DubboReference
     private final RemoteUserAccountService userAccountService;
+    @DubboReference
+    private final RemoteUserService userService;
 
     /**
      * 远程调用开户
@@ -85,9 +87,11 @@ public class SyncRemotePtUserAccountService {
     }
 
     /**
-     * 新增
+     * 新增
      */
     public void addUserAccount(Object msg) throws ServiceException{
-       userAccountService.insertByBo(JSONUtil.toBean(msg.toString(), RemoteUserAccountBo.class));
+        if(R.isError(userService.insertUser(JSONUtil.parseObj(msg).toBean(RemoteUserBo.class)))){
+            throw new ServiceException("新增用户失败");
+        }
     }
 }

+ 76 - 0
ruoyi-server/ruoyi-server-base/src/main/java/org/dromara/server/base/service/yktOperation/SyncRemoteXfLimitedService.java

@@ -0,0 +1,76 @@
+package org.dromara.server.base.service.yktOperation;
+
+import cn.hutool.json.JSONUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.backstage.api.RemoteXfLimitedService;
+import org.dromara.backstage.api.RemoteXfQuotaService;
+import org.dromara.backstage.api.domain.bo.RemoteXfLimitedBo;
+import org.dromara.backstage.api.domain.bo.RemoteXfQuotaBo;
+import org.dromara.common.core.exception.ServiceException;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+
+/**
+ * 消费限次同步
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class SyncRemoteXfLimitedService {
+
+    @DubboReference
+    private final RemoteXfLimitedService limitedService;
+
+    /**
+     * 远程调用消费限次新增
+     */
+    public void addXfLimited(Object msg) throws Exception{
+        boolean flag = limitedService.insertByBo(JSONUtil.parseObj(msg).toBean(RemoteXfLimitedBo.class));
+        if (!flag){
+            throw new ServiceException("消费限次增失败");
+        }
+    }
+
+    /**
+     * 远程调用消费限次修改
+     */
+    public void editXfLimited(Object msg) throws Exception{
+        boolean flag = limitedService.updateByBo(JSONUtil.parseObj(msg).toBean(RemoteXfLimitedBo.class));
+        if (!flag){
+            throw new ServiceException("消费限次修改失败");
+        }
+    }
+
+    /**
+     * 远程调用消费限次删除
+     */
+    public void delXfLimited(Object msg) throws Exception{
+        Collection<Long> ids = JSONUtil.parseArray(msg).toBean(Collection.class);
+        boolean flag = limitedService.deleteWithValidByIds(ids,false);
+        if (!flag){
+            throw new ServiceException("消费限次删除失败");
+        }
+    }
+
+    /**
+     * 远程调用消费限次设备新增
+     */
+    public void addLimitedTerm(Object msg) throws ServiceException{
+        boolean flag = limitedService.addLimitedTerm(JSONUtil.parseArray(msg).toList(Long.class));
+        if (!flag){
+            throw new ServiceException("新增限次设备失败");
+        }
+    }
+    /**
+     * 远程调用消费限次设备删除
+     */
+    public void deleteLimitedTerm(Object msg) throws ServiceException{
+        boolean flag = limitedService.deleteLimitedTerm(JSONUtil.parseArray(msg).toList(Long.class));
+        if (!flag){
+            throw new ServiceException("删除限次设备失败");
+        }
+    }
+}

+ 76 - 0
ruoyi-server/ruoyi-server-base/src/main/java/org/dromara/server/base/service/yktOperation/SyncRemoteXfQuotaService.java

@@ -0,0 +1,76 @@
+package org.dromara.server.base.service.yktOperation;
+
+import cn.hutool.json.JSONUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.backstage.api.RemoteXfDiscountService;
+import org.dromara.backstage.api.RemoteXfQuotaService;
+import org.dromara.backstage.api.domain.bo.RemoteXfDiscountBo;
+import org.dromara.backstage.api.domain.bo.RemoteXfQuotaBo;
+import org.dromara.common.core.exception.ServiceException;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+
+/**
+ * 消费限额同步
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class SyncRemoteXfQuotaService {
+
+    @DubboReference
+    private final RemoteXfQuotaService quotaService;
+
+    /**
+     * 远程调用消费限额新增
+     */
+    public void addXfQuota(Object msg) throws Exception{
+        boolean flag = quotaService.insertByBo(JSONUtil.parseObj(msg).toBean(RemoteXfQuotaBo.class));
+        if (!flag){
+            throw new ServiceException("消费限额增失败");
+        }
+    }
+
+    /**
+     * 远程调用消费限额修改
+     */
+    public void editXfQuota(Object msg) throws Exception{
+        boolean flag = quotaService.updateByBo(JSONUtil.parseObj(msg).toBean(RemoteXfQuotaBo.class));
+        if (!flag){
+            throw new ServiceException("消费限额修改失败");
+        }
+    }
+
+    /**
+     * 远程调用消费限额删除
+     */
+    public void delXfQuota(Object msg) throws Exception{
+        Collection<Long> ids = JSONUtil.parseArray(msg).toBean(Collection.class);
+        boolean flag = quotaService.deleteWithValidByIds(ids,false);
+        if (!flag){
+            throw new ServiceException("消费限额删除失败");
+        }
+    }
+
+    /**
+     * 远程调用消费限额设备新增
+     */
+    public void addQuotaTerm(Object msg) throws ServiceException{
+        boolean flag = quotaService.addQuotaTerm(JSONUtil.parseArray(msg).toList(Long.class));
+        if (!flag){
+            throw new ServiceException("新增限额设备失败");
+        }
+    }
+    /**
+     * 远程调用消费限额设备删除
+     */
+    public void deleteQuotaTerm(Object msg) throws ServiceException{
+        boolean flag = quotaService.deleteQuotaTerm(JSONUtil.parseArray(msg).toList(Long.class));
+        if (!flag){
+            throw new ServiceException("删除限额设备失败");
+        }
+    }
+}

+ 2 - 0
ruoyi-server/ruoyi-server-base/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -17,3 +17,5 @@ org.dromara.server.base.service.yktOperation.SyncRemotePtUserAccountService
 org.dromara.server.base.service.yktOperation.SyncRemotePtCardCenterService
 org.dromara.server.base.service.yktOperation.SyncRemoteXfTermService
 org.dromara.server.base.service.yktOperation.SyncRemoteXfDiscountService
+org.dromara.server.base.service.yktOperation.SyncRemoteXfQuotaService
+org.dromara.server.base.service.yktOperation.SyncRemoteXfLimitedService

+ 6 - 1
ruoyi-server/ruoyi-server-common/src/main/java/org/dromara/server/common/constant/DefaultConstants.java

@@ -43,9 +43,14 @@ public interface DefaultConstants {
     String DATE_FORMAT = "yyyy-MM-dd";
 
     /**
-     * 时间格式
+     * 日期时间格式
      */
     String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
+    /**
+     * 时间格式
+     */
+    String TIME_FORMAT = "HH:mm:ss";
+
     /**
      * 消费机接收的时间格式
      */

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

@@ -81,8 +81,9 @@ public class ConsumptionBo {
      * 餐类Id
      */
     private Long mealType;
+
     /**
-     * 原始记录Id
+     * 消费明细记录Id
      */
     private String consumeId;
 
@@ -126,6 +127,11 @@ public class ConsumptionBo {
      * 防伪验证码(记录消费模式的中文信息)
      */
     private String digitalSign;
+
+    /**
+     * 原始消费记录Id
+     */
+    private String originalId;
     //endregion
 
     //region 错扣补款属性

+ 26 - 0
ruoyi-server/ruoyi-server-common/src/main/java/org/dromara/server/common/util/CardDateUtils.java

@@ -0,0 +1,26 @@
+package org.dromara.server.common.util;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+public class CardDateUtils {
+    static ThreadLocal<DateFormat> YYYYMMDD_SHORT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd", Locale.CHINA));
+    static ThreadLocal<DateFormat> YYYYMMDD_LONG = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA));
+    static ThreadLocal<DateFormat> YYYYMMDDHHMMSS_SHORT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA));
+    static ThreadLocal<DateFormat> YYYYMMDDHHMMSS_LONG = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA));
+    static ThreadLocal<DateFormat> YYYYMMDDHHMMSS_LONG_T = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.CHINA));
+
+    public static Date parseDateTime(String str) {
+        try {
+            if (str.contains("T")) {
+                return YYYYMMDDHHMMSS_LONG_T.get().parse(str);
+            }
+            return YYYYMMDDHHMMSS_LONG.get().parse(str);
+        } catch (ParseException e) {
+            return null;
+        }
+    }
+}

+ 189 - 5
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/BaseBusiness.java

@@ -1,20 +1,31 @@
 package org.dromara.server.consume.business;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjUtil;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.dromara.backstage.api.domain.vo.RemoteCardVo;
+import org.dromara.backstage.api.domain.vo.RemoteMealTypeVo;
 import org.dromara.backstage.api.domain.vo.RemoteUserAccountVo;
+import org.dromara.common.core.constant.ApiErrorTypeConstants;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.domain.model.ErrorInfo;
+import org.dromara.common.core.enums.BalanceUpdateEnum;
+import org.dromara.common.core.exception.consume.ConsumeException;
 import org.dromara.common.core.utils.RecordIdUtils;
+import org.dromara.server.common.constant.DefaultConstants;
 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 org.dromara.server.consume.service.IXfConsumeDetailOriginalService;
+import org.dromara.server.consume.domain.bo.*;
+import org.dromara.server.consume.domain.vo.*;
+import org.dromara.server.consume.service.*;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * name: BaseBusiness
@@ -32,6 +43,10 @@ import java.util.Date;
 @RequiredArgsConstructor
 public class BaseBusiness {
     private final IXfConsumeDetailOriginalService originalService;
+    private final IXfUserTotalService userTotalService;
+    private final IXfTermTotalService termTotalService;
+    private final IXfConsumeDetailService consumeDetailService;
+    private final IPtBagService bagService;
 
     /**
      * 生成原始消费记录
@@ -41,8 +56,11 @@ public class BaseBusiness {
      * @param originalVo 原始记录
      * @return 生成结果
      */
-    public R<ErrorInfo> createOriginalOrder(ConsumptionBo consumeBo, RemoteUserAccountVo accountVo, XfConsumeDetailOriginalVo originalVo) {
-        String originalId = RecordIdUtils.getRecordId(new Date(), consumeBo.getTermNo().shortValue(), consumeBo.getTermRecordId().shortValue(), accountVo.getUserNo().intValue(), 0);
+    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);
 
         XfConsumeDetailOriginalBo originalBo = new XfConsumeDetailOriginalBo();
         BeanUtil.copyProperties(consumeBo, originalBo);
@@ -61,4 +79,170 @@ public class BaseBusiness {
         }
         return R.fail();
     }
+
+    @Transactional(rollbackFor = ConsumeException.class)
+    public R<ErrorInfo> postConsumeRecord(ConsumptionBo bo, RemoteUserAccountVo userAccountVo, RemoteCardVo cardVo,
+                                          List<PtBagVo> bagVos, XfTermVo termVo, RemoteMealTypeVo mealTypeVo,
+                                          String remark) {
+        ErrorInfo errorInfo;
+        // 1.入消费明细表,根据消费金额与扣款方式及扣款钱包的余额,可能会从多个钱包扣钱,则对应的有多笔消费明细记录
+        AtomicReference<Boolean> result = new AtomicReference<>();
+        List<XfConsumeDetailVo> detailVos = new ArrayList<>();
+        for (PtBagVo bagVo : bagVos) {
+            XfConsumeDetailVo vo = createConsumeRecord(bo, userAccountVo, cardVo, bagVo, termVo, mealTypeVo, remark);
+            // 多钱包扣费时,只要有一个钱包入消费明细表失败,则都失败
+            if (ObjUtil.isEmpty(vo)) {
+                result.set(false);
+                break;
+            }
+            result.set(true);
+            detailVos.add(vo);
+        }
+        if (!result.get()) {
+            errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.EXCEPTION, "入消费明细表失败", "");
+            return R.fail(errorInfo);
+        }
+        // 2.更新人员日统计表
+        if (!createOrUpdateUserTotal(bo, userAccountVo, cardVo)) {
+            return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.EXCEPTION, "更新个人日统计表失败", ""));
+        }
+        // 3.更新设备日统计表
+        if (!createOrUpdateTermTotal(bo, termVo, mealTypeVo)) {
+            return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.EXCEPTION, "更新设备日统计表失败", ""));
+        }
+        // 4.更新钱包余额
+        if (!updateBagBalance(bagVos)) {
+            return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.EXCEPTION, "更新钱包余额表失败", ""));
+        }
+        // 5.更新记录入库成功状态
+        return R.ok();
+    }
+
+    /**
+     * 创建或更新人员日统计表
+     *
+     * @param bo        消费业务对象
+     * @param accountVo 人员账户视图
+     * @param cardVo    账户卡片视图
+     * @return 更新后的人员日统计表
+     */
+    public boolean createOrUpdateUserTotal(ConsumptionBo bo, RemoteUserAccountVo accountVo, RemoteCardVo cardVo) {
+        XfUserTotalBo xfUserTotalBo = new XfUserTotalBo();
+        xfUserTotalBo.setUserId(accountVo.getUserId());
+        xfUserTotalBo.setUserNumb(accountVo.getUserNumb());
+        xfUserTotalBo.setRealName(accountVo.getRealName());
+        xfUserTotalBo.setDeptName(accountVo.getDeptName());
+        xfUserTotalBo.setCardNo(cardVo.getCardNo());
+        xfUserTotalBo.setDateDay(DateUtil.format(new Date(), DefaultConstants.DATE_FORMAT));
+        xfUserTotalBo.setUseType(bo.getUseType().code());
+        xfUserTotalBo.setConsumeMoney(bo.getConsumeMoney());
+
+        XfUserTotalVo vo = userTotalService.createOrUpdateUserTotal(xfUserTotalBo);
+        return !ObjUtil.isEmpty(vo);
+    }
+
+    /**
+     * 创建或更新设备日统计
+     *
+     * @param bo         消费业务对象
+     * @param termVo     设备视图
+     * @param mealTypeVo 餐类视图
+     * @return 日统计视图
+     */
+    public boolean createOrUpdateTermTotal(ConsumptionBo bo, XfTermVo termVo, RemoteMealTypeVo mealTypeVo) {
+        XfTermTotalBo termTotalBo = new XfTermTotalBo();
+        BeanUtil.copyProperties(termVo, termTotalBo);
+        termTotalBo.setDateDay(DateUtil.format(new Date(), "yyyy-MM-dd"));
+        termTotalBo.setMealType(Long.valueOf(mealTypeVo.getTypeId()));
+        termTotalBo.setUseType(bo.getUseType().code());
+        switch (bo.getCreditType()) {
+            case TERM_CONSUME:
+                termTotalBo.setMealCount(1L);
+                termTotalBo.setMealAmount(bo.getConsumeMoney());
+                break;
+            case ERROR_FILL:
+                termTotalBo.setErrFillCount(1L);
+                termTotalBo.setErrFillMoney(bo.getOperatorMoney());
+                break;
+        }
+        XfTermTotalVo vo = termTotalService.createOrUpdateTermTotal(termTotalBo);
+        return !ObjUtil.isEmpty(vo);
+    }
+
+    /**
+     * 更新账户钱包余额
+     *
+     * @param bagVos 账户钱包视图
+     * @return 更新后的账户钱包
+     */
+    public boolean updateBagBalance(List<PtBagVo> bagVos) {
+        AtomicReference<Boolean> result = new AtomicReference<>();
+        bagVos.parallelStream().forEach(bagVo -> {
+            PtBagBo bagBo = new PtBagBo();
+            bagBo.setUserId(bagVo.getUserId());
+            bagBo.setBagId(bagVo.getBagId());
+            bagBo.setBagCode(bagVo.getBagCode());
+            bagBo.setReceiptMoney(bagVo.getReceiptMoney());
+            bagBo.setOperationMode(BalanceUpdateEnum.CONSUME);
+            PtBagVo vo = bagService.updateBalanceByBo(bagBo);
+            // 多钱包更新余额时,只要有一个钱包更新余额失败,则都失败
+            if (ObjUtil.isEmpty(vo)) {
+                result.set(false);
+            }
+            result.set(true);
+        });
+        return result.get();
+    }
+
+    /**
+     * 创建一条消费明细记录
+     *
+     * @param bo            消费业务对象
+     * @param userAccountVo 账户对象视图
+     * @param cardVo        账户卡片视图
+     * @param bagVo         账户钱包视图
+     * @param termVo        消费设备视图
+     * @param mealTypeVo    消费餐类视图
+     * @return 消费明细视图
+     */
+    private XfConsumeDetailVo createConsumeRecord(ConsumptionBo bo, RemoteUserAccountVo userAccountVo,
+                                                  RemoteCardVo cardVo, PtBagVo bagVo, XfTermVo termVo,
+                                                  RemoteMealTypeVo mealTypeVo, String remark) {
+        String recordId = RecordIdUtils.getRecordId(new Date(), Short.parseShort(bo.getTermNo().toString()),
+                                                    bo.getTermRecordId().intValue(),
+                                                    userAccountVo.getUserNo().intValue(),
+                                                    Integer.parseInt(bagVo.getBagCode()));
+
+        XfConsumeDetailBo consumeDetailBo = new XfConsumeDetailBo();
+        BeanUtil.copyProperties(bo, consumeDetailBo);
+        consumeDetailBo.setConsumeId(recordId);
+        // 设置消费账户信息
+        BeanUtil.copyProperties(userAccountVo, consumeDetailBo);
+        // 设置消费信息
+        consumeDetailBo.setConsumeDate(bo.getConsumeDate());
+        consumeDetailBo.setConsumeMoney(bo.getConsumeMoney());
+        consumeDetailBo.setConsumeBalance(bagVo.getBalance());
+        consumeDetailBo.setCardValue(bagVo.getBalance());
+        // 设置卡片信息
+        consumeDetailBo.setCardNo(cardVo.getCardNo());
+        consumeDetailBo.setFactoryId(cardVo.getFactoryId());
+        consumeDetailBo.setCardValue(bo.getBalance());
+        // 设置设备信息
+        BeanUtil.copyProperties(termVo, consumeDetailBo);
+        // 设置操作员信息
+        consumeDetailBo.setOperatorId(bo.getOperatorId());
+        consumeDetailBo.setOperatorName(bo.getOperatorName());
+        // 设置餐类信息
+        consumeDetailBo.setMealType(Long.valueOf(mealTypeVo.getTypeId()));
+        consumeDetailBo.setMealName(mealTypeVo.getMealName());
+        // 设置钱包信息
+        consumeDetailBo.setBagType(bagVo.getBagCode());
+        consumeDetailBo.setStatusFlag(bo.getStatusFlag().longValue());
+        // 该字段为补款记录对应的消费明细Id,以便追查消费记录是否有补扣以及对应的补扣记录
+        consumeDetailBo.setDetailId(bo.getConsumeId());
+        consumeDetailBo.setRemark(remark);
+
+        return consumeDetailService.createConsumeDetailRecord(consumeDetailBo);
+    }
+
 }

+ 212 - 49
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/CheckBusiness.java

@@ -7,18 +7,33 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.dromara.backstage.api.RemoteCardService;
+import org.dromara.backstage.api.RemoteMealTypeService;
+import org.dromara.backstage.api.RemoteOperatorService;
 import org.dromara.backstage.api.RemoteUserAccountService;
 import org.dromara.backstage.api.domain.vo.RemoteCardVo;
+import org.dromara.backstage.api.domain.vo.RemoteMealTypeVo;
+import org.dromara.backstage.api.domain.vo.RemoteOperatorVo;
 import org.dromara.backstage.api.domain.vo.RemoteUserAccountVo;
+import org.dromara.common.core.constant.ApiErrorTypeConstants;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.domain.model.ErrorInfo;
+import org.dromara.common.core.utils.RecordIdUtils;
 import org.dromara.server.common.domain.consume.bo.ConsumptionBo;
+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.service.IXfConsumeDetailOriginalService;
+import org.dromara.server.consume.service.IXfConsumeDetailService;
 import org.dromara.server.consume.service.IXfTermService;
 import org.jetbrains.annotations.NotNull;
 import org.springframework.stereotype.Service;
 
+import java.math.BigDecimal;
 import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -39,8 +54,15 @@ public class CheckBusiness {
     private final RemoteCardService remoteCardService;
     @DubboReference
     private final RemoteUserAccountService remoteUserAccountService;
+    @DubboReference
+    private final RemoteMealTypeService remoteMealTypeService;
+    @DubboReference
+    private final RemoteOperatorService remoteOperatorService;
 
     private final IXfTermService termService;
+    private final IXfConsumeDetailOriginalService consumeDetailOriginalService;
+    private final IXfConsumeDetailService consumeDetailService;
+    private final IPtBagService bagService;
 
     /**
      * 消费记录参数检检查
@@ -52,14 +74,15 @@ public class CheckBusiness {
      */
     public R<ErrorInfo> checkParam(ConsumptionBo bo) {
         ErrorInfo errorInfo;
-        //检查设备机号
+        // 检查设备机号
         if (ObjectUtil.isEmpty(bo.getTermNo()) || bo.getTermNo() == 0) {
             errorInfo = new ErrorInfo(1, "", "设备机号不正确", "设备机号必须大于零!");
             return R.fail(errorInfo);
         }
-        //检查交易人员标识
+        // 检查交易人员标识
         if (bo.getCardNo() <= 0 && bo.getFactoryId() == 0 && bo.getUserNo() <= 0 && StrUtil.isEmpty(bo.getUserNumb())) {
-            errorInfo = new ErrorInfo(1, "", "交易人员标识不满足", "必须提供 [CardNo | FactoryId | userNo | userNumb] 中至少1项来标识交易用户");
+            errorInfo = new ErrorInfo(1, "", "交易人员标识不满足",
+                                      "必须提供 [CardNo | FactoryId | userNo | userNumb] 中至少1项来标识交易用户");
             return R.fail(errorInfo);
         }
         return R.ok();
@@ -70,51 +93,73 @@ public class CheckBusiness {
      *
      * @param bo            消费记录
      * @param userAccountVo 消费账户
-     * @param userCardVo  消费卡片
-     * @param useTermVo 消费设备
+     * @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,
+                                  XfTermVo useTermVo) {
+        long termNo = ObjectUtil.isEmpty(bo.getTermNo()) ? 0 : bo.getTermNo();
         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();
-        XfTermVo termVo = termService.queryVoOneByNo(termNo);
-        if(ObjectUtil.isEmpty(termVo)){
-            ErrorInfo errorInfo = new ErrorInfo(400, "", "设备不存在", MessageFormat.format("设备机号为[{0}]的设备不存在,不允许交易", termNo));
-
-            return R.fail(errorInfo);
+        if (termNo > 0) {
+            R<ErrorInfo> result = checkTerm(bo, useTermVo);
+            if (R.isError(result)) {
+                return result;
+            }
         }
-       BeanUtil.copyProperties(termVo,useTermVo);
-        //卡流水号检查
+        // 卡流水号检查
         if (cardNo > 0) {
-            return checkCardNo(bo,userAccountVo ,userCardVo );
+            return checkCardNo(bo, userAccountVo, userCardVo);
         }
-        //物理卡号检查
+        // 物理卡号检查
         if (factoryId > 0) {
-            return checkFactoryId(bo,userAccountVo ,userCardVo);
+            return checkFactoryId(bo, userAccountVo, userCardVo);
         }
-        //用户流水号检查
+        // 用户流水号检查
         if (userNo > 0) {
-            return checkUserNo(bo,userAccountVo ,userCardVo);
+            return checkUserNo(bo, userAccountVo, userCardVo);
         }
-        //人员编号检查
+        // 人员编号检查
         if (StrUtil.isNotEmpty(userNumb)) {
-            return checkUserNumb(bo,userAccountVo ,userCardVo);
+            return checkUserNumb(bo, userAccountVo, userCardVo);
         }
         return R.ok();
     }
 
+    /**
+     * 消费设备检查
+     *
+     * @param bo        消费记录
+     * @param useTermVo 消费设备
+     * @return 检查结果
+     */
+    public R<ErrorInfo> checkTerm(ConsumptionBo bo, XfTermVo useTermVo) {
+        long termNo = bo.getTermNo();
+        XfTermVo termVo = termService.queryVoOneByNo(termNo);
+        if (ObjectUtil.isEmpty(termVo)) {
+            ErrorInfo errorInfo = new ErrorInfo(400, "", "设备不存在",
+                                                MessageFormat.format("机号为[{0}]的设备不存在,不允许交易", termNo));
+
+            return R.fail(errorInfo);
+        }
+        BeanUtil.copyProperties(termVo, useTermVo);
+        return R.ok();
+    }
+
     /**
      * 消费逻辑检查,检查是否能消费
-     * @param bo 消费记录
+     *
+     * @param bo            消费记录
      * @param userAccountVo 消费账户
-     * @param userCardVo 消费卡片
-     * @param useTermVo 消费设备
+     * @param userCardVo    消费卡片
+     * @param useTermVo     消费设备
      * @return 验证结果
      */
-    public R<ErrorInfo> checkConsume(ConsumptionBo bo, RemoteUserAccountVo userAccountVo, RemoteCardVo userCardVo, XfTermVo useTermVo){
+    public R<ErrorInfo> checkConsume(ConsumptionBo bo, RemoteUserAccountVo userAccountVo, RemoteCardVo userCardVo,
+                                     XfTermVo useTermVo) {
         // TODO 2024-11-03 14:36:42 luoyibo 1.获取传入的参数
         // TODO 2024-11-03 14:36:42 luoyibo 2.卡有效性验证
         // TODO 2024-11-03 14:39:21 luoyibo 3.消费账户状态验证
@@ -123,11 +168,38 @@ public class CheckBusiness {
         // TODO 2024-11-03 14:45:18 luoyibo 6.根据消费机的消费模式验证余额
         return R.ok();
     }
-    /**
-     * 验证后设置人员信息
-     * @param bo 消费记录
-     * @param accountVo 人员账户信息
-     */
+
+    public R<ErrorInfo> checkBill(ConsumptionBo bo, RemoteUserAccountVo userAccountVo, RemoteCardVo userCardVo,
+                                  XfTermVo useTermVo, List<PtBagVo> bagVoList, RemoteMealTypeVo mealTypeVo,
+                                  RemoteOperatorVo operatorVo) {
+        R<ErrorInfo> result = checkCardNo(bo, userAccountVo, userCardVo);
+        if (R.isError(result)) {
+            return result;
+        }
+        result = checkOriginalRecord(bo);
+        if (R.isError(result)) {
+            return result;
+        }
+        // 获取餐类信息
+        RemoteMealTypeVo mealType = remoteMealTypeService.queryMealTypeVoByTime(bo.getConsumeDate());
+        if (ObjectUtil.isEmpty(mealType)) {
+            mealType.setTypeId("0");
+            mealType.setMealName("未知");
+        }
+        BeanUtil.copyProperties(mealType, mealTypeVo);
+        // 获取扣费钱包
+        List<PtBagVo> bagVos = new ArrayList<>();
+        result = checkDeductionBag(bo, userAccountVo, useTermVo, bagVos);
+        if (R.isError(result)) {
+            return result;
+        }
+        bagVoList.addAll(bagVos);
+        // 获取营业员信息
+        BeanUtil.copyProperties(getOperatorVo(bo), operatorVo);
+
+        return R.ok();
+    }
+
     private void setUserInfo(ConsumptionBo bo, RemoteUserAccountVo accountVo) {
         bo.setRealName(StrUtil.isEmpty(accountVo.getRealName()) ? "----" : accountVo.getRealName());
         bo.setUserNo(accountVo.getUserNo());
@@ -140,25 +212,28 @@ public class CheckBusiness {
         ErrorInfo errorInfo;
         RemoteCardVo cardVo = remoteCardService.queryCardByCardNo(cardNo);
         if (ObjectUtil.isEmpty(cardVo)) {
-            errorInfo = new ErrorInfo(400, "", "卡片不存在", MessageFormat.format("流水号为[{0}]的卡片不存在,不允许交易", cardNo));
+            errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片不存在",
+                                      MessageFormat.format("流水号为[{0}]的卡片不存在,不允许交易", cardNo));
 
             return R.fail(errorInfo);
         }
         if (!"1".equals(cardVo.getStatus())) {
-            errorInfo = new ErrorInfo(400, "", "卡片状态不正确", MessageFormat.format("流水号为[{0}]的卡片状态不正确,不允许交易", cardNo));
+            errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片状态不正确",
+                                      MessageFormat.format("流水号为[{0}]的卡片状态不正确,不允许交易", cardNo));
 
             return R.fail(errorInfo);
         }
         if (!Objects.equals(cardVo.getFactoryId(), bo.getFactoryId())) {
-            errorInfo = new ErrorInfo(400, "", "卡片不正确", "物理卡不一致,不允许交易");
+            errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片不正确",
+                                      "物理卡号不一致,不允许交易");
 
             return R.fail(errorInfo);
         }
         RemoteUserAccountVo accountVo = remoteUserAccountService.getUserAccountVoById(cardVo.getUserId());
         setUserInfo(bo, accountVo);
         bo.setFactoryId(cardVo.getFactoryId());
-        BeanUtil.copyProperties(accountVo,userAccountVo);
-        BeanUtil.copyProperties(cardVo,userCardVo);
+        BeanUtil.copyProperties(accountVo, userAccountVo);
+        BeanUtil.copyProperties(cardVo, userCardVo);
         return R.ok();
     }
 
@@ -168,20 +243,23 @@ public class CheckBusiness {
         Long factoryId = bo.getFactoryId();
         RemoteCardVo cardVo = remoteCardService.queryCardByFactoryId(factoryId);
         if (ObjectUtil.isEmpty(cardVo)) {
-            errorInfo = new ErrorInfo(400, "", "卡片不存在", MessageFormat.format("物理卡号为[{0}]的卡片不存在,不允许交易", factoryId));
+            errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片不存在",
+                                      MessageFormat.format("物理卡号为[{0}]的卡片不存在,不允许交易", factoryId));
 
             return R.fail(errorInfo);
         }
         if (!"1".equals(cardVo.getStatus())) {
-            errorInfo = new ErrorInfo(400, "", "卡片状态不正确", MessageFormat.format("物理卡号为[{0}]的卡片卡片状态不正确,不允许交易", factoryId));
+            errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片状态不正确",
+                                      MessageFormat.format("物理卡号为[{0}]的卡片卡片状态不正确,不允许交易",
+                                                           factoryId));
 
             return R.fail(errorInfo);
         }
         RemoteUserAccountVo accountVo = remoteUserAccountService.getUserAccountVoById(cardVo.getUserId());
         setUserInfo(bo, accountVo);
         bo.setCardNo(cardVo.getCardNo());
-        BeanUtil.copyProperties(accountVo,userAccountVo);
-        BeanUtil.copyProperties(cardVo,userCardVo);
+        BeanUtil.copyProperties(accountVo, userAccountVo);
+        BeanUtil.copyProperties(cardVo, userCardVo);
         return R.ok();
     }
 
@@ -191,26 +269,29 @@ public class CheckBusiness {
         Long userNo = bo.getUserNo();
         RemoteUserAccountVo accountVo = remoteUserAccountService.getUserAccountVoByUserNo(userNo);
         if (ObjectUtil.isEmpty(accountVo)) {
-            errorInfo = new ErrorInfo(400, "", "人员不存在", MessageFormat.format("流水号为[{0}]的人员不存在,不允许交易", userNo));
+            errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "人员不存在",
+                                      MessageFormat.format("流水号为[{0}]的人员不存在,不允许交易", userNo));
 
             return R.fail(errorInfo);
         }
         RemoteCardVo cardVo = remoteCardService.queryMainCardByUserId(accountVo.getUserId());
         if (ObjectUtil.isEmpty(cardVo)) {
-            errorInfo = new ErrorInfo(400, "", "卡片不存在", MessageFormat.format("流水号为[{0}]的人员卡片不存在,不允许交易", userNo));
+            errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片不存在",
+                                      MessageFormat.format("流水号为[{0}]的人员卡片不存在,不允许交易", userNo));
 
             return R.fail(errorInfo);
         }
         if (!"1".equals(cardVo.getStatus())) {
-            errorInfo = new ErrorInfo(400, "", "卡片状态不正确", MessageFormat.format("流水号为[{0}]的人员卡片状态不正确,不允许交易", userNo));
+            errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片状态不正确",
+                                      MessageFormat.format("流水号为[{0}]的人员卡片状态不正确,不允许交易", userNo));
 
             return R.fail(errorInfo);
         }
         setUserInfo(bo, accountVo);
         bo.setCardNo(cardVo.getCardNo());
         bo.setFactoryId(cardVo.getFactoryId());
-        BeanUtil.copyProperties(accountVo,userAccountVo);
-        BeanUtil.copyProperties(cardVo,userCardVo);
+        BeanUtil.copyProperties(accountVo, userAccountVo);
+        BeanUtil.copyProperties(cardVo, userCardVo);
         return R.ok();
     }
 
@@ -221,18 +302,21 @@ public class CheckBusiness {
         RemoteUserAccountVo accountVo = remoteUserAccountService.getUserAccountVoByUserNumb(userNumb);
 
         if (ObjectUtil.isEmpty(accountVo)) {
-            errorInfo = new ErrorInfo(400, "", "人员不存在", MessageFormat.format("编号为[{0}]的人员不存在,不允许交易", userNumb));
+            errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "人员不存在",
+                                      MessageFormat.format("编号为[{0}]的人员不存在,不允许交易", userNumb));
 
             return R.fail(errorInfo);
         }
         RemoteCardVo cardVo = remoteCardService.queryMainCardByUserId(accountVo.getUserId());
         if (ObjectUtil.isEmpty(cardVo)) {
-            errorInfo = new ErrorInfo(400, "", "卡片不存在", MessageFormat.format("编号为[{0}]的人员卡片不存在,不允许交易", userNumb));
+            errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片不存在",
+                                      MessageFormat.format("编号为[{0}]的人员卡片不存在,不允许交易", userNumb));
 
             return R.fail(errorInfo);
         }
         if (!"1".equals(cardVo.getStatus())) {
-            errorInfo = new ErrorInfo(400, "", "卡片状态不正确", MessageFormat.format("编号为[{0}]的人员卡片状态不正确,不允许交易", userNumb));
+            errorInfo = new ErrorInfo(400, ApiErrorTypeConstants.PARAM_ERROR, "卡片状态不正确",
+                                      MessageFormat.format("编号为[{0}]的人员卡片状态不正确,不允许交易", userNumb));
 
             return R.fail(errorInfo);
         }
@@ -240,9 +324,88 @@ public class CheckBusiness {
         setUserInfo(bo, accountVo);
         bo.setCardNo(cardVo.getCardNo());
         bo.setFactoryId(cardVo.getFactoryId());
-        BeanUtil.copyProperties(accountVo,userAccountVo);
-        BeanUtil.copyProperties(cardVo,userCardVo);
+        BeanUtil.copyProperties(accountVo, userAccountVo);
+        BeanUtil.copyProperties(cardVo, userCardVo);
+
+        return R.ok();
+    }
 
+    private R<ErrorInfo> checkOriginalRecord(ConsumptionBo bo) {
+        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 正常应该是进行记录类型和脱机消费的检查,此处先按错误处理
+            return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.NOT_FOUND, "原始消费记录不存在",
+                                        MessageFormat.format("标识为[{0}]的原始消费记录不存在", bo.getRecordId())));
+        }
+        XfConsumeDetailVo consumeDetailVo = consumeDetailService.queryVoByOriginalId(originalId);
+        if (ObjectUtil.isNotEmpty(consumeDetailVo)) {
+            // 认为是重复上传,不再入账
+            return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.RECORD_IS_EXISTS, "原始消费记录已处理",
+                                        MessageFormat.format("标识为[{0}]的原始消费记录已处理", bo.getRecordId())));
+        }
+        bo.setOriginalId(originalId);
         return R.ok();
     }
+
+    public R<ErrorInfo> checkDeductionBag(ConsumptionBo bo, RemoteUserAccountVo userAccountVo, XfTermVo termVo,
+                                          List<PtBagVo> bagVos) {
+        // 设备的扣费钱包串
+        String consumeType = termVo.getConsumeType();
+        // 分解扣费钱包
+        List<String> bagCodes = StrUtil.split(consumeType, ",");
+        Long userId = userAccountVo.getUserId();
+        // 可能会在处理过程中更改实际的消费金额,因此先取出来
+        BigDecimal consumeMoney = bo.getConsumeMoney();
+        BigDecimal doMoney = bo.getConsumeMoney();
+        // 计算后实际需要扣费的钱包,会小于或等于指定的扣费钱包数
+        List<PtBagVo> doBagVos = new ArrayList<>();
+        BigDecimal totalBalance = BigDecimal.ZERO;
+        for (String bagCode : bagCodes) {
+            // 1.查询对应的钱包
+            PtBagVo bagVo = bagService.queryByUserBagCode(userId, bagCode);
+            if (ObjectUtil.isEmpty(bagVo)) {
+                log.warn("人员Id:[{}]没有代码:[{}]的钱包,无法从此钱包扣费", userId, bagCode);
+                continue;
+            }
+            // 2.比较扣费金额
+            BigDecimal balance = bagVo.getBalance();
+            if (balance.compareTo(BigDecimal.ZERO) > 0) {
+                totalBalance = totalBalance.add(balance);
+                if (balance.compareTo(doMoney) >= 0) {
+                    // 如果钱包金额>扣费金额,设置扣费结果并中断循环
+                    bagVo.setReceiptMoney(doMoney);
+                    bagVo.setBalance(balance.subtract(doMoney));
+                    doMoney = BigDecimal.ZERO;
+                    doBagVos.add(bagVo);
+                    break;
+                } else {
+                    // 将钱包扣费为0,剩余待扣金额=消费金额-原钱包余额
+                    bagVo.setReceiptMoney(balance);
+                    bagVo.setBalance(BigDecimal.ZERO);
+                    doMoney = doMoney.subtract(balance);
+                    doBagVos.add(bagVo);
+                }
+            }
+        }
+        if (doMoney.compareTo(BigDecimal.ZERO) > 0) {
+            // 循环扣费后,如果还有未扣的金额,表示钱包余额不足,不允许交易
+            return R.fail(new ErrorInfo(400, ApiErrorTypeConstants.CONSUME_CHECK_FAIL, "钱包余额不足",
+                                        MessageFormat.format("余额不足,余额[{0}],消费金额[{1}]", totalBalance,
+                                                             consumeMoney)));
+        }
+        bagVos.addAll(doBagVos);
+        return R.ok();
+    }
+
+    private RemoteOperatorVo getOperatorVo(ConsumptionBo bo) {
+        RemoteOperatorVo operatorVo = remoteOperatorService.getVoById(bo.getOperatorId());
+        if (ObjectUtil.isEmpty(operatorVo)) {
+            operatorVo = new RemoteOperatorVo();
+            operatorVo.setOperatorId(bo.getOperatorId());
+            operatorVo.setOperatorName(bo.getOperatorName());
+        }
+        return operatorVo;
+    }
 }

+ 43 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/ConsumeBusiness.java

@@ -4,15 +4,21 @@ import cn.hutool.json.JSONUtil;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.dromara.backstage.api.domain.vo.RemoteCardVo;
+import org.dromara.backstage.api.domain.vo.RemoteMealTypeVo;
+import org.dromara.backstage.api.domain.vo.RemoteOperatorVo;
 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;
 import org.springframework.stereotype.Service;
 
 import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * name: ConsumeBusiness
@@ -30,6 +36,7 @@ import java.text.MessageFormat;
 public class ConsumeBusiness {
     private final CheckBusiness checkBusiness;
     private final BaseBusiness baseBusiness;
+    private final RecordConvertStrategyContent recordConvertStrategy;
 
     /**
      * 请求消费
@@ -82,6 +89,42 @@ public class ConsumeBusiness {
             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;
+        }
+
+        RemoteUserAccountVo userAccountVo = new RemoteUserAccountVo();
+        RemoteCardVo userCardVo = new RemoteCardVo();
+        List<PtBagVo> bagVos = new ArrayList<>();
+        RemoteMealTypeVo mealTypeVo = new RemoteMealTypeVo();
+        RemoteOperatorVo operatorVo = new RemoteOperatorVo();
+        result = checkBusiness.checkBill(bo, userAccountVo, userCardVo, termVo, bagVos, mealTypeVo,operatorVo);
+        if (R.isError(result)) {
+            log.error("[上传交易]-[交易账单处理失败]-[{}]", result.getData());
+            return result;
+        }
+        result = baseBusiness.postConsumeRecord(bo, userAccountVo, userCardVo, bagVos, termVo, mealTypeVo, "");
+        if (R.isError(result)) {
+            log.error("[上传交易]-[交易入库失败]-[{}]", result.getData());
+            return result;
+        }
+        return R.ok();
+    }
+
+    public R<ErrorInfo> fullOrder(ConsumptionBo bo) {
+        R<ErrorInfo> result = this.createOrder(bo);
+        if(!R.isSuccess(result)) {
+            log.error("[请求交易]-[请求交易处理失败]-[{}]", JSONUtil.toJsonStr(result.getData()));
+            return result;
+        }
+        result = this.postOrder(bo);
+        if(!R.isSuccess(result)) {
+            log.error("[交易上传]-[交易上传处理失败]-[{}]", JSONUtil.toJsonStr(result.getData()));
+            return result;
+        }
         return R.ok();
     }
 }

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

@@ -5,6 +5,8 @@ import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.domain.model.ErrorInfo;
 import org.dromara.common.core.domain.model.ErrorResult;
+import org.dromara.common.core.enums.CreditTypeEnum;
+import org.dromara.common.core.enums.SystemUseTypeEnum;
 import org.dromara.server.common.domain.consume.bo.ConsumptionBo;
 import org.dromara.server.consume.business.ConsumeBusiness;
 import org.dromara.server.consume.convert.strategy.RecordConvertStrategyContent;
@@ -39,34 +41,46 @@ public class ConsumeController {
 
     @PostMapping("/ConsumeOriginal")
     public Object consumeOriginal(@RequestBody Object record) {
-        return doRecordData(record,"requestConsume");
+        return doRecordData(record, "requestConsume");
     }
+
     @PostMapping("/Consume")
     public Object uploadRecord(@RequestBody Object record) {
-        return doRecordData(record,"uploadRecord");
+        return doRecordData(record, "uploadRecord");
+    }
+
+    @PostMapping("/Consume/all")
+    public Object consumeAll(@RequestBody Object record) {
+        return doRecordData(record, "fullRecord");
     }
 
-    private Object doRecordData(Object record,String type){
-        if(ObjectUtil.length(record)==0){
+    private Object doRecordData(Object record, String type) {
+        if (ObjectUtil.length(record) == 0) {
             ErrorResult result = ErrorResult.instance(HttpStatus.FORBIDDEN, "处理失败", 1, "PARAM_ERROR", "参数错误",
-                "json字符串无法还原为对象,可能原因是格式不正确。");
+                                                      "json字符串无法还原为对象,可能原因是格式不正确。");
             return new ResponseEntity<Object>(result, null, HttpStatus.FORBIDDEN);
         }
         ConsumptionBo bo = recordConvertStrategy.convert(record, "YC");
+        bo.setStatusFlag(4);
+        bo.setUseType(SystemUseTypeEnum.CONSUME);
+        bo.setCreditType(CreditTypeEnum.TERM_CONSUME);
+
         R<ErrorInfo> errorInfo;
-        if(Objects.equals(type, "requestConsume")) {
-             errorInfo = consumeBusiness.createOrder(bo);
-        } else {
+        if (Objects.equals(type, "requestConsume")) {
+            errorInfo = consumeBusiness.createOrder(bo);
+        } else if (Objects.equals(type, "uploadRecord")) {
             errorInfo = consumeBusiness.postOrder(bo);
+        } else {
+            errorInfo = consumeBusiness.fullOrder(bo);
         }
-        if(!R.isSuccess(errorInfo)){
+        if (!R.isSuccess(errorInfo)) {
             ErrorResult result = new ErrorResult();
             result.setStatusCode(HttpStatus.NOT_FOUND.value());
             result.setMessage(errorInfo.getMsg());
             result.getErrors().add(errorInfo.getData());
             return new ResponseEntity<Object>(result, null, HttpStatus.NOT_FOUND);
         }
-        return recordConvertStrategy.reConvert(bo,"YC");
+        return recordConvertStrategy.reConvert(bo, "YC");
     }
 
 }

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

@@ -7,6 +7,7 @@ import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONObject;
 import org.dromara.server.common.constant.DefaultConstants;
 import org.dromara.server.common.domain.consume.bo.ConsumptionBo;
+import org.dromara.server.common.util.CardDateUtils;
 import org.dromara.server.consume.constant.TermSupplierConstants;
 import org.dromara.server.consume.convert.strategy.IRecordConvertStrategy;
 import org.dromara.server.consume.domain.YcConsumeInfo;
@@ -41,7 +42,7 @@ public class YcRecordConvertStrategyImpl implements IRecordConvertStrategy {
             consumptionBo.setConsumeMoney(ObjectUtil.isEmpty(yc.getConsumeValue()) ? BigDecimal.ZERO : yc.getConsumeValue().setScale(2, RoundingMode.HALF_UP));
             String tempDate = yc.getConsumeDate();
             if(StrUtil.isNotEmpty(tempDate)){
-                consumptionBo.setConsumeDate(DateUtil.parse(tempDate, DefaultConstants.DATE_TIME_FORMAT));
+                consumptionBo.setConsumeDate(CardDateUtils.parseDateTime(tempDate));
             }
             consumptionBo.setTermNo(ObjectUtil.isEmpty(yc.getTermID()) ? 0 : yc.getTermID());
             consumptionBo.setTermRecordId(ObjectUtil.isEmpty(yc.getTermRecordID()) ? 0 : yc.getTermRecordID());

+ 80 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/domain/PtBag.java

@@ -0,0 +1,80 @@
+package org.dromara.server.consume.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+
+import java.io.Serial;
+import java.math.BigDecimal;
+
+/**
+ * 账户钱包对象 t_pt_bag
+ *
+ * @author LionLi
+ * @date 2024-08-06
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("t_pt_bag")
+public class PtBag extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 钱包Id,主键
+     */
+    @TableId(value = "bag_id")
+    private Long bagId;
+
+    /**
+     * 所属账户Id
+     */
+    private Long userId;
+
+    /**
+     * 钱包代码,见CARDBAGTYPE数据字典类别
+     */
+    private String bagCode;
+
+    /**
+     * 钱包余额
+     */
+    private BigDecimal balance;
+
+    /**
+     * 钱包加密余额
+     */
+    private String encryptBalance;
+
+    /**
+     * 消费总金额
+     */
+    private BigDecimal consumeTotal;
+
+    /**
+     * 消费总次数
+     */
+    private Long consumeCount;
+
+    /**
+     * 充值总金额
+     */
+    private BigDecimal rechargeTotal;
+
+    /**
+     * 充值总次数
+     */
+    private Long rechargeCount;
+
+    /**
+     * 删除标志(0-未删除 2-已删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+
+}

+ 92 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/domain/bo/PtBagBo.java

@@ -0,0 +1,92 @@
+package org.dromara.server.consume.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.core.enums.BalanceUpdateEnum;
+import org.dromara.common.core.enums.CreditTypeEnum;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.server.consume.domain.PtBag;
+
+import java.math.BigDecimal;
+
+/**
+ * 账户钱包业务对象 t_pt_bag
+ *
+ * @author LionLi
+ * @date 2024-08-06
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = PtBag.class, reverseConvertGenerate = false)
+public class PtBagBo extends BaseEntity {
+
+    //region 这是钱包数据库对应的数据
+    /**
+     * 钱包Id,主键
+     */
+    private Long bagId;
+
+    /**
+     * 所属账户Id
+     */
+    private Long userId;
+
+    /**
+     * 钱包代码,见CARDBAGTYPE数据字典类别
+     */
+    @NotBlank(message = "钱包代码", groups = { AddGroup.class, EditGroup.class })
+    private String bagCode;
+
+    /**
+     * 钱包余额
+     */
+    private BigDecimal balance;
+
+    /**
+     * 钱包加密余额
+     */
+    private String encryptBalance;
+
+    /**
+     * 消费总金额
+     */
+    private BigDecimal consumeTotal;
+
+    /**
+     * 消费总次数
+     */
+    private Long consumeCount;
+
+    /**
+     * 充值总金额
+     */
+    private BigDecimal rechargeTotal;
+
+
+    /**
+     * 充值总次数
+     */
+    private Long rechargeCount;
+    //endregion
+
+    //region 以下属性是钱包余额变更业务所需要的
+    /**
+     * 充值/退款/重置余额时的操作金额
+     */
+   private BigDecimal receiptMoney;
+
+    /**
+     * 卡余的操作类型
+     */
+   private BalanceUpdateEnum operationMode;
+
+    /**
+     * 交易类型
+     */
+   private CreditTypeEnum creditType;
+    //endregion
+}

+ 82 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/domain/vo/PtBagVo.java

@@ -0,0 +1,82 @@
+package org.dromara.server.consume.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.server.consume.domain.PtBag;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+
+/**
+ * 账户钱包视图对象 t_pt_bag
+ *
+ * @author LionLi
+ * @date 2024-08-06
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = PtBag.class)
+public class PtBagVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 钱包Id,主键
+     */
+    private Long bagId;
+
+    /**
+     * 所属账户Id
+     */
+    private Long userId;
+
+    /**
+     * 钱包代码,见CARDBAGTYPE数据字典类别
+     */
+    private String bagCode;
+
+    /**
+     * 钱包余额
+     */
+    private BigDecimal balance;
+
+    /**
+     * 钱包加密余额
+     */
+    private String encryptBalance;
+
+    /**
+     * 消费总金额
+     */
+    private BigDecimal consumeTotal;
+
+    /**
+     * 消费总次数
+     */
+    private Long consumeCount;
+
+    /**
+     * 充值总金额
+     */
+    private BigDecimal rechargeTotal;
+
+    /**
+     * 充值总次数
+     */
+    private Long rechargeCount;
+
+    /**
+     * 钱包名称
+     */
+    private String bagName;
+
+    /**
+     * 充值/退款/消费/错扣补款/重置余额等的操作金额
+     */
+    private BigDecimal receiptMoney;
+
+}

+ 15 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/mapper/PtBagMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.server.consume.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.server.consume.domain.PtBag;
+import org.dromara.server.consume.domain.vo.PtBagVo;
+
+/**
+ * 账户钱包Mapper接口
+ *
+ * @author LionLi
+ * @date 2024-08-06
+ */
+public interface PtBagMapper extends BaseMapperPlus<PtBag, PtBagVo> {
+
+}

+ 110 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/IPtBagService.java

@@ -0,0 +1,110 @@
+package org.dromara.server.consume.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.server.consume.domain.PtBag;
+import org.dromara.server.consume.domain.bo.PtBagBo;
+import org.dromara.server.consume.domain.vo.PtBagVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 账户钱包Service接口
+ *
+ * @author LionLi
+ * @date 2024-08-06
+ */
+public interface IPtBagService {
+
+    /**
+     * 查询账户钱包
+     *
+     * @param bagId 主键
+     * @return 账户钱包
+     */
+    PtBagVo queryById(Long bagId);
+
+    /**
+     * 分页查询账户钱包列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 账户钱包分页列表
+     */
+    TableDataInfo<PtBagVo> queryPageList(PtBagBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的账户钱包列表
+     *
+     * @param bo 查询条件
+     * @return 账户钱包列表
+     */
+    List<PtBagVo> queryList(PtBagBo bo);
+
+    /**
+     * 新增账户钱包
+     *
+     * @param bo 账户钱包
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(PtBagBo bo);
+
+    /**
+     * 修改账户钱包
+     *
+     * @param bo 账户钱包
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(PtBagBo bo);
+
+    /**
+     * 校验并批量删除账户钱包信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 根据Id获取账户钱包余额
+     *
+     * @param userIds  人员Id串,英文逗号分隔
+     * @return 余额串,英文逗号分隔
+     */
+    String selectAccountBalanceByIds(String userIds);
+
+    /**
+     * 初始化账户钱包
+     *
+     * @param userId 账户Id
+     * @return 是否初始化成功
+     */
+    Boolean initAccountBag(Long userId);
+
+    /**
+     * 更新钱包
+     *
+     * @param bo 账户钱包
+     * @return 更新后的钱包
+     */
+    PtBagVo updateBalanceByBo(PtBagBo bo);
+    /**
+     * 钱包更新前的验证
+     *
+     * @param bo 账户钱包
+     * @return 验证通过后的账户钱包
+     */
+    PtBag verification(PtBagBo bo);
+    /**
+     * 查询账户钱包
+     *
+     * @param userId 用户Id
+     * @param bagCode 敖包代码
+     * @return 账户钱包
+     */
+    PtBagVo queryByUserBagCode(Long userId,String bagCode);
+
+
+}

+ 4 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/IXfConsumeDetailService.java

@@ -21,6 +21,7 @@ public interface IXfConsumeDetailService {
      * @return 消费明细
      */
     XfConsumeDetailVo queryById(String consumeId);
+
     /**
      * 查询符合条件的消费明细列表
      *
@@ -61,4 +62,7 @@ public interface IXfConsumeDetailService {
      */
     Boolean updateByBoId(XfConsumeDetailBo bo);
 
+    XfConsumeDetailVo queryVoByBo(XfConsumeDetailBo bo);
+
+    XfConsumeDetailVo queryVoByOriginalId(String originalId);
 }

+ 380 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/impl/PtBagServiceImpl.java

@@ -0,0 +1,380 @@
+package org.dromara.server.consume.service.impl;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.exception.consume.BagException;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.encrypt.utils.YcEncryptUtil;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.server.consume.domain.PtBag;
+import org.dromara.server.consume.domain.bo.PtBagBo;
+import org.dromara.server.consume.domain.vo.PtBagVo;
+import org.dromara.server.consume.mapper.PtBagMapper;
+import org.dromara.server.consume.service.IPtBagService;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+/**
+ * 账户钱包Service业务层处理
+ *
+ * @author LionLi
+ * @date 2024-08-06
+ */
+@RequiredArgsConstructor
+@Service
+public class PtBagServiceImpl implements IPtBagService {
+
+    private final PtBagMapper baseMapper;
+
+    /**
+     * 查询账户钱包
+     *
+     * @param bagId 主键
+     * @return 账户钱包
+     */
+    @Override
+    public PtBagVo queryById(Long bagId) {
+        return baseMapper.selectVoById(bagId);
+    }
+    /**
+     * 查询账户钱包
+     *
+     * @param userId 用户Id
+     * @param bagCode 敖包代码
+     * @return 账户钱包
+     */
+    @Override
+    public PtBagVo queryByUserBagCode(Long userId, String bagCode) {
+        PtBagVo vo = baseMapper.selectVoOne(Wrappers.<PtBag>lambdaQuery()
+            .eq(PtBag::getUserId, userId)
+            .eq(PtBag::getBagCode,bagCode),PtBagVo.class);
+        if (ObjectUtil.isNull(vo)) {
+            return null;
+        }
+        return vo;
+    }
+
+    /**
+     * 分页查询账户钱包列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 账户钱包分页列表
+     */
+    @Override
+    public TableDataInfo<PtBagVo> queryPageList(PtBagBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<PtBag> lqw = buildQueryWrapper(bo);
+        Page<PtBagVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的账户钱包列表
+     *
+     * @param bo 查询条件
+     * @return 账户钱包列表
+     */
+    @Override
+    public List<PtBagVo> queryList(PtBagBo bo) {
+        LambdaQueryWrapper<PtBag> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<PtBag> buildQueryWrapper(PtBagBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<PtBag> lqw = Wrappers.lambdaQuery();
+        lqw.eq(bo.getUserId() != null, PtBag::getUserId, bo.getUserId());
+        lqw.eq(StringUtils.isNotBlank(bo.getBagCode()), PtBag::getBagCode, bo.getBagCode());
+        lqw.eq(bo.getBalance() != null, PtBag::getBalance, bo.getBalance());
+        lqw.eq(bo.getConsumeTotal() != null, PtBag::getConsumeTotal, bo.getConsumeTotal());
+        lqw.eq(bo.getConsumeCount() != null, PtBag::getConsumeCount, bo.getConsumeCount());
+        lqw.eq(bo.getRechargeTotal() != null, PtBag::getRechargeTotal, bo.getRechargeTotal());
+        lqw.eq(bo.getRechargeCount() != null, PtBag::getRechargeCount, bo.getRechargeCount());
+        return lqw;
+    }
+
+    /**
+     * 新增账户钱包
+     *
+     * @param bo 账户钱包
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(PtBagBo bo) {
+        PtBag add = MapstructUtils.convert(bo, PtBag.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            if (add != null) {
+                bo.setBagId(add.getBagId());
+            }
+        }
+        return flag;
+    }
+
+    /**
+     * 修改账户钱包
+     *
+     * @param bo 账户钱包
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(PtBagBo bo) {
+        PtBag update = MapstructUtils.convert(bo, PtBag.class);
+        if (update != null) {
+            verification(bo);
+            setEncryptBalance(update);
+            return baseMapper.updateById(update) > 0;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(PtBag entity) {
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除账户钱包信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+
+    /**
+     * 根据Id获取账户钱包余额
+     *
+     * @param userIds 人员Id串,英文逗号分隔
+     * @return 余额串,英文逗号分隔
+     */
+    @Override
+    public String selectAccountBalanceByIds(String userIds) {
+        List<String> list = new ArrayList<>();
+        for (Long id : StringUtils.splitTo(userIds, Convert::toLong)) {
+            PtBagBo bo = new PtBagBo();
+            bo.setUserId(id);
+            List<PtBagVo> listVo = SpringUtils.getAopProxy(this).queryList(bo);
+            BigDecimal balance1 = BigDecimal.ZERO;
+            BigDecimal balance2 = BigDecimal.ZERO;
+            Optional<PtBagVo> vo = listVo.stream().filter(p -> "1".equals(p.getBagCode()))
+                .findFirst();
+            if (vo.isPresent()) {
+                balance1 = vo.get().getBalance();
+            }
+            vo = listVo.stream().filter(p -> "3".equals(p.getBagCode()))
+                .findFirst();
+            if (vo.isPresent()) {
+                balance2 = vo.get().getBalance();
+            }
+            BigDecimal total = balance1.add(balance2);
+
+            list.add(total.toString());
+        }
+        return String.join(StringUtils.SEPARATOR, list);
+    }
+
+    /**
+     * 初始化账户钱包
+     *
+     * @param userId 账户Id
+     * @return 是否初始化成功
+     */
+    @Override
+    public Boolean initAccountBag(Long userId) {
+        PtBagBo bo = new PtBagBo();
+        bo.setUserId(userId);
+        List<PtBagVo> listVo = SpringUtils.getAopProxy(this).queryList(bo);
+        if (listVo.isEmpty()) {
+            //没有钱包,初始化
+            for (int i = 1; i < 7; i++) {
+                bo = new PtBagBo();
+                bo.setUserId(userId);
+                bo.setBagCode(String.valueOf(i));
+                insertByBo(bo);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 更新钱包
+     *
+     * @param bo 账户钱包
+     * @return 更新后的账户钱包
+     */
+    @Override
+    public PtBagVo updateBalanceByBo(PtBagBo bo) {
+        //校验入库数据
+        PtBag entity = verification(bo);
+        //根据不同的操作组装入库数据
+        switch (bo.getOperationMode()) {
+            case REFUND:
+                refundBag(entity, bo);
+                break;
+            case CONSUME:
+                consumeBag(entity, bo);
+                break;
+            case RECOVER:
+                recoverBag(entity, bo);
+                break;
+            case COMPENSATE:
+                compensateBag(entity, bo);
+                break;
+            case RECHARGE:
+            default:
+                rechargeBag(entity, bo);
+        }
+        //数据入库
+        if (baseMapper.updateById(entity) > 0) {
+            return queryById(entity.getBagId());
+        } else {
+            return null;
+        }
+    }
+    /**
+     * 钱包更新前的验证
+     *
+     * @param bo 账户钱包
+     * @return 验证通过后的账户钱包
+     */
+    @Override
+    public PtBag verification(PtBagBo bo) {
+        //获取数据库中对应的钱包
+        PtBag entity = baseMapper.selectOne(Wrappers.<PtBag>lambdaQuery()
+            .eq(PtBag::getUserId, bo.getUserId())
+            .eq(PtBag::getBagCode,bo.getBagCode()));
+
+        //解密余额密文,得到加密的余额
+        //加密余额默认=明文余额
+        BigDecimal entryptValue = entity.getBalance();
+        if (StrUtil.isNotBlank(entity.getEncryptBalance())) {
+            String decryptValue = YcEncryptUtil.decryptBagBalanceByPublicKey(entity.getEncryptBalance(), entity.getUserId().toString());
+            entryptValue = new BigDecimal(decryptValue);
+        }
+        //如果明文余额与解密后余额不一致则验证不通过
+        if (entryptValue.compareTo(entity.getBalance()) != 0) {
+            throw new BagException("bag.balance.valid", JSONUtil.parse(bo));
+        }
+        return entity;
+    }
+    /**
+     * 组装充值钱包数据
+     * 1.设置充值后余额=账户原余额+充值金额
+     * 2.设置充值次数+1
+     * 3.设置充值总金额额=原总金额+充值金额
+     * 4.对充值后余额进行加密处理
+     *
+     * @param bag 账户钱包(数据库)
+     * @param bo 账户钱包(业务)
+     */
+    private void rechargeBag(PtBag bag, PtBagBo bo) {
+        bag.setBalance(bag.getBalance().add(bo.getReceiptMoney()));
+        bag.setRechargeCount(bag.getRechargeCount() + 1);
+        bag.setRechargeTotal(bag.getRechargeTotal().add(bo.getReceiptMoney()));
+        setEncryptBalance(bag);
+    }
+    /**
+     * 组装退款钱包数据
+     * 1.如果原账户余额>退款金额,则设置退款后余额=账户原余额-退款金额,否则为0
+     * 2.对退款后余额进行加密处理
+     *
+     * @param bag 账户钱包(数据库)
+     * @param bo 账户钱包(业务)
+     */
+    private void refundBag(PtBag bag, PtBagBo bo) {
+        //退款后的余额
+        BigDecimal after = BigDecimal.ZERO;
+        //账户现有余额
+        BigDecimal balance  = bag.getBalance();
+        //退款金额,传入的金额为负数,转换成正数进行余额处理
+        BigDecimal doValue = bo.getReceiptMoney().negate();
+        //如果账户现有余额比退款金额大,则最后余额为账户余额-退款金额,否则为0
+        if(balance.compareTo(doValue)>0){
+            after = balance.subtract(doValue);
+        }
+        bag.setBalance(after);
+        setEncryptBalance(bag);
+    }
+    /**
+     * 组装重置钱包数据
+     * 1.设置重置后余额=操作金额
+     * 2.对退款后余额进行加密处理
+     *
+     * @param bag 账户钱包(数据库)
+     * @param bo 账户钱包(业务)
+     */
+    private void recoverBag(PtBag bag, PtBagBo bo) {
+        if(bag.getBalance().compareTo(bo.getReceiptMoney())>0){
+            //如果钱包余额比设置的金额大,则最后余额=设置的金额
+            bag.setBalance(bo.getReceiptMoney());
+        }
+        setEncryptBalance(bag);
+    }
+    /**
+     * 组装消费钱包数据
+     * 1.设置消费后余额=账户原余额-消费金额
+     * 2.设置消费次数+1
+     * 3.设置消费总金额额=原总金额+消费金额
+     * 4.对消费后余额进行加密处理
+     *
+     * @param bag 账户钱包(数据库)
+     * @param bo 账户钱包(业务)
+     */
+    private void consumeBag(PtBag bag, PtBagBo bo) {
+        bag.setBalance(bag.getBalance().subtract(bo.getReceiptMoney()));
+        bag.setConsumeCount(bag.getConsumeCount() + 1);
+        bag.setConsumeTotal(bag.getConsumeTotal().add(bo.getReceiptMoney()));
+        setEncryptBalance(bag);
+    }
+    /**
+     * 组装错扣补款钱包数据
+     * 1.设置补款后余额=账户原余额+补款金额
+     * 2.设置消费次数+1
+     * 3.设置消费总金额额=原总金额-补款金额
+     * 4.对补款后余额进行加密处理
+     *
+     * @param bag 账户钱包(数据库)
+     * @param bo 账户钱包(业务)
+     */
+    private void compensateBag(PtBag bag, PtBagBo bo) {
+        //余额=原余额+补款金额
+        bag.setBalance(bag.getBalance().add(bo.getReceiptMoney()));
+        //消费次数+1
+        bag.setConsumeCount(bag.getConsumeCount() + 1);
+        //因为是多消费了才补款,所以总消费金额=原总消费金额-补款的金额
+        bag.setConsumeTotal(bag.getConsumeTotal().subtract(bo.getReceiptMoney()));
+        setEncryptBalance(bag);
+    }
+    /**
+     * 设置加密卡余
+     *
+     * @param bag 账户钱包(数据库)
+     */
+    private void setEncryptBalance(PtBag bag) {
+        String encryptValue = YcEncryptUtil.encryptBagBalanceByPublicKey(bag.getBalance().toString(), bag.getUserId().toString());
+        bag.setEncryptBalance(encryptValue);
+    }
+
+}

+ 18 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/impl/XfConsumeDetailServiceImpl.java

@@ -1,5 +1,6 @@
 package org.dromara.server.consume.service.impl;
 
+import cn.hutool.core.collection.CollectionUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -161,4 +162,21 @@ public class XfConsumeDetailServiceImpl implements IXfConsumeDetailService {
         updateWrapper.eq(XfConsumeDetail::getConsumeId, bo.getConsumeId());
         return baseMapper.update(null, updateWrapper)>0;
     }
+
+    @Override
+    public XfConsumeDetailVo queryVoByBo(XfConsumeDetailBo bo) {
+        List<XfConsumeDetailVo> list = this.queryList(bo);
+        if(CollectionUtil.isNotEmpty(list)){
+            return list.get(0);
+        }
+        return null;
+    }
+
+    @Override
+    public XfConsumeDetailVo queryVoByOriginalId(String originalId) {
+        XfConsumeDetailBo bo = new XfConsumeDetailBo();
+        bo.setOriginalId(originalId);
+
+        return this.queryVoByBo(bo);
+    }
 }

+ 20 - 0
ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/service/impl/XfTermServiceImpl.java

@@ -5,6 +5,11 @@ import cn.hutool.core.util.ObjUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.backstage.api.RemotePtAccountService;
+import org.dromara.backstage.api.RemotePtRoomService;
+import org.dromara.backstage.api.domain.vo.RemotePtAccountVo;
+import org.dromara.backstage.api.domain.vo.RemotePtRoomVo;
 import org.dromara.common.core.constant.CacheNames;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.redis.utils.RedisUtils;
@@ -29,8 +34,15 @@ import java.util.Objects;
 @Service
 public class XfTermServiceImpl implements IXfTermService {
 
+    @DubboReference
+    RemotePtRoomService remotePtRoomService;
+
+    @DubboReference
+    RemotePtAccountService remotePtAccountService;
+
     private final XfTermMapper baseMapper;
 
+
     /**
      * 查询消费设备列表
      *
@@ -66,6 +78,14 @@ public class XfTermServiceImpl implements IXfTermService {
         LambdaQueryWrapper<XfTerm> lqw = this.buildQueryWrapper(bo);
         XfTermVo vo =  baseMapper.selectVoOne(lqw);
         if(ObjUtil.isNotEmpty(vo)){
+            RemotePtAccountVo accountVo = remotePtAccountService.selectVoById(vo.getAccountId());
+            if(ObjUtil.isNotEmpty(accountVo)){
+                vo.setAccountName(accountVo.getAccountName());
+            }
+            RemotePtRoomVo roomVo = remotePtRoomService.selectVoById(vo.getRoomId());
+            if(ObjUtil.isNotEmpty(roomVo)){
+                vo.setRoomName(roomVo.getRoomName());
+            }
             List<XfTermVo> redisList = RedisUtils.getCacheList(CacheNames.PT_TERM);
             if (CollectionUtil.isNotEmpty(redisList)) {
                 RedisUtils.addCacheList(CacheNames.PT_TERM, vo);

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

@@ -24,8 +24,8 @@ import java.math.BigDecimal;
 @RequiredArgsConstructor
 @Service
 public class XfUserTotalServiceImpl implements IXfUserTotalService {
-
     private final XfUserTotalMapper baseMapper;
+
     /**
      * 新增个人与消费与充值统计
      *

+ 25 - 0
ruoyi-server/ruoyi-server-consume/src/main/resources/mapper/consume/PtBagMapper.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.server.consume.mapper.PtBagMapper">
+
+    <resultMap type="org.dromara.server.consume.domain.PtBag" id="PtBagResult">
+            <result property="bagId"    column="bag_id"    />
+            <result property="tenantId"    column="tenant_id"    />
+            <result property="userId"    column="user_id"    />
+            <result property="bagCode"    column="bag_code"    />
+            <result property="balance"    column="balance"    />
+            <result property="encryptBalance"    column="encrypt_balance"    />
+            <result property="consumeTotal"    column="consume_total"    />
+            <result property="consumeCount"    column="consume_count"    />
+            <result property="rechargeTotal"    column="recharge_total"    />
+            <result property="rechargeCount"    column="recharge_count"    />
+            <result property="delFlag"    column="del_flag"    />
+            <result property="createDept"    column="create_dept"    />
+            <result property="createBy"    column="create_by"    />
+            <result property="createTime"    column="create_time"    />
+            <result property="updateBy"    column="update_by"    />
+            <result property="updateTime"    column="update_time"    />
+    </resultMap>
+</mapper>

+ 1 - 1
ruoyi-server/ruoyi-server-mqdata/src/main/java/org/dromara/server/mq/event/kafka/impl/yktOperation/PtXfDiscountEventStrategyImpl.java

@@ -26,7 +26,7 @@ public class PtXfDiscountEventStrategyImpl implements IYktEventStrategy {
             case MessageEventTypeConstants.xfDiscount_EDIT -> discountService.editXfDiscount(msg);
             case MessageEventTypeConstants.xfDiscount_REMOVE -> discountService.delXfDiscount(msg);
             case MessageEventTypeConstants.DISCOUNT_TERM_ADD -> discountService.addDiscountTerm(msg);
-            case MessageEventTypeConstants.DISCOUNT_TERM_DEL -> discountService.deleteDiscountTerm(msg);
+            case MessageEventTypeConstants.DISCOUNT_TERM_REMOVE -> discountService.deleteDiscountTerm(msg);
             default -> log.info("未知事件");
 
         }

+ 36 - 0
ruoyi-server/ruoyi-server-mqdata/src/main/java/org/dromara/server/mq/event/kafka/impl/yktOperation/PtXfLimitedEventStrategyImpl.java

@@ -0,0 +1,36 @@
+package org.dromara.server.mq.event.kafka.impl.yktOperation;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.message.kafka.constant.MessageEventTypeConstants;
+import org.dromara.server.base.service.yktOperation.SyncRemoteXfLimitedService;
+import org.dromara.server.base.service.yktOperation.SyncRemoteXfQuotaService;
+import org.dromara.server.mq.event.kafka.IYktEventStrategy;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author Hz
+ * @date 2024/10/29
+ * @description 限次管理同步策略
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service(MessageEventTypeConstants.XF_LIMITED_SENDER)
+public class PtXfLimitedEventStrategyImpl implements IYktEventStrategy {
+
+    private final SyncRemoteXfLimitedService limitedService;
+    @Override
+    public void doMsgHandle(String eventType, Object msg) throws Exception{
+
+        switch (eventType) {
+            case MessageEventTypeConstants.xfLimited_ADD -> limitedService.addXfLimited(msg);
+            case MessageEventTypeConstants.xfLimited_EDIT -> limitedService.editXfLimited(msg);
+            case MessageEventTypeConstants.xfLimited_REMOVE -> limitedService.delXfLimited(msg);
+            case MessageEventTypeConstants.LIMITED_TERM_ADD -> limitedService.addLimitedTerm(msg);
+            case MessageEventTypeConstants.LIMITED_TERM_REMOVE -> limitedService.deleteLimitedTerm(msg);
+            default -> log.info("未知事件");
+
+        }
+    }
+
+}

+ 36 - 0
ruoyi-server/ruoyi-server-mqdata/src/main/java/org/dromara/server/mq/event/kafka/impl/yktOperation/PtXfQuotaEventStrategyImpl.java

@@ -0,0 +1,36 @@
+package org.dromara.server.mq.event.kafka.impl.yktOperation;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.message.kafka.constant.MessageEventTypeConstants;
+import org.dromara.server.base.service.yktOperation.SyncRemoteXfDiscountService;
+import org.dromara.server.base.service.yktOperation.SyncRemoteXfQuotaService;
+import org.dromara.server.mq.event.kafka.IYktEventStrategy;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author Hz
+ * @date 2024/10/29
+ * @description 限额管理同步策略
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service(MessageEventTypeConstants.XF_QUOTA_SENDER)
+public class PtXfQuotaEventStrategyImpl implements IYktEventStrategy {
+
+    private final SyncRemoteXfQuotaService quotaService;
+    @Override
+    public void doMsgHandle(String eventType, Object msg) throws Exception{
+
+        switch (eventType) {
+            case MessageEventTypeConstants.xfQuota_ADD -> quotaService.addXfQuota(msg);
+            case MessageEventTypeConstants.xfQuota_EDIT -> quotaService.editXfQuota(msg);
+            case MessageEventTypeConstants.xfQuota_REMOVE -> quotaService.delXfQuota(msg);
+            case MessageEventTypeConstants.QUOTA_TERM_ADD -> quotaService.addQuotaTerm(msg);
+            case MessageEventTypeConstants.QUOTA_TERM_REMOVE -> quotaService.deleteQuotaTerm(msg);
+            default -> log.info("未知事件");
+
+        }
+    }
+
+}