李学强 6 maanden geleden
commit
603f9dd3b0
100 gewijzigde bestanden met toevoegingen van 5709 en 0 verwijderingen
  1. 63 0
      .gitignore
  2. 146 0
      README.md
  3. 47 0
      kxs-common/kxs-common-cash/pom.xml
  4. 75 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/CashAutoConfiguration.java
  5. 40 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/constant/CashConstants.java
  6. 25 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/factory/CashFactory.java
  7. 32 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/handlers/CashEventHandlers.java
  8. 24 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/handlers/CashMessageHandler.java
  9. 31 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/handlers/DefaultCashMessageHandler.java
  10. 23 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/common/CashMessage.java
  11. 20 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/common/CashRequest.java
  12. 57 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/common/CommCashReq.java
  13. 49 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/common/CommCashRes.java
  14. 27 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/common/CommQueryReq.java
  15. 41 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/hs/HsCashBackMessage.java
  16. 23 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/hs/HsCashCommRes.java
  17. 17 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/hs/HsCashPayRes.java
  18. 51 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/hs/HsCashRequest.java
  19. 26 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/hs/HsQueryRequest.java
  20. 42 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/hs/HsQueryRes.java
  21. 24 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/ldys/LdysCashCommReq.java
  22. 19 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/ldys/LdysCashCommRes.java
  23. 26 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/ldys/LdysCashNotifyRes.java
  24. 18 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/ldys/LdysCashQueryReq.java
  25. 41 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/ldys/LdysCashQueryRes.java
  26. 60 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/ldys/LdysCashSubmitReq.java
  27. 19 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgAddBankCardReq.java
  28. 16 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgApiBaseReq.java
  29. 18 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgApiBaseRes.java
  30. 18 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgApiReqBody.java
  31. 27 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgCashSubmitReq.java
  32. 32 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgCashSubmitRes.java
  33. 63 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgCommHead.java
  34. 19 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgCommonReq.java
  35. 18 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgCommonRes.java
  36. 14 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgFileUploadReq.java
  37. 19 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgIdCardAuthReq.java
  38. 18 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgQueryInfoReq.java
  39. 19 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgRegisterReq.java
  40. 17 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgRegisterRes.java
  41. 29 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/params/IsvParams.java
  42. 48 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/params/hs/HsIsvParams.java
  43. 55 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/params/ldys/LdysIsvParams.java
  44. 67 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/params/lg/LgIsvParams.java
  45. 43 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/params/sft/SftIsvParams.java
  46. 57 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/params/yhs/YhsIsvParams.java
  47. 42 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCashBackMessage.java
  48. 25 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCashCommRequest.java
  49. 21 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCashCommRes.java
  50. 33 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCashSubmitReq.java
  51. 36 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCashSubmitRes.java
  52. 22 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCreateRequest.java
  53. 21 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCreateRes.java
  54. 18 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftQueryInfoReq.java
  55. 33 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftQueryInfoRes.java
  56. 19 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftQuerySignStateReq.java
  57. 25 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftSignStateRes.java
  58. 23 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftSignUrlRequest.java
  59. 23 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftSignUrlRes.java
  60. 33 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftUserInfos.java
  61. 63 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/CashSubmitReq.java
  62. 32 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/CashSubmitRes.java
  63. 18 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/QueryInfoReq.java
  64. 19 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/QueryInfoRes.java
  65. 18 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/QueryYhsReq.java
  66. 31 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/YhsBaseRequest.java
  67. 76 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/YhsCashBackMessage.java
  68. 25 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/YhsCashCommRequest.java
  69. 21 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/YhsCashCommRes.java
  70. 24 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/YhsCreateRequest.java
  71. 19 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/YhsFileDataRequest.java
  72. 83 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/CashService.java
  73. 39 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/ConfigContextService.java
  74. 258 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/hs/HsCashService.java
  75. 357 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/ldys/LdysCashService.java
  76. 417 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/lg/LgCashService.java
  77. 388 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/sft/SftCashService.java
  78. 337 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/yhs/YhsCashService.java
  79. 117 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/utils/LgRSAEncryptUtils.java
  80. 70 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/utils/LgRSASignatureUtils.java
  81. 231 0
      kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/utils/YHS_RSA_Util.java
  82. 1 0
      kxs-common/kxs-common-cash/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  83. 95 0
      kxs-common/kxs-common-core/pom.xml
  84. 69 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/base/PayBaseDTO.java
  85. 42 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/config/JacksonConfiguration.java
  86. 37 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/config/RedisProperties.java
  87. 91 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/config/RedisTemplateConfiguration.java
  88. 20 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/config/RestTemplateConfiguration.java
  89. 55 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/config/TaskExecutorConfiguration.java
  90. 55 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/config/WebMvcConfiguration.java
  91. 53 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/CacheConstants.java
  92. 77 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/CommonConstants.java
  93. 136 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/SecurityConstants.java
  94. 55 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/ServiceNameConstants.java
  95. 37 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/enums/DictTypeEnum.java
  96. 50 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/enums/ErrorTypeEnum.java
  97. 37 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/enums/LoginTypeEnum.java
  98. 42 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/enums/MenuTypeEnum.java
  99. 39 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/enums/PrintChannelCodeEnum.java
  100. 38 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/enums/UserStatusEnum.java

+ 63 - 0
.gitignore

@@ -0,0 +1,63 @@
+### gradle ###
+.gradle
+/build/
+!gradle/wrapper/gradle-wrapper.jar
+
+### STS ###
+.settings/
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+bin/
+db/
+data/
+target/
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+rebel.xml
+
+### NetBeans ###
+nbproject/private/
+build/
+nbbuild/
+nbdist/
+.nb-gradle/
+
+### maven ###
+target/
+*.war
+*.ear
+*.zip
+*.tar
+*.tar.gz
+*.versionsBackup
+*.yml
+### vscode ###
+.vscode
+yarn.lock
+
+### logs ###
+/logs/
+*.log
+
+### temp ignore ###
+*.cache
+*.diff
+*.patch
+*.tmp
+*.java~
+*.properties~
+*.xml~
+
+### system ignore ###
+.DS_Store
+Thumbs.db
+Servers
+.metadata

+ 146 - 0
README.md

@@ -0,0 +1,146 @@
+### 系统说明
+- 定位JAVA开发多模块架构;功能聚合;服务分离;轻量级快速开发框架
+- 内置基于OAuth2 的 RBAC 权限管理系统
+- 基于网关统一路由所有服务,可实现动态负载均衡
+- 前置网关加解密、请求限流、header头清洗
+- 内置定时任务统一调度
+- 支持容器化一键部署架构
+- 管理后台基于vue3的element-plus开发
+- 可无缝对接nacos、dubbo、spring-cloud
+### 后端版本说明
+| Jdk                         | 17    |
+|-----------------------------|-------|
+| Spring Boot                 | 3.1.5 |
+| Mybatis Plus                | 3.5.4 |
+| hutool                      | 5.8.2 |
+| Spring Authorization Server | 1.1.3 |
+
+### 模块说明
+
+```
+kxs
+ └── kxs-admin-ui-- 后台管理页面
+ └── kxs-codegen-- 代码生成模块[4002]
+ └── kxs-common -- 系统公共模块
+     ├── kxs-common-core -- 公共工具核心包
+     ├── kxs-common-datasource -- 动态数据源
+     ├── kxs-common-log -- 异步日志服务
+     ├── kxs-common-mybatis -- mybatis 扩展封装
+     ├── kxs-common-security -- oauth2 工具包 鉴权登录
+ ├── kxs-gateway -- Gateway网关[9999]
+ ├── kxs-quartz-- 定时任务模块[4000]
+ └── kxs-system-- 通用用户权限管理模块
+      └── kxs-system-api -- 通用用户权限管理系统公共api模块
+      └── kxs-system-biz -- 通用用户权限管理系统业务处理模块[8080]
+
+```
+### 快速开始
+ **导入db文件下的sql语句** 
+
+ **开启本地redis** 
+
+ ### 后端启动
+
+```
+1.kxs-gateway/KxsGatewayApplication.java
+2.kxs-system-biz/KxsAdminApplication.java
+3.kxs-codegen/KxsCodeGenApplication.java 代码生成按需启动
+4.kxs-quartz/KxsQuartzApplication.java 定时任务按需启动
+```
+### 管理后台启动
+
+```
+#  检查node版本 = 16
+node -v 
+
+# 若高版本node 可执行n 命令切换至 16 版本
+n 16.17.1
+
+# 切换到kxs-admin-ui目录下依次执行
+npm install  --registry=https://registry.npmmirror.com
+npm run dev
+```
+### 访问管理后台
+
+```
+浏览器访问:  http://localhost:8888
+默认用户名: admin
+默认密码: 123456
+```
+
+### Gateway网关
+网关采用的是spring官方的网关组件,通过异步背压的高性能网关。路由配置是整个架构最为核心的功能
+
+网关内置了图形验证码以及验证逻辑,基于webflux 响应式接口实现
+
+ **路由及限流配置**
+
+```
+      routes:
+        #ADMIN 模块
+        - id: kxs-system-biz  # 唯一的服务ID
+          uri: lb://kxs-system-biz # instances下的服务名称,实现负载均衡
+          predicates:
+            - Path=/v1/kxs/admin/** #当前服务的请求前缀,
+          filters:
+            # 过滤前缀
+            - StripPrefix=3
+            # 限流配置
+            - name: RequestRateLimiter
+              args:
+                key-resolver: '#{@remoteAddrKeyResolver}' #SPEL表达式去的对应的bean
+                redis-rate-limiter.replenishRate: 100 # 令牌桶每秒填充平均速率
+                redis-rate-limiter.burstCapacity: 200 # 令牌桶总容量
+            # 验证码处理
+            - ValidateCodeGatewayFilter #如果该服务需要验证码则开启此过滤器
+            # 解密过滤器
+            - RequestDecoderFilter
+```
+**网关跨域支持** 
+
+```
+spring:
+  cloud:
+    gateway:
+      globalcors:
+        corsConfigurations:
+          '[/**]':
+            allowedOriginPatterns: "*"        
+            allowed-methods: "*"
+            allowed-headers: "*"
+            allow-credentials: true
+            exposedHeaders: "Content-Disposition,Content-Type,Cache-Control"
+```
+
+ **网关加解密**
+
+ 解密使用的是标准的aes对称加密
+
+
+```
+解密关键方法
+BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters
+				.fromPublisher(modifiedBody, String.class);
+			HttpHeaders headers = new HttpHeaders();
+			headers.putAll(exchange.getRequest().getHeaders());
+			headers.remove(HttpHeaders.CONTENT_LENGTH);
+			// 创建CachedBodyOutputMessage并且把请求param加入
+			CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
+			return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
+				ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
+					@Override
+					public Flux<DataBuffer> getBody() {
+						return outputMessage.getBody();
+					}
+				};
+				return chain.filter(exchange.mutate().request(decorator).build());
+			}));
+```
+
+### 认证授权
+
+ **登陆授权流程图** 
+
+![流程图](1700399839501.jpg)
+
+ **后续文档编写中...** 

+ 47 - 0
kxs-common/kxs-common-cash/pom.xml

@@ -0,0 +1,47 @@
+<?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>
+    <parent>
+        <groupId>com.kxs</groupId>
+        <artifactId>kxs-common</artifactId>
+        <version>1.1.0</version>
+    </parent>
+
+    <artifactId>kxs-common-cash</artifactId>
+    <description>代付提现插件</description>
+
+    <properties>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.kxs</groupId>
+            <artifactId>kxs-system-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-json</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-extra</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-crypto</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.kxs</groupId>
+            <artifactId>kxs-common-core</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 75 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/CashAutoConfiguration.java

@@ -0,0 +1,75 @@
+package com.kxs.common.cash;
+
+import com.kxs.common.cash.factory.CashFactory;
+import com.kxs.common.cash.handlers.CashEventHandlers;
+import com.kxs.common.cash.service.ConfigContextService;
+import com.kxs.common.cash.service.hs.HsCashService;
+import com.kxs.common.cash.service.lg.LgCashService;
+import com.kxs.common.cash.service.sft.SftCashService;
+import com.kxs.common.cash.service.yhs.YhsCashService;
+import com.kxs.system.api.feign.RemoteSysService;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+/**
+ * 提现自动配置
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EnableAsync
+@Configuration(proxyBeanMethods = false)
+public class CashAutoConfiguration {
+
+
+	@Bean
+	@ConditionalOnMissingBean
+	public ConfigContextService configContextService(RemoteSysService remoteSysService) {
+
+		return new ConfigContextService(remoteSysService);
+	}
+
+	@Bean
+	@ConditionalOnMissingBean
+	public HsCashService hsCashService(ConfigContextService configContextService) {
+
+		return new HsCashService(configContextService);
+	}
+
+	@Bean
+	@ConditionalOnMissingBean
+	public YhsCashService yhsCashService(ConfigContextService configContextService) {
+
+		return new YhsCashService(configContextService);
+	}
+
+	@Bean
+	@ConditionalOnMissingBean
+	public SftCashService sftCashService(ConfigContextService configContextService) {
+
+		return new SftCashService(configContextService);
+	}
+
+	@Bean
+	@ConditionalOnMissingBean
+	public LgCashService lgCashService(ConfigContextService configContextService) {
+
+		return new LgCashService(configContextService);
+	}
+
+	@Bean
+	@ConditionalOnMissingBean
+	public CashFactory cashFactory() {
+
+		return new CashFactory();
+	}
+
+	@Bean
+	@ConditionalOnMissingBean
+	public CashEventHandlers cashEventHandlers(CashFactory cashFactory) {
+
+		return new CashEventHandlers(cashFactory);
+	}
+}

+ 40 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/constant/CashConstants.java

@@ -0,0 +1,40 @@
+
+package com.kxs.common.cash.constant;
+
+/**
+ * @author 没秃顶的码农
+ * @date 2021年01月01日
+ * <p>
+ * 缓存的key 常量
+ */
+public interface CashConstants {
+
+
+	String PUBLIC_PARAMS = "IsvParams";
+	String PUBLIC_SERVICE = "CashService";
+
+	/**
+	 * 盒子慧算
+	 */
+	String CASH_HS = "hs";
+
+	/**
+	 * 云汇算
+	 */
+	String CASH_YHS = "yhs";
+
+	/**
+	 * 盛付通
+	 */
+	String CASH_SFT = "sft";
+
+	/**
+	 * 乐工
+	 */
+	String CASH_LG = "lg";
+
+	/**
+	 * 联动优势
+	 */
+	String CASH_LDYS = "ldys";
+}

+ 25 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/factory/CashFactory.java

@@ -0,0 +1,25 @@
+package com.kxs.common.cash.factory;
+
+import cn.hutool.core.util.StrUtil;
+import com.kxs.common.cash.constant.CashConstants;
+import com.kxs.common.cash.service.CashService;
+import com.kxs.common.core.util.SpringContextHolder;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * 工厂类
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-26
+ */
+@RequiredArgsConstructor
+public class CashFactory {
+
+    public CashService getCashService(String ifCode) {
+
+        //必须下划线命名,否则找不到
+        String[] split = ifCode.split(StrUtil.UNDERLINE);
+        return SpringContextHolder.getBean(split[0] + CashConstants.PUBLIC_SERVICE, CashService.class);
+    }
+
+}

+ 32 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/handlers/CashEventHandlers.java

@@ -0,0 +1,32 @@
+package com.kxs.common.cash.handlers;
+
+import com.kxs.common.cash.factory.CashFactory;
+import com.kxs.common.cash.service.CashService;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 提现事件处理程序
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-28
+ */
+@RestController
+@RequestMapping("/cash")
+@RequiredArgsConstructor
+public class CashEventHandlers {
+
+    private final CashFactory cashFactory;
+
+
+    @RequestMapping(value = "cashBack/{ifCode}")
+    public String cashBack(HttpServletRequest request, @PathVariable String ifCode) {
+
+        CashService cashService = cashFactory.getCashService(ifCode);
+        return cashService.cashBack(request, ifCode);
+    }
+
+}

+ 24 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/handlers/CashMessageHandler.java

@@ -0,0 +1,24 @@
+package com.kxs.common.cash.handlers;
+
+import com.kxs.common.cash.model.common.CashMessage;
+import org.springframework.core.Ordered;
+
+/**
+ * 支付消息处理程序
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-28
+ */
+public interface CashMessageHandler<M extends CashMessage>  extends Ordered {
+
+    String handle(M var1);
+
+    /**
+     * 是否支持此支付平台
+     *
+     * @return boolean
+     */
+    default boolean support(String ifCode) {
+        return true;
+    }
+}

+ 31 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/handlers/DefaultCashMessageHandler.java

@@ -0,0 +1,31 @@
+package com.kxs.common.cash.handlers;
+
+import com.alibaba.fastjson.JSON;
+import com.kxs.common.cash.model.common.CashMessage;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 默认支付回调处理程序
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-28
+ */
+@Slf4j
+public class DefaultCashMessageHandler implements CashMessageHandler<CashMessage> {
+
+    public DefaultCashMessageHandler() {
+    }
+
+    public String handle(CashMessage cashMessage) {
+        log.info("回调提现消息处理器,回调消息:" + JSON.toJSONString(cashMessage));
+
+        return "{" +
+                " \"code\": 0\n" +
+                "}";
+    }
+
+    @Override
+    public int getOrder() {
+        return Integer.MIN_VALUE;
+    }
+}

+ 23 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/common/CashMessage.java

@@ -0,0 +1,23 @@
+package com.kxs.common.cash.model.common;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 回调公共
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-28
+ */
+@Data
+public class CashMessage implements Serializable {
+
+
+    /**
+     * 标识
+     */
+    private String ifCode;
+
+
+}

+ 20 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/common/CashRequest.java

@@ -0,0 +1,20 @@
+package com.kxs.common.cash.model.common;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * 提交请求公共参数
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@Data
+public class CashRequest {
+
+    @NotBlank(message = "ifCode不能为空")
+    private String ifCode;
+
+
+
+}

+ 57 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/common/CommCashReq.java

@@ -0,0 +1,57 @@
+package com.kxs.common.cash.model.common;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 提交提现请求公共参数
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@Data
+public class CommCashReq{
+
+
+    @NotBlank(message = "ifCode不能为空")
+    private String ifCode;
+
+    /**
+     * 提现金额 分
+     */
+    private BigDecimal amount;
+
+    /**
+     * 收款人身份证姓名
+     */
+    private String acctName;
+
+    /**
+     * 收款人银行卡号
+     */
+    private String acctNo;
+    /**
+     * 收款人身份证号
+     */
+    private String acctIdCard;
+    /**
+     * 出单号
+     */
+    private String outOrderNo;
+    /**
+     * 银行名称
+     */
+    private String bankName;
+    /**
+     * 银行预留手机号
+     */
+    private String acctMobile;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 49 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/common/CommCashRes.java

@@ -0,0 +1,49 @@
+package com.kxs.common.cash.model.common;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 提现结果公共参数
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class CommCashRes extends CashMessage{
+
+
+
+    /**
+     * 1提现成功 2处理中 0失败
+     */
+    private Integer status;
+
+    /**
+     * 提现金额 分
+     */
+    private BigDecimal amount;
+
+    /**
+     * 手续费
+     */
+    private BigDecimal fee;
+
+    /**
+     * 出单号
+     */
+    private String outOrderNo;
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 个税
+     */
+    private BigDecimal taxAmount;
+
+}

+ 27 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/common/CommQueryReq.java

@@ -0,0 +1,27 @@
+package com.kxs.common.cash.model.common;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 提现订单查询请求公共参数
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@Data
+public class CommQueryReq {
+
+
+    @NotBlank(message = "ifCode不能为空")
+    private String ifCode;
+
+    /**
+     * 系统订单号
+     */
+    @NotBlank(message = "系统订单号不能为空")
+    private String outOrderNo;
+
+}

+ 41 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/hs/HsCashBackMessage.java

@@ -0,0 +1,41 @@
+package com.kxs.common.cash.model.hs;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.kxs.common.cash.model.common.CashMessage;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 盒子慧算提现通知地址
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class HsCashBackMessage  extends CashMessage {
+
+    private BigDecimal amount;
+    private BigDecimal fee;
+    private String appCode;
+    private String outOrderNo;
+    private String outBatchNo;
+    private String orderNo;
+    private String batchNo;
+    @JsonFormat(pattern = "MMM dd, yyyy hh:mm:ss a")
+    private Date finalStateTime;
+    /**
+     * 状态: 0-提现成功 1-已受理 3-挂单 4-失败 5-发起转账 6-转账成功7-已请求提 现
+     * 0-提现成功: 交易成功
+     * 1-已受理 : 系统已接收该笔订单
+     * 3-挂单 : 企业余额不足,订单挂起(后续调用挂单接口进行出款) 4-失败 :交易失败
+     * 5-发起转账:业务校验通过,进入内部虚拟账户划账 6-转账成功:内部虚拟账户划账成功,开始出款 7-已请求提现:已经发往通道
+     * 1,5,6,7 可以统一认定为 处理中
+     */
+    private Integer state;
+    private String sign;
+    private String errorMsg;
+}

+ 23 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/hs/HsCashCommRes.java

@@ -0,0 +1,23 @@
+package com.kxs.common.cash.model.hs;
+
+import lombok.Data;
+
+/**
+ * 提现公共响应
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@Data
+public class HsCashCommRes<T> {
+
+    private T data;
+
+    private String ifCode;
+    private Integer code;
+    private String errorCode;
+    private String errorDesc;
+    private String success;
+
+
+}

+ 17 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/hs/HsCashPayRes.java

@@ -0,0 +1,17 @@
+package com.kxs.common.cash.model.hs;
+
+import lombok.Data;
+
+/**
+ * 提现公共响应
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@Data
+public class HsCashPayRes {
+
+    private String orderNo;
+
+
+}

+ 51 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/hs/HsCashRequest.java

@@ -0,0 +1,51 @@
+package com.kxs.common.cash.model.hs;
+
+import com.kxs.common.cash.model.common.CashRequest;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 提交请求公共参数
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class HsCashRequest extends CashRequest {
+
+    /**
+     * 提现金额 分
+     */
+    private BigDecimal amount;
+
+    /**
+     * 收款人身份证姓名
+     */
+    private String acctName;
+
+    /**
+     * 收款人银行卡号
+     */
+    private String acctNo;
+    /**
+     * 收款人身份证号
+     */
+    private String acctIdCard;
+    /**
+     * 出单号
+     */
+    private String outOrderNo;
+    /**
+     * 银行名称
+     */
+    private String bankName;
+    /**
+     * 银行预留手机号
+     */
+    private String acctMobile;
+
+}

+ 26 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/hs/HsQueryRequest.java

@@ -0,0 +1,26 @@
+package com.kxs.common.cash.model.hs;
+
+import com.kxs.common.cash.model.common.CashRequest;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 查询提现订单信息
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class HsQueryRequest extends CashRequest {
+
+    /**
+     * 系统订单号
+     */
+    @NotBlank(message = "系统订单号不能为空")
+    private String outOrderNo;
+
+}

+ 42 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/hs/HsQueryRes.java

@@ -0,0 +1,42 @@
+package com.kxs.common.cash.model.hs;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 提现订单查询请求
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@Data
+public class HsQueryRes {
+
+
+    private BigDecimal amount;
+    private BigDecimal fee;
+    private String appCode;
+    private String outOrderNo;
+    private String outBatchNo;
+    private String orderNo;
+    private String batchNo;
+    @JsonFormat(pattern = "MMM dd, yyyy hh:mm:ss a")
+    private Date finalStateTime;
+    /**
+     *
+     * 状态: 0-提现成功 1-已受理 3-挂单 4-失败 5-发起转账 6-转账成功7-已请求提 现
+     * 0-提现成功: 交易成功
+     * 1-已受理 : 系统已接收该笔订单
+     * 3-挂单 : 企业余额不足,订单挂起(后续调用挂单接口进行出款) 4-失败 :交易失败
+     * 5-发起转账:业务校验通过,进入内部虚拟账户划账 6-转账成功:内部虚拟账户划账成功,开始出款 7-已请求提现:已经发往通道
+     * 1,5,6,7 可以统一认定为 处理中
+     */
+    private Integer state;
+    private String sign;
+    private String errorMsg;
+
+}

+ 24 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/ldys/LdysCashCommReq.java

@@ -0,0 +1,24 @@
+package com.kxs.common.cash.model.ldys;
+
+import com.kxs.common.cash.model.common.CashRequest;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 联动公共请求
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LdysCashCommReq extends CashRequest {
+
+    private String appId;
+    private String encryptKey;
+    private String requestId;
+    private String sign;
+    private String timestamp;
+    private String data;
+
+}

+ 19 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/ldys/LdysCashCommRes.java

@@ -0,0 +1,19 @@
+package com.kxs.common.cash.model.ldys;
+
+import lombok.Data;
+
+/**
+ * 联动公共响应
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@Data
+public class LdysCashCommRes<T> {
+
+    private T data;
+
+    private String code;
+    private String msg;
+
+}

+ 26 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/ldys/LdysCashNotifyRes.java

@@ -0,0 +1,26 @@
+package com.kxs.common.cash.model.ldys;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 代付出款通知
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LdysCashNotifyRes extends LdysCashCommReq{
+
+    private String code;
+    private String msg;
+    private String appId;
+    private String data;
+    private String encryptKey;
+    private String serviceType;
+    private String sign;
+    private String timestamp;
+    private String responseId;
+
+}

+ 18 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/ldys/LdysCashQueryReq.java

@@ -0,0 +1,18 @@
+package com.kxs.common.cash.model.ldys;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 代付申请查询
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LdysCashQueryReq extends LdysCashCommReq{
+
+    private String orderNo;
+
+}

+ 41 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/ldys/LdysCashQueryRes.java

@@ -0,0 +1,41 @@
+package com.kxs.common.cash.model.ldys;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 代付申请查询返回
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LdysCashQueryRes extends LdysCashCommReq{
+
+    private String orderNo;
+
+    /**
+     * 提现状态
+     * INIT-待提交
+     * WAIT_AUDIT-待审核
+     * WAIT_PAY-出款中
+     * SUCCESS-成功
+     * FAIL-失败
+     * REJECT-拒绝
+     */
+    private String status;
+
+    private BigDecimal amount;
+    private BigDecimal withdrawFeeTotal;
+    private BigDecimal withdrawFee;
+    private BigDecimal withdrawTaxFee;
+
+    /**
+     * 失败原因
+     */
+    private String failReason;
+
+}

+ 60 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/ldys/LdysCashSubmitReq.java

@@ -0,0 +1,60 @@
+package com.kxs.common.cash.model.ldys;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 代付申请
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LdysCashSubmitReq extends LdysCashCommReq{
+
+    private String orderNo;
+
+    /**
+     * "单位:元 保留小数点两位
+     * 实际到账金额"
+     */
+    private BigDecimal amount;
+
+    /**
+     * 账户类型
+     */
+    private String accountType;
+
+    /**
+     * 结算方式
+     */
+    private String settleType;
+    /**
+     * 结算账户
+     */
+    private String accountNo;
+    /**
+     * 结算账户名称
+     */
+    private String accountName;
+    /**
+     * 银行名称
+     */
+    private String bankName;
+
+    /**
+     * 结算账户手机号
+     */
+    private String phoneNo;
+
+    /**
+     * 结算账户身份证号
+     */
+    private String idCard;
+
+    private BigDecimal entrustAmount;
+
+}

+ 19 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgAddBankCardReq.java

@@ -0,0 +1,19 @@
+package com.kxs.common.cash.model.lg;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * LG 注册
+ *
+ * @author lixueqiang
+ * @date 2025/11/05
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LgAddBankCardReq extends LgCommonReq{
+
+    private String idCardNo;
+    private String bankCardNo;
+    private String bank;
+}

+ 16 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgApiBaseReq.java

@@ -0,0 +1,16 @@
+package com.kxs.common.cash.model.lg;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * api请求体
+ * @author lixueqiang
+ */
+@Data
+public class LgApiBaseReq implements Serializable {
+
+    private LgCommHead head;
+    private LgApiReqBody body;
+}

+ 18 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgApiBaseRes.java

@@ -0,0 +1,18 @@
+package com.kxs.common.cash.model.lg;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * api返回体
+ * @author lixueqiang
+ */
+@Data
+public class LgApiBaseRes implements Serializable {
+
+    private LgCommHead head;
+
+    private LgCommonRes body;
+
+}

+ 18 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgApiReqBody.java

@@ -0,0 +1,18 @@
+package com.kxs.common.cash.model.lg;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * api请求体
+ * @author lixueqiang
+ */
+@Data
+public class LgApiReqBody implements Serializable {
+
+    /**
+     * 请求体
+     */
+    private String data;
+}

+ 27 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgCashSubmitReq.java

@@ -0,0 +1,27 @@
+package com.kxs.common.cash.model.lg;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * LG 提现
+ *
+ * @author lixueqiang
+ * @date 2025/11/05
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LgCashSubmitReq extends LgCommonReq{
+
+    private String projectCode;
+    private String outOrderNo;
+    private String name;
+    private String idCard;
+    private String payChannel = "1";
+    private String accountNo;
+    private BigDecimal amount;
+    private String remark;
+    private String notifyUrl;
+}

+ 32 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgCashSubmitRes.java

@@ -0,0 +1,32 @@
+package com.kxs.common.cash.model.lg;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * LG 提现
+ *
+ * @author lixueqiang
+ * @date 2025/11/05
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LgCashSubmitRes extends LgCommonReq{
+
+    private String orderNo;
+    private String outOrderNo;
+    private String status;
+    private String desc;
+    private String successTime;
+    private String payChannel;
+    private BigDecimal amount;
+    private String totalAmount;
+    private String receivedAmount;
+    private BigDecimal inComeTax;
+    private BigDecimal valueAddedTax;
+    private BigDecimal additionalTax;
+    private BigDecimal serviceCharge;
+    private BigDecimal platformServic;
+}

+ 63 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgCommHead.java

@@ -0,0 +1,63 @@
+package com.kxs.common.cash.model.lg;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * api请求头
+ * @author lixueqiang
+ */
+@Data
+public class LgCommHead implements Serializable {
+
+    /**
+     * 字符集
+     */
+    private String charset = "01";
+
+    /**
+     * 接口版本
+     */
+    private String version = "1.0.0";
+
+    /**
+     * 签名
+     */
+    private String sign;
+
+    /**
+     * 签名和密钥加密算法类型
+     */
+    private String signType = "0";
+
+    /**
+     * 请求流水号
+     */
+    private String requestId;
+
+    /**
+     * 交易发起时间
+     */
+    private String requestTime;
+
+    /**
+     * 随机字符串
+     */
+    private String nonce;
+
+    /**
+     * 机构编号
+     */
+    private String orgCode;
+
+    /**
+     * 敏感数据加解密算法
+     */
+    private String encAlgo = "0";
+
+    /**
+     * 租户编码
+     */
+    private String tenantCode;
+}

+ 19 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgCommonReq.java

@@ -0,0 +1,19 @@
+package com.kxs.common.cash.model.lg;
+
+import com.kxs.common.cash.model.common.CashRequest;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 提交请求公共参数
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LgCommonReq extends CashRequest {
+
+    private String companyCode;
+
+}

+ 18 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgCommonRes.java

@@ -0,0 +1,18 @@
+package com.kxs.common.cash.model.lg;
+
+import lombok.Data;
+
+/**
+ * 返回公共参数
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@Data
+public class LgCommonRes {
+
+    private String code;
+    private String msg;
+    private String data;
+
+}

+ 14 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgFileUploadReq.java

@@ -0,0 +1,14 @@
+package com.kxs.common.cash.model.lg;
+
+import lombok.Data;
+
+/**
+ * @author chenwuqiang
+ * @date 2022/6/15 12:32
+ */
+@Data
+public class LgFileUploadReq {
+    private String fileName;
+    private String fileHash;
+    private Integer fileType;
+}

+ 19 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgIdCardAuthReq.java

@@ -0,0 +1,19 @@
+package com.kxs.common.cash.model.lg;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * LG 注册
+ *
+ * @author lixueqiang
+ * @date 2025/11/05
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LgIdCardAuthReq extends LgCommonReq{
+
+    private String freelancerId;
+    private String frontImgId;
+    private String backImgId;
+}

+ 18 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgQueryInfoReq.java

@@ -0,0 +1,18 @@
+package com.kxs.common.cash.model.lg;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 查询订单状态
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LgQueryInfoReq extends LgCommonReq{
+
+    private String outOrderNo;
+
+}

+ 19 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgRegisterReq.java

@@ -0,0 +1,19 @@
+package com.kxs.common.cash.model.lg;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * LG 注册
+ *
+ * @author lixueqiang
+ * @date 2025/11/05
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LgRegisterReq extends LgCommonReq{
+
+    private String freelancerName;
+    private String mobilePhone;
+    private String idCardNo;
+}

+ 17 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/lg/LgRegisterRes.java

@@ -0,0 +1,17 @@
+package com.kxs.common.cash.model.lg;
+
+import lombok.Data;
+
+/**
+ * LG 注册
+ *
+ * @author lixueqiang
+ * @date 2025/11/05
+ */
+@Data
+public class LgRegisterRes{
+
+    private String freelancerId;
+    private String validateStatus;
+    private String remark;
+}

+ 29 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/params/IsvParams.java

@@ -0,0 +1,29 @@
+package com.kxs.common.cash.model.params;
+
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.kxs.common.cash.constant.CashConstants;
+
+/**
+ * 平台公共 参数
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+public abstract class IsvParams {
+
+    public static IsvParams factory(String ifCode, String paramsStr){
+
+        try {
+
+            //必须下划线命名,否则找不到
+            String[] split = ifCode.split(StrUtil.UNDERLINE);
+            return (IsvParams) JSONObject.parseObject(paramsStr, Class.forName(IsvParams.class.getPackage().getName() +
+                    StrUtil.DOT + split[0] + StrUtil.DOT + StrUtil.upperFirst(split[0]) + CashConstants.PUBLIC_PARAMS));
+
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException("IsvParams is not found");
+        }
+    }
+
+}

+ 48 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/params/hs/HsIsvParams.java

@@ -0,0 +1,48 @@
+package com.kxs.common.cash.model.params.hs;
+
+import com.kxs.common.cash.model.params.IsvParams;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+/**
+ * 盒子慧算配置参数
+ *
+ * @author 系统
+ * @since 2024-07-12 16:45:55
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class HsIsvParams extends IsvParams {
+
+
+    /**
+     * appId
+     */
+    private String appId;
+
+    /**
+     * 密钥
+     */
+    private String secret;
+
+    /**
+     * 签名类型
+     */
+    private String signType;
+
+    /**
+     * 网址
+     */
+    private String url;
+
+    /**
+     * 回调 URL
+     */
+    private String callbackUrl;
+
+
+
+
+}
+

+ 55 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/params/ldys/LdysIsvParams.java

@@ -0,0 +1,55 @@
+package com.kxs.common.cash.model.params.ldys;
+
+import com.kxs.common.cash.model.params.IsvParams;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+/**
+ * 联动优势配置参数
+ *
+ * @author 系统
+ * @since 2024-11-12 16:45:55
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LdysIsvParams extends IsvParams {
+
+    /**
+     * 代理商编号
+     */
+    private String appid;
+
+    /**
+     * 平台公钥
+     */
+    private String platformPublicKey;
+
+    /**
+     * 公钥
+     */
+    private String publicKey;
+
+    /**
+     * 私钥
+     */
+    private String privateKey;
+
+    /**
+     * 网关
+     */
+    private String url;
+
+    /**
+     * 回调 URL
+     */
+    private String callbackUrl;
+
+    /**
+     * 账户类型
+     */
+    private String accountType;
+
+
+}
+

+ 67 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/params/lg/LgIsvParams.java

@@ -0,0 +1,67 @@
+package com.kxs.common.cash.model.params.lg;
+
+import com.kxs.common.cash.model.params.IsvParams;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+/**
+ * 盒子慧算配置参数
+ *
+ * @author 系统
+ * @since 2024-07-12 16:45:55
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LgIsvParams extends IsvParams {
+
+
+    /**
+     * 机构号
+     */
+    private String orgCode;
+
+    /**
+     * 接口地址
+     */
+    private String url;
+
+    /**
+     * 文件上传地址
+     */
+    private String fileUploadUrl;
+
+    /**
+     * 公钥
+     */
+    private String serverPublicKey;
+    private String clientPublicKey;
+
+    /**
+     * 私钥
+     */
+    private String serverPrivateKey;
+    private String clientPrivateKey;
+
+    /**
+     * 公司编号
+     */
+    private String companyCode;
+
+    /**
+     * 租户编码
+     */
+    private String tenantCode;
+
+    /**
+     * 项目编码
+     */
+    private String projectCode;
+
+    /**
+     * 回调地址
+     */
+    private String notifyUrl;
+
+}
+

+ 43 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/params/sft/SftIsvParams.java

@@ -0,0 +1,43 @@
+package com.kxs.common.cash.model.params.sft;
+
+import com.kxs.common.cash.model.params.IsvParams;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+/**
+ * 盛付通配置参数
+ *
+ * @author 系统
+ * @since 2024-11-12 16:45:55
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SftIsvParams extends IsvParams {
+
+    /**
+     * 商户ID
+     */
+    private String mchId;
+
+    /**
+     * 密钥
+     */
+    private String mchKey;
+
+
+    /**
+     * 提现 URL
+     */
+    private String url;
+
+    /**
+     * 其他 URL
+     */
+    private String queryUrl;
+
+
+
+
+}
+

+ 57 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/params/yhs/YhsIsvParams.java

@@ -0,0 +1,57 @@
+package com.kxs.common.cash.model.params.yhs;
+
+import com.kxs.common.cash.model.params.IsvParams;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+/**
+ * 云汇算配置参数
+ *
+ * @author 系统
+ * @since 2024-11-12 16:45:55
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class YhsIsvParams extends IsvParams {
+
+    /**
+     * 企业后台创建的任务编码
+     */
+    private String taskNo;
+
+    /**
+     * 平台分配的企业编码
+     */
+    private String enterpriseNo;
+
+    /**
+     * 平台分配的渠道编码
+     */
+    private String channelNo;
+
+    /**
+     * 公钥
+     */
+    private String publicKey;
+
+    /**
+     * 私钥
+     */
+    private String privateKey;
+
+    /**
+     * 网关
+     */
+    private String url;
+
+    /**
+     * 回调 URL
+     */
+    private String callbackUrl;
+
+
+
+
+}
+

+ 42 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCashBackMessage.java

@@ -0,0 +1,42 @@
+package com.kxs.common.cash.model.sft;
+
+import com.kxs.common.cash.model.common.CashMessage;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 盛付通回调
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SftCashBackMessage extends CashMessage {
+
+
+    private String mch_id;
+    private BigDecimal total_fee;
+    private BigDecimal real_amount;
+    private BigDecimal tax_amount;
+    private String out_trade_no;
+    private String trade_no;
+    private BigDecimal tax_fee;
+    private BigDecimal add_fee;
+    private BigDecimal add_amount;
+    private String trade_state_desc;
+    private String trade_date;
+
+    /**
+     * FAIL—交易失败
+     * SUCCESS—交易成功
+     * REFUND—退票
+     */
+    private String trade_state;
+    private String nonce_str;
+    private String sign;
+
+
+}

+ 25 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCashCommRequest.java

@@ -0,0 +1,25 @@
+package com.kxs.common.cash.model.sft;
+
+import com.kxs.common.cash.model.common.CashRequest;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 提交请求公共参数
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SftCashCommRequest extends CashRequest {
+
+    private String mch_id;
+
+    private String action;
+
+    private String nonce_str;
+
+    private String sign;
+
+}

+ 21 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCashCommRes.java

@@ -0,0 +1,21 @@
+package com.kxs.common.cash.model.sft;
+
+import lombok.Data;
+
+/**
+ * 提现公共响应
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@Data
+public class SftCashCommRes {
+
+    /**
+     * SUCCESS 表示请求成功,FAIL 表示请求失败
+     */
+    private String status;
+    private String message;
+
+
+}

+ 33 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCashSubmitReq.java

@@ -0,0 +1,33 @@
+package com.kxs.common.cash.model.sft;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 提现申请
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SftCashSubmitReq extends SftCashCommRequest {
+
+
+    private Integer settlement_mode = 0;
+
+    private String trade_type = "trade";
+
+    private String invoice_trade_name = "服务费";
+
+    private Integer invoice_amount;
+
+    private String out_trade_no;
+
+    private String user_infos;
+
+
+
+}
+
+

+ 36 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCashSubmitRes.java

@@ -0,0 +1,36 @@
+package com.kxs.common.cash.model.sft;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 提现申请返回
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SftCashSubmitRes extends SftCashCommRes{
+
+    /**
+     * 当订单状态FAIL时为失败,无状态或其他状态请通过查询接口确认
+     * SUCCESS 提现成功
+     * FAIL 提现失败,
+     * PROCESSING 处理中
+     * RECEIVED接收成功
+     * 未知
+     */
+    private String trade_state;
+
+    /**
+     * 平台单号
+     */
+    private String out_trade_no;
+
+    /**
+     * 总金额,以分为单位,不允许包含任何字符
+     */
+    private Integer total_fee;
+
+}

+ 22 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCreateRequest.java

@@ -0,0 +1,22 @@
+package com.kxs.common.cash.model.sft;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 从业者数据提交
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SftCreateRequest extends SftCashCommRequest {
+
+    private String face_url;
+
+    private String back_url;
+
+    private String mobile;
+
+}

+ 21 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftCreateRes.java

@@ -0,0 +1,21 @@
+package com.kxs.common.cash.model.sft;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 获取签约地址
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SftCreateRes extends SftCashCommRes {
+
+
+    private String check_state;
+
+    private String check_state_desc;
+
+}

+ 18 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftQueryInfoReq.java

@@ -0,0 +1,18 @@
+package com.kxs.common.cash.model.sft;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 查询订单状态
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SftQueryInfoReq extends SftCashCommRequest {
+
+    private String out_trade_no;
+
+}

+ 33 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftQueryInfoRes.java

@@ -0,0 +1,33 @@
+package com.kxs.common.cash.model.sft;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 订单查询返回
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SftQueryInfoRes extends SftCashCommRes{
+
+    private BigDecimal total_fee;
+    private BigDecimal real_amount;
+    private String out_trade_no;
+    private BigDecimal tax_amount;
+    private BigDecimal add_amount;
+
+    /**
+     * FAIL—交易失败
+     * SUCCESS—交易成功
+     * REFUND—退票
+     */
+    private String trade_state;
+    private String trade_state_desc;
+
+
+}

+ 19 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftQuerySignStateReq.java

@@ -0,0 +1,19 @@
+package com.kxs.common.cash.model.sft;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 获取签约状态
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SftQuerySignStateReq extends SftCashCommRequest {
+
+    private String mobile;
+    private String id_no;
+
+}

+ 25 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftSignStateRes.java

@@ -0,0 +1,25 @@
+package com.kxs.common.cash.model.sft;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 获取签约结果
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SftSignStateRes extends SftCashCommRes {
+
+
+    private String state;
+
+    private String state_desc;
+
+    private String sign_url;
+
+    private String sign_date;
+
+}

+ 23 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftSignUrlRequest.java

@@ -0,0 +1,23 @@
+package com.kxs.common.cash.model.sft;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 获取签约地址
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SftSignUrlRequest extends SftCashCommRequest {
+
+
+    private String id_no;
+
+    private String mobile;
+
+    private String redirect_url;
+
+}

+ 23 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftSignUrlRes.java

@@ -0,0 +1,23 @@
+package com.kxs.common.cash.model.sft;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 获取签约地址
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SftSignUrlRes extends SftCashCommRes {
+
+
+    private String state;
+
+    private String state_desc;
+
+    private String sign_url;
+
+}

+ 33 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/sft/SftUserInfos.java

@@ -0,0 +1,33 @@
+package com.kxs.common.cash.model.sft;
+
+import lombok.Data;
+
+/**
+ * 盛付通 用户信息
+ *
+ * @author 没秃顶的码农
+ * @date 2025-03-21
+ */
+@Data
+public class SftUserInfos {
+
+
+    private String user_name;
+
+    private String id_no;
+
+    /**
+     * 分
+     */
+    private Integer amount;
+
+    private String bank_card;
+
+    private String mobile;
+
+    private String order_no;
+
+    private String remark;
+
+
+}

+ 63 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/CashSubmitReq.java

@@ -0,0 +1,63 @@
+package com.kxs.common.cash.model.yhs;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 提现申请
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class CashSubmitReq extends YhsCashCommRequest{
+
+    /**
+     * 企业后台创建的任务编码
+     */
+    private String taskNo;
+
+    /**
+     * 订单号
+     */
+    private String entOrderNo;
+    /**
+     * 收款人姓名
+     */
+    private String payeeName;
+    /**
+     * 收款人身份证
+     */
+    private String payeeIdCard;
+    /**
+     * 收款人电话
+     */
+    private String payeePhone;
+    /**
+     * 银行卡号
+     */
+    private String payeeAccount;
+    /**
+     * 	支付类型
+     * 100:银行卡
+     * 200:支付宝
+     * 300:微信
+     */
+    private String paymentModel = "100";
+    /**
+     * 金额(单位:分)
+     */
+    private BigDecimal amount;
+    /**
+     * 回调 URL, 自动填充无需赋值
+     */
+    private String callbackUrl;
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 32 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/CashSubmitRes.java

@@ -0,0 +1,32 @@
+package com.kxs.common.cash.model.yhs;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 提现申请返回
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@Data
+public class CashSubmitRes{
+
+    /**
+     * 订单号
+     */
+    private String entOrderNo;
+    /**
+     * 平台单号
+     */
+    private String pfOrderNo;
+    /**
+     * 申请时间
+     */
+    private String applyTime;
+    /**
+     * 当前路由出款渠道,开启渠道路由时返回
+     */
+    private String routePayChannelNo;
+
+}

+ 18 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/QueryInfoReq.java

@@ -0,0 +1,18 @@
+package com.kxs.common.cash.model.yhs;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 签约状态查询
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class QueryInfoReq extends YhsCashCommRequest{
+
+    private String idCard;
+
+}

+ 19 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/QueryInfoRes.java

@@ -0,0 +1,19 @@
+package com.kxs.common.cash.model.yhs;
+
+import lombok.Data;
+
+/**
+ * 签约状态查询
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@Data
+public class QueryInfoRes{
+
+    private String result;
+    private String signTime;
+    private String lack;
+
+
+}

+ 18 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/QueryYhsReq.java

@@ -0,0 +1,18 @@
+package com.kxs.common.cash.model.yhs;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 单笔提现结果查询
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class QueryYhsReq extends YhsCashCommRequest{
+
+    private String entOrderNo;
+
+}

+ 31 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/YhsBaseRequest.java

@@ -0,0 +1,31 @@
+package com.kxs.common.cash.model.yhs;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * 提交请求公共参数
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@Data
+@Builder
+public class YhsBaseRequest {
+
+
+
+    @NotBlank(message = "平台分配的企业编码")
+    private String enterpriseNo;
+
+
+    @NotBlank(message = "平台分配的渠道编码")
+    private String channelNo;
+
+    @NotBlank(message = "交易正文加密,并做BASE64编码")
+    private String body;
+
+
+
+}

+ 76 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/YhsCashBackMessage.java

@@ -0,0 +1,76 @@
+package com.kxs.common.cash.model.yhs;
+
+import com.kxs.common.cash.model.common.CashMessage;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 云汇算提现通知地址
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class YhsCashBackMessage extends CashMessage {
+
+
+    private String enterpriseNo;
+
+    private String channelNo;
+
+    private String sign;
+
+    private YhsBody body;
+
+    @Data
+    public static class YhsBody{
+
+        /**
+         * 申请时间
+         */
+        private String applyTime;
+
+        /**
+         * 订单号
+         */
+        private String entOrderNo;
+        /**
+         * 平台单号
+         */
+        private String refOrderNo;
+        /**
+         * 支付类型
+         * 100:银行卡
+         * 200:支付宝
+         */
+        private String paymentModel;
+        /**
+         * 金额(单位:分)
+         */
+        private BigDecimal amount;
+        /**
+         * 实扣个税(单位:分)
+         */
+        private BigDecimal actualTaxPayment;
+        /**
+         * 服务费(单位:分)
+         */
+        private BigDecimal fee;
+        /**
+         * 提现状态
+         * 100:待发放
+         * 200:交易中
+         * 300:交易成功
+         * 400:交易失败
+         * 500:交易终止
+         * 600:已退款
+         * 601:已退汇
+         */
+        private Integer status;
+
+        private String payMsg;
+    }
+}

+ 25 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/YhsCashCommRequest.java

@@ -0,0 +1,25 @@
+package com.kxs.common.cash.model.yhs;
+
+import com.kxs.common.cash.model.common.CashRequest;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 提交请求公共参数
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class YhsCashCommRequest extends CashRequest {
+
+    private String apiCode;
+
+    private String enterpriseNo;
+
+    private String channelNo;
+
+    private String timestamp;
+
+}

+ 21 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/YhsCashCommRes.java

@@ -0,0 +1,21 @@
+package com.kxs.common.cash.model.yhs;
+
+import lombok.Data;
+
+/**
+ * 提现公共响应
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@Data
+public class YhsCashCommRes<T> {
+
+    private T data;
+
+    private String ifCode;
+    private String code;
+    private String msg;
+
+
+}

+ 24 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/YhsCreateRequest.java

@@ -0,0 +1,24 @@
+package com.kxs.common.cash.model.yhs;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 从业者数据提交
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class YhsCreateRequest extends YhsCashCommRequest {
+
+    private String name;
+
+    private String idCard;
+
+    private String phone;
+
+    private String bankCard;
+
+}

+ 19 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/model/yhs/YhsFileDataRequest.java

@@ -0,0 +1,19 @@
+package com.kxs.common.cash.model.yhs;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 从业者数据提交
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@Data
+public class YhsFileDataRequest{
+
+    private String frontCertImg;
+
+    private String backCertImg;
+
+}

+ 83 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/CashService.java

@@ -0,0 +1,83 @@
+package com.kxs.common.cash.service;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.kxs.common.cash.handlers.CashMessageHandler;
+import com.kxs.common.cash.model.common.CashRequest;
+import com.kxs.common.cash.model.common.CommCashReq;
+import com.kxs.common.cash.model.common.CommQueryReq;
+import com.kxs.common.cash.model.yhs.YhsFileDataRequest;
+import com.kxs.common.core.exception.GlobalCustomerException;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.core.Ordered;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * 提现代付公共service
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+public interface CashService {
+
+    /**
+     * 提现发起
+     */
+    Boolean cash(CommCashReq req);
+
+    /**
+     * 查询提现结果
+     *
+     * @param req 要求
+     * @return {@link Boolean }
+     */
+    Boolean query(CommQueryReq req);
+
+    /**
+     * 进件
+     */
+    default Object create(CashRequest req, YhsFileDataRequest fileDataReq){
+        return Boolean.TRUE;
+    };
+
+    /**
+     * 查询进件状态
+     */
+    default Object queryInfo(CashRequest req){
+        return null;
+    };
+
+    /**
+     * 查询签约状态
+     */
+    default Object querySignUrl(CashRequest req){
+        return null;
+    };
+
+
+    /**
+     * 回调
+     */
+    String cashBack(HttpServletRequest request, String ifCode);
+
+    default CashMessageHandler getCashMessageHandler(String ifCode){
+        //必须下划线命名,否则找不到
+        String[] split = ifCode.split(StrUtil.UNDERLINE);
+        Map<String, CashMessageHandler> payMessageHandlerMap = SpringUtil
+                .getBeansOfType(CashMessageHandler.class);
+        Optional<CashMessageHandler> optional = payMessageHandlerMap.values()
+                .stream()
+                .filter(service -> service.support(split[0]))
+                .max(Comparator.comparingInt(Ordered::getOrder));
+
+        if (optional.isEmpty()) {
+            throw new GlobalCustomerException("DefaultCashMessageHandler error , not register");
+        }
+        return optional.get();
+    }
+
+
+}

+ 39 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/ConfigContextService.java

@@ -0,0 +1,39 @@
+package com.kxs.common.cash.service;
+
+import com.kxs.common.cash.model.params.IsvParams;
+import com.kxs.common.core.constant.SecurityConstants;
+import com.kxs.common.core.exception.GlobalCustomerException;
+import com.kxs.common.core.util.R;
+import com.kxs.common.core.util.RetOps;
+import com.kxs.system.api.feign.RemoteSysService;
+import com.kxs.system.api.model.SysInterfaceConfig;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 配置上下文服务
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@RequiredArgsConstructor
+@Slf4j
+public class ConfigContextService {
+
+    private final RemoteSysService remoteSysService;
+
+    public IsvParams getParams(String ifCode) {
+
+        try {
+            R<SysInterfaceConfig> configR = remoteSysService.loadByIfCode(ifCode, SecurityConstants.FROM_IN);
+            SysInterfaceConfig interfaceConfig = RetOps.of(configR)
+                    .getData()
+                    .orElseThrow(() -> new GlobalCustomerException("IsvParams get is null"));
+            return IsvParams.factory(ifCode, interfaceConfig.getParams());
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            throw new GlobalCustomerException(e.getMessage());
+        }
+    }
+
+}

+ 258 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/hs/HsCashService.java

@@ -0,0 +1,258 @@
+package com.kxs.common.cash.service.hs;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.lang.TypeReference;
+import cn.hutool.core.map.MapBuilder;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.kxs.common.cash.handlers.CashMessageHandler;
+import com.kxs.common.cash.model.common.CommCashReq;
+import com.kxs.common.cash.model.common.CommCashRes;
+import com.kxs.common.cash.model.common.CommQueryReq;
+import com.kxs.common.cash.model.hs.*;
+import com.kxs.common.cash.model.params.IsvParams;
+import com.kxs.common.cash.model.params.hs.HsIsvParams;
+import com.kxs.common.cash.service.CashService;
+import com.kxs.common.cash.service.ConfigContextService;
+import com.kxs.common.core.exception.GlobalCustomerException;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StreamUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.time.LocalDateTime;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 盒子慧算提现服务
+ *
+ * @author 没秃顶的码农
+ * @date 2024-07-23
+ */
+@RequiredArgsConstructor
+@Slf4j
+@Service
+public class HsCashService implements CashService {
+
+    private final ConfigContextService configContextService;
+
+    @Override
+    public Boolean cash(CommCashReq cashReq) {
+
+        if(StrUtil.isBlank(cashReq.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+
+        IsvParams isvParams = configContextService.getParams(cashReq.getIfCode());
+        if (isvParams instanceof HsIsvParams hsIsvParams) {
+
+            try {
+                HsCashRequest hsCashRequest = new HsCashRequest();
+                BeanUtil.copyProperties(cashReq, hsCashRequest);
+
+                Map<String, Object> requestParam = MapBuilder
+                        .create(new HashMap<String, Object>())
+                        .put("amount", cashReq.getAmount())
+                        .put("acctName", cashReq.getAcctName())
+                        .put("acctNo", cashReq.getAcctNo())
+                        .put("acctIdCard", cashReq.getAcctIdCard())
+                        .put("outOrderNo", cashReq.getOutOrderNo())
+                        .put("bankName", cashReq.getBankName())
+                        .put("callbackUrl", hsIsvParams.getCallbackUrl())
+                        .put("acctMobile", cashReq.getAcctMobile()).map();
+
+                String response = send(hsIsvParams, requestParam);
+                HsCashCommRes<HsCashPayRes> commRes = JSONUtil.toBean(response, new TypeReference<>() {}, true);
+                if(commRes.getCode() != 0){
+                    throw new GlobalCustomerException(hsCashRequest.getAcctName() + commRes.getErrorDesc());
+                }
+                return Boolean.TRUE;
+
+            } catch (Exception e) {
+                log.error("发起提现任务失败{}", JSONUtil.toJsonStr(cashReq), e);
+                throw new GlobalCustomerException("发起提现任务失败,请联系管理员");
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    @Override
+    public Boolean query(CommQueryReq req) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+
+        IsvParams isvParams = configContextService.getParams(req.getIfCode());
+        if (isvParams instanceof HsIsvParams hsIsvParams) {
+
+            try {
+                HsQueryRequest queryRequest = new HsQueryRequest();
+                BeanUtil.copyProperties(req, queryRequest);
+
+                Map<String, Object> requestParam = MapBuilder
+                        .create(new HashMap<String, Object>())
+                        .put("outOrderNo", req.getOutOrderNo()).map();
+                String response = send(hsIsvParams, requestParam);
+                HsCashCommRes<String> commRes = JSONUtil.toBean(response, new TypeReference<>() {}, true);
+                if(commRes.getCode() != 0){
+                    throw new GlobalCustomerException(queryRequest.getOutOrderNo() + commRes.getErrorDesc());
+                }
+                JSONObject dataObj = JSONUtil.parseObj(commRes.getData());
+                String list = dataObj.getStr("list");
+                List<HsCashBackMessage> queryResList = JSONUtil.toList(list, HsCashBackMessage.class);
+                if(!queryResList.isEmpty()){
+                    queryResList.forEach(item -> {
+                        if(item.getOutOrderNo().equals(req.getOutOrderNo())){
+                            //获取处理的handler方法
+                            CashMessageHandler cashMessageHandler = getCashMessageHandler(req.getIfCode());
+                            CommCashRes commCashRes = new CommCashRes();
+                            commCashRes.setOutOrderNo(item.getOutOrderNo());
+                            if(item.getState() == 0){
+                                commCashRes.setStatus(1);
+                                commCashRes.setRemark("交易成功");
+                            }else if(item.getState() == 1 || item.getState() == 5 || item.getState() == 6 || item.getState() == 7){
+                                commCashRes.setStatus(2);
+                                commCashRes.setRemark("处理中");
+                            }else{
+                                //失败
+                                commCashRes.setStatus(0);
+                                commCashRes.setRemark(StrUtil.isNotBlank(item.getErrorMsg()) ? item.getErrorMsg() : "交易失败");
+                            }
+                            cashMessageHandler.handle(item);
+                        }
+                    });
+
+                }
+
+
+                return Boolean.TRUE;
+
+            } catch (Exception e) {
+                log.error("盒子查询提现订单失败{}", JSONUtil.toJsonStr(req), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    @Override
+    public String cashBack(HttpServletRequest request, String ifCode) {
+
+        IsvParams isvParams = configContextService.getParams(ifCode);
+        if (isvParams instanceof HsIsvParams hsIsvParams) {
+
+            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+
+                StreamUtils.copy(request.getInputStream(), baos);
+                String bodyStr = baos.toString();
+                HsCashBackMessage cashBackMessage = JSONUtil.toBean(bodyStr, HsCashBackMessage.class);
+                log.info("盒子提现回调接口:{}", JSONUtil.toJsonStr(cashBackMessage));
+                String timestamp = request.getHeader("X-Timestamp");
+                //验证签名
+                if (!checkSign(cashBackMessage, hsIsvParams, timestamp)) {
+                    throw new GlobalCustomerException("签名错误");
+                }
+                //获取处理的handler方法
+                CashMessageHandler cashMessageHandler = getCashMessageHandler(ifCode);
+
+                CommCashRes commCashRes = new CommCashRes();
+                commCashRes.setOutOrderNo(cashBackMessage.getOutOrderNo());
+                if(cashBackMessage.getState() == 0){
+                    commCashRes.setStatus(1);
+                    commCashRes.setRemark("交易成功");
+                }else if(cashBackMessage.getState() == 1 || cashBackMessage.getState() == 5 || cashBackMessage.getState() == 6 || cashBackMessage.getState() == 7){
+                    commCashRes.setStatus(2);
+                    commCashRes.setRemark("处理中");
+                }else{
+                    //失败
+                    commCashRes.setStatus(0);
+                    commCashRes.setRemark(StrUtil.isNotBlank(cashBackMessage.getErrorMsg()) ? cashBackMessage.getErrorMsg() : "交易失败");
+                }
+                return cashMessageHandler.handle(cashBackMessage);
+            } catch (Exception e) {
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+
+        return "{" +
+                " \"code\": 0\n" +
+                "}";
+    }
+
+    private String send(HsIsvParams hsIsvParams, Map<String, Object> requestParam) throws Exception {
+
+
+
+        String timestamp = LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN);
+        // 计算签名
+        String requestJsonStr = generateRequestBody(requestParam);
+        String waitSignStr = timestamp + hsIsvParams.getSecret() + requestJsonStr;
+        String sign = generateSignature(waitSignStr, hsIsvParams.getSignType());
+
+        return HttpRequest.post(hsIsvParams.getUrl())
+                .header("X-Up-AppId", hsIsvParams.getAppId())
+                .header("X-Timestamp", timestamp)
+                .header("X-Sign-Type", hsIsvParams.getSignType())
+                .header("X-Sign", sign)
+                .body(JSONUtil.toJsonStr(requestParam))
+                .timeout(50000)
+                .execute().body();
+
+    }
+
+
+    /**
+     * 验证签名
+     *
+     * @param cashBackMessage 回调参数
+     * @param hsIsvParams       isv参数
+     * @param timestamp       时间
+     * @return {@link Boolean}
+     */
+    private Boolean checkSign(HsCashBackMessage cashBackMessage, HsIsvParams hsIsvParams, String timestamp) throws Exception {
+        String sign = cashBackMessage.getSign();
+        Map<String, Object> requestParam = BeanUtil.beanToMap(cashBackMessage);
+        requestParam.remove("sign");
+
+        // 计算签名
+        String requestJsonStr = generateRequestBody(requestParam);
+        String waitSignStr = timestamp + hsIsvParams.getSecret() + requestJsonStr;
+        String checkSign = generateSignature(waitSignStr, hsIsvParams.getSignType());
+
+        return ObjectUtil.equals(sign, checkSign);
+    }
+
+    private String generateRequestBody(Map<String, Object> requestParam) throws Exception {
+        ObjectMapper objectMapper = new ObjectMapper();
+
+        return objectMapper.writeValueAsString(requestParam);
+    }
+
+
+    /**
+     * 生成签名
+     *
+     */
+    private String generateSignature(String waitSignStr, String signType) throws Exception {
+        MessageDigest messageDigest = MessageDigest.getInstance(signType);
+        messageDigest.reset();
+        messageDigest.update(waitSignStr.getBytes(StandardCharsets.UTF_8));
+        byte[] signBytes = messageDigest.digest();
+        return Base64.getEncoder().encodeToString(signBytes);
+    }
+}

+ 357 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/ldys/LdysCashService.java

@@ -0,0 +1,357 @@
+package com.kxs.common.cash.service.ldys;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.codec.Base64Decoder;
+import cn.hutool.core.codec.Base64Encoder;
+import cn.hutool.core.lang.TypeReference;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.Mode;
+import cn.hutool.crypto.Padding;
+import cn.hutool.crypto.asymmetric.KeyType;
+import cn.hutool.crypto.asymmetric.RSA;
+import cn.hutool.crypto.symmetric.AES;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
+import com.kxs.common.cash.handlers.CashMessageHandler;
+import com.kxs.common.cash.model.common.CashRequest;
+import com.kxs.common.cash.model.common.CommCashReq;
+import com.kxs.common.cash.model.common.CommCashRes;
+import com.kxs.common.cash.model.common.CommQueryReq;
+import com.kxs.common.cash.model.ldys.*;
+import com.kxs.common.cash.model.params.IsvParams;
+import com.kxs.common.cash.model.params.ldys.LdysIsvParams;
+import com.kxs.common.cash.service.CashService;
+import com.kxs.common.cash.service.ConfigContextService;
+import com.kxs.common.core.exception.GlobalCustomerException;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StreamUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+
+/**
+ * 联动优势提现实现
+ *
+ * @author 没秃顶的码农
+ * @date 2025-03-21
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class LdysCashService implements CashService {
+
+    private final ConfigContextService configContextService;
+
+    @Override
+    public Boolean cash(CommCashReq req) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+        IsvParams commIsvParams = configContextService.getParams(req.getIfCode());
+        if (commIsvParams instanceof LdysIsvParams isvParams) {
+            try {
+                LdysCashSubmitReq cashRequest = new LdysCashSubmitReq();
+
+                //提现用户信息
+                cashRequest.setOrderNo(req.getOutOrderNo());
+                cashRequest.setAmount(NumberUtil.div(req.getAmount(), BigDecimal.valueOf(100)));
+                cashRequest.setAccountType(isvParams.getAccountType());
+                cashRequest.setSettleType("TO_PUBLIC_UNINCORPORATED");
+                cashRequest.setAccountNo(req.getAcctNo());
+                cashRequest.setAccountName(req.getAcctName());
+                cashRequest.setBankName(req.getBankName());
+                cashRequest.setPhoneNo(req.getAcctMobile());
+                cashRequest.setIdCard(req.getAcctIdCard());
+                cashRequest.setEntrustAmount(NumberUtil.div(cashRequest.getAmount(), BigDecimal.valueOf(0.92)));
+                log.info("联动优势提现信息{}", JSON.toJSONString(cashRequest));
+
+                LdysCashCommReq ldysCashCommReq = encryptByApi(isvParams, cashRequest);
+                ldysCashCommReq.setAppId(isvParams.getAppid());
+                cashRequest.setTimestamp(System.currentTimeMillis() / 1000 + "");
+
+                String sign = createSign(isvParams, ldysCashCommReq);
+                ldysCashCommReq.setSign(sign);
+
+
+                isvParams.setUrl(isvParams.getUrl() + "/submitWithdraw");
+                String response = send(isvParams, ldysCashCommReq);
+                LdysCashCommRes<?> commRes = JSONUtil.toBean(response, new TypeReference<>() {}, true);
+                log.info("联动优势发起提现返回信息{}", response);
+                if(!"00".equals(commRes.getCode())){
+                    throw new GlobalCustomerException(commRes.getMsg());
+                }
+                return Boolean.TRUE;
+
+            }catch (Exception e) {
+                log.error("联动优势发起提现任务失败{}", JSON.toJSONString(req), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    @Override
+    public Boolean query(CommQueryReq req) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+        IsvParams commIsvParams = configContextService.getParams(req.getIfCode());
+        if (commIsvParams instanceof LdysIsvParams isvParams) {
+            try {
+                LdysCashQueryReq cashRequest = new LdysCashQueryReq();
+
+                //提现用户信息
+                cashRequest.setOrderNo(req.getOutOrderNo());
+
+                LdysCashCommReq ldysCashCommReq = encryptByApi(isvParams, cashRequest);
+                ldysCashCommReq.setAppId(isvParams.getAppid());
+                ldysCashCommReq.setTimestamp(System.currentTimeMillis() / 1000 + "");
+
+                String sign = createSign(isvParams, ldysCashCommReq);
+                ldysCashCommReq.setSign(sign);
+
+
+                isvParams.setUrl(isvParams.getUrl() + "/withdrawResultQuery");
+                String response = send(isvParams, ldysCashCommReq);
+                LdysCashNotifyRes commRes = JSONUtil.toBean(response, LdysCashNotifyRes.class);
+                if(!"00".equals(commRes.getCode())){
+                    throw new GlobalCustomerException(commRes.getMsg());
+                }
+                String dataStr = decryptByApi(isvParams, commRes);
+                log.info("联动优势查询订单返回信息{}", dataStr);
+
+                LdysCashQueryRes data = JSONUtil.toBean(dataStr, LdysCashQueryRes.class);
+
+                //获取处理的handler方法
+                CashMessageHandler cashMessageHandler = getCashMessageHandler(req.getIfCode());
+
+                CommCashRes commCashRes = new CommCashRes();
+                commCashRes.setIfCode(req.getIfCode());
+                commCashRes.setOutOrderNo(data.getOrderNo());
+                switch (data.getStatus()) {
+                    case "WAIT_AUDIT","WAIT_PAY" -> {
+                        commCashRes.setStatus(2);
+                        commCashRes.setRemark("处理中");
+                    }
+                    case "SUCCESS" -> {
+                        commCashRes.setStatus(1);
+                        commCashRes.setRemark("交易成功");
+                    }
+                    default -> {
+                        commCashRes.setStatus(0);
+                        commCashRes.setRemark(StrUtil.isNotBlank(data.getFailReason()) ? data.getFailReason() : "交易失败");
+                    }
+                }
+                cashMessageHandler.handle(commCashRes);
+                commCashRes.setAmount(NumberUtil.mul(data.getAmount(), 100));
+                commCashRes.setTaxAmount(NumberUtil.mul(data.getWithdrawTaxFee(), 100));
+                return Boolean.TRUE;
+            }catch (Exception e) {
+                log.error("联动优势查询订单任务失败{}", JSON.toJSONString(req), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    @Override
+    public String cashBack(HttpServletRequest request, String ifCode) {
+        
+        IsvParams commIsvParams = configContextService.getParams(ifCode);
+        if (commIsvParams instanceof LdysIsvParams isvParams) {
+
+            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+
+                StreamUtils.copy(request.getInputStream(), baos);
+                String bodyStr = baos.toString();
+
+
+                LdysCashNotifyRes commRes = JSONUtil.toBean(bodyStr, LdysCashNotifyRes.class);
+
+                if(!Objects.equals(commRes.getAppId(), isvParams.getAppid())){
+                    throw new GlobalCustomerException("代理商编号不匹配");
+                }
+                //验证签名
+                if (!verify(isvParams, commRes)) {
+                    throw new GlobalCustomerException("签名错误");
+                }
+                String content = decryptByApi(isvParams, commRes);
+                log.info("联动优势{}回调接口:{}", ifCode, content);
+
+                LdysCashQueryRes data = JSONUtil.toBean(content, LdysCashQueryRes.class);
+
+                //获取处理的handler方法
+                CashMessageHandler cashMessageHandler = getCashMessageHandler(ifCode);
+
+                CommCashRes commCashRes = new CommCashRes();
+                commCashRes.setIfCode(ifCode);
+                commCashRes.setOutOrderNo(data.getOrderNo());
+                switch (data.getStatus()) {
+                    case "WAIT_AUDIT","WAIT_PAY" -> {
+                        commCashRes.setStatus(2);
+                        commCashRes.setRemark("处理中");
+                    }
+                    case "SUCCESS" -> {
+                        commCashRes.setStatus(1);
+                        commCashRes.setRemark("交易成功");
+                    }
+                    default -> {
+                        commCashRes.setStatus(0);
+                        commCashRes.setRemark(StrUtil.isNotBlank(data.getFailReason()) ? data.getFailReason() : "交易失败");
+                    }
+                }
+                commCashRes.setAmount(NumberUtil.mul(data.getAmount(), 100));
+                commCashRes.setTaxAmount(NumberUtil.mul(data.getWithdrawTaxFee(), 100));
+                return cashMessageHandler.handle(commCashRes);
+            } catch (Exception e) {
+                log.error("联动优势回调接口异常", e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+
+        return "OK";
+    }
+
+
+
+    private String send(LdysIsvParams isvParams, CashRequest req) {
+
+        String response = HttpRequest.post(isvParams.getUrl())
+                .body(JSONUtil.toJsonStr(req))
+                .timeout(50000)
+                .execute().body();
+        log.info("联动优势提现请求参数{},接口{},返回{}", JSONUtil.toJsonStr(req), isvParams.getUrl(), response);
+        return response;
+
+    }
+
+
+    /**
+     * 加密数据
+     */
+    private LdysCashCommReq encryptByApi(LdysIsvParams isvParams, LdysCashCommReq req) {
+
+        String randomKey = RandomUtil.randomString(RandomUtil.BASE_CHAR.toUpperCase() + RandomUtil.BASE_CHAR_NUMBER, 16);
+        String encryptData = encryptToBase64(randomKey, JSON.toJSONString(req));
+        RSA rsa = new RSA(null, StrUtil.cleanBlank(isvParams.getPlatformPublicKey()));
+        String encryptRandomKey = rsa.encryptBase64(randomKey, KeyType.PublicKey);
+
+        LdysCashCommReq ldysCashCommReq = new LdysCashCommReq();
+        ldysCashCommReq.setData(encryptData);
+        ldysCashCommReq.setEncryptKey(encryptRandomKey);
+        return ldysCashCommReq;
+    }
+
+    /**
+     * 解密数据
+     */
+    private String decryptByApi(LdysIsvParams isvParams, LdysCashNotifyRes res) {
+        String decryptRandomKey;
+        try {
+            RSA rsa = new RSA(StrUtil.cleanBlank(isvParams.getPrivateKey()), null);
+            decryptRandomKey = rsa.decryptStr(res.getEncryptKey(), KeyType.PrivateKey);
+        } catch (Exception e) {
+            throw new RuntimeException("解密失败");
+        }
+        return decryptFromBase64(decryptRandomKey, res.getData());
+    }
+
+    /**
+     * AES对称加密,填充模式:PKCS5Padding
+     */
+    public static String encryptToBase64(String key, String content) {
+        AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, key.getBytes(StandardCharsets.UTF_8));
+        return aes.encryptBase64(content);
+    }
+
+    /**
+     * 根据公钥进行签名验签
+     */
+    private boolean verify(LdysIsvParams isvParams, LdysCashNotifyRes res) {
+
+        String sign = res.getSign();
+
+        Map<String, Object> map = BeanUtil.beanToMap(res, false, true);
+
+        map.remove("success");
+        map.remove("sign");
+        String content = sortStringByMap(map);
+        try {
+            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Decoder.decode(isvParams.getPlatformPublicKey()));
+            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+            PublicKey pubKey = keyFactory.generatePublic(keySpec);
+            Signature signature = Signature.getInstance("SHA256WithRSA");
+            signature.initVerify(pubKey);
+            signature.update(content.getBytes(StandardCharsets.UTF_8));
+            return signature.verify(Base64Decoder.decode(String.valueOf(sign)));
+        } catch (Exception e) {
+            log.error("异常{}{}", e, e.getStackTrace());
+        }
+        return false;
+    }
+
+    /**
+     * 生成签名
+     */
+    private String createSign(LdysIsvParams isvParams, LdysCashCommReq req) {
+        Map<String, Object> stringObjectMap = BeanUtil.beanToMap(req, false, true);
+        String content = sortStringByMap(stringObjectMap);
+        try {
+            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64Decoder.decode(StrUtil.cleanBlank(isvParams.getPrivateKey())));
+            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+            PrivateKey priKey = keyFactory.generatePrivate(keySpec);
+            Signature signature = Signature.getInstance("SHA256WithRSA");
+            signature.initSign(priKey);
+            signature.update(content.getBytes(StandardCharsets.UTF_8));
+            byte[] signed = signature.sign();
+            return Base64Encoder.encode(signed);
+        } catch (Exception e) {
+            log.error("异常{}{}", e, e.getStackTrace());
+        }
+        return null;
+    }
+
+    /**
+     * 加密
+     */
+    private static String decryptFromBase64(String key, String content) {
+        AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, key.getBytes(StandardCharsets.UTF_8));
+        return aes.decryptStr(content);
+    }
+
+    /**
+     * 按映射排序字符串
+     *
+     * @param map map
+     * @return {@link String}
+     */
+    private String sortStringByMap(Map<String, Object> map) {
+        TreeMap<String, Object> treeMap = new TreeMap<>(map);
+        StringBuilder strBuilder = new StringBuilder();
+        for (Map.Entry<String, Object> entry : treeMap.entrySet()) {
+            if (!strBuilder.isEmpty()) {
+                strBuilder.append("&");
+            }
+            strBuilder.append(entry.getKey()).append("=").append(entry.getValue());
+        }
+        return strBuilder.toString();
+    }
+}

+ 417 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/lg/LgCashService.java

@@ -0,0 +1,417 @@
+package com.kxs.common.cash.service.lg;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.google.common.base.Joiner;
+import com.kxs.common.cash.handlers.CashMessageHandler;
+import com.kxs.common.cash.model.common.CashRequest;
+import com.kxs.common.cash.model.common.CommCashReq;
+import com.kxs.common.cash.model.common.CommCashRes;
+import com.kxs.common.cash.model.common.CommQueryReq;
+import com.kxs.common.cash.model.lg.*;
+import com.kxs.common.cash.model.params.IsvParams;
+import com.kxs.common.cash.model.params.lg.LgIsvParams;
+import com.kxs.common.cash.model.yhs.YhsFileDataRequest;
+import com.kxs.common.cash.service.CashService;
+import com.kxs.common.cash.service.ConfigContextService;
+import com.kxs.common.cash.utils.LgRSAEncryptUtils;
+import com.kxs.common.cash.utils.LgRSASignatureUtils;
+import com.kxs.common.core.exception.GlobalCustomerException;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.compress.utils.IOUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StreamUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.net.URL;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * 乐工提现实现
+ *
+ * @author 没秃顶的码农
+ * @date 2025-03-21
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class LgCashService implements CashService {
+
+    private final ConfigContextService configContextService;
+
+    @Override
+    public Boolean cash(CommCashReq req) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+        IsvParams commIsvParams = configContextService.getParams(req.getIfCode());
+        if (commIsvParams instanceof LgIsvParams isvParams) {
+
+            try {
+                LgCashSubmitReq cashRequest = new LgCashSubmitReq();
+                //公共字段
+                cashRequest.setProjectCode(isvParams.getProjectCode());
+                cashRequest.setNotifyUrl(isvParams.getNotifyUrl());
+                cashRequest.setOutOrderNo(req.getOutOrderNo());
+                cashRequest.setName(req.getAcctName());
+                cashRequest.setIdCard(req.getAcctIdCard());
+                cashRequest.setAccountNo(req.getAcctNo());
+                cashRequest.setAmount(NumberUtil.div(req.getAmount(), 100));
+                cashRequest.setRemark(req.getOutOrderNo());
+
+                String response = send(isvParams, cashRequest, "settlement/settleApi/pay");
+                JSONUtil.toBean(response, LgCashSubmitRes.class);
+                return Boolean.TRUE;
+
+            }catch (Exception e) {
+                log.error("乐工发起提现任务失败{}", JSONUtil.toJsonStr(req), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    @Override
+    public Object create(CashRequest req, YhsFileDataRequest fileDataReq) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+
+        IsvParams commIsvParams = configContextService.getParams(req.getIfCode());
+        if (commIsvParams instanceof LgIsvParams isvParams && req instanceof LgRegisterReq createRequest) {
+
+            try {
+
+                createRequest.setCompanyCode(isvParams.getCompanyCode());
+                String response = send(isvParams, createRequest, "member/freelancerSpecialApi/register");
+                LgRegisterRes commRes = JSONUtil.toBean(response, LgRegisterRes.class);
+                if("2".equals(commRes.getValidateStatus())){
+                    throw new GlobalCustomerException(commRes.getRemark());
+                }
+
+                //上传身份证
+
+                String data = upload(isvParams, fileDataReq.getFrontCertImg());
+                JSONObject entries = JSONUtil.parseObj(data);
+                String frontId = entries.getStr("fileId");
+
+                data = upload(isvParams, fileDataReq.getBackCertImg());
+                entries = JSONUtil.parseObj(data);
+                String backId = entries.getStr("fileId");
+
+                LgIdCardAuthReq idCardAuthReq = new LgIdCardAuthReq();
+                idCardAuthReq.setFreelancerId(commRes.getFreelancerId());
+                idCardAuthReq.setFrontImgId(frontId);
+                idCardAuthReq.setBackImgId(backId);
+                send(isvParams, idCardAuthReq, "member/freelancerApi/idCardAuth");
+
+                return commRes.getFreelancerId();
+            } catch (Exception e) {
+                log.error("创客{}上传身份证乐工失败", createRequest.getMobilePhone(), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        throw new GlobalCustomerException("未查询到通道配置参数");
+    }
+
+    /**
+     * 绑定银行卡
+     */
+    @Override
+    public Object queryInfo(CashRequest req) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+
+        IsvParams commIsvParams = configContextService.getParams(req.getIfCode());
+        if (commIsvParams instanceof LgIsvParams isvParams && req instanceof LgAddBankCardReq bankCardReq) {
+
+            try {
+
+                bankCardReq.setCompanyCode(isvParams.getCompanyCode());
+                return send(isvParams, bankCardReq, "member/freelancerApi/addBankCard");
+            } catch (Exception e) {
+                log.error("创客{}绑定银行卡失败", bankCardReq.getIdCardNo(), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        throw new GlobalCustomerException("未查询到通道配置参数");
+    }
+
+
+
+    @Override
+    public Boolean query(CommQueryReq req) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+        IsvParams commIsvParams = configContextService.getParams(req.getIfCode());
+        if (commIsvParams instanceof LgIsvParams isvParams) {
+
+            try {
+                LgQueryInfoReq queryReq = new LgQueryInfoReq();
+                queryReq.setOutOrderNo(req.getOutOrderNo());
+
+                String response = send(isvParams, queryReq, "settlement/settleApi/query");
+                LgCashSubmitRes commRes = JSONUtil.toBean(response, LgCashSubmitRes.class);
+                log.info("乐工查询订单信息{}", response);
+
+                //获取处理的handler方法
+                CashMessageHandler cashMessageHandler = getCashMessageHandler(req.getIfCode());
+
+                CommCashRes commCashRes = new CommCashRes();
+                commCashRes.setIfCode(req.getIfCode());
+                commCashRes.setOutOrderNo(commRes.getOutOrderNo());
+                switch (commRes.getStatus()) {
+                    case "P" -> {
+                        commCashRes.setStatus(2);
+                        commCashRes.setRemark("处理中");
+                    }
+                    case "S" -> {
+                        commCashRes.setStatus(1);
+                        commCashRes.setRemark("交易成功");
+                    }
+                    default -> {
+                        commCashRes.setStatus(0);
+                        commCashRes.setRemark(StrUtil.isNotBlank(commRes.getDesc()) ? commRes.getDesc() : "交易失败");
+                    }
+                }
+
+                commCashRes.setAmount(commRes.getAmount());
+                commCashRes.setTaxAmount(commRes.getInComeTax());
+
+                cashMessageHandler.handle(commCashRes);
+
+                return Boolean.TRUE;
+            }catch (Exception e) {
+                log.error("乐工查询订单任务失败{}", JSONUtil.toJsonStr(req), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    @Override
+    public String cashBack(HttpServletRequest request, String ifCode) {
+
+        IsvParams commIsvParams = configContextService.getParams(ifCode);
+        if (commIsvParams instanceof LgIsvParams isvParams) {
+
+            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+
+                StreamUtils.copy(request.getInputStream(), baos);
+                String bodyStr = baos.toString();
+                log.info("乐工{}回调接口:{}", ifCode, bodyStr);
+
+                LgApiBaseRes baseRes = JSONUtil.toBean(bodyStr, LgApiBaseRes.class);
+
+                //验证签名
+                if (!checkSign(isvParams, bodyStr)) {
+                    throw new GlobalCustomerException("签名错误");
+                }
+                String decrypt = LgRSAEncryptUtils.decrypt(baseRes.getBody().getData(), isvParams.getClientPrivateKey());
+                log.info("乐工{}回调解密接口:{}", ifCode, decrypt);
+
+                LgCashSubmitRes commRes = JSONUtil.toBean(decrypt, LgCashSubmitRes.class);
+                //获取处理的handler方法
+                CashMessageHandler cashMessageHandler = getCashMessageHandler(ifCode);
+
+                CommCashRes commCashRes = new CommCashRes();
+                commCashRes.setIfCode(ifCode);
+                commCashRes.setOutOrderNo(commRes.getOutOrderNo());
+                switch (commRes.getStatus()) {
+                    case "P" -> {
+                        commCashRes.setStatus(2);
+                        commCashRes.setRemark("处理中");
+                    }
+                    case "S" -> {
+                        commCashRes.setStatus(1);
+                        commCashRes.setRemark("交易成功");
+                    }
+                    default -> {
+                        commCashRes.setStatus(0);
+                        commCashRes.setRemark(StrUtil.isNotBlank(commRes.getDesc()) ? commRes.getDesc() : "交易失败");
+                    }
+                }
+
+                commCashRes.setAmount(NumberUtil.mul(commRes.getAmount(), 100));
+                commCashRes.setTaxAmount(NumberUtil.mul(commRes.getInComeTax(), 100));
+
+                return cashMessageHandler.handle(commCashRes);
+            } catch (Exception e) {
+                log.error("乐工回调接口异常", e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+
+        return "SUCCESS";
+    }
+
+
+
+    private String send(LgIsvParams isvParams, CashRequest req, String url) {
+
+        req.setIfCode(null);
+        LgCommHead header = new LgCommHead();
+        header.setNonce(RandomUtil.randomString(32));
+        header.setOrgCode(isvParams.getOrgCode());
+        header.setRequestId(RandomUtil.randomString(32));
+        header.setRequestTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
+        header.setTenantCode(isvParams.getTenantCode());
+        LgApiReqBody body = new LgApiReqBody();
+        // 加密
+        String jsonStr = JSONUtil.toJsonStr(req);
+        body.setData(LgRSAEncryptUtils.encrypt(jsonStr, isvParams.getServerPublicKey()));
+        // 加签
+        String signContent = createSign(header, body);
+        String sign = LgRSASignatureUtils.sign(isvParams.getClientPrivateKey(), signContent);
+        header.setSign(sign);
+        LgApiBaseReq baseReq = new LgApiBaseReq();
+        baseReq.setHead(header);
+        baseReq.setBody(body);
+        log.info("乐工请求参数{},url:{}", JSONUtil.toJsonStr(req), url);
+        String response = HttpRequest.post(isvParams.getUrl() + url)
+                .body(JSONUtil.toJsonStr(baseReq))
+                .timeout(50000)
+                .execute().body();
+
+        // 解密
+        LgApiBaseRes baseRes = JSONUtil.toBean(response, LgApiBaseRes.class);
+        if(!"00000".equals(baseRes.getBody().getCode())){
+            log.error("乐工请求错误返回参数{},url:{}", response, url);
+            throw new GlobalCustomerException(baseRes.getBody().getMsg());
+        }
+        //验证签名
+        if (!checkSign(isvParams, response)) {
+            throw new GlobalCustomerException("签名错误");
+        }
+        String data = baseRes.getBody().getData();
+        String decrypt = LgRSAEncryptUtils.decrypt(data, isvParams.getClientPrivateKey());
+        log.info("乐工返回参数{},url:{}", decrypt, url);
+        return decrypt;
+    }
+
+    private String upload(LgIsvParams isvParams, String fileUrl) throws Exception {
+
+        LgCommHead header = new LgCommHead();
+        header.setNonce(RandomUtil.randomString(32));
+        header.setOrgCode(isvParams.getOrgCode());
+        header.setRequestId(RandomUtil.randomString(32));
+        header.setRequestTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
+        header.setTenantCode(isvParams.getTenantCode());
+
+
+        URL url = new URL(fileUrl);
+        byte[] byteArray = IOUtils.toByteArray(url.openStream());
+
+
+        LgFileUploadReq uploadReq = new LgFileUploadReq();
+        String fileType = StrUtil.subAfter(fileUrl, StrUtil.DOT, true);
+        uploadReq.setFileName("frontCertImg." + fileType);
+        uploadReq.setFileHash(DigestUtils.md5Hex(byteArray));
+        uploadReq.setFileType(1);
+
+        LgApiReqBody body = new LgApiReqBody();
+        // 加密
+        String jsonStr = JSONUtil.toJsonStr(uploadReq);
+        body.setData(LgRSAEncryptUtils.encrypt(jsonStr, isvParams.getServerPublicKey()));
+        // 加签
+        String signContent = createSign(header, body);
+        String sign = LgRSASignatureUtils.sign(isvParams.getClientPrivateKey(), signContent);
+        header.setSign(sign);
+
+        String response = HttpRequest.post(isvParams.getUrl() + "sys/file/upload/file")
+                .form("file", byteArray, uploadReq.getFileName())
+                .form("head", JSONUtil.toJsonStr(header))
+                .form("body", JSONUtil.toJsonStr(body))
+                .timeout(100000)
+                .execute().body();
+
+        // 解密
+        LgApiBaseRes baseRes = JSONUtil.toBean(response, LgApiBaseRes.class);
+        if(!"00000".equals(baseRes.getBody().getCode())){
+            throw new GlobalCustomerException(baseRes.getBody().getMsg());
+        }
+        String data = baseRes.getBody().getData();
+        String decrypt = LgRSAEncryptUtils.decrypt(data, isvParams.getClientPrivateKey());
+        return decrypt;
+
+    }
+
+    /**
+     * 所有参数按照字段名的 ascii  码从小到大排序后使用 QueryString 的格式(即
+     * key1=value1&key2=value2…)拼接而成
+     */
+    private String createSign(Object head, Object body) {
+
+        Map<String, Object> headMap = BeanUtil.beanToMap(head);
+        Map<String, Object> bodyMap = BeanUtil.beanToMap(body);
+        TreeMap<String, Object> mergedMap = new TreeMap<>();
+
+        // 剔除空值字段
+        for (String key : headMap.keySet()) {
+            if ("sign".equals(key)) {
+                continue;
+            }
+            Object value = headMap.get(key);
+            if (value != null && StrUtil.isNotBlank(value.toString())) {
+                mergedMap.put(key, value);
+            }
+        }
+        for (String key : bodyMap.keySet()) {
+            if (bodyMap.get(key) != null && StrUtil.isNotBlank(bodyMap.get(key).toString())) {
+                mergedMap.put(key, bodyMap.get(key));
+            }
+        }
+        return Joiner.on("&").withKeyValueSeparator("=").join(mergedMap);
+    }
+
+
+    /**
+     * 校验SIGN
+     */
+    private boolean checkSign(LgIsvParams isvParams, String res) {
+
+        JSONObject entries = JSONUtil.parseObj(res);
+        JSONObject headMap = entries.getJSONObject("head");
+        JSONObject bodyMap = entries.getJSONObject("body");
+        TreeMap<String, Object> mergedMap = new TreeMap<>();
+
+        // 剔除空值字段
+        for (String key : headMap.keySet()) {
+            if ("sign".equals(key)) {
+                continue;
+            }
+            Object value = headMap.get(key);
+            if (value != null && StrUtil.isNotBlank(value.toString())) {
+                mergedMap.put(key, value);
+            }
+        }
+
+        for (String key : bodyMap.keySet()) {
+            if (bodyMap.get(key) != null && StrUtil.isNotBlank(bodyMap.get(key).toString())) {
+                mergedMap.put(key, bodyMap.get(key));
+            }
+        }
+
+        String join = Joiner.on("&").withKeyValueSeparator("=").join(mergedMap);
+
+        return LgRSASignatureUtils.verify(isvParams.getServerPublicKey(), headMap.getStr("sign"), join);
+    }
+
+}

+ 388 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/sft/SftCashService.java

@@ -0,0 +1,388 @@
+package com.kxs.common.cash.service.sft;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
+import com.aliyun.oss.common.utils.HttpUtil;
+import com.kxs.common.cash.handlers.CashMessageHandler;
+import com.kxs.common.cash.model.common.CashRequest;
+import com.kxs.common.cash.model.common.CommCashReq;
+import com.kxs.common.cash.model.common.CommCashRes;
+import com.kxs.common.cash.model.common.CommQueryReq;
+import com.kxs.common.cash.model.params.IsvParams;
+import com.kxs.common.cash.model.params.sft.SftIsvParams;
+import com.kxs.common.cash.model.sft.*;
+import com.kxs.common.cash.model.yhs.YhsFileDataRequest;
+import com.kxs.common.cash.service.CashService;
+import com.kxs.common.cash.service.ConfigContextService;
+import com.kxs.common.core.constant.CommonConstants;
+import com.kxs.common.core.exception.GlobalCustomerException;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StreamUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.math.BigDecimal;
+import java.util.*;
+
+/**
+ * 盛付通提现实现
+ *
+ * @author 没秃顶的码农
+ * @date 2025-03-21
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class SftCashService implements CashService {
+
+    private final ConfigContextService configContextService;
+
+    @Override
+    public Boolean cash(CommCashReq req) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+        IsvParams commIsvParams = configContextService.getParams(req.getIfCode());
+        if (commIsvParams instanceof SftIsvParams isvParams) {
+
+            try {
+                SftCashSubmitReq cashRequest = new SftCashSubmitReq();
+                //公共字段
+                cashRequest.setMch_id(isvParams.getMchId());
+                cashRequest.setNonce_str(UUID.randomUUID().toString().replace("-", ""));
+                cashRequest.setAction("factoring/userContractFileSignature");
+                cashRequest.setOut_trade_no(req.getOutOrderNo());
+                cashRequest.setInvoice_amount(req.getAmount().intValue());
+                //提现用户信息
+                ArrayList<SftUserInfos> userInfoList = new ArrayList<>();
+                SftUserInfos userInfos = new SftUserInfos();
+                userInfos.setUser_name(req.getAcctName());
+                userInfos.setMobile(req.getAcctMobile());
+                userInfos.setId_no(req.getAcctIdCard());
+                userInfos.setBank_card(req.getAcctNo());
+                userInfos.setAmount(req.getAmount().intValue());
+                userInfos.setOrder_no(req.getOutOrderNo());
+                userInfoList.add(userInfos);
+                String userInfoStr = Base64.getEncoder().encodeToString(JSONUtil.toJsonStr(userInfoList).getBytes());
+
+                cashRequest.setUser_infos(userInfoStr);
+                String sign = createSign(isvParams.getMchKey(), cashRequest);
+                cashRequest.setSign(sign);
+
+                isvParams.setUrl(isvParams.getUrl());
+                String response = send(isvParams, cashRequest);
+                SftCashSubmitRes commRes = JSONUtil.toBean(response, SftCashSubmitRes.class);
+                log.info("盛付通发起提现返回信息{}", response);
+                if(!"SUCCESS".equals(commRes.getStatus())){
+                    throw new GlobalCustomerException(commRes.getMessage());
+                }
+                return Boolean.TRUE;
+
+            }catch (Exception e) {
+                log.error("盛付通发起提现任务失败{}", JSON.toJSONString(req), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    @Override
+    public Boolean create(CashRequest req, YhsFileDataRequest fileDataReq) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+
+        IsvParams commIsvParams = configContextService.getParams(req.getIfCode());
+        if (commIsvParams instanceof SftIsvParams isvParams && req instanceof SftCreateRequest createRequest) {
+
+            try {
+                createRequest.setMch_id(isvParams.getMchId());
+                createRequest.setNonce_str(UUID.randomUUID().toString().replace("-", ""));
+                createRequest.setAction("cert_check");
+                String sign = createSign(isvParams.getMchKey(), createRequest);
+                createRequest.setSign(sign);
+
+                isvParams.setUrl(isvParams.getQueryUrl());
+                String response = send(isvParams, createRequest);
+                SftCreateRes commRes = JSONUtil.toBean(response, SftCreateRes.class);
+                log.info("盛付通上传身份证返回信息{}", response);
+                if(!"SUCCESS".equals(commRes.getStatus())){
+                    throw new GlobalCustomerException(commRes.getMessage());
+                }
+                if(!"SUCCESS".equals(commRes.getCheck_state())){
+                    throw new GlobalCustomerException(commRes.getCheck_state_desc());
+                }
+
+                return Boolean.TRUE;
+            } catch (Exception e) {
+                log.error("创客{}上传身份证盛付通失败", createRequest.getMobile(), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    /**
+     * 查询签约信息
+     */
+    @Override
+    public Object queryInfo(CashRequest req) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+
+        IsvParams commIsvParams = configContextService.getParams(req.getIfCode());
+        if (commIsvParams instanceof SftIsvParams isvParams && req instanceof SftQuerySignStateReq signStateReq) {
+
+            try {
+                signStateReq.setMch_id(isvParams.getMchId());
+                signStateReq.setNonce_str(UUID.randomUUID().toString().replace("-", ""));
+                signStateReq.setAction("get_document_url");
+                String sign = createSign(isvParams.getMchKey(), req);
+                signStateReq.setSign(sign);
+
+                isvParams.setUrl(isvParams.getQueryUrl());
+                String response = send(isvParams, signStateReq);
+                SftSignStateRes commRes = JSONUtil.toBean(response, SftSignStateRes.class);
+                log.info("盛付通获取签约状态返回信息{}", response);
+                if(!"SUCCESS".equals(commRes.getStatus())){
+                    throw new GlobalCustomerException(commRes.getMessage());
+                }
+
+                return commRes;
+            } catch (Exception e) {
+                log.error("创客{}获取盛付通签约状态失败", signStateReq.getId_no(), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        throw new GlobalCustomerException("未查询到通道配置参数");
+
+    }
+
+    /**
+     * 查询签约地址
+     */
+    @Override
+    public Object querySignUrl(CashRequest req){
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+
+        IsvParams commIsvParams = configContextService.getParams(req.getIfCode());
+        if (commIsvParams instanceof SftIsvParams isvParams && req instanceof SftSignUrlRequest signUrlReq) {
+
+            try {
+                signUrlReq.setMch_id(isvParams.getMchId());
+                signUrlReq.setNonce_str(UUID.randomUUID().toString().replace("-", ""));
+                signUrlReq.setAction("get_sign_url");
+                String sign = createSign(isvParams.getMchKey(), req);
+                signUrlReq.setSign(sign);
+
+                isvParams.setUrl(isvParams.getQueryUrl());
+                String response = send(isvParams, signUrlReq);
+                SftSignUrlRes commRes = JSONUtil.toBean(response, SftSignUrlRes.class);
+                log.info("盛付通获取签约地址返回信息{}", response);
+                if(!"SUCCESS".equals(commRes.getStatus())){
+                    throw new GlobalCustomerException(commRes.getMessage());
+                }
+                if(!"SUCCESS".equals(commRes.getState())){
+                    throw new GlobalCustomerException(commRes.getState_desc());
+                }
+
+                return commRes;
+            } catch (Exception e) {
+                log.error("创客{}获取盛付通签约地址失败", signUrlReq.getId_no(), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        throw new GlobalCustomerException("未查询到通道配置参数");
+    }
+
+    @Override
+    public Boolean query(CommQueryReq req) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+        IsvParams commIsvParams = configContextService.getParams(req.getIfCode());
+        if (commIsvParams instanceof SftIsvParams isvParams) {
+
+            try {
+                SftQueryInfoReq queryReq = new SftQueryInfoReq();
+                queryReq.setMch_id(isvParams.getMchId());
+                queryReq.setAction("qry");
+                queryReq.setNonce_str(UUID.randomUUID().toString().replace("-", ""));
+                queryReq.setOut_trade_no(req.getOutOrderNo());
+
+
+                String sign = createSign(isvParams.getMchKey(), queryReq);
+                queryReq.setSign(sign);
+
+                isvParams.setUrl(isvParams.getQueryUrl());
+                String response = send(isvParams, queryReq);
+                SftQueryInfoRes commRes = JSONUtil.toBean(response, SftQueryInfoRes.class);
+                log.info("盛付通查询订单信息{}", response);
+                if(!"SUCCESS".equals(commRes.getStatus())){
+                    throw new GlobalCustomerException(commRes.getMessage());
+                }
+
+                //获取处理的handler方法
+                CashMessageHandler cashMessageHandler = getCashMessageHandler(req.getIfCode());
+
+                CommCashRes commCashRes = new CommCashRes();
+                commCashRes.setIfCode(req.getIfCode());
+                commCashRes.setOutOrderNo(commRes.getOut_trade_no());
+                switch (commRes.getTrade_state()) {
+                    case "PROCESSING" -> {
+                        commCashRes.setStatus(2);
+                        commCashRes.setRemark("处理中");
+                    }
+                    case "SUCCESS" -> {
+                        commCashRes.setStatus(1);
+                        commCashRes.setRemark("交易成功");
+                    }
+                    default -> {
+                        commCashRes.setStatus(0);
+                        commCashRes.setRemark(StrUtil.isNotBlank(commRes.getTrade_state_desc()) ? commRes.getTrade_state_desc() : "交易失败");
+                    }
+                }
+
+                commCashRes.setAmount(commRes.getReal_amount());
+                commCashRes.setTaxAmount(commRes.getTax_amount());
+
+                cashMessageHandler.handle(commCashRes);
+
+                return Boolean.TRUE;
+            }catch (Exception e) {
+                log.error("盛付通查询订单任务失败{}", JSON.toJSONString(req), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    @Override
+    public String cashBack(HttpServletRequest request, String ifCode) {
+
+        IsvParams commIsvParams = configContextService.getParams(ifCode);
+        if (commIsvParams instanceof SftIsvParams isvParams) {
+
+            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+
+                StreamUtils.copy(request.getInputStream(), baos);
+                String bodyStr = baos.toString();
+                log.info("盛付通{}回调接口:{}", ifCode, bodyStr);
+
+                SftCashBackMessage commRes = JSONUtil.toBean(bodyStr, SftCashBackMessage.class);
+
+                if(!Objects.equals(commRes.getMch_id(), isvParams.getMchId())){
+                    throw new GlobalCustomerException("商户号不匹配");
+                }
+                //验证签名
+                if (!checkSign(commRes, commRes.getSign(), isvParams.getMchKey())) {
+                    throw new GlobalCustomerException("签名错误");
+                }
+                //获取处理的handler方法
+                CashMessageHandler cashMessageHandler = getCashMessageHandler(ifCode);
+
+                CommCashRes commCashRes = new CommCashRes();
+                commCashRes.setIfCode(ifCode);
+                commCashRes.setOutOrderNo(commRes.getOut_trade_no());
+                switch (commRes.getTrade_state()) {
+                    case "PROCESSING" -> {
+                        commCashRes.setStatus(2);
+                        commCashRes.setRemark("处理中");
+                    }
+                    case "SUCCESS" -> {
+                        commCashRes.setStatus(1);
+                        commCashRes.setRemark("交易成功");
+                    }
+                    default -> {
+                        commCashRes.setStatus(0);
+                        commCashRes.setRemark(StrUtil.isNotBlank(commRes.getTrade_state_desc()) ? commRes.getTrade_state_desc() : "交易失败");
+                    }
+                }
+
+                commCashRes.setAmount(commRes.getReal_amount());
+                commCashRes.setTaxAmount(commRes.getTax_amount());
+
+                return cashMessageHandler.handle(commCashRes);
+            } catch (Exception e) {
+                log.error("盛付通回调接口异常", e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+
+        return "SUCCESS";
+    }
+
+
+
+    private String send(SftIsvParams isvParams, CashRequest req) {
+
+        String response = HttpRequest.post(isvParams.getUrl())
+                .body(JSONUtil.toJsonStr(req))
+                .timeout(50000)
+                .execute().body();
+        log.info("盛付通提现请求参数{},接口{},返回{}", JSONUtil.toJsonStr(req), isvParams.getUrl(), response);
+        return response;
+
+    }
+
+    /**
+     * 所有参数按照字段名的 ascii  码从小到大排序后使用 QueryString 的格式(即
+     * key1=value1&key2=value2…)拼接而成
+     */
+    private String createSign(String mchKey, CashRequest req) {
+
+        Map<String, Object> params = BeanUtil.beanToMap(req, false, true);
+        String paramToQueryString = sortStringByMap(params);
+        paramToQueryString = paramToQueryString + "key=" + mchKey;
+
+        return SecureUtil.md5(paramToQueryString).toUpperCase();
+    }
+
+
+    /**
+     * 校验SIGN
+     */
+    private boolean checkSign(SftCashBackMessage req, String sign, String mchKey) {
+
+        req.setSign(null);
+        Map<String, Object> params = BeanUtil.beanToMap(req, false, true);
+        String paramToQueryString = sortStringByMap(params);
+        paramToQueryString = paramToQueryString + "key=" + mchKey;
+        String verSign = SecureUtil.md5(paramToQueryString).toUpperCase();
+
+        return sign.equals(verSign);
+    }
+
+
+    /**
+     * 按映射排序字符串
+     *
+     * @param map map
+     * @return {@link String}
+     */
+    private String sortStringByMap(Map<String, Object> map) {
+        TreeMap<String, Object> treeMap = new TreeMap<>(map);
+        StringBuilder strBuilder = new StringBuilder();
+        for (Map.Entry<String, Object> entry : treeMap.entrySet()) {
+            if (entry.getValue() != null && !String.valueOf(entry.getValue()).isEmpty()) {
+                strBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
+            }
+        }
+        return strBuilder.toString();
+    }
+}

+ 337 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/service/yhs/YhsCashService.java

@@ -0,0 +1,337 @@
+package com.kxs.common.cash.service.yhs;
+
+import cn.hutool.core.lang.TypeReference;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
+import com.kxs.common.cash.handlers.CashMessageHandler;
+import com.kxs.common.cash.model.common.CashRequest;
+import com.kxs.common.cash.model.common.CommCashReq;
+import com.kxs.common.cash.model.common.CommCashRes;
+import com.kxs.common.cash.model.common.CommQueryReq;
+import com.kxs.common.cash.model.params.IsvParams;
+import com.kxs.common.cash.model.params.yhs.YhsIsvParams;
+import com.kxs.common.cash.model.yhs.*;
+import com.kxs.common.cash.service.CashService;
+import com.kxs.common.cash.service.ConfigContextService;
+import com.kxs.common.cash.utils.YHS_RSA_Util;
+import com.kxs.common.core.exception.GlobalCustomerException;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StreamUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Objects;
+
+
+/**
+ * 云汇算提现服务
+ *
+ * @author 没秃顶的码农
+ * @date 2024-11-23
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class YhsCashService implements CashService {
+
+    private final ConfigContextService configContextService;
+
+    @Override
+    public Boolean cash(CommCashReq req) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+
+        IsvParams isvParams = configContextService.getParams(req.getIfCode());
+        if (isvParams instanceof YhsIsvParams yhsIsvParams) {
+
+            try {
+                CashSubmitReq cashRequest = new CashSubmitReq();
+                cashRequest.setIfCode(req.getIfCode());
+                cashRequest.setAmount(req.getAmount());
+                cashRequest.setPayeeAccount(req.getAcctNo());
+                cashRequest.setPayeeName(req.getAcctName());
+                cashRequest.setPayeeIdCard(req.getAcctIdCard());
+                cashRequest.setPayeePhone(req.getAcctMobile());
+                cashRequest.setEntOrderNo(req.getOutOrderNo());
+
+                cashRequest.setApiCode("1000");
+                cashRequest.setTaskNo(yhsIsvParams.getTaskNo());
+                cashRequest.setTimestamp(String.valueOf(System.currentTimeMillis()));
+                cashRequest.setChannelNo(yhsIsvParams.getChannelNo());
+                cashRequest.setEnterpriseNo(yhsIsvParams.getEnterpriseNo());
+                cashRequest.setCallbackUrl(yhsIsvParams.getCallbackUrl());
+
+                yhsIsvParams.setUrl(yhsIsvParams.getUrl() + "/api/pay/apply");
+                String response = send(yhsIsvParams, cashRequest);
+                YhsCashCommRes<CashSubmitRes> commRes = JSONUtil.toBean(response, new TypeReference<>() {}, true);
+                log.info("云汇算发起提现返回信息{}", response);
+                if(!"000000".equals(commRes.getCode())){
+                    throw new GlobalCustomerException(commRes.getMsg());
+                }
+                return Boolean.TRUE;
+
+            }catch (Exception e) {
+                log.error("发起提现任务失败{}", JSON.toJSONString(req), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    @Override
+    public Boolean query(CommQueryReq req) {
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+        IsvParams isvParams = configContextService.getParams(req.getIfCode());
+        if (isvParams instanceof YhsIsvParams yhsIsvParams) {
+
+            try {
+                QueryYhsReq queryYhsReq = new QueryYhsReq();
+                queryYhsReq.setIfCode(req.getIfCode());
+                queryYhsReq.setEntOrderNo(req.getOutOrderNo());
+
+                queryYhsReq.setApiCode("1001");
+                queryYhsReq.setTimestamp(String.valueOf(System.currentTimeMillis()));
+                queryYhsReq.setChannelNo(yhsIsvParams.getChannelNo());
+                queryYhsReq.setEnterpriseNo(yhsIsvParams.getEnterpriseNo());
+
+                yhsIsvParams.setUrl(yhsIsvParams.getUrl() + "/api/pay/query");
+                String response = send(yhsIsvParams, queryYhsReq);
+                YhsCashCommRes<YhsCashBackMessage.YhsBody> commRes = JSONUtil.toBean(response, new TypeReference<>() {}, true);
+                log.info("云汇算查询订单信息{}", response);
+                if(!"000000".equals(commRes.getCode())){
+                    throw new GlobalCustomerException(commRes.getMsg());
+                }
+
+                //获取处理的handler方法
+                CashMessageHandler cashMessageHandler = getCashMessageHandler(req.getIfCode());
+                YhsCashBackMessage.YhsBody body = commRes.getData();
+
+                CommCashRes commCashRes = new CommCashRes();
+                commCashRes.setIfCode(req.getIfCode());
+                commCashRes.setOutOrderNo(body.getEntOrderNo());
+                switch (body.getStatus()) {
+                    case 100, 200 -> {
+                        commCashRes.setStatus(2);
+                        commCashRes.setRemark("处理中");
+                    }
+                    case 300 -> {
+                        commCashRes.setStatus(1);
+                        commCashRes.setRemark("交易成功");
+                    }
+                    default -> {
+                        commCashRes.setStatus(0);
+                        commCashRes.setRemark(StrUtil.isNotBlank(body.getPayMsg()) ? body.getPayMsg() : "交易失败");
+                    }
+                }
+                commCashRes.setAmount(body.getAmount());
+                commCashRes.setTaxAmount(body.getActualTaxPayment());
+
+                cashMessageHandler.handle(commCashRes);
+
+                return Boolean.TRUE;
+            }catch (Exception e) {
+                log.error("云汇算查询订单任务失败{}", JSON.toJSONString(req), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    @Override
+    public Boolean create(CashRequest req, YhsFileDataRequest fileDataReq) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+
+        IsvParams isvParams = configContextService.getParams(req.getIfCode());
+        if (isvParams instanceof YhsIsvParams yhsIsvParams && req instanceof YhsCreateRequest createRequest) {
+
+            try {
+                String url = yhsIsvParams.getUrl();
+                createRequest.setApiCode("3000");
+                createRequest.setTimestamp(String.valueOf(System.currentTimeMillis()));
+                createRequest.setChannelNo(yhsIsvParams.getChannelNo());
+                createRequest.setEnterpriseNo(yhsIsvParams.getEnterpriseNo());
+
+                yhsIsvParams.setUrl(url + "/api/personal/create");
+                String response = send(yhsIsvParams, createRequest, fileDataReq);
+
+                YhsCashCommRes commRes = JSONUtil.toBean(response, YhsCashCommRes.class);
+                if(!"000000".equals(commRes.getCode())){
+                    throw new GlobalCustomerException(createRequest.getName() + commRes.getMsg());
+                }
+                //发起签约确认
+                QueryInfoReq queryInfoReq = new QueryInfoReq();
+                queryInfoReq.setApiCode("3004");
+                queryInfoReq.setChannelNo(yhsIsvParams.getChannelNo());
+                queryInfoReq.setEnterpriseNo(yhsIsvParams.getEnterpriseNo());
+                queryInfoReq.setTimestamp(String.valueOf(System.currentTimeMillis()));
+                queryInfoReq.setIdCard(createRequest.getIdCard());
+                yhsIsvParams.setUrl(url + "/api/personal/sign");
+                String confirmRes = send(yhsIsvParams, queryInfoReq);
+                YhsCashCommRes<String> confirmCommRes = JSONUtil.toBean(confirmRes, new TypeReference<>() {}, true);
+                log.info("云汇算发起签约确认返回信息{}", confirmRes);
+                if(!"000000".equals(confirmCommRes.getCode())){
+                    throw new GlobalCustomerException(createRequest.getName() + confirmCommRes.getMsg());
+                }
+
+                return Boolean.TRUE;
+            } catch (Exception e) {
+                log.error("创客{}进件云汇算失败", createRequest.getName(), e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    @Override
+    public Object queryInfo(CashRequest req) {
+
+        if(StrUtil.isBlank(req.getIfCode())){
+            throw new GlobalCustomerException("ifCode不能为空");
+        }
+
+        IsvParams isvParams = configContextService.getParams(req.getIfCode());
+        if (isvParams instanceof YhsIsvParams yhsIsvParams && req instanceof QueryInfoReq queryInfoReq) {
+
+            try {
+                queryInfoReq.setApiCode("3003");
+                queryInfoReq.setTimestamp(String.valueOf(System.currentTimeMillis()));
+                queryInfoReq.setChannelNo(yhsIsvParams.getChannelNo());
+                queryInfoReq.setEnterpriseNo(yhsIsvParams.getEnterpriseNo());
+
+                yhsIsvParams.setUrl(yhsIsvParams.getUrl() + "/api/personal/signed/result");
+                String response = send(yhsIsvParams, queryInfoReq);
+
+                YhsCashCommRes<QueryInfoRes> commRes = JSONUtil.toBean(response, new TypeReference<>() {}, true);
+                if(!"000000".equals(commRes.getCode())){
+                    return null;
+                }
+
+                return commRes.getData();
+            } catch (Exception e) {
+                log.error("查询签约信息失败{}进件失败", queryInfoReq.getIdCard(), e);
+                throw new GlobalCustomerException("查询签约信息失败,请联系管理员");
+            }
+        }
+        throw new GlobalCustomerException("未查询到通道配置参数");
+    }
+
+    @Override
+    public String cashBack(HttpServletRequest request, String ifCode) {
+
+        IsvParams isvParams = configContextService.getParams(ifCode);
+        if (isvParams instanceof YhsIsvParams yhsIsvParams) {
+
+            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+
+                StreamUtils.copy(request.getInputStream(), baos);
+                String bodyStr = baos.toString();
+                log.info("云汇算{}回调接口:{}", ifCode, bodyStr);
+
+                YhsCashBackMessage cashBackMessage = JSONUtil.toBean(bodyStr, YhsCashBackMessage.class);
+                JSONObject jsonObject = JSONUtil.parseObj(bodyStr);
+
+                if(!Objects.equals(cashBackMessage.getEnterpriseNo(), yhsIsvParams.getEnterpriseNo())){
+                    throw new GlobalCustomerException("企业号不匹配");
+                }
+                //验证签名
+                if (!checkSign(jsonObject.getStr("body"), cashBackMessage.getSign(), yhsIsvParams)) {
+                    throw new GlobalCustomerException("签名错误");
+                }
+                //获取处理的handler方法
+                CashMessageHandler cashMessageHandler = getCashMessageHandler(ifCode);
+                YhsCashBackMessage.YhsBody body = cashBackMessage.getBody();
+
+                CommCashRes commCashRes = new CommCashRes();
+                commCashRes.setIfCode(ifCode);
+                commCashRes.setOutOrderNo(body.getEntOrderNo());
+                if(body.getStatus() == 100 || body.getStatus() == 200 ){
+                    commCashRes.setStatus(2);
+                    commCashRes.setRemark("处理中");
+                }else if(body.getStatus() == 300){
+                    commCashRes.setStatus(1);
+                    commCashRes.setRemark("交易成功");
+                }else{
+                    //失败
+                    commCashRes.setStatus(0);
+                    commCashRes.setRemark(StrUtil.isNotBlank(body.getPayMsg()) ? body.getPayMsg() : "交易失败");
+                }
+                commCashRes.setAmount(body.getAmount());
+                commCashRes.setTaxAmount(body.getActualTaxPayment());
+
+                return cashMessageHandler.handle(commCashRes);
+            } catch (Exception e) {
+                log.error("云汇算回调接口异常", e);
+                throw new GlobalCustomerException(e.getMessage());
+            }
+        }
+
+        return "{" +
+                " \"handled\": SUCCESS" +
+                "}";
+    }
+
+    private String send(YhsIsvParams isvParams, CashRequest req) throws Exception {
+
+        RSAPublicKey rsaPublicKey = YHS_RSA_Util.loadPublicKey(isvParams.getPublicKey());
+        String encryptStr = YHS_RSA_Util.encrypt(rsaPublicKey, JSONUtil.toJsonStr(req).getBytes());
+
+        YhsBaseRequest commReq = YhsBaseRequest.builder()
+                .channelNo(isvParams.getChannelNo())
+                .enterpriseNo(isvParams.getEnterpriseNo())
+                .body(encryptStr).build();
+        String response = HttpRequest.post(isvParams.getUrl())
+                .body(JSONUtil.toJsonStr(commReq))
+                .timeout(50000)
+                .execute().body();
+        log.info("云汇算请求参数{},接口{},返回{}", JSONUtil.toJsonStr(req), isvParams.getUrl(), response);
+        return response;
+
+    }
+
+    private String send(YhsIsvParams isvParams, CashRequest req, YhsFileDataRequest fileDataReq) throws Exception {
+
+        RSAPublicKey rsaPublicKey = YHS_RSA_Util.loadPublicKey(isvParams.getPublicKey());
+        String encryptStr = YHS_RSA_Util.encrypt(rsaPublicKey, JSONUtil.toJsonStr(req).getBytes());
+
+        JSONObject reqStr = JSONUtil.createObj()
+                .set("channelNo", isvParams.getChannelNo())
+                .set("enterpriseNo", isvParams.getEnterpriseNo())
+                .set("body", encryptStr)
+                .set("fileData", JSONUtil.toJsonStr(fileDataReq));
+
+        String response = HttpRequest.post(isvParams.getUrl())
+                .body(JSONUtil.toJsonStr(reqStr))
+                .timeout(50000)
+                .execute().body();
+        log.info("云汇算请求参数{},接口{},返回{}", req, isvParams.getUrl(), response);
+        return response;
+    }
+
+
+    /**
+     * 验证签名
+     *
+     * @param bodyStr 回调参数
+     * @param isvParams       isv参数
+     * @return {@link Boolean}
+     */
+    private Boolean checkSign(String bodyStr, String sign, YhsIsvParams isvParams) throws Exception {
+
+        RSAPublicKey rsaPublicKey = YHS_RSA_Util.loadPublicKey(isvParams.getPublicKey());
+        return YHS_RSA_Util.doCheck(bodyStr, sign, rsaPublicKey);
+    }
+
+}

+ 117 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/utils/LgRSAEncryptUtils.java

@@ -0,0 +1,117 @@
+package com.kxs.common.cash.utils;
+
+import org.apache.commons.codec.binary.Base64;
+
+import javax.crypto.Cipher;
+import java.io.ByteArrayOutputStream;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+public final class LgRSAEncryptUtils {
+
+    private static final String DEFAULT_CIPHER_ALGORITHM = "RSA";
+
+    private static final String DEFAULT_CHARSET = "UTF-8";
+
+    /**
+     * RSA最大加密明文大小
+     */
+    private static final int MAX_ENCRYPT_BLOCK = 117;
+
+    /**
+     * RSA最大解密密文大小
+     */
+    private static final int MAX_DECRYPT_BLOCK = 1024 / 4;
+
+    public static String encrypt(String content, String publicKeyStr) {
+        return encrypt(content, DEFAULT_CIPHER_ALGORITHM, publicKeyStr);
+    }
+
+    public static String encrypt(String content, String cipherAlgorithm, String publicKeyStr) {
+        try {
+            byte[] publicKeyBytes = Base64.decodeBase64(publicKeyStr);
+            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
+
+            // 取得待加密数据
+            byte[] data = content.getBytes(DEFAULT_CHARSET);
+            KeyFactory factory = KeyFactory.getInstance(cipherAlgorithm);
+            PublicKey publicKey = factory.generatePublic(keySpec);
+
+            // 对数据加密
+            Cipher cipher = Cipher.getInstance(factory.getAlgorithm());
+            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+
+            int inputLen = data.length;
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            int offSet = 0;
+            byte[] cache;
+            int i = 0;
+            // 对数据分段加密
+            while (inputLen - offSet > 0) {
+                if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
+                    cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
+                } else {
+                    cache = cipher.doFinal(data, offSet, inputLen - offSet);
+                }
+                out.write(cache, 0, cache.length);
+                i++;
+                offSet = i * MAX_ENCRYPT_BLOCK;
+            }
+            byte[] encryptedData = out.toByteArray();
+            out.close();
+
+            // 返回加密后由Base64编码的加密信息
+            return Base64.encodeBase64String(encryptedData);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static String decrypt(String content, String privateKeyStr) {
+        return decrypt(content, DEFAULT_CIPHER_ALGORITHM, privateKeyStr);
+    }
+
+    public static String decrypt(String content, String cipherAlgorithm, String privateKeyStr) {
+        try {
+            byte[] privateKeyBytes = Base64.decodeBase64(privateKeyStr);
+            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
+
+            // 获得待解密数据
+            byte[] data = Base64.decodeBase64(content);
+            KeyFactory factory = KeyFactory.getInstance(cipherAlgorithm);
+            PrivateKey privateKey = factory.generatePrivate(keySpec);
+
+            // 对数据解密
+            Cipher cipher = Cipher.getInstance(factory.getAlgorithm());
+            cipher.init(Cipher.DECRYPT_MODE, privateKey);
+
+            int inputLen = data.length;
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            int offSet = 0;
+            byte[] cache;
+            int i = 0;
+            // 对数据分段解密
+            while (inputLen - offSet > 0) {
+                if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
+                    cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
+                } else {
+                    cache = cipher.doFinal(data, offSet, inputLen - offSet);
+                }
+                out.write(cache, 0, cache.length);
+                i++;
+                offSet = i * MAX_DECRYPT_BLOCK;
+            }
+            byte[] decryptedData = out.toByteArray();
+            out.close();
+
+            // 返回UTF-8编码的解密信息
+            return new String(decryptedData, DEFAULT_CHARSET);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}

+ 70 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/utils/LgRSASignatureUtils.java

@@ -0,0 +1,70 @@
+package com.kxs.common.cash.utils;
+
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+public class LgRSASignatureUtils {
+
+    private final static String RSA = "RSA";
+
+    public final static String signAlg = "SHA256withRSA";
+
+    public static String sign(String rsaPrivateKey, String src) {
+        return sign(rsaPrivateKey, src, signAlg);
+    }
+
+    public static String sign(String rsaPrivateKey, String src, String signAlg) {
+        // base64解码私钥
+        byte[] decodePrivateKey = Base64.getDecoder().decode(rsaPrivateKey.replace("\r\n", ""));
+
+        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decodePrivateKey);
+        byte[] result;
+        try {
+            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
+            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
+            Signature signature = Signature.getInstance(signAlg);
+            signature.initSign(privateKey);
+            signature.update(src.getBytes());
+
+            // 生成签名
+            result = signature.sign();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+        // base64编码签名为字符串
+        return Base64.getEncoder().encodeToString(result);
+    }
+
+    public static boolean verify(String rsaPublicKey, String sign, String src) {
+        return verify(rsaPublicKey, sign, src, signAlg);
+    }
+
+    public static boolean verify(String rsaPublicKey, String sign, String src, String signAlg) {
+        // base64解码公钥
+        byte[] decodePublicKey = Base64.getDecoder().decode(rsaPublicKey.replace("\r\n", ""));
+
+        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(decodePublicKey);
+
+        try {
+            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
+            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
+            Signature signature = Signature.getInstance(signAlg);
+            signature.initVerify(publicKey);
+            signature.update(src.getBytes());
+            // base64解码签名为字节数组
+            byte[] decodeSign = Base64.getDecoder().decode(sign);
+
+            // 验证签名
+            return signature.verify(decodeSign);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}

+ 231 - 0
kxs-common/kxs-common-cash/src/main/java/com/kxs/common/cash/utils/YHS_RSA_Util.java

@@ -0,0 +1,231 @@
+package com.kxs.common.cash.utils;
+
+import org.apache.commons.codec.binary.Base64;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+/**
+ * 云汇算工具
+ * @author mac
+ */
+public class YHS_RSA_Util {
+    /**
+     * 加密算法
+     */
+    public static final String KEY_ALGORITHMS = "RSA";
+    /**
+     * 签名算法
+     */
+    public static final String SIGNATURE_ALGORITHM = "SHA1WithRSA";
+    /**
+     * RSA最大加密明文大小
+     */
+    private static final int MAX_ENCRYPT_BLOCK = 245;
+    /**
+     * RSA最大解密密文大小
+     */
+    private static final int MAX_DECRYPT_BLOCK = 256;
+    /**
+     * 从字符串中加载公钥
+     *
+     * @param publicKeyStr 公钥数据字符串
+     * @throws Exception 加载公钥时产生的异常
+     */
+    public static RSAPublicKey loadPublicKey(String publicKeyStr) throws Exception {
+        try {
+            byte[] buffer = Base64.decodeBase64(publicKeyStr);
+            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHMS);
+            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
+            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
+        } catch (NoSuchAlgorithmException e) {
+            throw new Exception("无此算法");
+        } catch (InvalidKeySpecException e) {
+            throw new Exception("公钥非法");
+        } catch (NullPointerException e) {
+            throw new Exception("公钥数据为空");
+        }
+    }
+    /**
+     * 从字符串中加载私钥
+     *
+     * @param privateKeyStr 私钥数据字符串
+     * @throws Exception 加载私钥时产生的异常
+     */
+    public static RSAPrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
+        try {
+            byte[] buffer = Base64.decodeBase64(privateKeyStr);
+            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
+            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHMS);
+            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
+        } catch (NoSuchAlgorithmException e) {
+            throw new Exception("无此算法");
+        } catch (InvalidKeySpecException e) {
+            throw new Exception("私钥非法");
+        } catch (NullPointerException e) {
+            throw new Exception("私钥数据为空");
+        }
+    }
+    /**
+     * RSA签名
+     *
+     * @param content    待签名数据
+     * @param privateKey 云汇算平台私钥
+     * @return 签名值
+     */
+    public static String sign(String content, RSAPrivateKey privateKey) throws Exception {
+        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
+        signature.initSign(privateKey);
+        signature.update(content.getBytes(StandardCharsets.UTF_8.name()));
+        byte[] signed = signature.sign();
+        return (new Base64()).encodeAsString(signed);
+    }
+    /**
+     * RSA验签名检查
+     *
+     * @param content   待签名数据
+     * @param sign      签名值
+     * @param publicKey 分配给企业的公钥
+     * @return 布尔值
+     */
+    public static boolean doCheck(String content, String sign, RSAPublicKey publicKey) throws Exception {
+        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
+        signature.initVerify(publicKey);
+        signature.update(content.getBytes(StandardCharsets.UTF_8));
+        return signature.verify(new Base64().decode(sign));
+    }
+    /**
+     * 加密过程
+     *
+     * @param publicKey 公钥
+     * @param data      明文数据
+     * @return
+     * @throws Exception 加密过程中的异常信息
+     */
+    public static String encrypt(RSAPublicKey publicKey, byte[] data) throws Exception {
+        if (publicKey == null) {
+            throw new Exception("加密公钥为空, 请设置");
+        }
+        Cipher cipher;
+        try {
+            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHMS);
+            cipher = Cipher.getInstance(keyFactory.getAlgorithm());
+            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+            int inputLen = data.length;
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            int offSet = 0;
+            byte[] cache;
+            int i = 0;
+            // 对数据分段加密
+            while (inputLen - offSet > 0) {
+                if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
+                    cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
+                } else {
+                    cache = cipher.doFinal(data, offSet, inputLen - offSet);
+                }
+                out.write(cache, 0, cache.length);
+                i++;
+                offSet = i * MAX_ENCRYPT_BLOCK;
+            }
+            byte[] encryptedData = out.toByteArray();
+            out.close();
+            return (new Base64()).encodeToString(encryptedData);
+        } catch (NoSuchAlgorithmException e) {
+            throw new Exception("无此加密算法");
+        } catch (NoSuchPaddingException e) {
+            e.printStackTrace();
+            return null;
+        } catch (InvalidKeyException e) {
+            throw new Exception("加密公钥非法,请检查");
+        } catch (IllegalBlockSizeException e) {
+            throw new Exception("明文长度非法");
+        } catch (BadPaddingException e) {
+            throw new Exception("明文数据已损坏");
+        }
+    }
+    /**
+     * 解密过程
+     *
+     * @param privateKey    私钥
+     * @param encryptedData 密文数据
+     * @return 明文
+     * @throws Exception 解密过程中的异常信息
+     */
+    public static byte[] decrypt(RSAPrivateKey privateKey, byte[] encryptedData) throws Exception {
+        if (privateKey == null) {
+            throw new Exception("解密私钥为空, 请设置");
+        }
+        Cipher cipher;
+        try {
+            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHMS);
+            cipher = Cipher.getInstance(keyFactory.getAlgorithm());
+            cipher.init(Cipher.DECRYPT_MODE, privateKey);
+            int inputLen = encryptedData.length;
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            int offSet = 0;
+            byte[] cache;
+            int i = 0;
+            // 对数据分段解密
+            while (inputLen - offSet > 0) {
+                if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
+                    cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
+                } else {
+                    cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
+                }
+                out.write(cache, 0, cache.length);
+                i++;
+                offSet = i * MAX_DECRYPT_BLOCK;
+            }
+            byte[] decryptedData = out.toByteArray();
+            out.close();
+            return decryptedData;
+        } catch (NoSuchAlgorithmException e) {
+            throw new Exception("无此解密算法");
+        } catch (NoSuchPaddingException e) {
+            e.printStackTrace();
+            return null;
+        } catch (InvalidKeyException e) {
+            throw new Exception("解密私钥非法,请检查");
+        } catch (IllegalBlockSizeException e) {
+            throw new Exception("密文长度非法");
+        } catch (BadPaddingException e) {
+            e.printStackTrace();
+            throw new Exception("密文数据已损坏");
+        }
+    }
+    public static void main(String[] args) {
+        try {
+            String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnCyX7UNF0JehRJmm8W1BpacKFKfJVgYGFJ6PpQKbfmx3AePuRYPerBKTfBgaLlsMvY525a0hslAKknTWuRzJln2/ZNjt0DWVgShL0/oP5JmHTrC/krmlLZkfY4dRnF0Qrtnh7mpYbjrzOnDISP7dFckhK9BPKnhZCisSVTUB/xQTdv/8gbSi5yGHlvfaIBZiPdMYsVxr8+KP3cy/yoacyoh8zUL7HjVKQirmPUGRBucYGreRbLSSH8UhR6F8GxriJ12QMNRMVhAHCCj+8gI/LS3JK+6pnuw8uhPK/hnvWz2oIFc1m6u5nBHkYFNRzu4sb9J2y7vRwSv8r8gCQmN2AQIDAQAB";
+            String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCcLJftQ0XQl6FEmabxbUGlpwoUp8lWBgYUno+lApt+bHcB4+5Fg96sEpN8GBouWwy9jnblrSGyUAqSdNa5HMmWfb9k2O3QNZWBKEvT+g/kmYdOsL+SuaUtmR9jh1GcXRCu2eHualhuOvM6cMhI/t0VySEr0E8qeFkKKxJVNQH/FBN2//yBtKLnIYeW99ogFmI90xixXGvz4o/dzL/KhpzKiHzNQvseNUpCKuY9QZEG5xgat5FstJIfxSFHoXwbGuInXZAw1ExWEAcIKP7yAj8tLckr7qme7Dy6E8r+Ge9bPaggVzWbq7mcEeRgU1HO7ixv0nbLu9HBK/yvyAJCY3YBAgMBAAECggEAKILIxhr1OS6b/pd83pNgu/+gdcswRsg5O5AF4t/SRfEMzci6Pf3+/NO0vjPiVrP3khQqc1l9rCtnEqaMLGhzQj5ML3S1+5Rqu54gTVRIWxwYJSlkCENvvWxC4CZx3QWnFjQejyG23qEcBsTKr2StJMGPgTX/0xx0vFZ/4uplIi1C7vMkHMwZQMIAgH48T59FBJTwl6yskUYbj3aOsQK4471iaDctOG4UuVSj9Ian3KeMeen5BNF1Yw8EghXTtoEdVNvGSne2lT/5BsKyScJywpfBfiycR+bdppmaw4KLQXn6ZQnbc8hvDOtX1ZhUv5vMBkt9GLKgXf4H+Saiajy3gQKBgQDYiHFcorqPH6ClTqFDPR4rgxkE7noILcP7QxBwa0I1f2ROkqMqvNPz4bJ3hNGveBmq7QW0iMnYCoNVKjO9PpWuMntNoRhbYXrSz8BReTGyA7IIOte2+mAEKTWu5rY80AM5HztWqY2I7ddmDFcVs3B9zSs2ufaMIOEiFz1v3S7k/QKBgQC4o8YdhdNQqg9C3MbqhoPE8nmrALoWtCEJnjKE/PEhM+qfoMJtet7KR0WDqi6j8o+bqHpIIJjGD+PWN8bOdRV4DZBK5oDGO+xUhonKjwfHg62RJWcGF08PCqyyPb1YnLz3JqG00QzrkgCYk5ddmV/fCHVTCm0SS93b0BllclmGVQKBgBr+V3N+7dbt0G8LMFtbgSlXfIT63cerPOVT5Pa+yFA3A2AwM5seUyXyGvd/XUyMolaGmdF5w//kv56cVjrfVHWU4X45+PlbW55wO79sZl+UylrwSvgKmnGqWYDmTg2iuOQkGpehJ33MeK/GFg0kg3+LVjnKUQwDBU3kq8oIPtJZAoGAHD5pKtDyzy8EYJz3DJ8A/xp8RA07agep2hfJ8I/krsiLRlnIBAk8lUKrvoVE1y1ejFgMRIY/+GQr3PPxRFmirBefD+YMosY903w1DbgtX3akEUTgao0cOdfPLQDGuwxW15OgPtGPoWVXE//Xsqiogqx/jfFVV+wlBt/hUqRYT3UCgYEAs5qRAZJHAj87Aq5h+HsKmpAHJo35ySWo5a0j1kic8flWkhRit67o9lwhIn3uOzekhnplht9yXX7FahYeFC8XjAMpHCbQULYDWEhVytPRpBKu8upVO3NOUrRIDpplZzlNmWfyc4abJbzm9irU68oNwziz9v4f09b4YvqYIJG76BA=";
+            String str = "{\"enterpriseNo\":\"8882008000002\",\"apiCode\":\"1000\",\"taskNo\":\"T490514755253960704\",\"entOrderNo\":\"E20200123456789\",\"payeeName\":\"张三\",\"payeeIdCard\":\"445224199404206312\",\"payeePhone\":\"18888888888\",\"payeeAccount\":\"1564561\",\"paymentModel\":\"100\",\"amount\":\"100\"}";
+            RSAPublicKey pubKey = loadPublicKey(publicKey);
+            RSAPrivateKey priKey = loadPrivateKey(privateKey);
+            String body = encrypt(pubKey, str.getBytes(StandardCharsets.UTF_8.name()));
+            System.out.println("公钥加密:" + body);
+            String bodyText = new String(decrypt(priKey, Base64.decodeBase64(body)), StandardCharsets.UTF_8.name());
+            System.out.println("私钥解密:" + bodyText);
+
+            String sign = sign(str, priKey);
+            System.out.println("私钥签名:" + sign);
+            boolean signCheck = doCheck(str, sign, pubKey);
+            System.out.println("公钥验签:" + signCheck);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}

+ 1 - 0
kxs-common/kxs-common-cash/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -0,0 +1 @@
+com.kxs.common.cash.CashAutoConfiguration

+ 95 - 0
kxs-common/kxs-common-core/pom.xml

@@ -0,0 +1,95 @@
+<?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>
+    <parent>
+        <groupId>com.kxs</groupId>
+        <artifactId>kxs-common</artifactId>
+        <version>1.1.0</version>
+    </parent>
+
+    <artifactId>kxs-common-core</artifactId>
+    <packaging>jar</packaging>
+
+    <description>kxs 公共工具类核心包</description>
+
+    <properties>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <!--hutool-->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-core</artifactId>
+        </dependency>
+        <!--redis-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <!--server-api-->
+        <dependency>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
+        </dependency>
+        <!--hibernate-validator-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <!--json模块-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-json</artifactId>
+        </dependency>
+        <!--oauth server 依赖-->
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-core</artifactId>
+        </dependency>
+
+        <!--阿里云SLS日志包-->
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java</artifactId>
+            <version>2.5.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun.openservices</groupId>
+            <artifactId>aliyun-log-logback-appender</artifactId>
+            <version>0.1.18</version>
+        </dependency>
+        <!--spring-webmvc-->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <!--redisson-->
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson-spring-boot-starter</artifactId>
+            <version>3.24.3</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>14</source>
+                    <target>14</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 69 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/base/PayBaseDTO.java

@@ -0,0 +1,69 @@
+package com.kxs.common.core.base;
+
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+
+
+/**
+ * 支付公共参数
+ *
+ * @author 系统
+ * @date 2025-04-21 11:28:18
+ */
+@Data
+public class PayBaseDTO {
+
+    /**
+     * 订单金额(分)
+     */
+    @NotNull(message = "请输入支付金额")
+    @Min(value = 1L, message = "订单金额不能小于0")
+    private Long totalAmt;
+
+    /**
+     * 支付方式 wechat alipay
+     */
+    @NotBlank
+    private String payType;
+
+
+    /**
+     * 积分(爽贝)抵扣金额
+     */
+    @NotNull
+    private Long integralAmt;
+
+    /**
+     * 用户 ID
+     */
+    @NotNull
+    private Long userId;
+
+
+    /**
+     * 优惠券Id
+     */
+    private Integer couponId;
+
+
+    /**
+     * 终端ip
+     */
+    private String terminalIp;
+
+
+    /**
+     * 订单名称
+     */
+    private String orderName;
+
+    /**
+     * 订单类型 0码牌 1商城 2团购
+     */
+    private Integer orderType;
+
+
+}

+ 42 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/config/JacksonConfiguration.java

@@ -0,0 +1,42 @@
+package com.kxs.common.core.config;
+
+import cn.hutool.core.date.DatePattern;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.kxs.common.core.jackson.SkyJavaTimeModule;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+
+import java.time.ZoneId;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * JacksonConfig 配置一些序列化场景,雪花id转字符串等 长数字精度丢失问题
+ *
+ * @author 没秃顶的码农
+ * @date 2023/11/08
+ */
+@AutoConfiguration
+@ConditionalOnClass(ObjectMapper.class)
+@AutoConfigureBefore(JacksonAutoConfiguration.class)
+public class JacksonConfiguration {
+
+	@Bean
+	@ConditionalOnMissingBean
+	public Jackson2ObjectMapperBuilderCustomizer customizer() {
+		return builder -> {
+			builder.locale(Locale.CHINA);
+			builder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
+			builder.simpleDateFormat(DatePattern.NORM_DATETIME_PATTERN);
+			builder.serializerByType(Long.class, ToStringSerializer.instance);
+			builder.modules(new SkyJavaTimeModule());
+		};
+	}
+
+}

+ 37 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/config/RedisProperties.java

@@ -0,0 +1,37 @@
+package com.kxs.common.core.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * redis配置属性
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@Data
+@ConfigurationProperties(prefix = "spring.data.redis")
+public class RedisProperties {
+
+    private String prefix = "redis://";
+
+    /**
+     * host
+     */
+    private String host;
+
+    /**
+     * username
+     */
+    private String username;
+
+    /**
+     * pwd
+     */
+    private String password;
+
+    /**
+     * port
+     */
+    private String port = "6379";
+}

+ 91 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/config/RedisTemplateConfiguration.java

@@ -0,0 +1,91 @@
+
+package com.kxs.common.core.config;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.AllArgsConstructor;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.codec.JsonJacksonCodec;
+import org.redisson.config.Config;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.*;
+import org.springframework.data.redis.serializer.RedisSerializer;
+
+/**
+ * Redis 配置类
+ *
+ * @author 没秃顶的码农
+ * @date 2023/10/27
+ */
+@EnableCaching
+@AutoConfiguration
+@AutoConfigureBefore(RedisAutoConfiguration.class)
+@EnableConfigurationProperties({ RedisProperties.class })
+@AllArgsConstructor
+public class RedisTemplateConfiguration {
+
+
+	private final RedisProperties redisProperties;
+
+	@Bean
+	@Primary
+	public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
+		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+		redisTemplate.setKeySerializer(RedisSerializer.string());
+		redisTemplate.setHashKeySerializer(RedisSerializer.string());
+		redisTemplate.setValueSerializer(RedisSerializer.java());
+		redisTemplate.setHashValueSerializer(RedisSerializer.java());
+		redisTemplate.setConnectionFactory(factory);
+		return redisTemplate;
+	}
+
+	@Bean
+	public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
+		return redisTemplate.opsForHash();
+	}
+
+	@Bean
+	public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
+		return redisTemplate.opsForValue();
+	}
+
+	@Bean
+	public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
+		return redisTemplate.opsForList();
+	}
+
+	@Bean
+	public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
+		return redisTemplate.opsForSet();
+	}
+
+	@Bean
+	public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
+		return redisTemplate.opsForZSet();
+	}
+
+	@Bean
+	public RedissonClient redissonClient() {
+		Config config = new Config();
+		config.useSingleServer().setAddress(redisProperties.getPrefix()
+				+ redisProperties.getHost() + ":" + redisProperties.getPort());
+		if(StrUtil.isNotBlank(redisProperties.getUsername())){
+			config.useSingleServer().setUsername(redisProperties.getUsername());
+		}
+		if(StrUtil.isNotBlank(redisProperties.getPassword())){
+			config.useSingleServer().setPassword(redisProperties.getPassword());
+		}
+		//指定序列化工具
+		config.setCodec(new JsonJacksonCodec());
+		return Redisson.create(config);
+	}
+
+}

+ 20 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/config/RestTemplateConfiguration.java

@@ -0,0 +1,20 @@
+
+package com.kxs.common.core.config;
+
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author 没秃顶的码农
+ * @date 2023/2/1 RestTemplate
+ */
+@AutoConfiguration
+public class RestTemplateConfiguration {
+
+	@Bean
+	public RestTemplate restTemplate() {
+		return new RestTemplate();
+	}
+
+}

+ 55 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/config/TaskExecutorConfiguration.java

@@ -0,0 +1,55 @@
+package com.kxs.common.core.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.scheduling.annotation.AsyncConfigurer;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.Optional;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * @author 没秃顶的码农
+ * @date 2022/5/20
+ */
+@AutoConfiguration
+public class TaskExecutorConfiguration implements AsyncConfigurer {
+
+	/**
+	 * 获取当前机器的核数, 不一定准确 请根据实际场景 CPU密集 || IO 密集
+	 */
+	public static final int CPU_NUM = Runtime.getRuntime().availableProcessors();
+
+	@Value("${thread.pool.corePoolSize:}")
+	private Optional<Integer> corePoolSize;
+
+	@Value("${thread.pool.maxPoolSize:}")
+	private Optional<Integer> maxPoolSize;
+
+	@Value("${thread.pool.queueCapacity:}")
+	private Optional<Integer> queueCapacity;
+
+	@Value("${thread.pool.awaitTerminationSeconds:}")
+	private Optional<Integer> awaitTerminationSeconds;
+
+	@Override
+	@Bean
+	public Executor getAsyncExecutor() {
+		ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
+		// 核心线程大小 默认区 CPU 数量
+		taskExecutor.setCorePoolSize(corePoolSize.orElse(CPU_NUM));
+		// 最大线程大小 默认区 CPU * 2 数量
+		taskExecutor.setMaxPoolSize(maxPoolSize.orElse(CPU_NUM * 2));
+		// 队列最大容量
+		taskExecutor.setQueueCapacity(queueCapacity.orElse(500));
+		taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+		taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
+		taskExecutor.setAwaitTerminationSeconds(awaitTerminationSeconds.orElse(60));
+		taskExecutor.setThreadNamePrefix("LKB-Thread-");
+		taskExecutor.initialize();
+		return taskExecutor;
+	}
+
+}

+ 55 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/config/WebMvcConfiguration.java

@@ -0,0 +1,55 @@
+
+package com.kxs.common.core.config;
+
+import cn.hutool.core.date.DatePattern;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.context.MessageSource;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.support.ReloadableResourceBundleMessageSource;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.SERVLET;
+
+/**
+ * @author 没秃顶的码农
+ * @date 2023-06-24
+ * <p>
+ * 注入自自定义SQL 过滤
+ */
+@AutoConfiguration
+@ConditionalOnWebApplication(type = SERVLET)
+public class WebMvcConfiguration implements WebMvcConfigurer {
+
+	/**
+	 * 增加GET请求参数中时间类型转换 {@link com.kxs.common.core.jackson.SkyJavaTimeModule}
+	 * <ul>
+	 * <li>HH:mm:ss -> LocalTime</li>
+	 * <li>yyyy-MM-dd -> LocalDate</li>
+	 * <li>yyyy-MM-dd HH:mm:ss -> LocalDateTime</li>
+	 * </ul>
+	 * @param registry
+	 */
+	@Override
+	public void addFormatters(FormatterRegistry registry) {
+		DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
+		registrar.setTimeFormatter(DatePattern.NORM_TIME_FORMATTER);
+		registrar.setDateFormatter(DatePattern.NORM_DATE_FORMATTER);
+		registrar.setDateTimeFormatter(DatePattern.NORM_DATETIME_FORMATTER);
+		registrar.registerFormatters(registry);
+	}
+
+	/**
+	 * 系统国际化文件配置
+	 * @return MessageSource
+	 */
+	@Bean
+	public MessageSource messageSource() {
+		ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
+		messageSource.setBasename("classpath:i18n/messages");
+		return messageSource;
+	}
+
+}

+ 53 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/CacheConstants.java

@@ -0,0 +1,53 @@
+
+package com.kxs.common.core.constant;
+
+/**
+ * @author 没秃顶的码农
+ * @date 2021年01月01日
+ * <p>
+ * 缓存的key 常量
+ */
+public interface CacheConstants {
+
+	/**
+	 * oauth 缓存前缀
+	 */
+	String PROJECT_OAUTH_ACCESS = "token::access_token";
+
+	/**
+	 * 验证码前缀
+	 */
+	String DEFAULT_CODE_KEY = "DEFAULT_CODE_KEY:";
+
+	/**
+	 * 菜单信息缓存
+	 */
+	String MENU_DETAILS = "kxs_menu_details";
+
+	/**
+	 * 用户信息缓存
+	 */
+	String USER_DETAILS = "kxs_user_details";
+
+	/**
+	 * 字典信息缓存
+	 */
+	String DICT_DETAILS = "kxs_dict_details";
+
+
+	/**
+	 * 角色信息缓存
+	 */
+	String ROLE_DETAILS = "kxs_role_details";
+
+
+	/**
+	 * oauth 客户端信息
+	 */
+	String CLIENT_DETAILS_KEY = "kxs:client:details";
+
+	/**
+	 * 参数缓存
+	 */
+	String PARAMS_DETAILS = "kxs_params_details";
+}

+ 77 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/CommonConstants.java

@@ -0,0 +1,77 @@
+
+package com.kxs.common.core.constant;
+
+/**
+ * 公共常量
+ *
+ * @author 没秃顶的码农
+ * @date 2023/10/29
+ */
+public interface CommonConstants {
+
+    /**
+     * 删除
+     */
+    String STATUS_DEL = "1";
+
+    /**
+     * 正常
+     */
+    String STATUS_NORMAL = "0";
+
+    /**
+     * 锁定
+     */
+    String STATUS_LOCK = "9";
+
+    /**
+     * 菜单树根节点
+     */
+    Long MENU_TREE_ROOT_ID = -1L;
+
+    /**
+     * 菜单
+     */
+    String MENU = "0";
+
+    /**
+     * 编码
+     */
+    String UTF8 = "UTF-8";
+
+    /**
+     * JSON 资源
+     */
+    String CONTENT_TYPE = "application/json; charset=utf-8";
+
+    /**
+     * 包名
+     */
+    String PACKAGE_NAME = "com.kxs";
+
+    /**
+     * 成功标记
+     */
+    Integer SUCCESS = 1;
+
+    /**
+     * 失败标记
+     */
+    Integer FAIL = 0;
+
+    /**
+     * 当前页
+     */
+    String CURRENT = "pageNum";
+
+    /**
+     * size
+     */
+    String SIZE = "pageSize";
+
+    /**
+     * 请求开始时间
+     */
+    String REQUEST_START_TIME = "REQUEST-START-TIME";
+
+}

+ 136 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/SecurityConstants.java

@@ -0,0 +1,136 @@
+
+package com.kxs.common.core.constant;
+
+/**
+ * @author 没秃顶的码农
+ * @date 2023/2/1
+ */
+public interface SecurityConstants {
+
+	/**
+	 * 角色前缀
+	 */
+	String ROLE = "ROLE_";
+
+	/**
+	 * 三方登录模式
+	 */
+	String OPEN = "open";
+
+
+	/**
+	 * 三方登录参数
+	 */
+	String OPEN_PARAMETER_NAME = "open_code";
+
+	/**
+	 * BEARER
+	 */
+	String TOKEN_TYPE = "Bearer";
+
+	/**
+	 * 内部
+	 */
+	String FROM_IN = "Y";
+
+	/**
+	 * 标志
+	 */
+	String FROM = "from";
+
+	/**
+	 * 请求header
+	 */
+	String HEADER_FROM_IN = FROM + "=" + FROM_IN;
+
+	/**
+	 * 默认登录URL
+	 */
+	String OAUTH_TOKEN_URL = "/oauth2/token";
+
+	/**
+	 * grant_type
+	 */
+	String REFRESH_TOKEN = "refresh_token";
+
+	/**
+	 * 手机号登录
+	 */
+	String MOBILE = "mobile";
+
+	/**
+	 * 客户端
+	 */
+	String KXS = "kxs";
+
+	/**
+	 * {bcrypt} 加密的特征码
+	 */
+	String BCRYPT = "{bcrypt}";
+
+	/**
+	 * {noop} 加密的特征码
+	 */
+	String NOOP = "{noop}";
+
+	/***
+	 * 资源服务器默认bean名称
+	 */
+	String RESOURCE_SERVER_CONFIGURER = "resourceServerConfigurerAdapter";
+
+	/**
+	 * 用户名
+	 */
+	String USERNAME = "username";
+
+	/**
+	 * 用户信息
+	 */
+	String DETAILS_USER = "user_info";
+
+	/**
+	 * 用户ID
+	 */
+	String DETAILS_USER_ID = "user_id";
+
+	/**
+	 * 协议字段
+	 */
+	String DETAILS_LICENSE = "license";
+
+	/**
+	 * 验证码有效期,默认 60秒
+	 */
+	long CODE_TIME = 60;
+
+	/**
+	 * 验证码长度
+	 */
+	String CODE_SIZE = "6";
+
+	/**
+	 * 客户端模式
+	 */
+	String CLIENT_CREDENTIALS = "client_credentials";
+
+	/**
+	 * 客户端ID
+	 */
+	String CLIENT_ID = "clientId";
+
+	/**
+	 * 短信登录 参数名称
+	 */
+	String SMS_PARAMETER_NAME = "mobile";
+
+	/**
+	 * 授权码模式confirm
+	 */
+	String CUSTOM_CONSENT_PAGE_URI = "/token/confirm_access";
+
+	/**
+	 * 项目的license
+	 */
+	String PROJECT_LICENSE = "https://apigateway.kexiaoshuang.com";
+
+}

+ 55 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/ServiceNameConstants.java

@@ -0,0 +1,55 @@
+
+
+package com.kxs.common.core.constant;
+
+
+import java.time.Duration;
+
+/**
+ * 服务名称
+ *
+ * @author Lxq
+ * @date 2023-12-04
+ */
+public interface ServiceNameConstants {
+
+	String CLIENT_NAME = "http://";
+
+	/**
+	 * 请求超时时间
+	 * 单位:秒
+	 */
+	Duration REQ_TIMEOUT = Duration.ofSeconds(30);
+
+	/**
+	 * 系统模块
+	 */
+	String SYSTEM_SERVICE = "lkb-system-biz";
+
+	/**
+	 * merchant模块
+	 */
+	String MCH_SERVICE = "lkb-merchant-biz";
+
+	/**
+	 * 交易模块
+	 */
+	String TRADE_SERVICE = "lkb-trade-biz";
+
+	/**
+	 * 团购模块
+	 */
+	String GROUPON_SERVICE = "lkb-groupon-biz";
+
+	/**
+	 * 积分模块
+	 */
+	String POINT_SERVICE = "lkb-point-biz";
+
+	/**
+	 * 商城模块
+	 */
+	String MALL_SERVICE = "lkb-mall-biz";
+
+	String DCM_SERVICE = "lkb-dcm-biz";
+}

+ 37 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/enums/DictTypeEnum.java

@@ -0,0 +1,37 @@
+package com.kxs.common.core.constant.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * <p>
+ * 字典类型
+ *
+ * @author 没秃顶的码农
+ * @date 2023/10/27
+ */
+@Getter
+@RequiredArgsConstructor
+public enum DictTypeEnum {
+
+	/**
+	 * 字典类型-系统内置(不可修改)
+	 */
+	SYSTEM("1", "系统内置"),
+
+	/**
+	 * 字典类型-业务类型
+	 */
+	BIZ("0", "业务类");
+
+	/**
+	 * 类型
+	 */
+	private final String type;
+
+	/**
+	 * 描述
+	 */
+	private final String description;
+
+}

+ 50 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/enums/ErrorTypeEnum.java

@@ -0,0 +1,50 @@
+package com.kxs.common.core.constant.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * 错误类型枚举
+ *
+ * @author 没秃顶的码农
+ * @date 2023/10/29
+ */
+@Getter
+@RequiredArgsConstructor
+public enum ErrorTypeEnum {
+
+	DECRYPT_ERROR(-1, "解密异常"),
+	TOKEN_EXPIRED(-1, "令牌已过期"),
+	TOKEN_EMPTY(-1, "令牌不存在"),
+	CLIENT_NULL(-1, "客户端查询异常,请检查数据库链接"),
+	ACCESS_DENIED(-1, "不允许范访问"),
+	BAD_CREDENTIALS(-1, "用户名或密码错误"),
+	CREDENTIALS_EXPIRED(-1, "用户凭证已过期"),
+	USER_DISABLED(-1, "用户已失效"),
+	ACCOUNT_DISABLED(-1, "用户帐号已过期"),
+	ACCOUNT_LOCKED(-1, "用户帐号已被锁定"),
+	USER_NOT_FOUND(-1, "未找到用户"),
+	PHONE_NOT_FOUND(-1, "手机号不存在"),
+	VERIFICATION_CODE_IS_ILLEGAL(-1, "验证码不正确"),
+	VERIFICATION_CODE_IS_EMPTY(-1, "验证码不能为空"),
+	UNAUTHORIZED_CLIENT(-1, "客户端不允许登陆"),
+
+	SYS_PARAM_DELETE_SYSTEM(-1, "系统内置参数不能删除"),
+	SYS_DICT_DELETE_SYSTEM(-1, "系统内置字典不允许删除"),
+	SYS_MENU_DELETE_EXISTING(-1, "菜单存在下级节点 删除失败"),
+	SYS_USER_USERNAME_EXISTING(-1, "用户名已存在"),
+	REMOTE_DATA_ERROR(-1, "远程数据调用错误"),
+	SYS_USER_UPDATE_PASSWORD_ERROR(-1, "用户原密码错误"),
+	SYS_DICT_UPDATE_SYSTEM(-1, "系统内置字典不能修改"),
+	SYS_MORNING_IS_NULL(-1, "该晨会不存在"),
+	SYS_SCHOOL_IS_NULL(-1, "该学堂不存在"),
+	SYS_TYPE_ERROR(-1, "类型异常"),
+	PARAM_ERROR(0, "参数错误"),
+
+	;
+
+	private final int code;
+
+	private final String description;
+
+}

+ 37 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/enums/LoginTypeEnum.java

@@ -0,0 +1,37 @@
+package com.kxs.common.core.constant.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * <p>
+ * 社交登录类型
+ *
+ * @author 没秃顶的码农
+ * @date 2023/10/27
+ */
+@Getter
+@RequiredArgsConstructor
+public enum LoginTypeEnum {
+
+	/**
+	 * 账号密码登录
+	 */
+	PWD("PWD", "账号密码登录"),
+
+	/**
+	 * 验证码登录
+	 */
+	SMS("SMS", "验证码登录");
+
+	/**
+	 * 类型
+	 */
+	private final String type;
+
+	/**
+	 * 描述
+	 */
+	private final String description;
+
+}

+ 42 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/enums/MenuTypeEnum.java

@@ -0,0 +1,42 @@
+package com.kxs.common.core.constant.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * <p>
+ * 菜单类型
+ *
+ * @author 没秃顶的码农
+ * @date 2023/10/27
+ */
+@Getter
+@RequiredArgsConstructor
+public enum MenuTypeEnum {
+
+	/**
+	 * 左侧菜单
+	 */
+	LEFT_MENU("0", "left"),
+
+	/**
+	 * 顶部菜单
+	 */
+	TOP_MENU("2", "top"),
+
+	/**
+	 * 按钮
+	 */
+	BUTTON("1", "button");
+
+	/**
+	 * 类型
+	 */
+	private final String type;
+
+	/**
+	 * 描述
+	 */
+	private final String description;
+
+}

+ 39 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/enums/PrintChannelCodeEnum.java

@@ -0,0 +1,39 @@
+package com.kxs.common.core.constant.enums;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 打印通道代码枚举
+ *
+ * @author lixueqiang
+ * @date 2025/07/22
+ */
+@Getter
+@RequiredArgsConstructor
+public enum PrintChannelCodeEnum {
+
+    FEIE("feie", "飞鹅打印"),
+    SUNMI("sunmi", "商米打印")
+    ;
+
+    private final String code;
+
+    private final String description;
+
+    private static final Map<String, PrintChannelCodeEnum> CHANNEL_CODE_ENUM_MAP = new HashMap<>();
+
+    static {
+        for (PrintChannelCodeEnum value : PrintChannelCodeEnum.values()) {
+            CHANNEL_CODE_ENUM_MAP.put(value.getCode(), value);
+        }
+    }
+
+    public static PrintChannelCodeEnum getChannelCode(@NotBlank String ifCode) {
+        return CHANNEL_CODE_ENUM_MAP.get(ifCode);
+    }
+}

+ 38 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/enums/UserStatusEnum.java

@@ -0,0 +1,38 @@
+package com.kxs.common.core.constant.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * <p>
+ * 登录类型
+ *
+ * @author 没秃顶的码农
+ * @date 2023/10/27
+ */
+@Getter
+@RequiredArgsConstructor
+public enum UserStatusEnum {
+
+	/**
+	 * 账号密码登录
+	 */
+	REMOVE(0, "注销"),
+	NORMAL(1, "正常"),
+
+	/**
+	 * 锁定不可登陆和提现
+	 */
+	LOCKED(1, "锁定");
+
+	/**
+	 * 类型
+	 */
+	private final Integer code;
+
+	/**
+	 * 描述
+	 */
+	private final String description;
+
+}

Some files were not shown because too many files changed in this diff