Переглянути джерело

对接海康设备,添加部分消费逻辑

xiari 11 місяців тому
батько
коміт
6749e21179
28 змінених файлів з 2413 додано та 119 видалено
  1. 2 0
      ruoyi-api/ruoyi-api-consume/src/main/java/org/dromara/consume/api/domain/bo/RemoteConsumeBo.java
  2. 2 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java
  3. 1 0
      ruoyi-server/ruoyi-server-consume/src/main/java/org/dromara/server/consume/business/ConsumeBusiness.java
  4. 1 1
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/controller/TestController.java
  5. 205 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/domain/XfConsumeDetail.java
  6. 171 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/domain/XfConsumeDetailOriginal.java
  7. 358 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/domain/XfTerm.java
  8. 162 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/domain/vo/XfConsumeDetailOriginalVo.java
  9. 199 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/domain/vo/XfConsumeDetailVo.java
  10. 329 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/domain/vo/XfTermVo.java
  11. 68 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/enums/ConsumeModeEnum.java
  12. 60 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/enums/ModeTypeEnum.java
  13. 146 59
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/event/handler/ConsumptionEventHandler.java
  14. 67 58
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/event/handler/TransactionRecordEventHandler.java
  15. 2 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/event/timedtask/HandleTask.java
  16. 21 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/mapper/ConsumeDetailOriginalMapper.java
  17. 13 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/mapper/XfConsumeDetailMapper.java
  18. 15 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/mapper/XfTermMapper.java
  19. 66 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/IConsumeDetailOriginalService.java
  20. 36 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/IXfConsumeDetailService.java
  21. 27 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/IXfTermService.java
  22. 117 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/impl/ConsumeDetailOriginalServiceImpl.java
  23. 1 1
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/impl/SendDeviceServiceImpl.java
  24. 71 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/impl/XfConsumeDetailServiceImpl.java
  25. 87 0
      ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/impl/XfTermServiceImpl.java
  26. 57 0
      ruoyi-server/ruoyi-server-hik/src/main/resources/mapper/hik/ConsumeDetailOriginalMapper.xml
  27. 49 0
      ruoyi-server/ruoyi-server-hik/src/main/resources/mapper/hik/XfConsumeDetailMapper.xml
  28. 80 0
      ruoyi-server/ruoyi-server-hik/src/main/resources/mapper/hik/XfTermMapper.xml

+ 2 - 0
ruoyi-api/ruoyi-api-consume/src/main/java/org/dromara/consume/api/domain/bo/RemoteConsumeBo.java

@@ -154,4 +154,6 @@ public class RemoteConsumeBo implements Serializable {
      */
     private BigDecimal operatorMoney;
     //endregion
+
+    private String deptName;
 }

+ 2 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java

@@ -150,4 +150,6 @@ public interface CacheNames {
      * 消费设备列表
      */
     String PT_TERM_LIST = "pt_term_list";
+
+    String PT_TERM_MAC_MAP = "pt_term_mac_map";
 }

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

@@ -113,6 +113,7 @@ public class ConsumeBusiness {
 
         bo.setRecordId(originalVo.getRecordId());
         bo.setStatusFlag(originalVo.getStatusFlag().intValue());
+        bo.setDeptName(userAccountVo.getDeptName());
         return R.ok();
     }
 

+ 1 - 1
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/controller/TestController.java

@@ -261,7 +261,7 @@ public class TestController {
      *         如果在处理过程中发生错误或内容类型不受支持,则返回 null。
      */
     @PostMapping(("/info"))
-    public Map<String, Object> receiveDevicePushData(HttpServletRequest request) throws IOException {
+    public Map<String, Object> receiveDevicePushData(HttpServletRequest request) {
         try {
             // 判断请求类型
             List<String> jsonListString = new ArrayList<>();

+ 205 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/domain/XfConsumeDetail.java

@@ -0,0 +1,205 @@
+package org.dromara.server.hik.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;
+import java.util.Date;
+
+/**
+ * 消费明细对象 t_xf_consumeDetail
+ *
+ * @author LionLi
+ * @date 2024-08-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("t_xf_consumeDetail")
+public class XfConsumeDetail extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 消费记录Id,主键
+     */
+    @TableId(value = "consume_id")
+    private String consumeId;
+
+    /**
+     * 原始记录Id,主键
+     */
+    private String originalId;
+
+    /**
+     * 记录Id
+     */
+    private Long recordId;
+
+    /**
+     * 人员Id
+     */
+    private Long userId;
+
+    /**
+     * 学/工号
+     */
+    private String userNumb;
+
+    /**
+     * 用户姓名
+     */
+    private String realName;
+
+    /**
+     * 部门Id
+     */
+    private Long deptId;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    /**
+     * 消费日期
+     */
+    private Date consumeDate;
+
+    /**
+     * 消费金额
+     */
+    private BigDecimal consumeMoney;
+
+    /**
+     * 卡流水号
+     */
+    private Long cardNo;
+
+    /**
+     * 物理卡号
+     */
+    private Long factoryId;
+
+    /**
+     * 卡上余额
+     */
+    private BigDecimal cardValue;
+
+    /**
+     * 卡使用次数
+     */
+    private Long cardCount;
+
+    /**
+     * 消费账户金额
+     */
+    private BigDecimal consumeBalance;
+
+    /**
+     * 设备机号
+     */
+    private Long termNo;
+
+    /**
+     * 设备名称
+     */
+    private String termName;
+
+    /**
+     * 机器流水号
+     */
+    private Long termRecordId;
+
+    /**
+     * 是否已处理(和灰记录处理有关)
+     */
+    private Long analysisFlag;
+
+    /**
+     * 状态标识(记录消费类型?)
+     */
+    private Long statusFlag;
+
+    /**
+     * 营业员Id
+     */
+    private Long operatorId;
+
+    /**
+     * 营业员名称
+     */
+    private String operatorName;
+
+    /**
+     * 结算账户Id
+     */
+    private Long accountId;
+
+    /**
+     * 结算账户名称
+     */
+    private String accountName;
+
+    /**
+     * 房间Id
+     */
+    private Long roomId;
+
+    /**
+     * 房间名称
+     */
+    private String roomName;
+
+    /**
+     * 钱包类型
+     */
+    private String bagType;
+
+    /**
+     * 餐类Id
+     */
+    private Long mealType;
+
+    /**
+     * 餐类名称
+     */
+    private String mealName;
+
+    /**
+     * 是否发送短信,见 sys_yes_no字典类别
+     */
+    private String smsSend;
+
+    /**
+     * 消费记录标志位?
+     */
+    private Long recordStatus;
+
+    /**
+     * 同步标志,0-未同步,1-已同步
+     */
+    private Long syncStatus;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 删除标志(0-未删除 2-已删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 针对错扣补款的消费记录的id
+     */
+    private String detailId;
+
+}

+ 171 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/domain/XfConsumeDetailOriginal.java

@@ -0,0 +1,171 @@
+package org.dromara.server.hik.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;
+import java.util.Date;
+
+/**
+ * 原始消费记录对象 t_xf_consumeDetailOriginal
+ *
+ * @author LionLi
+ * @date 2024-08-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("t_xf_consumeDetailOriginal")
+public class XfConsumeDetailOriginal extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 原始记录Id,主键
+     */
+    @TableId(value = "original_id")
+    private String originalId;
+
+    /**
+     * 记录Id,自增长,唯一id值,用于应答给消费设备
+     */
+    private Long recordId;
+
+    /**
+     * 人员Id
+     */
+    private Long userId;
+
+    /**
+     * 学/工号
+     */
+    private String userNumb;
+
+    /**
+     * 用户姓名
+     */
+    private String realName;
+
+    /**
+     * 部门Id
+     */
+    private Long deptId;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    /**
+     * 消费日期
+     */
+    private Date consumeDate;
+
+    /**
+     * 消费金额
+     */
+    private BigDecimal consumeMoney;
+
+    /**
+     * 卡流水号
+     */
+    private Long cardNo;
+
+    /**
+     * 物理卡号
+     */
+    private Long factoryId;
+
+    /**
+     * 卡上余额
+     */
+    private BigDecimal cardValue;
+
+    /**
+     * 卡使用次数
+     */
+    private Long cardCount;
+
+    /**
+     * 消费账户金额
+     */
+    private BigDecimal consumeBalance;
+
+    /**
+     * 设备机号
+     */
+    private Long termNo;
+
+    /**
+     * 设备名称
+     */
+    private String termName;
+
+    /**
+     * 机器流水号
+     */
+    private Long termRecordId;
+
+    /**
+     * 是否已处理(和灰记录处理有关)
+     */
+    private Long analysisFlag;
+
+    /**
+     * 消费记录标志
+     */
+    private Long dataFlag;
+
+    /**
+     * 状态标识(记录消费类型?)
+     */
+    private Long statusFlag;
+
+    /**
+     * 防伪验证码(记录消费模式的中文信息)
+     */
+    private String digitalSign;
+
+    /**
+     * 水控金额
+     */
+    private BigDecimal waterValue;
+
+    /**
+     * 水控历史余额
+     */
+    private BigDecimal waterHistoryValue;
+
+    /**
+     * 水控当天总和
+     */
+    private BigDecimal waterDaySum;
+
+    /**
+     * 水控错误余额
+     */
+    private BigDecimal waterErrValue;
+
+    /**
+     * 水控错误消费金额
+     */
+    private BigDecimal waterErrMoney;
+
+    /**
+     * 营业员Id
+     */
+    private Long operatorId;
+
+    /**
+     * 删除标志(0-未删除 2-已删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+
+}

+ 358 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/domain/XfTerm.java

@@ -0,0 +1,358 @@
+package org.dromara.server.hik.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+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;
+import java.util.Date;
+
+/**
+ * 消费设备对象 t_xf_term
+ *
+ * @author bing
+ * @date 2024-08-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("t_xf_term")
+public class XfTerm extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 设备Id,主键
+     */
+    @TableId(value = "term_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;
+
+    /**
+     * 允许使用的卡类
+     */
+    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字典类别
+     */
+    private String workMode;
+
+    /**
+     * 开机模式,0-不需要营业员开机,1-需要营业员开机
+     */
+    private String openMode;
+
+    /**
+     * 卡上最大金额
+     */
+    private BigDecimal maxCardMoney;
+
+    /**
+     * 定值消费金额
+     */
+    private BigDecimal constantValue;
+
+    /**
+     * 编号0代表的金额
+     */
+    private BigDecimal rationZero;
+
+    /**
+     * 编号1代表的金额
+     */
+    private BigDecimal rationOne;
+
+    /**
+     * 编号2代表的金额
+     */
+    private BigDecimal rationTwo;
+
+    /**
+     * 编号3代表的金额
+     */
+    private BigDecimal rationThree;
+
+    /**
+     * 编号4代表的金额
+     */
+    private BigDecimal rationFour;
+
+    /**
+     * 编号5代表的金额
+     */
+    private BigDecimal rationFive;
+
+    /**
+     * 编号6代表的金额
+     */
+    private BigDecimal rationSix;
+
+    /**
+     * 编号7代表的金额
+     */
+    private BigDecimal rationSeven;
+
+    /**
+     * 编号8代表的金额
+     */
+    private BigDecimal rationEight;
+
+    /**
+     * 编号9代表的金额
+     */
+    private BigDecimal rationNine;
+
+    /**
+     * 每天最大消费次数,0-不限
+     */
+    private BigDecimal dayCount;
+
+    /**
+     * 每天最大消费金额
+     */
+    private BigDecimal dayMoney;
+
+    /**
+     * 每餐最大消费次数,0-不限
+     */
+    private Long mealCount;
+
+    /**
+     * 单次最大消费金额
+     */
+    private BigDecimal singleMoney;
+
+    /**
+     * 早餐消费金额
+     */
+    private BigDecimal breakfastMoney;
+
+    /**
+     * 午餐消费金额
+     */
+    private BigDecimal lunchMoney;
+
+    /**
+     * 晚餐消费金额
+     */
+    private BigDecimal supperMoney;
+
+    /**
+     * 夜宵消费金额
+     */
+    private BigDecimal nightMoney;
+
+    /**
+     * 早餐开始时间
+     */
+    private Long breakfastBegin;
+
+    /**
+     * 早餐结束时间
+     */
+    private Long breakfastEnd;
+
+    /**
+     * 午餐开始时间
+     */
+    private Long lunchBegin;
+
+    /**
+     * 午餐结束时间
+     */
+    private Long lunchEnd;
+
+    /**
+     * 晚餐开始时间
+     */
+    private Long supperBegin;
+
+    /**
+     * 晚餐结束时间
+     */
+    private Long supperEnd;
+
+    /**
+     * 宵夜开始时间
+     */
+    private Long nightBegin;
+
+    /**
+     * 宵夜结束时间
+     */
+    private Long nightEnd;
+
+    /**
+     * 两次刷卡间隔,0-不限制
+     */
+    private Long swipeInterval;
+
+    /**
+     * 是否启用卡片有效限制,0-禁用 1-启用
+     */
+    private String termValidity;
+
+    /**
+     * 设备记录流水号
+     */
+    private Long recordId;
+
+    /**
+     * 数据上传时间
+     */
+    private Date uploadTime;
+
+    /**
+     * 黑名单下载时间
+     */
+    private Date blackDownTime;
+
+    /**
+     * 最后校时时间
+     */
+    private Date lastCheck;
+
+    /**
+     * 重启时间
+     */
+    private String rebootTime;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 删除标志(0-未删除 2-已删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    @TableField(exist = false)
+    private Long roomName;
+
+    @TableField(exist = false)
+    private Long accountName;
+
+
+}

+ 162 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/domain/vo/XfConsumeDetailOriginalVo.java

@@ -0,0 +1,162 @@
+package org.dromara.server.hik.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.server.hik.domain.XfConsumeDetailOriginal;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 原始消费记录视图对象 t_xf_consumeDetailOriginal
+ *
+ * @author LionLi
+ * @date 2024-08-15
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = XfConsumeDetailOriginal.class)
+public class XfConsumeDetailOriginalVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 原始记录Id,主键
+     */
+    private String originalId;
+
+    /**
+     * 记录Id
+     */
+    private Long recordId;
+
+    /**
+     * 人员Id
+     */
+    private Long userId;
+
+    /**
+     * 学/工号
+     */
+    private String userNumb;
+
+    /**
+     * 用户姓名
+     */
+    private String realName;
+
+    /**
+     * 部门Id
+     */
+    private Long deptId;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    /**
+     * 消费日期
+     */
+    private Date consumeDate;
+
+    /**
+     * 消费金额
+     */
+    private Long consumeMoney;
+
+    /**
+     * 卡流水号
+     */
+    private Long cardNo;
+
+    /**
+     * 物理卡号
+     */
+    private Long factoryId;
+
+    /**
+     * 卡上余额
+     */
+    private Long cardValue;
+
+    /**
+     * 卡使用次数
+     */
+    private Long cardCount;
+
+    /**
+     * 消费账户金额
+     */
+    private Long consumeBalance;
+
+    /**
+     * 设备机号
+     */
+    private Long termNo;
+
+    /**
+     * 设备名称
+     */
+    private String termName;
+
+    /**
+     * 机器流水号
+     */
+    private Long termRecordId;
+
+    /**
+     * 是否已处理(和灰记录处理有关)
+     */
+    private Long anlysFlag;
+
+    /**
+     * 消费记录标志
+     */
+    private Long dataFlag;
+
+    /**
+     * 状态标识(记录消费类型?)
+     */
+    private Long statusFlag;
+
+    /**
+     * 防伪验证码(记录消费模式的中文信息)
+     */
+    private String digitalSign;
+
+    /**
+     * 水控金额
+     */
+    private Long waterValue;
+
+    /**
+     * 水控历史余额
+     */
+    private Long waterHistoryValue;
+
+    /**
+     * 水控当天总和
+     */
+    private Long waterDaySum;
+
+    /**
+     * 水控错误余额
+     */
+    private Long waterErrValue;
+
+    /**
+     * 水控错误消费金额
+     */
+    private Long waterErrMoney;
+
+    /**
+     * 营业员Id
+     */
+    private Long operatorId;
+
+}

+ 199 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/domain/vo/XfConsumeDetailVo.java

@@ -0,0 +1,199 @@
+package org.dromara.server.hik.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.server.hik.domain.XfConsumeDetail;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+
+/**
+ * 消费明细视图对象 t_xf_consumeDetail
+ *
+ * @author LionLi
+ * @date 2024-08-15
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = XfConsumeDetail.class)
+public class XfConsumeDetailVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 消费记录Id,主键
+     */
+    private String consumeId;
+
+    /**
+     * 原始记录Id,主键
+     */
+    private String originalId;
+
+    /**
+     * 记录Id
+     */
+    private Long recordId;
+
+    /**
+     * 人员Id
+     */
+    private Long userId;
+
+    /**
+     * 学/工号
+     */
+    private String userNumb;
+
+    /**
+     * 用户姓名
+     */
+    private String realName;
+
+    /**
+     * 部门Id
+     */
+    private Long deptId;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    /**
+     * 消费日期
+     */
+    private Date consumeDate;
+
+    /**
+     * 消费金额
+     */
+    private BigDecimal consumeMoney;
+
+    /**
+     * 卡流水号
+     */
+    private Long cardNo;
+
+    /**
+     * 物理卡号
+     */
+    private Long factoryId;
+
+    /**
+     * 卡上余额
+     */
+    private BigDecimal cardValue;
+
+    /**
+     * 卡使用次数
+     */
+    private Long cardCount;
+
+    /**
+     * 消费账户金额
+     */
+    private BigDecimal consumeBalance;
+
+    /**
+     * 设备机号
+     */
+    private Long termNo;
+
+    /**
+     * 设备名称
+     */
+    private String termName;
+
+    /**
+     * 机器流水号
+     */
+    private Long termRecordId;
+
+    /**
+     * 是否已处理(和灰记录处理有关)
+     */
+    private Long analysisFlag;
+
+    /**
+     * 状态标识(记录消费类型?)
+     */
+    private Long statusFlag;
+
+    /**
+     * 营业员Id
+     */
+    private Long operatorId;
+
+    /**
+     * 营业员名称
+     */
+    private String operatorName;
+
+    /**
+     * 结算账户Id
+     */
+    private Long accountId;
+
+    /**
+     * 结算账户名称
+     */
+    private String accountName;
+
+    /**
+     * 房间Id
+     */
+    private Long roomId;
+
+    /**
+     * 房间名称
+     */
+    private String roomName;
+
+    /**
+     * 钱包类型
+     */
+    private String bagType;
+
+    /**
+     * 餐类Id
+     */
+    private Long mealType;
+
+    /**
+     * 餐类名称
+     */
+    private String mealName;
+
+    /**
+     * 是否发送短信,见 sys_yes_no字典类别
+     */
+    private String smsSend;
+
+    /**
+     * 消费记录标志位?
+     */
+    private Long recordStatus;
+
+    /**
+     * 同步标志,0-未同步,1-已同步
+     */
+    private Long syncStatus;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 针对错扣补款的消费记录的id
+     */
+    private String detailId;
+
+
+}

+ 329 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/domain/vo/XfTermVo.java

@@ -0,0 +1,329 @@
+package org.dromara.server.hik.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.server.hik.domain.XfTerm;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+
+/**
+ * 消费设备视图对象 t_xf_term
+ *
+ * @author bing
+ * @date 2024-08-21
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = XfTerm.class)
+public class XfTermVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 设备Id,主键
+     */
+    private Long termId;
+
+    /**
+     * 设备机号
+     */
+    private Long termNo;
+
+    /**
+     * 工作站Id
+     */
+    private Long stationId;
+
+    /**
+     * 设备名称
+     */
+    private String termName;
+    /**
+     * 设备二维码
+     */
+    private String qrCode;
+    /**
+     * 所属餐厅Id
+     */
+    private Long roomId;
+    /**
+     * 所属餐厅名称
+     */
+    private String roomName;
+
+    /**
+     * 结算账户Id
+     */
+    private Long accountId;
+    /**
+     * 结算账户名称
+     */
+    private String accountName;
+
+    /**
+     * 设备类型,见XF_TermType字典类型
+     */
+    private String termType;
+
+    /**
+     * 扣费类型,见XF_ConsumeType字典类型
+     */
+    private String consumeType;
+
+    /**
+     * 设备IP
+     */
+    private String termIp;
+
+    /**
+     * 设备MAC地址
+     */
+    private String termMac;
+
+    /**
+     * 设备通讯端口
+     */
+    private Long commPort;
+    /**
+     * 子网掩码
+     */
+    private String mask;
+
+    /**
+     * 网关IP
+     */
+    private String gatewayIp;
+
+    /**
+     * 服务器IP
+     */
+    private String serverIp;
+
+    /**
+     * 服务器端口
+     */
+    private Long serverPort;
+
+    /**
+     * 是否自动下载参数
+     */
+    private String autoDown;
+
+    /**
+     * 备注
+     */
+    private String remark;
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+    /**
+     * 应用系统,见YYXT数据字典类别
+     */
+    private String useType;
+    //region 以下为消费机的参数属性
+    private Integer cardType;
+
+    /**
+     * 消费工作模式,见XF_WorkMode字典类别
+     */
+    private String workMode;
+
+    /**
+     * 开机模式,0-不需要营业员开机,1-需要营业员开机
+     */
+    private String openMode;
+
+    /**
+     * 卡上最大金额
+     */
+    private BigDecimal maxCardMoney;
+
+    /**
+     * 定值消费金额
+     */
+    private BigDecimal constantValue;
+
+    /**
+     * 编号0代表的金额
+     */
+    private BigDecimal rationZero;
+
+    /**
+     * 编号1代表的金额
+     */
+    private BigDecimal rationOne;
+
+    /**
+     * 编号2代表的金额
+     */
+    private BigDecimal rationTwo;
+
+    /**
+     * 编号3代表的金额
+     */
+    private BigDecimal rationThree;
+
+    /**
+     * 编号4代表的金额
+     */
+    private BigDecimal rationFour;
+
+    /**
+     * 编号5代表的金额
+     */
+    private BigDecimal rationFive;
+
+    /**
+     * 编号6代表的金额
+     */
+    private BigDecimal rationSix;
+
+    /**
+     * 编号7代表的金额
+     */
+    private BigDecimal rationSeven;
+
+    /**
+     * 编号8代表的金额
+     */
+    private BigDecimal rationEight;
+
+    /**
+     * 编号9代表的金额
+     */
+    private BigDecimal rationNine;
+
+    /**
+     * 每天最大消费次数,0-不限
+     */
+    private Integer dayCount;
+
+    /**
+     * 每天最大消费金额
+     */
+    private BigDecimal dayMoney;
+
+    /**
+     * 每餐最大消费次数,0-不限
+     */
+    private Integer mealCount;
+
+    /**
+     * 单次最大消费金额
+     */
+    private BigDecimal singleMoney;
+
+    /**
+     * 早餐消费金额
+     */
+    private BigDecimal breakfastMoney;
+
+    /**
+     * 午餐消费金额
+     */
+    private BigDecimal lunchMoney;
+
+    /**
+     * 晚餐消费金额
+     */
+    private BigDecimal supperMoney;
+
+    /**
+     * 夜宵消费金额
+     */
+    private BigDecimal nightMoney;
+
+    /**
+     * 早餐开始时间
+     */
+    private Long breakfastBegin;
+
+    /**
+     * 早餐结束时间
+     */
+    private Long breakfastEnd;
+
+    /**
+     * 午餐开始时间
+     */
+    private Long lunchBegin;
+
+    /**
+     * 午餐结束时间
+     */
+    private Long lunchEnd;
+
+    /**
+     * 晚餐开始时间
+     */
+    private Long supperBegin;
+
+    /**
+     * 晚餐结束时间
+     */
+    private Long supperEnd;
+
+    /**
+     * 宵夜开始时间
+     */
+    private Long nightBegin;
+
+    /**
+     * 宵夜结束时间
+     */
+    private Long nightEnd;
+
+    /**
+     * 两次刷卡间隔,0-不限制
+     */
+    private Integer swipeInterval;
+
+    /**
+     * 是否启用卡片有效限制,0-禁用 1-启用
+     */
+    private String termValidity;
+
+    /**
+     * 重启时间
+     */
+    private String rebootTime;
+
+    /**
+     * 最后校时时间
+     */
+    private Date lastCheck;
+    /**
+     * 允许脱机时间
+     */
+    private Long offlineTime;
+    /**
+     * 通讯超时时间,以100毫秒为单位
+     */
+    private Long timeout;
+    /**
+     * 心跳间隔,以秒为单位
+     */
+    private Long beatInterval;
+    /**
+     * 设备记录流水号
+     */
+    private Long recordId;
+
+    /**
+     * 数据上传时间
+     */
+    private Date uploadTime;
+
+    /**
+     * 黑名单下载时间
+     */
+    private Date blackDownTime;
+    //endregion
+}

+ 68 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/enums/ConsumeModeEnum.java

@@ -0,0 +1,68 @@
+package org.dromara.server.hik.enums;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.dromara.common.core.utils.StringUtils;
+
+import java.util.Arrays;
+
+/**
+ * 消费机的消费模式
+ *
+ * @author bing
+ * @version 2.2.0
+ * @date 2025-05-21
+ * @since JDK17
+ */
+@Getter
+@AllArgsConstructor
+public enum ConsumeModeEnum {
+
+
+    /**
+     * 金额模式
+     */
+    amount("amount", "金额模式"),
+
+    /**
+     * 定额模式
+     */
+    quota("quota", "定额模式"),
+
+    /**
+     * 计次模式
+     */
+    count("count", "计次模式"),
+
+
+   ;
+
+    /**
+     * 编码
+     */
+    private final String code;
+
+    /**
+     * 描述
+     */
+    private final String message;
+
+    /**
+     * 获根据类型编码获取类型信息
+     *
+     * @param code 编码
+     */
+    public static String getMessage(String code) {
+        if (StringUtils.isBlank(code)) {
+            return StrUtil.EMPTY;
+        }
+        return Arrays.stream(ConsumeModeEnum.values())
+                   .filter(item -> item.getCode().equals(code))
+                   .findFirst()
+                   .map(ConsumeModeEnum::getMessage)
+                   .orElse(StrUtil.EMPTY);
+    }
+
+
+}

+ 60 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/enums/ModeTypeEnum.java

@@ -0,0 +1,60 @@
+package org.dromara.server.hik.enums;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.dromara.common.core.utils.StringUtils;
+
+import java.util.Arrays;
+
+/**
+ * 消费机的交易模式
+ *
+ * @author bing
+ * @version 2.2.0
+ * @date 2025-05-21
+ * @since JDK17
+ */
+@Getter
+@AllArgsConstructor
+public enum ModeTypeEnum {
+    /**
+     * 实时交易
+     */
+    current("current", "实时交易"),
+
+    /**
+     * 离线交易
+     */
+    offLine("offLine", "离线交易"),
+
+   ;
+
+    /**
+     * 编码
+     */
+    private final String code;
+
+    /**
+     * 描述
+     */
+    private final String message;
+
+    /**
+     * 获根据类型编码获取类型信息
+     *
+     * @param code 编码
+     */
+    public static String getMessage(String code) {
+        if (StringUtils.isBlank(code)) {
+            return StrUtil.EMPTY;
+        }
+        return Arrays.stream(ModeTypeEnum.values())
+                   .filter(item -> item.getCode().equals(code))
+                   .findFirst()
+                   .map(ModeTypeEnum::getMessage)
+                   .orElse(StrUtil.EMPTY);
+    }
+
+
+}

+ 146 - 59
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/event/handler/ConsumptionEventHandler.java

@@ -1,97 +1,92 @@
 package org.dromara.server.hik.event.handler;
 
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSONObject;
+import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+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.utils.StringUtils;
+import org.dromara.consume.api.RemoteConsumeService;
+import org.dromara.consume.api.domain.bo.RemoteConsumeBo;
+import org.dromara.consume.api.domain.bo.RemoteResultDto;
+import org.dromara.server.hik.domain.vo.XfTermVo;
+import org.dromara.server.hik.enums.ConsumeModeEnum;
 import org.dromara.server.hik.event.HikEventHandler;
 import org.dromara.server.hik.event.domain.ConsumptionEventConfirmBo;
 import org.dromara.server.hik.event.domain.ConsumptionEventDetail;
 import org.dromara.server.hik.event.domain.ConsumptionEventReceive;
 import org.dromara.server.hik.event.domain.FileContent;
+import org.dromara.server.hik.service.IXfTermService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Component;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 
 @Slf4j
 @Component
+@RequiredArgsConstructor
 public class ConsumptionEventHandler implements HikEventHandler {
 
     public static final String TRANSACTION_PREPROCESSING_REQUEST = "transactionPreprocessingRequest";
     public static final String TRANSACTION_CONFIRMING_REQUEST = "transactionConfirmingRequest";
 
+    private final IXfTermService termService;
+
+    @DubboReference
+    private final RemoteConsumeService remoteConsumeService;
+
     @Override
     public Map<String, Object> handleEvent(JSONObject jsonObject, FileContent fileContent) {
         ConsumptionEventReceive receive = jsonObject.toJavaObject(ConsumptionEventReceive.class);
         ConsumptionEventDetail consumptionEvent = receive.getConsumptionEvent();
         log.info("消费机事件,类型:{}", consumptionEvent.getMinor());
         if (TRANSACTION_PREPROCESSING_REQUEST.equals(consumptionEvent.getMinor())) {
-            /**
+            XfTermVo termVo = termService.queryByMac(receive.getMacAddress());
+            if (termVo == null) {
+                log.error("消费机交易记录事件,设备信息为空,mac:{}, 不存在系统中", receive.getMacAddress());
+                // 直接应答失败
+                return answerEvent(consumptionEvent, false,null,  "设备未注册", "");
+            }
+            /*
              * 消费机 交易预处理请求事件上报
              * eventType: ConsumptionEvent
              * minor:transactionPreprocessingRequest
              */
             // 业务逻辑处理的结果
             boolean bussiRs  = true;
-            // todo 处理交易预处理请求事件上报 根据业务数据校验 能不能消费 权限、账号、金额、设备、业务数据
-
+            // 处理交易预处理请求事件上报 根据业务数据校验 能不能消费 权限、账号、金额、设备、业务数据
+            // 调用现有原始消费请求接口
+            RemoteConsumeBo remoteBo = setParamBo(receive, consumptionEvent);
+            remoteBo.setTermNo(termVo.getTermNo());
+            RemoteResultDto result = remoteConsumeService.dealHikRequestConsume(remoteBo);
+            R<ErrorInfo> errorInfo = result.getErrorInfo();
+            RemoteConsumeBo updatedRemoteBo = result.getUpdatedRemoteBo();
+            String reason = "";
+            BigDecimal balance = updatedRemoteBo.getBalance();
+            String deptName =updatedRemoteBo.getDeptName();
+            if (R.isError(errorInfo)) {
+                // 应答失败
+                bussiRs = false;
+                reason = errorInfo.getMsg();
+                ErrorInfo data = errorInfo.getData();
+                if(data != null){
+                    reason = data.getMessage();
+                }
 
-            // 应答
-            ConsumptionEventConfirmBo confirmBo = new ConsumptionEventConfirmBo();
-            confirmBo.setSerialNo(consumptionEvent.getSerialNo());
-            confirmBo.setResult(bussiRs ?"success":"failed");
-//            confirmBo.setReason("platformException");
-            confirmBo.setMode(consumptionEvent.getMode());
-            log.info("消费机交易预处理请求事件,moshi:{}", consumptionEvent.getMode());
-             confirmBo.setName(consumptionEvent.getName());
-            confirmBo.setEmployeeNoString(consumptionEvent.getEmployeeNoString());
-            if(StringUtils.isNotBlank(consumptionEvent.getCardNo())){
-                confirmBo.setCardNo(consumptionEvent.getCardNo());
-            }else{
-                log.info("消费机交易预处理请求事件,方式:{}", "人脸或者二维码,不带卡号");
-            }
-            confirmBo.setActualPayment(consumptionEvent.getTotalPayment());
-            confirmBo.setBalanceBeforeDeduct("10000"); // 未扣款前的余额,要根据余额加上扣款金额,单位为分 ,金额模式必填
-//            confirmBo.setTimes(0); // 次数模式必填,有这个字段时可以不填
-                      /*
-            confirmBo.setQRCodeType("Alipay");
-            confirmBo.setPaymentOrderNo("1234567890");
-            confirmBo.setSecondaryConfirmType("none");*/
-//            confirmBo.setRemainingTimes(0); // 次数模式必填,有这个字段时必填
-
-           /* String broadcastVoice = "成功消费了"
-                + new BigDecimal(consumptionEvent.getTotalPayment()).divide(new BigDecimal("100.0"),  2, RoundingMode.HALF_UP)
-                +  "元";*/
-
-            String broadcastVoice = "count".equals(consumptionEvent.getMode())  ? "刷卡成功":"消费成功";
-
-
-            if(!bussiRs){
-                broadcastVoice = "count".equals(consumptionEvent.getMode())  ? "刷卡失败":"消费失败";
-                // 提示刷卡失败的原因
-                broadcastVoice += "就是不让你刷卡";
-                confirmBo.setReason("platformException");
             }
 
-            confirmBo.setCustomTTSBroadcastVoice(broadcastVoice);
-
-            /**
-             * 这可以显示刷卡人的信息,比如姓名,工号,卡号,金额,消费时间等
-             */
-            ConsumptionEventConfirmBo.ContentInfo contentInfo = new ConsumptionEventConfirmBo.ContentInfo();
-            contentInfo.setTitle("****这是一个标题*****");
-            contentInfo.setContent("*****这是一个偶然的内容********");
-            confirmBo.setContentInfo(contentInfo);
-
-            // confirmBo 转Map,如果字段为空则丢弃
-
-
-
-            HashMap<String, Object> rs = new HashMap<>(1);
-            rs.put("ConsumptionEventConfirm", confirmBo.toMap());
-            return rs;
+            // 应答
+            return answerEvent(consumptionEvent, bussiRs, balance, reason,deptName);
         }else if(TRANSACTION_CONFIRMING_REQUEST.equals(consumptionEvent.getMinor())){
             HashMap<String, Object> rs = new HashMap<>(1);
             rs.put("result", "success");
@@ -106,15 +101,107 @@ public class ConsumptionEventHandler implements HikEventHandler {
                 return rs;
             }
 
-            // todo 进行扣费逻辑处理
-
+            // 进行扣费逻辑处理 调用现有的消费入库的接口
+            RemoteConsumeBo remoteBo = setParamBo(receive, consumptionEvent);;
+            remoteBo.setRecordStatus(364L);
+            remoteBo.setRecordId(0L);
+            RemoteResultDto result = remoteConsumeService.dealHikUploadRecord(remoteBo);
+//            log.info("消费机消费请求确认事件,结果:{}", JSONObject.toJSONString(result.getUpdatedRemoteBo()));
+            R<ErrorInfo> errorInfo = result.getErrorInfo();
+            if (R.isError(errorInfo)) {
+                // 应答失败
+                rs.put("result", "failed");
+            }
             return rs;
-
-
         }else{
             log.warn("消费机事件,未知类型:{}", consumptionEvent.getMinor());
         }
 
         return null;
     }
+
+    private RemoteConsumeBo setParamBo(ConsumptionEventReceive receive, ConsumptionEventDetail consumptionEvent) {
+        RemoteConsumeBo remoteBo = new RemoteConsumeBo();
+        remoteBo.setUserNo(Long.valueOf(consumptionEvent.getEmployeeNoString()));
+        String cardNo = consumptionEvent.getCardNo();
+        if(StringUtils.isNotBlank(cardNo)){
+            remoteBo.setFactoryId(Long.valueOf(cardNo));
+        } else {
+            remoteBo.setFactoryId(0L);
+        }
+        Date dateTime = receive.getDateTime();
+        remoteBo.setConsumeDate(dateTime);
+        remoteBo.setTermRecordId(Long.valueOf(consumptionEvent.getSerialNo()));
+        if(StringUtils.isNotBlank(consumptionEvent.getTotalPayment())){
+            BigDecimal money = new BigDecimal(consumptionEvent.getTotalPayment()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
+            remoteBo.setConsumeMoney(money);
+        }else{
+            remoteBo.setConsumeMoney(BigDecimal.ZERO);
+        }
+
+        remoteBo.setTermMac(receive.getMacAddress());
+        remoteBo.setCardNo(0L);
+        remoteBo.setStatusFlag(4);
+        remoteBo.setCreditType(CreditTypeEnum.TERM_CONSUME.code());
+        return remoteBo;
+    }
+
+
+    public Map<String, Object>  answerEvent(ConsumptionEventDetail consumptionEvent, boolean answerResult, BigDecimal balance, String reason,String deptName)
+    {
+        ConsumptionEventConfirmBo confirmBo = new ConsumptionEventConfirmBo();
+        confirmBo.setSerialNo(consumptionEvent.getSerialNo());
+        confirmBo.setResult(answerResult ?"success":"failed");
+        confirmBo.setMode(consumptionEvent.getMode());
+        log.info("消费机交易预处理请求事件,消费模式:{}", consumptionEvent.getMode());
+        confirmBo.setName(consumptionEvent.getName());
+        confirmBo.setEmployeeNoString(consumptionEvent.getEmployeeNoString());
+        if(StringUtils.isNotBlank(consumptionEvent.getCardNo())){
+            confirmBo.setCardNo(consumptionEvent.getCardNo());
+        }else{
+            log.info("消费机交易预处理请求事件,方式:{}", "人脸或者二维码,不带卡号");
+        }
+        boolean isCountMode = ConsumeModeEnum.count.getCode().equals(consumptionEvent.getMode());
+        if(isCountMode){
+            confirmBo.setTimes(0);
+            if(answerResult){
+                confirmBo.setTimes(1);
+            }
+        }else{
+            if(answerResult){
+                confirmBo.setActualPayment(consumptionEvent.getTotalPayment());
+                BigDecimal balanceBeforeDeduct = balance.multiply(new BigDecimal("100")).add(new BigDecimal(consumptionEvent.getTotalPayment()));
+                confirmBo.setBalanceBeforeDeduct(balanceBeforeDeduct.toString().split("\\.")[0]); // 未扣款前的余额,要根据余额加上扣款金额,单位为分 ,金额模式必填
+            }else{
+                confirmBo.setActualPayment("0");
+                confirmBo.setBalanceBeforeDeduct("0");
+            }
+        }
+
+        String broadcastVoice = isCountMode ? "刷卡成功":"消费成功";
+        if(!answerResult){
+            broadcastVoice = isCountMode ? "刷卡失败":"消费失败";
+            // 提示刷卡失败的原因
+            broadcastVoice += reason;
+            confirmBo.setReason("platformException");
+        }
+        confirmBo.setCustomTTSBroadcastVoice(broadcastVoice);
+
+        /**
+         * 这可以显示刷卡人的信息,比如姓名,部门等
+         */
+        ConsumptionEventConfirmBo.ContentInfo contentInfo = new ConsumptionEventConfirmBo.ContentInfo();
+        contentInfo.setTitle(consumptionEvent.getName());
+        contentInfo.setContent(deptName);
+        if(!answerResult){
+            contentInfo.setTitle(consumptionEvent.getName()+ "  "+deptName);
+            contentInfo.setContent("消费失败原因:"+reason);
+        }
+        confirmBo.setContentInfo(contentInfo);
+
+        // confirmBo 转Map,如果字段为空则丢弃
+        HashMap<String, Object> rs = new HashMap<>(1);
+        rs.put("ConsumptionEventConfirm", confirmBo.toMap());
+        return rs;
+    }
 }

+ 67 - 58
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/event/handler/TransactionRecordEventHandler.java

@@ -1,29 +1,39 @@
 package org.dromara.server.hik.event.handler;
 
 import com.alibaba.fastjson.JSONObject;
+import io.seata.common.util.CollectionUtils;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.dromara.backstage.api.RemotePtXfTermService;
 import org.dromara.backstage.api.domain.vo.RemoteXfTermVo;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.DateUtils;
 import org.dromara.common.core.utils.file.FileUtils;
+import org.dromara.consume.api.RemoteConsumeService;
 import org.dromara.server.hik.domain.dto.DeviceDto;
+import org.dromara.server.hik.domain.vo.XfConsumeDetailOriginalVo;
+import org.dromara.server.hik.domain.vo.XfConsumeDetailVo;
+import org.dromara.server.hik.domain.vo.XfTermVo;
 import org.dromara.server.hik.enums.ContentTypeEnum;
+import org.dromara.server.hik.enums.ModeTypeEnum;
 import org.dromara.server.hik.event.HikEventHandler;
 import org.dromara.server.hik.event.domain.FileContent;
 import org.dromara.server.hik.event.domain.TransactionRecordEventConfirmBo;
 import org.dromara.server.hik.event.domain.TransactionRecordEventDetail;
 import org.dromara.server.hik.event.domain.TransactionRecordEventReceive;
+import org.dromara.server.hik.service.IConsumeDetailOriginalService;
 import org.dromara.server.hik.service.ISendDeviceService;
+import org.dromara.server.hik.service.IXfConsumeDetailService;
+import org.dromara.server.hik.service.IXfTermService;
 import org.dromara.server.hik.service.impl.SendDeviceServiceImpl;
 import org.dromara.server.hik.utils.DigestHttpUtil;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 
+import java.io.File;
 import java.math.BigDecimal;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.UUID;
+import java.util.*;
 
 import static org.dromara.server.hik.constant.HikApiConstants.TRANSACTION_RECORD_Event_confirm;
 
@@ -32,91 +42,90 @@ import static org.dromara.server.hik.constant.HikApiConstants.TRANSACTION_RECORD
 @RequiredArgsConstructor
 public class TransactionRecordEventHandler implements HikEventHandler {
 
-    @DubboReference
-    private RemotePtXfTermService ptXfTermService;
+    private final IXfTermService termService;
+
+    private final IXfConsumeDetailService  consumeDetailService;
+
+    private final IConsumeDetailOriginalService consumeDetailOriginalService;
 
-    private final DigestHttpUtil httpUtil;
+    @Value("${upload.upload-path}/")
+    private String uploadPath;
+
+    @DubboReference
+    private final RemoteConsumeService remoteConsumeService;
 
     @Override
     public Map<String, Object> handleEvent(JSONObject jsonObject, FileContent fileContent) {
         TransactionRecordEventReceive transactionRecordEventReceive = jsonObject.toJavaObject(TransactionRecordEventReceive.class);
         TransactionRecordEventDetail transactionRecordEvent = transactionRecordEventReceive.getTransactionRecordEvent();
-        log.info("消费机交易记录事件,类型,在线or离线:{},{}", transactionRecordEvent.getModeType(), jsonObject.toJSONString());
-
+        String modeType = transactionRecordEvent.getModeType();
+        log.info("消费机交易记录事件,类型,在线or离线:{},{}", modeType, jsonObject.toJSONString());
 
-        //查询设备信息
-        RemoteXfTermVo remoteXfTermVo = ptXfTermService.queryByMac(transactionRecordEventReceive.getMacAddress());
-        if (remoteXfTermVo == null) {
-            log.error("消费机交易记录事件,设备信息为空,mac:{}, 不存在系统中", transactionRecordEventReceive.getMacAddress());
-
-            // 直接应答失败
-//            answerEvent(transactionRecordEventReceive, false,null,  null);
-            return null;
+        //设备信息
+        XfTermVo termVo = termService.queryByMac(transactionRecordEventReceive.getMacAddress());
+        // 消费抓拍图片保存
+        if (fileContent != null) {
+            String fileName = termVo.getTermNo()+"_"+transactionRecordEvent.getSerialNo()+  "_" + DateUtils.dateTimeNow() + fileContent.getSuffix();
+            byte[] content = fileContent.getContent();
+            String image_dir_path = uploadPath + "snapshot-pic/";
+            File image_dir = new File(image_dir_path);
+            if(!image_dir.exists()){
+                image_dir.mkdir();
+            }
+            FileUtils.writeBytes(content, image_dir_path+fileName);
         }
-        DeviceDto deviceDto = SendDeviceServiceImpl.getDeviceDto(remoteXfTermVo);
-
-
 
-        boolean answerResult;
-        // todo 业务处理
+        BigDecimal balance = null;
+        // 这里只能应答成功,如果是业务异常(余额不足、脏数据等)失败,要存入表中,使用定时任务处理失败,所以即使失败的 也要回复成功
         // 如果是在线交易的,平台根据流水号判断当前交易记录事件是否为未处理事件,若为未处理事件,则平台进行处理并进行扣费;
         // 如果是离线交易记录事件,则平台进行处理并进行扣费;
-
-
-        //todo 这里只能应答成功,如果是业务异常(余额不足、脏数据等)失败,要存入表中,使用定时任务处理失败,所以即使失败的 也要回复成功
-
-        // 判断流水号是奇数还是偶数,这里做测试用
-        if (transactionRecordEvent.getSerialNo() % 2 == 0) {
-            answerResult = false;
-        } else {
-            answerResult = true;
-        }
-
-        // 图片保存
-        if (fileContent != null) {
-            String fileName = deviceDto.getTermNo()+"_"+transactionRecordEvent.getSerialNo()+  "_" + UUID.randomUUID().toString() + fileContent.getSuffix();
-            byte[] content = fileContent.getContent();
-            String image_dir_path = "C:\\image\\";
-            FileUtils.writeBytes(content, image_dir_path+fileName);
-            log.info("消费机交易记录事件,图片名称:{}", fileName);
+        try  {
+            if (ModeTypeEnum.offLine.getCode().equals(modeType)) {
+                // todo 离线交易,调用原始消费记录请求+消费记录入库的接口
+                // 如果失败要记录入库
+
+            }else{
+                // 在线交易,调用消费记录入库的接口
+                // 根据机号和交易记录流水号查询消费记录
+                List<XfConsumeDetailVo> vos = consumeDetailService.queryByTermNoAndRecordId(termVo.getTermNo(), transactionRecordEvent.getSerialNo().longValue());
+                if (CollectionUtils.isEmpty(vos)){
+                    // 调用消费记录入库的接口
+                    // todo 组装参数,调用消费记录入库的接口
+                    // 如果失败要记录入库
+
+                }else{
+                    XfConsumeDetailVo consumeDetailVo = vos.get(0);
+                    balance = consumeDetailVo.getCardValue();
+                }
+            }
+        } catch (Exception e) {
+            log.error("消费机交易记录事件处理异常:{}", e.getMessage());
+            // 失败,记录入库
         }
 
         //应答
-        return answerEvent(transactionRecordEventReceive, answerResult,new BigDecimal("1000"));
-
+        return answerEvent(transactionRecordEventReceive,balance);
     }
 
     /**
      * @param info 事件信息
-     * @param answerResult 应答结果
      * 应答事件 put /ISAPI/Consume/transactionRecordEventConfirm?format=json 应答TransactionRecordEvent
      */
-    public Map<String, Object>  answerEvent(TransactionRecordEventReceive info, boolean answerResult, BigDecimal balance){
+    public Map<String, Object> answerEvent(TransactionRecordEventReceive info, BigDecimal balance){
         TransactionRecordEventConfirmBo confirmBo = new TransactionRecordEventConfirmBo();
 
-//        String ipAddress = info.getIpAddress();
-//        Integer port = info.getPortNo() != null ? info.getPortNo() : 80;
-
         TransactionRecordEventDetail transactionDetail  = info.getTransactionRecordEvent();
         confirmBo.setSerialNo(transactionDetail.getSerialNo());
-        confirmBo.setResult(answerResult ? "success" : "failed");
+        confirmBo.setResult("success");
         confirmBo.setEmployeeNo(transactionDetail.getEmployeeNoString());
         //余额的单位为分
         if(balance == null){
             balance = new BigDecimal("0");
         }
-        confirmBo.setBalance(balance.multiply(new BigDecimal("100")).toString());
+        confirmBo.setBalance(balance.multiply(new BigDecimal("100")).toString().split("\\.")[0]);
 
         HashMap<String, Object> map = new HashMap<>(1);
-        if(answerResult){
-            map.put("TransactionRecordEventConfirm", confirmBo);
-        }else{
-            HashMap<String, Object> hashMap = new HashMap<>(2);
-            hashMap.put("serialNo", transactionDetail.getSerialNo());
-            hashMap.put("result", "failed");
-            map.put("TransactionRecordEventConfirm", hashMap);
-        }
-
+        map.put("TransactionRecordEventConfirm", confirmBo);
         return map;
     }
 }

+ 2 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/event/timedtask/HandleTask.java

@@ -20,5 +20,7 @@ public class HandleTask {
     {
         /*有离线的,也有非离线的,重复在执行下 处理处理TransactionRecordEvent的逻辑 */
         log.info("开始处理 TransactionRecordEvent事件,");
+        // todo 获取所有失败的 TransactionRecordEvent
+        // 设计t_xf_failed_record表  ID 、event_type、event_data、fail_msg、status、create_time、update_time、delete_flag、create_by、update_by
     }
 }

+ 21 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/mapper/ConsumeDetailOriginalMapper.java

@@ -0,0 +1,21 @@
+package org.dromara.server.hik.mapper;
+
+import org.apache.ibatis.annotations.Param;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.server.common.domain.consume.bo.ConsumptionBo;
+import org.dromara.server.hik.domain.XfConsumeDetailOriginal;
+import org.dromara.server.hik.domain.vo.XfConsumeDetailOriginalVo;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 原始消费记录Mapper接口
+ *
+ * @author LionLi
+ * @date 2024-08-15
+ */
+public interface ConsumeDetailOriginalMapper extends BaseMapperPlus<XfConsumeDetailOriginal, XfConsumeDetailOriginalVo> {
+
+//    List<ConsumptionBo> selectReconciliationData(@Param("consumeDate") Date consumeDate);
+}

+ 13 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/mapper/XfConsumeDetailMapper.java

@@ -0,0 +1,13 @@
+package org.dromara.server.hik.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.server.hik.domain.XfConsumeDetail;
+import org.dromara.server.hik.domain.vo.XfConsumeDetailVo;
+
+/**
+ * 消费明细Mapper接口
+ *
+ * @author LionLi
+ * @date 2024-08-15
+ */
+public interface XfConsumeDetailMapper extends BaseMapperPlus<XfConsumeDetail, XfConsumeDetailVo> {}

+ 15 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/mapper/XfTermMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.server.hik.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.server.hik.domain.XfTerm;
+import org.dromara.server.hik.domain.vo.XfTermVo;
+
+/**
+ * 消费设备Mapper接口
+ *
+ * @author bing
+ * @date 2024-08-21
+ */
+public interface XfTermMapper extends BaseMapperPlus<XfTerm, XfTermVo> {
+
+}

+ 66 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/IConsumeDetailOriginalService.java

@@ -0,0 +1,66 @@
+package org.dromara.server.hik.service;
+
+import org.dromara.server.common.domain.consume.bo.ConsumptionBo;
+import org.dromara.server.hik.domain.vo.XfConsumeDetailOriginalVo;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 原始消费记录Service接口
+ *
+ * @author LionLi
+ * @date 2024-08-15
+ */
+public interface IConsumeDetailOriginalService {
+    /**
+     * 查询原始消费记录
+     *
+     * @param originalId 主键
+     * @return 原始消费记录
+     */
+    XfConsumeDetailOriginalVo queryById(String originalId);
+
+
+
+    /**
+     * 根据记录ID查询原始消费记录
+     *
+     * @param recordId 记录ID
+     * @return 原始消费记录视图对象
+     */
+    XfConsumeDetailOriginalVo queryByRecordId(Long recordId);
+
+    /**
+     * 根据卡号、设备机号和消费金额查询原始消费记录
+     *
+     * @param cardNo       卡流水号
+     * @param termNo       设备机号
+     * @param termRecordId 机器流水号
+     * @param consumeMoney 消费金额
+     * @return 原始消费记录视图对象
+     */
+    XfConsumeDetailOriginalVo queryByConsumeMoney(Long cardNo, Long termNo, Long termRecordId, BigDecimal consumeMoney);
+
+
+    /**
+     * 根据卡号、设备机号、机器流水号和消费日期查询原始消费记录。
+     *
+     * @param cardNo       卡流水号
+     * @param termNo       设备机号
+     * @param termRecordId 机器流水号
+     * @param consumeDate  消费日期
+     * @return 原始消费记录视图对象,如果未找到匹配的记录则返回null
+     */
+    XfConsumeDetailOriginalVo queryByConsumeDate(Long cardNo, Long termNo, Long termRecordId, Date consumeDate);
+
+
+    /**
+     * 根据设备机号和机器流水号查询原始消费记录。
+     * @param termNo 设备机号
+     * @param termRecordId 机器流水号
+     * @return
+     */
+    XfConsumeDetailOriginalVo queryByTermNoAndRecordId(Long termNo, Long termRecordId);
+}

+ 36 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/IXfConsumeDetailService.java

@@ -0,0 +1,36 @@
+package org.dromara.server.hik.service;
+
+
+import org.dromara.server.hik.domain.vo.XfConsumeDetailOriginalVo;
+import org.dromara.server.hik.domain.vo.XfConsumeDetailVo;
+
+import java.util.List;
+
+/**
+ * 消费明细Service接口
+ *
+ * @author LionLi
+ * @date 2024-08-15
+ */
+public interface IXfConsumeDetailService {
+
+    /**
+     * 查询消费明细
+     *
+     * @param consumeId 主键
+     * @return 消费明细
+     */
+    XfConsumeDetailVo queryById(String consumeId);
+
+
+
+    XfConsumeDetailVo queryVoByOriginalId(String originalId);
+
+    /**
+     * 根据设备机号和机器流水号查询消费记录。
+     * @param termNo 设备机号
+     * @param termRecordId 机器流水号
+     * @return
+     */
+    List<XfConsumeDetailVo> queryByTermNoAndRecordId(Long termNo, Long termRecordId);
+}

+ 27 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/IXfTermService.java

@@ -0,0 +1,27 @@
+package org.dromara.server.hik.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.server.hik.domain.vo.XfTermVo;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 消费设备Service接口
+ *
+ * @author bing
+ * @date 2024-08-21
+ */
+public interface IXfTermService {
+
+
+    /**
+     * 根据设备的mac查询设备信息
+     */
+    XfTermVo queryByMac(String termMac);
+
+    public XfTermVo getByMac(String termMac);
+
+}

+ 117 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/impl/ConsumeDetailOriginalServiceImpl.java

@@ -0,0 +1,117 @@
+package org.dromara.server.hik.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;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.server.common.domain.consume.bo.ConsumptionBo;
+import org.dromara.server.hik.domain.XfConsumeDetailOriginal;
+import org.dromara.server.hik.domain.vo.XfConsumeDetailOriginalVo;
+import org.dromara.server.hik.mapper.ConsumeDetailOriginalMapper;
+import org.dromara.server.hik.service.IConsumeDetailOriginalService;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 原始消费记录Service业务层处理
+ *
+ * @author LionLi
+ * @date 2024-08-15
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class ConsumeDetailOriginalServiceImpl implements IConsumeDetailOriginalService {
+
+    private final ConsumeDetailOriginalMapper baseMapper;
+
+    /**
+     * 查询原始消费记录
+     *
+     * @param originalId 主键
+     * @return 原始消费记录
+     */
+    @Override
+    public XfConsumeDetailOriginalVo queryById(String originalId) {
+        return baseMapper.selectVoById(originalId);
+    }
+
+
+    /**
+     * 根据记录ID查询原始消费记录
+     *
+     * @param recordId 记录ID
+     * @return 原始消费记录视图对象
+     */
+    @Override
+    public XfConsumeDetailOriginalVo queryByRecordId(Long recordId) {
+        return baseMapper.selectVoOne(new LambdaQueryWrapper<XfConsumeDetailOriginal>().eq(XfConsumeDetailOriginal::getRecordId, recordId));
+    }
+
+    /**
+     * 根据卡号、设备机号和消费金额查询原始消费记录
+     *
+     * @param cardNo       卡流水号
+     * @param termNo       设备机号
+     * @param termRecordId 机器流水号
+     * @param consumeMoney 消费金额
+     * @return 原始消费记录视图对象
+     */
+    @Override
+    public XfConsumeDetailOriginalVo queryByConsumeMoney(Long cardNo, Long termNo, Long termRecordId, BigDecimal consumeMoney) {
+        return baseMapper.selectVoOne(new LambdaQueryWrapper<XfConsumeDetailOriginal>().eq(XfConsumeDetailOriginal::getCardNo, cardNo)
+                                          .eq(XfConsumeDetailOriginal::getTermNo, termNo)
+                                          .eq(XfConsumeDetailOriginal::getTermRecordId, termRecordId)
+                                          .eq(XfConsumeDetailOriginal::getConsumeMoney, consumeMoney));
+    }
+
+
+    /**
+     * 根据卡号、设备机号、机器流水号和消费日期查询原始消费记录。
+     *
+     * @param cardNo       卡流水号
+     * @param termNo       设备机号
+     * @param termRecordId 机器流水号
+     * @param consumeDate  消费日期
+     * @return 原始消费记录视图对象,如果未找到匹配的记录则返回null
+     */
+    @Override
+    public XfConsumeDetailOriginalVo queryByConsumeDate(Long cardNo, Long termNo, Long termRecordId, Date consumeDate) {
+        return baseMapper.selectVoOne(new LambdaQueryWrapper<XfConsumeDetailOriginal>().eq(XfConsumeDetailOriginal::getCardNo, cardNo)
+                                          .eq(XfConsumeDetailOriginal::getTermNo, termNo)
+                                          .eq(XfConsumeDetailOriginal::getTermRecordId, termRecordId)
+                                          .eq(XfConsumeDetailOriginal::getConsumeDate, consumeDate));
+    }
+
+    /**
+     * 根据设备机号和机器流水号查询原始消费记录。
+     *
+     * @param termNo       设备机号
+     * @param termRecordId 机器流水号
+     * @return
+     */
+    @Override
+    public XfConsumeDetailOriginalVo queryByTermNoAndRecordId(Long termNo, Long termRecordId) {
+        LambdaQueryWrapper<XfConsumeDetailOriginal> eq = Wrappers.lambdaQuery(XfConsumeDetailOriginal.class)
+            .eq(XfConsumeDetailOriginal::getTermNo, termNo)
+            .eq(XfConsumeDetailOriginal::getTermRecordId, termRecordId);
+        List<XfConsumeDetailOriginalVo> xfConsumeDetailVos = this.baseMapper.selectVoList(eq);
+        if(CollectionUtil.isEmpty(xfConsumeDetailVos)){
+            return null;
+        }
+        if (xfConsumeDetailVos.size() > 1){
+            log.error("根据设备机号: {} 和机器流水号: {} 找到多条原始请求数据",  termNo, termRecordId);
+            throw new ServiceException("根据设备机号和机器流水号找到多条原始请求数据");
+        }
+        return xfConsumeDetailVos.get(0);
+    }
+}

+ 1 - 1
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/impl/SendDeviceServiceImpl.java

@@ -137,7 +137,7 @@ public class SendDeviceServiceImpl implements ISendDeviceService {
      * @return 转换后的 DeviceDto 对象
      */
     @NotNull
-    public static DeviceDto getDeviceDto(@NotNull RemoteXfTermVo termVo) {
+    private static DeviceDto getDeviceDto(@NotNull RemoteXfTermVo termVo) {
         DeviceDto dto = new DeviceDto();
         dto.setTermNo(termVo.getTermNo().intValue());
         dto.setAdminName(termVo.getAdminName());

+ 71 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/impl/XfConsumeDetailServiceImpl.java

@@ -0,0 +1,71 @@
+package org.dromara.server.hik.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;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.server.hik.domain.XfConsumeDetail;
+import org.dromara.server.hik.domain.vo.XfConsumeDetailVo;
+import org.dromara.server.hik.mapper.XfConsumeDetailMapper;
+import org.dromara.server.hik.service.IXfConsumeDetailService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 消费明细Service业务层处理
+ *
+ * @author LionLi
+ * @date 2024-08-15
+ */
+@RequiredArgsConstructor
+@Service
+@Slf4j
+public class XfConsumeDetailServiceImpl implements IXfConsumeDetailService {
+
+    private final XfConsumeDetailMapper baseMapper;
+
+    /**
+     * 查询消费明细
+     *
+     * @param consumeId 主键
+     * @return 消费明细
+     */
+    @Override
+    public XfConsumeDetailVo queryById(String consumeId) {
+        return baseMapper.selectVoById(consumeId);
+    }
+
+
+
+    @Override
+    public XfConsumeDetailVo queryVoByOriginalId(String originalId) {
+        LambdaQueryWrapper<XfConsumeDetail> eq = Wrappers.lambdaQuery(XfConsumeDetail.class).eq(XfConsumeDetail::getOriginalId, originalId);
+        List<XfConsumeDetailVo> vos = this.baseMapper.selectVoList(eq);
+        if(CollectionUtil.isEmpty(vos)){
+            return null;
+        }
+        return vos.get(0);
+    }
+
+    /**
+     * 根据设备机号和机器流水号查询消费记录。
+     *
+     * @param termNo       设备机号
+     * @param termRecordId 机器流水号
+     * @return
+     */
+    @Override
+    public List<XfConsumeDetailVo> queryByTermNoAndRecordId(Long termNo, Long termRecordId) {
+        LambdaQueryWrapper<XfConsumeDetail> eq = Wrappers.lambdaQuery(XfConsumeDetail.class)
+            .eq(XfConsumeDetail::getTermNo, termNo).eq(XfConsumeDetail::getTermRecordId, termRecordId)
+            .orderByDesc(XfConsumeDetail::getCreateTime);
+        return this.baseMapper.selectVoList(eq);
+    }
+}

+ 87 - 0
ruoyi-server/ruoyi-server-hik/src/main/java/org/dromara/server/hik/service/impl/XfTermServiceImpl.java

@@ -0,0 +1,87 @@
+package org.dromara.server.hik.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+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.config.DefaultConfig;
+import org.dromara.common.core.constant.CacheNames;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.server.hik.domain.XfTerm;
+import org.dromara.server.hik.domain.vo.XfTermVo;
+import org.dromara.server.hik.mapper.XfTermMapper;
+import org.dromara.server.hik.service.IXfTermService;
+import org.springframework.stereotype.Service;
+
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 消费设备Service业务层处理
+ *
+ * @author bing
+ * @date 2024-08-21
+ */
+@RequiredArgsConstructor
+@Service
+public class XfTermServiceImpl implements IXfTermService {
+
+    private final XfTermMapper baseMapper;
+    private final DefaultConfig defaultConfig;
+
+
+    @Override
+    public XfTermVo queryByMac(String termMac) {
+        boolean existsObject = RedisUtils.isExistsObject(CacheNames.PT_TERM_MAC_MAP);
+        if(!existsObject){
+            // 缓存设备mac地址
+            XfTermVo byMac = getByMac(termMac);
+            if(ObjUtil.isNull(byMac)){
+                return null;
+            }
+            Map<String, XfTermVo> map = new HashMap<>();
+            map.put(termMac, byMac);
+            RedisUtils.setCacheMap(CacheNames.PT_TERM_MAC_MAP, map);
+            RedisUtils.expire(CacheNames.PT_TERM_MAC_MAP, Duration.ofHours(1));
+            return byMac;
+        }
+        Object value = RedisUtils.getCacheMapValue(CacheNames.PT_TERM_MAC_MAP, termMac);
+        if(ObjUtil.isNull(value)){
+            // 缓存设备mac地址
+            XfTermVo byMac = getByMac(termMac);
+            if(ObjUtil.isNull(byMac)){
+                return null;
+            }
+            RedisUtils.setCacheMapValue(CacheNames.PT_TERM_MAC_MAP, termMac, byMac);
+            return byMac;
+        }
+        return (XfTermVo) value;
+
+    }
+
+    @Override
+    public XfTermVo getByMac(String termMac){
+        List<XfTermVo> termVos = TenantHelper.ignore(() -> {
+            return baseMapper.selectVoList(new LambdaQueryWrapper<XfTerm>().eq(XfTerm::getTermMac, termMac));
+        });
+        if(CollectionUtil.isEmpty(termVos)){
+            return null;
+        }else if(termVos.size()>1){
+            throw new ServiceException("设备mac地址重复");
+        }
+        return termVos.get(0);
+    }
+
+}

+ 57 - 0
ruoyi-server/ruoyi-server-hik/src/main/resources/mapper/hik/ConsumeDetailOriginalMapper.xml

@@ -0,0 +1,57 @@
+<?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.hik.mapper.ConsumeDetailOriginalMapper">
+
+    <resultMap type="org.dromara.server.hik.domain.XfConsumeDetailOriginal" id="ConsumeDetailOriginalResult">
+            <result property="originalId"    column="original_id"    />
+            <result property="tenantId"    column="tenant_id"    />
+            <result property="recordId"    column="record_id"    />
+            <result property="userId"    column="user_id"    />
+            <result property="userNumb"    column="user_numb"    />
+            <result property="realName"    column="real_name"    />
+            <result property="deptId"    column="dept_id"    />
+            <result property="deptName"    column="dept_name"    />
+            <result property="consumeDate"    column="consume_date"    />
+            <result property="consumeMoney"    column="consume_money"    />
+            <result property="cardNo"    column="card_no"    />
+            <result property="factoryId"    column="factory_id"    />
+            <result property="cardValue"    column="card_value"    />
+            <result property="cardCount"    column="card_count"    />
+            <result property="consumeBalance"    column="consume_balance"    />
+            <result property="termNo"    column="term_no"    />
+            <result property="termName"    column="term_name"    />
+            <result property="termRecordId"    column="term_record_id"    />
+            <result property="analysisFlag"    column="analysis_flag"    />
+            <result property="dataFlag"    column="data_flag"    />
+            <result property="statusFlag"    column="status_flag"    />
+            <result property="digitalSign"    column="digital_sign"    />
+            <result property="waterValue"    column="water_value"    />
+            <result property="waterHistoryValue"    column="water_history_value"    />
+            <result property="waterDaySum"    column="water_day_sum"    />
+            <result property="waterErrValue"    column="water_err_value"    />
+            <result property="waterErrMoney"    column="water_err_money"    />
+            <result property="operatorId"    column="operator_id"    />
+            <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>
+
+    <!--<select id="selectReconciliationData" resultType="org.dromara.server.common.domain.consume.bo.ConsumptionBo">
+        SELECT term_no as termNo,user_numb as userNumb,card_no as cardNo,factory_id as factoryId
+        ,consume_date as consumeDate,consume_money as consumeMoney,term_record_id as termRecordId,status_flag as
+        statusFlag,record_id as recordId
+        FROM t_xf_consumedetailoriginal txc
+        <where>
+            and original_id NOT IN (SELECT original_id FROM t_xf_consumedetail where consume_date>#{consumeDate})
+            <if test="consumeDate != null">
+                AND txc.consume_date>#{consumeDate}
+            </if>
+        </where>
+        ORDER BY consume_date ASC;
+    </select>-->
+</mapper>

+ 49 - 0
ruoyi-server/ruoyi-server-hik/src/main/resources/mapper/hik/XfConsumeDetailMapper.xml

@@ -0,0 +1,49 @@
+<?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.hik.mapper.XfConsumeDetailMapper">
+
+    <resultMap type="org.dromara.server.hik.domain.XfConsumeDetail" id="XfConsumeDetailResult">
+            <result property="consumeId"    column="consume_id"    />
+            <result property="tenantId"    column="tenant_id"    />
+            <result property="originalId"    column="original_id"    />
+            <result property="recordId"    column="record_id"    />
+            <result property="userId"    column="user_id"    />
+            <result property="userNumb"    column="user_numb"    />
+            <result property="realName"    column="real_name"    />
+            <result property="deptId"    column="dept_id"    />
+            <result property="deptName"    column="dept_name"    />
+            <result property="consumeDate"    column="consume_date"    />
+            <result property="consumeMoney"    column="consume_money"    />
+            <result property="cardNo"    column="card_no"    />
+            <result property="factoryId"    column="factory_id"    />
+            <result property="cardValue"    column="card_value"    />
+            <result property="cardCount"    column="card_count"    />
+            <result property="consumeBalance"    column="consume_balance"    />
+            <result property="termNo"    column="term_no"    />
+            <result property="termName"    column="term_name"    />
+            <result property="termRecordId"    column="term_record_id"    />
+            <result property="analysisFlag"    column="analysis_flag"    />
+            <result property="statusFlag"    column="status_flag"    />
+            <result property="operatorId"    column="operator_id"    />
+            <result property="operatorName"    column="operator_name"    />
+            <result property="accountId"    column="account_id"    />
+            <result property="accountName"    column="account_name"    />
+            <result property="roomId"    column="room_id"    />
+            <result property="roomName"    column="room_name"    />
+            <result property="bagType"    column="bag_type"    />
+            <result property="mealType"    column="meal_type"    />
+            <result property="mealName"    column="meal_name"    />
+            <result property="smsSend"    column="sms_send"    />
+            <result property="recordStatus"    column="record_status"    />
+            <result property="syncStatus"    column="sync_status"    />
+            <result property="remark"    column="remark"    />
+            <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>

+ 80 - 0
ruoyi-server/ruoyi-server-hik/src/main/resources/mapper/hik/XfTermMapper.xml

@@ -0,0 +1,80 @@
+<?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.hik.mapper.XfTermMapper">
+
+    <resultMap type="org.dromara.server.hik.domain.XfTerm" id="XfTermResult">
+            <result property="termId"    column="term_id"    />
+            <result property="tenantId"    column="tenant_id"    />
+            <result property="termNo"    column="term_no"    />
+            <result property="termName"    column="term_name"    />
+            <result property="roomId"    column="room_id"    />
+            <result property="accountId"    column="account_id"    />
+            <result property="termType"    column="term_type"    />
+            <result property="qrCode"    column="qr_code"    />
+            <result property="stationId"    column="station_id"    />
+            <result property="consumeType"    column="consume_type"    />
+            <result property="autoDown"    column="auto_down"    />
+            <result property="useType"    column="use_type"    />
+            <result property="cardType"    column="card_type"    />
+            <result property="termIp"    column="term_ip"    />
+            <result property="termMac"    column="term_mac"    />
+            <result property="commPort"    column="comm_port"    />
+            <result property="mask"    column="mask"    />
+            <result property="serverIp"    column="server_ip"    />
+            <result property="serverPort"    column="server_port"    />
+            <result property="gatewayIp"    column="gateway_ip"    />
+            <result property="beatInterval"    column="beat_interval"    />
+            <result property="timeout"    column="timeout"    />
+            <result property="offlineTime"    column="offline_time"    />
+            <result property="advParam"    column="adv_param"    />
+            <result property="posParam"    column="pos_param"    />
+            <result property="rateParam"    column="rate_param"    />
+            <result property="workMode"    column="work_mode"    />
+            <result property="openMode"    column="open_mode"    />
+            <result property="maxCardMoney"    column="max_card_money"    />
+            <result property="constantValue"    column="constant_value"    />
+            <result property="rationZero"    column="ration_zero"    />
+            <result property="rationOne"    column="ration_one"    />
+            <result property="rationTwo"    column="ration_two"    />
+            <result property="rationThree"    column="ration_three"    />
+            <result property="rationFour"    column="ration_four"    />
+            <result property="rationFive"    column="ration_five"    />
+            <result property="rationSix"    column="ration_six"    />
+            <result property="rationSeven"    column="ration_seven"    />
+            <result property="rationEight"    column="ration_eight"    />
+            <result property="rationNine"    column="ration_nine"    />
+            <result property="dayCount"    column="day_count"    />
+            <result property="dayMoney"    column="day_money"    />
+            <result property="mealCount"    column="meal_count"    />
+            <result property="singleMoney"    column="single_money"    />
+            <result property="breakfastMoney"    column="breakfast_money"    />
+            <result property="lunchMoney"    column="lunch_money"    />
+            <result property="supperMoney"    column="supper_money"    />
+            <result property="nightMoney"    column="night_money"    />
+            <result property="breakfastBegin"    column="breakfast_begin"    />
+            <result property="breakfastEnd"    column="breakfast_end"    />
+            <result property="lunchBegin"    column="lunch_begin"    />
+            <result property="lunchEnd"    column="lunch_end"    />
+            <result property="supperBegin"    column="supper_begin"    />
+            <result property="supperEnd"    column="supper_end"    />
+            <result property="nightBegin"    column="night_begin"    />
+            <result property="nightEnd"    column="night_end"    />
+            <result property="swipeInterval"    column="swipe_interval"    />
+            <result property="termValidity"    column="term_validity"    />
+            <result property="recordId"    column="record_id"    />
+            <result property="uploadTime"    column="upload_time"    />
+            <result property="blackDownTime"    column="black_down_time"    />
+            <result property="lastCheck"    column="last_check"    />
+            <result property="rebootTime"    column="reboot_time"    />
+            <result property="remark"    column="remark"    />
+            <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>