gao 1 ay önce
ebeveyn
işleme
ebd14c7d2e
86 değiştirilmiş dosya ile 4941 ekleme ve 0 silme
  1. 0 0
      README.md
  2. 30 0
      crm-model/.gitignore
  3. 97 0
      crm-model/pom.xml
  4. 35 0
      crm-model/src/main/java/com/crm/rely/backend/model/constant/ConfigConstants.java
  5. 560 0
      crm-model/src/main/java/com/crm/rely/backend/model/constant/SettlementConstant.java
  6. 65 0
      crm-model/src/main/java/com/crm/rely/backend/model/dto/user/info/UserInfoDto.java
  7. 80 0
      crm-model/src/main/java/com/crm/rely/backend/model/dto/user/info/UserInfoSearchDto.java
  8. 24 0
      crm-model/src/main/java/com/crm/rely/backend/model/dto/vaultody/TxResult.java
  9. 59 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/base/SettlementBaseEntity.java
  10. 12 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/property/SettlementPropertyEntity.java
  11. 34 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/property/vaultody/VaultodyPayPropertyEntity.java
  12. 15 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/property/vaultody/VaultodyPayWithdrawAddressPropertyEntity.java
  13. 29 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/property/vaultody/VaultodyPayWithdrawPropertyEntity.java
  14. 45 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/SettlementDepositEntity.java
  15. 45 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/SettlementWithdrawEntity.java
  16. 10 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/VaultodyDepositRequestEntity.java
  17. 10 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/VaultodyWithdrawRequestEntity.java
  18. 27 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/callback/VaultodyCallbackEntity.java
  19. 13 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/callback/VaultodyDataEntity.java
  20. 46 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/callback/VaultodyItemEntity.java
  21. 21 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/callback/VaultodyMinedInBlockEntity.java
  22. 26 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/callback/VaultodyTokenEntity.java
  23. 62 0
      crm-model/src/main/java/com/crm/rely/backend/model/entity/system/email/SysEmailSendEntity.java
  24. 25 0
      crm-model/src/main/java/com/crm/rely/backend/model/pojo/table/FinanceDepositAddressTable.java
  25. 63 0
      crm-model/src/main/java/com/crm/rely/backend/model/pojo/table/SettlementDepositRecordTable.java
  26. 79 0
      crm-model/src/main/java/com/crm/rely/backend/model/pojo/table/SettlementWithdrawRecordTable.java
  27. 22 0
      crm-model/src/main/java/com/crm/rely/backend/model/pojo/table/SysRemitChannelTable.java
  28. 22 0
      crm-model/src/main/java/com/crm/rely/backend/model/pojo/table/SysRemittanceChannelTable.java
  29. 16 0
      crm-model/src/main/java/com/crm/rely/backend/model/pojo/table/SysSettlementConfigTable.java
  30. 120 0
      crm-model/src/main/java/com/crm/rely/backend/model/pojo/view/UserInfoView.java
  31. 50 0
      crm-model/src/main/java/com/crm/rely/backend/model/util/Base64Util.java
  32. 32 0
      crm-model/src/main/java/com/crm/rely/backend/model/util/BeanCopyUtils.java
  33. 36 0
      crm-model/src/main/java/com/crm/rely/backend/model/util/BeanUtilPlus.java
  34. 295 0
      crm-model/src/main/java/com/crm/rely/backend/model/util/EncryptionHashQueryUtil.java
  35. 100 0
      crm-model/src/main/java/com/crm/rely/backend/model/util/HAMCSHA256Util.java
  36. 79 0
      crm-model/src/main/java/com/crm/rely/backend/model/util/ImageBase64ConverterUtil.java
  37. 94 0
      crm-model/src/main/java/com/crm/rely/backend/model/util/RandomChoiceUtil.java
  38. 20 0
      crm-model/src/main/java/com/crm/rely/backend/model/util/UUIDUtil.java
  39. 62 0
      crm-model/src/main/java/com/crm/rely/backend/model/util/ValueUtil.java
  40. 10 0
      crm-model/src/main/resources/i18n/defaultMessages.properties
  41. 10 0
      crm-model/src/main/resources/i18n/defaultMessages_en_US.properties
  42. 10 0
      crm-model/src/main/resources/i18n/defaultMessages_zh_CN.properties
  43. 1 0
      crm-model/打包.bat
  44. 190 0
      crm-settlement/pom.xml
  45. 20 0
      crm-settlement/src/main/java/com/crm/settlement/SettlementApplication.java
  46. 20 0
      crm-settlement/src/main/java/com/crm/settlement/config/SettlementHttpConfig.java
  47. 38 0
      crm-settlement/src/main/java/com/crm/settlement/controller/SettlementController.java
  48. 47 0
      crm-settlement/src/main/java/com/crm/settlement/controller/VaultodyController.java
  49. 10 0
      crm-settlement/src/main/java/com/crm/settlement/dao/mapper/FinanceDepositAddressMapper.java
  50. 16 0
      crm-settlement/src/main/java/com/crm/settlement/dao/mapper/SettlementDepositRecordMapper.java
  51. 17 0
      crm-settlement/src/main/java/com/crm/settlement/dao/mapper/SettlementWithdrawRecordMapper.java
  52. 18 0
      crm-settlement/src/main/java/com/crm/settlement/dao/repository/FinanceDepositAddressRepository.java
  53. 19 0
      crm-settlement/src/main/java/com/crm/settlement/dao/repository/SettlementDepositRecordRepository.java
  54. 19 0
      crm-settlement/src/main/java/com/crm/settlement/dao/repository/SettlementWithdrawRecordRepository.java
  55. 30 0
      crm-settlement/src/main/java/com/crm/settlement/dao/repository/SysConfigRepository.java
  56. 33 0
      crm-settlement/src/main/java/com/crm/settlement/dao/repository/SysRemitChannelRepository.java
  57. 37 0
      crm-settlement/src/main/java/com/crm/settlement/dao/repository/SysRemittanceChannelRepository.java
  58. 16 0
      crm-settlement/src/main/java/com/crm/settlement/dao/repository/SysSettlementConfigRepository.java
  59. 17 0
      crm-settlement/src/main/java/com/crm/settlement/service/SettlementService.java
  60. 18 0
      crm-settlement/src/main/java/com/crm/settlement/service/SysConfigService.java
  61. 12 0
      crm-settlement/src/main/java/com/crm/settlement/service/SysRemitChannelService.java
  62. 16 0
      crm-settlement/src/main/java/com/crm/settlement/service/SysRemittanceChannelService.java
  63. 10 0
      crm-settlement/src/main/java/com/crm/settlement/service/SysSettlementConfigService.java
  64. 18 0
      crm-settlement/src/main/java/com/crm/settlement/service/VaultodyService.java
  65. 396 0
      crm-settlement/src/main/java/com/crm/settlement/service/impl/SettlementServiceImpl.java
  66. 51 0
      crm-settlement/src/main/java/com/crm/settlement/service/impl/SysConfigServiceImpl.java
  67. 24 0
      crm-settlement/src/main/java/com/crm/settlement/service/impl/SysRemitChannelServiceImpl.java
  68. 29 0
      crm-settlement/src/main/java/com/crm/settlement/service/impl/SysRemittanceChannelServiceImpl.java
  69. 52 0
      crm-settlement/src/main/java/com/crm/settlement/service/impl/SysSettlementConfigServiceImpl.java
  70. 611 0
      crm-settlement/src/main/java/com/crm/settlement/service/impl/VaultodyServiceImpl.java
  71. 198 0
      crm-settlement/src/main/java/com/crm/settlement/service/impl/base/BaseSettlementServiceImpl.java
  72. 25 0
      crm-settlement/src/main/resources/application-dev.yml
  73. 29 0
      crm-settlement/src/main/resources/application-ho.yml
  74. 26 0
      crm-settlement/src/main/resources/application-hu.yml
  75. 21 0
      crm-settlement/src/main/resources/application-prod.yml
  76. 25 0
      crm-settlement/src/main/resources/application-test.yml
  77. 19 0
      crm-settlement/src/main/resources/application.yml
  78. 41 0
      crm-settlement/src/main/resources/i18n/messages.properties
  79. 43 0
      crm-settlement/src/main/resources/i18n/messages_en_US.properties
  80. 42 0
      crm-settlement/src/main/resources/i18n/messages_vn_VN.properties
  81. 43 0
      crm-settlement/src/main/resources/i18n/messages_zh_CN.properties
  82. 57 0
      crm-settlement/src/main/resources/logback-dev.xml
  83. 57 0
      crm-settlement/src/main/resources/logback-prod.xml
  84. 10 0
      crm-settlement/src/main/resources/mapper/FinanceDepositAddressMapper.xml
  85. 15 0
      crm-settlement/src/main/resources/mapper/SettlementDepositRecordMapper.xml
  86. 15 0
      crm-settlement/src/main/resources/mapper/SettlementWithdrawRecordMapper.xml

+ 0 - 0
README.md


+ 30 - 0
crm-model/.gitignore

@@ -0,0 +1,30 @@
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/build/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### maven ###
+/mvnw
+/mvnw.cmd
+/.mvn/*

+ 97 - 0
crm-model/pom.xml

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.crm.model</groupId>
+    <artifactId>crm-model</artifactId>
+    <version>3.0.0</version>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.11.0</version>
+                <configuration>
+                    <source>17</source>
+                    <target>17</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <layout>ZIP</layout>
+                    <includes>
+                        <include>
+                            <groupId>com.crm.model</groupId>
+                            <artifactId>crm-model</artifactId>
+                        </include>
+                    </includes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>17</java.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.crm.core</groupId>
+            <artifactId>crm-core</artifactId>
+            <version>2.1.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.30</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.80</version>
+        </dependency>
+        <!-- 导出 -->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>5.2.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>5.2.5</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>3.3.4</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>33.0.0-jre</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.14.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.25</version>
+        </dependency>
+    </dependencies>
+</project>

+ 35 - 0
crm-model/src/main/java/com/crm/rely/backend/model/constant/ConfigConstants.java

@@ -0,0 +1,35 @@
+package com.crm.rely.backend.model.constant;
+
+public class ConfigConstants {
+
+    public static final String FINANCE_PROPERTY_KEY = "FINANCE_PROPERTY_KEY";
+    public static final String ADDRESS_ID_CONTROL_AUTO_NUMBER = "ADDRESS_ID_CONTROL_AUTO_NUMBER";
+    public static final String VAULTODY_ADDRESS_TOTAL_NUMBER = "VAULTODY_ADDRESS_TOTAL_NUMBER";
+
+    //CALLBACE
+    public static final Integer CALLBACE_PROCESSING_STATUS = 0;
+    public static final Integer CALLBACE_SUCCESS_STATUS = 1;
+    public static final Integer CALLBACE_FAIL_STATUS = 2;
+    /**
+     * 回调重试次数
+     */
+    public static final String CALLBACK_MAX_RETRY_COUNT = "CALLBACK_MAX_RETRY_COUNT";
+
+    /**
+     * 入金回调
+     */
+    public static final String DEPOSIT_CALLBACK = "DEPOSIT_CALLBACK";
+    /**
+     * 入金回调重试
+     */
+    public static final String DEPOSIT_RETRY_CALLBACK = "DEPOSIT_RETRY_CALLBACK";
+
+    /**
+     * 出金回调
+     */
+    public static final String WITHDRAW_CALLBACK = "WITHDRAW_CALLBACK";
+    /**
+     * 出金回调重试
+     */
+    public static final String WITHDRAW_RETRY_CALLBACK = "WITHDRAW_RETRY_CALLBACK";
+}

+ 560 - 0
crm-model/src/main/java/com/crm/rely/backend/model/constant/SettlementConstant.java

@@ -0,0 +1,560 @@
+package com.crm.rely.backend.model.constant;
+
+/**
+ * @program: crm-backend
+ * @description:
+ * @author: houn
+ * @create: 2020-05-05 18:09
+ */
+public class SettlementConstant {
+
+    // <editor-fold desc="支付">
+
+    /**
+     * 支付-交易账号不能为空
+     */
+    public final static String PAY_LOGIN_NOT_EMPTY = "login_not_empty";
+
+    /**
+     * 支付入金金额不能为空
+     */
+    public final static String PAY_REMITTANCE_AMOUNT_NOT_EMPTY = "remittance_amount_not_empty";
+
+    public final static String FINANCE_DEPOSIT_AMOUNT_LESS_THAN = "finance_deposit_amount_less_than";
+
+    public final static String FINANCE_DEPOSIT_AMOUNT_GREATER_THAN = "finance_deposit_amount_greater_than";
+
+    /**
+     * 不存在汇率
+     */
+    public final static String FINANCE_DEPOSIT_RATE_NOT_EMPTY = "finance_deposit_rate_not_empty";
+
+    public final static String FINANCE_DEPOSIT_CHANNEL_NOT_EMPTY = "finance_deposit_channel_not_empty";
+
+    public final static String PAY_REMIT_AMOUNT_NOT_EMPTY = "remit_amount_not_empty";
+
+    public final static String FINANCE_WITHDRAW_AMOUNT_LESS_THAN = "finance_withdraw_amount_less_than";
+
+    public final static String FINANCE_WITHDRAW_AMOUNT_GREATER_THAN = "finance_withdraw_amount_greater_than";
+
+    public final static String FINANCE_WITHDRAW_CHANNEL_NOT_EMPTY = "finance_withdraw_channel_not_empty";
+
+    public final static String FINANCE_WITHDRAW_RATE_NOT_EMPTY = "finance_withdraw_rate_not_empty";
+
+    public final static String FINANCE_TRANSFER_AMOUNT_NOT_EMPTY = "finance_transfer_amount_not_empty";
+
+    public final static String FINANCE_TRANSFER_AMOUNT_LESS_THAN = "finance_transfer_amount_less_than";
+
+    public final static String FINANCE_TRANSFER_AMOUNT_GREATER_THAN = "finance_transfer_amount_greater_than";
+
+    public final static String FINANCE_TRANSFER_RATE_NOT_EMPTY = "finance_transfer_rate_not_empty";
+
+    public final static String ENCRYPTED_WALLET_NOT_AUTHENTICATED = "encrypted_wallet_not_authenticated";
+
+    /**
+     * 内部转账交易账户不能相同
+     */
+    public final static String FINANCE_TRANSFER_LOGIN_EQUAL = "finance_transfer_login_equal";
+    //</editor-fold>
+
+    // <editor-fold desc="支付类型">
+
+    /**
+     * 支付类型 safe pay支付
+     */
+    public final static String PAY_TYPE_SAFE = "SAFE_PAY";
+
+    public final static String FLASHEX_TYPE_KEY = "FLASHEX_PAY";
+
+    public final static String GROUP_OTC_TYPE_KEY = "GROUP_OTC_PAY";
+
+    public final static String PAY_MTPAY_CNY = "MTPAY_CNY";
+    public final static String PAY_MTPAY_MYR = "MTPAY_MYR";
+    public final static String PAY_MTPAY_IDR = "MTPAY_IDR";
+    public final static String PAY_MTPAY_VND = "MTPAY_VND";
+    public final static String MTPAY_CNY = "cny";
+    public final static String MTPAY_MYR = "myr";
+    public final static String MTPAY_IDR = "idr";
+    public final static String MTPAY_VND = "vnd";
+
+    public final static String HELP_PAY_TYPE = "HELP_PAY";
+
+    public final static String EX_PAY_KEY = "EX_PAY";
+
+    public final static String B2BIN_PAY_TYPE_KEY = "B2BINPAY";
+
+    public final static String B2BIN_V2_PAY_TYPE_KEY = "B2BINV2PAY";
+
+    public final static String TELEGRAPHIC_PAY_TYPE_KEY = "UNION_PAY_TELEGRAPHIC";
+    public final static String UNION_PAY_TELEGRAPHIC_SPECIAL_TYPE_KEY = "UNION_PAY_TELEGRAPHIC_SPECIAL";
+    public final static String NGANLUONG_PAY_TYPE_KEY = "NGANLUONG_PAY";
+    public final static String UNION_PAY_TELEGRAPHIC_TWO = "UNION_PAY_TELEGRAPHIC_TWO";
+
+    public final static String PAY_TYPE_STIC = "STIC_PAY";
+
+    public final static String SKRILL_PAY_TYPE = "SKRILL_PAY";
+
+    public final static String OTC_365_CN_PAY_KEY = "OTC_365_CN_PAY_KEY";
+
+    public final static String OTC_365_CN_V2_PAY_KEY = "OTC_365_CN_V2_PAY_KEY";
+
+    public final static String UNIOTC_TYPE_KEY = "UNIOTC_TYPE_KEY";
+
+    public final static String NETELLER_PAY_TYPE = "NETELLER_PAY";
+
+
+    public final static String ACE_PAY_KEY = "ACE_PAY";
+
+    public final static String BIFUTONG_PAY_KEY = "BIFUTONG_PAY_KEY";
+    public final static String SPAYSWAORLD_PAY_KEY = "SPAYSWAORLD_PAY_KEY";
+
+
+    public final static String VA5_PAY_KEY_VA = "VA5_PAY_KEY_VA";
+    public final static String VA5_PAY_KEY_C2C = "VA5_PAY_KEY_C2C";
+    public final static String VA5_PAY_KEY_F2F = "VA5_PAY_KEY_F2F";
+
+
+    public final static String UENJOY_PAY_KEY = "UENJOY_PAY";
+
+
+    public final static String ALPHA_PAY_TYPE_KEY = "ALPHA_PAY";
+    public final static String ALLBT_PAY_TYPE = "ALLBT_PAY_TYPE";
+
+
+    public final static String BIPI_PAY_TYPE_KEY = "BIPI_PAY_TYPE_KEY";
+
+    public final static String CLICK_PAY_KEY = "CLICK_PAY_KEY";
+    public final static String DIGITAL_PAY_TYPE_KEY = "DIGITAL_PAY_TYPE_KEY";
+    public final static String DIGITAL_PAY_TYPE_KEY_UK = "DIGITAL_PAY_TYPE_KEY_UK";
+
+
+    public final static String ACCEPAYMENT_PAY_TYPE_KEY = "ACCEPAYMENT_PAY_TYPE_KEY";
+
+
+    public final static String G_PAY_ZFB_ZYM_HH_KEY = "G_PAY_ZFB_ZYM_HH_KEY";
+    public final static String G_PAY_USDT_KEY = "G_PAY_USDT_KEY";
+
+
+    public final static String ANX_PAY_KEY = "ANX_PAY_KEY";
+
+    public final static String NE_PAY_KEY = "NE_PAY_KEY";
+    public final static String NE_PAY_KEY_HC = "NE_PAY_KEY_HC";
+    public final static String NE_PAY_KEY_REMIT = "NE_PAY_KEY_REMIT";
+
+    public final static String NE_PAY_KEY_XJ = "NE_PAY_KEY_XJ";
+
+    public final static String MT_PAY_KEY = "MT_PAY_KEY";
+
+    public final static String PAY3_PAY_KEY = "PAY3_PAY_KEY";
+
+    public final static String UCARD_PAY_KEY = "UCARD_PAY_KEY";
+    public final static String VAULTODY_PAY_KEY = "VAULTODY_PAY_KEY";
+
+    public final static String VAULTODY_PAY_REMIT = "VAULTODY_PAY_REMIT";
+
+    public final static String UNION_PAY_REMIT = "UNION_PAY_REMIT";
+
+    public final static String UGATE_PAY_KEY = "UGATE_PAY_KEY";
+    public final static String UGATE_PAY_KEY_HC = "UGATE_PAY_KEY_HC";
+
+    public final static String FLASH_PAY_KEY = "FLASH_PAY_KEY";
+    public final static String FLASH_PAY_KEY_HC = "FLASH_PAY_KEY_HC";
+
+    public final static String PARTNER_PAY_KEY = "PARTNER_PAY_KEY";
+
+    public final static String ANX_PAY_SUCCESS = "0000";
+
+    public final static String HY_PAY_KEY = "HY_PAY_KEY";
+    public final static String HY_PAY_KEY_HC = "HY_PAY_KEY_HC";
+
+    public final static String PARTNER_TW_PAY_KEY = "PARTNER_TW_PAY_KEY";
+
+    public final static String ALLLAND_PAY_KEY = "ALLLAND_PAY_KEY";
+    public final static String ALLLAND_PAY_KEY_HC = "ALLLAND_PAY_KEY_HC";
+    public final static String ALLLAND_PAY_TYPE_REMIT = "ALLLAND_PAY_TYPE_REMIT";
+
+    public final static String S2S_PAY_KEY = "S2S_PAY_KEY";
+
+    public final static String XFG_PAY_KEY = "XFG_PAY_KEY";
+    public final static String XFG_PAY_TYPE_REMIT = "XFG_PAY_TYPE_REMIT";
+    public final static String XFG_PAY_REMIT_QUERY = "XFG_PAY_REMIT_QUERY";
+
+    /**
+     * safe pay 成功标识
+     */
+    public final static String HY_CANCEL_RESULT = "cancel";
+    public final static String HY_SUCCESS_RESULT = "success";
+
+    public final static String SAFE_SUCCESS = "success";
+    public final static String MTPAY_SUCCESS_STATUS = "1";
+    public final static int FLASHEX_SUCCESS = 3;
+
+    public final static String EX_PAY_SUCCESS_CHECK = "check";
+    public final static String EX_PAY_SUCCESS_PAY = "pay";
+
+    public final static String EX_PAY_MIDDLE = "LO";
+    public final static String GROUP_OTC_SUCCESS = "1";
+
+    public final static String HELP_SUCCESS_STATUS000 = "000";
+    public final static String HELP_SUCCESS_STATUS006 = "006";
+    public final static String NGANLUONG_SUCCESS_STATUS = "00";
+
+    public final static Integer STIC_SUCCESS = -1;
+
+    public final static String SKRILL_SUCCESS_STATUS000 = "2";
+
+    public final static String MTPAY_FAIL_RESULT = "FAIL";
+    public final static String MTPAY_SUCCESS_RESULT = "SUCCESS";
+
+    public final static String FLASHEX_SUCCESS_RESULT = "SUCCESS";
+
+    public final static String B2BIN_PAY_SUCCESS_RESULT = "OK";
+
+    public final static String HELP_FAIL_RESULT = "FAIL";
+    public final static String GROUP_OTC_SUCCESS_RESULT = "success";
+
+    public final static String NGANLUONG_SUCCESS_RESULT = "success";
+
+    public final static String STIC_SUCCESS_RESULT = "OK";
+
+    public final static String STIC_FAIL_RESULT = "FAIL";
+
+    public final static String OTC_365_CN_SUCCESS_CHECK = "1";
+
+
+    public final static String UNIOTC_CHECK_SUCCESS = "Success";
+    public final static String UNIOTC_SUCCESS = "CODE_SUCCESS";
+
+    public final static String UNIOTC_SUCCESS_RESULT = "success";
+
+    public final static String ALLBTPAY_SUCCESS_STATUS000 = "success";
+    public final static String NETELLER_PAYMENT_HANDLE_PAYABLE = "PAYMENT_HANDLE_PAYABLE";
+    public final static String NETELLER_SUCCESS = "PAYMENT_COMPLETED";
+
+
+    public final static String ACE_SUCCESS_STATUS = "SUCCESS";
+    public final static String ACE_EXPIRE_STATUS = "EXPIRE";
+    public final static String ACE_WAIT_STATUS = "WAIT";
+
+    public final static String BIFUTONG_SUCCESS_STATUS = "1";
+
+    public final static String UENJOY_SUCCESS_STATUS = "1";
+
+
+    public final static String SPAYSWORLD_SUCCESS_STATUS = "1";
+
+
+    public final static String SPAYSWORLD_RESULT_SUCCESS_STATUS = "SUCCESS";
+
+    public final static String CLICKPAY_STATUS = "0";
+
+    public final static String VA5PAY_SUCCESS_STATUS = "4";
+
+    public final static String VA5PAY_C2C_SUCCESS_STATUS = "3";
+    public final static String VA5PAY_RESULT_SUCCESS_STATUS = "SUCCESS";
+
+    public final static String CWG_PAY_KEY = "CWG_PAY_KEY";
+    public final static String CWG_PAY_KEY_HC = "CWG_PAY_KEY_HC";
+
+    public final static String CWG_SUCCESS_STATUS = "2";
+
+    public final static String CWG_RESULT_SUCCESS_STATUS = "SUCCESS";
+
+    public final static String LONG77_VA_PAY_KEY = "LONG77_VA_PAY_KEY";
+    public final static String LONG77_RESULT_SUCCESS_STATUS = "success";
+    public final static String LONG77_SUCCESS_STATUS = "4";
+
+    public final static String KUBO_PAY_TYPE_KEY = "KUBO_PAY_TYPE_KEY";
+    public final static String KUBO_SUCCESS_STATUS = "Y";
+    public final static String KUBO_CANCELLED_STATUS = "C";
+
+    public final static String BEAR_EX_PAY_KEY = "BEAR_EX_PAY_KEY";
+
+    public final static String BEAR_EX_E_ADMIN_FINISH_STATUS = "E_ADMIN_FINISH";
+
+    public final static String BEAR_EX_E_FINISH_STATUS = "E_FINISH";
+    public final static String BEAR_EX_RESULT_SUCCESS = "success";
+
+    public final static String INOUTWORK_PAY_KEY = "INOUTWORK_PAY_KEY";
+
+    public final static String INOUTWORK_SUCCESS_STATUS = "SUCCESS";
+
+    public final static String INOUTWORK_RESULT_SUCCESS = "success";
+
+    public final static String KONNEXPY_PAY = "KONNEXPY_PAY";
+
+    public final static int KONNEXPY_CONFIRMED_STATUS = 1;
+    public final static int KONNEXPY_COMPLETED_STATUS = 2;
+
+    public final static int KONNEXPY_CANCELED_STATUS = 3;
+
+
+    public final static String CHIP_PAY_KEY = "CHIP_PAY_KEY";
+    public final static String CHIP_SUCCESS = "1";
+    public final static String CHIP_FAIL = "0";
+
+    public final static String EXLINK_PAY_KEY = "EXLINK_PAY_KEY";
+    public final static String EXLINK_SUCCESS_STATUS = "1";
+
+
+    public final static String EXLINK_DIGITAL_PAY_KEY = "EXLINK_DIGITAL_PAY_KEY";
+    public final static String EXLINK_DIGITAL_SUCCESS_STATUS = "1";
+
+    public final static String PAY_RETAILER_PAY_KEY_AR = "PAY_RETAILER_PAY_KEY_AR";
+    public final static String PAY_RETAILER_PAY_KEY_BR = "PAY_RETAILER_PAY_KEY_BR";
+    public final static String PAY_RETAILER_PAY_KEY_BRW = "PAY_RETAILER_PAY_KEY_BRW";
+    public final static String PAY_RETAILER_PAY_KEY_CL = "PAY_RETAILER_PAY_KEY_CL";
+    public final static String PAY_RETAILER_PAY_KEY_CO = "PAY_RETAILER_PAY_KEY_CO";
+    public final static String PAY_RETAILER_PAY_KEY_CR = "PAY_RETAILER_PAY_KEY_CR";
+    public final static String PAY_RETAILER_PAY_KEY_EC = "PAY_RETAILER_PAY_KEY_EC";
+    public final static String PAY_RETAILER_PAY_KEY_SV = "PAY_RETAILER_PAY_KEY_SV";
+    public final static String PAY_RETAILER_PAY_KEY_MX = "PAY_RETAILER_PAY_KEY_MX";
+    public final static String PAY_RETAILER_PAY_KEY_PA = "PAY_RETAILER_PAY_KEY_PA";
+    public final static String PAY_RETAILER_PAY_KEY_PE = "PAY_RETAILER_PAY_KEY_PE";
+    public final static String PAY_RETAILER_PAY_KEY_GT = "PAY_RETAILER_PAY_KEY_GT";
+
+
+    public final static String KORAPAY_PAY = "KORAPAY_PAY";
+
+    public final static String KORAPAY_SUCCESS_STATUS = "success";
+    public final static String KORAPAY_PROCESSING_STATUS = "processing";
+    public final static String KORAPAY_FAILED_STATUS = "failed";
+    public final static String KORAPAY_PAY_PIN = "PIN";
+    public final static String KORAPAY_PAY_3DS = "3DS";
+    public final static String KORAPAY_PAY_OTP = "OTP";
+    public final static String KORAPAY_PAY_AVS = "AVS";
+    public final static String KORAPAY_PAY_PHONE = "CARD_ENROLL";
+
+    public final static String KORAPAY_CHECKOUT_PAY = "KORAPAY_CHECKOUT_PAY";
+
+    public final static String KORAPAY_CHECKOUT_SUCCESS_STATUS = "success";
+    public final static String KORAPAY_CHECKOUT_PROCESSING_STATUS = "processing";
+    public final static String KORAPAY_CHECKOUT_FAILED_STATUS = "failed";
+
+    public final static String PAYMENTASIA_PAY = "PAYMENTASIA_PAY_KEY";
+    public final static String PAYMENTASIA_PROCESSING_STATUS = "0";
+    public final static String PAYMENTASIA_SUCCESS_STATUS = "1";
+
+
+    public final static String VERTU_PAY_KEY_IDR_IB_P2P = "VERTU_PAY_KEY_IDR_IB_P2P";
+    public final static String VERTU_PAY_KEY_IDR_VA = "VERTU_PAY_KEY_IDR_VA";
+    public final static String VERTU_PAY_KEY_MYR_IB_FPX = "VERTU_PAY_KEY_MYR_IB_FPX";
+    public final static String VERTU_PAY_KEY_MYR_IB_P2C = "VERTU_PAY_KEY_MYR_IB_P2C";
+    public final static String VERTU_PAY_KEY_SGD_IB_P2P = "VERTU_PAY_KEY_SGD_IB_P2P";
+    public final static String VERTU_PAY_KEY_THB_IB_P2P = "VERTU_PAY_KEY_THB_IB_P2P";
+    public final static String VERTU_PAY_KEY_THB_QR_QRC = "VERTU_PAY_KEY_THB_QR_QRC";
+    public final static String VERTU_PAY_KEY_THB_QR_QRP = "VERTU_PAY_KEY_THB_QR_QRP";
+    public final static String VERTU_PAY_KEY_VND_IB_P2P = "VERTU_PAY_KEY_VND_IB_P2P";
+    public final static String VERTU_PAY_KEY_VND_QR = "VERTU_PAY_KEY_VND_QR";
+    public final static String VERTU_PAY_KEY_JPY_FT = "VERTU_PAY_KEY_JPY_FT";
+    public final static String VERTU_PAY_KEY_JPY_IB_FO = "VERTU_PAY_KEY_JPY_IB_FO";
+
+    public final static String VERTU_PAY_SUCCESS_STATUS = "success";
+
+
+    public final static String BRIGHTCART_PAY_KEY = "BRIGHTCART_PAY_KEY";
+    public final static String BRIGHTCART_SUCCESS_STATUS = "success";
+    public final static String BRIGHTCART_FAIL_STATUS = "fail";
+    public final static String BRIGHTCART_WAITING_STATUS = "waiting";
+
+    public final static String PAY_RETAILER_SUCCESS = "APPROVED";
+    public final static String PAY_RETAILER_REJECTED = "REJECTED";
+    public final static String PAY_RETAILER_PENDING = "PENDING";
+    public final static String PAY_RETAILER_FAILED = "FAILED";
+    public final static String PAY_RETAILER_EXPIRED = "EXPIRED";
+
+
+    public final static String ECOM_PAY_TYPE_KEY = "ECOM_PAY_TYPE_KEY";
+
+    public final static String BIEASE_PAY_TYPE_KEY = "BIEASE_PAY_TYPE_KEY";
+
+    public final static String BIEASE_SUCCESS_STATUS = "3";
+
+    public final static String BIEASE_RESULT_SUCCESS_STATUS = "SUCCESS";
+
+
+    public final static String ECOM_PAY_SUCCESS_STATUS = "1";
+
+
+    public final static String PAYPAGA_PAY_TYPE = "PAYPAGA_PAY_TYPE";
+
+
+    public final static String PAYPAGA_PAY_SUCCESS_STATUS = "Approved";
+
+
+    public final static String PAYPAGA_PAY_HANDLE_PAYABLE = "Payin";
+
+
+    public final static String XPAY_PAY_TYPE = "XPAY_PAY_TYPE";
+    public final static String XPAY_PAY_CRYPTOCURRENCIE_TYPE = "XPAY_PAY_CRYPTOCURRENCIE_TYPE";
+    public final static String XPAY_PAY_SUCCESS_STATUS = "COMPLETE";
+
+
+    public final static String XPAY_PAY_CRYPTOCURRENCIE_SUCCESS_STATUS = "0";
+    public final static String PAYOK_PAY_TYPE_THB = "PAYOK_PAY_TYPE_THB";
+    public final static String PAYOK_PAY_TYPE_IDR = "PAYOK_PAY_TYPE_IDR";
+    public final static String PAYOK_PAY_SUCCESS_STATUS = "SUCCESS";
+
+
+    public final static String PAYOK_PAY_RESULT_STATUS = "SUCCESS";
+
+
+    public final static String EVIRTUALPAY_PAY_TYPE = "EVIRTUALPAY_PAY_TYPE";
+    public final static String EVIRTUALPAY_PAY_SUCCESS_STATUS = "0";
+
+    public final static String NACE_PAY_TYPE = "NACE_PAY_TYPE";
+    public final static String NACE_PAY_SUCCESS_STATUS = "Paid";
+
+
+    public final static String PRAXIS_PAY_KEY = "PRAXIS_PAY_KEY";
+
+    public final static String PRAXIS_SUCCESS_STATUS = "approved";
+
+
+    public final static String HYPER_HASHING_PAY_TYPE = "HYPER_HASHING_PAY_TYPE";
+    public final static String HYPER_HASHING_SUCCESS_STATUS = "200";
+
+
+    public final static String PROXPAY_PAY_TYPE = "PROXPAY_PAY_TYPE";
+    public final static String PROXPAY_PAY_TYPE_REMIT = "PROXPAY_PAY_TYPE_REMIT";
+    public final static String PROXPAY_CP_SUCCESS_STATUS = "CP";
+    public final static String PROXPAY_Y_SUCCESS_STATUS = "Y";
+    public final static String OFA_PAY_TYPE = "OFA_PAY_TYPE";
+    public final static String OFA_PAY_TYPE_REMIT = "OFA_PAY_TYPE_REMIT";
+    public final static String OFA_PAY_SUCCESS_STATUS = "1";
+    public final static String OFA_PAY_TYPE_REMIT_SUCCESS_STATUS = "s";
+    public final static String HUABO_PAY_TYPE = "HUABO_PAY_TYPE";
+    public final static String HUABO_PAY_SUCCESS_STATUS = "2";
+    public final static String HUABO_PAY_CALLBACK_SUCCESS_STATUS = "3";
+
+    public final static String CHEEZEEPAY_PAY_TYPE_REMIT = "CHEEZEEPAY_PAY_TYPE_REMIT";
+    public final static String CWG_UNION_PAY_TYPE_REMIT = "CWG_UNION_PAY_TYPE_REMIT";
+
+    public final static String OZOW_PAY_TYPE = "OZOW_PAY_TYPE";
+    public final static String OZOW_PAY_SUCCESS_STATUS = "Complete";
+
+
+    public final static String PAY_ECOM_REMIT_KEY = "PAY_ECOM_REMIT_KEY";
+    public final static String PAY_ECOM_REMIT_KEY_SUCCESS_STATUS = "1";
+
+    public final static String OZOW_PAY_TYPE_REMIT = "OZOW_PAY_TYPE_REMIT";
+    public final static String OZOW_PAY_TYPE_REMIT_SUCCESS_STATUS = "PayoutReceived";
+
+    public final static String CWG_PAY_TYPE_REMIT = "CWG_PAY_TYPE_REMIT";
+    public final static String CWG_PAY_TYPE_REMIT_SUCCESS_STATUS = "2";
+    public final static String CHEEZEEPAY_PAY_TYPE = "CHEEZEEPAY_PAY_TYPE";
+    public final static String CHEEZEEPAY_THAILAND_PAY_TYPE = "CHEEZEEPAY_THAILAND_PAY_TYPE";
+    public final static String CHEEZEEPAY_BRAZIL_PAY_TYPE = "CHEEZEEPAY_BRAZIL_PAY_TYPE";
+    public final static String CHEEZEEPAY_INDONESIA_PAY_TYPE = "CHEEZEEPAY_INDONESIA_PAY_TYPE";
+    public final static String CHEEZEEPAY_PAY_TYPE_SUCCESS_STATUS = "1";
+    public final static String CHEEZEEPAY_PAY_TYPE_PARTIAL_SUCCESS_STATUS = "3";
+
+    public final static String MT_PAY_TYPE_REMIT = "MT_PAY_TYPE_REMIT";
+    public final static String UGATE_PAY_TYPE_REMIT = "UGATE_PAY_TYPE_REMIT";
+    public final static String FLASH_PAY_TYPE_REMIT = "FLASH_PAY_TYPE_REMIT";
+    public final static String PARTNER_PAY_TYPE_REMIT = "PARTNER_PAY_TYPE_REMIT";
+    public final static String PARTNER_TW_PAY_TYPE_REMIT = "PARTNER_TW_PAY_TYPE_REMIT";
+
+    public final static String VA5_PAY_TYPE_USDT_REMIT = "VA5_PAY_TYPE_USDT_REMIT";
+    public final static String VA5_PAY_TYPE_F2F_REMIT = "VA5_PAY_TYPE_F2F_REMIT";
+    public final static String MT_BANK = "MT_BANK";
+    public final static String MT_WECHAT = "MT_WECHAT";
+    public final static String MT_ALIPAY = "MT_ALIPAY";
+    public final static String MT_BLOCKCHIN = "MT_BLOCKCHIN";
+    public final static String MT_ACCOUNT = "MT_ACCOUNT";
+
+    public final static String VA5_PAY_TYPE_F2F_INR = "INR";
+    public final static String VA5_PAY_TYPE_F2F_MYR = "MYR";
+
+    public final static String PAY3_PAY_TYPE_REMIT = "PAY3_PAY_TYPE_REMIT";
+
+    public final static String UCARD_PAY_TYPE_REMIT = "UCARD_PAY_TYPE_REMIT";
+
+    public final static String UGATE_BANK = "UGATE_BANK";
+    public final static String UGATE_WECHAT = "UGATE_WECHAT";
+    public final static String UGATE_ALIPAY = "UGATE_ALIPAY";
+
+    public final static String CHEEZEEPAY_PAY_TYPE_RESULT_SUCCESS_STATUS = "SUCCESS";
+    //</editor-fold>
+
+    //佣金内转头
+    public final static String AGENT_COMMISSION_TRANSFER_HEAD = "PACT";
+    //入金
+    public final static String DEPOSIT_HEAD = "PD";
+    //出金
+    public final static String WITHDRAW_HEAD = "PW";
+
+    //退款
+    public final static String WITHDRAW_REFUND_HEAD = "PWR";
+    //
+    public final static String WITHDRAW_AGENT_HEAD = "PWC";
+    //内转
+    public final static String TRANSFER_HEAD = "PT";
+    //跨系统内转
+    public final static String TRANSFER_SYSTEM_HEAD = "PTS";
+    //代理内转
+    public final static String FINANCE_AGENT_TRANSFER_HEAD = "PAT";
+    //异名内转
+    public final static String FINANCE_SYNONYM_TRANSFER_HEAD = "PST";
+    //赠金
+    public final static String DEPOSIT_GIVE_HEAD = "PDG";
+    //风控撤销信用金
+    public final static String REVOKE_CREDIT_HEAD = "PRC";
+    //组别
+    public final static String LEVERAGE_RULE_HEAD = "AL";
+
+    //跟单内转
+    public final static String TRANSFER_FOLLOW_HEAD = "PTF";
+
+    public final static String TRANSFER_CUSTOM_HEAD = "PTC";
+
+    public final static String TRADE_INTEREST_HEAD = "PTI";
+
+    //跟单出金(分润)
+    public final static String TRADE_FOLLOW_WITHDRAW_HEAD = "PFW";
+
+    //代理余额内转
+    public final static String AGENT_BALANCE_COMMISSION_TRANSFER_HEAD = "PBCT";
+
+    //ucard单号
+    public final static String UCARD_HEAD = "UCARD";
+
+    //补零
+    public final static String COMPLETE_HEAD = "PCM";
+
+    /**
+     * 通道类型 银行通道
+     */
+    public final static String CHANNEL_TYPE_BANK = "BANK";
+    public final static String CHANNEL_TYPE_BANK_TELEGRAPHIC = "BANK_TELEGRAPHIC";
+
+    /**
+     * 通道类型 数字货币通道
+     */
+    public final static String CHANNEL_TYPE_DIGITAL_CURRENCY = "DIGITAL_CURRENCY";
+
+    /**
+     * 信用卡
+     */
+    public final static String CHANNEL_TYPE_CARD = "CHANNEL_TYPE_CARD";
+
+    public final static String CHANNEL_TYPE_WALLET = "CHANNEL_TYPE_WALLET";
+    public final static String CHANNEL_TYPE_ALI_WALLET = "CHANNEL_TYPE_ALI_WALLET";
+
+    /**
+     * 通道类型 ucard通道
+     */
+    public final static String UCARD_WALLET = "UCARD_WALLET";
+
+    public final static String WALLET_IS_EMPTY = "WALLET_IS_EMPTY";
+
+    public final static String PAYMENTASIA_PAY_TYPE_REMIT = "PAYMENTASIA_PAY_TYPE_REMIT";
+    public final static String PAYMENTASIA_PAY_TYPE_SUCCESS_STATUS = "200";
+
+    public final static String HELP_PAY_TYPE_REMIT = "HELP_PAY_TYPE_REMIT";
+
+    public final static String IBIT_PAY_TYPE = "IBIT_PAY_TYPE";
+    public final static String IBIT_PAY_TYPE_HC = "IBIT_PAY_TYPE_HC";
+    public final static String IBIT_PAY_TYPE_REMIT = "IBIT_PAY_TYPE_REMIT";
+
+}

+ 65 - 0
crm-model/src/main/java/com/crm/rely/backend/model/dto/user/info/UserInfoDto.java

@@ -0,0 +1,65 @@
+package com.crm.rely.backend.model.dto.user.info;
+
+import com.crm.rely.backend.core.dto.user.role.UserNodeDisplayDto;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+@Data
+public class UserInfoDto {
+
+    private Long id;
+    /**
+     * 登录名
+     */
+    private String username;
+    private String name;
+
+    private String email;
+
+    private String phone;
+
+    private Long departmentId;
+
+    private String departmentName;
+
+    private String roleCode;
+
+    private Long roleId;
+
+    private String roleName;
+
+    private String ibNo;
+
+    private Date thisTime;
+
+    private String thisIp;
+
+    private String thisAddress;
+
+    private Date lastTime;
+
+    private String lastIp;
+
+    private String lastAddress;
+
+    private String stamp;
+
+    private Integer valid;
+    private Integer userType;
+    // 谷歌验证 0 未绑定 1 绑定
+    private Integer verified;
+    private Integer applyRealStatus; // 完善信息审核状态 1 申请中 2同意 3拒绝
+
+    private List<String> addressLines;
+
+    private String areaCode;
+
+    private String identity;      // 身份证件号
+    private String firstName;     // 名
+    private String lastName;      // 姓
+    private String state;         // 州/省
+    private String cardType;
+    private List<UserNodeDisplayDto> display;
+}

+ 80 - 0
crm-model/src/main/java/com/crm/rely/backend/model/dto/user/info/UserInfoSearchDto.java

@@ -0,0 +1,80 @@
+package com.crm.rely.backend.model.dto.user.info;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @program: crm-backend
+ * @description:
+ * @author: houn
+ * @create: 2019-07-30 18:06
+ */
+@Data
+public class UserInfoSearchDto {
+
+    private Long id;
+
+    private String username;
+
+    private String name;
+
+    private String email;
+
+    private Long roleId;
+
+    private Long departmentId;
+
+    private String roleName;
+
+    private Long pid;
+
+    private String ibNo;
+
+    private Integer valid;
+
+    private Date lastTime;
+
+    private String lastIp;
+
+    private String lastAddress;
+
+    /**
+     * 是否ip限制 0否 1是
+     */
+    private Integer ipLimit;
+    /**
+     * 限制ip 如果不为空和null 则只有这个ip才能登录
+     */
+    private String limitIp;
+
+    private Date addTime;
+
+    private Integer userType;
+    private BigDecimal inRate;
+    private BigDecimal outRate;
+    private BigDecimal inProfitRate;
+    private BigDecimal outProfitRate;
+    //
+    private List<String> addressLines;   // 详细地址
+    private String areaCode;      // 区号
+//    private Date birth;           // 出生日期
+//    private String city;          // 城市
+    //    private Integer gender;       // 性别 1:男 2:女
+//    private String identity;      // 身份证件号
+    //    private String state;         // 州/省
+//    private String zipCode;       // 邮政编码
+    private String cardType;      // 证件类型
+    private Integer authStatus;   // '认证状态 0:未认证。1:已认证'
+    private String externalUserId;
+    private String kycVerifyUrl;   // 认证url
+    private Integer status;            // 状态码 0:刚注册 剩下待补充
+    private Integer applyRealStatus;   // 完善信息审核状态 1 申请中 2同意 3拒绝
+    private Date applyRealTime;        // 申请时间
+    /**
+     * 谷歌验证状态
+     */
+    private Integer verified;
+}

+ 24 - 0
crm-model/src/main/java/com/crm/rely/backend/model/dto/vaultody/TxResult.java

@@ -0,0 +1,24 @@
+package com.crm.rely.backend.model.dto.vaultody;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * @author gao
+ * @date 2026/1/13
+ */
+@Data
+public class TxResult {
+
+    private String chain;
+    private String txHash;
+    private String status;
+    private Long timestamp;
+    private String from;
+    private String to;
+    private BigDecimal amount;
+    private String content;
+    private String contractAddress;
+
+}

+ 59 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/base/SettlementBaseEntity.java

@@ -0,0 +1,59 @@
+package com.crm.rely.backend.model.entity.base;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class SettlementBaseEntity {
+
+    @NotBlank
+    private String merchantId;
+
+    @NotBlank
+    private String channelCode;
+
+    @NotNull
+    @NotBlank
+    private String serial;
+
+    @NotNull
+    private BigDecimal amount;
+
+    @NotNull
+    @NotBlank
+    private String currency;
+
+    @NotNull
+    @NotBlank
+    private String callbackUrl;
+
+    @NotNull
+    private Date requestTime;
+
+    @NotNull
+    @NotBlank
+    private String sign;
+
+    private String address;
+
+    private String bankUname;
+
+    private String bankCardNum;
+
+    private String bankName;
+
+    private String bankBranchName;
+
+    private String bankAddr;
+
+    private String swiftCode;
+
+    private String cvv;
+
+    private String expiryMonth;
+
+}

+ 12 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/property/SettlementPropertyEntity.java

@@ -0,0 +1,12 @@
+package com.crm.rely.backend.model.entity.property;
+
+import lombok.Data;
+
+@Data
+public class SettlementPropertyEntity {
+
+    private String privateKey;
+
+    private String merchantPublicKey;
+
+}

+ 34 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/property/vaultody/VaultodyPayPropertyEntity.java

@@ -0,0 +1,34 @@
+package com.crm.rely.backend.model.entity.property.vaultody;
+
+import lombok.Data;
+
+@Data
+public class VaultodyPayPropertyEntity {
+
+    private String apiKey;
+
+    private String apiSecret;
+
+    private String passphrase;
+
+    private String method;
+
+    private String vaultId;
+
+    private String baseUrl;
+
+    private String network;
+
+    private String addressPathTemplate;
+
+    private String webhooksPassphrase;
+
+    private String addressUrl;
+
+    private String successUrl;
+    /**
+     * url加密密钥。前端解密用
+     */
+    private String decryptKey;
+
+}

+ 15 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/property/vaultody/VaultodyPayWithdrawAddressPropertyEntity.java

@@ -0,0 +1,15 @@
+package com.crm.rely.backend.model.entity.property.vaultody;
+
+import lombok.Data;
+
+@Data
+public class VaultodyPayWithdrawAddressPropertyEntity {
+
+    private String bankCode;
+
+    private String fromAddress;
+
+    private String contractAddress;
+
+
+}

+ 29 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/property/vaultody/VaultodyPayWithdrawPropertyEntity.java

@@ -0,0 +1,29 @@
+package com.crm.rely.backend.model.entity.property.vaultody;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class VaultodyPayWithdrawPropertyEntity {
+
+    private String apiKey;
+
+    private String apiSecret;
+
+    private String passphrase;
+
+    private String method;
+
+    private String vaultId;
+
+    private String baseUrl;
+
+    private String network;
+
+    private String webhooksPassphrase;
+
+    private String createTokenTransactionPathTemplate;
+
+    private List<VaultodyPayWithdrawAddressPropertyEntity> withdrawAddress;
+}

+ 45 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/SettlementDepositEntity.java

@@ -0,0 +1,45 @@
+package com.crm.rely.backend.model.entity.settlement;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class SettlementDepositEntity {
+
+    @NotBlank
+    private String merchantId;
+
+    @NotBlank
+    private String channelCode;
+
+    @NotNull
+    @NotBlank
+    private String serial;
+
+    @NotNull
+    private BigDecimal callbackAmount;
+
+    @NotNull
+    @NotBlank
+    private String callbackCurrency;
+
+    @NotNull
+    private Integer status;
+
+    @NotNull
+    @NotBlank
+    private String callbackSerial;
+
+    @NotNull
+    private Date requestTime;
+
+    @NotNull
+    @NotBlank
+    private String sign;
+
+
+}

+ 45 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/SettlementWithdrawEntity.java

@@ -0,0 +1,45 @@
+package com.crm.rely.backend.model.entity.settlement;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class SettlementWithdrawEntity {
+
+    @NotBlank
+    private String merchantId;
+
+    @NotBlank
+    private String channelCode;
+
+    @NotNull
+    @NotBlank
+    private String serial;
+
+    @NotNull
+    private BigDecimal callbackAmount;
+
+    @NotNull
+    @NotBlank
+    private String callbackCurrency;
+
+    @NotNull
+    private Integer status;
+
+    @NotNull
+    @NotBlank
+    private String callbackSerial;
+
+    @NotNull
+    private Date requestTime;
+
+    @NotNull
+    @NotBlank
+    private String sign;
+
+
+}

+ 10 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/VaultodyDepositRequestEntity.java

@@ -0,0 +1,10 @@
+package com.crm.rely.backend.model.entity.settlement.vaultody;
+
+import com.crm.rely.backend.model.entity.base.SettlementBaseEntity;
+import lombok.Data;
+
+@Data
+public class VaultodyDepositRequestEntity extends SettlementBaseEntity {
+
+
+}

+ 10 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/VaultodyWithdrawRequestEntity.java

@@ -0,0 +1,10 @@
+package com.crm.rely.backend.model.entity.settlement.vaultody;
+
+import com.crm.rely.backend.model.entity.base.SettlementBaseEntity;
+import lombok.Data;
+
+@Data
+public class VaultodyWithdrawRequestEntity extends SettlementBaseEntity {
+
+
+}

+ 27 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/callback/VaultodyCallbackEntity.java

@@ -0,0 +1,27 @@
+package com.crm.rely.backend.model.entity.settlement.vaultody.callback;
+
+import lombok.Data;
+
+@Data
+public class VaultodyCallbackEntity {
+
+    /**
+     * 钱包id
+     */
+    private String walletId;
+    /**
+     * Webhook 事件 ID
+     */
+    private String webhookId;
+    /**
+     * API 版本
+     */
+    private String apiVersion;
+    /**
+     * 系统生成的唯一ID,附加到每个回调。服务器用它来识别具有相同数据的数据连续请求,目的是避免重复执行相同的操作。
+     */
+    private String idempotencyKey;
+
+    private VaultodyDataEntity data;
+
+}

+ 13 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/callback/VaultodyDataEntity.java

@@ -0,0 +1,13 @@
+package com.crm.rely.backend.model.entity.settlement.vaultody.callback;
+
+import lombok.Data;
+
+@Data
+public class VaultodyDataEntity {
+    /**
+     * 定义了设置回调订阅的具体事件。
+     */
+    private String event;
+    private VaultodyItemEntity item;
+
+}

+ 46 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/callback/VaultodyItemEntity.java

@@ -0,0 +1,46 @@
+package com.crm.rely.backend.model.entity.settlement.vaultody.callback;
+
+import lombok.Data;
+
+@Data
+public class VaultodyItemEntity {
+
+    /**
+     * 表示特定的区块链协议名称,例如以太坊、比特币等。
+     */
+    private String blockchain;
+    /**
+     * 它表示所使用的区块链网络名称
+     */
+    private String network;
+    private String requestId;
+    private String transactionType;
+    private String tokenType;
+    private String direction;
+    private Integer requiredApprovals;
+    private Integer requiredRejections;
+    private Integer currentApprovals;
+    private Integer currentRejections;
+    /**
+     * 它定义了已发送并确认的硬币交易的具体地址。
+     */
+    private String address;
+    private VaultodyMinedInBlockEntity minedInBlock;
+    private Integer currentConfirmations;
+    private Integer targetConfirmations;
+    /**
+     * 定义了特定交易的唯一ID,即其识别号码。
+     */
+    private String transactionId;
+    private String amount;
+    /**
+     * 定义交易单位,例如BTC。
+     */
+    private String unit;
+    private String parentTransactionId;
+    private String operationId;
+    private String failedReason;
+    private String status;
+    private VaultodyTokenEntity token;
+
+}

+ 21 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/callback/VaultodyMinedInBlockEntity.java

@@ -0,0 +1,21 @@
+package com.crm.rely.backend.model.entity.settlement.vaultody.callback;
+
+import lombok.Data;
+
+@Data
+public class VaultodyMinedInBlockEntity {
+
+    /**
+     * 定义了此特定区块之前区块链中的区块数量。
+     */
+    private Integer height;
+    /**
+     * 代表区块头部的哈希值,即具有固定长度的输出。
+     */
+    private String hash;
+    /**
+     * 时间戳-秒
+     */
+    private Long timestamp;
+
+}

+ 26 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/settlement/vaultody/callback/VaultodyTokenEntity.java

@@ -0,0 +1,26 @@
+package com.crm.rely.backend.model.entity.settlement.vaultody.callback;
+
+import lombok.Data;
+
+@Data
+public class VaultodyTokenEntity {
+
+    /**
+     * 指定了令牌的名称。
+     */
+    private String tokenName;
+    /**
+     * 指定一个标识符,最多可以使用五个字母数字字符。
+     */
+    private String tokenSymbol;
+    /**
+     * 定义了可以用于拆分令牌的小数位数。
+     */
+    private Integer decimals;
+    private String tokensAmount;
+    /**
+     * 定义合同的地址。
+     */
+    private String contract;
+
+}

+ 62 - 0
crm-model/src/main/java/com/crm/rely/backend/model/entity/system/email/SysEmailSendEntity.java

@@ -0,0 +1,62 @@
+package com.crm.rely.backend.model.entity.system.email;
+
+import com.crm.rely.backend.core.constant.EmailSendEnum;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.File;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class SysEmailSendEntity {
+    private String users;
+
+    /**
+     * 模版名称或者code 优先级高于邮件内容(content) 设置名称 将覆盖content
+     * 如果当前值不为空 首先在sys_email_template中根据code搜索
+     * 如果不存在则根据默认为地址名称在本地搜索
+     * 如果还不存在则直接用content中的值
+     */
+    private String templateName;
+
+    private Long emailConfigId;
+
+    /**
+     * 邮件标题
+     */
+    private String subject;
+
+    /**
+     * 邮件内容
+     */
+    private String content;
+
+    private Map<String, String> map;
+
+    private Date sendDate;
+
+    /**
+     * 用于添加到邮件中的图片
+     */
+    private Map<String, File> imageFilesMap;
+    /**
+     * 用于添加到邮件中的附件
+     */
+    private List<File> attachContentsList;
+
+    private EmailSendEnum emailSendEnum;
+
+    private String callbackMq;
+
+    private boolean isBbc;
+
+    private Date addTime;
+    private Long addUser;
+    private String addIp;
+    private String note;
+}

+ 25 - 0
crm-model/src/main/java/com/crm/rely/backend/model/pojo/table/FinanceDepositAddressTable.java

@@ -0,0 +1,25 @@
+package com.crm.rely.backend.model.pojo.table;
+
+import com.crm.rely.backend.core.pojo.BaseTable;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@Entity
+@Table(name = "finance_deposit_address")
+public class FinanceDepositAddressTable extends BaseTable {
+
+    private String serial;
+
+    private String address;
+
+    private Boolean isUsed;
+
+    private String content;
+
+    private Date lastUsedTime;
+
+}

+ 63 - 0
crm-model/src/main/java/com/crm/rely/backend/model/pojo/table/SettlementDepositRecordTable.java

@@ -0,0 +1,63 @@
+package com.crm.rely.backend.model.pojo.table;
+
+import com.crm.rely.backend.core.pojo.BaseTable;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@Entity
+@Table(name = "settlement_deposit_record")
+public class SettlementDepositRecordTable extends BaseTable {
+
+    /**
+     * 商户号
+     */
+    private String merchantId;
+
+    private String serial;
+    /**
+     * 标识,用于回调是确认订单
+     */
+    private String requestSerial;
+
+    private BigDecimal amount;
+
+    private String currency;
+
+    private Integer status;
+
+    private String channelCode;
+
+    private String channelProperty;
+
+    private String requestBody;
+
+    private String callbackUrl;
+
+    private Date callbackTime;
+
+    private Boolean callbackStatus;
+
+    private String callbackSerial;
+
+    private BigDecimal callbackAmount;
+
+    private String callbackCurrency;
+
+    private String callbackData;
+
+    private Integer callbackMerchantStatus;
+
+    private Integer callbackMerchantCount;
+
+    private Integer callbackMerchantMaxRetryCount;
+
+    private String callbackMerchantBody;
+
+    private Date callbackMerchantTime;
+
+}

+ 79 - 0
crm-model/src/main/java/com/crm/rely/backend/model/pojo/table/SettlementWithdrawRecordTable.java

@@ -0,0 +1,79 @@
+package com.crm.rely.backend.model.pojo.table;
+
+import com.crm.rely.backend.core.pojo.BaseTable;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@Entity
+@Table(name = "settlement_withdraw_record")
+public class SettlementWithdrawRecordTable extends BaseTable {
+
+    private String merchantId;
+
+    private String serial;
+
+    private String requestSerial;
+
+    private BigDecimal amount;
+
+    private String currency;
+
+    private String address;
+
+    private String bankUname;
+
+    private String bankCardNum;
+
+    private String bankName;
+
+    private String bankBranchName;
+
+    private String bankAddr;
+
+    private String swiftCode;
+
+    private String cvv;
+
+    private String expiryMonth;
+
+    private String channelCode;
+
+    private String channelProperty;
+
+    private String requestBody;
+
+    /**
+     * 三方回调订单状态 :0-初始 2-成功 3-失败
+     */
+    private Integer status;
+
+    private String callbackUrl;
+
+    private Date callbackTime;
+
+    private Boolean callbackStatus;
+
+    private String callbackSerial;
+
+    private BigDecimal callbackAmount;
+
+    private String callbackCurrency;
+
+    private String callbackData;
+
+    private Integer callbackMerchantStatus;
+
+    private Integer callbackMerchantCount;
+
+    private Integer callbackMerchantMaxRetryCount;
+
+    private String callbackMerchantBody;
+
+    private Date callbackMerchantTime;
+
+}

+ 22 - 0
crm-model/src/main/java/com/crm/rely/backend/model/pojo/table/SysRemitChannelTable.java

@@ -0,0 +1,22 @@
+package com.crm.rely.backend.model.pojo.table;
+
+import com.crm.rely.backend.core.pojo.BaseTable;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+@Data
+@Entity
+@Table(name = "sys_remit_channel")
+public class SysRemitChannelTable extends BaseTable {
+
+    /**
+     * 通道位置code
+     */
+    private String code;
+    /**
+     * 通道配置信息
+     */
+    private String property;
+
+}

+ 22 - 0
crm-model/src/main/java/com/crm/rely/backend/model/pojo/table/SysRemittanceChannelTable.java

@@ -0,0 +1,22 @@
+package com.crm.rely.backend.model.pojo.table;
+
+import com.crm.rely.backend.core.pojo.BaseTable;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+@Data
+@Entity
+@Table(name = "sys_remittance_channel")
+public class SysRemittanceChannelTable extends BaseTable {
+
+    /**
+     * 通道位置code
+     */
+    private String code;
+    /**
+     * 通道配置信息
+     */
+    private String property;
+
+}

+ 16 - 0
crm-model/src/main/java/com/crm/rely/backend/model/pojo/table/SysSettlementConfigTable.java

@@ -0,0 +1,16 @@
+package com.crm.rely.backend.model.pojo.table;
+
+import com.crm.rely.backend.core.pojo.BaseTable;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+@Data
+@Entity
+@Table(name = "sys_settlement_config")
+public class SysSettlementConfigTable extends BaseTable {
+
+    private String code;
+
+    private String value;
+}

+ 120 - 0
crm-model/src/main/java/com/crm/rely/backend/model/pojo/view/UserInfoView.java

@@ -0,0 +1,120 @@
+package com.crm.rely.backend.model.pojo.view;
+
+import com.crm.rely.backend.core.pojo.BaseTable;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * @program: crm-backend
+ * @description:
+ * @author: houn
+ * @create: 2019-07-25 16:22
+ */
+@Data
+public class UserInfoView extends BaseTable {
+
+    private String name;
+
+    private String email;
+
+    private String phone;
+
+    private String firstName;
+
+    private String username;
+
+    private String middle;
+
+    private String lastName;
+
+    private String country;
+
+    private Long roleId;
+
+    private String roleName;
+
+    private String roleCode;
+
+    private String pIbNo;
+
+    private Long pid;
+
+    private Long cId;
+
+    private Long customId;
+
+    private String ibNo;
+
+    private Integer valid;
+
+    private Date addTime;
+
+    private String lastIp;
+
+    private Long departmentId;
+
+    private String departmentName;
+    /**
+     * 用户组ID
+     */
+    private Long groupId;
+
+    private String groupName;
+    private String groupEnName;
+    /**
+     * 销售ID
+     */
+    private Long salesId;
+
+    /**
+     * 销售编号
+     */
+    private String salesNo;
+
+    /**
+     * 内部数据权限代码
+     */
+    private String stamp;
+
+    private String pStamp;
+
+    /**
+     * 是否ip限制 0否 1是
+     */
+    private Integer ipLimit;
+    /**
+     * 限制ip 如果不为空和null 则只有这个ip才能登录
+     */
+    private String limitIp;
+
+
+    private Integer userType;
+
+    private BigDecimal inRate;
+    private BigDecimal outRate;
+
+    private BigDecimal inProfitRate;
+    private BigDecimal outProfitRate;
+    //
+    private String addressLine;   // 详细地址
+    private String areaCode;      // 区号
+//    private Date birth;           // 出生日期
+//    private String city;          // 城市
+    //    private Integer gender;       // 性别 1:男 2:女
+//    private String identity;      // 身份证件号
+    //    private String state;         // 州/省
+//    private String zipCode;       // 邮政编码
+    private String cardType;      // 证件类型
+    private Integer authStatus;   // '认证状态 0:未认证。1:已认证'
+    private String externalUserId;
+    private String kycVerifyUrl;   // 认证url
+    private Integer status;            // 状态码 0:刚注册 剩下待补充
+    private Integer applyRealStatus;   // 完善信息审核状态 1 申请中 2同意 3拒绝
+    private Date applyRealTime;        // 申请时间
+    /**
+     * 谷歌验证状态
+     */
+    private Integer verified;
+}

+ 50 - 0
crm-model/src/main/java/com/crm/rely/backend/model/util/Base64Util.java

@@ -0,0 +1,50 @@
+package com.crm.rely.backend.model.util;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Base64;
+
+/**
+ * @program: cwg-my-base
+ * @description:
+ * @author: houn
+ * @create: 2019-08-02 09:52
+ */
+public class Base64Util {
+
+    public static String toBase64String(String str) {
+
+        String base64Sign = null;
+        try {
+            base64Sign = Base64.getEncoder().encodeToString(str.getBytes("UTF-8"));
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+
+        return base64Sign;
+    }
+
+    public static String toBase64String(byte[] byteArray) {
+
+        String base64Str = Base64.getEncoder().encodeToString(byteArray);
+
+        return base64Str;
+    }
+
+    public static byte[] toByteArray(String str) {
+
+        byte[] byteArray = Base64.getDecoder().decode(str);
+
+        return byteArray;
+    }
+
+    public static String toBase64DecoderString(byte[] byteArray) {
+        String base64Decodedr = null;
+        try {
+            base64Decodedr = new String(byteArray);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return base64Decodedr;
+    }
+}

+ 32 - 0
crm-model/src/main/java/com/crm/rely/backend/model/util/BeanCopyUtils.java

@@ -0,0 +1,32 @@
+package com.crm.rely.backend.model.util;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+
+import java.beans.PropertyDescriptor;
+import java.util.HashSet;
+import java.util.Set;
+
+public class BeanCopyUtils extends BeanUtils {
+    /**
+     * 拷贝源对象的非空属性到目标对象
+     * @param source 源对象
+     * @param target 目标对象
+     */
+    public static void copyNonNullProperties(Object source, Object target) {
+        BeanWrapper srcWrapper = new BeanWrapperImpl(source);
+        PropertyDescriptor[] pds = srcWrapper.getPropertyDescriptors();
+        
+        Set<String> emptyNames = new HashSet<>();
+        for (PropertyDescriptor pd : pds) {
+            String propertyName = pd.getName();
+            Object srcValue = srcWrapper.getPropertyValue(propertyName);
+            if (srcValue == null) {
+                emptyNames.add(propertyName);
+            }
+        }
+        
+        BeanUtils.copyProperties(source, target, emptyNames.toArray(new String[0]));
+    }
+}

+ 36 - 0
crm-model/src/main/java/com/crm/rely/backend/model/util/BeanUtilPlus.java

@@ -0,0 +1,36 @@
+package com.crm.rely.backend.model.util;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+
+import java.beans.PropertyDescriptor;
+import java.util.HashSet;
+import java.util.Set;
+
+public class BeanUtilPlus {
+    /**
+     * 复制非 null 属性
+     */
+    public static void copyPropertiesIgnoreNull(Object source, Object target) {
+        BeanUtils.copyProperties(source, target, getNullPropertyNames(source));
+    }
+
+    /**
+     * 获取所有为 null 的属性名
+     */
+    private static String[] getNullPropertyNames(Object source) {
+        final BeanWrapper src = new BeanWrapperImpl(source);
+        PropertyDescriptor[] pds = src.getPropertyDescriptors();
+
+        Set<String> emptyNames = new HashSet<>();
+        for (PropertyDescriptor pd : pds) {
+            Object srcValue = src.getPropertyValue(pd.getName());
+            if (srcValue == null) {
+                emptyNames.add(pd.getName());
+            }
+        }
+        String[] result = new String[emptyNames.size()];
+        return emptyNames.toArray(result);
+    }
+}

+ 295 - 0
crm-model/src/main/java/com/crm/rely/backend/model/util/EncryptionHashQueryUtil.java

@@ -0,0 +1,295 @@
+package com.crm.rely.backend.model.util;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.crm.rely.backend.core.exception.ServiceException;
+import com.crm.rely.backend.model.dto.vaultody.TxResult;
+import com.google.common.base.Strings;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+import java.util.Locale;
+
+@Slf4j
+public class EncryptionHashQueryUtil {
+
+    /* ================= API ================= */
+
+    private static final String ETH_API = "https://eth.blockscout.com/api/v2/transactions/";
+    private static final String ETC_API = "https://blockscout.com/etc/mainnet/api/v2/transactions/";
+    private static final String POLYGON_API = "https://polygon.blockscout.com/api/v2/transactions/";
+    private static final String TRON_API = "https://apilist.tronscanapi.com/api/transaction-info?hash=";
+    private static final String SOL_API = "https://api.mainnet-beta.solana.com";
+
+    /* ================= 合约地址 ================= */
+
+    private static final String ETH_USDT = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
+    private static final String ETH_USDC = "0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";
+
+    private static final String ETC_USDT = "0x1953cab0E5bFa6D4a9BaD6E05fD46C1CC6527a5a";
+
+    private static final String POLYGON_USDT = "0xC2132D05D31c914a87C6611C10748AaCbEFD1b2";
+    private static final String POLYGON_USDC = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
+
+    private static final String TRC_USDT = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";
+    private static final String TRC_USDC = "TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8";
+
+    private static final String SOL_USDT = "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB";
+    private static final String SOL_USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
+
+    public static void main(String[] args) throws Exception {
+        String txHash = "c370e2f1b6ac1d9f7078282ddad20d70f3623a2eaa8481b498d82585de9d94ee";
+        String chain = "tron";
+
+        System.out.println(query(txHash, chain));
+    }
+
+    /* ================= 统一入口 ================= */
+
+    public static TxResult query(String txHash, String chain) throws Exception {
+        return query(txHash, chain, "usdt");
+    }
+
+    public static TxResult query(String txHash, String chain, String currency) throws Exception {
+        if (Strings.isNullOrEmpty(currency)) {
+            currency = "usdt";
+        }
+
+        switch (chain.toLowerCase(Locale.ROOT)) {
+            case "ethereum":
+                return evmTxQuery(ETH_API, txHash, ethContract(currency));
+            case "ethereum-classic":
+                return evmTxQuery(ETC_API, txHash, ETC_USDT);
+            case "polygon":
+                return evmTxQuery(POLYGON_API, txHash, polygonContract(currency));
+            case "tron":
+                return tronTxQuery(txHash, currency);
+            case "solana":
+                return solTxQuery(txHash, currency);
+            default:
+                throw new ServiceException("Unsupported chain: " + chain);
+        }
+    }
+
+    /* ================= EVM ================= */
+
+    private static TxResult evmTxQuery(String api, String txHash, String expectedContract) throws Exception {
+
+        String json = get(api + txHash);
+        JSONObject obj = JSONObject.parseObject(json);
+
+        JSONArray transfers = obj.getJSONArray("token_transfers");
+        if (transfers == null || transfers.isEmpty()) {
+            throw new ServiceException("No token transfer");
+        }
+
+        for (Object o : transfers) {
+            JSONObject t = (JSONObject) o;
+            JSONObject token = t.getJSONObject("token");
+            if (token == null) {
+                continue;
+            }
+
+            String contract = token.getString("contract_address");
+            if (contract == null) {
+                contract = token.getString("address_hash");
+            }
+            if (!expectedContract.equalsIgnoreCase(contract)) {
+                continue;
+            }
+
+            JSONObject total = t.getJSONObject("total");
+            if (total == null) {
+                continue;
+            }
+
+            BigDecimal amount = new BigDecimal(total.getString("value"))
+                    .movePointLeft(total.getIntValue("decimals"));
+
+            JSONObject fromObj = t.getJSONObject("from");
+            JSONObject toObj = t.getJSONObject("to");
+
+            String from = fromObj == null ? null : fromObj.getString("hash");
+            String to = toObj == null ? null : toObj.getString("hash");
+
+            String status = obj.getString("status");
+            boolean success = !"failed".equalsIgnoreCase(status)
+                    && !"error".equalsIgnoreCase(status);
+
+            TxResult r = new TxResult();
+            r.setTxHash(txHash);
+            r.setFrom(from);
+            r.setTo(to);
+            r.setAmount(amount);
+            r.setContractAddress(contract);
+            r.setStatus(success ? "success" : "failed");
+            r.setContent(json);
+
+            String tsStr = obj.getString("timestamp");
+            r.setTimestamp(parseTimestamp(tsStr));
+
+            return r;
+        }
+
+        throw new ServiceException("Target token transfer not found");
+    }
+
+    /* ================= Tron ================= */
+
+    private static TxResult tronTxQuery(String txHash, String currency) throws Exception {
+
+        String json = get(TRON_API + txHash);
+        JSONObject obj = JSONObject.parseObject(json);
+
+        JSONArray arr = obj.getJSONArray("trc20TransferInfo");
+        if (arr == null || arr.isEmpty()) {
+            throw new ServiceException("No TRC20 transfer");
+        }
+
+        JSONObject t = arr.getJSONObject(0);
+        String contract = t.getString("contract_address");
+        String expect = currency.equalsIgnoreCase("usdc") ? TRC_USDC : TRC_USDT;
+
+        if (!expect.equalsIgnoreCase(contract)) {
+            throw new ServiceException("Invalid TRC20 contract");
+        }
+
+        BigDecimal amount = new BigDecimal(t.getString("amount_str"))
+                .movePointLeft(t.getIntValue("decimals"));
+
+        String ret = obj.getString("contractRet");
+
+        TxResult r = new TxResult();
+        r.setTxHash(txHash);
+        r.setFrom(t.getString("from"));
+        r.setTo(t.getString("to"));
+        r.setAmount(amount);
+        r.setContractAddress(contract);
+        r.setTimestamp(obj.getLongValue("timestamp") / 1000);
+        r.setStatus("SUCCESS".equalsIgnoreCase(ret) ? "success" : "failed");
+        r.setContent(json);
+        return r;
+    }
+
+    /* ================= Solana ================= */
+
+    private static TxResult solTxQuery(String txHash, String currency) throws Exception {
+
+        String mint = currency.equalsIgnoreCase("usdc") ? SOL_USDC : SOL_USDT;
+        String json = solanaRpc(txHash);
+
+        JSONObject result = JSONObject.parseObject(json).getJSONObject("result");
+        JSONObject meta = result.getJSONObject("meta");
+
+        JSONArray inner = meta.getJSONArray("innerInstructions");
+        if (inner == null) {
+            throw new ServiceException("No innerInstructions");
+        }
+
+        for (Object o : inner) {
+            JSONArray inst = ((JSONObject) o).getJSONArray("instructions");
+            for (Object i : inst) {
+                JSONObject p = ((JSONObject) i).getJSONObject("parsed");
+                if (p == null) {
+                    continue;
+                }
+
+                JSONObject info = p.getJSONObject("info");
+                if (!mint.equalsIgnoreCase(info.getString("mint"))) {
+                    continue;
+                }
+
+                BigDecimal amount = new BigDecimal(
+                        info.getJSONObject("tokenAmount").getString("amount")
+                ).movePointLeft(info.getJSONObject("tokenAmount").getIntValue("decimals"));
+
+                TxResult r = new TxResult();
+                r.setTxHash(txHash);
+                r.setFrom(info.getString("authority"));
+                r.setTo(info.getString("destination"));
+                r.setAmount(amount);
+                r.setContractAddress(mint);
+                r.setStatus(meta.get("err") == null ? "success" : "failed");
+                r.setContent(json);
+
+                Long blockTime = result.getLong("blockTime");
+                if (blockTime != null) {
+                    r.setTimestamp(blockTime);
+                }
+
+                return r;
+            }
+        }
+
+        throw new ServiceException("No SPL transfer");
+    }
+
+    /* ================= 工具 ================= */
+
+    private static long parseTimestamp(String ts) {
+        try {
+            return Instant.parse(ts).getEpochSecond();
+        } catch (Exception e) {
+            return Long.parseLong(ts.substring(0, 10));
+        }
+    }
+
+    private static String ethContract(String c) {
+        return c.equalsIgnoreCase("usdc") ? ETH_USDC : ETH_USDT;
+    }
+
+    private static String polygonContract(String c) {
+        return c.equalsIgnoreCase("usdc") ? POLYGON_USDC : POLYGON_USDT;
+    }
+
+    private static String solanaRpc(String tx) throws Exception {
+
+        JSONObject body = new JSONObject();
+        body.put("jsonrpc", "2.0");
+        body.put("id", 1);
+        body.put("method", "getTransaction");
+        body.put("params", new Object[]{tx, new JSONObject().fluentPut("encoding", "jsonParsed")});
+
+        HttpURLConnection conn = (HttpURLConnection) new URL(SOL_API).openConnection();
+        conn.setRequestMethod("POST");
+        conn.setDoOutput(true);
+        conn.getOutputStream().write(body.toJSONString().getBytes(StandardCharsets.UTF_8));
+
+        return read(conn);
+    }
+
+    private static String get(String url) throws Exception {
+        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
+        conn.setRequestProperty("User-Agent", "Mozilla/5.0");
+        return read(conn);
+    }
+
+    private static String read(HttpURLConnection conn) throws Exception {
+
+        InputStream in = null;
+        try {
+            in = conn.getResponseCode() >= 400
+                    ? conn.getErrorStream()
+                    : conn.getInputStream();
+
+            try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+                byte[] buf = new byte[8192];
+                int n;
+                while ((n = in.read(buf)) != -1) {
+                    out.write(buf, 0, n);
+                }
+                return new String(out.toByteArray(), StandardCharsets.UTF_8);
+            }
+        } finally {
+            if (in != null) {
+                in.close();
+            }
+        }
+    }
+}

+ 100 - 0
crm-model/src/main/java/com/crm/rely/backend/model/util/HAMCSHA256Util.java

@@ -0,0 +1,100 @@
+package com.crm.rely.backend.model.util;
+
+import org.apache.commons.codec.binary.Base64;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+public class HAMCSHA256Util {
+
+
+    private static final String HMAC_SHA1_ALGORITHM = "HmacSHA256";
+
+    public static String HMACSHA256(String data, String key) {
+        try {
+            Mac sha256_HMAC = Mac.getInstance(HMAC_SHA1_ALGORITHM);
+            SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("utf-8"), HMAC_SHA1_ALGORITHM);
+            sha256_HMAC.init(secretKey);
+            byte[] hash = sha256_HMAC.doFinal(data.getBytes("utf-8"));
+            return Base64.encodeBase64String(hash);
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        } catch (InvalidKeyException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    public static String sha256_HMAC(String message, String secret) {
+        return sha256_HMAC(message, secret.getBytes());
+    }
+
+    public static String sha256_HMAC(String message, byte[] secret) {
+        String hash = "";
+        try {
+            Mac sha256_HMAC = Mac.getInstance(HMAC_SHA1_ALGORITHM);
+            SecretKeySpec secret_key = new SecretKeySpec(secret, HMAC_SHA1_ALGORITHM);
+            sha256_HMAC.init(secret_key);
+            byte[] bytes = sha256_HMAC.doFinal(message.getBytes());
+            StringBuilder sb = new StringBuilder();
+            for (byte item : bytes) {
+                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
+            }
+            hash = sb.toString();
+        } catch (Exception e) {
+            System.out.println("Error HmacSHA256 ===========" + e.getMessage());
+        }
+        return hash;
+    }
+
+    /**
+     * 测试
+     *
+     * @param args
+     */
+    public static void main(String[] args) throws Exception {
+
+//        String genHMAC = HMACSHA256("apiKey%3Dc8de0fbc398643a59043f561ec37c578%26signVersion%3D1%26timestamp%3D2021
+//        -09-09T14%3A47%3A44",
+//                "a2699ccbb4c442c2866a13b274924a99f0530f2b1a3b777eedbfcd11131d03c6");
+
+//        String data = "2021-12-14T07:33:16.421542ZeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
+//        .eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYzOTQ4ODc5NiwianRpIjoiNTEyNzdlMzJjYjVjNGFiN2JhYmEyY2M5OWYzNjNjOTMiLCJ1c2VyX2lkIjo0MjZ9.JhXN3R1F6jGhDrSbKbH0qoTMeB9N5nL45hc-3P3aFvQ";
+//        String secretString = "pHXivvW5pA9Y58t3eK7QnuZVSTMqXyip";
+//
+//        String genHMAC = HMACSHA256(data, secretString);
+//        String sha256_HMAC = sha256_HMAC(data, secretString);
+////        System.out.println(genHMAC);
+//        System.out.println(sha256_HMAC);
+
+
+//        System.out.println(HMACSHA256("buy=7.2216&legal_currency_max_amount=50000.00&legal_currency_min_amount=100" +
+//                        ".00&sell=7.10861&symbol=USDT&timestamp=1586487661938",
+//                "02d82ba350d6f95e4a6005f9c17d5c0a7dc4c013ed09c4fd04971660f1d0d8c299"));
+//
+
+//        System.out.println(HMACSHA256("count=100&created_time=2024-04-25%2015%3A26%3A16&currency=USDT&legal_amount" +
+//                        "=737.00000000&legal_currency=CNY&merchant_order_no=PD202404251527169849_66753851" +
+//                        "&merchant_profit=0.00000000&mobile=null&nonce=1714030080312&pass_time=2024-04-25%2015%3A27" +
+//                        "%3A51&platform_fee=0.60000000&side=BUY&status=COMPLETE",
+//                "02ad904d8600b1f7d33823ba54b2f533983659c68c55da075f6d0948e4931625e9"));
+//
+//        System.out.println(HMACSHA256("count=100&created_time=2024-04-25%2015%3A26%3A16&currency=USDT" +
+//                        "&merchant_order_no=PD202404251527169849_66753851&merchant_profit=0" +
+//                        ".00000000&nonce=1714030080312&platform_fee=0.60000000&side=BUY&status=COMPLETE",
+//                "02d82ba350d6f95e4a6005f9c17d5c0a7dc4c013ed09c4fd04971660f1d0d8c299"));
+
+        System.out.println(HMACSHA256("count=100&created_time=2024-04-25%2015%3A26%3A16&currency=USDT&legal_amount" +
+                        "=737.00000000&legal_currency=CNY&merchant_order_no=PD202404251527169849_66753851" +
+                        "&merchant_profit=0.00000000&mobile=null&nonce=1714030080312&pass_time=2024-04-25%2015%3A27" +
+                        "%3A51&platform_fee=0.60000000&side=BUY&status=COMPLETE",
+                "0329a81f77955ec6a8b697e25d413c56fd71d084d7882280ff5ebdf3c1c6c67dc7"));
+        //yJghhncCqfqhcDZw16G5BughLVRVvEbS8bo8NW3lKj4=
+    }
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 79 - 0
crm-model/src/main/java/com/crm/rely/backend/model/util/ImageBase64ConverterUtil.java


+ 94 - 0
crm-model/src/main/java/com/crm/rely/backend/model/util/RandomChoiceUtil.java

@@ -0,0 +1,94 @@
+package com.crm.rely.backend.model.util;
+
+/**
+ * @author gao
+ * @date 2026/3/17
+ */
+
+import java.util.*;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * 随机选择器
+ */
+public class RandomChoiceUtil {
+
+    /**
+     * 根据权重随机选择一个用户 ID
+     * @param userScores Map<Long, Integer> key=用户 ID, value=分数 (权重)
+     * @return 选中的用户 ID
+     */
+    public static Long selectByWeight(Map<Long, Integer> userScores) {
+        if (userScores == null || userScores.isEmpty()) {
+            return null;
+        }
+
+        if (userScores.size() == 1) {
+            return userScores.keySet().iterator().next();
+        }
+
+        // 构建前缀和数组
+        int size = userScores.size();
+        long[] userIds = new long[size];
+        int[] prefixSums = new int[size];
+
+        int index = 0;
+        int sum = 0;
+
+        // 使用有序的 Map 保证顺序一致性
+        Map<Long, Integer> sortedMap = new TreeMap<>(userScores);
+
+        for (Map.Entry<Long, Integer> entry : sortedMap.entrySet()) {
+            userIds[index] = entry.getKey();
+            sum += entry.getValue();
+            prefixSums[index] = sum;
+            index++;
+        }
+
+        int totalWeight = sum;
+
+        // 生成随机数
+        int random = ThreadLocalRandom.current().nextInt(totalWeight);
+
+        // 二分查找
+        int left = 0;
+        int right = prefixSums.length - 1;
+
+        while (left < right) {
+            int mid = (left + right) >>> 1;
+            if (prefixSums[mid] <= random) {
+                left = mid + 1;
+            } else {
+                right = mid;
+            }
+        }
+
+        return userIds[left];
+    }
+
+    /**
+     * 批量选择多个用户 ID(不重复)
+     * @param userScores Map<Long, Integer> key=用户 ID, value=分数 (权重)
+     * @param count 需要选择的数量
+     * @return 选中的用户 ID 数组
+     */
+    public static List<Long> selectMultipleByWeight(Map<Long, Integer> userScores, int count) {
+        if (userScores == null || userScores.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        Set<Long> selectedIds = new LinkedHashSet<>();
+        Map<Long, Integer> remainingScores = new HashMap<>(userScores);
+
+        while (selectedIds.size() < count && !remainingScores.isEmpty()) {
+            Long selectedId = selectByWeight(remainingScores);
+            if (selectedId != null) {
+                selectedIds.add(selectedId);
+                remainingScores.remove(selectedId); // 移除已选中的,避免重复
+            }
+        }
+
+        return new ArrayList<>(selectedIds);
+    }
+
+}

+ 20 - 0
crm-model/src/main/java/com/crm/rely/backend/model/util/UUIDUtil.java

@@ -0,0 +1,20 @@
+package com.crm.rely.backend.model.util;
+
+import java.util.UUID;
+
+/**
+ * Created by max on 2018/5/4.
+ * UUID工具类
+ */
+public class UUIDUtil {
+    /**
+     * 生成UUID结果
+     * @return
+     */
+    public static String getUUID(){
+        String uuidStr = UUID.randomUUID().toString();
+        uuidStr = uuidStr.replace("-","");
+        return uuidStr;
+    }
+
+}

+ 62 - 0
crm-model/src/main/java/com/crm/rely/backend/model/util/ValueUtil.java

@@ -0,0 +1,62 @@
+package com.crm.rely.backend.model.util;
+
+/**
+ * @author gao
+ * @date 2026/3/11
+ */
+
+import java.util.Arrays;
+
+/**
+ * 值比较工具类
+ */
+public class ValueUtil {
+
+    /**
+     * 判断目标值是否在给定的多个值中
+     *
+     * @param target 目标值
+     * @param values 多个值
+     * @return 如果目标值在给定的值中返回 true,否则返回 false
+     */
+    public static <T> boolean in(T target, T... values) {
+        if (target == null || values == null || values.length == 0) {
+            return false;
+        }
+        return Arrays.asList(values).contains(target);
+    }
+
+    /**
+     * 判断目标值是否不在给定的多个值中
+     *
+     * @param target 目标值
+     * @param values 多个值
+     * @return 如果目标值不在给定的值中返回 true,否则返回 false
+     */
+    public static <T> boolean notIn(T target, T... values) {
+        return !in(target, values);
+    }
+
+    /**
+     * 判断目标值是否等于任意一个给定值(支持 null 安全比较)
+     *
+     * @param target 目标值
+     * @param values 多个值
+     * @return 如果相等返回 true,否则返回 false
+     */
+    public static <T> boolean equalsAny(T target, T... values) {
+        return in(target, values);
+    }
+
+    /**
+     * 判断目标值是否不等于所有给定值
+     *
+     * @param target 目标值
+     * @param values 多个值
+     * @return 如果不等于任何值返回 true,否则返回 false
+     */
+    public static <T> boolean notEqualsAny(T target, T... values) {
+        return notIn(target, values);
+    }
+}
+

+ 10 - 0
crm-model/src/main/resources/i18n/defaultMessages.properties

@@ -0,0 +1,10 @@
+order_expire_time=\u8BA2\u5355\u8FC7\u671F
+acceptors_busy=\u627F\u5151\u5458\u7E41\u5FD9
+merchant_not_exist=\u5546\u6237\u4E0D\u5B58\u5728
+order_status_error=\u5F53\u524D\u8BA2\u5355\u72B6\u6001\u4E0D\u5141\u8BB8\u64CD\u4F5C
+approve_status_error=\u5F53\u524D\u8BA2\u5355\u4E0D\u5728\u5BA1\u6279\u72B6\u6001
+order_been_accepted=\u8BA2\u5355\u5DF2\u88AB\u62A2\u5355
+amount_error=\u91D1\u989D\u9519\u8BEF
+balance_locked_error=\u9501\u5B9A\u91D1\u989D\u5F02\u5E38
+complaint_record_error=\u6295\u8BC9\u8BB0\u5F55\u5F02\u5E38
+settlement_type_error=\u7ED3\u7B97\u7C7B\u578B\u5F02\u5E38

+ 10 - 0
crm-model/src/main/resources/i18n/defaultMessages_en_US.properties

@@ -0,0 +1,10 @@
+order_expire_time=order expire time
+acceptors_busy=acceptor is busy
+merchant_not_exist=merchant does not exist
+order_status_error=current order status does not allow for action
+approve_status_error=current order is not in an approval state
+order_been_accepted=order has been snapped up
+amount_error=amount is wrong
+balance_locked_error=locked amount is abnormal
+complaint_record_error=complaint record is abnormal
+settlement_type_error=settlement type is abnormal

+ 10 - 0
crm-model/src/main/resources/i18n/defaultMessages_zh_CN.properties

@@ -0,0 +1,10 @@
+order_expire_time=\u8BA2\u5355\u8FC7\u671F
+acceptors_busy=\u627F\u5151\u5458\u7E41\u5FD9
+merchant_not_exist=\u5546\u6237\u4E0D\u5B58\u5728
+order_status_error=\u5F53\u524D\u8BA2\u5355\u72B6\u6001\u4E0D\u5141\u8BB8\u64CD\u4F5C
+approve_status_error=\u5F53\u524D\u8BA2\u5355\u4E0D\u5728\u5BA1\u6279\u72B6\u6001
+order_been_accepted=\u8BA2\u5355\u5DF2\u88AB\u62A2\u5355
+amount_error=\u91D1\u989D\u9519\u8BEF
+balance_locked_error=\u9501\u5B9A\u91D1\u989D\u5F02\u5E38
+complaint_record_error=\u6295\u8BC9\u8BB0\u5F55\u5F02\u5E38
+settlement_type_error=\u7ED3\u7B97\u7C7B\u578B\u5F02\u5E38

+ 1 - 0
crm-model/打包.bat

@@ -0,0 +1 @@
+mvn install

+ 190 - 0
crm-settlement/pom.xml

@@ -0,0 +1,190 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.crm.settlement</groupId>
+    <artifactId>crm-settlement</artifactId>
+    <version>1.0.0</version>
+    <packaging>jar</packaging>
+
+    <name>crm_settlement</name>
+    <description>crm settlement module</description>
+
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>3.2.12</version>
+        <relativePath/>
+    </parent>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>17</java.version>
+        <spring-cloud.version>2023.0.3</spring-cloud.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.crm.login.backend</groupId>
+            <artifactId>crm-login-backend</artifactId>
+            <version>2.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.crm.model</groupId>
+            <artifactId>crm-model</artifactId>
+            <version>3.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+        </dependency>
+
+        <!--mybatis-->
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+            <version>3.0.3</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.bitcoinj</groupId>
+            <artifactId>bitcoinj-core</artifactId>
+            <version>0.17-alpha3</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>1.55</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>18.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.12.3</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.12.3</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>0.12.3</version>
+            <scope>runtime</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.fastjson2</groupId>
+            <artifactId>fastjson2</artifactId>
+            <version>2.0.49</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <outputDirectory>${project.build.directory}/../../lib</outputDirectory>
+                    <layout>ZIP</layout>
+                    <includes>
+                        <include>
+                            <groupId>com.crm.settlement</groupId>
+                            <artifactId>crm-settlement</artifactId>
+                        </include>
+                    </includes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.11.0</version>
+                <configuration>
+                    <release>17</release>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <version>3.6.1</version>
+                <executions>
+                    <execution>
+                        <id>copy-dependencies</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.build.directory}/../../dependency</outputDirectory>
+                            <includeScope>runtime</includeScope>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <repositories>
+        <repository>
+            <id>spring-snapshots</id>
+            <name>Spring Snapshots</name>
+            <url>https://repo.spring.io/snapshot</url>
+            <snapshots>
+                <enabled>true</enabled>
+            </snapshots>
+        </repository>
+        <repository>
+            <id>spring-milestones</id>
+            <name>Spring Milestones</name>
+            <url>https://repo.spring.io/milestone</url>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
+
+    <pluginRepositories>
+        <pluginRepository>
+            <id>spring-snapshots</id>
+            <name>Spring Snapshots</name>
+            <url>https://repo.spring.io/snapshot</url>
+            <snapshots>
+                <enabled>true</enabled>
+            </snapshots>
+        </pluginRepository>
+        <pluginRepository>
+            <id>spring-milestones</id>
+            <name>Spring Milestones</name>
+            <url>https://repo.spring.io/milestone</url>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </pluginRepository>
+    </pluginRepositories>
+
+</project>

+ 20 - 0
crm-settlement/src/main/java/com/crm/settlement/SettlementApplication.java

@@ -0,0 +1,20 @@
+package com.crm.settlement;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@SpringBootApplication
+@EnableScheduling
+@MapperScan("com.crm.settlement.dao.mapper")
+@EntityScan(basePackages = {"com.crm.rely.backend.core.pojo.table", "com.crm.rely.backend.model.pojo.table"})
+@ComponentScan(basePackages = {"com.crm.settlement", "com.crm.rely.backend","com.crm.login.rely.backend.property"})
+public class SettlementApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(SettlementApplication.class, args);
+    }
+}

+ 20 - 0
crm-settlement/src/main/java/com/crm/settlement/config/SettlementHttpConfig.java

@@ -0,0 +1,20 @@
+package com.crm.settlement.config;
+
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+import java.time.Duration;
+
+@Configuration
+public class SettlementHttpConfig {
+
+    @Bean
+    public RestTemplate restTemplate(RestTemplateBuilder builder) {
+        return builder
+                .setConnectTimeout(Duration.ofSeconds(10))
+                .setReadTimeout(Duration.ofSeconds(20))
+                .build();
+    }
+}

+ 38 - 0
crm-settlement/src/main/java/com/crm/settlement/controller/SettlementController.java

@@ -0,0 +1,38 @@
+package com.crm.settlement.controller;
+
+import com.crm.rely.backend.core.dto.base.BaseResultDto;
+import com.crm.rely.backend.model.entity.settlement.SettlementDepositEntity;
+import com.crm.rely.backend.model.entity.settlement.SettlementWithdrawEntity;
+import com.crm.settlement.service.SettlementService;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/settlement")
+public class SettlementController {
+
+    @Autowired
+    private SettlementService settlementService;
+
+    @PostMapping("/validate/deposit/order")
+    public BaseResultDto validateDepositOrder(@Valid @RequestBody SettlementDepositEntity entity) throws Exception {
+        return settlementService.validateDepositOrder(entity);
+    }
+
+    @PostMapping("/validate/withdraw/order")
+    public BaseResultDto validateWithdrawOrder(@Valid @RequestBody SettlementWithdrawEntity request) throws Exception {
+        return settlementService.validateWithdrawOrder(request);
+    }
+
+    @PostMapping("/test")
+    public BaseResultDto test() throws Exception {
+        settlementService.scheduleCallback();
+        return BaseResultDto.success();
+    }
+
+
+}

+ 47 - 0
crm-settlement/src/main/java/com/crm/settlement/controller/VaultodyController.java

@@ -0,0 +1,47 @@
+package com.crm.settlement.controller;
+
+import com.crm.rely.backend.core.dto.base.BaseResultDto;
+import com.crm.rely.backend.model.entity.settlement.vaultody.VaultodyDepositRequestEntity;
+import com.crm.rely.backend.model.entity.settlement.vaultody.VaultodyWithdrawRequestEntity;
+import com.crm.settlement.service.VaultodyService;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/vaultody")
+public class VaultodyController {
+
+    @Autowired
+    private VaultodyService vaultodyService;
+
+    @PostMapping("/deposit")
+    public BaseResultDto deposit(@Valid @RequestBody VaultodyDepositRequestEntity entity) throws Exception {
+        return vaultodyService.deposit(entity);
+    }
+
+    @PostMapping("/withdraw")
+    public BaseResultDto withdraw(
+            @Valid @RequestBody VaultodyWithdrawRequestEntity request) throws Exception {
+        return vaultodyService.withdraw(request);
+    }
+
+    @PostMapping("/callback/deposit")
+    public BaseResultDto depositCallback(@RequestBody String content, @RequestHeader("x-signature") String signature) throws Exception {
+        try {
+            return vaultodyService.callback(content, signature);
+        } catch (Exception e) {
+            return BaseResultDto.error("system error");
+        }
+    }
+
+    @PostMapping("/callback/withdraw")
+    public BaseResultDto withdrawCallback(@RequestBody String content, @RequestHeader("x-signature") String signature) throws Exception {
+        try {
+            return vaultodyService.withdrawCallback(content, signature);
+        } catch (Exception e) {
+            return BaseResultDto.error("system error");
+        }
+    }
+
+}

+ 10 - 0
crm-settlement/src/main/java/com/crm/settlement/dao/mapper/FinanceDepositAddressMapper.java

@@ -0,0 +1,10 @@
+package com.crm.settlement.dao.mapper;
+
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface FinanceDepositAddressMapper {
+
+    String getTableByIndex(int index);
+
+}

+ 16 - 0
crm-settlement/src/main/java/com/crm/settlement/dao/mapper/SettlementDepositRecordMapper.java

@@ -0,0 +1,16 @@
+package com.crm.settlement.dao.mapper;
+
+import com.crm.rely.backend.model.pojo.table.SettlementDepositRecordTable;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface SettlementDepositRecordMapper {
+
+    /**
+     * 获取可回调的记录
+     */
+    List<SettlementDepositRecordTable> getAllCallback();
+
+}

+ 17 - 0
crm-settlement/src/main/java/com/crm/settlement/dao/mapper/SettlementWithdrawRecordMapper.java

@@ -0,0 +1,17 @@
+package com.crm.settlement.dao.mapper;
+
+import com.crm.rely.backend.model.pojo.table.SettlementWithdrawRecordTable;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface SettlementWithdrawRecordMapper {
+
+    /**
+     * 获取可回调的记录
+     */
+    List<SettlementWithdrawRecordTable> getAllCallback();
+
+
+}

+ 18 - 0
crm-settlement/src/main/java/com/crm/settlement/dao/repository/FinanceDepositAddressRepository.java

@@ -0,0 +1,18 @@
+package com.crm.settlement.dao.repository;
+
+
+import com.crm.rely.backend.dao.repository.BaseRepository;
+import com.crm.rely.backend.model.pojo.table.FinanceDepositAddressTable;
+import jakarta.persistence.LockModeType;
+import org.springframework.data.jpa.repository.Lock;
+import org.springframework.stereotype.Repository;
+
+
+
+@Repository
+public interface FinanceDepositAddressRepository extends BaseRepository<FinanceDepositAddressTable> {
+
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    FinanceDepositAddressTable findByAddressOrderByAddTimeDesc(String address);
+
+}

+ 19 - 0
crm-settlement/src/main/java/com/crm/settlement/dao/repository/SettlementDepositRecordRepository.java

@@ -0,0 +1,19 @@
+package com.crm.settlement.dao.repository;
+
+import com.crm.rely.backend.dao.repository.BaseRepository;
+import com.crm.rely.backend.model.pojo.table.SettlementDepositRecordTable;
+import jakarta.persistence.LockModeType;
+import org.springframework.data.jpa.repository.Lock;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface SettlementDepositRecordRepository extends BaseRepository<SettlementDepositRecordTable> {
+
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    SettlementDepositRecordTable findFirstById(Long id);
+
+    SettlementDepositRecordTable findFirstBySerialAndChannelCode(String serial, String channelCode);
+
+    SettlementDepositRecordTable findFirstByRequestSerialAndChannelCode(String requestSerial, String channelCode);
+
+}

+ 19 - 0
crm-settlement/src/main/java/com/crm/settlement/dao/repository/SettlementWithdrawRecordRepository.java

@@ -0,0 +1,19 @@
+package com.crm.settlement.dao.repository;
+
+import com.crm.rely.backend.dao.repository.BaseRepository;
+import com.crm.rely.backend.model.pojo.table.SettlementWithdrawRecordTable;
+import jakarta.persistence.LockModeType;
+import org.springframework.data.jpa.repository.Lock;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface SettlementWithdrawRecordRepository extends BaseRepository<SettlementWithdrawRecordTable> {
+
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    SettlementWithdrawRecordTable findFirstById(Long id);
+
+    SettlementWithdrawRecordTable findFirstBySerialAndChannelCode(String serial, String channelCode);
+
+    SettlementWithdrawRecordTable findFirstByRequestSerialAndChannelCode(String requestSerial, String channelCode);
+
+}

+ 30 - 0
crm-settlement/src/main/java/com/crm/settlement/dao/repository/SysConfigRepository.java

@@ -0,0 +1,30 @@
+package com.crm.settlement.dao.repository;
+
+import com.crm.rely.backend.core.pojo.table.SysConfigTable;
+import com.crm.rely.backend.dao.repository.BaseRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * @author: houn
+ */
+@Repository
+public interface SysConfigRepository extends BaseRepository<SysConfigTable> {
+
+    /**
+     * 根据codes 获取系统配置信息
+     *
+     * @param codes code的列表
+     * @return
+     */
+    List<SysConfigTable> getByCodeIn(List<String> codes);
+
+    /**
+     * 根据银行code 获取系统配置信息
+     *
+     * @param code code
+     * @return
+     */
+    SysConfigTable getByCode(String code);
+}

+ 33 - 0
crm-settlement/src/main/java/com/crm/settlement/dao/repository/SysRemitChannelRepository.java

@@ -0,0 +1,33 @@
+package com.crm.settlement.dao.repository;
+
+import com.crm.rely.backend.dao.repository.BaseRepository;
+import com.crm.rely.backend.model.pojo.table.SysRemitChannelTable;
+import jakarta.persistence.LockModeType;
+import org.springframework.data.jpa.repository.Lock;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface SysRemitChannelRepository extends BaseRepository<SysRemitChannelTable> {
+
+    List<SysRemitChannelTable> getAllByCodeIn(List<String> codes);
+
+    /**
+     * 根据code获取通道信息
+     *
+     * @param code
+     * @return
+     */
+    SysRemitChannelTable getFirstByCode(String code);
+
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    SysRemitChannelTable findFirstById(Long id);
+
+    SysRemitChannelTable getFirstById(Long id);
+
+    boolean existsByCode(String code);
+
+    List<SysRemitChannelTable> getAllByIdNotNull();
+
+}

+ 37 - 0
crm-settlement/src/main/java/com/crm/settlement/dao/repository/SysRemittanceChannelRepository.java

@@ -0,0 +1,37 @@
+package com.crm.settlement.dao.repository;
+
+import com.crm.rely.backend.dao.repository.BaseRepository;
+import com.crm.rely.backend.model.pojo.table.SysRemittanceChannelTable;
+import jakarta.persistence.LockModeType;
+import org.springframework.data.jpa.repository.Lock;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * @author: houn
+ */
+@Repository
+public interface SysRemittanceChannelRepository extends BaseRepository<SysRemittanceChannelTable> {
+
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    SysRemittanceChannelTable findFirstById(Long id);
+
+    SysRemittanceChannelTable getFirstById(Long id);
+
+    boolean existsByCode(String code);
+
+
+    List<SysRemittanceChannelTable> getAllByCodeIsNotNull();
+
+    /**
+     * 根据code获取通道信息
+     *
+     * @param code
+     * @return
+     */
+    SysRemittanceChannelTable getFirstByCode(String code);
+
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    SysRemittanceChannelTable findFirstByCode(String code);
+}

+ 16 - 0
crm-settlement/src/main/java/com/crm/settlement/dao/repository/SysSettlementConfigRepository.java

@@ -0,0 +1,16 @@
+package com.crm.settlement.dao.repository;
+
+import com.crm.rely.backend.dao.repository.BaseRepository;
+import com.crm.rely.backend.model.pojo.table.SysSettlementConfigTable;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+
+@Repository
+public interface SysSettlementConfigRepository extends BaseRepository<SysSettlementConfigTable> {
+
+    List<SysSettlementConfigTable> getByCodeIn(List<String> codes);
+
+    SysSettlementConfigTable getByCode(String code);
+}

+ 17 - 0
crm-settlement/src/main/java/com/crm/settlement/service/SettlementService.java

@@ -0,0 +1,17 @@
+package com.crm.settlement.service;
+
+import com.crm.rely.backend.core.dto.base.BaseResultDto;
+import com.crm.rely.backend.core.exception.PayValidatedException;
+import com.crm.rely.backend.core.exception.ServiceException;
+import com.crm.rely.backend.model.entity.settlement.SettlementDepositEntity;
+import com.crm.rely.backend.model.entity.settlement.SettlementWithdrawEntity;
+
+public interface SettlementService {
+
+
+    BaseResultDto validateDepositOrder(SettlementDepositEntity entity) throws PayValidatedException;
+
+    BaseResultDto validateWithdrawOrder(SettlementWithdrawEntity entity) throws PayValidatedException;
+
+    void scheduleCallback() throws ServiceException;
+}

+ 18 - 0
crm-settlement/src/main/java/com/crm/settlement/service/SysConfigService.java

@@ -0,0 +1,18 @@
+package com.crm.settlement.service;
+
+import com.crm.rely.backend.core.exception.ServiceException;
+import com.crm.rely.backend.core.pojo.table.SysConfigTable;
+
+import java.util.List;
+
+/**
+ * @author gao
+ * @date 2026/4/23
+ */
+public interface SysConfigService {
+
+    SysConfigTable getByCode(String code) throws ServiceException;
+
+    String getPropertyKey() throws ServiceException;
+
+}

+ 12 - 0
crm-settlement/src/main/java/com/crm/settlement/service/SysRemitChannelService.java

@@ -0,0 +1,12 @@
+package com.crm.settlement.service;
+
+import com.crm.rely.backend.core.exception.ServiceException;
+import com.crm.rely.backend.model.pojo.table.SysRemitChannelTable;
+
+
+public interface SysRemitChannelService {
+
+    SysRemitChannelTable getRemitChannelByCode(String code) throws ServiceException;
+
+
+}

+ 16 - 0
crm-settlement/src/main/java/com/crm/settlement/service/SysRemittanceChannelService.java

@@ -0,0 +1,16 @@
+package com.crm.settlement.service;
+
+import com.crm.rely.backend.core.exception.ServiceException;
+import com.crm.rely.backend.model.pojo.table.SysRemittanceChannelTable;
+
+public interface SysRemittanceChannelService {
+
+    /**
+     * 根据code 获取通道实体
+     *
+     * @param code
+     * @return
+     */
+    SysRemittanceChannelTable getSysRemittanceChannelByCode(String code) throws ServiceException;
+
+}

+ 10 - 0
crm-settlement/src/main/java/com/crm/settlement/service/SysSettlementConfigService.java

@@ -0,0 +1,10 @@
+package com.crm.settlement.service;
+
+import com.crm.rely.backend.core.exception.ServiceException;
+import com.crm.rely.backend.model.pojo.table.SysSettlementConfigTable;
+
+public interface SysSettlementConfigService {
+
+    SysSettlementConfigTable getByCode(String code) throws ServiceException;
+
+}

+ 18 - 0
crm-settlement/src/main/java/com/crm/settlement/service/VaultodyService.java

@@ -0,0 +1,18 @@
+package com.crm.settlement.service;
+
+import com.crm.rely.backend.core.dto.base.BaseResultDto;
+import com.crm.rely.backend.model.entity.settlement.vaultody.VaultodyDepositRequestEntity;
+import com.crm.rely.backend.model.entity.settlement.vaultody.VaultodyWithdrawRequestEntity;
+
+public interface VaultodyService {
+
+    BaseResultDto deposit(VaultodyDepositRequestEntity request) throws Exception;
+
+    BaseResultDto callback(String content, String signature) throws Exception;
+
+    BaseResultDto withdraw(VaultodyWithdrawRequestEntity request) throws Exception;
+
+    BaseResultDto withdrawCallback(String content, String signature) throws Exception;
+
+
+}

+ 396 - 0
crm-settlement/src/main/java/com/crm/settlement/service/impl/SettlementServiceImpl.java

@@ -0,0 +1,396 @@
+package com.crm.settlement.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.TypeReference;
+import com.crm.rely.backend.core.dto.base.BaseResultDto;
+import com.crm.rely.backend.core.exception.PayValidatedException;
+import com.crm.rely.backend.core.exception.ServiceException;
+import com.crm.rely.backend.service.MqSendService;
+import com.crm.rely.backend.util.DateUtil;
+import com.crm.rely.backend.util.HttpUtil;
+import com.crm.rely.backend.util.MapUtil;
+import com.crm.rely.backend.model.constant.ConfigConstants;
+import com.crm.rely.backend.model.entity.settlement.SettlementDepositEntity;
+import com.crm.rely.backend.model.entity.settlement.SettlementWithdrawEntity;
+import com.crm.rely.backend.model.pojo.table.SettlementDepositRecordTable;
+import com.crm.rely.backend.model.pojo.table.SettlementWithdrawRecordTable;
+import com.crm.settlement.dao.mapper.SettlementDepositRecordMapper;
+import com.crm.settlement.dao.mapper.SettlementWithdrawRecordMapper;
+import com.crm.settlement.dao.repository.SettlementDepositRecordRepository;
+import com.crm.settlement.dao.repository.SettlementWithdrawRecordRepository;
+import com.crm.settlement.service.SettlementService;
+import com.crm.settlement.service.impl.base.BaseSettlementServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Connection;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jms.annotation.JmsListener;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Service
+public class SettlementServiceImpl extends BaseSettlementServiceImpl implements SettlementService {
+
+    @Autowired
+    private SettlementDepositRecordRepository settlementDepositRecordRepository;
+    @Autowired
+    private SettlementWithdrawRecordRepository settlementWithdrawRecordRepository;
+    @Autowired
+    private SettlementDepositRecordMapper settlementDepositRecordMapper;
+    @Autowired
+    private SettlementWithdrawRecordMapper settlementWithdrawRecordMapper;
+    @Autowired
+    private MqSendService mqSendService;
+
+
+    /**
+     * 验证入金订单
+     */
+    @Override
+    public BaseResultDto validateDepositOrder(SettlementDepositEntity entity) {
+        try {
+            //验证时间
+            long time = DateUtil.getTimestampByZero() - entity.getRequestTime().getTime();
+            if ((time > (10 * 1000)) || (time < 0)) {
+                log.error("time out ,time:" + time);
+                throw ServiceException.exception("Time out");
+            }
+
+            verifySign(entity.getSign(), getDepositSignString(entity), entity.getMerchantId());
+            SettlementDepositRecordTable recordTable = settlementDepositRecordRepository.findFirstBySerialAndChannelCode(entity.getSerial(), entity.getChannelCode());
+            if (recordTable == null) {
+                return BaseResultDto.error();
+            }
+            if (recordTable.getCallbackAmount().compareTo(entity.getCallbackAmount()) != 0
+                    || !recordTable.getCallbackCurrency().equals(entity.getCallbackCurrency())
+                    || !recordTable.getCallbackSerial().equals(entity.getCallbackSerial())
+                    || !recordTable.getStatus().equals(entity.getStatus())){
+                return BaseResultDto.error();
+            }
+        } catch (Exception e){
+            return BaseResultDto.error();
+        }
+        return BaseResultDto.success();
+    }
+
+    /**
+     * 验证出金订单
+     */
+    @Override
+    public BaseResultDto validateWithdrawOrder(SettlementWithdrawEntity entity) throws PayValidatedException {
+        try {
+            //验证时间
+            long time = DateUtil.getTimestampByZero() - entity.getRequestTime().getTime();
+            if ((time > (10 * 1000)) || (time < 0)) {
+                log.error("time out ,time:" + time);
+                throw ServiceException.exception("Time out");
+            }
+
+            verifySign(entity.getSign(), getWithdrawSignString(entity), entity.getMerchantId());
+            SettlementWithdrawRecordTable recordTable = settlementWithdrawRecordRepository.findFirstBySerialAndChannelCode(entity.getSerial(), entity.getChannelCode());
+            if (recordTable == null) {
+                return BaseResultDto.error();
+            }
+            if (recordTable.getCallbackAmount().compareTo(entity.getCallbackAmount()) != 0
+                    || !recordTable.getCallbackCurrency().equals(entity.getCallbackCurrency())
+                    || !recordTable.getCallbackSerial().equals(entity.getCallbackSerial())
+                    || !recordTable.getStatus().equals(entity.getStatus())){
+                return BaseResultDto.error();
+            }
+        } catch (Exception e){
+            return BaseResultDto.error();
+        }
+        return BaseResultDto.success();
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @JmsListener(destination = ConfigConstants.DEPOSIT_CALLBACK)
+    public void depositCallback(String content) throws Exception {
+        log.info("DEPOSIT_CALLBACK: content:" + content);
+        SettlementDepositRecordTable recordTable = JSON.parseObject(content, SettlementDepositRecordTable.class);
+
+        SettlementDepositRecordTable table = settlementDepositRecordRepository.findFirstById(recordTable.getId());
+
+        if (table == null){
+            log.error("DEPOSIT_CALLBACK SettlementDepositRecordTable is null");
+            return;
+        }
+        // 判断状态
+        if (table.getCallbackMerchantStatus().equals(1)){
+            log.info("DEPOSIT_CALLBACK: callbackMerchantStatus is Success ");
+            return;
+        }
+        if (table.getCallbackMerchantCount() >= table.getCallbackMerchantMaxRetryCount()){
+            log.info("DEPOSIT_CALLBACK: callbackMerchantCount is max retry count");
+            return;
+        }
+
+        String callbackUrl = table.getCallbackUrl();
+        Map<String, String> params = new HashMap<>();
+        params.put("serial", table.getSerial());
+        params.put("channelCode", table.getChannelCode());
+        params.put("callbackAmount", table.getCallbackAmount().stripTrailingZeros().toPlainString());
+        params.put("callbackCurrency", table.getCallbackCurrency());
+        params.put("callbackSerial", table.getCallbackSerial());
+        params.put("status", String.valueOf(table.getStatus()));
+        String callbackMerchantBody = JSON.toJSONString(params);
+        String sign = getSign(params, table.getMerchantId());
+        params.put("sign", sign);
+
+        String callbackContent = JSON.toJSONString(params);
+        try {
+            Connection.Response response = HttpUtil.post(callbackUrl, callbackContent);
+            //打印请求日志
+            log.info("DEPOSIT_CALLBACK Request URL: {},data:{},response body:{}", callbackUrl, callbackContent, response.body());
+            BaseResultDto requestEntity = JSON.parseObject(response.body(), BaseResultDto.class);
+            if (requestEntity == null || requestEntity.getCode() != 200) {
+                table.setCallbackMerchantStatus(2);
+            } else {
+                table.setCallbackMerchantStatus(1);
+            }
+        } catch (Exception e) {
+            log.error("DEPOSIT_CALLBACK, Request URL: {}, data:{}", callbackUrl, callbackContent, e);
+            table.setCallbackMerchantStatus(2);
+        }
+
+        table.setCallbackMerchantBody(callbackMerchantBody);
+        table.setCallbackMerchantCount(table.getCallbackMerchantCount() + 1);
+        table.setCallbackMerchantTime(new Date());
+        settlementDepositRecordRepository.save(table);
+        if (table.getCallbackMerchantStatus().equals(2) && table.getCallbackMerchantCount() < table.getCallbackMerchantMaxRetryCount()){
+            mqSendService.send(ConfigConstants.DEPOSIT_RETRY_CALLBACK, table , 3 * 60 * 1000);
+        }
+    }
+
+
+    @Transactional(rollbackFor = Exception.class)
+    @JmsListener(destination = ConfigConstants.WITHDRAW_CALLBACK)
+    public void withdrawCallback(String content) throws Exception {
+        log.info("WITHDRAW_CALLBACK: content:" + content);
+        SettlementWithdrawRecordTable withdrawRecordTable = JSON.parseObject(content, SettlementWithdrawRecordTable.class);
+
+        SettlementWithdrawRecordTable table = settlementWithdrawRecordRepository.findFirstById(withdrawRecordTable.getId());
+
+        if (table == null){
+            log.error("WITHDRAW_CALLBACK SettlementDepositRecordTable is null");
+            return;
+        }
+        // 判断状态
+        if (table.getCallbackMerchantStatus().equals(1)){
+            log.info("WITHDRAW_CALLBACK: callbackMerchantStatus is Success ");
+            return;
+        }
+        if (table.getCallbackMerchantCount() >= table.getCallbackMerchantMaxRetryCount()){
+            log.info("WITHDRAW_CALLBACK: callbackMerchantCount is max retry count");
+            return;
+        }
+
+        String callbackUrl = table.getCallbackUrl();
+        Map<String, String> params = new HashMap<>();
+        params.put("serial", table.getSerial());
+        params.put("channelCode", table.getChannelCode());
+        params.put("callbackAmount", table.getCallbackAmount().stripTrailingZeros().toPlainString());
+        params.put("callbackCurrency", table.getCallbackCurrency());
+        params.put("callbackSerial", table.getCallbackSerial());
+        params.put("status", String.valueOf(table.getStatus()));
+        String callbackMerchantBody = JSON.toJSONString(params);
+        String sign = getSign(params, table.getMerchantId());
+        params.put("sign", sign);
+
+        String callbackContent = JSON.toJSONString(params);
+        try {
+            Connection.Response response = HttpUtil.post(callbackUrl, callbackContent);
+            //打印请求日志
+            log.info("WITHDRAW_CALLBACK Request URL: {},data:{},response body:{}", callbackUrl, callbackContent, response.body());
+            BaseResultDto requestEntity = JSON.parseObject(response.body(), BaseResultDto.class);
+            if (requestEntity == null || requestEntity.getCode() != 200) {
+                table.setCallbackMerchantStatus(2);
+            } else {
+                table.setCallbackMerchantStatus(1);
+            }
+        } catch (Exception e) {
+            log.error("WITHDRAW_CALLBACK, Request URL: {}, data:{}", callbackUrl, callbackContent, e);
+            table.setCallbackMerchantStatus(2);
+        }
+
+        table.setCallbackMerchantBody(callbackMerchantBody);
+        table.setCallbackMerchantCount(table.getCallbackMerchantCount() + 1);
+        table.setCallbackMerchantTime(new Date());
+        settlementWithdrawRecordRepository.save(table);
+        if (table.getCallbackMerchantStatus().equals(2) && table.getCallbackMerchantCount() < table.getCallbackMerchantMaxRetryCount()){
+            mqSendService.send(ConfigConstants.WITHDRAW_RETRY_CALLBACK, table , 3 * 60 * 1000);
+        }
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @JmsListener(destination = ConfigConstants.DEPOSIT_RETRY_CALLBACK)
+    public void depositRetryCallback(String content) throws Exception {
+        log.info("DEPOSIT_RETRY_CALLBACK: content:" + content);
+        SettlementDepositRecordTable table = JSON.parseObject(content, SettlementDepositRecordTable.class);
+
+        if (table == null){
+            log.error("DEPOSIT_RETRY_CALLBACK SettlementDepositRecordTable is null");
+            return;
+        }
+        // 判断状态
+        if (table.getCallbackMerchantStatus().equals(1)){
+            log.info("DEPOSIT_RETRY_CALLBACK: callbackMerchantStatus is Success , serial:{}", table.getSerial());
+            return;
+        }
+        if (table.getCallbackMerchantCount() >= table.getCallbackMerchantMaxRetryCount()){
+            log.info("DEPOSIT_RETRY_CALLBACK: callbackMerchantCount is max retry count, serial:{}", table.getSerial());
+            return;
+        }
+
+        String callbackUrl = table.getCallbackUrl();
+        String body = table.getCallbackMerchantBody();
+
+        if (StringUtils.isEmpty(callbackUrl) || StringUtils.isEmpty(body)){
+            log.error("DEPOSIT_RETRY_CALLBACK: callbackUrl or callbackMerchantBody is null , serial:{}", table.getSerial());
+            return;
+        }
+
+        Map<String, String> params = JSON.parseObject(body, new TypeReference<Map<String, String>>(){});
+        String sign = getSign(params, table.getMerchantId());
+        params.put("sign", sign);
+
+        String callbackContent = JSON.toJSONString(params);
+        try {
+            Connection.Response response = HttpUtil.post(callbackUrl, callbackContent);
+            //打印请求日志
+            log.info("DEPOSIT_RETRY_CALLBACK Request URL: {},data:{},response body:{}", callbackUrl, callbackContent, response.body());
+            BaseResultDto requestEntity = JSON.parseObject(response.body(), BaseResultDto.class);
+            if (requestEntity == null || requestEntity.getCode() != 200) {
+                table.setCallbackMerchantStatus(2);
+            } else {
+                table.setCallbackMerchantStatus(1);
+            }
+        } catch (Exception e) {
+            log.error("DEPOSIT_RETRY_CALLBACK, Request URL: {}, data:{}", callbackUrl, callbackContent, e);
+            table.setCallbackMerchantStatus(2);
+        }
+
+        table.setCallbackMerchantCount(table.getCallbackMerchantCount() + 1);
+        settlementDepositRecordRepository.save(table);
+        if (table.getCallbackMerchantStatus().equals(2) && table.getCallbackMerchantCount() < table.getCallbackMerchantMaxRetryCount()){
+            mqSendService.send(ConfigConstants.DEPOSIT_RETRY_CALLBACK, table , 3 * 60 * 1000);
+        }
+    }
+
+
+    @Transactional(rollbackFor = Exception.class)
+    @JmsListener(destination = ConfigConstants.WITHDRAW_RETRY_CALLBACK)
+    public void withdrawRetryCallback(String content) throws Exception {
+        log.info("WITHDRAW_RETRY_CALLBACK: content:" + content);
+        SettlementWithdrawRecordTable table = JSON.parseObject(content, SettlementWithdrawRecordTable.class);
+
+        if (table == null){
+            log.error("WITHDRAW_RETRY_CALLBACK SettlementDepositRecordTable is null");
+            return;
+        }
+        // 判断状态
+        if (table.getCallbackMerchantStatus().equals(1)){
+            log.info("WITHDRAW_RETRY_CALLBACK: callbackMerchantStatus is Success ");
+            return;
+        }
+        if (table.getCallbackMerchantCount() >= table.getCallbackMerchantMaxRetryCount()){
+            log.info("WITHDRAW_RETRY_CALLBACK: callbackMerchantCount is max retry count");
+            return;
+        }
+
+        String callbackUrl = table.getCallbackUrl();
+        String body = table.getCallbackMerchantBody();
+
+        if (StringUtils.isEmpty(callbackUrl) || StringUtils.isEmpty(body)){
+            log.error("WITHDRAW_RETRY_CALLBACK: callbackUrl or callbackMerchantBody is null , serial:{}", table.getSerial());
+            return;
+        }
+
+        Map<String, String> params = JSON.parseObject(body, new TypeReference<Map<String, String>>(){});
+        String sign = getSign(params, table.getMerchantId());
+        params.put("sign", sign);
+
+        String callbackContent = JSON.toJSONString(params);
+        try {
+            Connection.Response response = HttpUtil.post(callbackUrl, callbackContent);
+            //打印请求日志
+            log.info("WITHDRAW_CALLBACK Request URL: {},data:{},response body:{}", callbackUrl, callbackContent, response.body());
+            BaseResultDto requestEntity = JSON.parseObject(response.body(), BaseResultDto.class);
+            if (requestEntity == null || requestEntity.getCode() != 200) {
+                table.setCallbackMerchantStatus(2);
+            } else {
+                table.setCallbackMerchantStatus(1);
+            }
+        } catch (Exception e) {
+            log.error("WITHDRAW_CALLBACK, Request URL: {}, data:{}", callbackUrl, callbackContent, e);
+            table.setCallbackMerchantStatus(2);
+        }
+
+        table.setCallbackMerchantCount(table.getCallbackMerchantCount() + 1);
+        settlementWithdrawRecordRepository.save(table);
+        if (table.getCallbackMerchantStatus().equals(2) && table.getCallbackMerchantCount() < table.getCallbackMerchantMaxRetryCount()){
+            mqSendService.send(ConfigConstants.WITHDRAW_RETRY_CALLBACK, table , 3 * 60 * 1000);
+        }
+    }
+
+    /**
+     * 订单通知失败重新发送通知
+     * 查询入金和出金 回调失败的 未达到最大回调次数的 超过一天的数据 分别发送回调
+     */
+    @Scheduled(cron = "0 23 0 * * ?")
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void scheduleCallback() throws ServiceException {
+        log.info("scheduleCallback start");
+        List<SettlementDepositRecordTable> depositRecordTables = settlementDepositRecordMapper.getAllCallback();
+        List<SettlementWithdrawRecordTable> withdrawRecordTables = settlementWithdrawRecordMapper.getAllCallback();
+        if (!CollectionUtils.isEmpty(depositRecordTables)){
+            log.info("scheduleCallback: depositRecordTables size:{}", depositRecordTables.size());
+            for (SettlementDepositRecordTable table : depositRecordTables){
+                mqSendService.send(ConfigConstants.DEPOSIT_RETRY_CALLBACK, table);
+            }
+        }
+        if (!CollectionUtils.isEmpty(withdrawRecordTables)){
+            log.info("scheduleCallback: withdrawRecordTables size:{}", withdrawRecordTables.size());
+            for (SettlementWithdrawRecordTable table : withdrawRecordTables){
+                mqSendService.send(ConfigConstants.WITHDRAW_RETRY_CALLBACK, table);
+            }
+        }
+        log.info("scheduleCallback end");
+    }
+
+    private String getDepositSignString(SettlementDepositEntity entity) {
+        Map<String, String> map = new HashMap<>();
+        map.put("merchantId", entity.getMerchantId());
+        map.put("channelCode", entity.getChannelCode());
+        map.put("serial", entity.getSerial());
+        map.put("callbackAmount", entity.getCallbackAmount().stripTrailingZeros().toPlainString());
+        map.put("callbackCurrency", entity.getCallbackCurrency());
+        map.put("callbackSerial", entity.getCallbackSerial());
+        map.put("status", String.valueOf(entity.getStatus()));
+        map.put("requestTime", DateUtil.formatTime(entity.getRequestTime()));
+        return MapUtil.getStringSortByKey(map);
+    }
+
+    private String getWithdrawSignString(SettlementWithdrawEntity entity) {
+        Map<String, String> map = new HashMap<>();
+        map.put("merchantId", entity.getMerchantId());
+        map.put("channelCode", entity.getChannelCode());
+        map.put("serial", entity.getSerial());
+        map.put("callbackAmount", entity.getCallbackAmount().stripTrailingZeros().toPlainString());
+        map.put("callbackCurrency", entity.getCallbackCurrency());
+        map.put("callbackSerial", entity.getCallbackSerial());
+        map.put("status", String.valueOf(entity.getStatus()));
+        map.put("requestTime", DateUtil.formatTime(entity.getRequestTime()));
+        return MapUtil.getStringSortByKey(map);
+    }
+
+}

+ 51 - 0
crm-settlement/src/main/java/com/crm/settlement/service/impl/SysConfigServiceImpl.java

@@ -0,0 +1,51 @@
+package com.crm.settlement.service.impl;
+
+import com.crm.rely.backend.core.constant.Constants;
+import com.crm.rely.backend.core.exception.ServiceException;
+import com.crm.rely.backend.core.pojo.table.SysConfigTable;
+import com.crm.rely.backend.model.constant.ConfigConstants;
+import com.crm.rely.backend.service.RedisService;
+import com.crm.settlement.dao.repository.SysConfigRepository;
+import com.crm.settlement.service.SysConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class SysConfigServiceImpl implements SysConfigService {
+
+    @Autowired
+    private SysConfigRepository configRepository;
+
+    @Autowired
+    private RedisService redisService;
+
+    @Override
+    public SysConfigTable getByCode(String code) throws ServiceException {
+        SysConfigTable sysConfigTable = redisService.getEntity(code, SysConfigTable.class);
+
+        if (sysConfigTable == null) {
+            sysConfigTable = configRepository.getByCode(code);
+            if (sysConfigTable == null) {
+                throw ServiceException.exception(Constants.INFO_NOT_FOUND);
+            } else {
+                redisService.save(sysConfigTable.getCode(), sysConfigTable);
+            }
+
+        }
+        return sysConfigTable;
+    }
+
+    @Override
+    public String getPropertyKey() throws ServiceException {
+        SysConfigTable table = getByCode(ConfigConstants.FINANCE_PROPERTY_KEY);
+        if (table == null) {
+            throw ServiceException.exception(Constants.SYSTEM_ERROR);
+        }
+        return table.getValue();
+    }
+
+}

+ 24 - 0
crm-settlement/src/main/java/com/crm/settlement/service/impl/SysRemitChannelServiceImpl.java

@@ -0,0 +1,24 @@
+package com.crm.settlement.service.impl;
+
+import com.crm.rely.backend.core.exception.ServiceException;
+import com.crm.rely.backend.model.pojo.table.SysRemitChannelTable;
+import com.crm.settlement.dao.repository.SysRemitChannelRepository;
+import com.crm.settlement.service.SysRemitChannelService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+public class SysRemitChannelServiceImpl implements SysRemitChannelService {
+
+    @Autowired
+    private SysRemitChannelRepository sysRemitChannelRepository;
+
+    @Override
+    public SysRemitChannelTable getRemitChannelByCode(String code) throws ServiceException {
+        SysRemitChannelTable sysRemitChannelTable = sysRemitChannelRepository.getFirstByCode(code);
+        return sysRemitChannelTable;
+    }
+
+}

+ 29 - 0
crm-settlement/src/main/java/com/crm/settlement/service/impl/SysRemittanceChannelServiceImpl.java

@@ -0,0 +1,29 @@
+package com.crm.settlement.service.impl;
+
+import com.crm.rely.backend.core.exception.ServiceException;
+import com.crm.rely.backend.model.pojo.table.SysRemittanceChannelTable;
+import com.crm.settlement.dao.repository.SysRemittanceChannelRepository;
+import com.crm.settlement.service.SysRemittanceChannelService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SysRemittanceChannelServiceImpl implements SysRemittanceChannelService {
+
+    @Autowired
+    private SysRemittanceChannelRepository sysRemittanceChannelRepository;
+
+    /**
+     * 获取通道配置实体
+     *
+     * @param code  获取的是否是开启的通道
+     * @return
+     * @throws ServiceException
+     */
+    @Override
+    public SysRemittanceChannelTable getSysRemittanceChannelByCode(String code) throws ServiceException {
+        SysRemittanceChannelTable sysRemittanceChancelTable = sysRemittanceChannelRepository.getFirstByCode(code);
+        return sysRemittanceChancelTable;
+    }
+
+}

+ 52 - 0
crm-settlement/src/main/java/com/crm/settlement/service/impl/SysSettlementConfigServiceImpl.java

@@ -0,0 +1,52 @@
+package com.crm.settlement.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.crm.rely.backend.core.constant.Constants;
+import com.crm.rely.backend.core.exception.ServiceException;
+import com.crm.rely.backend.model.pojo.table.SysSettlementConfigTable;
+import com.crm.rely.backend.service.RedisService;
+import com.crm.rely.backend.util.AESUtil;
+import com.crm.settlement.dao.repository.SysSettlementConfigRepository;
+import com.crm.settlement.service.SysConfigService;
+import com.crm.settlement.service.SysSettlementConfigService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SysSettlementConfigServiceImpl implements SysSettlementConfigService {
+
+    @Autowired
+    private SysSettlementConfigRepository sysSettlementConfigRepository;
+    @Autowired
+    private RedisService redisService;
+    @Autowired
+    private SysConfigService sysConfigService;
+
+    @Override
+    public SysSettlementConfigTable getByCode(String code){
+
+        SysSettlementConfigTable cacheEntity = redisService.getEntity(code, SysSettlementConfigTable.class);
+
+        SysSettlementConfigTable configTable;
+        if (cacheEntity == null || StringUtils.isEmpty(cacheEntity.getValue())) {
+            configTable = sysSettlementConfigRepository.getByCode(code);
+            if (configTable == null) {
+                throw ServiceException.exception(Constants.INFO_NOT_FOUND);
+            } else {
+                redisService.save(code, configTable, 24 * 60 * 60 * 1000);
+            }
+        } else {
+            configTable = cacheEntity;
+        }
+
+        SysSettlementConfigTable resultTable = JSON.parseObject(JSON.toJSONString(configTable), SysSettlementConfigTable.class);
+        String aesKey = sysConfigService.getPropertyKey();
+
+        String property = AESUtil.decrypt(configTable.getValue(), aesKey);
+        resultTable.setValue(property);
+        return resultTable;
+    }
+
+
+}

+ 611 - 0
crm-settlement/src/main/java/com/crm/settlement/service/impl/VaultodyServiceImpl.java

@@ -0,0 +1,611 @@
+package com.crm.settlement.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.crm.rely.backend.core.constant.Constants;
+import com.crm.rely.backend.core.dto.base.BaseResultDto;
+import com.crm.rely.backend.core.exception.PayValidatedException;
+import com.crm.rely.backend.core.exception.ServiceException;
+import com.crm.rely.backend.core.pojo.table.SysConfigTable;
+import com.crm.rely.backend.model.entity.settlement.vaultody.callback.*;
+import com.crm.rely.backend.model.pojo.table.*;
+import com.crm.rely.backend.service.MqSendService;
+import com.crm.rely.backend.service.RedisService;
+import com.crm.rely.backend.util.HAMCSHA256Util;
+import com.crm.rely.backend.util.HttpUtil;
+import com.crm.rely.backend.util.MapUtil;
+import com.crm.rely.backend.model.constant.ConfigConstants;
+import com.crm.rely.backend.model.constant.SettlementConstant;
+import com.crm.rely.backend.model.dto.vaultody.TxResult;
+
+import com.crm.rely.backend.model.entity.property.vaultody.VaultodyPayPropertyEntity;
+import com.crm.rely.backend.model.entity.property.vaultody.VaultodyPayWithdrawAddressPropertyEntity;
+import com.crm.rely.backend.model.entity.property.vaultody.VaultodyPayWithdrawPropertyEntity;
+import com.crm.rely.backend.model.entity.settlement.vaultody.VaultodyDepositRequestEntity;
+import com.crm.rely.backend.model.entity.settlement.vaultody.VaultodyWithdrawRequestEntity;
+import com.crm.rely.backend.model.util.EncryptionHashQueryUtil;
+import com.crm.settlement.dao.mapper.FinanceDepositAddressMapper;
+import com.crm.settlement.dao.repository.FinanceDepositAddressRepository;
+import com.crm.settlement.dao.repository.SettlementDepositRecordRepository;
+import com.crm.settlement.dao.repository.SettlementWithdrawRecordRepository;
+import com.crm.settlement.dao.repository.SysConfigRepository;
+import com.crm.settlement.service.*;
+import com.crm.settlement.service.impl.base.BaseSettlementServiceImpl;
+import com.google.common.base.Strings;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Connection;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+@Slf4j
+@Service
+public class VaultodyServiceImpl extends BaseSettlementServiceImpl implements VaultodyService {
+
+    @Autowired
+    private FinanceDepositAddressMapper depositAddressMapper;
+    @Autowired
+    private FinanceDepositAddressRepository depositAddressRepository;
+    @Autowired
+    private RedisService redisService;
+    @Autowired
+    private SysConfigService sysConfigService;
+    @Autowired
+    private SysConfigRepository sysConfigRepository;
+    @Autowired
+    private SettlementDepositRecordRepository settlementDepositRecordRepository;
+    @Autowired
+    private SettlementWithdrawRecordRepository settlementWithdrawRecordRepository;
+    @Autowired
+    private MqSendService mqSendService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public BaseResultDto deposit(VaultodyDepositRequestEntity request) throws Exception {
+        verifyRequestTimeAndAmount(request);
+        String signString = getSignString(request);
+        verifySign(request.getSign(), signString, request.getMerchantId());
+
+        SysRemittanceChannelTable channelTable = getPayChannel(SettlementConstant.VAULTODY_PAY_KEY);
+        VaultodyPayPropertyEntity property = getPayProperty(channelTable, VaultodyPayPropertyEntity.class);
+        String address = reallocationAddress(request, property);
+        // 保存记录
+        saveDepositRecord(request, channelTable.getProperty(), address);
+        return BaseResultDto.success(address);
+    }
+
+    /**
+     * 回调处理数据
+     *
+     * @param
+     * @throws ServiceException
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public BaseResultDto callback(String content, String signature) throws Exception {
+        log.info("Vaultody回调通知开始:{},signature:{}", content, signature);
+
+        if (Strings.isNullOrEmpty(content) || Strings.isNullOrEmpty(signature)) {
+            log.error("Vaultody回调通知处理失败,数据为空,content:{},signature:{}", content, signature);
+            return BaseResultDto.error();
+        }
+        VaultodyPayPropertyEntity propertyEntity = getPayProperty(SettlementConstant.VAULTODY_PAY_KEY, VaultodyPayPropertyEntity.class);
+        if (!isCallbackValid(content, propertyEntity.getWebhooksPassphrase(), signature)) {
+            //打印日志 详细说明
+            log.error("callback valid error");
+            throw new ServiceException("callback valid error");
+        }
+        VaultodyCallbackEntity entity = JSON.parseObject(content, VaultodyCallbackEntity.class);
+        VaultodyDataEntity dataEntity = entity.getData();
+        String event = dataEntity.getEvent();
+        VaultodyItemEntity itemEntity = dataEntity.getItem();
+        VaultodyTokenEntity tokenEntity = itemEntity.getToken();
+        String transactionId = itemEntity.getTransactionId();
+        VaultodyMinedInBlockEntity blockEntity = itemEntity.getMinedInBlock();
+        if (blockEntity == null || StringUtils.isEmpty(blockEntity.getHash())) {
+            log.error("hash is empty,{}", JSON.toJSONString(blockEntity));
+            throw new ServiceException("hash is empty");
+        }
+        String address = itemEntity.getAddress();
+        if (StringUtils.isEmpty(address)) {
+            log.error("Vaultody通知回调:address为空,{}", content);
+            return BaseResultDto.error();
+        }
+//        String direction = itemEntity.getDirection();
+        // 只处理入账
+//        if (!"INCOMING_CONFIRMED_TOKEN_TX".equals(event) && !"incoming".equals(direction)) {
+//            log.error("Vaultody通知回调:不是入账类型,{}", content);
+//            return BaseResultDto.error();
+//        }
+
+        if (StringUtils.isEmpty(event)) {
+            log.error("Vaultody通知回调:event为空,暂不处理,{}", JSON.toJSONString(entity));
+            return BaseResultDto.error();
+        }
+        if (!"INCOMING_CONFIRMED_TOKEN_TX".equals(event)) {
+            log.error("Vaultody通知回调:不是入账类型,{}", content);
+            return BaseResultDto.error();
+        }
+
+        FinanceDepositAddressTable addressTable = depositAddressRepository.findByAddressOrderByAddTimeDesc(address);
+        if (addressTable == null || StringUtils.isEmpty(addressTable.getSerial())) {
+            log.error("Vaultody通知回调:addressTable为空,{}", content);
+            return BaseResultDto.error();
+        }
+        SettlementDepositRecordTable recordTable = settlementDepositRecordRepository.findFirstByRequestSerialAndChannelCode(address, SettlementConstant.VAULTODY_PAY_KEY);
+
+        if (recordTable == null || recordTable.getId() == null) {
+            log.error("Vaultody通知回调:未找到对应的请求记录,{}", content);
+            return BaseResultDto.error();
+        }
+
+        String currency = recordTable.getCurrency();
+
+        TxResult txResult;
+        try {
+
+            txResult = EncryptionHashQueryUtil.query(itemEntity.getTransactionId(), itemEntity.getBlockchain(),
+                    currency);
+        } catch (Exception e) {
+            log.error("Vaultody通知回调:查询交易hash结果失败,{}", JSON.toJSONString(entity));
+            return BaseResultDto.error();
+        }
+
+        if (txResult == null || StringUtils.isEmpty(txResult.getTo()) || StringUtils.isEmpty(itemEntity.getAddress())) {
+            log.error("Vaultody通知回调:通过hash:{} 未查询到结果,{}", blockEntity.getHash(), JSON.toJSONString(entity));
+            return BaseResultDto.error();
+        }
+        if (!txResult.getTo().equals(itemEntity.getAddress())) {
+            log.error("Vaultody通知回调:通过hash:{} 比对地址不相同,{}", txResult.getTo(), itemEntity.getAddress());
+            return BaseResultDto.error();
+        }
+        if (!txResult.getStatus().equals(itemEntity.getStatus())) {
+            log.error("Vaultody通知回调:通过hash:{} 比对状态不相同,{}", txResult.getStatus(), itemEntity.getStatus());
+            return BaseResultDto.error();
+        }
+        if (!txResult.getTimestamp().equals(blockEntity.getTimestamp())) {
+            log.error("Vaultody通知回调:通过hash:{} 比对交易时间不相同,{}", txResult.getTimestamp(), blockEntity.getTimestamp());
+            return BaseResultDto.error();
+        }
+        if (txResult.getAmount().compareTo(new BigDecimal(tokenEntity.getTokensAmount())) != 0) {
+            log.error("Vaultody通知回调:通过hash:{} 比对金额不相同,{}", txResult.getAmount(), itemEntity.getAmount());
+            return BaseResultDto.error();
+        }
+
+        addressTable.setContent(txResult.getContent());
+        addressTable.setIsUsed(false);
+        depositAddressRepository.save(addressTable);
+
+        int status = ConfigConstants.CALLBACE_PROCESSING_STATUS;
+        if ("success".equals(txResult.getStatus())) {
+            status = ConfigConstants.CALLBACE_SUCCESS_STATUS;
+        } else if ("failed".equals(txResult.getStatus())) {
+            status = ConfigConstants.CALLBACE_FAIL_STATUS;
+        }
+
+        recordTable.setCallbackAmount(new BigDecimal(tokenEntity.getTokensAmount()));
+        recordTable.setCallbackCurrency(itemEntity.getBlockchain());
+        recordTable.setStatus(status);
+        recordTable.setCallbackTime(new Date());
+        recordTable.setCallbackStatus(true);
+        recordTable.setCallbackSerial(transactionId);
+        recordTable.setCallbackData(content);
+        settlementDepositRecordRepository.save(recordTable);
+
+        if (recordTable.getCallbackAmount() == null || recordTable.getCallbackAmount().compareTo(recordTable.getAmount()) < 0) {
+            log.error("Vaultody 通知回调:回调金额:{} 小于订单金额,{}", recordTable.getCallbackAmount(), recordTable.getAmount());
+            return BaseResultDto.error();
+        }
+
+        mqSendService.send(ConfigConstants.DEPOSIT_CALLBACK, recordTable, 3 * 1000);
+
+        return BaseResultDto.success();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public BaseResultDto withdraw(VaultodyWithdrawRequestEntity request) throws Exception {
+        verifyRequestTimeAndAmount(request);
+        String signString = getWithdrawSignString(request);
+        verifySign(request.getSign(), signString, request.getMerchantId());
+
+        SysRemitChannelTable withdrawChannel = getWithdrawChannel(request.getChannelCode());
+        VaultodyPayWithdrawPropertyEntity property = getWithdrawPayProperty(withdrawChannel, VaultodyPayWithdrawPropertyEntity.class);
+        String requestId = createWithdraw(request, property);
+        saveWithdrawRecord(request, withdrawChannel.getProperty(), requestId);
+        return BaseResultDto.success(requestId);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public BaseResultDto withdrawCallback(String content, String signature) throws Exception {
+        log.info("vaultody withdraw callback:{},signature:{}", content, signature);
+
+        if (Strings.isNullOrEmpty(content) || Strings.isNullOrEmpty(signature)) {
+            log.error("vaultody withdraw callback error,data is empty,content:{},signature:{}", content, signature);
+            return BaseResultDto.error();
+        }
+        VaultodyPayWithdrawPropertyEntity withdrawProperty = getWithdrawProperty(SettlementConstant.VAULTODY_PAY_REMIT,
+                VaultodyPayWithdrawPropertyEntity.class);
+
+        if (!isCallbackValid(content, withdrawProperty.getWebhooksPassphrase(), signature)) {
+            //打印日志 详细说明
+            log.error("vaultody withdraw callback valid error");
+            throw new ServiceException("vaultody withdraw callback valid error");
+        }
+        VaultodyCallbackEntity entity = com.alibaba.fastjson.JSON.parseObject(content, VaultodyCallbackEntity.class);
+        VaultodyDataEntity dataEntity = entity.getData();
+        String event = dataEntity.getEvent();
+        VaultodyItemEntity itemEntity = dataEntity.getItem();
+        String transactionId = itemEntity.getTransactionId();
+        String requestId = itemEntity.getRequestId();
+        if (StringUtils.isEmpty(requestId)) {
+            log.error("vaultody withdraw callback:requestId is empty,{}", content);
+            return BaseResultDto.error();
+        }
+
+        if (StringUtils.isEmpty(event)) {
+            log.error("vaultody withdraw callback:event is empty,{}", com.alibaba.fastjson.JSON.toJSONString(entity));
+            return BaseResultDto.error();
+        }
+
+        if (!"OUTGOING_MINED".equals(event)) {
+            log.error("vaultody withdraw callback:notice type error,{}", content);
+            return BaseResultDto.error();
+        }
+
+        SettlementWithdrawRecordTable withdrawRecordTable = settlementWithdrawRecordRepository.findFirstByRequestSerialAndChannelCode(requestId, SettlementConstant.VAULTODY_PAY_KEY);
+        if (withdrawRecordTable == null) {
+            log.error("Vaultody withdraw 通知回调:未找到对应的记录,{}", content);
+            return BaseResultDto.error();
+        }
+
+        String currency = withdrawRecordTable.getCurrency();
+        String address = withdrawRecordTable.getAddress();
+        BigDecimal transformAmount = withdrawRecordTable.getAmount();
+
+        TxResult txResult;
+        try {
+            txResult = EncryptionHashQueryUtil.query(transactionId, itemEntity.getBlockchain(), currency);
+        } catch (Exception e) {
+            log.error("vaultody withdraw callback:查询交易hash结果失败,{}", content);
+            return BaseResultDto.error();
+        }
+
+        if (txResult == null || StringUtils.isEmpty(txResult.getTo())) {
+            log.error("vaultody withdraw callback:通过hash:{} 未查询到结果,{}", transactionId, content);
+            return BaseResultDto.error();
+        }
+        if (!txResult.getTo().equals(address)) {
+            log.error("vaultody withdraw callback:通过hash:{} 比对地址不相同,{}", txResult.getTo(), address);
+            return BaseResultDto.error();
+        }
+        if (txResult.getAmount().compareTo(transformAmount) != 0) {
+            log.error("vaultody withdraw callback:通过hash:{} 比对金额不相同,{}", txResult.getAmount(), transformAmount);
+            return BaseResultDto.error();
+        }
+
+
+        if ("success".equals(txResult.getStatus())) {
+            withdrawRecordTable.setStatus(1);
+        } else if ("failed".equals(txResult.getStatus())) {
+            withdrawRecordTable.setStatus(2);
+        }
+
+        withdrawRecordTable.setCallbackAmount(txResult.getAmount());
+        withdrawRecordTable.setCallbackCurrency(itemEntity.getBlockchain());
+        withdrawRecordTable.setCallbackTime(new Date());
+        withdrawRecordTable.setCallbackStatus(true);
+        withdrawRecordTable.setCallbackSerial(transactionId);
+        withdrawRecordTable.setCallbackData(content);
+        settlementWithdrawRecordRepository.save(withdrawRecordTable);
+
+        if (withdrawRecordTable.getCallbackAmount() == null || withdrawRecordTable.getCallbackAmount().compareTo(withdrawRecordTable.getAmount()) < 0) {
+            log.error("Vaultody withdraw 通知回调:回调金额:{} 小于订单金额,{}", withdrawRecordTable.getCallbackAmount(), withdrawRecordTable.getAmount());
+            return BaseResultDto.error();
+        }
+
+        mqSendService.send(ConfigConstants.WITHDRAW_CALLBACK, withdrawRecordTable, 3 * 1000);
+        return BaseResultDto.success();
+    }
+
+
+    /**
+     * 分配地址
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public String reallocationAddress(VaultodyDepositRequestEntity request, VaultodyPayPropertyEntity propertyEntity) throws Exception {
+        SysConfigTable configTable = sysConfigService.getByCode(ConfigConstants.VAULTODY_ADDRESS_TOTAL_NUMBER);
+        if (configTable == null || configTable.getValue() == null || Long.parseLong(configTable.getValue()) <= 0) {
+            throw ServiceException.exception(Constants.SYSTEM_ERROR);
+        }
+        long addressTotalNumber = Long.parseLong(configTable.getValue());
+
+        long addressNumber = updateConfigTableInNewTransaction();
+
+        String address;
+        if (addressNumber > addressTotalNumber) {
+            //计算查询数据下标
+            int index = Math.toIntExact((addressNumber - 1) % addressTotalNumber);
+            address = depositAddressMapper.getTableByIndex(index);
+        } else {
+            address = getAddress(propertyEntity, request);
+        }
+        // 保存数据
+        FinanceDepositAddressTable table = new FinanceDepositAddressTable();
+        table.setAddress(address);
+        table.setSerial(request.getSerial());
+        table.setLastUsedTime(new Date());
+        table.setAddTime(new Date());
+        table.setIsUsed(true);
+        depositAddressRepository.save(table);
+        return address;
+    }
+
+
+    /**
+     * 获取钱包地址
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public String getAddress(VaultodyPayPropertyEntity propertyEntity, VaultodyDepositRequestEntity request) throws Exception {
+        String requestPath = getPath(propertyEntity.getAddressPathTemplate(), propertyEntity.getVaultId(),
+                request.getCurrency(),
+                propertyEntity.getNetwork());
+        String apiKey = propertyEntity.getApiKey();
+        String apiSecret = propertyEntity.getApiSecret();
+        String passphrase = propertyEntity.getPassphrase();
+        String method = propertyEntity.getMethod();
+        String baseUrl = propertyEntity.getBaseUrl();
+
+        Map<String, Object> labelMap = new HashMap<>();
+        labelMap.put("label", request.getSerial());
+        Map<String, Object> itemMap = new HashMap<>();
+        itemMap.put("item", labelMap);
+        Map<String, Object> paramMap = new HashMap<>();
+        paramMap.put("data", itemMap);
+
+        // POST JSON body
+        String body = JSON.toJSONString(paramMap);
+        String query = "{}"; // POST 时 query 通常为空,否则按接口要求填写
+
+        // ------------------ 时间戳(秒) ------------------
+        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
+
+        // ------------------ 构建消息用于签名 ------------------
+        String message = timestamp + method + requestPath + body + query;
+
+        String signature = getSignature(message, apiSecret);
+        Map<String, String> headers = new HashMap<>();
+        headers.put("x-api-key", apiKey);
+        headers.put("x-api-sign", signature);
+        headers.put("x-api-timestamp", timestamp);
+        headers.put("x-api-passphrase", passphrase);
+        headers.put("Content-Type", "application/json");
+
+        Connection.Response response = HttpUtil.post(baseUrl + requestPath, headers, body);
+        log.info(String.format("url:%s,json:%s,body:%s", baseUrl + requestPath, body, response.body()));
+        if (response.statusCode() != 200) {
+            log.error("订单: {} 钱包地址生成失败: {}", request.getSerial(), response.body());
+            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
+                    " service.");
+        }
+        // 解析 JSON 字符串
+        JSONObject jsonObject = JSON.parseObject(response.body());
+
+        // 获取 address 和 label 字段
+        String address = jsonObject.getJSONObject("data").getJSONObject("item").getString("address");
+
+        return address;
+    }
+
+    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
+    public long updateConfigTableInNewTransaction() throws ServiceException {
+        Long number = redisService.incr(ConfigConstants.ADDRESS_ID_CONTROL_AUTO_NUMBER);
+        SysConfigTable sysConfigTable = sysConfigRepository.getByCode(ConfigConstants.ADDRESS_ID_CONTROL_AUTO_NUMBER);
+        sysConfigTable.setValue(String.valueOf(number));
+        sysConfigRepository.save(sysConfigTable);
+        return number;
+    }
+
+    private String createWithdraw(VaultodyWithdrawRequestEntity entity, VaultodyPayWithdrawPropertyEntity propertyEntity) throws Exception {
+        String blockchain = null;
+        if (entity.getCurrency().contains("-")) {
+            blockchain = entity.getCurrency().split("-")[0];
+        }
+        if (StringUtils.isEmpty(blockchain)) {
+            blockchain = entity.getCurrency();
+        }
+        String requestPath = getPath(propertyEntity.getCreateTokenTransactionPathTemplate(),
+                propertyEntity.getVaultId(),
+                blockchain, propertyEntity.getNetwork());
+        AtomicReference<String> contractAddress = new AtomicReference<>();
+        AtomicReference<String> fromAddress = new AtomicReference<>();
+        List<VaultodyPayWithdrawAddressPropertyEntity> withdrawAddress = propertyEntity.getWithdrawAddress();
+        if (CollectionUtils.isNotEmpty(withdrawAddress)) {
+            withdrawAddress.forEach(item -> {
+                if (item.getBankCode().equals(entity.getCurrency())) {
+                    fromAddress.set(item.getFromAddress());
+                    contractAddress.set(item.getContractAddress());
+                }
+            });
+        }
+
+        Map<String, Object> itemMap = new HashMap<>();
+        itemMap.put("amount", entity.getAmount().stripTrailingZeros().toPlainString());
+        itemMap.put("contractAddress", contractAddress.get());
+        itemMap.put("fromAddress", fromAddress.get());
+        itemMap.put("recipientAddress", entity.getAddress());
+        Map<String, Object> dataMap = new HashMap<>();
+        dataMap.put("item", itemMap);
+        Map<String, Object> paramMap = new HashMap<>();
+        paramMap.put("data", dataMap);
+
+
+        // POST JSON body
+        String body = JSON.toJSONString(paramMap);
+        String query = "{}"; // POST 时 query 通常为空,否则按接口要求填写
+
+        // ------------------ 时间戳(秒) ------------------
+        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
+
+        // ------------------ 构建消息用于签名 ------------------
+        String message = timestamp + propertyEntity.getMethod() + requestPath + body + query;
+
+        String signature = getSignature(message, propertyEntity.getApiSecret());
+        System.out.println("Signature: " + signature);
+        Map<String, String> headers = new HashMap<>();
+        headers.put("x-api-key", propertyEntity.getApiKey());
+        headers.put("x-api-sign", signature);
+        headers.put("x-api-timestamp", timestamp);
+        headers.put("x-api-passphrase", propertyEntity.getPassphrase());
+        headers.put("Content-Type", "application/json");
+
+        Connection.Response response = HttpUtil.post(propertyEntity.getBaseUrl() + requestPath, headers, body);
+        log.info(String.format("url:%s,json:%s,body:%s",
+                propertyEntity.getBaseUrl() + requestPath, body, response.body()));
+
+        // 解析 JSON 字符串
+        JSONObject jsonObject = JSON.parseObject(response.body());
+        if (response.statusCode() != 200
+                && response.statusCode() != 201
+                && (jsonObject.getJSONObject("data") == null || !"created".equals(jsonObject.getJSONObject("data").getJSONObject("item").getString("transactionRequestStatus")))
+        ) {
+            log.error("vaultody withdraw address: {} error: {}", entity.getAddress(), response.body());
+            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact " +
+                    "customer service.");
+        }
+
+        String requestId = jsonObject.getJSONObject("data").getJSONObject("item").getString("transactionRequestId");
+        if (Strings.isNullOrEmpty(requestId)) {
+            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
+                    " service.");
+        }
+
+        return requestId;
+
+    }
+
+    /**
+     * 保存记录
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void saveDepositRecord(VaultodyDepositRequestEntity request, String property, String address) {
+        SysConfigTable configTable = sysConfigService.getByCode(ConfigConstants.CALLBACK_MAX_RETRY_COUNT);
+        Integer maxRetryCount = Integer.parseInt(configTable.getValue());
+        SettlementDepositRecordTable table = new SettlementDepositRecordTable();
+        table.setMerchantId(request.getMerchantId());
+        table.setSerial(request.getSerial());
+        table.setRequestSerial(address);
+        table.setAmount(request.getAmount());
+        table.setCurrency(request.getCurrency());
+        table.setStatus(0);
+        table.setChannelCode(request.getChannelCode());
+        table.setChannelProperty(property);
+        table.setRequestBody(JSON.toJSONString(request));
+        table.setCallbackUrl(request.getCallbackUrl());
+        table.setCallbackStatus(false);
+        table.setCallbackMerchantStatus(0);
+        table.setCallbackMerchantCount(0);
+        table.setCallbackMerchantMaxRetryCount(maxRetryCount);
+        table.setAddTime(new Date());
+        settlementDepositRecordRepository.save(table);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public void saveWithdrawRecord(VaultodyWithdrawRequestEntity request, String property, String requestId) {
+        String blockchain = null;
+        if (request.getCurrency().contains("-")) {
+            blockchain = request.getCurrency().split("-")[0];
+        }
+        if (StringUtils.isEmpty(blockchain)) {
+            blockchain = request.getCurrency();
+        }
+        SysConfigTable configTable = sysConfigService.getByCode(ConfigConstants.CALLBACK_MAX_RETRY_COUNT);
+        Integer maxRetryCount = Integer.parseInt(configTable.getValue());
+        SettlementWithdrawRecordTable table = new SettlementWithdrawRecordTable();
+        table.setMerchantId(request.getMerchantId());
+        table.setSerial(request.getSerial());
+        table.setRequestSerial(requestId);
+        table.setAmount(request.getAmount());
+        table.setCurrency(blockchain);
+        table.setAddress(request.getAddress());
+        table.setBankUname(request.getBankUname());
+        table.setBankCardNum(request.getBankCardNum());
+        table.setBankName(request.getBankName());
+        table.setBankBranchName(request.getBankBranchName());
+        table.setBankAddr(request.getBankAddr());
+        table.setSwiftCode(request.getSwiftCode());
+        table.setCvv(request.getCvv());
+        table.setExpiryMonth(request.getExpiryMonth());
+        table.setChannelCode(request.getChannelCode());
+        table.setChannelProperty(property);
+        table.setRequestBody(JSON.toJSONString(request));
+        table.setStatus(0);
+        table.setCallbackUrl(request.getCallbackUrl());
+        table.setCallbackStatus(false);
+        table.setCallbackMerchantStatus(0);
+        table.setCallbackMerchantCount(0);
+        table.setCallbackMerchantMaxRetryCount(maxRetryCount);
+        table.setAddTime(new Date());
+        settlementWithdrawRecordRepository.save(table);
+    }
+
+    private String getSignString(VaultodyDepositRequestEntity entity) {
+        Map<String, String> map = getBaseSignString(entity);
+        return MapUtil.getStringSortByKey(map);
+    }
+
+    private String getWithdrawSignString(VaultodyWithdrawRequestEntity entity) {
+        Map<String, String> map = getBaseSignString(entity);
+        map.put("address", entity.getAddress());
+        map.put("bankUname", entity.getBankUname());
+        map.put("bankCardNum", entity.getBankCardNum());
+        map.put("bankName", entity.getBankName());
+        map.put("bankBranchName", entity.getBankBranchName());
+        map.put("bankAddr", entity.getBankAddr());
+        map.put("swiftCode", entity.getSwiftCode());
+        map.put("cvv", entity.getCvv());
+        map.put("expiryMonth", entity.getExpiryMonth());
+        return MapUtil.getStringSortByKey(map);
+    }
+
+    public static String getSignature(String message, String apiSecret) {
+        try {
+            byte[] decodedSecret = Base64.getDecoder().decode(apiSecret);
+            Mac mac = Mac.getInstance("HmacSHA256");
+            SecretKeySpec secretKeySpec = new SecretKeySpec(decodedSecret, "HmacSHA256");
+            mac.init(secretKeySpec);
+            byte[] hash = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
+
+            return Base64.getEncoder().encodeToString(hash);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public String getPath(String pathTemplate, String vaultId, String blockchain, String network) {
+        String actualPath = String.format(pathTemplate, vaultId, blockchain, network);
+        return actualPath;
+    }
+
+    private boolean isCallbackValid(String message, String apiSecret, String signature) {
+        String _signature = HAMCSHA256Util.sha256_HMAC(message, apiSecret);
+
+        //打印签名 两个签名数据和比对结果
+        log.info("isCallbackValid,_signature:{},signature:{}", _signature, signature);
+
+        return _signature.equals(signature);
+    }
+
+}

+ 198 - 0
crm-settlement/src/main/java/com/crm/settlement/service/impl/base/BaseSettlementServiceImpl.java

@@ -0,0 +1,198 @@
+package com.crm.settlement.service.impl.base;
+
+import com.alibaba.fastjson2.JSON;
+import com.crm.rely.backend.core.constant.Constants;
+import com.crm.rely.backend.core.exception.ServiceException;
+import com.crm.rely.backend.core.pojo.table.SysConfigTable;
+import com.crm.rely.backend.util.AESUtil;
+import com.crm.rely.backend.util.DateUtil;
+import com.crm.rely.backend.util.MapUtil;
+import com.crm.rely.backend.util.SignUtil;
+import com.crm.rely.backend.model.constant.ConfigConstants;
+import com.crm.rely.backend.model.constant.SettlementConstant;
+import com.crm.rely.backend.model.entity.base.SettlementBaseEntity;
+import com.crm.rely.backend.model.entity.property.SettlementPropertyEntity;
+import com.crm.rely.backend.model.pojo.table.SysRemitChannelTable;
+import com.crm.rely.backend.model.pojo.table.SysRemittanceChannelTable;
+import com.crm.rely.backend.model.pojo.table.SysSettlementConfigTable;
+import com.crm.settlement.service.SysConfigService;
+import com.crm.settlement.service.SysRemitChannelService;
+import com.crm.settlement.service.SysRemittanceChannelService;
+import com.crm.settlement.service.SysSettlementConfigService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * 通道基础类 提供基础的方法
+ *
+ * @author houn
+ */
+@Slf4j
+public class BaseSettlementServiceImpl {
+
+    @Autowired
+    private SysRemittanceChannelService sysRemittanceChannelService;
+    @Autowired
+    private SysRemitChannelService sysRemitChannelService;
+    @Autowired
+    private SysConfigService sysConfigService;
+    @Autowired
+    private SysSettlementConfigService sysSettlementConfigService;
+
+    /**
+     * 获取配置数据
+     *
+     * @return
+     * @throws ServiceException
+     */
+    protected <T> T getPayProperty(String code, Class<T> tClass) throws ServiceException {
+
+        SysRemittanceChannelTable payChannel = this.getPayChannel(code);
+
+        T object = this.getPayProperty(payChannel, tClass);
+
+        return object;
+    }
+
+    /**
+     * 获取通道
+     *
+     * @param code 通道code
+     * @return
+     * @throws Exception
+     */
+    protected SysRemittanceChannelTable getPayChannel(String code) throws ServiceException {
+
+        SysRemittanceChannelTable channel = sysRemittanceChannelService.getSysRemittanceChannelByCode(code);
+        if (channel == null || channel.getProperty() == null || channel.getProperty().trim().length() <= 0) {
+            throw new ServiceException(SettlementConstant.FINANCE_DEPOSIT_CHANNEL_NOT_EMPTY);
+        }
+
+        return channel;
+    }
+
+    protected <T> T getPayProperty(SysRemittanceChannelTable payChannel, Class<T> tClass) throws ServiceException {
+
+        String property = AESUtil.decrypt(payChannel.getProperty(), this.getPropertyKey());
+        T object = JSON.parseObject(property, tClass);
+        if (object == null) {
+            throw new ServiceException(Constants.SYSTEM_ERROR);
+        }
+
+        return object;
+    }
+
+    protected SysRemitChannelTable getWithdrawChannel(String code) throws ServiceException {
+
+        SysRemitChannelTable channel = sysRemitChannelService.getRemitChannelByCode(code);
+        if (channel == null || channel.getProperty() == null || channel.getProperty().trim().length() <= 0) {
+            throw new ServiceException(SettlementConstant.FINANCE_WITHDRAW_CHANNEL_NOT_EMPTY);
+        }
+        return channel;
+    }
+
+    protected <T> T getWithdrawPayProperty(SysRemitChannelTable payChannel, Class<T> tClass) throws ServiceException {
+
+        String property = AESUtil.decrypt(payChannel.getProperty(), this.getPropertyKey());
+        T object = JSON.parseObject(property, tClass);
+        if (object == null) {
+            throw new ServiceException(Constants.SYSTEM_ERROR);
+        }
+
+        return object;
+    }
+
+    protected <T> T getWithdrawProperty(String code, Class<T> tClass) throws ServiceException {
+
+        SysRemitChannelTable channel = sysRemitChannelService.getRemitChannelByCode(code);
+        if (channel == null || channel.getProperty() == null || channel.getProperty().trim().length() <= 0) {
+            throw new ServiceException(SettlementConstant.FINANCE_WITHDRAW_CHANNEL_NOT_EMPTY);
+        }
+        String property = AESUtil.decrypt(channel.getProperty(), this.getPropertyKey());
+        T object = JSON.parseObject(property, tClass);
+        if (object == null) {
+            throw new ServiceException(Constants.SYSTEM_ERROR);
+        }
+
+        return object;
+    }
+
+    private String getPropertyKey() throws ServiceException {
+        SysConfigTable table = sysConfigService.getByCode(ConfigConstants.FINANCE_PROPERTY_KEY);
+        if (table == null) {
+            throw ServiceException.exception(Constants.SYSTEM_ERROR);
+        }
+        return table.getValue();
+    }
+
+    /**
+     * 获取加密配置
+     */
+    protected String getMerchantConfig(String merchantId) throws ServiceException {
+        SysSettlementConfigTable table = sysSettlementConfigService.getByCode(merchantId);
+        if (table == null || StringUtils.isEmpty(table.getValue())) {
+            throw ServiceException.exception("Merchant error");
+        }
+        return table.getValue();
+    }
+
+    /**
+     * 验证签名
+     */
+    protected void verifySign(String sign, String signString, String merchantId) throws ServiceException {
+        String value = getMerchantConfig(merchantId);
+        SettlementPropertyEntity propertyEntity = JSON.parseObject(value, SettlementPropertyEntity.class);
+        boolean verifyResult = SignUtil.verifySignSHA256withRSA(propertyEntity.getMerchantPublicKey(), signString, sign);
+        if (!verifyResult) {
+            throw ServiceException.exception("signature error");
+        }
+    }
+
+    /**
+     * 验证请求时间和金额
+     */
+    protected void verifyRequestTimeAndAmount(SettlementBaseEntity entity) throws ServiceException {
+        //验证时间
+        long time = DateUtil.getTimestampByZero() - entity.getRequestTime().getTime();
+        if ((time > (10 * 1000)) || (time < 0)) {
+            log.error("time out ,time:" + time);
+            throw ServiceException.exception("Time out");
+        }
+        // 验证金额
+        if (entity.getAmount() == null || entity.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
+            throw ServiceException.exception("Invalid amount");
+        }
+    }
+
+    /**
+     * 获取签名
+     */
+    protected String getSign(Map<String, String> params, String merchantId) throws ServiceException {
+        String value = getMerchantConfig(merchantId);
+        SettlementPropertyEntity propertyEntity = JSON.parseObject(value, SettlementPropertyEntity.class);
+        String signString = MapUtil.getStringSortByKey(params);
+
+        return SignUtil.signSHA256withRSA(propertyEntity.getPrivateKey(), signString);
+    }
+
+    protected Map<String, String> getBaseSignString(SettlementBaseEntity entity) {
+        Map<String, String> map = new HashMap<>();
+        map.put("merchantId", entity.getMerchantId());
+        map.put("channelCode", entity.getChannelCode());
+        map.put("serial", entity.getSerial());
+        map.put("amount", entity.getAmount().stripTrailingZeros().toPlainString());
+        map.put("currency", entity.getCurrency());
+        map.put("callbackUrl", entity.getCallbackUrl());
+        map.put("requestTime", DateUtil.formatTime(entity.getRequestTime()));
+
+        return map;
+    }
+
+
+}

+ 25 - 0
crm-settlement/src/main/resources/application-dev.yml

@@ -0,0 +1,25 @@
+spring:
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://103.214.175.29:28571/cwg_settlement?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowMultiQueries=true&useSSL=false
+    username: root
+    password: NSH01Y0GTmUNjgg6xw80qg==
+  jpa:
+    database: MYSQL
+    hibernate:
+      naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
+    show-sql: true
+  activemq:
+    broker-url: tcp://localhost:61616
+logging:
+  level:
+    com.crm.manager.dao.mapper: trace
+web:
+  front-path: ./front
+  upload-path: ../upload
+role:
+  excludePathPatterns:
+    /**
+#logging:
+#  config: classpath:logback-dev.xml
+

+ 29 - 0
crm-settlement/src/main/resources/application-ho.yml

@@ -0,0 +1,29 @@
+spring:
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://192.168.110.19:3306/cwg_settlement?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowMultiQueries=true&useSSL=false
+    username: root
+    password: 123123
+  jpa:
+    database: MYSQL
+    hibernate:
+      naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
+    show-sql: true
+  activemq:
+    broker-url: tcp://localhost:61616
+logging:
+  level:
+    com.crm.manager.dao.mapper: trace
+
+web:
+  front-path: ./front
+  upload-path: ./upload
+#role:
+#  excludePathPatterns:
+#    /**
+#login:
+#  excludePathPatterns:
+#    /**
+#logging:
+#  config: classpath:logback-dev.xml
+

+ 26 - 0
crm-settlement/src/main/resources/application-hu.yml

@@ -0,0 +1,26 @@
+spring:
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://8.210.194.53:13542/crm_business?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&allowMultiQueries=true&useSSL=false
+    username: root
+    password: F28c49d8be
+  jpa:
+    database: MYSQL
+    hibernate:
+      naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
+    show-sql: true
+  activemq:
+    broker-url: tcp://localhost:61616
+logging:
+  level:
+    com.crm.manager.dao.mapper: trace
+#  redis:
+#    host: 127.0.0.1
+
+web:
+  front-path: G:/crm/crm-back/crm-core/front
+  upload-path: D:\upload
+  export-path: D:/work/cwg/export
+#logging:
+#  config: classpath:logback-dev.xml
+

+ 21 - 0
crm-settlement/src/main/resources/application-prod.yml

@@ -0,0 +1,21 @@
+spring:
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://localhost:18654/cwg_settlement?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowMultiQueries=true&useSSL=false
+    username: pay_user
+    password: YcEWyqZTHWj8KvASB+gQ629m
+  jpa:
+    database: MYSQL
+    hibernate:
+      naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
+    show-sql: true
+  activemq:
+    broker-url: tcp://localhost:61616
+  data:
+    redis:
+      host: 127.0.0.1
+login:
+  single-sign-on: true
+logging:
+  config: classpath:logback-prod.xml
+

+ 25 - 0
crm-settlement/src/main/resources/application-test.yml

@@ -0,0 +1,25 @@
+spring:
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://localhost:28571/cwg_settlement?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&allowMultiQueries=true&useSSL=false
+    username: root
+    password: NSH01Y0GTmUNjgg6xw80qg==
+  jpa:
+    database: MYSQL
+    hibernate:
+      naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
+    show-sql: true
+logging:
+  level:
+    com.crm.manager.dao.mapper: trace
+  config: classpath:logback-prod.xml
+#  data:
+#    redis:
+#      host: 127.0.0.1
+login:
+  single-sign-on: true
+web:
+  upload-path: C:\server\cwg_crm\upload
+  export-path: C:\server\cwg_crm\export
+
+

+ 19 - 0
crm-settlement/src/main/resources/application.yml

@@ -0,0 +1,19 @@
+server:
+  port: 8110
+
+spring:
+  profiles:
+    active: dev
+  application:
+    name: crm-settlement
+
+logging:
+  level:
+    com.crm.settlement: info
+
+login:
+  excludePathPatterns:
+    /**
+
+role:
+  excludePathPatterns: /**

+ 41 - 0
crm-settlement/src/main/resources/i18n/messages.properties

@@ -0,0 +1,41 @@
+department_name_not_null_error=\u90E8\u95E8\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+department_name_is_exist_error=\u90E8\u95E8\u540D\u79F0\u5DF2\u5B58\u5728
+name_not_null=\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+role_name_not_null_error=\u89D2\u8272\u540D\u4E0D\u80FD\u4E3A\u7A7A
+role_not_null_error=\u89D2\u8272\u4E0D\u80FD\u4E3A\u7A7A
+role_not_exist_error=\u89D2\u8272\u4E0D\u5B58\u5728
+config_code_not_null=\u914D\u7F6Ecode\u4E0D\u80FD\u4E3A\u7A7A
+config_code_exist=\u914D\u7F6Ecode\u5DF2\u5B58\u5728
+config_pwd_error=\u914D\u7F6E\u5BC6\u7801\u9A8C\u8BC1\u9519\u8BEF
+email_code_not_null=\u90AE\u4EF6code\u4E0D\u80FD\u4E3A\u7A7A
+email_code_exist=\u90AE\u4EF6code\u5DF2\u5B58\u5728
+email_name_not_null=\u90AE\u4EF6\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+email_subject_not_null=\u90AE\u4EF6\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A
+email_content_not_null=\u90AE\u4EF6\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A
+email_type_not_null=\u90AE\u4EF6\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_code_not_null=\u5165\u91D1\u901A\u9053code\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_code_exist=\u5165\u91D1\u901A\u9053code\u5DF2\u5B58\u5728
+remittance_channel_name_not_null=\u5165\u91D1\u901A\u9053\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_request_url_not_null=\u5165\u91D1\u901A\u9053\u8BF7\u6C42\u8DEF\u7531\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_property_not_null=\u5165\u91D1\u901A\u9053\u914D\u7F6E\u4FE1\u606F\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_Icon_not_null=\u5165\u91D1\u901A\u9053icon\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_introduce_not_null=\u5165\u91D1\u901A\u9053\u4ECB\u7ECD\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_banks_not_null=\u5165\u91D1\u901A\u9053\u94F6\u884C(\u8D27\u5E01)\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A
+remit_channel_code_not_null=\u51FA\u91D1\u901A\u9053code\u4E0D\u80FD\u4E3A\u7A7A
+remit_channel_code_exist=\u51FA\u91D1\u901A\u9053code\u5DF2\u5B58\u5728
+remit_channel_name_not_null=\u51FA\u91D1\u901A\u9053\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+remit_channel_request_url_not_null=\u51FA\u91D1\u901A\u9053\u8BF7\u6C42\u8DEF\u7531\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
+remit_channel_Icon_not_null=\u51FA\u91D1\u901A\u9053icon\u4E0D\u80FD\u4E3A\u7A7A
+remit_channel_introduce_not_null=\u51FA\u91D1\u901A\u9053\u4ECB\u7ECD\u4E0D\u80FD\u4E3A\u7A7A
+remit_channel_banks_not_null=\u51FA\u91D1\u901A\u9053\u94F6\u884C(\u8D27\u5E01)\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A
+role_code_not_null_error=\u89D2\u8272code\u4E0D\u80FD\u4E3A\u7A7A
+role_code_not_admin_error=\u89D2\u8272code\u4E0D\u80FD\u4E3A\u8D85\u7EA7\u7BA1\u7406\u5458
+custom_group_code_not_null=\u7EC4\u522B\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A
+custom_group_code_is_exist=\u7EC4\u522B\u6807\u8BC6\u5DF2\u5B58\u5728
+custom_group_name_not_null=\u7EC4\u522B\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+custom_group_type_not_null=\u7EC4\u522B\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
+custom_group_com_point_not_null=\u7EC4\u522B\u4F63\u91D1\u4E0D\u80FD\u4E3A\u7A7A
+custom_group_pos_not_null=\u7EC4\u522BA/B\u4ED3\u4E0D\u80FD\u4E3A\u7A7A
+custom_group_hide_not_null=\u7EC4\u522BHIDE\u4E0D\u80FD\u4E3A\u7A7A
+custom_group_currency_not_null=\u7EC4\u522B\u8D27\u5E01\u4E0D\u80FD\u4E3A\u7A7A
+markets_email_interval_error=\u8425\u9500\u90AE\u4EF6\u53D1\u9001\u9891\u7E41

+ 43 - 0
crm-settlement/src/main/resources/i18n/messages_en_US.properties

@@ -0,0 +1,43 @@
+department_name_not_null_error=Department name cannot be empty
+department_name_is_exist_error=Department name already exists
+name_not_null=The name cannot be empty
+role_name_not_null_error=Role name cannot be empty
+role_not_null_error=Role cannot be empty
+role_not_exist_error=The role does not exist
+config_code_not_null=Configuration code cannot be empty
+config_code_exist=Configuration code already exists
+config_pwd_error=Configuration password validation error
+email_code_not_null=Email code cannot be empty
+email_code_exist=Message code already exists
+email_name_not_null=The message name cannot be empty
+email_subject_not_null=Email Subject cannot be empty
+email_content_not_null=Email content cannot be empty
+email_type_not_null=Email type cannot be empty
+remittance_channel_code_not_null=Deposit channel code cannot be empty
+remittance_channel_code_exist=Deposit channel code already exists
+remittance_channel_name_not_null=Deposit channel name cannot be empty
+remittance_channel_request_url_not_null=Deposit channel request routing address cannot be empty
+remittance_channel_property_not_null=Deposit channel configuration information cannot be empty
+remittance_channel_Icon_not_null=Deposit channel icon cannot be empty
+remittance_channel_introduce_not_null=Deposit channel description cannot be empty
+remittance_channel_banks_not_null=Deposit channel Bank (currency) list cannot be empty
+remit_channel_code_not_null=Withdrawal channel code cannot be empty
+remit_channel_code_exist=Withdrawal channel code already exists
+remit_channel_name_not_null=Withdrawal channel name cannot be empty
+remit_channel_request_url_not_null=Withdrawal channel request routing address cannot be empty
+remit_channel_Icon_not_null=Withdrawal channel icon cannot be empty
+remit_channel_introduce_not_null=Withdrawal channel description cannot be empty
+remit_channel_banks_not_null=Withdrawal channel Bank (currency) list cannot be empty
+role_code_not_null_error=Role code cannot be empty
+role_code_not_admin_error=Role code cannot be super administrator
+custom_group_code_not_null=Group ID cannot be empty
+custom_group_code_is_exist=Group ID already exists
+custom_group_name_not_null=Group name cannot be empty
+custom_group_type_not_null=Group type cannot be empty
+custom_group_com_point_not_null=Group Commission cannot be empty
+custom_group_pos_not_null=Group A / b position cannot be empty
+custom_group_hide_not_null=Group HIDE cannot be empty
+custom_group_currency_not_null=Group currency cannot be empty
+positive=Positive
+negative=Negative
+markets_email_interval_error=Frequent marketing emails

+ 42 - 0
crm-settlement/src/main/resources/i18n/messages_vn_VN.properties

@@ -0,0 +1,42 @@
+department_name_not_null_error=Department name cannot be empty
+department_name_is_exist_error=Department name already exists
+name_not_null=The name cannot be empty
+role_name_not_null_error=Role name cannot be empty
+role_not_null_error=Role cannot be empty
+role_not_exist_error=The role does not exist
+config_code_not_null=Configuration code cannot be empty
+config_code_exist=Configuration code already exists
+config_pwd_error=Configuration password validation error
+email_code_not_null=Email code cannot be empty
+email_code_exist=Message code already exists
+email_name_not_null=The message name cannot be empty
+email_subject_not_null=Email Subject cannot be empty
+email_content_not_null=Email content cannot be empty
+email_type_not_null=Email type cannot be empty
+remittance_channel_code_not_null=Deposit channel code cannot be empty
+remittance_channel_code_exist=Deposit channel code already exists
+remittance_channel_name_not_null=Deposit channel name cannot be empty
+remittance_channel_request_url_not_null=Deposit channel request routing address cannot be empty
+remittance_channel_property_not_null=Deposit channel configuration information cannot be empty
+remittance_channel_Icon_not_null=Deposit channel icon cannot be empty
+remittance_channel_introduce_not_null=Deposit channel description cannot be empty
+remittance_channel_banks_not_null=Deposit channel Bank (currency) list cannot be empty
+remit_channel_code_not_null=Withdrawal channel code cannot be empty
+remit_channel_code_exist=Withdrawal channel code already exists
+remit_channel_name_not_null=Withdrawal channel name cannot be empty
+remit_channel_request_url_not_null=Withdrawal channel request routing address cannot be empty
+remit_channel_Icon_not_null=Withdrawal channel icon cannot be empty
+remit_channel_introduce_not_null=Withdrawal channel description cannot be empty
+remit_channel_banks_not_null=Withdrawal channel Bank (currency) list cannot be empty
+role_code_not_null_error=Role code cannot be empty
+role_code_not_admin_error=Role code cannot be super administrator
+custom_group_code_not_null=Group ID cannot be empty
+custom_group_code_is_exist=Group ID already exists
+custom_group_name_not_null=Group name cannot be empty
+custom_group_type_not_null=Group type cannot be empty
+custom_group_com_point_not_null=Group Commission cannot be empty
+custom_group_pos_not_null=Group A / b position cannot be empty
+custom_group_hide_not_null=Group HIDE cannot be empty
+custom_group_currency_not_null=Group currency cannot be empty
+positive=Positive
+negative=Negative

+ 43 - 0
crm-settlement/src/main/resources/i18n/messages_zh_CN.properties

@@ -0,0 +1,43 @@
+department_name_not_null_error=\u90E8\u95E8\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+department_name_is_exist_error=\u90E8\u95E8\u540D\u79F0\u5DF2\u5B58\u5728
+name_not_null=\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+role_name_not_null_error=\u89D2\u8272\u540D\u4E0D\u80FD\u4E3A\u7A7A
+role_not_null_error=\u89D2\u8272\u4E0D\u80FD\u4E3A\u7A7A
+role_not_exist_error=\u89D2\u8272\u4E0D\u5B58\u5728
+config_code_not_null=\u914D\u7F6Ecode\u4E0D\u80FD\u4E3A\u7A7A
+config_code_exist=\u914D\u7F6Ecode\u5DF2\u5B58\u5728
+config_pwd_error=\u914D\u7F6E\u5BC6\u7801\u9A8C\u8BC1\u9519\u8BEF
+email_code_not_null=\u90AE\u4EF6code\u4E0D\u80FD\u4E3A\u7A7A
+email_code_exist=\u90AE\u4EF6code\u5DF2\u5B58\u5728
+email_name_not_null=\u90AE\u4EF6\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+email_subject_not_null=\u90AE\u4EF6\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A
+email_content_not_null=\u90AE\u4EF6\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A
+email_type_not_null=\u90AE\u4EF6\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_code_not_null=\u5165\u91D1\u901A\u9053code\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_code_exist=\u5165\u91D1\u901A\u9053code\u5DF2\u5B58\u5728
+remittance_channel_name_not_null=\u5165\u91D1\u901A\u9053\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_request_url_not_null=\u5165\u91D1\u901A\u9053\u8BF7\u6C42\u8DEF\u7531\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_property_not_null=\u5165\u91D1\u901A\u9053\u914D\u7F6E\u4FE1\u606F\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_Icon_not_null=\u5165\u91D1\u901A\u9053icon\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_introduce_not_null=\u5165\u91D1\u901A\u9053\u4ECB\u7ECD\u4E0D\u80FD\u4E3A\u7A7A
+remittance_channel_banks_not_null=\u5165\u91D1\u901A\u9053\u94F6\u884C(\u8D27\u5E01)\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A
+remit_channel_code_not_null=\u51FA\u91D1\u901A\u9053code\u4E0D\u80FD\u4E3A\u7A7A
+remit_channel_code_exist=\u51FA\u91D1\u901A\u9053code\u5DF2\u5B58\u5728
+remit_channel_name_not_null=\u51FA\u91D1\u901A\u9053\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+remit_channel_request_url_not_null=\u51FA\u91D1\u901A\u9053\u8BF7\u6C42\u8DEF\u7531\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
+remit_channel_Icon_not_null=\u51FA\u91D1\u901A\u9053icon\u4E0D\u80FD\u4E3A\u7A7A
+remit_channel_introduce_not_null=\u51FA\u91D1\u901A\u9053\u4ECB\u7ECD\u4E0D\u80FD\u4E3A\u7A7A
+remit_channel_banks_not_null=\u51FA\u91D1\u901A\u9053\u94F6\u884C(\u8D27\u5E01)\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A
+role_code_not_null_error=\u89D2\u8272code\u4E0D\u80FD\u4E3A\u7A7A
+role_code_not_admin_error=\u89D2\u8272code\u4E0D\u80FD\u4E3A\u8D85\u7EA7\u7BA1\u7406\u5458
+custom_group_code_not_null=\u7EC4\u522B\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A
+custom_group_code_is_exist=\u7EC4\u522B\u6807\u8BC6\u5DF2\u5B58\u5728
+custom_group_name_not_null=\u7EC4\u522B\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+custom_group_type_not_null=\u7EC4\u522B\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
+custom_group_com_point_not_null=\u7EC4\u522B\u4F63\u91D1\u4E0D\u80FD\u4E3A\u7A7A
+custom_group_pos_not_null=\u7EC4\u522BA/B\u4ED3\u4E0D\u80FD\u4E3A\u7A7A
+custom_group_hide_not_null=\u7EC4\u522BHIDE\u4E0D\u80FD\u4E3A\u7A7A
+custom_group_currency_not_null=\u7EC4\u522B\u8D27\u5E01\u4E0D\u80FD\u4E3A\u7A7A
+positive=\u662F
+negative=\u5426
+markets_email_interval_error=\u8425\u9500\u90AE\u4EF6\u53D1\u9001\u9891\u7E41

+ 57 - 0
crm-settlement/src/main/resources/logback-dev.xml

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<configuration>
+    <property name="LOG_HOME" value="../log/settlement"/>
+
+    <appender name="errorFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+
+            <fileNamePattern>${LOG_HOME}/errorFile.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{35} - %msg%n</pattern>
+        </encoder>
+    </appender>
+    <appender name="infoFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>INFO</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_HOME}/infoFile.%d{yyyy-MM-dd}.log</fileNamePattern>
+
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{35} - %msg%n</pattern>
+        </encoder>
+    </appender>
+    <appender name="consoleFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>CONSOLE</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+
+            <fileNamePattern>${LOG_HOME}/consoleFile.%d{yyyy-MM-dd}.log</fileNamePattern>
+
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{35} - %msg%n</pattern>
+        </encoder>
+    </appender>
+    <root additivity="false">
+        <level value="CONSOLE"></level>
+        <appender-ref ref="consoleFile" />
+        <appender-ref ref="infoFile" />
+        <appender-ref ref="errorFile" />
+    </root>
+</configuration>

+ 57 - 0
crm-settlement/src/main/resources/logback-prod.xml

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<configuration>
+    <property name="LOG_HOME" value="../log/settlement"/>
+
+    <appender name="errorFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+
+            <fileNamePattern>${LOG_HOME}/error/errorFile.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{35} - %msg%n</pattern>
+        </encoder>
+    </appender>
+    <appender name="infoFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>INFO</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_HOME}/info/infoFile.%d{yyyy-MM-dd}.log</fileNamePattern>
+
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{35} - %msg%n</pattern>
+        </encoder>
+    </appender>
+    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_HOME}/hibernate/logback.%d{yyyy-MM-dd}.log</fileNamePattern>
+        </rollingPolicy>
+        <encoder>
+            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+        </encoder>
+    </appender>
+    <logger name="org.hibernate.SQL" additivity="false" >
+        <level value="DEBUG" />
+        <appender-ref ref="file" />
+    </logger>
+    <logger name="org.hibernate.type.descriptor.sql.BasicBinder" additivity="false" level="TRACE" >
+        <level value="TRACE" />
+        <appender-ref ref="file" />
+    </logger>
+    <root additivity="false">
+        <level value="info"></level>
+        <appender-ref ref="infoFile" />
+        <appender-ref ref="errorFile" />
+        <appender-ref ref="file" />
+    </root>
+</configuration>

+ 10 - 0
crm-settlement/src/main/resources/mapper/FinanceDepositAddressMapper.xml

@@ -0,0 +1,10 @@
+<?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.crm.settlement.dao.mapper.FinanceDepositAddressMapper">
+
+    <select id="getTableByIndex" resultType="String">
+        select address from finance_deposit_address limit 1 offset #{index}
+    </select>
+</mapper>

+ 15 - 0
crm-settlement/src/main/resources/mapper/SettlementDepositRecordMapper.xml

@@ -0,0 +1,15 @@
+<?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.crm.settlement.dao.mapper.SettlementDepositRecordMapper">
+
+    <select id="getAllCallback" resultType="com.crm.rely.backend.model.pojo.table.SettlementDepositRecordTable">
+        SELECT *
+        FROM settlement_deposit_record
+        WHERE callback_merchant_status = 2
+          AND callback_merchant_count <![CDATA[ < ]]> callback_merchant_max_retry_count
+          AND callback_merchant_time IS NOT NULL
+          AND callback_merchant_time <![CDATA[ < ]]> DATE_SUB(NOW(), INTERVAL 1 DAY)
+    </select>
+</mapper>

+ 15 - 0
crm-settlement/src/main/resources/mapper/SettlementWithdrawRecordMapper.xml

@@ -0,0 +1,15 @@
+<?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.crm.settlement.dao.mapper.SettlementWithdrawRecordMapper">
+
+    <select id="getAllCallback" resultType="com.crm.rely.backend.model.pojo.table.SettlementWithdrawRecordTable">
+        SELECT *
+        FROM settlement_withdraw_record
+        WHERE callback_merchant_status = 2
+        AND callback_merchant_count <![CDATA[ < ]]> callback_merchant_max_retry_count
+        AND callback_merchant_time IS NOT NULL
+        AND callback_merchant_time <![CDATA[ < ]]> DATE_SUB(NOW(), INTERVAL 1 DAY)
+    </select>
+</mapper>

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor