Explorar el Código

Merge branch 'test'

Mr.qian hace 1 mes
padre
commit
563ea463d4
Se han modificado 38 ficheros con 1466 adiciones y 279 borrados
  1. 17 35
      cif-api/src/main/java/com/txz/cif/dto/EarningsDTO.java
  2. 35 0
      cif-api/src/main/java/com/txz/cif/dto/RecycleInfoDTO.java
  3. 47 39
      cif-api/src/main/java/com/txz/cif/dto/UserDTO.java
  4. 74 0
      cif-api/src/main/java/com/txz/cif/dto/tkpay/CheckSignDTO.java
  5. 74 0
      cif-api/src/main/java/com/txz/cif/dto/tkpay/CheckWithdrawSignDTO.java
  6. 64 0
      cif-api/src/main/java/com/txz/cif/dto/tkpay/TKCreateWithdrawDTO.java
  7. 40 0
      cif-api/src/main/java/com/txz/cif/dto/tkpay/TKPayCreateOrderDTO.java
  8. 51 0
      cif-api/src/main/java/com/txz/cif/dto/tkpay/TKPayOrderDTO.java
  9. 51 0
      cif-api/src/main/java/com/txz/cif/dto/tkpay/TKWithdrawDTO.java
  10. 3 0
      cif-api/src/main/java/com/txz/cif/enums/BizTypeEnum.java
  11. 5 5
      cif-api/src/main/java/com/txz/cif/service/RedEnvelopeDubboService.java
  12. 23 5
      cif-api/src/main/java/com/txz/cif/service/UserDubboService.java
  13. 24 0
      cif-service/src/main/java/com/txz/cif/configurer/TKPayConfig.java
  14. 12 3
      cif-service/src/main/java/com/txz/cif/constants/MyConstants.java
  15. 2 0
      cif-service/src/main/java/com/txz/cif/dao/RedEnvelopeMapper.java
  16. 20 3
      cif-service/src/main/java/com/txz/cif/dubbo/client/OperatingConfigDubboServiceClient.java
  17. 1 1
      cif-service/src/main/java/com/txz/cif/dubbo/impl/AccountDubboServiceImpl.java
  18. 14 0
      cif-service/src/main/java/com/txz/cif/dubbo/impl/RedEnvelopeDubboServiceImpl.java
  19. 34 9
      cif-service/src/main/java/com/txz/cif/dubbo/impl/UserDubboServiceImpl.java
  20. 10 0
      cif-service/src/main/java/com/txz/cif/model/User.java
  21. 2 0
      cif-service/src/main/java/com/txz/cif/service/RedEnvelopeService.java
  22. 24 0
      cif-service/src/main/java/com/txz/cif/service/TKPayService.java
  23. 9 0
      cif-service/src/main/java/com/txz/cif/service/impl/RedEnvelopeServiceImpl.java
  24. 75 0
      cif-service/src/main/java/com/txz/cif/service/impl/TKPayServiceImpl.java
  25. 104 57
      cif-service/src/main/java/com/txz/cif/service/impl/WithdrawRecordServiceImpl.java
  26. 110 0
      cif-service/src/main/java/com/txz/cif/util/TKPayUtil.java
  27. 107 9
      cif-service/src/main/java/com/txz/cif/web/RechargeRecordApiController.java
  28. 13 0
      cif-service/src/main/java/com/txz/cif/web/UserApiController.java
  29. 141 36
      cif-service/src/main/java/com/txz/cif/web/WithdrawRecordApiController.java
  30. 78 0
      cif-service/src/main/java/com/txz/cif/web/ro/TKPayNotifyRO.java
  31. 79 0
      cif-service/src/main/java/com/txz/cif/web/ro/TKWithdrawCallbackRO.java
  32. 31 0
      cif-service/src/main/java/com/txz/cif/web/vo/RecycleInfoVO.java
  33. 77 77
      cif-service/src/main/resources/mapper/RedEnvelopeMapper.xml
  34. 1 0
      cif-service/src/main/resources/static/i18n/messages_bn_BD.properties
  35. 1 0
      cif-service/src/main/resources/static/i18n/messages_en.properties
  36. 1 0
      cif-service/src/main/resources/static/i18n/messages_en_US.properties
  37. 1 0
      cif-service/src/main/resources/static/i18n/messages_zh_CN.properties
  38. 11 0
      sql/20260107/20260107_recycle.sql

+ 17 - 35
cif-api/src/main/java/com/txz/cif/dto/EarningsDTO.java

@@ -1,59 +1,41 @@
 /*
-*
-* @date 2025-07-15
-*/
+ *
+ * @date 2025-07-15
+ */
 package com.txz.cif.dto;
 
+import lombok.Data;
+
 import java.io.Serializable;
 import java.math.BigDecimal;
-import java.util.Date;
 
+@Data
 public class EarningsDTO implements Serializable {
-
+    
     /**
      * 订单号
      */
     private String orderNo;
-
-
+    
+    
     /**
      * 用户id
      */
     private Long userId;
-
+    
     /**
      * 收益金额
      */
     private BigDecimal earnings;
-
-
-
+    
+    /**
+     * 业务类型
+     */
+    private Integer bizType;
+    
     /**
      * c_red_envelope
      */
     private static final long serialVersionUID = 1L;
-
-    public String getOrderNo() {
-        return orderNo;
-    }
-
-    public void setOrderNo(String orderNo) {
-        this.orderNo = orderNo;
-    }
-
-    public Long getUserId() {
-        return userId;
-    }
-
-    public void setUserId(Long userId) {
-        this.userId = userId;
-    }
-
-    public BigDecimal getEarnings() {
-        return earnings;
-    }
-
-    public void setEarnings(BigDecimal earnings) {
-        this.earnings = earnings;
-    }
+    
 }

+ 35 - 0
cif-api/src/main/java/com/txz/cif/dto/RecycleInfoDTO.java

@@ -0,0 +1,35 @@
+package com.txz.cif.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ *
+ * @author: MTD®️
+ * @date: 2026/1/4
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class RecycleInfoDTO {
+    
+    /**
+     * 订单回收比例
+     */
+    private BigDecimal recycleProportion;
+    
+    /**
+     * 成功订单总数
+     */
+    private BigDecimal successTotalOrderNum;
+    
+    /**
+     * 回收数量
+     */
+    private BigDecimal recycleNum;
+}

+ 47 - 39
cif-api/src/main/java/com/txz/cif/dto/UserDTO.java

@@ -1,154 +1,162 @@
 /*
-*
-* UserDTO.java
-* Copyright(C) 2017-2020 fendo公司
-* @date 2022-11-02
-*/
+ *
+ * UserDTO.java
+ * Copyright(C) 2017-2020 fendo公司
+ * @date 2022-11-02
+ */
 package com.txz.cif.dto;
 
-import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
-import javax.persistence.Column;
 import java.io.Serializable;
 import java.util.Date;
 
 @Data
 public class UserDTO implements Serializable {
     /**
-     * 
+     *
      */
     private Long id;
-
+    
     /**
      * 登录账号
      */
     private String loginAccount;
-
+    
     /**
      * 用户所属平台 创建用户时定义
      */
     private String appCode;
-
+    
     /**
      * 资金账户(目前只有一个)
      */
     private Long accountId;
-
+    
     /**
      * 单位
      */
     private String unit;
-
+    
     /**
      * 名字
      */
     private String name;
-
+    
     /**
      * 身份证号
      */
     private String identity;
-
+    
     /**
      * 状态 0未启用 1正常 2禁用 3停用 4锁定
      */
     private Byte status;
-
+    
     /**
      * 工作状态 1空闲 2不接诊
      */
     private Byte workStatus;
-
+    
     /**
      * 电话
      */
     private String phoneNo;
-
+    
     /**
      * 邮箱
      */
     private String email;
-
+    
     /**
      * 密码
      */
     private String pwd;
-
+    
     /**
      * 盐
      */
     private String salt;
-
+    
     private Integer vipLevel;
-
+    
     /**
      * 头像
      */
     private String headPic;
-
+    
     /**
      * 居住地址
      */
     private String address;
-
+    
     /**
      * 性别 0未知 1男 2女
      */
     private Byte sex;
-
+    
     /**
      * 所属省份代码
      */
     private String provinceCode;
-
+    
     /**
      * 所属城市码
      */
     private String cityCode;
-
+    
     /**
      * 所属区域代码
      */
     private String areaCode;
-
+    
     /**
      * 创建人
      */
     private String createUser;
-
+    
     /**
      * 更新人
      */
     private String updateUser;
-
+    
     /**
      * 更新时间
      */
     private Date updateTime;
-
+    
     /**
      * 创建时间
      */
     private Date createTime;
-
+    
     /**
      * 版本号
      */
     private String version;
-
+    
     private String signUrl;
-
+    
     private Integer hasWithdraw;
-
+    
     private Integer hasLogin;
-
+    
     private Integer hasOrder;
-
+    
+    /**
+     * 总中奖次数
+     */
+    private Integer totalNumberOfWin;
+    
+    /**
+     * 总回收次数
+     */
+    private Integer totalNumberOfRecycle;
+    
     /**
      * c_user
      */
     private static final long serialVersionUID = 1L;
-
-
+    
+    
 }

+ 74 - 0
cif-api/src/main/java/com/txz/cif/dto/tkpay/CheckSignDTO.java

@@ -0,0 +1,74 @@
+package com.txz.cif.dto.tkpay;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/2
+ */
+@Data
+public class CheckSignDTO {
+    
+    /**
+     *
+     */
+    @JSONField(name = "mchid")
+    private String mchid;
+    
+    /**
+     * 代付交易状态:
+     * 5-代付交易成功;
+     * 6-代付交易失败;
+     * 3-交易中(实际上不会同步)
+     */
+    @JSONField(name = "status")
+    private Integer status;
+    
+    /**
+     * 当status=6时,返回代付失败的原因
+     */
+    @JSONField(name = "msg")
+    private String msg;
+    
+    /**
+     * 三方单号
+     */
+    @JSONField(name = "trade_no")
+    private String tradeNo;
+    
+    /**
+     * 商户单号
+     */
+    @JSONField(name = "out_trade_no")
+    private String outTradeNo;
+    
+    @JSONField(name = "utr")
+    private String utr;
+    
+    /**
+     * 代付订单金额
+     */
+    @JSONField(name = "amount")
+    private String amount;
+    
+    /**
+     * 持卡人姓名
+     */
+    @JSONField(name = "to_holder")
+    private String toHolder;
+    
+    /**
+     * 银行卡卡号
+     */
+    @JSONField(name = "to_account")
+    private String toAccount;
+    
+    /**
+     * 银行类型,暂时仅支持:
+     * bKash; Nagad 两种类型
+     */
+    @JSONField(name = "to_bank_type")
+    private String toBankType;
+    
+}

+ 74 - 0
cif-api/src/main/java/com/txz/cif/dto/tkpay/CheckWithdrawSignDTO.java

@@ -0,0 +1,74 @@
+package com.txz.cif.dto.tkpay;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/2
+ */
+@Data
+public class CheckWithdrawSignDTO {
+    
+    /**
+     *
+     */
+    @JSONField(name = "mchid")
+    private String mchid;
+    
+    /**
+     * 代付交易状态:
+     * 5-代付交易成功;
+     * 6-代付交易失败;
+     * 3-交易中(实际上不会同步)
+     */
+    @JSONField(name = "status")
+    private Integer status;
+    
+    /**
+     * 当status=6时,返回代付失败的原因
+     */
+    @JSONField(name = "msg")
+    private String msg;
+    
+    /**
+     * 三方单号
+     */
+    @JSONField(name = "trade_no")
+    private String tradeNo;
+    
+    /**
+     * 商户单号
+     */
+    @JSONField(name = "out_trade_no")
+    private String outTradeNo;
+    
+    @JSONField(name = "utr")
+    private String utr;
+    
+    /**
+     * 代付订单金额
+     */
+    @JSONField(name = "amount")
+    private String amount;
+    
+    /**
+     * 持卡人姓名
+     */
+    @JSONField(name = "to_holder")
+    private String toHolder;
+    
+    /**
+     * 银行卡卡号
+     */
+    @JSONField(name = "to_account")
+    private String toAccount;
+    
+    /**
+     * 银行类型,暂时仅支持:
+     * bKash; Nagad 两种类型
+     */
+    @JSONField(name = "to_bank_type")
+    private String toBankType;
+    
+}

+ 64 - 0
cif-api/src/main/java/com/txz/cif/dto/tkpay/TKCreateWithdrawDTO.java

@@ -0,0 +1,64 @@
+package com.txz.cif.dto.tkpay;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/3
+ */
+@Data
+@Builder
+public class TKCreateWithdrawDTO {
+    
+    /**
+     *
+     */
+    @JSONField(name = "mchid")
+    private String mchid;
+    
+    /**
+     * 商户传入订单号
+     */
+    @JSONField(name = "out_trade_no")
+    private String outTradeNo;
+    
+    /**
+     * 订单金额
+     */
+    @JSONField(name = "amount")
+    private String amount;
+    
+    /**
+     * 持卡人姓名
+     */
+    @JSONField(name = "to_holder")
+    private String toHolder;
+    
+    /**
+     * 银行卡卡号
+     */
+    @JSONField(name = "to_account")
+    private String toAccount;
+    
+    /**
+     * 银行类型,暂时仅支持:
+     * bKash; Nagad 两种类型
+     */
+    @JSONField(name = "to_bank_type")
+    private String toBankType;
+    
+    /**
+     * 回调地址
+     */
+    @JSONField(name = "notify_url")
+    private String notifyUrl;
+    
+    /**
+     * 签名
+     */
+    @JSONField(name = "sign")
+    private String sign;
+    
+}

+ 40 - 0
cif-api/src/main/java/com/txz/cif/dto/tkpay/TKPayCreateOrderDTO.java

@@ -0,0 +1,40 @@
+package com.txz.cif.dto.tkpay;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ *
+ * @author: MTD®️
+ * @date: 2026/1/4
+ */
+@Data
+@Builder
+public class TKPayCreateOrderDTO {
+    
+    /**
+     * 商户id
+     */
+    private String mchid;
+    
+    /**
+     * 通道id
+     */
+    private String payId;
+    
+    /**
+     * 订单号
+     */
+    private String outTradeNo;
+    
+    /**
+     * 金额
+     */
+    private String amount;
+    
+    /**
+     * 回调地址
+     */
+    private String notifyUrl;
+    
+}

+ 51 - 0
cif-api/src/main/java/com/txz/cif/dto/tkpay/TKPayOrderDTO.java

@@ -0,0 +1,51 @@
+package com.txz.cif.dto.tkpay;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ *
+ * @author: MTD®️
+ * @date: 2026/1/4
+ */
+@Data
+@Builder
+public class TKPayOrderDTO {
+    
+    /**
+     * 商户id
+     */
+    @JSONField(name = "mchid")
+    private String mchid;
+    
+    /**
+     * 通道id
+     */
+    @JSONField(name = "pay_id")
+    private String payId;
+    
+    /**
+     * 订单号
+     */
+    @JSONField(name = "out_trade_no")
+    private String outTradeNo;
+    
+    /**
+     * 金额
+     */
+    @JSONField(name = "amount")
+    private String amount;
+    
+    /**
+     * 回调地址
+     */
+    @JSONField(name = "notify_url")
+    private String notifyUrl;
+    
+    /**
+     * 签名
+     */
+    @JSONField(name = "sign")
+    private String sign;
+}

+ 51 - 0
cif-api/src/main/java/com/txz/cif/dto/tkpay/TKWithdrawDTO.java

@@ -0,0 +1,51 @@
+package com.txz.cif.dto.tkpay;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/3
+ */
+@Data
+@Builder
+public class TKWithdrawDTO {
+    
+    /**
+     *
+     */
+    private String mchid;
+    
+    /**
+     * 商户传入订单号
+     */
+    private String outTradeNo;
+    
+    /**
+     * 订单金额
+     */
+    private String amount;
+    
+    /**
+     * 持卡人姓名
+     */
+    private String toHolder;
+    
+    /**
+     * 银行卡卡号
+     */
+    private String toAccount;
+    
+    /**
+     * 银行类型,暂时仅支持:
+     * bKash; Nagad 两种类型
+     */
+    private String toBankType;
+    
+    /**
+     * 回调地址
+     */
+    private String notifyUrl;
+    
+    
+}

+ 3 - 0
cif-api/src/main/java/com/txz/cif/enums/BizTypeEnum.java

@@ -33,6 +33,8 @@ public enum BizTypeEnum {
     
     PROMOTION_COMMISSION(5006, "直推佣金"),
     
+    RECYCLE(5007, "回收"),
+    
     REFUND_RECHARGE(6001, "充值返点"),
     
     WITHDRAWAL_FEE(7001, "提现手续费"),
@@ -72,6 +74,7 @@ public enum BizTypeEnum {
         ret.add(BizTypeEnum.FIRST_COMMISSION);
         ret.add(BizTypeEnum.SECONDARY_COMMISSION);
         ret.add(BizTypeEnum.PROMOTION_COMMISSION);
+        ret.add(BizTypeEnum.RECYCLE);
         return ret;
     }
     

+ 5 - 5
cif-api/src/main/java/com/txz/cif/service/RedEnvelopeDubboService.java

@@ -3,14 +3,14 @@ package com.txz.cif.service;
 
 import com.txz.cif.dto.EarningsDTO;
 import com.txz.cif.dto.OrderParam;
-import com.txz.cif.dto.Result;
-import com.txz.cif.dto.UserDTO;
 
 import java.util.List;
 
 public interface RedEnvelopeDubboService {
-
-
+    
+    
     List<EarningsDTO> getEarningsByOrders(List<OrderParam> params);
-
+    
+    List<EarningsDTO> getEarningsInfoByOrders(List<OrderParam> params);
+    
 }

+ 23 - 5
cif-api/src/main/java/com/txz/cif/service/UserDubboService.java

@@ -8,20 +8,38 @@ import com.txz.cif.dto.UserDTO;
 import java.util.List;
 
 public interface UserDubboService {
-
+    
     /**
      * 验证登录
+     *
      * @param token
+     *
      * @return
      */
     Result<UserDTO> validateLogin(String token);
-
+    
     UserDTO getUser(Long userId);
-
+    
     void updateLastTime(Long userId);
     
     List<UserDTO> getUsersByIds(List<Long> userIds);
-
+    
     ConfigMemberDTO getConfigByLevel(Integer level);
-
+    
+    /**
+     * 添加中奖次数
+     */
+    Boolean addNumberOfWin(Long userId);
+    
+    /**
+     * 添加中奖次数
+     */
+    Boolean addNumberOfWin(Long userId, Integer number);
+    
+    /**
+     * 添加回收次数
+     */
+    Boolean addNumberOfRecycle(Long userId, Integer number);
+    
+    
 }

+ 24 - 0
cif-service/src/main/java/com/txz/cif/configurer/TKPayConfig.java

@@ -0,0 +1,24 @@
+package com.txz.cif.configurer;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/8/29
+ */
+
+@Component
+@Data
+@RefreshScope
+public class TKPayConfig {
+    
+    @Value("${tkpay.paymenturl}")
+    private String paymenturl;
+    
+    @Value("${tkpay.withdrawurl}")
+    private String withdrawurl;
+    
+}

+ 12 - 3
cif-service/src/main/java/com/txz/cif/constants/MyConstants.java

@@ -19,13 +19,12 @@ public class MyConstants {
      * 主营收入账户(损益类)id
      */
     public static final Long INNER_INCOME = 3L;
-
+    
     /**
      * 主营业务支出-红包(损益类)id
      */
     public static final Long INNER_RED = 4L;
-
-
+    
     
     /**
      * 主营支出-退款(损益类)id
@@ -60,6 +59,11 @@ public class MyConstants {
      */
     public static final String TF_PAY_NOTIFY_LOCK = "TF_PAY_NOTIFY_LOCK:%s";
     
+    /**
+     * 支付回调锁
+     */
+    public static final String TK_PAY_NOTIFY_LOCK = "TK_PAY_NOTIFY_LOCK:%s";
+    
     /**
      * 提现提交锁
      */
@@ -69,4 +73,9 @@ public class MyConstants {
      * 提现回调锁
      */
     public static final String TF_PAY_WITHDRAW_NOTIFY_LOCK = "TF_PAY_WITHDRAW_NOTIFY_LOCK:%s";
+    
+    /**
+     * 提现回调锁
+     */
+    public static final String TK_PAY_WITHDRAW_NOTIFY_LOCK = "TK_PAY_WITHDRAW_NOTIFY_LOCK:%s";
 }

+ 2 - 0
cif-service/src/main/java/com/txz/cif/dao/RedEnvelopeMapper.java

@@ -16,6 +16,8 @@ public interface RedEnvelopeMapper extends Mapper<RedEnvelope> {
     List<UserTopBo> top(HashMap<String, Object> map);
 
     List<EarningsDTO> sumWithOrderNo(String orderNo, Long userId);
+    
+    List<EarningsDTO> queryOrderNo(String orderNo, Long userId);
 
     BigDecimal sumByStatus(HashMap map);
 }

+ 20 - 3
cif-service/src/main/java/com/txz/cif/dubbo/client/OperatingConfigDubboServiceClient.java

@@ -1,6 +1,7 @@
 package com.txz.cif.dubbo.client;
 
 
+import com.txz.cif.dto.RecycleInfoDTO;
 import com.txz.operating.dto.ConfigDTO;
 import com.txz.operating.result.Result;
 import com.txz.operating.service.OperatingConfigDubboService;
@@ -8,14 +9,30 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.Reference;
 import org.springframework.stereotype.Component;
 
+import java.math.BigDecimal;
+
 @Slf4j
 @Component
 public class OperatingConfigDubboServiceClient {
-
+    
     @Reference
     private OperatingConfigDubboService operatingConfigDubboService;
-
-    public Result<ConfigDTO> getConfigByCode(String code){
+    
+    public Result<ConfigDTO> getConfigByCode(String code) {
         return operatingConfigDubboService.getConfigByCode(code);
     }
+    
+    /**
+     * 获取回收信息
+     */
+    public RecycleInfoDTO getRecycleInfo() {
+        BigDecimal recycleProportion = new BigDecimal(this.getConfigByCode("recycle_proportion").getData().getValueInfo());
+        BigDecimal successTotalOrderNum = new BigDecimal(this.getConfigByCode("success_total_order_num").getData().getValueInfo());
+        BigDecimal recycleNum = new BigDecimal(this.getConfigByCode("recycle_num").getData().getValueInfo());
+        return RecycleInfoDTO.builder()
+                .recycleProportion(recycleProportion)
+                .successTotalOrderNum(successTotalOrderNum)
+                .recycleNum(recycleNum)
+                .build();
+    }
 }

+ 1 - 1
cif-service/src/main/java/com/txz/cif/dubbo/impl/AccountDubboServiceImpl.java

@@ -236,7 +236,7 @@ public class AccountDubboServiceImpl implements AccountDubboService {
     public Result addRedEnvelope(SignRedEnvelopeParam param) {
         RedEnvelopeParam p = BeanUtil.toBean(param, RedEnvelopeParam.class);
         p.setUserIds(CollUtil.newArrayList(OrderParam.builder().userId(param.getUserId()).orderNo(param.getOrderNo()).build()));
-        if (param.getBizType() == BizTypeEnum.SIGN_RED_ENVELOPE || param.getBizType() == BizTypeEnum.PROMOTION_COMMISSION) {
+        if (param.getBizType() == BizTypeEnum.SIGN_RED_ENVELOPE || param.getBizType() == BizTypeEnum.PROMOTION_COMMISSION || param.getBizType() == BizTypeEnum.RECYCLE) {
             p.setBizType(param.getBizType().getKey());
         } else {
             return Result.genFailResult(LocalUtil.get("unsupported.type"));

+ 14 - 0
cif-service/src/main/java/com/txz/cif/dubbo/impl/RedEnvelopeDubboServiceImpl.java

@@ -36,4 +36,18 @@ public class RedEnvelopeDubboServiceImpl implements RedEnvelopeDubboService {
         }
         return ret;
     }
+    
+    @Override
+    public List<EarningsDTO> getEarningsInfoByOrders(List<OrderParam> params) {
+        List<EarningsDTO> ret = new ArrayList<>();
+        for (OrderParam param : params) {
+            List<EarningsDTO> dtos = redEnvelopeService.queryOrderNo(param.getOrderNo(), param.getUserId());
+            for (EarningsDTO dto : dtos) {
+                if (dto.getUserId().compareTo(param.getUserId()) == 0) {
+                    ret.add(dto);
+                }
+            }
+        }
+        return ret;
+    }
 }

+ 34 - 9
cif-service/src/main/java/com/txz/cif/dubbo/impl/UserDubboServiceImpl.java

@@ -19,11 +19,9 @@ import com.txz.cif.service.UserDubboService;
 import com.txz.cif.service.UserService;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboService;
-import org.apache.dubbo.config.annotation.Service;
 
 import javax.annotation.Resource;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -36,7 +34,7 @@ public class UserDubboServiceImpl implements UserDubboService {
     
     @Resource
     private UserService userService;
-
+    
     @Resource
     private ConfigMemberService configMemberService;
     
@@ -83,20 +81,47 @@ public class UserDubboServiceImpl implements UserDubboService {
             return new ArrayList<>();
         }
         List<User> users = userService.findByIds(Joiner.on(",").join(userIds));
-        if (CollUtil.isNotEmpty(users)){
-            return users.stream().map(u -> BeanUtil.toBean(u,UserDTO.class)).collect(Collectors.toList());
+        if (CollUtil.isNotEmpty(users)) {
+            return users.stream().map(u -> BeanUtil.toBean(u, UserDTO.class)).collect(Collectors.toList());
         }
         return CollUtil.newArrayList();
     }
-
+    
     @Override
     public ConfigMemberDTO getConfigByLevel(Integer level) {
-        ConfigMember dto =  configMemberService.findBy("level",level);
-        if (dto == null){
+        ConfigMember dto = configMemberService.findBy("level", level);
+        if (dto == null) {
             return null;
         }
         ConfigMemberDTO dto1 = BeanUtil.toBean(dto, ConfigMemberDTO.class);
         return dto1;
     }
-
+    
+    @Override
+    public Boolean addNumberOfWin(Long userId) {
+        return this.addNumberOfWin(userId, 1);
+    }
+    
+    @Override
+    public Boolean addNumberOfWin(Long userId, Integer number) {
+        try {
+            User user = userService.findById(userId);
+            userService.update(User.builder().id(userId).totalNumberOfWin(user.getTotalNumberOfWin() + number).build());
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+    
+    @Override
+    public Boolean addNumberOfRecycle(Long userId, Integer number) {
+        try {
+            User user = userService.findById(userId);
+            userService.update(User.builder().id(userId).totalNumberOfRecycle(user.getTotalNumberOfRecycle() + number).build());
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+    
 }

+ 10 - 0
cif-service/src/main/java/com/txz/cif/model/User.java

@@ -210,6 +210,16 @@ public class User {
     @ApiModelProperty(value = "salt盐")
     private String salt;
     
+    /**
+     * 总中奖次数
+     */
+    private Integer totalNumberOfWin;
+    
+    /**
+     * 总回收次数
+     */
+    private Integer totalNumberOfRecycle;
+    
     
     public UserInfoBO generator() {
         UserInfoBO bo = BeanUtil.toBean(this, UserInfoBO.class);

+ 2 - 0
cif-service/src/main/java/com/txz/cif/service/RedEnvelopeService.java

@@ -31,6 +31,8 @@ public interface RedEnvelopeService extends Service<RedEnvelope> {
     List<UserTopBo> top(Integer type, Integer page, Integer size);
 
     List<EarningsDTO> sumWithOrderNo(String orderNo, Long userId);
+    
+    List<EarningsDTO> queryOrderNo(String orderNo, Long userId);
 
     void settle(RedEnvelope redEnvelope);
 

+ 24 - 0
cif-service/src/main/java/com/txz/cif/service/TKPayService.java

@@ -0,0 +1,24 @@
+package com.txz.cif.service;
+
+import com.txz.cif.dto.BizLogDTO;
+import com.txz.cif.dto.tkpay.TKPayCreateOrderDTO;
+import com.txz.cif.dto.tkpay.TKWithdrawDTO;
+
+/**
+ *
+ * @author: MTD®️
+ * @date: 2026/1/4
+ */
+
+public interface TKPayService {
+    
+    /**
+     * 创单
+     */
+    BizLogDTO createTKPayOrder(TKPayCreateOrderDTO dto, String key);
+    
+    /**
+     * 提现
+     */
+    BizLogDTO withdraw(TKWithdrawDTO dto, String key);
+}

+ 9 - 0
cif-service/src/main/java/com/txz/cif/service/impl/RedEnvelopeServiceImpl.java

@@ -91,6 +91,8 @@ public class RedEnvelopeServiceImpl extends AbstractService<RedEnvelope> impleme
         } else if (BizTypeEnum.getByKey(param.getBizType()) == BizTypeEnum.PROMOTION_COMMISSION) {
             // 直推红包
             redEnvelopes.addAll(bulidRedEnvelope(param.getUserIds().get(0), param, settleTime));
+        } else if (BizTypeEnum.getByKey(param.getBizType()) == BizTypeEnum.RECYCLE) {
+            redEnvelopes.addAll(bulidRedEnvelope(param.getUserIds().get(0), param, settleTime));
         }
         if (CollUtil.isNotEmpty(redEnvelopes)) {
             save(redEnvelopes);
@@ -128,6 +130,11 @@ public class RedEnvelopeServiceImpl extends AbstractService<RedEnvelope> impleme
         return cRedEnvelopeMapper.sumWithOrderNo(orderNo, userId);
     }
     
+    @Override
+    public List<EarningsDTO> queryOrderNo(String orderNo, Long userId) {
+        return cRedEnvelopeMapper.queryOrderNo(orderNo, userId);
+    }
+    
     @Override
     public void settle(RedEnvelope redEnvelope) {
         flowService.settle(redEnvelope);
@@ -288,6 +295,8 @@ public class RedEnvelopeServiceImpl extends AbstractService<RedEnvelope> impleme
                 return NoticeEnum.REWARD_SECONDARY_COMMISSION;
             case 5006:
                 return NoticeEnum.REWARD_REFER_FRIENDS;
+            case 5007:
+                return NoticeEnum.ORDER_RECYCLE_REWARD;
         }
         return ret;
     }

+ 75 - 0
cif-service/src/main/java/com/txz/cif/service/impl/TKPayServiceImpl.java

@@ -0,0 +1,75 @@
+package com.txz.cif.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.http.HttpRequest;
+import com.alibaba.fastjson.JSON;
+import com.txz.cif.configurer.TKPayConfig;
+import com.txz.cif.dto.BizLogDTO;
+import com.txz.cif.dto.tkpay.TKCreateWithdrawDTO;
+import com.txz.cif.dto.tkpay.TKPayCreateOrderDTO;
+import com.txz.cif.dto.tkpay.TKPayOrderDTO;
+import com.txz.cif.dto.tkpay.TKWithdrawDTO;
+import com.txz.cif.service.TKPayService;
+import com.txz.cif.util.TKPayUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ *
+ * @author: MTD®️
+ * @date: 2026/1/4
+ */
+@Slf4j
+@Service
+public class TKPayServiceImpl implements TKPayService {
+    
+    @Autowired
+    private TKPayConfig tkPayConfig;
+    
+    @Override
+    public BizLogDTO createTKPayOrder(TKPayCreateOrderDTO dto, String key) {
+        TKPayOrderDTO tkPay = TKPayOrderDTO.builder()
+                .mchid(dto.getMchid())
+                .payId(dto.getPayId())
+                .outTradeNo(dto.getOutTradeNo())
+                .amount(dto.getAmount())
+                .notifyUrl(dto.getNotifyUrl())
+                .build();
+        
+        String signature = TKPayUtil.generateSignature(tkPay, key);
+        tkPay.setSign(signature);
+        
+        String reqParam = JSON.toJSONString(tkPay);
+        
+        String result = HttpRequest.post(tkPayConfig.getPaymenturl() + "/api/developer/order/create")
+                .contentType("application/json")
+                .body(reqParam)
+                .execute()
+                .body();
+        log.info("TKPay createOrder result: {}", result);
+        return BizLogDTO.builder()
+                .sendMessage(reqParam)
+                .receiveMessage(result)
+                .build();
+    }
+    
+    @Override
+    public BizLogDTO withdraw(TKWithdrawDTO dto, String key) {
+        TKCreateWithdrawDTO withdrawDTO = BeanUtil.copyProperties(dto, TKCreateWithdrawDTO.class);
+        // 生成签名
+        withdrawDTO.setSign(TKPayUtil.generateSignature(withdrawDTO, key));
+        
+        String reqParam = JSON.toJSONString(withdrawDTO);
+        String result = HttpRequest.post(tkPayConfig.getPaymenturl() + "/api/developer/payout/create")
+                .contentType("application/json")
+                .body(reqParam)
+                .execute()
+                .body();
+        log.info("TKPay withdraw result: {}", result);
+        return BizLogDTO.builder()
+                .sendMessage(reqParam)
+                .receiveMessage(result)
+                .build();
+    }
+}

+ 104 - 57
cif-service/src/main/java/com/txz/cif/service/impl/WithdrawRecordServiceImpl.java

@@ -17,6 +17,7 @@ import com.txz.cif.dao.WithdrawRecordMapper;
 import com.txz.cif.dto.BizLogDTO;
 import com.txz.cif.dto.Result;
 import com.txz.cif.dto.tfpay.TFWithdrawDTO;
+import com.txz.cif.dto.tkpay.TKWithdrawDTO;
 import com.txz.cif.enums.AccountFreezdTypeEnum;
 import com.txz.cif.enums.BizTypeEnum;
 import com.txz.cif.model.BizLog;
@@ -45,33 +46,36 @@ import java.util.List;
 @Slf4j
 @Transactional
 public class WithdrawRecordServiceImpl extends AbstractService<WithdrawRecord> implements WithdrawRecordService {
-
+    
     @Resource
     private WithdrawRecordMapper withdrawRecordMapper;
     @Resource
     private AccountService accountService;
-
+    
     @Resource
     private FlowService flowService;
-
+    
     @Resource
     private PaymentChannelService paymentChannelService;
-
+    
     @Resource
     private SequenceService sequenceService;
-
+    
     @Resource
     private UserService userService;
-
+    
     @Resource
     private TFPayService tfPayService;
-
+    
+    @Resource
+    private TKPayService tkPayService;
+    
     @Resource
     private BizLogService bizLogService;
-
+    
     @Resource
     private LockTemplate lockTemplate;
-
+    
     @Override
     public void add(WithdrawRecord withdrawRecord) {
         saveUseGeneratedKeys(withdrawRecord);
@@ -89,7 +93,7 @@ public class WithdrawRecordServiceImpl extends AbstractService<WithdrawRecord> i
                 .build());
         update(WithdrawRecord.builder().id(withdrawRecord.getId()).freezeId(Long.valueOf(freeze.getData().toString())).build());
     }
-
+    
     @Override
     public void review(WithdrawRecord w, Integer status, String review, Long channelId, String userName) {
         // 同意提现
@@ -102,23 +106,25 @@ public class WithdrawRecordServiceImpl extends AbstractService<WithdrawRecord> i
                 User user = userService.findById(w.getUserId());
                 DateTime now = DateUtil.date();
                 String thirdOrderNo = sequenceService.genSerialNumber("withdraw_rule", null);
-
+                
                 PaymentChannel paymentChannel = paymentChannelService.findById(channelId);
                 if (ObjectUtil.isEmpty(paymentChannel)) {
                     throw new ServiceException(LocalUtil.get("withdrawal.channel.does.not.exist"));
                 }
                 List<BizLog> bizLogList = new ArrayList<>();
+                BizLogDTO bizLog;
+                JSONObject resultJson;
                 switch (paymentChannel.getChannelName()) {
                     case "TFPAY":
-//                        Map<String, Object> payMentChannelMap = TFPayUtil.convertObjectToMap(paymentChannel);
-                        BizLogDTO bizLog = tfPayService.withdraw(
+                        //                        Map<String, Object> payMentChannelMap = TFPayUtil.convertObjectToMap(paymentChannel);
+                        bizLog = tfPayService.withdraw(
                                 TFWithdrawDTO.builder()
                                         .payMemberid(paymentChannel.getMerchantNum())
                                         .payOrderid(thirdOrderNo)
                                         .payApplydate(now.toString())
                                         // fixme 如需测试改成502
                                         // .payChannelcode("502")
-                                        .payChannelcode(getPayChannelcode(w.getBank(),paymentChannel))
+                                        .payChannelcode(getPayChannelcode(w.getBank(), paymentChannel))
                                         .payNotifyurl(paymentChannel.getWithdrawNotifyUrl())
                                         .payAmount(w.getAmount().subtract(w.getFee()).toString())
                                         .payProductname("提现")
@@ -128,43 +134,84 @@ public class WithdrawRecordServiceImpl extends AbstractService<WithdrawRecord> i
                                         .payBankcode(w.getBank().toLowerCase())
                                         .build()
                                 , paymentChannel.getSecretKey());
-                        JSONObject resultJson;
                         try {
                             resultJson = JSONUtil.parseObj(bizLog.getReceiveMessage());
                         } catch (Exception e) {
                             log.error("提现失败三方返回-->" + bizLog);
                             throw new ServiceException(LocalUtil.get("withdrawal.failed"));
                         }
-                      bizLogList.add(BizLog.builder()
-                        .bizType(2)
-                        .bizNo(w.getOrderNo())
-                        .thirdOrderNo(thirdOrderNo)
-                        .type(7)
-                        .receiveMessage(bizLog.getReceiveMessage())
-                        .sendMessage(bizLog.getSendMessage())
-                        .createTime(now)
-                        .createUser(userName)
-                        .memo(resultJson.getStr("msg")).build());
+                        bizLogList.add(BizLog.builder()
+                                .bizType(2)
+                                .bizNo(w.getOrderNo())
+                                .thirdOrderNo(thirdOrderNo)
+                                .type(7)
+                                .receiveMessage(bizLog.getReceiveMessage())
+                                .sendMessage(bizLog.getSendMessage())
+                                .createTime(now)
+                                .createUser(userName)
+                                .memo(resultJson.getStr("msg")).build());
                         if (!resultJson.getStr("status").equals("200")) {
                             log.error("提现失败--->" + resultJson.getStr("msg"));
                         }
                         break;
+                    case "tk":
+                        if (StrUtil.isBlank(getPayChannelcode(w.getBank(), paymentChannel))) {
+                            throw new ServiceException(LocalUtil.get("withdrawal.channel.does.not.exist"));
+                        }
+                        bizLog = tkPayService.withdraw(
+                                TKWithdrawDTO.builder()
+                                        .mchid(paymentChannel.getMerchantNum())
+                                        .outTradeNo(thirdOrderNo)
+                                        .amount(w.getAmount().subtract(w.getFee()).toString())
+                                        .toHolder(user.getName())
+                                        // 成功
+                                        // .toAccount("00012345678905")
+                                        // 失败
+                                        // .toAccount("00012345678906")
+                                        .toAccount(user.getBankAccount())
+                                        .toBankType(getPayChannelcode(w.getBank(), paymentChannel))
+                                        .notifyUrl(paymentChannel.getWithdrawNotifyUrl())
+                                        .build()
+                                , paymentChannel.getSecretKey());
+                        
+                        try {
+                            resultJson = JSONUtil.parseObj(bizLog.getReceiveMessage());
+                        } catch (Exception e) {
+                            log.error("提现失败三方返回-->" + bizLog);
+                            throw new ServiceException(LocalUtil.get("withdrawal.failed"));
+                        }
+                        bizLogList.add(BizLog.builder()
+                                .bizType(2)
+                                .bizNo(w.getOrderNo())
+                                .thirdOrderNo(thirdOrderNo)
+                                .type(7)
+                                .receiveMessage(bizLog.getReceiveMessage())
+                                .sendMessage(bizLog.getSendMessage())
+                                .createTime(now)
+                                .createUser(userName)
+                                .memo(resultJson.getStr("rMsg"))
+                                .build()
+                        );
+                        if (!resultJson.getStr("rMsg").equals("CREATE_SUCCESS")) {
+                            throw new ServiceException(LocalUtil.get("withdrawal.failed"));
+                        }
+                        break;
                     default:
                         throw new ServiceException(LocalUtil.get("this.channel.is.not.supported.temporarily"));
                 }
                 try {
-                  // 新增日志
-                  bizLogList.add(BizLog.builder()
-                    .bizType(2)
-                    .bizNo(w.getOrderNo())
-                    .thirdOrderNo(thirdOrderNo)
-                    .type(5)
-                    .createTime(now)
-                    .createUser(userName)
-                    .memo(review).build());
-                  bizLogService.save(bizLogList);
+                    // 新增日志
+                    bizLogList.add(BizLog.builder()
+                            .bizType(2)
+                            .bizNo(w.getOrderNo())
+                            .thirdOrderNo(thirdOrderNo)
+                            .type(5)
+                            .createTime(now)
+                            .createUser(userName)
+                            .memo(review).build());
+                    bizLogService.save(bizLogList);
                 } catch (Exception e) {
-                  log.error("新增提现提交订单日志失败", e);
+                    log.error("新增提现提交订单日志失败", e);
                 }
                 this.update(WithdrawRecord.builder().id(w.getId()).channel(paymentChannel.getChannelName()).review(review).status(status).updateUser(userName).thirdOrderNo(thirdOrderNo).build());
             } finally {
@@ -172,18 +219,18 @@ public class WithdrawRecordServiceImpl extends AbstractService<WithdrawRecord> i
             }
             return;
         } else {
-          try {
-            // 新增日志
-            bizLogService.save(BizLog.builder()
-              .bizType(2)
-              .bizNo(w.getOrderNo())
-              .type(6)
-              .createTime(DateUtil.date())
-              .createUser(userName)
-              .memo(review).build());
-          } catch (Exception e) {
-            log.error("新增提现提交订单日志失败", e);
-          }
+            try {
+                // 新增日志
+                bizLogService.save(BizLog.builder()
+                        .bizType(2)
+                        .bizNo(w.getOrderNo())
+                        .type(6)
+                        .createTime(DateUtil.date())
+                        .createUser(userName)
+                        .memo(review).build());
+            } catch (Exception e) {
+                log.error("新增提现提交订单日志失败", e);
+            }
         }
         // 解冻
         if (w.getFreezeId() != null) {
@@ -192,20 +239,20 @@ public class WithdrawRecordServiceImpl extends AbstractService<WithdrawRecord> i
         this.update(WithdrawRecord.builder().id(w.getId())
                 .review(review).updateUser(userName).status(status).build());
     }
-
+    
     private String getPayChannelcode(String channel, PaymentChannel paymentChannel) {
-        if (StrUtil.equals(channel.toLowerCase(),"bkash")){
+        if (StrUtil.equals(channel.toLowerCase(), "bkash")) {
             return paymentChannel.getBkashWithdrawCode();
         }
-        if (StrUtil.equals(channel.toLowerCase(),"nagad")){
+        if (StrUtil.equals(channel.toLowerCase(), "nagad")) {
             return paymentChannel.getNagadWithdrawCode();
         }
-        if (StrUtil.equals(channel.toLowerCase(),"rocket")){
+        if (StrUtil.equals(channel.toLowerCase(), "rocket")) {
             return paymentChannel.getRocketWithdrawCode();
         }
         return "";
     }
-
+    
     @Override
     public void success(WithdrawRecord record, Date successTime) {
         if (record.getFreezeId() != null) {
@@ -223,7 +270,7 @@ public class WithdrawRecordServiceImpl extends AbstractService<WithdrawRecord> i
                 .build());
         update(WithdrawRecord.builder().id(record.getId()).successTime(successTime).status(4).build());
     }
-
+    
     @Override
     public void fail(WithdrawRecord record) {
         if (record.getFreezeId() != null) {
@@ -231,17 +278,17 @@ public class WithdrawRecordServiceImpl extends AbstractService<WithdrawRecord> i
         }
         update(WithdrawRecord.builder().id(record.getId()).status(5).build());
     }
-
+    
     @Override
     public Integer countByUserId(HashMap map) {
         return withdrawRecordMapper.countByUserId(map);
     }
-
+    
     @Override
     public BigDecimal sumByStatus(HashMap map) {
         return withdrawRecordMapper.sumByStatus(map);
     }
-
+    
     @Override
     public BigDecimal sumByMap(HashMap<String, String> map) {
         return withdrawRecordMapper.sumByMap(map);

+ 110 - 0
cif-service/src/main/java/com/txz/cif/util/TKPayUtil.java

@@ -0,0 +1,110 @@
+package com.txz.cif.util;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/8/29
+ */
+public class TKPayUtil {
+    
+    /**
+     * 签名
+     *
+     * @param dto
+     *
+     * @return
+     */
+    public static String generateSignature(Object dto, String key) {
+        Map<String, Object> paramMap = convertObjectToMap(dto);
+        String signString = buildSignString(paramMap);
+        return md5(signString + "&key=" + key).toLowerCase();
+    }
+    
+    /**
+     * 验签
+     *
+     * @param dto
+     * @param expectedSign 签名
+     *
+     * @return
+     */
+    public static boolean verifySignature(Object dto, String key, String expectedSign) {
+        String generatedSign = generateSignature(dto, key);
+        return generatedSign.equals(expectedSign);
+    }
+    
+    public static Map<String, Object> convertObjectToMap(Object obj) {
+        Map<String, Object> paramMap = new TreeMap<>();
+        Class<?> clazz = obj.getClass();
+        Field[] fields = clazz.getDeclaredFields();
+        
+        for (Field field : fields) {
+            try {
+                field.setAccessible(true);
+                Object value = field.get(obj);
+                if (value != null && !value.toString().isEmpty()) {
+                    JSONField jsonProperty = field.getAnnotation(JSONField.class);
+                    String key = (jsonProperty != null) ? jsonProperty.name() : field.getName();
+                    
+                    if (value instanceof BigDecimal) {
+                        paramMap.put(key, ((BigDecimal) value).stripTrailingZeros().toPlainString());
+                    } else {
+                        paramMap.put(key, value);
+                    }
+                }
+            } catch (IllegalAccessException e) {
+            }
+        }
+        return paramMap;
+    }
+    
+    
+    private static String buildSignString(Map<String, Object> paramMap) {
+        StringBuilder sb = new StringBuilder();
+        Iterator<Map.Entry<String, Object>> iterator = paramMap.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry<String, Object> entry = iterator.next();
+            sb.append(entry.getKey()).append("=").append(entry.getValue());
+            if (iterator.hasNext()) {
+                sb.append("&");
+            }
+        }
+        return sb.toString();
+    }
+    
+    /**
+     * MD5加密
+     *
+     * @param input 输入字符串
+     *
+     * @return MD5加密结果
+     */
+    public static String md5(String input) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] messageDigest = md.digest(input.getBytes());
+            StringBuilder hexString = new StringBuilder();
+            
+            for (byte b : messageDigest) {
+                String hex = Integer.toHexString(0xFF & b);
+                if (hex.length() == 1) {
+                    hexString.append('0');
+                }
+                hexString.append(hex);
+            }
+            
+            return hexString.toString().toLowerCase();
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("MD5 algorithm not found", e);
+        }
+    }
+}

+ 107 - 9
cif-service/src/main/java/com/txz/cif/web/RechargeRecordApiController.java

@@ -19,14 +19,17 @@ import com.txz.cif.core.*;
 import com.txz.cif.dto.BizLogDTO;
 import com.txz.cif.dto.tfpay.CheckSignDTO;
 import com.txz.cif.dto.tfpay.TFCreateOrderDTO;
+import com.txz.cif.dto.tkpay.TKPayCreateOrderDTO;
 import com.txz.cif.dubbo.client.NoticeDubboServiceClient;
 import com.txz.cif.model.*;
 import com.txz.cif.service.*;
 import com.txz.cif.service.impl.BizLogServiceImpl;
 import com.txz.cif.util.IpUtils;
 import com.txz.cif.util.TFPayUtil;
+import com.txz.cif.util.TKPayUtil;
 import com.txz.cif.web.para.RecordParam;
 import com.txz.cif.web.ro.TFPayNotifyRO;
+import com.txz.cif.web.ro.TKPayNotifyRO;
 import com.txz.cif.web.vo.CreatePayVO;
 import com.txz.cif.web.vo.PaymentPriceVO;
 import com.txz.mall.enums.NoticeEnum;
@@ -81,16 +84,18 @@ public class RechargeRecordApiController {
     
     @Resource
     private TFPayService tfPayService;
+    @Resource
+    private TKPayService tkPayService;
     
     @Resource
     private LockTemplate lockTemplate;
     @Autowired
     private BizLogServiceImpl bizLogServiceImpl;
-
+    
     @Resource
     private NoticeDubboServiceClient noticeDubboServiceClient;
-
-
+    
+    
     @GetMapping("/goodsList/{methodId:^\\d+$}")
     @ApiOperation(value = "充值商品", httpMethod = "GET")
     public Result<PaymentPriceVO> goodsList(@PathVariable("methodId") Long methodId) {
@@ -171,9 +176,9 @@ public class RechargeRecordApiController {
     @ApiOperation(value = "创建充值订单", httpMethod = "GET")
     public Result<CreatePayVO> add(@RequestParam BigDecimal amount, @RequestParam Long methodId, HttpServletRequest request) {
         Long userId = authService.getTokenUserId(request);
-         if (userId == null) {
-             return ResultGenerator.genFailResult(ResultCode.OAUTH_INVALID_ACCESS_TOKEN);
-         }
+        if (userId == null) {
+            return ResultGenerator.genFailResult(ResultCode.OAUTH_INVALID_ACCESS_TOKEN);
+        }
         if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
             return ResultGenerator.genFailResult(ResultCode.AMOUNT_IS_NULL);
         }
@@ -226,6 +231,7 @@ public class RechargeRecordApiController {
             BizLogDTO bizLog;
             String payUrl = "";
             boolean issuccess = false;
+            JSONObject resultJson;
             switch (paymentChannel.getChannelName()) {
                 case "TFPAY":
                     bizLog = tfPayService.createTFPayOrder(
@@ -240,12 +246,35 @@ public class RechargeRecordApiController {
                                     .build()
                             , paymentChannel.getSecretKey());
                     
-                    JSONObject resultJson = JSONUtil.parseObj(bizLog.getReceiveMessage());
+                    resultJson = JSONUtil.parseObj(bizLog.getReceiveMessage());
                     if (resultJson.getStr("status").equals("200")) {
                         payUrl = resultJson.getStr("data");
                         issuccess = Boolean.TRUE;
                     }
                     break;
+                case "tk":
+                    bizLog = tkPayService.createTKPayOrder(
+                            TKPayCreateOrderDTO.builder()
+                                    .mchid(paymentChannel.getMerchantNum())
+                                    .payId(channelCode)
+                                    .outTradeNo(orderNo)
+                                    .amount(String.format("%.2f", amount))
+                                    .notifyUrl(paymentChannel.getNotifyUrl())
+                                    .build()
+                            , paymentChannel.getSecretKey());
+                    
+                    resultJson = JSONUtil.parseObj(bizLog.getReceiveMessage());
+                    // success
+                    // {"data":{"status":1,"msg":"CREATE_SUCCESS","pay_id":"11","trade_no":"T0105090723VAMG0P8T","out_trade_no":"20260105000000000003","amount":"300.00","pay_link":"https://tk.passpay-mobi.com/pay/BD?orderNo=T0105090723VAMG0P8T&amount=300.00"},"rCode":200,"rMsg":"CREATE_SUCCESS","elapsed_time":0.029}
+                    
+                    // error
+                    // {"data":{"status":2,"msg":"非法IP:50.7.252.26, 请联系运营加白"},"rCode":200,"rMsg":"非法IP:50.7.252.26, 请联系运营加白","elapsed_time":0.004}
+                    JSONObject data = JSONUtil.parseObj(resultJson.getStr("data"));
+                    if (data.getStr("status").equals("1")) {
+                        payUrl = data.getStr("pay_link");
+                        issuccess = Boolean.TRUE;
+                    }
+                    break;
                 default:
                     throw new ServiceException(LocalUtil.get("this.channel.is.not.supported.temporarily"));
             }
@@ -336,8 +365,8 @@ public class RechargeRecordApiController {
                 log.error("新增充值回调日志失败", e);
             }
             try {
-                noticeDubboServiceClient.addMoneyNotice(NoticeEnum.MONEY_RECHARGE_SUCCESS,record.getUserId());
-            }catch (Exception e) {
+                noticeDubboServiceClient.addMoneyNotice(NoticeEnum.MONEY_RECHARGE_SUCCESS, record.getUserId());
+            } catch (Exception e) {
                 log.error("新增充值回调消息失败", e);
             }
             return "OK";
@@ -346,6 +375,75 @@ public class RechargeRecordApiController {
         }
     }
     
+    /**
+     * 回调通知
+     */
+    @PostMapping("/callback/tkpay")
+    public Object tkpay(@RequestBody TKPayNotifyRO dto) {
+        LockInfo lockInfo = lockTemplate.lock(String.format(MyConstants.TK_PAY_NOTIFY_LOCK, dto.getOutTradeNo()), Long.valueOf(1000 * 60 * 2), Long.valueOf(1000 * 1), RedisTemplateLockExecutor.class);
+        if (null == lockInfo) {
+            return "WAIT";
+        }
+        try {
+            log.info("支付渠道--->tkpay,回调通知--->" + dto);
+            Condition condition = new Condition(PaymentChannel.class);
+            Example.Criteria criteria = condition.createCriteria();
+            criteria.andEqualTo("channelName", "tk");
+            criteria.andEqualTo("isValid", 1);
+            List<PaymentChannel> paymentChannels = paymentChannelService.findByCondition(condition);
+            if (CollectionUtil.isEmpty(paymentChannels)) {
+                log.error("支付渠道不存在");
+                return "fail";
+            }
+            boolean checkSignResult = TKPayUtil.verifySignature(BeanUtil.copyProperties(dto, com.txz.cif.dto.tkpay.CheckSignDTO.class), paymentChannels.get(0).getSecretKey(), dto.getSign());
+            if (!checkSignResult) {
+                log.error("签名校验失败");
+                return "fail";
+            }
+            RechargeRecord record = rechargeRecordService.findBy("orderNo", dto.getOutTradeNo());
+            if (ObjectUtil.isEmpty(record)) {
+                return "fail";
+            }
+            if (record.getStatus() == 2 || record.getStatus() == 3) {
+                return "fail";
+            }
+            record.setRealAmount(new BigDecimal(dto.getAmount()));
+            rechargeRecordService.success(record);
+            
+            // String orderNo = dto.getOrderid();
+            // RechargeRecord record = rechargeRecordService.findBy("orderNo", orderNo);
+            // if (record == null) {
+            //     return ResultGenerator.genFailResult("订单未找到");
+            // }
+            // if (StrUtil.equals("1", json.getStr("status"))) {
+            // rechargeRecordService.success(record);
+            // } else {
+            //     rechargeRecordService.fail(record);
+            // }
+            try {
+                // 新增充值回调日志
+                bizLogService.save(BizLog.builder()
+                        .bizType(1)
+                        .bizNo(dto.getOutTradeNo())
+                        .type(3)
+                        .receiveMessage(dto.toString())
+                        .createTime(DateUtil.date())
+                        .createUser("TKPAY")
+                        .memo("充值回调").build());
+            } catch (Exception e) {
+                log.error("新增充值回调日志失败", e);
+            }
+            try {
+                noticeDubboServiceClient.addMoneyNotice(NoticeEnum.MONEY_RECHARGE_SUCCESS, record.getUserId());
+            } catch (Exception e) {
+                log.error("新增充值回调消息失败", e);
+            }
+            return "success";
+        } finally {
+            lockTemplate.releaseLock(lockInfo);
+        }
+    }
+    
     
     @GetMapping("/detail")
     @ApiOperation(value = "rechargeRecord获取详情", httpMethod = "GET")

+ 13 - 0
cif-service/src/main/java/com/txz/cif/web/UserApiController.java

@@ -1,5 +1,6 @@
 package com.txz.cif.web;
 
+import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
@@ -30,6 +31,7 @@ import com.txz.cif.web.para.LoginAccountParameters;
 import com.txz.cif.web.para.MyUserParam;
 import com.txz.cif.web.para.RegisterAccountParameters;
 import com.txz.cif.web.para.UserInfoForm;
+import com.txz.cif.web.vo.RecycleInfoVO;
 import com.txz.operating.dto.ConfigDTO;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -485,4 +487,15 @@ public class UserApiController extends AbstractApiController {
     }
     
     
+    /**
+     * 获取回收数据
+     */
+    @GetMapping("getRecycleInfo")
+    public Result<RecycleInfoVO> getRecycleInfo() {
+        RecycleInfoVO recycleInfoVO = BeanUtil.copyProperties(configDubboServiceClient.getRecycleInfo(), RecycleInfoVO.class);
+        
+        return Result.success(recycleInfoVO);
+    }
+    
+    
 }

+ 141 - 36
cif-service/src/main/java/com/txz/cif/web/WithdrawRecordApiController.java

@@ -22,9 +22,11 @@ import com.txz.cif.dubbo.client.OperatingConfigDubboServiceClient;
 import com.txz.cif.model.*;
 import com.txz.cif.service.*;
 import com.txz.cif.util.TFPayUtil;
+import com.txz.cif.util.TKPayUtil;
 import com.txz.cif.web.para.RecordParam;
 import com.txz.cif.web.para.WithdrawParam;
 import com.txz.cif.web.ro.TFWithdrawCallbackRO;
+import com.txz.cif.web.ro.TKWithdrawCallbackRO;
 import com.txz.mall.enums.NoticeEnum;
 import com.txz.operating.dto.ConfigDTO;
 import io.jsonwebtoken.Claims;
@@ -42,6 +44,7 @@ import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.util.Date;
 import java.util.List;
 
 import static com.txz.cif.core.AbstractApiController.X_CLIENT_TOKEN;
@@ -53,40 +56,40 @@ import static com.txz.cif.core.AbstractApiController.X_CLIENT_TOKEN;
 @RestController
 @RequestMapping("/api/withdraw/record")
 public class WithdrawRecordApiController {
-
+    
     private static Logger log = LoggerFactory.getLogger(WithdrawRecordApiController.class);
-
+    
     @Resource
     private WithdrawRecordService withdrawRecordService;
-
+    
     @Resource
     private SequenceService sequenceService;
-
+    
     @Resource
     private AuthService authService;
-
+    
     @Resource
     private AccountService accountService;
-
+    
     @Resource
     private BizLogService bizLogService;
-
+    
     @Resource
     private PaymentChannelService paymentChannelService;
-
+    
     @Resource
     private UserService userService;
-
+    
     @Resource
     private LockTemplate lockTemplate;
-
+    
     @Resource
     private OperatingConfigDubboServiceClient operatingConfigDubboServiceClient;
-
-
+    
+    
     @Resource
     private NoticeDubboServiceClient noticeDubboServiceClient;
-
+    
     @PostMapping("/add")
     @ApiOperation(value = "新增提现订单", httpMethod = "POST")
     public Result add(@RequestBody WithdrawParam param, @RequestHeader(value = X_CLIENT_TOKEN, required = false) String token) {
@@ -95,7 +98,7 @@ public class WithdrawRecordApiController {
         }
         try {
             Triple<Boolean, String, Claims> result = authService.verifyToken(token);
-
+            
             if (!result.getLeft()) {
                 // token无效
                 String errorMsg = result.getMiddle();
@@ -104,7 +107,7 @@ public class WithdrawRecordApiController {
             }
             Claims claims = result.getRight();
             Long userId = Long.valueOf(claims.get("userId").toString());
-
+            
             User user = userService.findById(userId);
             if (user == null) {
                 return ResultGenerator.genFailResult(ResultCode.USER_IS_NULL);
@@ -141,7 +144,7 @@ public class WithdrawRecordApiController {
                 return ResultGenerator.genFailResult(ResultCode.AMOUNT_OUT_MAX);
             }
             String key = "";
-            if (param.getAccountType() == 1){
+            if (param.getAccountType() == 1) {
                 key = "withdraw_rate";
             } else {
                 key = "earning_withdraw_rate";
@@ -149,7 +152,7 @@ public class WithdrawRecordApiController {
             com.txz.operating.result.Result<ConfigDTO> openRedEnvelopeRate = operatingConfigDubboServiceClient.getConfigByCode(key);
             String rate = openRedEnvelopeRate.getData().getValueInfo();
             BigDecimal fee = param.getAmount().multiply(new BigDecimal(rate)).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);
-
+            
             Account account = accountService.getAccount(userId, param.getAccountType());
             if (account == null) {
                 return ResultGenerator.genFailResult(ResultCode.ACCOUNT_IS_NULL);
@@ -157,9 +160,9 @@ public class WithdrawRecordApiController {
             if (account.getEffectiveBalance().compareTo(param.getAmount().add(fee)) < 0) {
                 return ResultGenerator.genFailResult(ResultCode.EFFECTIVE_BALANCE_IS_INSUFFICIENT);
             }
-
+            
             String orderNo = sequenceService.genSerialNumber("withdraw_rule", null);
-
+            
             WithdrawRecord withdrawRecord = new WithdrawRecord();
             withdrawRecord.setOrderNo(orderNo);
             withdrawRecord.setStatus(1);
@@ -172,28 +175,28 @@ public class WithdrawRecordApiController {
             withdrawRecord.setBank(user.getBank());
             withdrawRecord.setBankAccount(user.getBankAccount());
             withdrawRecord.setBankAccountName(user.getBankAccountName());
-//             withdrawRecord.setChannel(param.getChannel());
+            //             withdrawRecord.setChannel(param.getChannel());
             withdrawRecord.setCurrency(param.getCurrency());
             withdrawRecord.setAmount(param.getAmount());
             withdrawRecord.setCreateTime(DateUtil.date());
             withdrawRecordService.add(withdrawRecord);
             // 新增日志
             bizLogService.save(BizLog.builder()
-              .bizType(2)
-              .bizNo(orderNo)
-              .type(4)
-              .createTime(DateUtil.date())
-              .createUser(user.getName())
-              .memo("提交提现申请").build());
+                    .bizType(2)
+                    .bizNo(orderNo)
+                    .type(4)
+                    .createTime(DateUtil.date())
+                    .createUser(user.getName())
+                    .memo("提交提现申请").build());
             return ResultGenerator.genSuccessResult(withdrawRecord);
         } catch (Exception e) {
             log.error("新增对象操作异常e:{}", e);
             return ResultGenerator.genFailResult(ResultCode.INTERNAL_SERVER_ERROR);
         }
-
+        
     }
-
-
+    
+    
     @PostMapping("/list")
     @ApiOperation(value = "withdrawRecord获取列表", httpMethod = "POST")
     public Result<List<WithdrawRecord>> list(@RequestBody RecordParam param, HttpServletRequest request) {
@@ -202,7 +205,7 @@ public class WithdrawRecordApiController {
             ResultGenerator.genFailResult(ResultCode.OAUTH_INVALID_ACCESS_TOKEN);
         }
         PageHelper.startPage(param.getPage(), param.getSize());
-
+        
         Condition condition = new Condition(WithdrawRecord.class);
         Criteria criteria = condition.createCriteria();
         criteria.andEqualTo("userId", userId);
@@ -246,7 +249,7 @@ public class WithdrawRecordApiController {
         }
         return ResultGenerator.genSuccessResult(pageInfo);
     }
-
+    
     /**
      * tfpay提现通知
      */
@@ -268,12 +271,12 @@ public class WithdrawRecordApiController {
                 return "FAIL";
             }
             boolean checkSignResult = TFPayUtil.verifySignature(BeanUtil.copyProperties(dto, CheckWithdrawSignDTO.class), paymentChannels.get(0).getSecretKey(), dto.getSign());
-
+            
             if (!checkSignResult) {
                 log.error("签名校验失败");
                 return "FAIL";
             }
-
+            
             Condition condition = new Condition(WithdrawRecord.class);
             Example.Criteria criteria = condition.createCriteria();
             criteria.andEqualTo("thirdOrderNo", dto.getOrderid());
@@ -283,7 +286,7 @@ public class WithdrawRecordApiController {
                 log.error("提现订单不存在");
                 return "FAIL";
             }
-
+            
             // //TODO 回调成功
             // JSONObject json = JSONUtil.parseObj(data);
             // String orderNo = json.getStr("orderNo");
@@ -336,7 +339,7 @@ public class WithdrawRecordApiController {
             } catch (Exception e) {
                 log.error("新增充值回调日志失败", e);
                 // }
-
+                
             }
             try {
                 noticeDubboServiceClient.addMoneyNotice(ne, record.getUserId());
@@ -348,5 +351,107 @@ public class WithdrawRecordApiController {
             lockTemplate.releaseLock(lockInfo);
         }
     }
-
+    
+    /**
+     * tkpay提现通知
+     */
+    @PostMapping("/callback/tkpay")
+    public String tkcallback(@RequestBody TKWithdrawCallbackRO dto) {
+        LockInfo lockInfo = lockTemplate.lock(String.format(MyConstants.TK_PAY_WITHDRAW_NOTIFY_LOCK, dto.getOutTradeNo()), Long.valueOf(1000 * 60 * 2), Long.valueOf(1000 * 1), RedisTemplateLockExecutor.class);
+        if (null == lockInfo) {
+            return "WAIT";
+        }
+        try {
+            log.info("提现渠道--->tkpay,提现回调通知--->" + dto);
+            Condition channelCondition = new Condition(PaymentChannel.class);
+            Example.Criteria channelCriteria = channelCondition.createCriteria();
+            channelCriteria.andEqualTo("channelName", "tk");
+            channelCriteria.andEqualTo("isValid", 1);
+            List<PaymentChannel> paymentChannels = paymentChannelService.findByCondition(channelCondition);
+            if (CollectionUtil.isEmpty(paymentChannels)) {
+                log.error("支付渠道不存在");
+                return "fail";
+            }
+            boolean checkSignResult = TKPayUtil.verifySignature(BeanUtil.copyProperties(dto, com.txz.cif.dto.tkpay.CheckWithdrawSignDTO.class), paymentChannels.get(0).getSecretKey(), dto.getSign());
+            
+            if (!checkSignResult) {
+                log.error("签名校验失败");
+                return "fail";
+            }
+            
+            Condition condition = new Condition(WithdrawRecord.class);
+            Example.Criteria criteria = condition.createCriteria();
+            criteria.andEqualTo("thirdOrderNo", dto.getOutTradeNo());
+            criteria.andEqualTo("status", 2);
+            List<WithdrawRecord> records = withdrawRecordService.findByCondition(condition);
+            if (CollectionUtil.isEmpty(records)) {
+                log.error("提现订单不存在");
+                return "fail";
+            }
+            
+            // //TODO 回调成功
+            // JSONObject json = JSONUtil.parseObj(data);
+            // String orderNo = json.getStr("orderNo");
+            // WithdrawRecord record = withdrawRecordService.findBy("orderNo", orderNo);
+            // if (record == null) {
+            //     return ResultGenerator.genFailResult("订单未找到");
+            // }
+            // Integer type = 8;
+            // if (StrUtil.equals("1", json.getStr("status"))) {
+            //     withdrawRecordService.success(record);
+            // } else {
+            //     withdrawRecordService.fail(record);
+            //     type = 9;
+            // }
+            // try {
+            //     // 新增充值回调日志
+            //     bizLogService.save(BizLog.builder().bizType(2).bizNo(orderNo)
+            //             .type(type).createTime(DateUtil.date()).createUser("第三方")
+            //             .memo("提现回调").build());
+            // } catch (Exception e) {
+            //     log.error("新增充值回调日志失败", e);
+            // }
+            // return ResultGenerator.genSuccessResult();
+            WithdrawRecord record = records.get(0);
+            NoticeEnum ne = null;
+            Integer type = 8;
+            if (dto.getStatus() == 5) {
+                withdrawRecordService.success(record, new Date());
+                if (record.getAccountType() == 1) {
+                    ne = NoticeEnum.MONEY_WITHDRAWAL_WALLET_SUCCESS;
+                } else if (record.getAccountType() == 2) {
+                    ne = NoticeEnum.MONEY_WITHDRAWAL_ACCOUNT_SUCCESS;
+                }
+            } else {
+                withdrawRecordService.fail(records.get(0));
+                type = 9;
+                ne = NoticeEnum.MONEY_WITHDRAWAL_FAIL;
+            }
+            try {
+                // 新增充值回调日志
+                bizLogService.save(BizLog.builder()
+                        .bizType(2)
+                        .bizNo(record.getOrderNo())
+                        .thirdOrderNo(dto.getOutTradeNo())
+                        .type(type)
+                        .receiveMessage(dto.toString())
+                        .createTime(DateUtil.date())
+                        .createUser("TFPAY")
+                        .memo("提现回调").build());
+            } catch (Exception e) {
+                log.error("新增充值回调日志失败", e);
+                // }
+                
+            }
+            try {
+                noticeDubboServiceClient.addMoneyNotice(ne, record.getUserId());
+            } catch (Exception e) {
+                log.error("新增提现回调消息失败", e);
+            }
+            return "success";
+        } finally {
+            lockTemplate.releaseLock(lockInfo);
+        }
+    }
+    
 }

+ 78 - 0
cif-service/src/main/java/com/txz/cif/web/ro/TKPayNotifyRO.java

@@ -0,0 +1,78 @@
+package com.txz.cif.web.ro;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/1
+ */
+
+@Data
+public class TKPayNotifyRO {
+    
+    /**
+     *
+     */
+    @JSONField(name = "mchid")
+    private String mchid;
+    
+    /**
+     *
+     */
+    @JSONField(name = "pay_id")
+    private String payId;
+    
+    /**
+     * 三方单号
+     */
+    @JSONField(name = "trade_no")
+    private String tradeNo;
+    
+    /**
+     * 商户传入订单号
+     */
+    @JSONField(name = "out_trade_no")
+    private String outTradeNo;
+    
+    /**
+     *
+     */
+    @JSONField(name = "utr")
+    private String utr;
+    
+    /**
+     * 订单金额:100.00
+     * 实际与 real_amount 一样,随便用哪个
+     */
+    @JSONField(name = "amount")
+    private String amount;
+    
+    /**
+     * 实际支付金额:100.00
+     * 实际与 amount 一样,随便用哪个
+     */
+    @JSONField(name = "real_amount")
+    private String realAmount;
+    
+    /**
+     * 交易状态:
+     * 3-交易中
+     * 5-交易成功
+     * 6-交易失败
+     */
+    @JSONField(name = "status")
+    private Integer status;
+    
+    /**
+     * 订单状态描述
+     */
+    @JSONField(name = "msg")
+    private String msg;
+    
+    /**
+     * 签名
+     */
+    @JSONField(name = "sign")
+    private String sign;
+}

+ 79 - 0
cif-service/src/main/java/com/txz/cif/web/ro/TKWithdrawCallbackRO.java

@@ -0,0 +1,79 @@
+package com.txz.cif.web.ro;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/3
+ */
+@Data
+public class TKWithdrawCallbackRO {
+    
+    /**
+     *
+     */
+    @JSONField(name = "mchid")
+    private String mchid;
+    
+    /**
+     * 代付交易状态:
+     * 5-代付交易成功;
+     * 6-代付交易失败;
+     * 3-交易中(实际上不会同步)
+     */
+    @JSONField(name = "status")
+    private Integer status;
+    
+    /**
+     * 当status=6时,返回代付失败的原因
+     */
+    @JSONField(name = "msg")
+    private String msg;
+    
+    /**
+     * 三方单号
+     */
+    @JSONField(name = "trade_no")
+    private String tradeNo;
+    
+    /**
+     * 商户单号
+     */
+    @JSONField(name = "out_trade_no")
+    private String outTradeNo;
+    
+    @JSONField(name = "utr")
+    private String utr;
+    
+    /**
+     * 代付订单金额
+     */
+    @JSONField(name = "amount")
+    private String amount;
+    
+    /**
+     * 持卡人姓名
+     */
+    @JSONField(name = "to_holder")
+    private String toHolder;
+    
+    /**
+     * 银行卡卡号
+     */
+    @JSONField(name = "to_account")
+    private String toAccount;
+    
+    /**
+     * 银行类型,暂时仅支持:
+     * bKash; Nagad 两种类型
+     */
+    @JSONField(name = "to_bank_type")
+    private String toBankType;
+    
+    /**
+     * 签名
+     */
+    @JSONField(name = "sign")
+    private String sign;
+}

+ 31 - 0
cif-service/src/main/java/com/txz/cif/web/vo/RecycleInfoVO.java

@@ -0,0 +1,31 @@
+package com.txz.cif.web.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ *
+ * @author: MTD®️
+ * @date: 2026/1/4
+ */
+@Data
+public class RecycleInfoVO {
+    
+    /**
+     * 订单回收比例
+     */
+    private BigDecimal recycleProportion;
+    
+    /**
+     * 成功订单总数
+     */
+    private BigDecimal successTotalOrderNum;
+    
+    /**
+     * 回收数量
+     */
+    private BigDecimal recycleNum;
+    
+    
+}

+ 77 - 77
cif-service/src/main/resources/mapper/RedEnvelopeMapper.xml

@@ -1,86 +1,86 @@
 <?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="com.txz.cif.dao.RedEnvelopeMapper">
-  <resultMap id="BaseResultMap" type="com.txz.cif.model.RedEnvelope">
-    <!--
-      WARNING - @mbg.generated
-    -->
-    <id column="id" jdbcType="BIGINT" property="id" />
-    <result column="order_no" jdbcType="VARCHAR" property="orderNo" />
-    <result column="tran_no" jdbcType="VARCHAR" property="tranNo" />
-    <result column="user_id" jdbcType="BIGINT" property="userId" />
-    <result column="amount" jdbcType="DECIMAL" property="amount" />
-    <result column="balance" jdbcType="DECIMAL" property="balance" />
-    <result column="debit_account" jdbcType="BIGINT" property="debitAccount" />
-    <result column="credit_account" jdbcType="BIGINT" property="creditAccount" />
-    <result column="status" jdbcType="INTEGER" property="status" />
-    <result column="biz_type" jdbcType="INTEGER" property="bizType" />
-    <result column="trans_time" jdbcType="TIMESTAMP" property="transTime" />
-    <result column="settle_time" jdbcType="TIMESTAMP" property="settleTime" />
-    <result column="create_user" jdbcType="VARCHAR" property="createUser" />
-    <result column="update_user" jdbcType="VARCHAR" property="updateUser" />
-    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
-    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
-    <result column="version" jdbcType="VARCHAR" property="version" />
+    <resultMap id="BaseResultMap" type="com.txz.cif.model.RedEnvelope">
+        <!--
+          WARNING - @mbg.generated
+        -->
+        <id column="id" jdbcType="BIGINT" property="id"/>
+        <result column="order_no" jdbcType="VARCHAR" property="orderNo"/>
+        <result column="tran_no" jdbcType="VARCHAR" property="tranNo"/>
+        <result column="user_id" jdbcType="BIGINT" property="userId"/>
+        <result column="amount" jdbcType="DECIMAL" property="amount"/>
+        <result column="balance" jdbcType="DECIMAL" property="balance"/>
+        <result column="debit_account" jdbcType="BIGINT" property="debitAccount"/>
+        <result column="credit_account" jdbcType="BIGINT" property="creditAccount"/>
+        <result column="status" jdbcType="INTEGER" property="status"/>
+        <result column="biz_type" jdbcType="INTEGER" property="bizType"/>
+        <result column="trans_time" jdbcType="TIMESTAMP" property="transTime"/>
+        <result column="settle_time" jdbcType="TIMESTAMP" property="settleTime"/>
+        <result column="create_user" jdbcType="VARCHAR" property="createUser"/>
+        <result column="update_user" jdbcType="VARCHAR" property="updateUser"/>
+        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
+        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
+        <result column="version" jdbcType="VARCHAR" property="version"/>
 
-    <result column="name" jdbcType="VARCHAR" property="name" />
-    <result column="phone_no" jdbcType="VARCHAR" property="phoneNo" />
-    <result column="memo" jdbcType="VARCHAR" property="memo" />
-  </resultMap>
+        <result column="name" jdbcType="VARCHAR" property="name"/>
+        <result column="phone_no" jdbcType="VARCHAR" property="phoneNo"/>
+        <result column="memo" jdbcType="VARCHAR" property="memo"/>
+    </resultMap>
 
-  <select id="sumWithDay" resultType="java.math.BigDecimal">
-    SELECT SUM(amount) AS total_amount
-    FROM c_red_envelope
-    WHERE user_id=#{userId}
-    <if test="day != null">
-      and trans_time >= #{day}
-    </if>
-    <if test="type == 1">
-      and status = 1
-    </if>
-    <if test="type == 2">
-      and status = 2
-    </if>
-  </select>
+    <select id="sumWithDay" resultType="java.math.BigDecimal">
+        SELECT SUM(amount) AS total_amount
+        FROM c_red_envelope
+        WHERE user_id=#{userId}
+        <if test="day != null">
+            and trans_time >= #{day}
+        </if>
+        <if test="type == 1">
+            and status = 1
+        </if>
+        <if test="type == 2">
+            and status = 2
+        </if>
+    </select>
 
-  <select id="top" resultType="com.txz.cif.web.bo.UserTopBo">
-    SELECT
-    user_id as userId,
-    SUM(amount) AS l7DEarnings,
-    count(id) AS groupNum
-    FROM
-      c_red_envelope
-    WHERE
-    create_time >= #{startTime}
-    GROUP BY
-    user_id
-    ORDER BY
-    l7DEarnings DESC
-  </select>
+    <select id="top" resultType="com.txz.cif.web.bo.UserTopBo">
+        SELECT user_id AS userId, SUM(amount) AS l7DEarnings, COUNT(id) AS groupNum
+        FROM c_red_envelope
+        WHERE create_time >= #{startTime}
+        GROUP BY user_id
+        ORDER BY l7DEarnings DESC
+    </select>
 
-  <select id="sumWithOrderNo" resultType="com.txz.cif.dto.EarningsDTO">
-    SELECT SUM(amount) AS earnings, user_id as userId,order_no as orderNo
-    FROM c_red_envelope
-    WHERE order_no=#{orderNo} 
-    AND user_id = #{userId}
-    group by user_id
-  </select>
+    <select id="sumWithOrderNo" resultType="com.txz.cif.dto.EarningsDTO">
+        SELECT SUM(amount) AS earnings, user_id AS userId, order_no AS orderNo
+        FROM c_red_envelope
+        WHERE order_no = #{orderNo}
+            AND user_id = #{userId}
+        GROUP BY user_id
+    </select>
 
-  <select id="sumByStatus" resultType="java.math.BigDecimal">
-    SELECT SUM(amount)
-    FROM c_red_envelope
-    WHERE
-    1=1
-    <if test="status == 1">
-       and status = 1
-    </if>
-    <if test="status == 2">
-      and status = 2
-    </if>
-    <if test="bizType != null">
-      and biz_type = #{bizType}
-    </if>
-    and create_time >= #{startTime} and #{endTime} >=  create_time
-    and user_id = #{userId} <!-- 添加分片键确保查询正确路由 -->
-  </select>
+    <select id="queryOrderNo" resultType="com.txz.cif.dto.EarningsDTO">
+        SELECT amount AS earnings, user_id AS userId, order_no AS orderNo, biz_type AS bizType
+        FROM c_red_envelope
+        WHERE order_no = #{orderNo}
+            AND user_id = #{userId}
+    </select>
+
+    <select id="sumByStatus" resultType="java.math.BigDecimal">
+        SELECT SUM(amount)
+        FROM c_red_envelope
+        WHERE
+        1=1
+        <if test="status == 1">
+            and status = 1
+        </if>
+        <if test="status == 2">
+            and status = 2
+        </if>
+        <if test="bizType != null">
+            and biz_type = #{bizType}
+        </if>
+        and create_time >= #{startTime} and #{endTime} >= create_time
+        and user_id = #{userId} <!-- 添加分片键确保查询正确路由 -->
+    </select>
 </mapper>

+ 1 - 0
cif-service/src/main/resources/static/i18n/messages_bn_BD.properties

@@ -15,6 +15,7 @@ BizTypeEnum.REFUND RECHARGE=refund recharge
 BizTypeEnum.WITHDRAWAL FEE=withdrawal fee
 BizTypeEnum.FREEZE=freeze
 BizTypeEnum.UNFREEZE=unfreeze
+BizTypeEnum.RECYCLE=recycle
 
 RechargeStatusEnum.PROCESSED=Processing
 RechargeStatusEnum.SUCCESS=Completed

+ 1 - 0
cif-service/src/main/resources/static/i18n/messages_en.properties

@@ -15,6 +15,7 @@ BizTypeEnum.REFUND_RECHARGE=refund recharge
 BizTypeEnum.WITHDRAWAL_FEE=withdrawal fee
 BizTypeEnum.FREEZE=freeze
 BizTypeEnum.UNFREEZE=unfreeze
+BizTypeEnum.RECYCLE=recycle
 
 RechargeStatusEnum.PROCESSED=Processing
 RechargeStatusEnum.SUCCESS=Completed

+ 1 - 0
cif-service/src/main/resources/static/i18n/messages_en_US.properties

@@ -15,6 +15,7 @@ BizTypeEnum.REFUND_RECHARGE=refund recharge
 BizTypeEnum.WITHDRAWAL_FEE=withdrawal fee
 BizTypeEnum.FREEZE=freeze
 BizTypeEnum.UNFREEZE=unfreeze
+BizTypeEnum.RECYCLE=recycle
 
 RechargeStatusEnum.PROCESSED=Processing
 RechargeStatusEnum.SUCCESS=Completed

+ 1 - 0
cif-service/src/main/resources/static/i18n/messages_zh_CN.properties

@@ -15,6 +15,7 @@ BizTypeEnum.REFUND_RECHARGE=\u5145\u503C\u8FD4\u70B9
 BizTypeEnum.WITHDRAWAL_FEE=\u63D0\u73B0\u624B\u7EED\u8D39
 BizTypeEnum.FREEZE=\u51BB\u7ED3
 BizTypeEnum.UNFREEZE=\u89E3\u51BB
+BizTypeEnum.RECYCLE=\u56de\u6536
 
 RechargeStatusEnum.PROCESSED=\u5F85\u5145\u503C
 RechargeStatusEnum.SUCCESS=\u6210\u529F

+ 11 - 0
sql/20260107/20260107_recycle.sql

@@ -0,0 +1,11 @@
+ALTER TABLE `cif`.`c_user`
+	ADD COLUMN `total_number_of_win` int NOT NULL DEFAULT 0 COMMENT '总中奖次数' AFTER `has_order`,
+ADD COLUMN `total_number_of_recycle` INT NOT NULL DEFAULT 0 COMMENT '总回收次数' AFTER `total_number_of_win`;
+
+INSERT INTO operating.o_config (id,parent_id,name,code,viwe_type,value_info,seq)
+VALUES (36,NULL,'订单回收比例','recycle_proportion',1,'0.8',1);
+INSERT INTO operating.o_config (id,parent_id,name,code,viwe_type,value_info,seq)
+VALUES (37,NULL,'成功订单总数','success_total_order_num',1,'6',1);
+INSERT INTO operating.o_config (id,parent_id,name,code,viwe_type,value_info,seq)
+VALUES (38,NULL,'回收数量','recycle_num',1,'5',1);
+