zheng.song 1 month ago
commit
c741b5a4be
100 changed files with 9464 additions and 0 deletions
  1. 39 0
      .flattened-pom.xml
  2. 8 0
      .idea/.gitignore
  3. 7 0
      .idea/MarsCodeWorkspaceAppSettings.xml
  4. 83 0
      .idea/compiler.xml
  5. 81 0
      .idea/encodings.xml
  6. 6 0
      .idea/inspectionProfiles/Project_Default.xml
  7. 40 0
      .idea/jarRepositories.xml
  8. 14 0
      .idea/misc.xml
  9. 9 0
      .idea/print-backend.iml
  10. 6 0
      .idea/vcs.xml
  11. 20 0
      LICENSE
  12. 23 0
      README.md
  13. 263 0
      hs_err_pid125144.log
  14. 263 0
      hs_err_pid207140.log
  15. 221 0
      hs_err_pid29328.log
  16. 660 0
      hs_err_pid39364.log
  17. 944 0
      hs_err_pid77932.log
  18. 221 0
      hs_err_pid97900.log
  19. 582 0
      jt-dependencies/.flattened-pom.xml
  20. 725 0
      jt-dependencies/pom.xml
  21. 45 0
      jt-framework/.flattened-pom.xml
  22. 179 0
      jt-framework/jt-common/.flattened-pom.xml
  23. 156 0
      jt-framework/jt-common/pom.xml
  24. 59 0
      jt-framework/jt-common/src/main/java/com/fhs/trans/service/AutoTransable.java
  25. 34 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/infra/logger/ApiAccessLogCommonApi.java
  26. 34 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/infra/logger/ApiErrorLogCommonApi.java
  27. 103 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/infra/logger/dto/ApiAccessLogCreateReqDTO.java
  28. 68 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/infra/logger/dto/ApiErrorLogCreateReqDTO.java
  29. 4 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/infra/package-info.java
  30. 4 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/package-info.java
  31. 26 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/dict/DictDataCommonApi.java
  32. 22 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/dict/dto/DictDataRespDTO.java
  33. 34 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/logger/OperateLogCommonApi.java
  34. 53 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/logger/dto/OperateLogCreateReqDTO.java
  35. 52 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/oauth2/OAuth2TokenCommonApi.java
  36. 33 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java
  37. 32 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenCreateReqDTO.java
  38. 30 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenRespDTO.java
  39. 4 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/package-info.java
  40. 43 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/permission/PermissionCommonApi.java
  41. 28 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/permission/dto/CompanytDataPermissionRespDTO.java
  42. 28 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/permission/dto/DeptDataPermissionRespDTO.java
  43. 29 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/tenant/TenantCommonApi.java
  44. 11 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/constant/DeviceConstant.java
  45. 15 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/core/ArrayValuable.java
  46. 22 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/core/KeyValue.java
  47. 46 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/CommonStatusEnum.java
  48. 46 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/DateIntervalEnum.java
  49. 21 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/DocumentEnum.java
  50. 52 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/EffectiveStatusEnum.java
  51. 60 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/MaintenanceTaskTypeEnum.java
  52. 40 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/RpcConstants.java
  53. 40 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/TerminalEnum.java
  54. 39 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/UserTypeEnum.java
  55. 36 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/WebFilterOrderEnum.java
  56. 32 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/exception/ErrorCode.java
  57. 60 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/exception/ServerException.java
  58. 60 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/exception/ServiceException.java
  59. 43 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/exception/enums/GlobalErrorCodeConstants.java
  60. 48 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/exception/enums/ServiceErrorCodeRange.java
  61. 77 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/exception/util/ServiceExceptionUtil.java
  62. 6 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/package-info.java
  63. 128 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/pojo/CommonResult.java
  64. 36 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/pojo/PageParam.java
  65. 41 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/pojo/PageResult.java
  66. 19 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/pojo/SortablePageParam.java
  67. 37 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/pojo/SortingField.java
  68. 187 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/AesUtil.java
  69. 90 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/DateStandardizer.java
  70. 271 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/EmployeeIdGenerator.java
  71. 61 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/cache/CacheUtils.java
  72. 58 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/collection/ArrayUtils.java
  73. 352 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/collection/CollectionUtils.java
  74. 68 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/collection/MapUtils.java
  75. 19 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/collection/SetUtils.java
  76. 149 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/date/DateUtils.java
  77. 328 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/date/LocalDateTimeUtils.java
  78. 175 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/http/HttpUtils.java
  79. 61 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/io/FileUtils.java
  80. 28 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/io/IoUtils.java
  81. 210 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/json/JsonUtils.java
  82. 37 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/json/databind/NumberSerializer.java
  83. 27 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java
  84. 26 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java
  85. 30 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/monitor/TracerUtils.java
  86. 131 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/number/MoneyUtils.java
  87. 78 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/number/NumberUtils.java
  88. 69 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/object/BeanUtils.java
  89. 63 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/object/ObjectUtils.java
  90. 67 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/object/PageUtils.java
  91. 7 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/package-info.java
  92. 105 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/servlet/ServletUtils.java
  93. 123 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/spring/SpringExpressionUtils.java
  94. 24 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/spring/SpringUtils.java
  95. 107 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/string/StrUtils.java
  96. 61 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/validation/ValidationUtils.java
  97. 35 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/validation/InEnum.java
  98. 44 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/validation/InEnumCollectionValidator.java
  99. 43 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/validation/InEnumValidator.java
  100. 0 0
      jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/validation/Mobile.java

+ 39 - 0
.flattened-pom.xml

@@ -0,0 +1,39 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4
+  <modelVersion>4.0.0</modelVersion>
5
+  <groupId>com.jt.cloud</groupId>
6
+  <artifactId>gtx-backend</artifactId>
7
+  <version>2.6.1-SNAPSHOT</version>
8
+  <packaging>pom</packaging>
9
+  <name>gtx-backend</name>
10
+  <description>jt项目基础脚手架</description>
11
+  <repositories>
12
+    <repository>
13
+      <id>huaweicloud</id>
14
+      <name>huawei</name>
15
+      <url>https://mirrors.huaweicloud.com/repository/maven/</url>
16
+    </repository>
17
+    <repository>
18
+      <id>aliyunmaven</id>
19
+      <name>aliyun</name>
20
+      <url>https://maven.aliyun.com/repository/public</url>
21
+    </repository>
22
+    <repository>
23
+      <snapshots>
24
+        <enabled>false</enabled>
25
+      </snapshots>
26
+      <id>spring-milestones</id>
27
+      <name>Spring Milestones</name>
28
+      <url>https://repo.spring.io/milestone</url>
29
+    </repository>
30
+    <repository>
31
+      <releases>
32
+        <enabled>false</enabled>
33
+      </releases>
34
+      <id>spring-snapshots</id>
35
+      <name>Spring Snapshots</name>
36
+      <url>https://repo.spring.io/snapshot</url>
37
+    </repository>
38
+  </repositories>
39
+</project>

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
1
+# 默认忽略的文件
2
+/shelf/
3
+/workspace.xml
4
+# 基于编辑器的 HTTP 客户端请求
5
+/httpRequests/
6
+# Datasource local storage ignored files
7
+/dataSources/
8
+/dataSources.local.xml

+ 7 - 0
.idea/MarsCodeWorkspaceAppSettings.xml

@@ -0,0 +1,7 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project version="4">
3
+  <component name="com.codeverse.userSettings.MarscodeWorkspaceAppSettingsState">
4
+    <option name="chatAppRouterInfo" value="builder/69ddf2a1b77c5203a0dfdd9c" />
5
+    <option name="progress" value="0.26" />
6
+  </component>
7
+</project>

+ 83 - 0
.idea/compiler.xml

@@ -0,0 +1,83 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project version="4">
3
+  <component name="CompilerConfiguration">
4
+    <annotationProcessing>
5
+      <profile default="true" name="Default" enabled="true" />
6
+      <profile name="Annotation profile for gtx-backend" enabled="true">
7
+        <sourceOutputDir name="target/generated-sources/annotations" />
8
+        <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
9
+        <outputRelativeToContentRoot value="true" />
10
+        <processorPath useClasspath="false">
11
+          <entry name="$PROJECT_DIR$/../repository/org/springframework/boot/spring-boot-configuration-processor/3.4.5/spring-boot-configuration-processor-3.4.5.jar" />
12
+          <entry name="$PROJECT_DIR$/../repository/org/projectlombok/lombok/1.18.38/lombok-1.18.38.jar" />
13
+          <entry name="$PROJECT_DIR$/../repository/org/projectlombok/lombok-mapstruct-binding/0.2.0/lombok-mapstruct-binding-0.2.0.jar" />
14
+          <entry name="$PROJECT_DIR$/../repository/org/mapstruct/mapstruct-processor/1.6.3/mapstruct-processor-1.6.3.jar" />
15
+        </processorPath>
16
+        <module name="jt-module-infra-api" />
17
+        <module name="jt-spring-boot-starter-biz-ip" />
18
+        <module name="jt-spring-boot-starter-mq" />
19
+        <module name="jt-spring-boot-starter-biz-tenant" />
20
+        <module name="jt-module-print-server" />
21
+        <module name="jt-spring-boot-starter-redis" />
22
+        <module name="jt-module-company-server" />
23
+        <module name="jt-spring-boot-starter-monitor" />
24
+        <module name="jt-spring-boot-starter-job" />
25
+        <module name="jt-module-company-api" />
26
+        <module name="jt-module-print-api" />
27
+        <module name="jt-module-bpm-server" />
28
+        <module name="jt-spring-boot-starter-web" />
29
+        <module name="jt-module-system-server" />
30
+        <module name="jt-spring-boot-starter-test" />
31
+        <module name="jt-spring-boot-starter-protection" />
32
+        <module name="jt-common" />
33
+        <module name="jt-spring-boot-starter-biz-data-permission" />
34
+        <module name="jt-spring-boot-starter-mybatis" />
35
+        <module name="jt-module-report-api" />
36
+        <module name="jt-spring-boot-starter-rpc" />
37
+        <module name="jt-module-infra-server" />
38
+        <module name="jt-module-report-server" />
39
+        <module name="jt-module-system-api" />
40
+        <module name="jt-spring-boot-starter-websocket" />
41
+        <module name="jt-spring-boot-starter-security" />
42
+        <module name="jt-gateway" />
43
+        <module name="jt-spring-boot-starter-excel" />
44
+        <module name="jt-module-bpm-api" />
45
+        <module name="jt-spring-boot-starter-env" />
46
+      </profile>
47
+    </annotationProcessing>
48
+  </component>
49
+  <component name="JavacSettings">
50
+    <option name="ADDITIONAL_OPTIONS_OVERRIDE">
51
+      <module name="jt-common" options="-parameters" />
52
+      <module name="jt-gateway" options="-parameters" />
53
+      <module name="jt-module-bpm-api" options="-parameters" />
54
+      <module name="jt-module-bpm-server" options="-parameters" />
55
+      <module name="jt-module-company-api" options="-parameters" />
56
+      <module name="jt-module-company-server" options="-parameters" />
57
+      <module name="jt-module-infra-api" options="-parameters" />
58
+      <module name="jt-module-infra-server" options="-parameters" />
59
+      <module name="jt-module-print-api" options="-parameters" />
60
+      <module name="jt-module-print-server" options="-parameters" />
61
+      <module name="jt-module-report-api" options="-parameters" />
62
+      <module name="jt-module-report-server" options="-parameters" />
63
+      <module name="jt-module-system-api" options="-parameters" />
64
+      <module name="jt-module-system-server" options="-parameters" />
65
+      <module name="jt-spring-boot-starter-biz-data-permission" options="-parameters" />
66
+      <module name="jt-spring-boot-starter-biz-ip" options="-parameters" />
67
+      <module name="jt-spring-boot-starter-biz-tenant" options="-parameters" />
68
+      <module name="jt-spring-boot-starter-env" options="-parameters" />
69
+      <module name="jt-spring-boot-starter-excel" options="-parameters" />
70
+      <module name="jt-spring-boot-starter-job" options="-parameters" />
71
+      <module name="jt-spring-boot-starter-monitor" options="-parameters" />
72
+      <module name="jt-spring-boot-starter-mq" options="-parameters" />
73
+      <module name="jt-spring-boot-starter-mybatis" options="-parameters" />
74
+      <module name="jt-spring-boot-starter-protection" options="-parameters" />
75
+      <module name="jt-spring-boot-starter-redis" options="-parameters" />
76
+      <module name="jt-spring-boot-starter-rpc" options="-parameters" />
77
+      <module name="jt-spring-boot-starter-security" options="-parameters" />
78
+      <module name="jt-spring-boot-starter-test" options="-parameters" />
79
+      <module name="jt-spring-boot-starter-web" options="-parameters" />
80
+      <module name="jt-spring-boot-starter-websocket" options="-parameters" />
81
+    </option>
82
+  </component>
83
+</project>

+ 81 - 0
.idea/encodings.xml

@@ -0,0 +1,81 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project version="4">
3
+  <component name="Encoding">
4
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-common/src/main/java" charset="UTF-8" />
5
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-common/src/main/resources" charset="UTF-8" />
6
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-biz-data-permission/src/main/java" charset="UTF-8" />
7
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-biz-data-permission/src/main/resources" charset="UTF-8" />
8
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-biz-ip/src/main/java" charset="UTF-8" />
9
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-biz-ip/src/main/resources" charset="UTF-8" />
10
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-biz-tenant/src/main/java" charset="UTF-8" />
11
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-biz-tenant/src/main/resources" charset="UTF-8" />
12
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-env/src/main/java" charset="UTF-8" />
13
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-env/src/main/resources" charset="UTF-8" />
14
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-excel/src/main/java" charset="UTF-8" />
15
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-excel/src/main/resources" charset="UTF-8" />
16
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-job/src/main/java" charset="UTF-8" />
17
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-job/src/main/resources" charset="UTF-8" />
18
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-monitor/src/main/java" charset="UTF-8" />
19
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-monitor/src/main/resources" charset="UTF-8" />
20
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-mq/src/main/java" charset="UTF-8" />
21
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-mq/src/main/resources" charset="UTF-8" />
22
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-mybatis/src/main/java" charset="UTF-8" />
23
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-mybatis/src/main/resources" charset="UTF-8" />
24
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-protection/src/main/java" charset="UTF-8" />
25
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-protection/src/main/resources" charset="UTF-8" />
26
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-redis/src/main/java" charset="UTF-8" />
27
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-redis/src/main/resources" charset="UTF-8" />
28
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-rpc/src/main/java" charset="UTF-8" />
29
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-rpc/src/main/resources" charset="UTF-8" />
30
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-security/src/main/java" charset="UTF-8" />
31
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-security/src/main/resources" charset="UTF-8" />
32
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-test/src/main/java" charset="UTF-8" />
33
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-test/src/main/resources" charset="UTF-8" />
34
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-web/src/main/java" charset="UTF-8" />
35
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-web/src/main/resources" charset="UTF-8" />
36
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-websocket/src/main/java" charset="UTF-8" />
37
+    <file url="file://$PROJECT_DIR$/jt-framework/jt-spring-boot-starter-websocket/src/main/resources" charset="UTF-8" />
38
+    <file url="file://$PROJECT_DIR$/jt-framework/src/main/java" charset="UTF-8" />
39
+    <file url="file://$PROJECT_DIR$/jt-framework/src/main/resources" charset="UTF-8" />
40
+    <file url="file://$PROJECT_DIR$/jt-gateway/src/main/java" charset="UTF-8" />
41
+    <file url="file://$PROJECT_DIR$/jt-gateway/src/main/resources" charset="UTF-8" />
42
+    <file url="file://$PROJECT_DIR$/jt-module-bpm/jt-module-bpm-api/src/main/java" charset="UTF-8" />
43
+    <file url="file://$PROJECT_DIR$/jt-module-bpm/jt-module-bpm-api/src/main/resources" charset="UTF-8" />
44
+    <file url="file://$PROJECT_DIR$/jt-module-bpm/jt-module-bpm-server/src/main/java" charset="UTF-8" />
45
+    <file url="file://$PROJECT_DIR$/jt-module-bpm/jt-module-bpm-server/src/main/resources" charset="UTF-8" />
46
+    <file url="file://$PROJECT_DIR$/jt-module-bpm/src/main/java" charset="UTF-8" />
47
+    <file url="file://$PROJECT_DIR$/jt-module-bpm/src/main/resources" charset="UTF-8" />
48
+    <file url="file://$PROJECT_DIR$/jt-module-company/jt-module-company-api/src/main/java" charset="UTF-8" />
49
+    <file url="file://$PROJECT_DIR$/jt-module-company/jt-module-company-api/src/main/resources" charset="UTF-8" />
50
+    <file url="file://$PROJECT_DIR$/jt-module-company/jt-module-company-server/src/main/java" charset="UTF-8" />
51
+    <file url="file://$PROJECT_DIR$/jt-module-company/jt-module-company-server/src/main/resources" charset="UTF-8" />
52
+    <file url="file://$PROJECT_DIR$/jt-module-company/src/main/java" charset="UTF-8" />
53
+    <file url="file://$PROJECT_DIR$/jt-module-company/src/main/resources" charset="UTF-8" />
54
+    <file url="file://$PROJECT_DIR$/jt-module-infra/jt-module-infra-api/src/main/java" charset="UTF-8" />
55
+    <file url="file://$PROJECT_DIR$/jt-module-infra/jt-module-infra-api/src/main/resources" charset="UTF-8" />
56
+    <file url="file://$PROJECT_DIR$/jt-module-infra/jt-module-infra-server/src/main/java" charset="UTF-8" />
57
+    <file url="file://$PROJECT_DIR$/jt-module-infra/jt-module-infra-server/src/main/resources" charset="UTF-8" />
58
+    <file url="file://$PROJECT_DIR$/jt-module-infra/src/main/java" charset="UTF-8" />
59
+    <file url="file://$PROJECT_DIR$/jt-module-infra/src/main/resources" charset="UTF-8" />
60
+    <file url="file://$PROJECT_DIR$/jt-module-print/jt-module-print-api/src/main/java" charset="UTF-8" />
61
+    <file url="file://$PROJECT_DIR$/jt-module-print/jt-module-print-api/src/main/resources" charset="UTF-8" />
62
+    <file url="file://$PROJECT_DIR$/jt-module-print/jt-module-print-server/src/main/java" charset="UTF-8" />
63
+    <file url="file://$PROJECT_DIR$/jt-module-print/jt-module-print-server/src/main/resources" charset="UTF-8" />
64
+    <file url="file://$PROJECT_DIR$/jt-module-print/src/main/java" charset="UTF-8" />
65
+    <file url="file://$PROJECT_DIR$/jt-module-print/src/main/resources" charset="UTF-8" />
66
+    <file url="file://$PROJECT_DIR$/jt-module-report/jt-module-report-api/src/main/java" charset="UTF-8" />
67
+    <file url="file://$PROJECT_DIR$/jt-module-report/jt-module-report-api/src/main/resources" charset="UTF-8" />
68
+    <file url="file://$PROJECT_DIR$/jt-module-report/jt-module-report-server/src/main/java" charset="UTF-8" />
69
+    <file url="file://$PROJECT_DIR$/jt-module-report/jt-module-report-server/src/main/resources" charset="UTF-8" />
70
+    <file url="file://$PROJECT_DIR$/jt-module-report/src/main/java" charset="UTF-8" />
71
+    <file url="file://$PROJECT_DIR$/jt-module-report/src/main/resources" charset="UTF-8" />
72
+    <file url="file://$PROJECT_DIR$/jt-module-system/jt-module-system-api/src/main/java" charset="UTF-8" />
73
+    <file url="file://$PROJECT_DIR$/jt-module-system/jt-module-system-api/src/main/resources" charset="UTF-8" />
74
+    <file url="file://$PROJECT_DIR$/jt-module-system/jt-module-system-server/src/main/java" charset="UTF-8" />
75
+    <file url="file://$PROJECT_DIR$/jt-module-system/jt-module-system-server/src/main/resources" charset="UTF-8" />
76
+    <file url="file://$PROJECT_DIR$/jt-module-system/src/main/java" charset="UTF-8" />
77
+    <file url="file://$PROJECT_DIR$/jt-module-system/src/main/resources" charset="UTF-8" />
78
+    <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
79
+    <file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
80
+  </component>
81
+</project>

+ 6 - 0
.idea/inspectionProfiles/Project_Default.xml

@@ -0,0 +1,6 @@
1
+<component name="InspectionProjectProfileManager">
2
+  <profile version="1.0">
3
+    <option name="myName" value="Project Default" />
4
+    <inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
5
+  </profile>
6
+</component>

+ 40 - 0
.idea/jarRepositories.xml

@@ -0,0 +1,40 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project version="4">
3
+  <component name="RemoteRepositoriesConfiguration">
4
+    <remote-repository>
5
+      <option name="id" value="central" />
6
+      <option name="name" value="Central Repository" />
7
+      <option name="url" value="https://repo.maven.apache.org/maven2" />
8
+    </remote-repository>
9
+    <remote-repository>
10
+      <option name="id" value="aliyunmaven" />
11
+      <option name="name" value="aliyun" />
12
+      <option name="url" value="https://maven.aliyun.com/repository/public" />
13
+    </remote-repository>
14
+    <remote-repository>
15
+      <option name="id" value="spring-milestones" />
16
+      <option name="name" value="Spring Milestones" />
17
+      <option name="url" value="https://repo.spring.io/milestone" />
18
+    </remote-repository>
19
+    <remote-repository>
20
+      <option name="id" value="central" />
21
+      <option name="name" value="Maven Central repository" />
22
+      <option name="url" value="https://repo1.maven.org/maven2" />
23
+    </remote-repository>
24
+    <remote-repository>
25
+      <option name="id" value="spring-snapshots" />
26
+      <option name="name" value="Spring Snapshots" />
27
+      <option name="url" value="https://repo.spring.io/snapshot" />
28
+    </remote-repository>
29
+    <remote-repository>
30
+      <option name="id" value="jboss.community" />
31
+      <option name="name" value="JBoss Community repository" />
32
+      <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
33
+    </remote-repository>
34
+    <remote-repository>
35
+      <option name="id" value="huaweicloud" />
36
+      <option name="name" value="huawei" />
37
+      <option name="url" value="https://mirrors.huaweicloud.com/repository/maven/" />
38
+    </remote-repository>
39
+  </component>
40
+</project>

+ 14 - 0
.idea/misc.xml

@@ -0,0 +1,14 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project version="4">
3
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
4
+  <component name="MavenProjectsManager">
5
+    <option name="originalFiles">
6
+      <list>
7
+        <option value="$PROJECT_DIR$/pom.xml" />
8
+      </list>
9
+    </option>
10
+  </component>
11
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
12
+    <output url="file://$PROJECT_DIR$/out" />
13
+  </component>
14
+</project>

+ 9 - 0
.idea/print-backend.iml

@@ -0,0 +1,9 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<module type="JAVA_MODULE" version="4">
3
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
4
+    <exclude-output />
5
+    <content url="file://$MODULE_DIR$" />
6
+    <orderEntry type="inheritedJdk" />
7
+    <orderEntry type="sourceFolder" forTests="false" />
8
+  </component>
9
+</module>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project version="4">
3
+  <component name="VcsDirectoryMappings">
4
+    <mapping directory="" vcs="Git" />
5
+  </component>
6
+</project>

+ 20 - 0
LICENSE

@@ -0,0 +1,20 @@
1
+The MIT License (MIT)
2
+
3
+Copyright (c) 2021 jt-cloud
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+this software and associated documentation files (the "Software"), to deal in
7
+the Software without restriction, including without limitation the rights to
8
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+the Software, and to permit persons to whom the Software is furnished to do so,
10
+subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in all
13
+copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 23 - 0
README.md

@@ -0,0 +1,23 @@
1
+### 系统功能
2
+
3
+|     | 功能    | 描述                              |
4
+|-----|-------|---------------------------------|
5
+|     | 用户管理  | 用户是系统操作者,该功能主要完成系统用户配置          |
6
+| ⭐️  | 在线用户  | 当前系统中活跃用户状态监控,支持手动踢下线           |
7
+|     | 角色管理  | 角色菜单权限分配、设置角色按机构进行数据范围权限划分      |
8
+|     | 菜单管理  | 配置系统菜单、操作权限、按钮权限标识等,本地缓存提供性能    |
9
+|     | 部门管理  | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限  |
10
+|     | 岗位管理  | 配置系统用户所属担任职务                    |
11
+| 🚀  | 租户管理  | 配置系统租户,支持 SaaS 场景下的多租户功能        |
12
+| 🚀  | 租户套餐  | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限       |
13
+|     | 字典管理  | 对系统中经常使用的一些较为固定的数据进行维护          |
14
+| 🚀  | 短信管理  | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 |
15
+| 🚀  | 邮件管理  | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台       |
16
+| 🚀  | 站内信   | 系统内的消息通知,提供站内信模版、站内信消息          |
17
+| 🚀  | 操作日志  | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
18
+| ⭐️  | 登录日志  | 系统登录日志记录查询,包含登录异常               |
19
+| 🚀  | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务     |
20
+|     | 通知公告  | 系统通知公告信息发布维护                    |
21
+| 🚀  | 敏感词   | 配置系统敏感词,支持标签分组                  |
22
+| 🚀  | 应用管理  | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 |
23
+| 🚀  | 地区管理  | 展示省份、城市、区镇等城市信息,支持 IP 对应城市      |

File diff suppressed because it is too large
+ 263 - 0
hs_err_pid125144.log


File diff suppressed because it is too large
+ 263 - 0
hs_err_pid207140.log


File diff suppressed because it is too large
+ 221 - 0
hs_err_pid29328.log


File diff suppressed because it is too large
+ 660 - 0
hs_err_pid39364.log


File diff suppressed because it is too large
+ 944 - 0
hs_err_pid77932.log


File diff suppressed because it is too large
+ 221 - 0
hs_err_pid97900.log


+ 582 - 0
jt-dependencies/.flattened-pom.xml

@@ -0,0 +1,582 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4
+  <modelVersion>4.0.0</modelVersion>
5
+  <groupId>com.jt.cloud</groupId>
6
+  <artifactId>jt-dependencies</artifactId>
7
+  <version>2.6.1-SNAPSHOT</version>
8
+  <packaging>pom</packaging>
9
+  <name>jt-dependencies</name>
10
+  <description>基础 bom 文件,管理整个项目的依赖版本</description>
11
+  <properties>
12
+    <tika-core.version>3.1.0</tika-core.version>
13
+    <podam.version>8.0.2.RELEASE</podam.version>
14
+    <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
15
+    <opengauss.jdbc.version>5.1.0</opengauss.jdbc.version>
16
+    <mapstruct.version>1.6.3</mapstruct.version>
17
+    <fastjson.version>1.2.83</fastjson.version>
18
+    <weixin-java.version>4.7.5.B</weixin-java.version>
19
+    <mybatis.version>3.5.19</mybatis.version>
20
+    <opentracing.version>0.33.0</opentracing.version>
21
+    <kingbase.jdbc.version>8.6.0</kingbase.jdbc.version>
22
+    <rocketmq-spring.version>2.3.2</rocketmq-spring.version>
23
+    <ip2region.version>2.7.0</ip2region.version>
24
+    <awssdk.version>2.30.14</awssdk.version>
25
+    <dynamic-datasource.version>4.3.1</dynamic-datasource.version>
26
+    <redisson.version>3.41.0</redisson.version>
27
+    <pf4j-spring.version>0.9.0</pf4j-spring.version>
28
+    <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
29
+    <mqtt.version>1.2.5</mqtt.version>
30
+    <vertx.version>4.5.13</vertx.version>
31
+    <spring.boot.version>3.4.5</spring.boot.version>
32
+    <shardingsphere-jdbc-core.version>5.4.1</shardingsphere-jdbc-core.version>
33
+    <springdoc.version>2.8.3</springdoc.version>
34
+    <lock4j.version>2.2.7</lock4j.version>
35
+    <hutool-6.version>6.0.0-M19</hutool-6.version>
36
+    <jsoup.version>1.18.1</jsoup.version>
37
+    <mybatis-plus.version>3.5.12</mybatis-plus.version>
38
+    <fastexcel.version>1.2.0</fastexcel.version>
39
+    <knife4j.version>4.6.0</knife4j.version>
40
+    <lombok.version>1.18.36</lombok.version>
41
+    <flowable.version>7.0.1</flowable.version>
42
+    <skywalking.version>9.0.0</skywalking.version>
43
+    <mockito-inline.version>5.2.0</mockito-inline.version>
44
+    <justauth.version>1.16.7</justauth.version>
45
+    <velocity.version>2.4.1</velocity.version>
46
+    <reflections.version>0.10.2</reflections.version>
47
+    <bizlog-sdk.version>3.0.6</bizlog-sdk.version>
48
+    <mybatis-plus-join.version>1.5.4</mybatis-plus-join.version>
49
+    <hutool-5.version>5.8.35</hutool-5.version>
50
+    <revision>2.6.1-SNAPSHOT</revision>
51
+    <jsch.version>0.1.55</jsch.version>
52
+    <justauth-starter.version>1.4.0</justauth-starter.version>
53
+    <xxl-job.version>2.4.0</xxl-job.version>
54
+    <spring-boot-admin.version>3.4.5</spring-boot-admin.version>
55
+    <netty.version>4.1.116.Final</netty.version>
56
+    <jedis-mock.version>1.1.4</jedis-mock.version>
57
+    <zxing.version>3.5.2</zxing.version>
58
+    <guava.version>33.4.8-jre</guava.version>
59
+    <jimubi.version>1.9.5</jimubi.version>
60
+    <spring.cloud.version>2024.0.1</spring.cloud.version>
61
+    <anji-plus-captcha.version>1.4.0</anji-plus-captcha.version>
62
+    <spring.cloud.alibaba.version>2023.0.3.2</spring.cloud.alibaba.version>
63
+    <taos.version>3.3.3</taos.version>
64
+    <commons-net.version>3.11.1</commons-net.version>
65
+    <jimureport.version>2.0.0</jimureport.version>
66
+    <druid.version>1.2.24</druid.version>
67
+    <easy-trans.version>3.0.6</easy-trans.version>
68
+    <dm8.jdbc.version>8.1.3.140</dm8.jdbc.version>
69
+  </properties>
70
+  <dependencyManagement>
71
+    <dependencies>
72
+      <dependency>
73
+        <groupId>io.netty</groupId>
74
+        <artifactId>netty-bom</artifactId>
75
+        <version>${netty.version}</version>
76
+        <type>pom</type>
77
+        <scope>import</scope>
78
+      </dependency>
79
+      <dependency>
80
+        <groupId>org.springframework.boot</groupId>
81
+        <artifactId>spring-boot-dependencies</artifactId>
82
+        <version>${spring.boot.version}</version>
83
+        <type>pom</type>
84
+        <scope>import</scope>
85
+      </dependency>
86
+      <dependency>
87
+        <groupId>org.springframework.cloud</groupId>
88
+        <artifactId>spring-cloud-dependencies</artifactId>
89
+        <version>${spring.cloud.version}</version>
90
+        <type>pom</type>
91
+        <scope>import</scope>
92
+      </dependency>
93
+      <dependency>
94
+        <groupId>com.alibaba.cloud</groupId>
95
+        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
96
+        <version>${spring.cloud.alibaba.version}</version>
97
+        <type>pom</type>
98
+        <scope>import</scope>
99
+      </dependency>
100
+      <dependency>
101
+        <groupId>io.github.mouzt</groupId>
102
+        <artifactId>bizlog-sdk</artifactId>
103
+        <version>${bizlog-sdk.version}</version>
104
+        <exclusions>
105
+          <exclusion>
106
+            <artifactId>spring-boot-starter</artifactId>
107
+            <groupId>org.springframework.boot</groupId>
108
+          </exclusion>
109
+        </exclusions>
110
+      </dependency>
111
+      <dependency>
112
+        <groupId>com.jt.cloud</groupId>
113
+        <artifactId>jt-spring-boot-starter-biz-tenant</artifactId>
114
+        <version>${revision}</version>
115
+      </dependency>
116
+      <dependency>
117
+        <groupId>com.jt.cloud</groupId>
118
+        <artifactId>jt-spring-boot-starter-biz-data-permission</artifactId>
119
+        <version>${revision}</version>
120
+      </dependency>
121
+      <dependency>
122
+        <groupId>com.jt.cloud</groupId>
123
+        <artifactId>jt-spring-boot-starter-biz-ip</artifactId>
124
+        <version>${revision}</version>
125
+      </dependency>
126
+      <dependency>
127
+        <groupId>org.springframework.boot</groupId>
128
+        <artifactId>spring-boot-configuration-processor</artifactId>
129
+        <version>${spring.boot.version}</version>
130
+      </dependency>
131
+      <dependency>
132
+        <groupId>com.jt.cloud</groupId>
133
+        <artifactId>jt-spring-boot-starter-env</artifactId>
134
+        <version>${revision}</version>
135
+      </dependency>
136
+      <dependency>
137
+        <groupId>com.jt.cloud</groupId>
138
+        <artifactId>jt-spring-boot-starter-web</artifactId>
139
+        <version>${revision}</version>
140
+      </dependency>
141
+      <dependency>
142
+        <groupId>com.jt.cloud</groupId>
143
+        <artifactId>jt-spring-boot-starter-security</artifactId>
144
+        <version>${revision}</version>
145
+      </dependency>
146
+      <dependency>
147
+        <groupId>com.jt.cloud</groupId>
148
+        <artifactId>jt-spring-boot-starter-websocket</artifactId>
149
+        <version>${revision}</version>
150
+      </dependency>
151
+      <dependency>
152
+        <groupId>com.github.xingfudeshi</groupId>
153
+        <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
154
+        <version>${knife4j.version}</version>
155
+      </dependency>
156
+      <dependency>
157
+        <groupId>org.springdoc</groupId>
158
+        <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
159
+        <version>${springdoc.version}</version>
160
+      </dependency>
161
+      <dependency>
162
+        <groupId>com.github.xiaoymin</groupId>
163
+        <artifactId>knife4j-gateway-spring-boot-starter</artifactId>
164
+        <version>4.5.0</version>
165
+      </dependency>
166
+      <dependency>
167
+        <groupId>com.jt.cloud</groupId>
168
+        <artifactId>jt-spring-boot-starter-mybatis</artifactId>
169
+        <version>${revision}</version>
170
+      </dependency>
171
+      <dependency>
172
+        <groupId>com.alibaba</groupId>
173
+        <artifactId>druid-spring-boot-3-starter</artifactId>
174
+        <version>${druid.version}</version>
175
+      </dependency>
176
+      <dependency>
177
+        <groupId>org.mybatis</groupId>
178
+        <artifactId>mybatis</artifactId>
179
+        <version>${mybatis.version}</version>
180
+      </dependency>
181
+      <dependency>
182
+        <groupId>com.baomidou</groupId>
183
+        <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
184
+        <version>${mybatis-plus.version}</version>
185
+      </dependency>
186
+      <dependency>
187
+        <groupId>com.baomidou</groupId>
188
+        <artifactId>mybatis-plus-jsqlparser</artifactId>
189
+        <version>${mybatis-plus.version}</version>
190
+      </dependency>
191
+      <dependency>
192
+        <groupId>com.baomidou</groupId>
193
+        <artifactId>mybatis-plus-generator</artifactId>
194
+        <version>${mybatis-plus.version}</version>
195
+      </dependency>
196
+      <dependency>
197
+        <groupId>com.baomidou</groupId>
198
+        <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
199
+        <version>${dynamic-datasource.version}</version>
200
+      </dependency>
201
+      <dependency>
202
+        <groupId>com.github.yulichang</groupId>
203
+        <artifactId>mybatis-plus-join-boot-starter</artifactId>
204
+        <version>${mybatis-plus-join.version}</version>
205
+      </dependency>
206
+      <dependency>
207
+        <groupId>org.apache.shardingsphere</groupId>
208
+        <artifactId>shardingsphere-jdbc-core</artifactId>
209
+        <version>${shardingsphere-jdbc-core.version}</version>
210
+      </dependency>
211
+      <dependency>
212
+        <groupId>com.fhs-opensource</groupId>
213
+        <artifactId>easy-trans-spring-boot-starter</artifactId>
214
+        <version>${easy-trans.version}</version>
215
+        <exclusions>
216
+          <exclusion>
217
+            <artifactId>spring-context</artifactId>
218
+            <groupId>org.springframework</groupId>
219
+          </exclusion>
220
+          <exclusion>
221
+            <artifactId>spring-cloud-commons</artifactId>
222
+            <groupId>org.springframework.cloud</groupId>
223
+          </exclusion>
224
+        </exclusions>
225
+      </dependency>
226
+      <dependency>
227
+        <groupId>com.fhs-opensource</groupId>
228
+        <artifactId>easy-trans-mybatis-plus-extend</artifactId>
229
+        <version>${easy-trans.version}</version>
230
+      </dependency>
231
+      <dependency>
232
+        <groupId>com.fhs-opensource</groupId>
233
+        <artifactId>easy-trans-anno</artifactId>
234
+        <version>${easy-trans.version}</version>
235
+      </dependency>
236
+      <dependency>
237
+        <groupId>com.jt.cloud</groupId>
238
+        <artifactId>jt-spring-boot-starter-redis</artifactId>
239
+        <version>${revision}</version>
240
+      </dependency>
241
+      <dependency>
242
+        <groupId>org.redisson</groupId>
243
+        <artifactId>redisson-spring-boot-starter</artifactId>
244
+        <version>${redisson.version}</version>
245
+      </dependency>
246
+      <dependency>
247
+        <groupId>com.dameng</groupId>
248
+        <artifactId>DmJdbcDriver18</artifactId>
249
+        <version>${dm8.jdbc.version}</version>
250
+      </dependency>
251
+      <dependency>
252
+        <groupId>org.opengauss</groupId>
253
+        <artifactId>opengauss-jdbc</artifactId>
254
+        <version>${opengauss.jdbc.version}</version>
255
+      </dependency>
256
+      <dependency>
257
+        <groupId>cn.com.kingbase</groupId>
258
+        <artifactId>kingbase8</artifactId>
259
+        <version>${kingbase.jdbc.version}</version>
260
+      </dependency>
261
+      <dependency>
262
+        <groupId>com.taosdata.jdbc</groupId>
263
+        <artifactId>taos-jdbcdriver</artifactId>
264
+        <version>${taos.version}</version>
265
+      </dependency>
266
+      <dependency>
267
+        <groupId>com.jt.cloud</groupId>
268
+        <artifactId>jt-spring-boot-starter-rpc</artifactId>
269
+        <version>${revision}</version>
270
+      </dependency>
271
+      <dependency>
272
+        <groupId>com.xuxueli</groupId>
273
+        <artifactId>xxl-job-core</artifactId>
274
+        <version>${xxl-job.version}</version>
275
+      </dependency>
276
+      <dependency>
277
+        <groupId>com.jt.cloud</groupId>
278
+        <artifactId>jt-spring-boot-starter-job</artifactId>
279
+        <version>${revision}</version>
280
+      </dependency>
281
+      <dependency>
282
+        <groupId>com.jt.cloud</groupId>
283
+        <artifactId>jt-spring-boot-starter-mq</artifactId>
284
+        <version>${revision}</version>
285
+      </dependency>
286
+      <dependency>
287
+        <groupId>org.apache.rocketmq</groupId>
288
+        <artifactId>rocketmq-spring-boot-starter</artifactId>
289
+        <version>${rocketmq-spring.version}</version>
290
+      </dependency>
291
+      <dependency>
292
+        <groupId>com.jt.cloud</groupId>
293
+        <artifactId>jt-spring-boot-starter-protection</artifactId>
294
+        <version>${revision}</version>
295
+      </dependency>
296
+      <dependency>
297
+        <groupId>com.baomidou</groupId>
298
+        <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
299
+        <version>${lock4j.version}</version>
300
+        <exclusions>
301
+          <exclusion>
302
+            <artifactId>redisson-spring-boot-starter</artifactId>
303
+            <groupId>org.redisson</groupId>
304
+          </exclusion>
305
+        </exclusions>
306
+      </dependency>
307
+      <dependency>
308
+        <groupId>com.jt.cloud</groupId>
309
+        <artifactId>jt-spring-boot-starter-monitor</artifactId>
310
+        <version>${revision}</version>
311
+      </dependency>
312
+      <dependency>
313
+        <groupId>org.apache.skywalking</groupId>
314
+        <artifactId>apm-toolkit-trace</artifactId>
315
+        <version>${skywalking.version}</version>
316
+      </dependency>
317
+      <dependency>
318
+        <groupId>org.apache.skywalking</groupId>
319
+        <artifactId>apm-toolkit-logback-1.x</artifactId>
320
+        <version>${skywalking.version}</version>
321
+      </dependency>
322
+      <dependency>
323
+        <groupId>org.apache.skywalking</groupId>
324
+        <artifactId>apm-toolkit-opentracing</artifactId>
325
+        <version>${skywalking.version}</version>
326
+      </dependency>
327
+      <dependency>
328
+        <groupId>io.opentracing</groupId>
329
+        <artifactId>opentracing-api</artifactId>
330
+        <version>${opentracing.version}</version>
331
+      </dependency>
332
+      <dependency>
333
+        <groupId>io.opentracing</groupId>
334
+        <artifactId>opentracing-util</artifactId>
335
+        <version>${opentracing.version}</version>
336
+      </dependency>
337
+      <dependency>
338
+        <groupId>io.opentracing</groupId>
339
+        <artifactId>opentracing-noop</artifactId>
340
+        <version>${opentracing.version}</version>
341
+      </dependency>
342
+      <dependency>
343
+        <groupId>de.codecentric</groupId>
344
+        <artifactId>spring-boot-admin-starter-server</artifactId>
345
+        <version>${spring-boot-admin.version}</version>
346
+      </dependency>
347
+      <dependency>
348
+        <groupId>de.codecentric</groupId>
349
+        <artifactId>spring-boot-admin-starter-client</artifactId>
350
+        <version>${spring-boot-admin.version}</version>
351
+      </dependency>
352
+      <dependency>
353
+        <groupId>com.jt.cloud</groupId>
354
+        <artifactId>jt-spring-boot-starter-test</artifactId>
355
+        <version>${revision}</version>
356
+        <scope>test</scope>
357
+      </dependency>
358
+      <dependency>
359
+        <groupId>org.mockito</groupId>
360
+        <artifactId>mockito-inline</artifactId>
361
+        <version>${mockito-inline.version}</version>
362
+      </dependency>
363
+      <dependency>
364
+        <groupId>org.springframework.boot</groupId>
365
+        <artifactId>spring-boot-starter-test</artifactId>
366
+        <version>${spring.boot.version}</version>
367
+        <exclusions>
368
+          <exclusion>
369
+            <artifactId>asm</artifactId>
370
+            <groupId>org.ow2.asm</groupId>
371
+          </exclusion>
372
+          <exclusion>
373
+            <artifactId>mockito-core</artifactId>
374
+            <groupId>org.mockito</groupId>
375
+          </exclusion>
376
+        </exclusions>
377
+      </dependency>
378
+      <dependency>
379
+        <groupId>com.github.fppt</groupId>
380
+        <artifactId>jedis-mock</artifactId>
381
+        <version>${jedis-mock.version}</version>
382
+      </dependency>
383
+      <dependency>
384
+        <groupId>uk.co.jemos.podam</groupId>
385
+        <artifactId>podam</artifactId>
386
+        <version>${podam.version}</version>
387
+      </dependency>
388
+      <dependency>
389
+        <groupId>org.flowable</groupId>
390
+        <artifactId>flowable-spring-boot-starter-process</artifactId>
391
+        <version>${flowable.version}</version>
392
+      </dependency>
393
+      <dependency>
394
+        <groupId>org.flowable</groupId>
395
+        <artifactId>flowable-spring-boot-starter-actuator</artifactId>
396
+        <version>${flowable.version}</version>
397
+      </dependency>
398
+      <dependency>
399
+        <groupId>com.jt.cloud</groupId>
400
+        <artifactId>jt-common</artifactId>
401
+        <version>${revision}</version>
402
+      </dependency>
403
+      <dependency>
404
+        <groupId>com.jt.cloud</groupId>
405
+        <artifactId>jt-spring-boot-starter-excel</artifactId>
406
+        <version>${revision}</version>
407
+      </dependency>
408
+      <dependency>
409
+        <groupId>org.projectlombok</groupId>
410
+        <artifactId>lombok</artifactId>
411
+        <version>${lombok.version}</version>
412
+      </dependency>
413
+      <dependency>
414
+        <groupId>org.mapstruct</groupId>
415
+        <artifactId>mapstruct</artifactId>
416
+        <version>${mapstruct.version}</version>
417
+      </dependency>
418
+      <dependency>
419
+        <groupId>org.mapstruct</groupId>
420
+        <artifactId>mapstruct-jdk8</artifactId>
421
+        <version>${mapstruct.version}</version>
422
+      </dependency>
423
+      <dependency>
424
+        <groupId>org.mapstruct</groupId>
425
+        <artifactId>mapstruct-processor</artifactId>
426
+        <version>${mapstruct.version}</version>
427
+      </dependency>
428
+      <dependency>
429
+        <groupId>cn.hutool</groupId>
430
+        <artifactId>hutool-all</artifactId>
431
+        <version>${hutool-5.version}</version>
432
+      </dependency>
433
+      <dependency>
434
+        <groupId>org.dromara.hutool</groupId>
435
+        <artifactId>hutool-extra</artifactId>
436
+        <version>${hutool-6.version}</version>
437
+      </dependency>
438
+      <dependency>
439
+        <groupId>cn.idev.excel</groupId>
440
+        <artifactId>fastexcel</artifactId>
441
+        <version>${fastexcel.version}</version>
442
+      </dependency>
443
+      <dependency>
444
+        <groupId>org.apache.tika</groupId>
445
+        <artifactId>tika-core</artifactId>
446
+        <version>${tika-core.version}</version>
447
+      </dependency>
448
+      <dependency>
449
+        <groupId>org.apache.velocity</groupId>
450
+        <artifactId>velocity-engine-core</artifactId>
451
+        <version>${velocity.version}</version>
452
+      </dependency>
453
+      <dependency>
454
+        <groupId>com.alibaba</groupId>
455
+        <artifactId>fastjson</artifactId>
456
+        <version>${fastjson.version}</version>
457
+      </dependency>
458
+      <dependency>
459
+        <groupId>com.google.guava</groupId>
460
+        <artifactId>guava</artifactId>
461
+        <version>${guava.version}</version>
462
+      </dependency>
463
+      <dependency>
464
+        <groupId>com.alibaba</groupId>
465
+        <artifactId>transmittable-thread-local</artifactId>
466
+        <version>${transmittable-thread-local.version}</version>
467
+      </dependency>
468
+      <dependency>
469
+        <groupId>commons-net</groupId>
470
+        <artifactId>commons-net</artifactId>
471
+        <version>${commons-net.version}</version>
472
+      </dependency>
473
+      <dependency>
474
+        <groupId>com.jcraft</groupId>
475
+        <artifactId>jsch</artifactId>
476
+        <version>${jsch.version}</version>
477
+      </dependency>
478
+      <dependency>
479
+        <groupId>com.anji-plus</groupId>
480
+        <artifactId>captcha-spring-boot-starter</artifactId>
481
+        <version>${anji-plus-captcha.version}</version>
482
+      </dependency>
483
+      <dependency>
484
+        <groupId>org.lionsoul</groupId>
485
+        <artifactId>ip2region</artifactId>
486
+        <version>${ip2region.version}</version>
487
+      </dependency>
488
+      <dependency>
489
+        <groupId>org.jsoup</groupId>
490
+        <artifactId>jsoup</artifactId>
491
+        <version>${jsoup.version}</version>
492
+      </dependency>
493
+      <dependency>
494
+        <groupId>org.reflections</groupId>
495
+        <artifactId>reflections</artifactId>
496
+        <version>${reflections.version}</version>
497
+      </dependency>
498
+      <dependency>
499
+        <groupId>software.amazon.awssdk</groupId>
500
+        <artifactId>s3</artifactId>
501
+        <version>${awssdk.version}</version>
502
+      </dependency>
503
+      <dependency>
504
+        <groupId>com.github.binarywang</groupId>
505
+        <artifactId>weixin-java-pay</artifactId>
506
+        <version>${weixin-java.version}</version>
507
+      </dependency>
508
+      <dependency>
509
+        <groupId>com.github.binarywang</groupId>
510
+        <artifactId>wx-java-mp-spring-boot-starter</artifactId>
511
+        <version>${weixin-java.version}</version>
512
+      </dependency>
513
+      <dependency>
514
+        <groupId>com.github.binarywang</groupId>
515
+        <artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
516
+        <version>${weixin-java.version}</version>
517
+      </dependency>
518
+      <dependency>
519
+        <groupId>me.zhyd.oauth</groupId>
520
+        <artifactId>JustAuth</artifactId>
521
+        <version>${justauth.version}</version>
522
+      </dependency>
523
+      <dependency>
524
+        <groupId>com.xkcoding.justauth</groupId>
525
+        <artifactId>justauth-spring-boot-starter</artifactId>
526
+        <version>${justauth-starter.version}</version>
527
+      </dependency>
528
+      <dependency>
529
+        <groupId>org.jeecgframework.jimureport</groupId>
530
+        <artifactId>jimureport-spring-boot3-starter-fastjson2</artifactId>
531
+        <version>${jimureport.version}</version>
532
+      </dependency>
533
+      <dependency>
534
+        <groupId>org.jeecgframework.jimureport</groupId>
535
+        <artifactId>jimubi-spring-boot3-starter</artifactId>
536
+        <version>${jimubi.version}</version>
537
+        <exclusions>
538
+          <exclusion>
539
+            <artifactId>jsqlparser</artifactId>
540
+            <groupId>com.github.jsqlparser</groupId>
541
+          </exclusion>
542
+        </exclusions>
543
+      </dependency>
544
+      <dependency>
545
+        <groupId>org.pf4j</groupId>
546
+        <artifactId>pf4j-spring</artifactId>
547
+        <version>${pf4j-spring.version}</version>
548
+        <exclusions>
549
+          <exclusion>
550
+            <artifactId>slf4j-log4j12</artifactId>
551
+            <groupId>org.slf4j</groupId>
552
+          </exclusion>
553
+        </exclusions>
554
+      </dependency>
555
+      <dependency>
556
+        <groupId>io.vertx</groupId>
557
+        <artifactId>vertx-core</artifactId>
558
+        <version>${vertx.version}</version>
559
+      </dependency>
560
+      <dependency>
561
+        <groupId>io.vertx</groupId>
562
+        <artifactId>vertx-web</artifactId>
563
+        <version>${vertx.version}</version>
564
+      </dependency>
565
+      <dependency>
566
+        <groupId>io.vertx</groupId>
567
+        <artifactId>vertx-mqtt</artifactId>
568
+        <version>${vertx.version}</version>
569
+      </dependency>
570
+      <dependency>
571
+        <groupId>org.eclipse.paho</groupId>
572
+        <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
573
+        <version>${mqtt.version}</version>
574
+      </dependency>
575
+      <dependency>
576
+        <groupId>com.google.zxing</groupId>
577
+        <artifactId>core</artifactId>
578
+        <version>${zxing.version}</version>
579
+      </dependency>
580
+    </dependencies>
581
+  </dependencyManagement>
582
+</project>

+ 725 - 0
jt-dependencies/pom.xml

@@ -0,0 +1,725 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xmlns="http://maven.apache.org/POM/4.0.0"
3
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5
+    <modelVersion>4.0.0</modelVersion>
6
+
7
+    <groupId>com.jt.cloud</groupId>
8
+    <artifactId>jt-dependencies</artifactId>
9
+    <version>${revision}</version>
10
+    <packaging>pom</packaging>
11
+
12
+    <name>${project.artifactId}</name>
13
+    <description>基础 bom 文件,管理整个项目的依赖版本</description>
14
+
15
+
16
+    <properties>
17
+        <revision>2.6.1-SNAPSHOT</revision>
18
+        <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
19
+        <!-- 统一依赖管理 -->
20
+        <spring.boot.version>3.4.5</spring.boot.version>
21
+        <spring.cloud.version>2024.0.1</spring.cloud.version>
22
+        <spring.cloud.alibaba.version>2023.0.3.2</spring.cloud.alibaba.version>
23
+        <!-- Web 相关 -->
24
+        <springdoc.version>2.8.3</springdoc.version>
25
+        <knife4j.version>4.6.0</knife4j.version>
26
+        <!-- DB 相关 -->
27
+        <druid.version>1.2.24</druid.version>
28
+        <mybatis.version>3.5.19</mybatis.version>
29
+        <mybatis-plus.version>3.5.12</mybatis-plus.version>
30
+        <mybatis-plus-join.version>1.5.4</mybatis-plus-join.version>
31
+        <shardingsphere-jdbc-core.version>5.4.1</shardingsphere-jdbc-core.version>
32
+        <dynamic-datasource.version>4.3.1</dynamic-datasource.version>
33
+        <easy-trans.version>3.0.6</easy-trans.version>
34
+        <redisson.version>3.41.0</redisson.version>
35
+        <dm8.jdbc.version>8.1.3.140</dm8.jdbc.version>
36
+        <kingbase.jdbc.version>8.6.0</kingbase.jdbc.version>
37
+        <opengauss.jdbc.version>5.1.0</opengauss.jdbc.version>
38
+        <taos.version>3.3.3</taos.version>
39
+        <!-- 消息队列 -->
40
+        <rocketmq-spring.version>2.3.2</rocketmq-spring.version>
41
+        <!-- RPC 相关 -->
42
+        <!-- Config 配置中心相关 -->
43
+        <!-- Job 定时任务相关 -->
44
+        <xxl-job.version>2.4.0</xxl-job.version>
45
+        <!-- 服务保障相关 -->
46
+        <lock4j.version>2.2.7</lock4j.version>
47
+        <!-- 监控相关 -->
48
+        <skywalking.version>9.0.0</skywalking.version>
49
+        <spring-boot-admin.version>3.4.5</spring-boot-admin.version>
50
+        <opentracing.version>0.33.0</opentracing.version>
51
+        <!-- Test 测试相关 -->
52
+        <podam.version>8.0.2.RELEASE</podam.version>
53
+        <jedis-mock.version>1.1.4</jedis-mock.version>
54
+        <mockito-inline.version>5.2.0</mockito-inline.version>
55
+        <!-- Bpm 工作流相关 -->
56
+        <flowable.version>7.0.1</flowable.version>
57
+        <!-- 工具类相关 -->
58
+        <anji-plus-captcha.version>1.4.0</anji-plus-captcha.version>
59
+        <jsoup.version>1.18.1</jsoup.version>
60
+        <lombok.version>1.18.36</lombok.version>
61
+        <mapstruct.version>1.6.3</mapstruct.version>
62
+        <hutool-5.version>5.8.35</hutool-5.version>
63
+        <hutool-6.version>6.0.0-M19</hutool-6.version>
64
+        <fastexcel.version>1.2.0</fastexcel.version>
65
+        <velocity.version>2.4.1</velocity.version>
66
+        <fastjson.version>1.2.83</fastjson.version>
67
+        <guava.version>33.4.8-jre</guava.version>
68
+        <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
69
+        <commons-net.version>3.11.1</commons-net.version>
70
+        <jsch.version>0.1.55</jsch.version>
71
+        <tika-core.version>3.1.0</tika-core.version>
72
+        <ip2region.version>2.7.0</ip2region.version>
73
+        <bizlog-sdk.version>3.0.6</bizlog-sdk.version>
74
+        <reflections.version>0.10.2</reflections.version>
75
+        <netty.version>4.1.116.Final</netty.version>
76
+        <mqtt.version>1.2.5</mqtt.version>
77
+        <pf4j-spring.version>0.9.0</pf4j-spring.version>
78
+        <vertx.version>4.5.13</vertx.version>
79
+        <!-- 三方云服务相关 -->
80
+        <awssdk.version>2.30.14</awssdk.version>
81
+        <justauth.version>1.16.7</justauth.version>
82
+        <justauth-starter.version>1.4.0</justauth-starter.version>
83
+        <jimureport.version>2.0.0</jimureport.version>
84
+        <jimubi.version>1.9.5</jimubi.version>
85
+        <weixin-java.version>4.7.5.B</weixin-java.version>
86
+        <zxing.version>3.5.2</zxing.version>
87
+    </properties>
88
+
89
+    <dependencyManagement>
90
+        <dependencies>
91
+            <!-- 统一依赖管理 -->
92
+            <dependency>
93
+                <groupId>io.netty</groupId>
94
+                <artifactId>netty-bom</artifactId>
95
+                <version>${netty.version}</version>
96
+                <type>pom</type>
97
+                <scope>import</scope>
98
+            </dependency>
99
+            <dependency>
100
+                <groupId>org.springframework.boot</groupId>
101
+                <artifactId>spring-boot-dependencies</artifactId>
102
+                <version>${spring.boot.version}</version>
103
+                <type>pom</type>
104
+                <scope>import</scope>
105
+            </dependency>
106
+            <dependency>
107
+                <groupId>org.springframework.cloud</groupId>
108
+                <artifactId>spring-cloud-dependencies</artifactId>
109
+                <version>${spring.cloud.version}</version>
110
+                <type>pom</type>
111
+                <scope>import</scope>
112
+            </dependency>
113
+            <dependency>
114
+                <groupId>com.alibaba.cloud</groupId>
115
+                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
116
+                <version>${spring.cloud.alibaba.version}</version>
117
+                <type>pom</type>
118
+                <scope>import</scope>
119
+            </dependency>
120
+
121
+            <!-- 业务组件 -->
122
+            <dependency>
123
+                <groupId>io.github.mouzt</groupId>
124
+                <artifactId>bizlog-sdk</artifactId>
125
+                <version>${bizlog-sdk.version}</version>
126
+                <exclusions>
127
+                    <exclusion> <!-- 排除掉springboot依赖使用项目的 -->
128
+                        <groupId>org.springframework.boot</groupId>
129
+                        <artifactId>spring-boot-starter</artifactId>
130
+                    </exclusion>
131
+                </exclusions>
132
+            </dependency>
133
+            <dependency>
134
+                <groupId>com.jt.cloud</groupId>
135
+                <artifactId>jt-spring-boot-starter-biz-tenant</artifactId>
136
+                <version>${revision}</version>
137
+            </dependency>
138
+            <dependency>
139
+                <groupId>com.jt.cloud</groupId>
140
+                <artifactId>jt-spring-boot-starter-biz-data-permission</artifactId>
141
+                <version>${revision}</version>
142
+            </dependency>
143
+            <dependency>
144
+                <groupId>com.jt.cloud</groupId>
145
+                <artifactId>jt-spring-boot-starter-biz-ip</artifactId>
146
+                <version>${revision}</version>
147
+            </dependency>
148
+
149
+            <!-- Spring 核心 -->
150
+            <dependency>
151
+                <!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 -->
152
+                <groupId>org.springframework.boot</groupId>
153
+                <artifactId>spring-boot-configuration-processor</artifactId>
154
+                <version>${spring.boot.version}</version>
155
+            </dependency>
156
+
157
+            <dependency>
158
+                <groupId>com.jt.cloud</groupId>
159
+                <artifactId>jt-spring-boot-starter-env</artifactId>
160
+                <version>${revision}</version>
161
+            </dependency>
162
+
163
+            <!-- Web 相关 -->
164
+            <dependency>
165
+                <groupId>com.jt.cloud</groupId>
166
+                <artifactId>jt-spring-boot-starter-web</artifactId>
167
+                <version>${revision}</version>
168
+            </dependency>
169
+
170
+            <dependency>
171
+                <groupId>com.jt.cloud</groupId>
172
+                <artifactId>jt-spring-boot-starter-security</artifactId>
173
+                <version>${revision}</version>
174
+            </dependency>
175
+
176
+            <dependency>
177
+                <groupId>com.jt.cloud</groupId>
178
+                <artifactId>jt-spring-boot-starter-websocket</artifactId>
179
+                <version>${revision}</version>
180
+            </dependency>
181
+
182
+            <dependency>
183
+                <groupId>com.github.xingfudeshi</groupId> <!-- TODO osy:https://github.com/xiaoymin/knife4j/issues/874 -->
184
+                <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
185
+                <version>${knife4j.version}</version>
186
+            </dependency>
187
+            <dependency>
188
+                <groupId>org.springdoc</groupId>
189
+                <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
190
+                <version>${springdoc.version}</version>
191
+            </dependency>
192
+            <dependency>
193
+                <groupId>com.github.xiaoymin</groupId> <!-- 接口文档 UI:knife4j【网关专属】 -->
194
+                <artifactId>knife4j-gateway-spring-boot-starter</artifactId>
195
+                <version>4.5.0</version> <!-- TODO osy:等 4.5.0 => 4.6.0 -->
196
+            </dependency>
197
+
198
+            <!-- DB 相关 -->
199
+            <dependency>
200
+                <groupId>com.jt.cloud</groupId>
201
+                <artifactId>jt-spring-boot-starter-mybatis</artifactId>
202
+                <version>${revision}</version>
203
+            </dependency>
204
+
205
+            <dependency>
206
+                <groupId>com.alibaba</groupId>
207
+                <artifactId>druid-spring-boot-3-starter</artifactId>
208
+                <version>${druid.version}</version>
209
+            </dependency>
210
+            <dependency>
211
+                <!-- 注意:必须声明,避免 flowable 和 mybatis-plus 引入的 mybatis 版本不一致!!! -->
212
+                <groupId>org.mybatis</groupId>
213
+                <artifactId>mybatis</artifactId>
214
+                <version>${mybatis.version}</version>
215
+            </dependency>
216
+            <dependency>
217
+                <groupId>com.baomidou</groupId>
218
+                <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
219
+                <version>${mybatis-plus.version}</version>
220
+            </dependency>
221
+            <dependency>
222
+                <groupId>com.baomidou</groupId>
223
+                <artifactId>mybatis-plus-jsqlparser</artifactId>
224
+                <version>${mybatis-plus.version}</version>
225
+            </dependency>
226
+            <dependency>
227
+                <groupId>com.baomidou</groupId>
228
+                <artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 -->
229
+                <version>${mybatis-plus.version}</version>
230
+            </dependency>
231
+            <dependency>
232
+                <groupId>com.baomidou</groupId>
233
+                <artifactId>dynamic-datasource-spring-boot3-starter</artifactId> <!-- 多数据源 -->
234
+                <version>${dynamic-datasource.version}</version>
235
+            </dependency>
236
+            <dependency>
237
+                <groupId>com.github.yulichang</groupId>
238
+                <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
239
+                <version>${mybatis-plus-join.version}</version>
240
+            </dependency>
241
+            <dependency>
242
+                <groupId>org.apache.shardingsphere</groupId>
243
+                <artifactId>shardingsphere-jdbc-core</artifactId>
244
+                <version>${shardingsphere-jdbc-core.version}</version>
245
+            </dependency>
246
+            <dependency>
247
+                <groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 -->
248
+                <artifactId>easy-trans-spring-boot-starter</artifactId>
249
+                <version>${easy-trans.version}</version>
250
+                <exclusions>
251
+                    <exclusion>
252
+                        <groupId>org.springframework</groupId>
253
+                        <artifactId>spring-context</artifactId>
254
+                    </exclusion>
255
+                    <exclusion>
256
+                        <groupId>org.springframework.cloud</groupId>
257
+                        <artifactId>spring-cloud-commons</artifactId>
258
+                    </exclusion>
259
+                </exclusions>
260
+            </dependency>
261
+            <dependency>
262
+                <groupId>com.fhs-opensource</groupId>
263
+                <artifactId>easy-trans-mybatis-plus-extend</artifactId>
264
+                <version>${easy-trans.version}</version>
265
+            </dependency>
266
+            <dependency>
267
+                <groupId>com.fhs-opensource</groupId>
268
+                <artifactId>easy-trans-anno</artifactId>
269
+                <version>${easy-trans.version}</version>
270
+            </dependency>
271
+
272
+            <dependency>
273
+                <groupId>com.jt.cloud</groupId>
274
+                <artifactId>jt-spring-boot-starter-redis</artifactId>
275
+                <version>${revision}</version>
276
+            </dependency>
277
+
278
+            <dependency>
279
+                <groupId>org.redisson</groupId>
280
+                <artifactId>redisson-spring-boot-starter</artifactId>
281
+                <version>${redisson.version}</version>
282
+            </dependency>
283
+
284
+            <dependency>
285
+                <groupId>com.dameng</groupId>
286
+                <artifactId>DmJdbcDriver18</artifactId>
287
+                <version>${dm8.jdbc.version}</version>
288
+            </dependency>
289
+
290
+            <dependency>
291
+                <groupId>org.opengauss</groupId>
292
+                <artifactId>opengauss-jdbc</artifactId>
293
+                <version>${opengauss.jdbc.version}</version>
294
+            </dependency>
295
+
296
+            <dependency>
297
+                <groupId>cn.com.kingbase</groupId>
298
+                <artifactId>kingbase8</artifactId>
299
+                <version>${kingbase.jdbc.version}</version>
300
+            </dependency>
301
+
302
+            <dependency>
303
+                <groupId>com.taosdata.jdbc</groupId>
304
+                <artifactId>taos-jdbcdriver</artifactId>
305
+                <version>${taos.version}</version>
306
+            </dependency>
307
+
308
+            <!-- RPC 远程调用相关 -->
309
+            <dependency>
310
+                <groupId>com.jt.cloud</groupId>
311
+                <artifactId>jt-spring-boot-starter-rpc</artifactId>
312
+                <version>${revision}</version>
313
+            </dependency>
314
+
315
+            <!-- Registry 注册中心相关 -->
316
+
317
+            <!-- Config 配置中心相关 -->
318
+
319
+            <!-- Job 定时任务相关 -->
320
+            <dependency>
321
+                <groupId>com.xuxueli</groupId>
322
+                <artifactId>xxl-job-core</artifactId>
323
+                <version>${xxl-job.version}</version>
324
+            </dependency>
325
+            <dependency>
326
+                <groupId>com.jt.cloud</groupId>
327
+                <artifactId>jt-spring-boot-starter-job</artifactId>
328
+                <version>${revision}</version>
329
+            </dependency>
330
+
331
+            <!-- 消息队列相关 -->
332
+            <dependency>
333
+                <groupId>com.jt.cloud</groupId>
334
+                <artifactId>jt-spring-boot-starter-mq</artifactId>
335
+                <version>${revision}</version>
336
+            </dependency>
337
+
338
+            <dependency>
339
+                <groupId>org.apache.rocketmq</groupId>
340
+                <artifactId>rocketmq-spring-boot-starter</artifactId>
341
+                <version>${rocketmq-spring.version}</version>
342
+            </dependency>
343
+
344
+            <!-- 服务保障相关 -->
345
+            <dependency>
346
+                <groupId>com.jt.cloud</groupId>
347
+                <artifactId>jt-spring-boot-starter-protection</artifactId>
348
+                <version>${revision}</version>
349
+            </dependency>
350
+
351
+            <dependency>
352
+                <groupId>com.baomidou</groupId>
353
+                <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
354
+                <version>${lock4j.version}</version>
355
+                <exclusions>
356
+                    <exclusion>
357
+                        <artifactId>redisson-spring-boot-starter</artifactId>
358
+                        <groupId>org.redisson</groupId>
359
+                    </exclusion>
360
+                </exclusions>
361
+            </dependency>
362
+
363
+            <!-- 监控相关 -->
364
+            <dependency>
365
+                <groupId>com.jt.cloud</groupId>
366
+                <artifactId>jt-spring-boot-starter-monitor</artifactId>
367
+                <version>${revision}</version>
368
+            </dependency>
369
+
370
+            <dependency>
371
+                <groupId>org.apache.skywalking</groupId>
372
+                <artifactId>apm-toolkit-trace</artifactId>
373
+                <version>${skywalking.version}</version>
374
+            </dependency>
375
+            <dependency>
376
+                <groupId>org.apache.skywalking</groupId>
377
+                <artifactId>apm-toolkit-logback-1.x</artifactId>
378
+                <version>${skywalking.version}</version>
379
+            </dependency>
380
+            <dependency>
381
+                <groupId>org.apache.skywalking</groupId>
382
+                <artifactId>apm-toolkit-opentracing</artifactId>
383
+                <version>${skywalking.version}</version>
384
+                <!--                <exclusions>-->
385
+                <!--                    <exclusion>-->
386
+                <!--                        <artifactId>opentracing-api</artifactId>-->
387
+                <!--                        <groupId>io.opentracing</groupId>-->
388
+                <!--                    </exclusion>-->
389
+                <!--                    <exclusion>-->
390
+                <!--                        <artifactId>opentracing-util</artifactId>-->
391
+                <!--                        <groupId>io.opentracing</groupId>-->
392
+                <!--                    </exclusion>-->
393
+                <!--                </exclusions>-->
394
+            </dependency>
395
+            <dependency>
396
+                <groupId>io.opentracing</groupId>
397
+                <artifactId>opentracing-api</artifactId>
398
+                <version>${opentracing.version}</version>
399
+            </dependency>
400
+            <dependency>
401
+                <groupId>io.opentracing</groupId>
402
+                <artifactId>opentracing-util</artifactId>
403
+                <version>${opentracing.version}</version>
404
+            </dependency>
405
+            <dependency>
406
+                <groupId>io.opentracing</groupId>
407
+                <artifactId>opentracing-noop</artifactId>
408
+                <version>${opentracing.version}</version>
409
+            </dependency>
410
+
411
+            <dependency>
412
+                <groupId>de.codecentric</groupId>
413
+                <artifactId>spring-boot-admin-starter-server</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 -->
414
+                <version>${spring-boot-admin.version}</version>
415
+            </dependency>
416
+            <dependency>
417
+                <groupId>de.codecentric</groupId>
418
+                <artifactId>spring-boot-admin-starter-client</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 -->
419
+                <version>${spring-boot-admin.version}</version>
420
+            </dependency>
421
+
422
+            <!-- Test 测试相关 -->
423
+            <dependency>
424
+                <groupId>com.jt.cloud</groupId>
425
+                <artifactId>jt-spring-boot-starter-test</artifactId>
426
+                <version>${revision}</version>
427
+                <scope>test</scope>
428
+            </dependency>
429
+
430
+            <dependency>
431
+                <groupId>org.mockito</groupId>
432
+                <artifactId>mockito-inline</artifactId>
433
+                <version>${mockito-inline.version}</version> <!-- 支持 Mockito 的 final 类与 static 方法的 mock -->
434
+            </dependency>
435
+
436
+            <dependency>
437
+                <groupId>org.springframework.boot</groupId>
438
+                <artifactId>spring-boot-starter-test</artifactId>
439
+                <version>${spring.boot.version}</version>
440
+                <exclusions>
441
+                    <exclusion>
442
+                        <artifactId>asm</artifactId>
443
+                        <groupId>org.ow2.asm</groupId>
444
+                    </exclusion>
445
+                    <exclusion>
446
+                        <groupId>org.mockito</groupId>
447
+                        <artifactId>mockito-core</artifactId>
448
+                    </exclusion>
449
+                </exclusions>
450
+            </dependency>
451
+
452
+            <dependency>
453
+                <groupId>com.github.fppt</groupId> <!-- 单元测试,我们采用内嵌的 Redis 数据库 -->
454
+                <artifactId>jedis-mock</artifactId>
455
+                <version>${jedis-mock.version}</version>
456
+            </dependency>
457
+
458
+            <dependency>
459
+                <groupId>uk.co.jemos.podam</groupId> <!-- 单元测试,随机生成 POJO 类 -->
460
+                <artifactId>podam</artifactId>
461
+                <version>${podam.version}</version>
462
+            </dependency>
463
+
464
+            <!-- 工作流相关 -->
465
+            <dependency>
466
+                <groupId>org.flowable</groupId>
467
+                <artifactId>flowable-spring-boot-starter-process</artifactId>
468
+                <version>${flowable.version}</version>
469
+            </dependency>
470
+            <dependency>
471
+                <groupId>org.flowable</groupId>
472
+                <artifactId>flowable-spring-boot-starter-actuator</artifactId>
473
+                <version>${flowable.version}</version>
474
+            </dependency>
475
+            <!-- 工作流相关结束 -->
476
+
477
+            <!-- 工具类相关 -->
478
+            <dependency>
479
+                <groupId>com.jt.cloud</groupId>
480
+                <artifactId>jt-common</artifactId>
481
+                <version>${revision}</version>
482
+            </dependency>
483
+
484
+            <dependency>
485
+                <groupId>com.jt.cloud</groupId>
486
+                <artifactId>jt-spring-boot-starter-excel</artifactId>
487
+                <version>${revision}</version>
488
+            </dependency>
489
+
490
+            <dependency>
491
+                <groupId>org.projectlombok</groupId>
492
+                <artifactId>lombok</artifactId>
493
+                <version>${lombok.version}</version>
494
+            </dependency>
495
+
496
+            <dependency>
497
+                <groupId>org.mapstruct</groupId>
498
+                <artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
499
+                <version>${mapstruct.version}</version>
500
+            </dependency>
501
+            <dependency>
502
+                <groupId>org.mapstruct</groupId>
503
+                <artifactId>mapstruct-jdk8</artifactId>
504
+                <version>${mapstruct.version}</version>
505
+            </dependency>
506
+            <dependency>
507
+                <groupId>org.mapstruct</groupId>
508
+                <artifactId>mapstruct-processor</artifactId>
509
+                <version>${mapstruct.version}</version>
510
+            </dependency>
511
+
512
+            <dependency>
513
+                <groupId>cn.hutool</groupId>
514
+                <artifactId>hutool-all</artifactId>
515
+                <version>${hutool-5.version}</version>
516
+            </dependency>
517
+            <dependency>
518
+                <groupId>org.dromara.hutool</groupId>
519
+                <artifactId>hutool-extra</artifactId>
520
+                <version>${hutool-6.version}</version>
521
+            </dependency>
522
+
523
+
524
+
525
+            <dependency>
526
+                <groupId>cn.idev.excel</groupId>
527
+                <artifactId>fastexcel</artifactId>
528
+                <version>${fastexcel.version}</version>
529
+            </dependency>
530
+
531
+            <dependency>
532
+                <groupId>org.apache.tika</groupId>
533
+                <artifactId>tika-core</artifactId> <!-- 文件类型的识别 -->
534
+                <version>${tika-core.version}</version>
535
+            </dependency>
536
+
537
+            <dependency>
538
+                <groupId>org.apache.velocity</groupId>
539
+                <artifactId>velocity-engine-core</artifactId>
540
+                <version>${velocity.version}</version>
541
+            </dependency>
542
+
543
+            <dependency>
544
+                <groupId>com.alibaba</groupId>
545
+                <artifactId>fastjson</artifactId>
546
+                <version>${fastjson.version}</version>
547
+            </dependency>
548
+
549
+            <dependency>
550
+                <groupId>com.google.guava</groupId>
551
+                <artifactId>guava</artifactId>
552
+                <version>${guava.version}</version>
553
+            </dependency>
554
+
555
+            <dependency>
556
+                <groupId>com.alibaba</groupId>
557
+                <artifactId>transmittable-thread-local</artifactId> <!-- 解决 ThreadLocal 父子线程的传值问题 -->
558
+                <version>${transmittable-thread-local.version}</version>
559
+            </dependency>
560
+
561
+            <dependency>
562
+                <groupId>commons-net</groupId>
563
+                <artifactId>commons-net</artifactId> <!-- 解决 ftp 连接 -->
564
+                <version>${commons-net.version}</version>
565
+            </dependency>
566
+            <dependency>
567
+                <groupId>com.jcraft</groupId>
568
+                <artifactId>jsch</artifactId> <!-- 解决 sftp 连接 -->
569
+                <version>${jsch.version}</version>
570
+            </dependency>
571
+
572
+            <dependency>
573
+                <groupId>com.anji-plus</groupId>
574
+                <artifactId>captcha-spring-boot-starter</artifactId> <!-- 验证码,一般用于登录使用 -->
575
+                <version>${anji-plus-captcha.version}</version>
576
+            </dependency>
577
+
578
+            <dependency>
579
+                <groupId>org.lionsoul</groupId>
580
+                <artifactId>ip2region</artifactId>
581
+                <version>${ip2region.version}</version>
582
+            </dependency>
583
+
584
+            <dependency>
585
+                <groupId>org.jsoup</groupId>
586
+                <artifactId>jsoup</artifactId>
587
+                <version>${jsoup.version}</version>
588
+            </dependency>
589
+
590
+            <dependency>
591
+                <groupId>org.reflections</groupId>
592
+                <artifactId>reflections</artifactId>
593
+                <version>${reflections.version}</version>
594
+            </dependency>
595
+
596
+            <!-- 三方云服务相关 -->
597
+            <dependency>
598
+                <groupId>software.amazon.awssdk</groupId>
599
+                <artifactId>s3</artifactId>
600
+                <version>${awssdk.version}</version>
601
+            </dependency>
602
+
603
+            <dependency>
604
+                <groupId>com.github.binarywang</groupId>
605
+                <artifactId>weixin-java-pay</artifactId>
606
+                <version>${weixin-java.version}</version>
607
+            </dependency>
608
+            <dependency>
609
+                <groupId>com.github.binarywang</groupId>
610
+                <artifactId>wx-java-mp-spring-boot-starter</artifactId>
611
+                <version>${weixin-java.version}</version>
612
+            </dependency>
613
+            <dependency>
614
+                <groupId>com.github.binarywang</groupId>
615
+                <artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
616
+                <version>${weixin-java.version}</version>
617
+            </dependency>
618
+
619
+            <dependency>
620
+                <groupId>me.zhyd.oauth</groupId>
621
+                <artifactId>JustAuth</artifactId> <!-- 社交登陆(例如说,个人微信、企业微信等等) -->
622
+                <version>${justauth.version}</version>
623
+            </dependency>
624
+            <dependency>
625
+                <groupId>com.xkcoding.justauth</groupId>
626
+                <artifactId>justauth-spring-boot-starter</artifactId>
627
+                <version>${justauth-starter.version}</version>
628
+            </dependency>
629
+
630
+            <!-- 积木报表-->
631
+            <dependency>
632
+                <groupId>org.jeecgframework.jimureport</groupId>
633
+                <artifactId>jimureport-spring-boot3-starter-fastjson2</artifactId>
634
+                <version>${jimureport.version}</version>
635
+            </dependency>
636
+            <dependency>
637
+                <groupId>org.jeecgframework.jimureport</groupId>
638
+                <artifactId>jimubi-spring-boot3-starter</artifactId>
639
+                <version>${jimubi.version}</version>
640
+                <exclusions>
641
+                    <exclusion>
642
+                        <groupId>com.github.jsqlparser</groupId>
643
+                        <artifactId>jsqlparser</artifactId>
644
+                    </exclusion>
645
+                </exclusions>
646
+            </dependency>
647
+
648
+            <!-- PF4J -->
649
+            <dependency>
650
+                <groupId>org.pf4j</groupId>
651
+                <artifactId>pf4j-spring</artifactId>
652
+                <version>${pf4j-spring.version}</version>
653
+                <exclusions>
654
+                    <exclusion>
655
+                        <groupId>org.slf4j</groupId>
656
+                        <artifactId>slf4j-log4j12</artifactId>
657
+                    </exclusion>
658
+                </exclusions>
659
+            </dependency>
660
+
661
+            <!-- Vert.x -->
662
+            <dependency>
663
+                <groupId>io.vertx</groupId>
664
+                <artifactId>vertx-core</artifactId>
665
+                <version>${vertx.version}</version>
666
+            </dependency>
667
+            <dependency>
668
+                <groupId>io.vertx</groupId>
669
+                <artifactId>vertx-web</artifactId>
670
+                <version>${vertx.version}</version>
671
+            </dependency>
672
+            <dependency>
673
+                <groupId>io.vertx</groupId>
674
+                <artifactId>vertx-mqtt</artifactId>
675
+                <version>${vertx.version}</version>
676
+            </dependency>
677
+
678
+            <!-- MQTT -->
679
+            <dependency>
680
+                <groupId>org.eclipse.paho</groupId>
681
+                <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
682
+                <version>${mqtt.version}</version>
683
+            </dependency>
684
+
685
+            <!-- 2. ZXing 核心库(Hutool 编译时所需) -->
686
+            <dependency>
687
+                <groupId>com.google.zxing</groupId>
688
+                <artifactId>core</artifactId>
689
+                <version>${zxing.version}</version>
690
+            </dependency>
691
+        </dependencies>
692
+    </dependencyManagement>
693
+
694
+    <build>
695
+        <plugins>
696
+            <!-- 统一 revision 版本 -->
697
+            <plugin>
698
+                <groupId>org.codehaus.mojo</groupId>
699
+                <artifactId>flatten-maven-plugin</artifactId>
700
+                <version>${flatten-maven-plugin.version}</version>
701
+                <configuration>
702
+                    <flattenMode>bom</flattenMode>
703
+                    <updatePomFile>true</updatePomFile>
704
+                </configuration>
705
+                <executions>
706
+                    <execution>
707
+                        <goals>
708
+                            <goal>flatten</goal>
709
+                        </goals>
710
+                        <id>flatten</id>
711
+                        <phase>process-resources</phase>
712
+                    </execution>
713
+                    <execution>
714
+                        <goals>
715
+                            <goal>clean</goal>
716
+                        </goals>
717
+                        <id>flatten.clean</id>
718
+                        <phase>clean</phase>
719
+                    </execution>
720
+                </executions>
721
+            </plugin>
722
+        </plugins>
723
+    </build>
724
+
725
+</project>

+ 45 - 0
jt-framework/.flattened-pom.xml

@@ -0,0 +1,45 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4
+  <modelVersion>4.0.0</modelVersion>
5
+  <groupId>com.jt.cloud</groupId>
6
+  <artifactId>jt-framework</artifactId>
7
+  <version>2.6.1-SNAPSHOT</version>
8
+  <packaging>pom</packaging>
9
+  <description>该包是技术组件,每个子包,代表一个组件。每个组件包括两部分:
10
+            1. core 包:是该组件的核心封装
11
+            2. config 包:是该组件基于 Spring 的配置
12
+
13
+        技术组件,也分成两类:
14
+            1. 框架组件:和我们熟悉的 MyBatis、Redis 等等的拓展
15
+            2. 业务组件:和业务相关的组件的封装,例如说数据字典、操作日志等等。
16
+        如果是业务组件,Maven 名字会包含 biz</description>
17
+  <repositories>
18
+    <repository>
19
+      <id>huaweicloud</id>
20
+      <name>huawei</name>
21
+      <url>https://mirrors.huaweicloud.com/repository/maven/</url>
22
+    </repository>
23
+    <repository>
24
+      <id>aliyunmaven</id>
25
+      <name>aliyun</name>
26
+      <url>https://maven.aliyun.com/repository/public</url>
27
+    </repository>
28
+    <repository>
29
+      <snapshots>
30
+        <enabled>false</enabled>
31
+      </snapshots>
32
+      <id>spring-milestones</id>
33
+      <name>Spring Milestones</name>
34
+      <url>https://repo.spring.io/milestone</url>
35
+    </repository>
36
+    <repository>
37
+      <releases>
38
+        <enabled>false</enabled>
39
+      </releases>
40
+      <id>spring-snapshots</id>
41
+      <name>Spring Snapshots</name>
42
+      <url>https://repo.spring.io/snapshot</url>
43
+    </repository>
44
+  </repositories>
45
+</project>

+ 179 - 0
jt-framework/jt-common/.flattened-pom.xml

@@ -0,0 +1,179 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4
+  <modelVersion>4.0.0</modelVersion>
5
+  <groupId>com.jt.cloud</groupId>
6
+  <artifactId>jt-common</artifactId>
7
+  <version>2.6.1-SNAPSHOT</version>
8
+  <name>jt-common</name>
9
+  <description>定义基础 pojo 类、枚举、工具类等等</description>
10
+  <dependencies>
11
+    <dependency>
12
+      <groupId>org.springframework</groupId>
13
+      <artifactId>spring-core</artifactId>
14
+      <version>6.2.6</version>
15
+      <scope>provided</scope>
16
+    </dependency>
17
+    <dependency>
18
+      <groupId>org.springframework</groupId>
19
+      <artifactId>spring-expression</artifactId>
20
+      <version>6.2.6</version>
21
+      <scope>provided</scope>
22
+    </dependency>
23
+    <dependency>
24
+      <groupId>org.springframework</groupId>
25
+      <artifactId>spring-aop</artifactId>
26
+      <version>6.2.6</version>
27
+      <scope>provided</scope>
28
+    </dependency>
29
+    <dependency>
30
+      <groupId>org.aspectj</groupId>
31
+      <artifactId>aspectjweaver</artifactId>
32
+      <version>1.9.24</version>
33
+      <scope>provided</scope>
34
+    </dependency>
35
+    <dependency>
36
+      <groupId>org.springframework.boot</groupId>
37
+      <artifactId>spring-boot-configuration-processor</artifactId>
38
+      <version>3.4.5</version>
39
+      <scope>compile</scope>
40
+      <optional>true</optional>
41
+    </dependency>
42
+    <dependency>
43
+      <groupId>org.springframework</groupId>
44
+      <artifactId>spring-web</artifactId>
45
+      <version>6.2.6</version>
46
+      <scope>provided</scope>
47
+    </dependency>
48
+    <dependency>
49
+      <groupId>jakarta.servlet</groupId>
50
+      <artifactId>jakarta.servlet-api</artifactId>
51
+      <version>6.0.0</version>
52
+      <scope>provided</scope>
53
+    </dependency>
54
+    <dependency>
55
+      <groupId>org.springdoc</groupId>
56
+      <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
57
+      <version>2.8.3</version>
58
+      <scope>provided</scope>
59
+    </dependency>
60
+    <dependency>
61
+      <groupId>org.springframework.cloud</groupId>
62
+      <artifactId>spring-cloud-openfeign-core</artifactId>
63
+      <version>4.2.1</version>
64
+      <scope>provided</scope>
65
+    </dependency>
66
+    <dependency>
67
+      <groupId>org.apache.skywalking</groupId>
68
+      <artifactId>apm-toolkit-trace</artifactId>
69
+      <version>9.0.0</version>
70
+      <scope>compile</scope>
71
+    </dependency>
72
+    <dependency>
73
+      <groupId>org.projectlombok</groupId>
74
+      <artifactId>lombok</artifactId>
75
+      <version>1.18.36</version>
76
+      <scope>compile</scope>
77
+    </dependency>
78
+    <dependency>
79
+      <groupId>org.mapstruct</groupId>
80
+      <artifactId>mapstruct</artifactId>
81
+      <version>1.6.3</version>
82
+      <scope>compile</scope>
83
+    </dependency>
84
+    <dependency>
85
+      <groupId>org.mapstruct</groupId>
86
+      <artifactId>mapstruct-jdk8</artifactId>
87
+      <version>1.6.3</version>
88
+      <scope>compile</scope>
89
+    </dependency>
90
+    <dependency>
91
+      <groupId>org.mapstruct</groupId>
92
+      <artifactId>mapstruct-processor</artifactId>
93
+      <version>1.6.3</version>
94
+      <scope>compile</scope>
95
+    </dependency>
96
+    <dependency>
97
+      <groupId>com.google.guava</groupId>
98
+      <artifactId>guava</artifactId>
99
+      <version>33.4.8-jre</version>
100
+      <scope>provided</scope>
101
+    </dependency>
102
+    <dependency>
103
+      <groupId>com.fasterxml.jackson.core</groupId>
104
+      <artifactId>jackson-databind</artifactId>
105
+      <version>2.18.3</version>
106
+      <scope>provided</scope>
107
+    </dependency>
108
+    <dependency>
109
+      <groupId>com.fasterxml.jackson.core</groupId>
110
+      <artifactId>jackson-core</artifactId>
111
+      <version>2.18.3</version>
112
+      <scope>provided</scope>
113
+    </dependency>
114
+    <dependency>
115
+      <groupId>com.fasterxml.jackson.datatype</groupId>
116
+      <artifactId>jackson-datatype-jsr310</artifactId>
117
+      <version>2.18.3</version>
118
+      <scope>provided</scope>
119
+    </dependency>
120
+    <dependency>
121
+      <groupId>org.slf4j</groupId>
122
+      <artifactId>slf4j-api</artifactId>
123
+      <version>2.0.17</version>
124
+      <scope>provided</scope>
125
+    </dependency>
126
+    <dependency>
127
+      <groupId>jakarta.validation</groupId>
128
+      <artifactId>jakarta.validation-api</artifactId>
129
+      <version>3.0.2</version>
130
+      <scope>provided</scope>
131
+    </dependency>
132
+    <dependency>
133
+      <groupId>cn.hutool</groupId>
134
+      <artifactId>hutool-all</artifactId>
135
+      <version>5.8.35</version>
136
+      <scope>compile</scope>
137
+    </dependency>
138
+    <dependency>
139
+      <groupId>com.alibaba</groupId>
140
+      <artifactId>transmittable-thread-local</artifactId>
141
+      <version>2.14.5</version>
142
+      <scope>compile</scope>
143
+    </dependency>
144
+    <dependency>
145
+      <groupId>com.fhs-opensource</groupId>
146
+      <artifactId>easy-trans-anno</artifactId>
147
+      <version>3.0.6</version>
148
+      <scope>compile</scope>
149
+    </dependency>
150
+  </dependencies>
151
+  <repositories>
152
+    <repository>
153
+      <id>huaweicloud</id>
154
+      <name>huawei</name>
155
+      <url>https://mirrors.huaweicloud.com/repository/maven/</url>
156
+    </repository>
157
+    <repository>
158
+      <id>aliyunmaven</id>
159
+      <name>aliyun</name>
160
+      <url>https://maven.aliyun.com/repository/public</url>
161
+    </repository>
162
+    <repository>
163
+      <snapshots>
164
+        <enabled>false</enabled>
165
+      </snapshots>
166
+      <id>spring-milestones</id>
167
+      <name>Spring Milestones</name>
168
+      <url>https://repo.spring.io/milestone</url>
169
+    </repository>
170
+    <repository>
171
+      <releases>
172
+        <enabled>false</enabled>
173
+      </releases>
174
+      <id>spring-snapshots</id>
175
+      <name>Spring Snapshots</name>
176
+      <url>https://repo.spring.io/snapshot</url>
177
+    </repository>
178
+  </repositories>
179
+</project>

+ 156 - 0
jt-framework/jt-common/pom.xml

@@ -0,0 +1,156 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xmlns="http://maven.apache.org/POM/4.0.0"
3
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5
+    <parent>
6
+        <groupId>com.jt.cloud</groupId>
7
+        <artifactId>jt-framework</artifactId>
8
+        <version>${revision}</version>
9
+    </parent>
10
+    <modelVersion>4.0.0</modelVersion>
11
+    <artifactId>jt-common</artifactId>
12
+    <packaging>jar</packaging>
13
+
14
+    <name>${project.artifactId}</name>
15
+    <description>定义基础 pojo 类、枚举、工具类等等</description>
16
+
17
+
18
+    <dependencies>
19
+        <!-- Spring 核心 -->
20
+        <dependency>
21
+            <groupId>org.springframework</groupId>
22
+            <artifactId>spring-core</artifactId>
23
+            <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
24
+        </dependency>
25
+        <dependency>
26
+            <groupId>org.springframework</groupId>
27
+            <artifactId>spring-expression</artifactId>
28
+            <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
29
+        </dependency>
30
+        <dependency>
31
+            <groupId>org.springframework</groupId>
32
+            <artifactId>spring-aop</artifactId>
33
+            <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
34
+        </dependency>
35
+        <dependency>
36
+            <groupId>org.aspectj</groupId>
37
+            <artifactId>aspectjweaver</artifactId>
38
+            <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
39
+        </dependency>
40
+
41
+        <dependency>
42
+            <!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 -->
43
+            <groupId>org.springframework.boot</groupId>
44
+            <artifactId>spring-boot-configuration-processor</artifactId>
45
+            <optional>true</optional>
46
+        </dependency>
47
+
48
+        <!-- Web 相关 -->
49
+        <dependency>
50
+            <groupId>org.springframework</groupId>
51
+            <artifactId>spring-web</artifactId>
52
+            <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
53
+        </dependency>
54
+
55
+        <dependency>
56
+            <groupId>jakarta.servlet</groupId>
57
+            <artifactId>jakarta.servlet-api</artifactId>
58
+            <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
59
+        </dependency>
60
+
61
+        <dependency>
62
+            <groupId>org.springdoc</groupId>
63
+            <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
64
+            <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 -->
65
+        </dependency>
66
+
67
+        <!-- RPC 远程调用相关 -->
68
+        <dependency>
69
+            <groupId>org.springframework.cloud</groupId>
70
+            <artifactId>spring-cloud-openfeign-core</artifactId>
71
+            <scope>provided</scope> <!-- 设置为 provided,主要是 api 包使用到 -->
72
+        </dependency>
73
+
74
+        <!-- 监控相关 -->
75
+        <dependency>
76
+            <groupId>org.apache.skywalking</groupId>
77
+            <artifactId>apm-toolkit-trace</artifactId>
78
+        </dependency>
79
+
80
+        <!-- 工具类相关 -->
81
+        <dependency>
82
+            <groupId>org.projectlombok</groupId>
83
+            <artifactId>lombok</artifactId>
84
+        </dependency>
85
+
86
+        <dependency>
87
+            <groupId>org.mapstruct</groupId>
88
+            <artifactId>mapstruct</artifactId>
89
+        </dependency>
90
+        <dependency>
91
+            <groupId>org.mapstruct</groupId>
92
+            <artifactId>mapstruct-jdk8</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
93
+        </dependency>
94
+        <dependency>
95
+            <groupId>org.mapstruct</groupId>
96
+            <artifactId>mapstruct-processor</artifactId>
97
+        </dependency>
98
+
99
+        <dependency>
100
+            <groupId>com.google.guava</groupId>
101
+            <artifactId>guava</artifactId>
102
+            <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
103
+        </dependency>
104
+
105
+        <dependency>
106
+            <groupId>com.fasterxml.jackson.core</groupId>
107
+            <artifactId>jackson-databind</artifactId>
108
+            <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
109
+        </dependency>
110
+        <dependency>
111
+            <groupId>com.fasterxml.jackson.core</groupId>
112
+            <artifactId>jackson-core</artifactId>
113
+            <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
114
+        </dependency>
115
+        <dependency>
116
+            <groupId>com.fasterxml.jackson.datatype</groupId>
117
+            <artifactId>jackson-datatype-jsr310</artifactId>
118
+            <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
119
+        </dependency>
120
+
121
+        <dependency>
122
+            <groupId>org.slf4j</groupId>
123
+            <artifactId>slf4j-api</artifactId>
124
+            <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
125
+        </dependency>
126
+
127
+        <dependency>
128
+            <groupId>jakarta.validation</groupId>
129
+            <artifactId>jakarta.validation-api</artifactId>
130
+            <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 -->
131
+        </dependency>
132
+
133
+        <dependency>
134
+            <groupId>cn.hutool</groupId>
135
+            <artifactId>hutool-all</artifactId>
136
+        </dependency>
137
+
138
+        <dependency>
139
+            <groupId>com.alibaba</groupId>
140
+            <artifactId>transmittable-thread-local</artifactId>
141
+        </dependency>
142
+
143
+        <dependency>
144
+            <groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 -->
145
+            <artifactId>easy-trans-anno</artifactId> <!-- 默认引入的原因,方便 xxx-module-api 包使用 -->
146
+        </dependency>
147
+
148
+        <!-- Test 测试相关 -->
149
+        <dependency>
150
+            <groupId>org.springframework.boot</groupId>
151
+            <artifactId>spring-boot-starter-test</artifactId>
152
+            <scope>test</scope>
153
+        </dependency>
154
+    </dependencies>
155
+
156
+</project>

+ 59 - 0
jt-framework/jt-common/src/main/java/com/fhs/trans/service/AutoTransable.java

@@ -0,0 +1,59 @@
1
+package com.fhs.trans.service;
2
+
3
+import com.fhs.core.trans.vo.VO;
4
+
5
+import java.util.ArrayList;
6
+import java.util.List;
7
+
8
+/**
9
+ * 只有实现了这个接口的才能自动翻译
10
+ *
11
+ * 为什么要赋值粘贴到 jt-common 包下?
12
+ * 因为 AutoTransable 属于 easy-trans-service 下,无法方便的在 jt-module-xxx-api 模块下使用
13
+ *
14
+ * @author jackwang
15
+ * @since  2020-05-19 10:26:15
16
+ */
17
+public interface AutoTransable<V extends VO> {
18
+
19
+    /**
20
+     * 根据 ids 查询数据列表
21
+     *
22
+     * 改方法已过期啦,请使用 selectByIds
23
+     *
24
+     * @param ids 编号数组
25
+     * @return 数据列表
26
+     */
27
+    @Deprecated
28
+    default List<V> findByIds(List<? extends Object> ids){
29
+        return new ArrayList<>();
30
+    }
31
+
32
+    /**
33
+     * 根据 ids 查询
34
+     *
35
+     * @param ids 编号数组
36
+     * @return 数据列表
37
+     */
38
+    default List<V> selectByIds(List<? extends Object> ids){
39
+        return this.findByIds(ids);
40
+    }
41
+
42
+    /**
43
+     * 获取 db 中所有的数据
44
+     *
45
+     * @return db 中所有的数据
46
+     */
47
+    default List<V> select(){
48
+        return new ArrayList<>();
49
+    }
50
+
51
+    /**
52
+     * 根据 id 获取 vo
53
+     *
54
+     * @param primaryValue id
55
+     * @return vo
56
+     */
57
+    V selectById(Object primaryValue);
58
+
59
+}

+ 34 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/infra/logger/ApiAccessLogCommonApi.java

@@ -0,0 +1,34 @@
1
+package com.jt.cloud.framework.common.biz.infra.logger;
2
+
3
+import com.jt.cloud.framework.common.biz.infra.logger.dto.ApiAccessLogCreateReqDTO;
4
+import com.jt.cloud.framework.common.enums.RpcConstants;
5
+import com.jt.cloud.framework.common.pojo.CommonResult;
6
+import io.swagger.v3.oas.annotations.Operation;
7
+import io.swagger.v3.oas.annotations.tags.Tag;
8
+import jakarta.validation.Valid;
9
+import org.springframework.cloud.openfeign.FeignClient;
10
+import org.springframework.scheduling.annotation.Async;
11
+import org.springframework.web.bind.annotation.PostMapping;
12
+import org.springframework.web.bind.annotation.RequestBody;
13
+
14
+@FeignClient(name = RpcConstants.INFRA_NAME) // TODO osy:fallbackFactory =
15
+@Tag(name = "RPC 服务 - API 访问日志")
16
+public interface ApiAccessLogCommonApi {
17
+
18
+    String PREFIX = RpcConstants.INFRA_PREFIX + "/api-access-log";
19
+
20
+    @PostMapping(PREFIX + "/create")
21
+    @Operation(summary = "创建 API 访问日志")
22
+    CommonResult<Boolean> createApiAccessLog(@Valid @RequestBody ApiAccessLogCreateReqDTO createDTO);
23
+
24
+    /**
25
+     * 【异步】创建 API 访问日志
26
+     *
27
+     * @param createDTO 访问日志 DTO
28
+     */
29
+    @Async
30
+    default void createApiAccessLogAsync(ApiAccessLogCreateReqDTO createDTO) {
31
+        createApiAccessLog(createDTO).checkError();
32
+    }
33
+
34
+}

+ 34 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/infra/logger/ApiErrorLogCommonApi.java

@@ -0,0 +1,34 @@
1
+package com.jt.cloud.framework.common.biz.infra.logger;
2
+
3
+import com.jt.cloud.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO;
4
+import com.jt.cloud.framework.common.enums.RpcConstants;
5
+import com.jt.cloud.framework.common.pojo.CommonResult;
6
+import io.swagger.v3.oas.annotations.Operation;
7
+import io.swagger.v3.oas.annotations.tags.Tag;
8
+import jakarta.validation.Valid;
9
+import org.springframework.cloud.openfeign.FeignClient;
10
+import org.springframework.scheduling.annotation.Async;
11
+import org.springframework.web.bind.annotation.PostMapping;
12
+import org.springframework.web.bind.annotation.RequestBody;
13
+
14
+@FeignClient(name = RpcConstants.INFRA_NAME) // TODO osy:fallbackFactory =
15
+@Tag(name = "RPC 服务 - API 异常日志")
16
+public interface ApiErrorLogCommonApi {
17
+
18
+    String PREFIX = RpcConstants.INFRA_PREFIX + "/api-error-log";
19
+
20
+    @PostMapping(PREFIX + "/create")
21
+    @Operation(summary = "创建 API 异常日志")
22
+    CommonResult<Boolean> createApiErrorLog(@Valid @RequestBody ApiErrorLogCreateReqDTO createDTO);
23
+
24
+    /**
25
+     * 【异步】创建 API 异常日志
26
+     *
27
+     * @param createDTO 异常日志 DTO
28
+     */
29
+    @Async
30
+    default void createApiErrorLogAsync(ApiErrorLogCreateReqDTO createDTO) {
31
+        createApiErrorLog(createDTO).checkError();
32
+    }
33
+
34
+}

+ 103 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/infra/logger/dto/ApiAccessLogCreateReqDTO.java

@@ -0,0 +1,103 @@
1
+package com.jt.cloud.framework.common.biz.infra.logger.dto;
2
+
3
+import jakarta.validation.constraints.NotNull;
4
+import lombok.Data;
5
+
6
+import java.time.LocalDateTime;
7
+
8
+/**
9
+ * API 访问日志
10
+ *
11
+ * @author jt
12
+ */
13
+@Data
14
+public class ApiAccessLogCreateReqDTO {
15
+
16
+    /**
17
+     * 链路追踪编号
18
+     */
19
+    private String traceId;
20
+    /**
21
+     * 用户编号
22
+     */
23
+    private Long userId;
24
+    /**
25
+     * 用户类型
26
+     */
27
+    private Integer userType;
28
+    /**
29
+     * 应用名
30
+     */
31
+    @NotNull(message = "应用名不能为空")
32
+    private String applicationName;
33
+
34
+    /**
35
+     * 请求方法名
36
+     */
37
+    @NotNull(message = "http 请求方法不能为空")
38
+    private String requestMethod;
39
+    /**
40
+     * 访问地址
41
+     */
42
+    @NotNull(message = "访问地址不能为空")
43
+    private String requestUrl;
44
+    /**
45
+     * 请求参数
46
+     */
47
+    private String requestParams;
48
+    /**
49
+     * 响应结果
50
+     */
51
+    private String responseBody;
52
+    /**
53
+     * 用户 IP
54
+     */
55
+    @NotNull(message = "ip 不能为空")
56
+    private String userIp;
57
+    /**
58
+     * 浏览器 UA
59
+     */
60
+    @NotNull(message = "User-Agent 不能为空")
61
+    private String userAgent;
62
+
63
+    /**
64
+     * 操作模块
65
+     */
66
+    private String operateModule;
67
+    /**
68
+     * 操作名
69
+     */
70
+    private String operateName;
71
+    /**
72
+     * 操作分类
73
+     *
74
+     * 枚举,参见 OperateTypeEnum 类
75
+     */
76
+    private Integer operateType;
77
+
78
+    /**
79
+     * 开始请求时间
80
+     */
81
+    @NotNull(message = "开始请求时间不能为空")
82
+    private LocalDateTime beginTime;
83
+    /**
84
+     * 结束请求时间
85
+     */
86
+    @NotNull(message = "结束请求时间不能为空")
87
+    private LocalDateTime endTime;
88
+    /**
89
+     * 执行时长,单位:毫秒
90
+     */
91
+    @NotNull(message = "执行时长不能为空")
92
+    private Integer duration;
93
+    /**
94
+     * 结果码
95
+     */
96
+    @NotNull(message = "错误码不能为空")
97
+    private Integer resultCode;
98
+    /**
99
+     * 结果提示
100
+     */
101
+    private String resultMsg;
102
+
103
+}

+ 68 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/infra/logger/dto/ApiErrorLogCreateReqDTO.java

@@ -0,0 +1,68 @@
1
+package com.jt.cloud.framework.common.biz.infra.logger.dto;
2
+
3
+import io.swagger.v3.oas.annotations.media.Schema;
4
+import jakarta.validation.constraints.NotNull;
5
+import lombok.Data;
6
+
7
+import java.time.LocalDateTime;
8
+
9
+@Schema(description = "RPC 服务 - API 错误日志创建 Request DTO")
10
+@Data
11
+public class ApiErrorLogCreateReqDTO {
12
+
13
+    @Schema(description = "链路追踪编号", example = "89aca178-a370-411c-ae02-3f0d672be4ab")
14
+    private String traceId;
15
+
16
+    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
17
+    private Long userId;
18
+    @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
19
+    private Integer userType;
20
+    @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "system-server")
21
+    @NotNull(message = "应用名不能为空")
22
+    private String applicationName;
23
+
24
+    @Schema(description = "请求方法名", requiredMode = Schema.RequiredMode.REQUIRED, example = "GET")
25
+    @NotNull(message = "http 请求方法不能为空")
26
+    private String requestMethod;
27
+    @Schema(description = "请求地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "/xxx/yyy")
28
+    @NotNull(message = "访问地址不能为空")
29
+    private String requestUrl;
30
+    @Schema(description = "请求参数", requiredMode = Schema.RequiredMode.REQUIRED)
31
+    @NotNull(message = "请求参数不能为空")
32
+    private String requestParams;
33
+    @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
34
+    @NotNull(message = "ip 不能为空")
35
+    private String userIp;
36
+    @Schema(description = "浏览器 UserAgent", requiredMode = Schema.RequiredMode.REQUIRED, example = "Mozilla/5.0")
37
+    @NotNull(message = "User-Agent 不能为空")
38
+    private String userAgent;
39
+
40
+    @Schema(description = "异常时间", requiredMode = Schema.RequiredMode.REQUIRED)
41
+    @NotNull(message = "异常时间不能为空")
42
+    private LocalDateTime exceptionTime;
43
+    @Schema(description = "异常名", requiredMode = Schema.RequiredMode.REQUIRED)
44
+    @NotNull(message = "异常名不能为空")
45
+    private String exceptionName;
46
+    @Schema(description = "异常发生的类全名", requiredMode = Schema.RequiredMode.REQUIRED)
47
+    @NotNull(message = "异常发生的类全名不能为空")
48
+    private String exceptionClassName;
49
+    @Schema(description = "异常发生的类文件", requiredMode = Schema.RequiredMode.REQUIRED)
50
+    @NotNull(message = "异常发生的类文件不能为空")
51
+    private String exceptionFileName;
52
+    @Schema(description = "异常发生的方法名", requiredMode = Schema.RequiredMode.REQUIRED)
53
+    @NotNull(message = "异常发生的方法名不能为空")
54
+    private String exceptionMethodName;
55
+    @Schema(description = "异常发生的方法所在行", requiredMode = Schema.RequiredMode.REQUIRED)
56
+    @NotNull(message = "异常发生的方法所在行不能为空")
57
+    private Integer exceptionLineNumber;
58
+    @Schema(description = "异常的栈轨迹异常的栈轨迹", requiredMode = Schema.RequiredMode.REQUIRED)
59
+    @NotNull(message = "异常的栈轨迹不能为空")
60
+    private String exceptionStackTrace;
61
+    @Schema(description = "异常导致的根消息", requiredMode = Schema.RequiredMode.REQUIRED)
62
+    @NotNull(message = "异常导致的根消息不能为空")
63
+    private String exceptionRootCauseMessage;
64
+    @Schema(description = "异常导致的消息", requiredMode = Schema.RequiredMode.REQUIRED)
65
+    @NotNull(message = "异常导致的消息不能为空")
66
+    private String exceptionMessage;
67
+
68
+}

+ 4 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/infra/package-info.java

@@ -0,0 +1,4 @@
1
+/**
2
+ * 针对 infra 模块的 api 包
3
+ */
4
+package com.jt.cloud.framework.common.biz.infra;

+ 4 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/package-info.java

@@ -0,0 +1,4 @@
1
+/**
2
+ * 特殊:用于 framework 下,starter 需要调用 biz 业务模块的接口定义!
3
+ */
4
+package com.jt.cloud.framework.common.biz;

+ 26 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/dict/DictDataCommonApi.java

@@ -0,0 +1,26 @@
1
+package com.jt.cloud.framework.common.biz.system.dict;
2
+
3
+import com.jt.cloud.framework.common.biz.system.dict.dto.DictDataRespDTO;
4
+import com.jt.cloud.framework.common.enums.RpcConstants;
5
+import com.jt.cloud.framework.common.pojo.CommonResult;
6
+import io.swagger.v3.oas.annotations.Operation;
7
+import io.swagger.v3.oas.annotations.Parameter;
8
+import io.swagger.v3.oas.annotations.tags.Tag;
9
+import org.springframework.cloud.openfeign.FeignClient;
10
+import org.springframework.web.bind.annotation.GetMapping;
11
+import org.springframework.web.bind.annotation.RequestParam;
12
+
13
+import java.util.List;
14
+
15
+@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false) // TODO osy:fallbackFactory =
16
+@Tag(name = "RPC 服务 - 字典数据")
17
+public interface DictDataCommonApi {
18
+
19
+    String PREFIX = RpcConstants.SYSTEM_PREFIX + "/dict-data";
20
+
21
+    @GetMapping(PREFIX + "/list")
22
+    @Operation(summary = "获得指定字典类型的字典数据列表")
23
+    @Parameter(name = "dictType", description = "字典类型", example = "SEX", required = true)
24
+    CommonResult<List<DictDataRespDTO>> getDictDataList(@RequestParam("dictType") String dictType);
25
+
26
+}

+ 22 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/dict/dto/DictDataRespDTO.java

@@ -0,0 +1,22 @@
1
+package com.jt.cloud.framework.common.biz.system.dict.dto;
2
+
3
+import io.swagger.v3.oas.annotations.media.Schema;
4
+import lombok.Data;
5
+
6
+@Schema(description = "RPC 服务 - 字典数据 Response DTO")
7
+@Data
8
+public class DictDataRespDTO {
9
+
10
+    @Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "jt")
11
+    private String label;
12
+
13
+    @Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "iocoder")
14
+    private String value;
15
+
16
+    @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex")
17
+    private String dictType;
18
+
19
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
20
+    private Integer status; // 参见 CommonStatusEnum 枚举
21
+
22
+}

+ 34 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/logger/OperateLogCommonApi.java

@@ -0,0 +1,34 @@
1
+package com.jt.cloud.framework.common.biz.system.logger;
2
+
3
+import com.jt.cloud.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO;
4
+import com.jt.cloud.framework.common.enums.RpcConstants;
5
+import com.jt.cloud.framework.common.pojo.CommonResult;
6
+import io.swagger.v3.oas.annotations.Operation;
7
+import io.swagger.v3.oas.annotations.tags.Tag;
8
+import jakarta.validation.Valid;
9
+import org.springframework.cloud.openfeign.FeignClient;
10
+import org.springframework.scheduling.annotation.Async;
11
+import org.springframework.web.bind.annotation.PostMapping;
12
+import org.springframework.web.bind.annotation.RequestBody;
13
+
14
+@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false) // TODO osy:fallbackFactory =
15
+@Tag(name = "RPC 服务 - 操作日志")
16
+public interface OperateLogCommonApi {
17
+
18
+    String PREFIX = RpcConstants.SYSTEM_PREFIX + "/operate-log";
19
+
20
+    @PostMapping(PREFIX + "/create")
21
+    @Operation(summary = "创建操作日志")
22
+    CommonResult<Boolean> createOperateLog(@Valid @RequestBody OperateLogCreateReqDTO createReqDTO);
23
+
24
+    /**
25
+     * 【异步】创建操作日志
26
+     *
27
+     * @param createReqDTO 请求
28
+     */
29
+    @Async
30
+    default void createOperateLogAsync(OperateLogCreateReqDTO createReqDTO) {
31
+        createOperateLog(createReqDTO).checkError();
32
+    }
33
+
34
+}

+ 53 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/logger/dto/OperateLogCreateReqDTO.java

@@ -0,0 +1,53 @@
1
+package com.jt.cloud.framework.common.biz.system.logger.dto;
2
+
3
+import io.swagger.v3.oas.annotations.media.Schema;
4
+import jakarta.validation.constraints.NotEmpty;
5
+import jakarta.validation.constraints.NotNull;
6
+import lombok.Data;
7
+
8
+@Schema(name = "RPC 服务 - 系统操作日志 Create Request DTO")
9
+@Data
10
+public class OperateLogCreateReqDTO {
11
+
12
+    @Schema(description = "链路追踪编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "89aca178-a370-411c-ae02-3f0d672be4ab")
13
+    private String traceId;
14
+
15
+    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
16
+    @NotNull(message = "用户编号不能为空")
17
+    private Long userId;
18
+    @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2" )
19
+    @NotNull(message = "用户类型不能为空")
20
+    private Integer userType;
21
+    @Schema(description = "操作模块类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单")
22
+    @NotEmpty(message = "操作模块类型不能为空")
23
+    private String type;
24
+    @Schema(description = "操作名", requiredMode = Schema.RequiredMode.REQUIRED, example = "创建订单")
25
+    @NotEmpty(message = "操作名不能为空")
26
+    private String subType;
27
+    @Schema(description = "操作模块业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "188")
28
+    @NotNull(message = "操作模块业务编号不能为空")
29
+    private Long bizId;
30
+    @Schema(description = "操作内容", requiredMode = Schema.RequiredMode.REQUIRED,
31
+            example = "修改编号为 1 的用户信息,将性别从男改成女,将姓名从jt改成源码")
32
+    @NotEmpty(message = "操作内容不能为空")
33
+    private String action;
34
+    @Schema(description = "拓展字段", example = "{\"orderId\": \"1\"}")
35
+    private String extra;
36
+
37
+    @Schema(description = "请求方法名", requiredMode = Schema.RequiredMode.REQUIRED, example = "GET")
38
+    @NotEmpty(message = "请求方法名不能为空")
39
+    private String requestMethod;
40
+    @Schema(description = "请求地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "/order/get")
41
+    @NotEmpty(message = "请求地址不能为空")
42
+    private String requestUrl;
43
+    @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
44
+    @NotEmpty(message = "用户 IP 不能为空")
45
+    private String userIp;
46
+    @Schema(description = "浏览器 UserAgent", requiredMode = Schema.RequiredMode.REQUIRED, example = "Mozilla/5.0")
47
+    @NotEmpty(message = "浏览器 UA 不能为空")
48
+    private String userAgent;
49
+
50
+    @Schema(description = "创建人")
51
+    private String creatorName;
52
+
53
+}

+ 52 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/oauth2/OAuth2TokenCommonApi.java

@@ -0,0 +1,52 @@
1
+package com.jt.cloud.framework.common.biz.system.oauth2;
2
+
3
+import com.jt.cloud.framework.common.enums.RpcConstants;
4
+import com.jt.cloud.framework.common.pojo.CommonResult;
5
+import com.jt.cloud.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
6
+import com.jt.cloud.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
7
+import com.jt.cloud.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO;
8
+import io.swagger.v3.oas.annotations.tags.Tag;
9
+import io.swagger.v3.oas.annotations.Parameter;
10
+import io.swagger.v3.oas.annotations.Parameters;
11
+import io.swagger.v3.oas.annotations.Operation;
12
+import org.springframework.cloud.openfeign.FeignClient;
13
+import org.springframework.web.bind.annotation.*;
14
+
15
+import jakarta.validation.Valid;
16
+
17
+@FeignClient(name = RpcConstants.SYSTEM_NAME) // TODO osy:fallbackFactory =
18
+@Tag(name = "RPC 服务 - OAuth2.0 令牌")
19
+public interface OAuth2TokenCommonApi {
20
+
21
+    String PREFIX = RpcConstants.SYSTEM_PREFIX + "/oauth2/token";
22
+
23
+    /**
24
+     * 校验 Token 的 URL 地址,主要是提供给 Gateway 使用
25
+     */
26
+    @SuppressWarnings("HttpUrlsUsage")
27
+    String URL_CHECK = "http://" + RpcConstants.SYSTEM_NAME + PREFIX + "/check";
28
+
29
+    @PostMapping(PREFIX + "/create")
30
+    @Operation(summary = "创建访问令牌")
31
+    CommonResult<OAuth2AccessTokenRespDTO> createAccessToken(@Valid @RequestBody OAuth2AccessTokenCreateReqDTO reqDTO);
32
+
33
+    @GetMapping(PREFIX + "/check")
34
+    @Operation(summary = "校验访问令牌")
35
+    @Parameter(name = "accessToken", description = "访问令牌", required = true, example = "tudou")
36
+    CommonResult<OAuth2AccessTokenCheckRespDTO> checkAccessToken(@RequestParam("accessToken") String accessToken);
37
+
38
+    @DeleteMapping(PREFIX + "/remove")
39
+    @Operation(summary = "移除访问令牌")
40
+    @Parameter(name = "accessToken", description = "访问令牌", required = true, example = "tudou")
41
+    CommonResult<OAuth2AccessTokenRespDTO> removeAccessToken(@RequestParam("accessToken") String accessToken);
42
+
43
+    @PutMapping(PREFIX + "/refresh")
44
+    @Operation(summary = "刷新访问令牌")
45
+    @Parameters({
46
+        @Parameter(name = "refreshToken", description = "刷新令牌", required = true, example = "haha"),
47
+        @Parameter(name = "clientId", description = "客户端编号", required = true, example = "jtyuanma")
48
+    })
49
+    CommonResult<OAuth2AccessTokenRespDTO> refreshAccessToken(@RequestParam("refreshToken") String refreshToken,
50
+                                                              @RequestParam("clientId") String clientId);
51
+
52
+}

+ 33 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java

@@ -0,0 +1,33 @@
1
+package com.jt.cloud.framework.common.biz.system.oauth2.dto;
2
+
3
+import io.swagger.v3.oas.annotations.media.Schema;
4
+import lombok.Data;
5
+
6
+import java.io.Serializable;
7
+import java.time.LocalDateTime;
8
+import java.util.List;
9
+import java.util.Map;
10
+
11
+@Schema(description = "RPC 服务 - OAuth2 访问令牌的校验 Response DTO")
12
+@Data
13
+public class OAuth2AccessTokenCheckRespDTO implements Serializable {
14
+
15
+    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
16
+    private Long userId;
17
+
18
+    @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
19
+    private Integer userType;
20
+
21
+    @Schema(description = "用户信息", example = "{\"nickname\": \"jt\"}")
22
+    private Map<String, String> userInfo;
23
+
24
+    @Schema(description = "租户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
25
+    private Long tenantId;
26
+
27
+    @Schema(description = "授权范围的数组", example = "user_info")
28
+    private List<String> scopes;
29
+
30
+    @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
31
+    private LocalDateTime expiresTime;
32
+
33
+}

+ 32 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenCreateReqDTO.java

@@ -0,0 +1,32 @@
1
+package com.jt.cloud.framework.common.biz.system.oauth2.dto;
2
+
3
+import com.jt.cloud.framework.common.enums.UserTypeEnum;
4
+import com.jt.cloud.framework.common.validation.InEnum;
5
+import io.swagger.v3.oas.annotations.media.Schema;
6
+import lombok.Data;
7
+
8
+import jakarta.validation.constraints.NotNull;
9
+import java.io.Serializable;
10
+import java.util.List;
11
+
12
+@Schema(description = "RPC 服务 - OAuth2 访问令牌创建 Request DTO")
13
+@Data
14
+public class OAuth2AccessTokenCreateReqDTO implements Serializable {
15
+
16
+    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
17
+    @NotNull(message = "用户编号不能为空")
18
+    private Long userId;
19
+
20
+    @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
21
+    @NotNull(message = "用户类型不能为空")
22
+    @InEnum(value = UserTypeEnum.class, message = "用户类型必须是 {value}")
23
+    private Integer userType;
24
+
25
+    @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "jtyuanma")
26
+    @NotNull(message = "客户端编号不能为空")
27
+    private String clientId;
28
+
29
+    @Schema(description = "授权范围的数组", example = "user_info")
30
+    private List<String> scopes;
31
+
32
+}

+ 30 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenRespDTO.java

@@ -0,0 +1,30 @@
1
+package com.jt.cloud.framework.common.biz.system.oauth2.dto;
2
+
3
+import io.swagger.v3.oas.annotations.media.Schema;
4
+import lombok.Data;
5
+import lombok.experimental.Accessors;
6
+
7
+import java.io.Serializable;
8
+import java.time.LocalDateTime;
9
+
10
+@Schema(description = "RPC 服务 - OAuth2 访问令牌的信息 Response DTO")
11
+@Data
12
+@Accessors(chain = true)
13
+public class OAuth2AccessTokenRespDTO implements Serializable {
14
+
15
+    @Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou")
16
+    private String accessToken;
17
+
18
+    @Schema(description = "刷新令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "haha")
19
+    private String refreshToken;
20
+
21
+    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
22
+    private Long userId;
23
+
24
+    @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1" )
25
+    private Integer userType;
26
+
27
+    @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
28
+    private LocalDateTime expiresTime;
29
+
30
+}

+ 4 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/package-info.java

@@ -0,0 +1,4 @@
1
+/**
2
+ * 针对 system 模块的 api 包
3
+ */
4
+package com.jt.cloud.framework.common.biz.system;

+ 43 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/permission/PermissionCommonApi.java

@@ -0,0 +1,43 @@
1
+package com.jt.cloud.framework.common.biz.system.permission;
2
+
3
+import com.jt.cloud.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
4
+import com.jt.cloud.framework.common.enums.RpcConstants;
5
+import com.jt.cloud.framework.common.pojo.CommonResult;
6
+import io.swagger.v3.oas.annotations.Operation;
7
+import io.swagger.v3.oas.annotations.Parameter;
8
+import io.swagger.v3.oas.annotations.Parameters;
9
+import io.swagger.v3.oas.annotations.tags.Tag;
10
+import org.springframework.cloud.openfeign.FeignClient;
11
+import org.springframework.web.bind.annotation.GetMapping;
12
+import org.springframework.web.bind.annotation.RequestParam;
13
+
14
+@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false) // TODO osy:fallbackFactory =
15
+@Tag(name = "RPC 服务 - 权限")
16
+public interface PermissionCommonApi {
17
+
18
+    String PREFIX = RpcConstants.SYSTEM_PREFIX + "/permission";
19
+
20
+    @GetMapping(PREFIX + "/has-any-permissions")
21
+    @Operation(summary = "判断是否有权限,任一一个即可")
22
+    @Parameters({
23
+            @Parameter(name = "userId", description = "用户编号", example = "1", required = true),
24
+            @Parameter(name = "permissions", description = "权限", example = "read,write", required = true)
25
+    })
26
+    CommonResult<Boolean> hasAnyPermissions(@RequestParam("userId") Long userId,
27
+                                            @RequestParam("permissions") String... permissions);
28
+
29
+    @GetMapping(PREFIX + "/has-any-roles")
30
+    @Operation(summary = "判断是否有角色,任一一个即可")
31
+    @Parameters({
32
+            @Parameter(name = "userId", description = "用户编号", example = "1", required = true),
33
+            @Parameter(name = "roles", description = "角色数组", example = "2", required = true)
34
+    })
35
+    CommonResult<Boolean> hasAnyRoles(@RequestParam("userId") Long userId,
36
+                                      @RequestParam("roles") String... roles);
37
+
38
+    @GetMapping(PREFIX + "/get-dept-data-permission")
39
+    @Operation(summary = "获得登陆用户的部门数据权限")
40
+    @Parameter(name = "userId", description = "用户编号", example = "2", required = true)
41
+    CommonResult<DeptDataPermissionRespDTO> getDeptDataPermission(@RequestParam("userId") Long userId);
42
+
43
+}

+ 28 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/permission/dto/CompanytDataPermissionRespDTO.java

@@ -0,0 +1,28 @@
1
+package com.jt.cloud.framework.common.biz.system.permission.dto;
2
+
3
+import io.swagger.v3.oas.annotations.media.Schema;
4
+import lombok.Data;
5
+
6
+import java.util.HashSet;
7
+import java.util.Set;
8
+
9
+@Schema(description = "RPC 服务 - 公司的数据权限 Response DTO")
10
+@Data
11
+public class CompanytDataPermissionRespDTO {
12
+
13
+    @Schema(description = "是否可查看全部数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
14
+    private Boolean all;
15
+
16
+    @Schema(description = "是否可查看自己的数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
17
+    private Boolean self;
18
+
19
+    @Schema(description = "可查看的公司编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 3]")
20
+    private Set<Long> ids;
21
+
22
+    public CompanytDataPermissionRespDTO() {
23
+        this.all = false;
24
+        this.self = false;
25
+        this.ids = new HashSet<>();
26
+    }
27
+
28
+}

+ 28 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/permission/dto/DeptDataPermissionRespDTO.java

@@ -0,0 +1,28 @@
1
+package com.jt.cloud.framework.common.biz.system.permission.dto;
2
+
3
+import io.swagger.v3.oas.annotations.media.Schema;
4
+import lombok.Data;
5
+
6
+import java.util.HashSet;
7
+import java.util.Set;
8
+
9
+@Schema(description = "RPC 服务 - 部门的数据权限 Response DTO")
10
+@Data
11
+public class DeptDataPermissionRespDTO {
12
+
13
+    @Schema(description = "是否可查看全部数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
14
+    private Boolean all;
15
+
16
+    @Schema(description = "是否可查看自己的数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
17
+    private Boolean self;
18
+
19
+    @Schema(description = "可查看的部门编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 3]")
20
+    private Set<Long> deptIds;
21
+
22
+    public DeptDataPermissionRespDTO() {
23
+        this.all = false;
24
+        this.self = false;
25
+        this.deptIds = new HashSet<>();
26
+    }
27
+
28
+}

+ 29 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/biz/system/tenant/TenantCommonApi.java

@@ -0,0 +1,29 @@
1
+package com.jt.cloud.framework.common.biz.system.tenant;
2
+
3
+import com.jt.cloud.framework.common.enums.RpcConstants;
4
+import com.jt.cloud.framework.common.pojo.CommonResult;
5
+import io.swagger.v3.oas.annotations.tags.Tag;
6
+import io.swagger.v3.oas.annotations.Parameter;
7
+import io.swagger.v3.oas.annotations.Operation;
8
+import org.springframework.cloud.openfeign.FeignClient;
9
+import org.springframework.web.bind.annotation.GetMapping;
10
+import org.springframework.web.bind.annotation.RequestParam;
11
+
12
+import java.util.List;
13
+
14
+@FeignClient(name = RpcConstants.SYSTEM_NAME) // TODO osy:fallbackFactory =
15
+@Tag(name = "RPC 服务 - 多租户")
16
+public interface TenantCommonApi {
17
+
18
+    String PREFIX = RpcConstants.SYSTEM_PREFIX + "/tenant";
19
+
20
+    @GetMapping(PREFIX + "/id-list")
21
+    @Operation(summary = "获得所有租户编号")
22
+    CommonResult<List<Long>> getTenantIdList();
23
+
24
+    @GetMapping(PREFIX + "/valid")
25
+    @Operation(summary = "校验租户是否合法")
26
+    @Parameter(name = "id", description = "租户编号", required = true, example = "1024")
27
+    CommonResult<Boolean> validTenant(@RequestParam("id") Long id);
28
+
29
+}

+ 11 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/constant/DeviceConstant.java

@@ -0,0 +1,11 @@
1
+package com.jt.cloud.framework.common.constant;
2
+
3
+/**
4
+ * @author hukai
5
+ * @date 2025/9/2 15:41
6
+ * @description: 设备常量
7
+ */
8
+public class DeviceConstant {
9
+
10
+    public static final String COMMA = ",";
11
+}

+ 15 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/core/ArrayValuable.java

@@ -0,0 +1,15 @@
1
+package com.jt.cloud.framework.common.core;
2
+
3
+/**
4
+ * 可生成 T 数组的接口
5
+ *
6
+ * @author HUIHUI
7
+ */
8
+public interface ArrayValuable<T> {
9
+
10
+    /**
11
+     * @return 数组
12
+     */
13
+    T[] array();
14
+
15
+} 

+ 22 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/core/KeyValue.java

@@ -0,0 +1,22 @@
1
+package com.jt.cloud.framework.common.core;
2
+
3
+import lombok.AllArgsConstructor;
4
+import lombok.Data;
5
+import lombok.NoArgsConstructor;
6
+
7
+import java.io.Serializable;
8
+
9
+/**
10
+ * Key Value 的键值对
11
+ *
12
+ * @author jt
13
+ */
14
+@Data
15
+@NoArgsConstructor
16
+@AllArgsConstructor
17
+public class KeyValue<K, V> implements Serializable {
18
+
19
+    private K key;
20
+    private V value;
21
+
22
+}

+ 46 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/CommonStatusEnum.java

@@ -0,0 +1,46 @@
1
+package com.jt.cloud.framework.common.enums;
2
+
3
+import cn.hutool.core.util.ObjUtil;
4
+import com.jt.cloud.framework.common.core.ArrayValuable;
5
+import lombok.AllArgsConstructor;
6
+import lombok.Getter;
7
+
8
+import java.util.Arrays;
9
+
10
+/**
11
+ * 通用状态枚举
12
+ *
13
+ * @author jt
14
+ */
15
+@Getter
16
+@AllArgsConstructor
17
+public enum CommonStatusEnum implements ArrayValuable<Integer> {
18
+
19
+    ENABLE(0, "开启"),
20
+    DISABLE(1, "关闭");
21
+
22
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CommonStatusEnum::getStatus).toArray(Integer[]::new);
23
+
24
+    /**
25
+     * 状态值
26
+     */
27
+    private final Integer status;
28
+    /**
29
+     * 状态名
30
+     */
31
+    private final String name;
32
+
33
+    @Override
34
+    public Integer[] array() {
35
+        return ARRAYS;
36
+    }
37
+
38
+    public static boolean isEnable(Integer status) {
39
+        return ObjUtil.equal(ENABLE.status, status);
40
+    }
41
+
42
+    public static boolean isDisable(Integer status) {
43
+        return ObjUtil.equal(DISABLE.status, status);
44
+    }
45
+
46
+}

+ 46 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/DateIntervalEnum.java

@@ -0,0 +1,46 @@
1
+package com.jt.cloud.framework.common.enums;
2
+
3
+import cn.hutool.core.util.ArrayUtil;
4
+import com.jt.cloud.framework.common.core.ArrayValuable;
5
+import lombok.AllArgsConstructor;
6
+import lombok.Getter;
7
+
8
+import java.util.Arrays;
9
+
10
+/**
11
+ * 时间间隔的枚举
12
+ *
13
+ * @author dhb52
14
+ */
15
+@Getter
16
+@AllArgsConstructor
17
+public enum DateIntervalEnum implements ArrayValuable<Integer> {
18
+
19
+    DAY(1, "天"),
20
+    WEEK(2, "周"),
21
+    MONTH(3, "月"),
22
+    QUARTER(4, "季度"),
23
+    YEAR(5, "年")
24
+    ;
25
+
26
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(DateIntervalEnum::getInterval).toArray(Integer[]::new);
27
+
28
+    /**
29
+     * 类型
30
+     */
31
+    private final Integer interval;
32
+    /**
33
+     * 名称
34
+     */
35
+    private final String name;
36
+
37
+    @Override
38
+    public Integer[] array() {
39
+        return ARRAYS;
40
+    }
41
+
42
+    public static DateIntervalEnum valueOf(Integer interval) {
43
+        return ArrayUtil.firstMatch(item -> item.getInterval().equals(interval), DateIntervalEnum.values());
44
+    }
45
+
46
+}

+ 21 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/DocumentEnum.java

@@ -0,0 +1,21 @@
1
+package com.jt.cloud.framework.common.enums;
2
+
3
+import lombok.AllArgsConstructor;
4
+import lombok.Getter;
5
+
6
+/**
7
+ * 文档地址
8
+ *
9
+ * @author jt
10
+ */
11
+@Getter
12
+@AllArgsConstructor
13
+public enum DocumentEnum {
14
+
15
+    REDIS_INSTALL("https://{ip}:{port}/jt-business-cloud/issues/I4VCSJ", "Redis 安装文档"),
16
+    TENANT("https://doc.iocoder.cn", "SaaS 多租户文档");
17
+
18
+    private final String url;
19
+    private final String memo;
20
+
21
+}

+ 52 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/EffectiveStatusEnum.java

@@ -0,0 +1,52 @@
1
+package com.jt.cloud.framework.common.enums;
2
+
3
+import cn.hutool.core.util.ObjUtil;
4
+import com.jt.cloud.framework.common.core.ArrayValuable;
5
+import lombok.AllArgsConstructor;
6
+import lombok.Getter;
7
+
8
+import java.util.Arrays;
9
+
10
+/**
11
+ * 通用状态枚举
12
+ *
13
+ * @author jt
14
+ */
15
+@Getter
16
+@AllArgsConstructor
17
+public enum EffectiveStatusEnum implements ArrayValuable<Integer> {
18
+
19
+    /**
20
+     * 生效
21
+     */
22
+    EFFECTIVE(0, "生效"),
23
+    /**
24
+     * 失效
25
+     */
26
+    INVALIDATED(1, "失效");
27
+
28
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(EffectiveStatusEnum::getStatus).toArray(Integer[]::new);
29
+
30
+    /**
31
+     * 状态值
32
+     */
33
+    private final Integer status;
34
+    /**
35
+     * 状态名
36
+     */
37
+    private final String name;
38
+
39
+    @Override
40
+    public Integer[] array() {
41
+        return ARRAYS;
42
+    }
43
+
44
+    public static boolean isEnable(Integer status) {
45
+        return ObjUtil.equal(EFFECTIVE.status, status);
46
+    }
47
+
48
+    public static boolean isDisable(Integer status) {
49
+        return ObjUtil.equal(INVALIDATED.status, status);
50
+    }
51
+
52
+}

+ 60 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/MaintenanceTaskTypeEnum.java

@@ -0,0 +1,60 @@
1
+package com.jt.cloud.framework.common.enums;
2
+
3
+import cn.hutool.core.util.ObjUtil;
4
+import com.jt.cloud.framework.common.core.ArrayValuable;
5
+import lombok.AllArgsConstructor;
6
+import lombok.Getter;
7
+
8
+import java.util.Arrays;
9
+
10
+/**
11
+ * 保养任务状态
12
+ *
13
+ * @author jt
14
+ */
15
+@Getter
16
+@AllArgsConstructor
17
+public enum MaintenanceTaskTypeEnum implements ArrayValuable<Integer> {
18
+
19
+    /**
20
+     * 审核中
21
+     */
22
+    NEW_STATUS(0, "新建"),
23
+    /**
24
+     * 已结束
25
+     */
26
+    FINISHED(1, "已结束"),
27
+    /**
28
+     * 已拒绝
29
+     */
30
+    REJECTED(2, "已拒绝");
31
+
32
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(MaintenanceTaskTypeEnum::getStatus).toArray(Integer[]::new);
33
+
34
+    /**
35
+     * 状态值
36
+     */
37
+    private final Integer status;
38
+    /**
39
+     * 状态名
40
+     */
41
+    private final String name;
42
+
43
+    @Override
44
+    public Integer[] array() {
45
+        return ARRAYS;
46
+    }
47
+
48
+    public static boolean isUnderReview(Integer status) {
49
+        return ObjUtil.equal(NEW_STATUS.status, status);
50
+    }
51
+
52
+    public static boolean isFinished(Integer status) {
53
+        return ObjUtil.equal(FINISHED.status, status);
54
+    }
55
+
56
+    public static boolean isRejected(Integer status) {
57
+        return ObjUtil.equal(REJECTED.status, status);
58
+    }
59
+
60
+}

+ 40 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/RpcConstants.java

@@ -0,0 +1,40 @@
1
+package com.jt.cloud.framework.common.enums;
2
+
3
+/**
4
+ * RPC 相关的枚举
5
+ *
6
+ * 虽然放在 jt-spring-boot-starter-rpc 会相对合适,但是每个 API 模块需要使用到,所以暂时只好放在此处
7
+ *
8
+ * @author jt
9
+ */
10
+public interface RpcConstants {
11
+
12
+    /**
13
+     * RPC API 的前缀
14
+     */
15
+    String RPC_API_PREFIX = "/rpc-api";
16
+
17
+    /**
18
+     * system 服务名
19
+     *
20
+     * 注意,需要保证和 spring.application.name 保持一致
21
+     */
22
+    String SYSTEM_NAME = "system-server";
23
+
24
+    /**
25
+     * system 服务的前缀
26
+     */
27
+    String SYSTEM_PREFIX = RPC_API_PREFIX + "/system";
28
+
29
+    /**
30
+     * infra 服务名
31
+     *
32
+     * 注意,需要保证和 spring.application.name 保持一致
33
+     */
34
+    String INFRA_NAME = "infra-server";
35
+    /**
36
+     * infra 服务的前缀
37
+     */
38
+    String INFRA_PREFIX = RPC_API_PREFIX + "/infra";
39
+
40
+}

+ 40 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/TerminalEnum.java

@@ -0,0 +1,40 @@
1
+package com.jt.cloud.framework.common.enums;
2
+
3
+import com.jt.cloud.framework.common.core.ArrayValuable;
4
+import lombok.Getter;
5
+import lombok.RequiredArgsConstructor;
6
+
7
+import java.util.Arrays;
8
+
9
+/**
10
+ * 终端的枚举
11
+ *
12
+ * @author jt
13
+ */
14
+@RequiredArgsConstructor
15
+@Getter
16
+public enum TerminalEnum implements ArrayValuable<Integer> {
17
+
18
+    UNKNOWN(0, "未知"), // 目的:在无法解析到 terminal 时,使用它
19
+    WECHAT_MINI_PROGRAM(10, "微信小程序"),
20
+    WECHAT_WAP(11, "微信公众号"),
21
+    H5(20, "H5 网页"),
22
+    APP(31, "手机 App"),
23
+    ;
24
+
25
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(TerminalEnum::getTerminal).toArray(Integer[]::new);
26
+
27
+    /**
28
+     * 终端
29
+     */
30
+    private final Integer terminal;
31
+    /**
32
+     * 终端名
33
+     */
34
+    private final String name;
35
+
36
+    @Override
37
+    public Integer[] array() {
38
+        return ARRAYS;
39
+    }
40
+}

+ 39 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/UserTypeEnum.java

@@ -0,0 +1,39 @@
1
+package com.jt.cloud.framework.common.enums;
2
+
3
+import cn.hutool.core.util.ArrayUtil;
4
+import com.jt.cloud.framework.common.core.ArrayValuable;
5
+import lombok.AllArgsConstructor;
6
+import lombok.Getter;
7
+
8
+import java.util.Arrays;
9
+
10
+/**
11
+ * 全局用户类型枚举
12
+ */
13
+@AllArgsConstructor
14
+@Getter
15
+public enum UserTypeEnum implements ArrayValuable<Integer> {
16
+
17
+    MEMBER(1, "会员"), // 面向 c 端,普通用户
18
+    ADMIN(2, "管理员"); // 面向 b 端,管理后台
19
+
20
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(UserTypeEnum::getValue).toArray(Integer[]::new);
21
+
22
+    /**
23
+     * 类型
24
+     */
25
+    private final Integer value;
26
+    /**
27
+     * 类型名
28
+     */
29
+    private final String name;
30
+
31
+    public static UserTypeEnum valueOf(Integer value) {
32
+        return ArrayUtil.firstMatch(userType -> userType.getValue().equals(value), UserTypeEnum.values());
33
+    }
34
+
35
+    @Override
36
+    public Integer[] array() {
37
+        return ARRAYS;
38
+    }
39
+}

+ 36 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/enums/WebFilterOrderEnum.java

@@ -0,0 +1,36 @@
1
+package com.jt.cloud.framework.common.enums;
2
+
3
+/**
4
+ * Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
5
+ *
6
+ *  考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 enum 包下
7
+ *
8
+ * @author jt
9
+ */
10
+public interface WebFilterOrderEnum {
11
+
12
+    int CORS_FILTER = Integer.MIN_VALUE;
13
+
14
+    int TRACE_FILTER = CORS_FILTER + 1;
15
+
16
+    int ENV_TAG_FILTER = TRACE_FILTER + 1;
17
+
18
+    int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500;
19
+
20
+    // OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等
21
+
22
+    int TENANT_CONTEXT_FILTER = - 104; // 需要保证在 ApiAccessLogFilter 前面
23
+
24
+    int API_ACCESS_LOG_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面
25
+
26
+    int XSS_FILTER = -102;  // 需要保证在 RequestBodyCacheFilter 后面
27
+
28
+    // Spring Security Filter 默认为 -100,可见 org.springframework.boot.autoconfigure.security.SecurityProperties 配置属性类
29
+
30
+    int TENANT_SECURITY_FILTER = -99; // 需要保证在 Spring Security 过滤器后面
31
+
32
+    int FLOWABLE_FILTER = -98; // 需要保证在 Spring Security 过滤后面
33
+
34
+    int DEMO_FILTER = Integer.MAX_VALUE;
35
+
36
+}

+ 32 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/exception/ErrorCode.java

@@ -0,0 +1,32 @@
1
+package com.jt.cloud.framework.common.exception;
2
+
3
+import com.jt.cloud.framework.common.exception.enums.GlobalErrorCodeConstants;
4
+import com.jt.cloud.framework.common.exception.enums.ServiceErrorCodeRange;
5
+import lombok.Data;
6
+
7
+/**
8
+ * 错误码对象
9
+ *
10
+ * 全局错误码,占用 [0, 999], 参见 {@link GlobalErrorCodeConstants}
11
+ * 业务异常错误码,占用 [1 000 000 000, +∞),参见 {@link ServiceErrorCodeRange}
12
+ *
13
+ * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备
14
+ */
15
+@Data
16
+public class ErrorCode {
17
+
18
+    /**
19
+     * 错误码
20
+     */
21
+    private final Integer code;
22
+    /**
23
+     * 错误提示
24
+     */
25
+    private final String msg;
26
+
27
+    public ErrorCode(Integer code, String message) {
28
+        this.code = code;
29
+        this.msg = message;
30
+    }
31
+
32
+}

+ 60 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/exception/ServerException.java

@@ -0,0 +1,60 @@
1
+package com.jt.cloud.framework.common.exception;
2
+
3
+import com.jt.cloud.framework.common.exception.enums.GlobalErrorCodeConstants;
4
+import lombok.Data;
5
+import lombok.EqualsAndHashCode;
6
+
7
+/**
8
+ * 服务器异常 Exception
9
+ */
10
+@Data
11
+@EqualsAndHashCode(callSuper = true)
12
+public final class ServerException extends RuntimeException {
13
+
14
+    /**
15
+     * 全局错误码
16
+     *
17
+     * @see GlobalErrorCodeConstants
18
+     */
19
+    private Integer code;
20
+    /**
21
+     * 错误提示
22
+     */
23
+    private String message;
24
+
25
+    /**
26
+     * 空构造方法,避免反序列化问题
27
+     */
28
+    public ServerException() {
29
+    }
30
+
31
+    public ServerException(ErrorCode errorCode) {
32
+        this.code = errorCode.getCode();
33
+        this.message = errorCode.getMsg();
34
+    }
35
+
36
+    public ServerException(Integer code, String message) {
37
+        this.code = code;
38
+        this.message = message;
39
+    }
40
+
41
+    public Integer getCode() {
42
+        return code;
43
+    }
44
+
45
+    public ServerException setCode(Integer code) {
46
+        this.code = code;
47
+        return this;
48
+    }
49
+
50
+    @Override
51
+    public String getMessage() {
52
+        return message;
53
+    }
54
+
55
+    public ServerException setMessage(String message) {
56
+        this.message = message;
57
+        return this;
58
+    }
59
+
60
+}

+ 60 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/exception/ServiceException.java

@@ -0,0 +1,60 @@
1
+package com.jt.cloud.framework.common.exception;
2
+
3
+import com.jt.cloud.framework.common.exception.enums.ServiceErrorCodeRange;
4
+import lombok.Data;
5
+import lombok.EqualsAndHashCode;
6
+
7
+/**
8
+ * 业务逻辑异常 Exception
9
+ */
10
+@Data
11
+@EqualsAndHashCode(callSuper = true)
12
+public final class ServiceException extends RuntimeException {
13
+
14
+    /**
15
+     * 业务错误码
16
+     *
17
+     * @see ServiceErrorCodeRange
18
+     */
19
+    private Integer code;
20
+    /**
21
+     * 错误提示
22
+     */
23
+    private String message;
24
+
25
+    /**
26
+     * 空构造方法,避免反序列化问题
27
+     */
28
+    public ServiceException() {
29
+    }
30
+
31
+    public ServiceException(ErrorCode errorCode) {
32
+        this.code = errorCode.getCode();
33
+        this.message = errorCode.getMsg();
34
+    }
35
+
36
+    public ServiceException(Integer code, String message) {
37
+        this.code = code;
38
+        this.message = message;
39
+    }
40
+
41
+    public Integer getCode() {
42
+        return code;
43
+    }
44
+
45
+    public ServiceException setCode(Integer code) {
46
+        this.code = code;
47
+        return this;
48
+    }
49
+
50
+    @Override
51
+    public String getMessage() {
52
+        return message;
53
+    }
54
+
55
+    public ServiceException setMessage(String message) {
56
+        this.message = message;
57
+        return this;
58
+    }
59
+
60
+}

+ 43 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/exception/enums/GlobalErrorCodeConstants.java

@@ -0,0 +1,43 @@
1
+package com.jt.cloud.framework.common.exception.enums;
2
+
3
+import com.jt.cloud.framework.common.exception.ErrorCode;
4
+
5
+/**
6
+ * 全局错误码枚举
7
+ * 0-999 系统异常编码保留
8
+ *
9
+ * 一般情况下,使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
10
+ * 虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的
11
+ * 比较特殊的是,因为之前一直使用 0 作为成功,就不使用 200 啦。
12
+ *
13
+ * @author jt
14
+ */
15
+public interface GlobalErrorCodeConstants {
16
+
17
+    ErrorCode SUCCESS = new ErrorCode(0, "成功");
18
+
19
+    // ========== 客户端错误段 ==========
20
+
21
+    ErrorCode BAD_REQUEST = new ErrorCode(400, "请求参数不正确");
22
+    ErrorCode UNAUTHORIZED = new ErrorCode(401, "账号未登录");
23
+    ErrorCode FORBIDDEN = new ErrorCode(403, "没有该操作权限");
24
+    ErrorCode NOT_FOUND = new ErrorCode(404, "请求未找到");
25
+    ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确");
26
+    ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许
27
+    ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试");
28
+
29
+    // ========== 服务端错误段 ==========
30
+
31
+    ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常");
32
+    ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启");
33
+    ErrorCode ERROR_CONFIGURATION = new ErrorCode(502, "错误的配置项");
34
+
35
+    // ========== 自定义错误段 ==========
36
+    ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求
37
+    ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作");
38
+
39
+    ErrorCode SCOPE_FORBIDDEN = new ErrorCode(10005, "飞书账号未授权!");
40
+
41
+    ErrorCode UNKNOWN = new ErrorCode(999, "未知错误");
42
+
43
+}

+ 48 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/exception/enums/ServiceErrorCodeRange.java

@@ -0,0 +1,48 @@
1
+package com.jt.cloud.framework.common.exception.enums;
2
+
3
+/**
4
+ * 业务异常的错误码区间,解决:解决各模块错误码定义,避免重复,在此只声明不做实际使用
5
+ *
6
+ * 一共 10 位,分成四段
7
+ *
8
+ * 第一段,1 位,类型
9
+ *      1 - 业务级别异常
10
+ *      x - 预留
11
+ * 第二段,3 位,系统类型
12
+ *      001 - 用户系统
13
+ *      002 - 商品系统
14
+ *      003 - 订单系统
15
+ *      004 - 支付系统
16
+ *      005 - 优惠劵系统
17
+ *      ... - ...
18
+ * 第三段,3 位,模块
19
+ *      不限制规则。
20
+ *      一般建议,每个系统里面,可能有多个模块,可以再去做分段。以用户系统为例子:
21
+ *          001 - OAuth2 模块
22
+ *          002 - User 模块
23
+ *          003 - MobileCode 模块
24
+ * 第四段,3 位,错误码
25
+ *       不限制规则。
26
+ *       一般建议,每个模块自增。
27
+ *
28
+ * @author jt
29
+ */
30
+public class ServiceErrorCodeRange {
31
+
32
+    // 模块 infra 错误码区间 [1-001-000-000 ~ 1-002-000-000)
33
+    // 模块 system 错误码区间 [1-002-000-000 ~ 1-003-000-000)
34
+    // 模块 report 错误码区间 [1-003-000-000 ~ 1-004-000-000)
35
+    // 模块 member 错误码区间 [1-004-000-000 ~ 1-005-000-000)
36
+    // 模块 mp 错误码区间 [1-006-000-000 ~ 1-007-000-000)
37
+    // 模块 pay 错误码区间 [1-007-000-000 ~ 1-008-000-000)
38
+    // 模块 bpm 错误码区间 [1-009-000-000 ~ 1-010-000-000)
39
+
40
+    // 模块 product 错误码区间 [1-008-000-000 ~ 1-009-000-000)
41
+    // 模块 trade 错误码区间 [1-011-000-000 ~ 1-012-000-000)
42
+    // 模块 promotion 错误码区间 [1-013-000-000 ~ 1-014-000-000)
43
+
44
+    // 模块 crm 错误码区间 [1-020-000-000 ~ 1-021-000-000)
45
+
46
+    // 模块 ai 错误码区间 [1-022-000-000 ~ 1-023-000-000)
47
+
48
+}

+ 77 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/exception/util/ServiceExceptionUtil.java

@@ -0,0 +1,77 @@
1
+package com.jt.cloud.framework.common.exception.util;
2
+
3
+import com.jt.cloud.framework.common.exception.ErrorCode;
4
+import com.jt.cloud.framework.common.exception.ServiceException;
5
+import com.jt.cloud.framework.common.exception.enums.GlobalErrorCodeConstants;
6
+import com.google.common.annotations.VisibleForTesting;
7
+import lombok.extern.slf4j.Slf4j;
8
+
9
+/**
10
+ * {@link ServiceException} 工具类
11
+ *
12
+ * 目的在于,格式化异常信息提示。
13
+ * 考虑到 String.format 在参数不正确时会报错,因此使用 {} 作为占位符,并使用 {@link #doFormat(int, String, Object...)} 方法来格式化
14
+ *
15
+ */
16
+@Slf4j
17
+public class ServiceExceptionUtil {
18
+
19
+    // ========== 和 ServiceException 的集成 ==========
20
+
21
+    public static ServiceException exception(ErrorCode errorCode) {
22
+        return exception0(errorCode.getCode(), errorCode.getMsg());
23
+    }
24
+
25
+    public static ServiceException exception(ErrorCode errorCode, Object... params) {
26
+        return exception0(errorCode.getCode(), errorCode.getMsg(), params);
27
+    }
28
+
29
+    public static ServiceException exception0(Integer code, String messagePattern, Object... params) {
30
+        String message = doFormat(code, messagePattern, params);
31
+        return new ServiceException(code, message);
32
+    }
33
+
34
+    public static ServiceException invalidParamException(String messagePattern, Object... params) {
35
+        return exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), messagePattern, params);
36
+    }
37
+
38
+    // ========== 格式化方法 ==========
39
+
40
+    /**
41
+     * 将错误编号对应的消息使用 params 进行格式化。
42
+     *
43
+     * @param code           错误编号
44
+     * @param messagePattern 消息模版
45
+     * @param params         参数
46
+     * @return 格式化后的提示
47
+     */
48
+    @VisibleForTesting
49
+    public static String doFormat(int code, String messagePattern, Object... params) {
50
+        StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
51
+        int i = 0;
52
+        int j;
53
+        int l;
54
+        for (l = 0; l < params.length; l++) {
55
+            j = messagePattern.indexOf("{}", i);
56
+            if (j == -1) {
57
+                log.error("[doFormat][参数过多:错误码({})|错误内容({})|参数({})", code, messagePattern, params);
58
+                if (i == 0) {
59
+                    return messagePattern;
60
+                } else {
61
+                    sbuf.append(messagePattern.substring(i));
62
+                    return sbuf.toString();
63
+                }
64
+            } else {
65
+                sbuf.append(messagePattern, i, j);
66
+                sbuf.append(params[l]);
67
+                i = j + 2;
68
+            }
69
+        }
70
+        if (messagePattern.indexOf("{}", i) != -1) {
71
+            log.error("[doFormat][参数过少:错误码({})|错误内容({})|参数({})", code, messagePattern, params);
72
+        }
73
+        sbuf.append(messagePattern.substring(i));
74
+        return sbuf.toString();
75
+    }
76
+
77
+}

+ 6 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/package-info.java

@@ -0,0 +1,6 @@
1
+/**
2
+ * 基础的通用类,和框架无关
3
+ *
4
+ * 例如说,CommonResult 为通用返回
5
+ */
6
+package com.jt.cloud.framework.common;

+ 128 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/pojo/CommonResult.java

@@ -0,0 +1,128 @@
1
+package com.jt.cloud.framework.common.pojo;
2
+
3
+import cn.hutool.core.lang.Assert;
4
+import com.jt.cloud.framework.common.exception.ErrorCode;
5
+import com.jt.cloud.framework.common.exception.ServiceException;
6
+import com.jt.cloud.framework.common.exception.enums.GlobalErrorCodeConstants;
7
+import com.jt.cloud.framework.common.exception.util.ServiceExceptionUtil;
8
+import com.fasterxml.jackson.annotation.JsonIgnore;
9
+import lombok.Data;
10
+
11
+import java.io.Serializable;
12
+import java.util.Objects;
13
+
14
+/**
15
+ * 通用返回
16
+ *
17
+ * @param <T> 数据泛型
18
+ */
19
+@Data
20
+public class CommonResult<T> implements Serializable {
21
+
22
+    /**
23
+     * 错误码
24
+     *
25
+     * @see ErrorCode#getCode()
26
+     */
27
+    private Integer code;
28
+    /**
29
+     * 返回数据
30
+     */
31
+    private T data;
32
+    /**
33
+     * 错误提示,用户可阅读
34
+     *
35
+     * @see ErrorCode#getMsg() ()
36
+     */
37
+    private String msg;
38
+
39
+    /**
40
+     * 将传入的 result 对象,转换成另外一个泛型结果的对象
41
+     *
42
+     * 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
43
+     *
44
+     * @param result 传入的 result 对象
45
+     * @param <T> 返回的泛型
46
+     * @return 新的 CommonResult 对象
47
+     */
48
+    public static <T> CommonResult<T> error(CommonResult<?> result) {
49
+        return error(result.getCode(), result.getMsg());
50
+    }
51
+
52
+    public static <T> CommonResult<T> error(Integer code, String message) {
53
+        Assert.notEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), code, "code 必须是错误的!");
54
+        CommonResult<T> result = new CommonResult<>();
55
+        result.code = code;
56
+        result.msg = message;
57
+        return result;
58
+    }
59
+
60
+    public static <T> CommonResult<T> error(ErrorCode errorCode, Object... params) {
61
+        Assert.notEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), errorCode.getCode(), "code 必须是错误的!");
62
+        CommonResult<T> result = new CommonResult<>();
63
+        result.code = errorCode.getCode();
64
+        result.msg = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMsg(), params);
65
+        return result;
66
+    }
67
+
68
+    public static <T> CommonResult<T> error(ErrorCode errorCode) {
69
+        return error(errorCode.getCode(), errorCode.getMsg());
70
+    }
71
+
72
+    public static <T> CommonResult<T> success(T data) {
73
+        CommonResult<T> result = new CommonResult<>();
74
+        result.code = GlobalErrorCodeConstants.SUCCESS.getCode();
75
+        result.data = data;
76
+        result.msg = "";
77
+        return result;
78
+    }
79
+
80
+    public static <T> CommonResult<T> success(String message) {
81
+        CommonResult<T> result = new CommonResult<>();
82
+        result.code = GlobalErrorCodeConstants.SUCCESS.getCode();
83
+        result.msg = message;
84
+        return result;
85
+    }
86
+
87
+    public static boolean isSuccess(Integer code) {
88
+        return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode());
89
+    }
90
+
91
+    @JsonIgnore // 避免 jackson 序列化
92
+    public boolean isSuccess() {
93
+        return isSuccess(code);
94
+    }
95
+
96
+    @JsonIgnore // 避免 jackson 序列化
97
+    public boolean isError() {
98
+        return !isSuccess();
99
+    }
100
+
101
+    // ========= 和 Exception 异常体系集成 =========
102
+
103
+    /**
104
+     * 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常
105
+     */
106
+    public void checkError() throws ServiceException {
107
+        if (isSuccess()) {
108
+            return;
109
+        }
110
+        // 业务异常
111
+        throw new ServiceException(code, msg);
112
+    }
113
+
114
+    /**
115
+     * 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常
116
+     * 如果没有,则返回 {@link #data} 数据
117
+     */
118
+    @JsonIgnore // 避免 jackson 序列化
119
+    public T getCheckedData() {
120
+        checkError();
121
+        return data;
122
+    }
123
+
124
+    public static <T> CommonResult<T> error(ServiceException serviceException) {
125
+        return error(serviceException.getCode(), serviceException.getMessage());
126
+    }
127
+
128
+}

+ 36 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/pojo/PageParam.java

@@ -0,0 +1,36 @@
1
+package com.jt.cloud.framework.common.pojo;
2
+
3
+import io.swagger.v3.oas.annotations.media.Schema;
4
+import lombok.Data;
5
+
6
+import jakarta.validation.constraints.Min;
7
+import jakarta.validation.constraints.Max;
8
+import jakarta.validation.constraints.NotNull;
9
+import java.io.Serializable;
10
+
11
+@Schema(description="分页参数")
12
+@Data
13
+public class PageParam implements Serializable {
14
+
15
+    private static final Integer PAGE_NO = 1;
16
+    private static final Integer PAGE_SIZE = 10;
17
+
18
+    /**
19
+     * 每页条数 - 不分页
20
+     *
21
+     * 例如说,导出接口,可以设置 {@link #pageSize} 为 -1 不分页,查询所有数据。
22
+     */
23
+    public static final Integer PAGE_SIZE_NONE = -1;
24
+
25
+    @Schema(description = "页码,从 1 开始", requiredMode = Schema.RequiredMode.REQUIRED,example = "1")
26
+    @NotNull(message = "页码不能为空")
27
+    @Min(value = 1, message = "页码最小值为 1")
28
+    private Integer pageNo = PAGE_NO;
29
+
30
+    @Schema(description = "每页条数,最大值为 10000", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
31
+    @NotNull(message = "每页条数不能为空")
32
+    @Min(value = 1, message = "每页条数最小值为 1")
33
+    @Max(value = 10000, message = "每页条数最大值为 10000")
34
+    private Integer pageSize = PAGE_SIZE;
35
+
36
+}

+ 41 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/pojo/PageResult.java

@@ -0,0 +1,41 @@
1
+package com.jt.cloud.framework.common.pojo;
2
+
3
+import io.swagger.v3.oas.annotations.media.Schema;
4
+import lombok.Data;
5
+
6
+import java.io.Serializable;
7
+import java.util.ArrayList;
8
+import java.util.List;
9
+
10
+@Schema(description = "分页结果")
11
+@Data
12
+public final class PageResult<T> implements Serializable {
13
+
14
+    @Schema(description = "数据", requiredMode = Schema.RequiredMode.REQUIRED)
15
+    private List<T> list;
16
+
17
+    @Schema(description = "总量", requiredMode = Schema.RequiredMode.REQUIRED)
18
+    private Long total;
19
+
20
+    public PageResult() {
21
+    }
22
+
23
+    public PageResult(List<T> list, Long total) {
24
+        this.list = list;
25
+        this.total = total;
26
+    }
27
+
28
+    public PageResult(Long total) {
29
+        this.list = new ArrayList<>();
30
+        this.total = total;
31
+    }
32
+
33
+    public static <T> PageResult<T> empty() {
34
+        return new PageResult<>(0L);
35
+    }
36
+
37
+    public static <T> PageResult<T> empty(Long total) {
38
+        return new PageResult<>(total);
39
+    }
40
+
41
+}

+ 19 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/pojo/SortablePageParam.java

@@ -0,0 +1,19 @@
1
+package com.jt.cloud.framework.common.pojo;
2
+
3
+import io.swagger.v3.oas.annotations.media.Schema;
4
+import lombok.Data;
5
+import lombok.EqualsAndHashCode;
6
+import lombok.ToString;
7
+
8
+import java.util.List;
9
+
10
+@Schema(description = "可排序的分页参数")
11
+@Data
12
+@EqualsAndHashCode(callSuper = true)
13
+@ToString(callSuper = true)
14
+public class SortablePageParam extends PageParam {
15
+
16
+    @Schema(description = "排序字段")
17
+    private List<SortingField> sortingFields;
18
+
19
+}

+ 37 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/pojo/SortingField.java

@@ -0,0 +1,37 @@
1
+package com.jt.cloud.framework.common.pojo;
2
+
3
+import lombok.AllArgsConstructor;
4
+import lombok.Data;
5
+import lombok.NoArgsConstructor;
6
+
7
+import java.io.Serializable;
8
+
9
+/**
10
+ * 排序字段 DTO
11
+ *
12
+ * 类名加了 ing 的原因是,避免和 ES SortField 重名。
13
+ */
14
+@Data
15
+@NoArgsConstructor
16
+@AllArgsConstructor
17
+public class SortingField implements Serializable {
18
+
19
+    /**
20
+     * 顺序 - 升序
21
+     */
22
+    public static final String ORDER_ASC = "asc";
23
+    /**
24
+     * 顺序 - 降序
25
+     */
26
+    public static final String ORDER_DESC = "desc";
27
+
28
+    /**
29
+     * 字段
30
+     */
31
+    private String field;
32
+    /**
33
+     * 顺序
34
+     */
35
+    private String order;
36
+
37
+}

+ 187 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/AesUtil.java

@@ -0,0 +1,187 @@
1
+package com.jt.cloud.framework.common.util;
2
+
3
+import cn.hutool.core.util.CharsetUtil;
4
+import cn.hutool.core.util.StrUtil;
5
+import cn.hutool.crypto.Mode;
6
+import cn.hutool.crypto.Padding;
7
+import cn.hutool.crypto.SecureUtil;
8
+import cn.hutool.crypto.symmetric.AES;
9
+import lombok.extern.slf4j.Slf4j;
10
+
11
+@Slf4j
12
+public class AesUtil {
13
+
14
+
15
+    /**
16
+     *
17
+     * 16位密钥
18
+     */
19
+    private static final String DEFAULT_KEY = "wbKR8392#$%;0GLl";
20
+
21
+    private static final String DEFAULT_IV = "JL@kl328549hdkl#";
22
+
23
+    // AES实例
24
+    private static AES aesInstance;
25
+
26
+    static {
27
+        // 初始化AES实例
28
+        // 使用CBC模式,PKCS5Padding填充
29
+        aesInstance = new AES(Mode.CBC, Padding.PKCS5Padding,
30
+                DEFAULT_KEY.getBytes(), // 密钥(16字节)
31
+                DEFAULT_IV.getBytes()); // IV(初始化向量,16字节)
32
+    }
33
+
34
+    /**
35
+     * 私有构造函数,防止实例化
36
+     * 添加@UtilityClass注解,此处多余动作
37
+     */
38
+//    private HutoolAesUtil() {
39
+//    }
40
+
41
+    /**
42
+     * 加密文本
43
+     *
44
+     * @param text 待加密的文本
45
+     * @return 加密后的十六进制字符串
46
+     */
47
+    public static String encrypt(String text) {
48
+        if (StrUtil.isBlank(text)) {
49
+            return text;
50
+        }
51
+
52
+        try {
53
+            // 加密并返回十六进制字符串
54
+            return aesInstance.encryptHex(text);
55
+        } catch (Exception e) {
56
+            throw new RuntimeException("AES加密失败", e);
57
+        }
58
+    }
59
+
60
+    /**
61
+     * 解密文本
62
+     *
63
+     * @param encryptedText 待解密的十六进制字符串
64
+     * @return 解密后的原始文本
65
+     */
66
+    public static String decrypt(String encryptedText) {
67
+        if (StrUtil.isBlank(encryptedText)) {
68
+            return encryptedText;
69
+        }
70
+        try {
71
+            // 从十六进制字符串解密
72
+            return aesInstance.decryptStr(encryptedText, CharsetUtil.CHARSET_UTF_8);
73
+        } catch (Exception e) {
74
+            log.error("AES解密失败", e.getMessage());
75
+            return "";
76
+        }
77
+    }
78
+
79
+    /**
80
+     * 使用自定义密钥加密文本
81
+     *
82
+     * @param text 待加密的文本
83
+     * @param key  自定义密钥(16/24/32位)
84
+     * @return 加密后的十六进制字符串
85
+     */
86
+    public static String encryptWithKey(String text, String key) {
87
+        if (StrUtil.isBlank(text)) {
88
+            return text;
89
+        }
90
+
91
+        try {
92
+            // 使用自定义密钥创建AES实例
93
+            AES customAes = new AES(Mode.CBC, Padding.PKCS5Padding,
94
+                    key.getBytes(CharsetUtil.CHARSET_UTF_8),
95
+                    "0000000000000000".getBytes(CharsetUtil.CHARSET_UTF_8));
96
+            return customAes.encryptHex(text);
97
+        } catch (Exception e) {
98
+            throw new RuntimeException("AES加密失败", e);
99
+        }
100
+    }
101
+
102
+    /**
103
+     * 使用自定义密钥解密文本
104
+     *
105
+     * @param encryptedText 待解密的十六进制字符串
106
+     * @param key           自定义密钥(16/24/32位)
107
+     * @return 解密后的原始文本
108
+     */
109
+    public static String decryptWithKey(String encryptedText, String key) {
110
+        if (StrUtil.isBlank(encryptedText)) {
111
+            return encryptedText;
112
+        }
113
+
114
+        try {
115
+            // 使用自定义密钥创建AES实例
116
+            AES customAes = new AES(Mode.CBC, Padding.PKCS5Padding,
117
+                    key.getBytes(CharsetUtil.CHARSET_UTF_8),
118
+                    "0000000000000000".getBytes(CharsetUtil.CHARSET_UTF_8));
119
+            return customAes.decryptStr(encryptedText, CharsetUtil.CHARSET_UTF_8);
120
+        } catch (Exception e) {
121
+            throw new RuntimeException("AES解密失败", e);
122
+        }
123
+    }
124
+
125
+    /**
126
+     * 判断文本是否为加密内容
127
+     *
128
+     * @param text 待判断的文本
129
+     * @return 是否为加密内容
130
+     */
131
+    public static boolean isEncrypted(String text) {
132
+        if (StrUtil.isBlank(text)) {
133
+            return false;
134
+        }
135
+
136
+        try {
137
+            // 尝试解密,如果成功则认为是加密内容
138
+            String decrypted = decrypt(text);
139
+            // 如果解密后的内容与原内容不同,且不是空字符串,则认为是加密内容
140
+            return !text.equals(decrypted) && StrUtil.isNotBlank(decrypted);
141
+        } catch (Exception e) {
142
+            return false;
143
+        }
144
+    }
145
+
146
+    /**
147
+     * 安全解密方法,如果解密失败返回原值
148
+     *
149
+     * @param text 待解密的文本
150
+     * @return 解密后的文本或原文本
151
+     */
152
+    public static String safeDecrypt(String text) {
153
+        if (StrUtil.isBlank(text)) {
154
+            return text;
155
+        }
156
+
157
+        try {
158
+            // 尝试解密
159
+            String decrypted = decrypt(text);
160
+            // 如果解密后的内容与原内容不同,返回解密后的内容
161
+            if (!text.equals(decrypted)) {
162
+                return decrypted;
163
+            }
164
+            return text;
165
+        } catch (Exception e) {
166
+            // 解密失败,返回原值
167
+            return text;
168
+        }
169
+    }
170
+
171
+    /**
172
+     * 生成随机密钥
173
+     *
174
+     * @return 16位随机密钥
175
+     */
176
+    public static String generateKey() {
177
+        return SecureUtil.generateKey("AES").getEncoded().toString();
178
+    }
179
+
180
+    public static void main(String[] args) {
181
+        String encrypt = AesUtil.encrypt("120102200007289604");
182
+        System.out.println("加密:" + encrypt);
183
+        String decrypt = AesUtil.decrypt(encrypt);
184
+        System.out.println("解密:" + decrypt);
185
+    }
186
+
187
+}

+ 90 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/DateStandardizer.java

@@ -0,0 +1,90 @@
1
+package com.jt.cloud.framework.common.util;
2
+
3
+import java.time.LocalDate;
4
+import java.time.format.DateTimeFormatter;
5
+import java.time.format.DateTimeParseException;
6
+import java.util.Arrays;
7
+import java.util.List;
8
+
9
+public class DateStandardizer {
10
+
11
+    // 定义所有可能的输入格式
12
+    private static final List<DateTimeFormatter> INPUT_FORMATTERS = Arrays.asList(
13
+            DateTimeFormatter.ofPattern("yyyy/MM/dd"),  // 2024/10/21
14
+            DateTimeFormatter.ofPattern("yyyy/MM/d"),   // 2024/3/1
15
+            DateTimeFormatter.ofPattern("yyyy/M/d"),    // 2024/3/1
16
+            DateTimeFormatter.ofPattern("yyyy/M/dd"),   // 2024/3/11
17
+            DateTimeFormatter.ofPattern("yyyy.MM.dd"),  // 2023.10.17
18
+            DateTimeFormatter.ofPattern("yyyy.M.d"),    // 2023.5.22
19
+            DateTimeFormatter.ofPattern("yyyy.MM.d"),   // 2023.10.5
20
+            DateTimeFormatter.ofPattern("yyyy.M.dd"),   // 2023.5.15
21
+            DateTimeFormatter.ofPattern("yyyy/MM/yy"),  // 防止意外(可选)
22
+            DateTimeFormatter.ofPattern("yyyy.MM.yy")   // 防止意外(可选)
23
+    );
24
+
25
+    // 输出格式
26
+    private static final DateTimeFormatter OUTPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
27
+
28
+    /**
29
+     * 将任意常见日期格式转换为 yyyy-MM-dd
30
+     * @param dateStr 输入的日期字符串
31
+     * @return 标准化后的字符串,解析失败返回 null
32
+     */
33
+    public static String convertToStandardDate(String dateStr) {
34
+        if (dateStr == null || dateStr.trim().isEmpty()) {
35
+            return null;
36
+        }
37
+        String trimmed = dateStr.trim();
38
+
39
+        for (DateTimeFormatter formatter : INPUT_FORMATTERS) {
40
+            try {
41
+                LocalDate localDate = LocalDate.parse(trimmed, formatter);
42
+                return localDate.format(OUTPUT_FORMATTER);
43
+            } catch (DateTimeParseException e) {
44
+                // 继续尝试下一个格式
45
+            }
46
+        }
47
+
48
+        // 所有格式都失败
49
+        System.err.println("无法解析日期: " + trimmed);
50
+        return null;
51
+    }
52
+
53
+    // 测试主函数
54
+    public static void main(String[] args) {
55
+        List<String> dates = Arrays.asList(
56
+                "2024/3/1", "2025/5/29", "2024.5.18", "2025.7.22", "2022/7/1", "2023/2/1",
57
+                "2023/4/1", "2025.6.1", "2025/6/28", "2025/7/1", "2025/6/29", "2023.6.19",
58
+                "1905/7/13", "2023.09.15", "2023.10.16", "2024.5.27", "2024.7.1", "2024.7.17",
59
+                "2023.10.12", "2023.11.11", "2024/9/2", "2024/11/1", "2025/3/6", "2025/7/18",
60
+                "2025/7/10", "2025/8/1", "2025/8/4", "2025/7/28", "2019/1/1", "2022/1/1",
61
+                "2024/9/9", "2024/9/1", "2023.10.07", "2024/10/8", "2024/10/21", "2023/4/17",
62
+                "2025/5/6", "2024/2/20", "2025/5/19", "2025/8/13", "2025/2/18", "2025/3/3",
63
+                "2025/4/10", "2024/6/3", "2024/12/2", "2025/2/10", "2021.10.7", "2023.10.04",
64
+                "2021.10.01", "2023.04.10", "2023.10.17", "2022.06.23", "2025/7/7", "2025/4/29",
65
+                "2025/4/1", "2025/4/25", "2025/4/20", "2025/8/8", "2025/8/9", "2024.8.1",
66
+                "2025/5/16", "2024.8.20", "2023.12.20", "2021.01", "2023.05.01", "2023.7.21",
67
+                "2024.03.11", "2023.6.20", "2025.07.01", "2025.5.8", "2025.2.21", "2025.8.19",
68
+                "2025.6.1", "2025.8.9", "2025.8.26", "2025.8.3", "2025.6.30", "2024/3/22",
69
+                "2025/6/17", "2025/5/29", "2025/6/7", "2024.2.23", "2024.3.1", "2024.3.9",
70
+                "2024.5.1", "2024.5.11", "2024.5.15", "2024.9.12", "2024/9/24", "2025/1/14",
71
+                "2025/2/9", "2025/2/11", "2025/2/24", "2024/7/9", "2024/8/14", "2025/2/26",
72
+                "2025/3/5", "2025/6/4", "2025/7/3", "2025/7/21", "2025/8/17", "2025.8.1",
73
+                "2025/7/14", "2023/10/10", "2024/08/11", "2025/7/26", "2025/7/27", "2025/7/31",
74
+                "2025/8/2", "2025/8/11", "2023.5.22", "2022.07.02", "2023.03.29", "2023.8.17",
75
+                "2023.8.9", "2023.10.15", "2023.10.2", "2023.1.22", "2024.2.18", "2024.2.29",
76
+                "2024.2.1", "2023.9.14", "2024.9.29", "2024.06.01", "2024.9.26", "2024.11.02",
77
+                "2024.6.30", "2024.10.2", "2024.7.14", "2024.7.20", "2024.7.6", "2025.2.21",
78
+                "2025.3.11", "2024.8.21", "2025.2.6", "2024.10.31", "2024.11.01", "2024.11.10",
79
+                "2025.2.5", "2025.1.11", "2025.3.15", "2025.7.17", "2025.8.19", "2025.8.10"
80
+        );
81
+
82
+        System.out.printf("%-15s -> %-12s%n", "原始输入", "标准化输出");
83
+        System.out.println("-".repeat(30));
84
+
85
+        for (String date : dates) {
86
+            String result = convertToStandardDate(date);
87
+            System.out.printf("%-15s -> %-12s%n", date, result != null ? result : "解析失败");
88
+        }
89
+    }
90
+}

+ 271 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/EmployeeIdGenerator.java

@@ -0,0 +1,271 @@
1
+package com.jt.cloud.framework.common.util;
2
+
3
+import java.util.concurrent.atomic.AtomicLong;
4
+
5
+
6
+/**
7
+ * 自动递增工号工具类
8
+ * 格式:固定前缀 + 固定位数数字(自动补零)
9
+ */
10
+public class EmployeeIdGenerator {
11
+
12
+    // 默认配置
13
+    private static final String DEFAULT_PREFIX = "";
14
+    private static final int DEFAULT_LENGTH = 6;
15
+    private static final long DEFAULT_START_NUMBER = 1L;
16
+
17
+    // 当前序号(线程安全)
18
+    private final AtomicLong currentNumber;
19
+    private final String prefix;
20
+    private final int numberLength;
21
+
22
+    /**
23
+     * 使用默认配置创建生成器(前缀为空,6位数字,从000001开始)
24
+     */
25
+    public EmployeeIdGenerator() {
26
+        this(DEFAULT_PREFIX, DEFAULT_LENGTH, DEFAULT_START_NUMBER);
27
+    }
28
+
29
+    /**
30
+     * 创建生成器
31
+     *
32
+     * @param prefix       前缀(如:"EMP")
33
+     * @param numberLength 数字部分长度
34
+     * @param startNumber  起始数字
35
+     */
36
+    public EmployeeIdGenerator(String prefix, int numberLength, long startNumber) {
37
+        if (numberLength <= 0) {
38
+            throw new IllegalArgumentException("数字长度必须大于0");
39
+        }
40
+        if (startNumber < 0) {
41
+            throw new IllegalArgumentException("起始数字不能为负数");
42
+        }
43
+
44
+        this.prefix = prefix != null ? prefix : "";
45
+        this.numberLength = numberLength;
46
+        this.currentNumber = new AtomicLong(startNumber);
47
+    }
48
+
49
+    /**
50
+     * 根据现有工号创建生成器(自动解析前缀和序号)
51
+     *
52
+     * @param lastEmployeeId 最后一个工号,如 "EMP000123"
53
+     * @param numberLength   数字部分长度
54
+     */
55
+    public EmployeeIdGenerator(String lastEmployeeId, int numberLength) {
56
+        if (lastEmployeeId == null || lastEmployeeId.trim().isEmpty()) {
57
+            throw new IllegalArgumentException("工号不能为空");
58
+        }
59
+        if (numberLength <= 0) {
60
+            throw new IllegalArgumentException("数字长度必须大于0");
61
+        }
62
+
63
+        ParseResult parseResult = parseEmployeeId(lastEmployeeId, numberLength);
64
+        this.prefix = parseResult.prefix;
65
+        this.numberLength = numberLength;
66
+        this.currentNumber = new AtomicLong(parseResult.number + 1); // 从下一个开始
67
+    }
68
+
69
+    /**
70
+     * 根据现有工号创建生成器(自动推断数字长度)
71
+     *
72
+     * @param lastEmployeeId 最后一个工号
73
+     */
74
+    public EmployeeIdGenerator(String lastEmployeeId) {
75
+        if (lastEmployeeId == null || lastEmployeeId.trim().isEmpty()) {
76
+            throw new IllegalArgumentException("工号不能为空");
77
+        }
78
+
79
+        // 自动推断数字长度
80
+        ParseResult parseResult = parseEmployeeId(lastEmployeeId);
81
+        this.prefix = parseResult.prefix;
82
+        this.numberLength = parseResult.numberLength;
83
+        this.currentNumber = new AtomicLong(parseResult.number + 1); // 从下一个开始
84
+    }
85
+
86
+    /**
87
+     * 生成下一个工号
88
+     *
89
+     * @return 格式化的工号
90
+     */
91
+    public String generateNext() {
92
+        long number = currentNumber.getAndIncrement();
93
+        return formatEmployeeId(number);
94
+    }
95
+
96
+    /**
97
+     * 格式化工号
98
+     *
99
+     * @param number 数字
100
+     * @return 格式化的工号
101
+     */
102
+    private String formatEmployeeId(long number) {
103
+        // 检查数字是否超出范围
104
+        long maxNumber = (long) Math.pow(10, numberLength) - 1;
105
+        if (number > maxNumber) {
106
+            throw new IllegalStateException("工号已超出最大范围: " + maxNumber);
107
+        }
108
+
109
+        // 格式化数字部分(自动补零)
110
+        String numberStr = String.format("%0" + numberLength + "d", number);
111
+        return prefix + numberStr;
112
+    }
113
+
114
+    /**
115
+     * 获取当前序号(不递增)
116
+     *
117
+     * @return 当前序号
118
+     */
119
+    public long getCurrentNumber() {
120
+        return currentNumber.get();
121
+    }
122
+
123
+    /**
124
+     * 重置序号
125
+     *
126
+     * @param newNumber 新的起始数字
127
+     */
128
+    public void reset(long newNumber) {
129
+        if (newNumber < 0) {
130
+            throw new IllegalArgumentException("新的起始数字不能为负数");
131
+        }
132
+        currentNumber.set(newNumber);
133
+    }
134
+
135
+    /**
136
+     * 根据指定工号重置(自动解析前缀和序号)
137
+     *
138
+     * @param employeeId 指定的工号
139
+     */
140
+    public void resetFromEmployeeId(String employeeId) {
141
+        if (employeeId == null || employeeId.trim().isEmpty()) {
142
+            throw new IllegalArgumentException("工号不能为空");
143
+        }
144
+
145
+        ParseResult parseResult = parseEmployeeId(employeeId, this.numberLength);
146
+
147
+        // 检查前缀是否匹配
148
+        if (!this.prefix.equals(parseResult.prefix)) {
149
+            throw new IllegalArgumentException("工号前缀不匹配,当前前缀: " + this.prefix +
150
+                    ", 指定工号前缀: " + parseResult.prefix);
151
+        }
152
+
153
+        currentNumber.set(parseResult.number + 1); // 从下一个开始
154
+    }
155
+
156
+    /**
157
+     * 解析工号(指定数字长度)
158
+     */
159
+    private ParseResult parseEmployeeId(String employeeId, int expectedNumberLength) {
160
+        // 提取数字部分(从末尾取指定长度的数字)
161
+        if (employeeId.length() < expectedNumberLength) {
162
+            throw new IllegalArgumentException("工号长度不足,无法提取" + expectedNumberLength + "位数字");
163
+        }
164
+
165
+        String numberStr = employeeId.substring(employeeId.length() - expectedNumberLength);
166
+        String prefix = employeeId.substring(0, employeeId.length() - expectedNumberLength);
167
+
168
+        try {
169
+            long number = Long.parseLong(numberStr);
170
+            return new ParseResult(prefix, number, expectedNumberLength);
171
+        } catch (NumberFormatException e) {
172
+            throw new IllegalArgumentException("工号数字部分格式错误: " + numberStr);
173
+        }
174
+    }
175
+
176
+    /**
177
+     * 解析工号(自动推断数字长度)
178
+     */
179
+    private ParseResult parseEmployeeId(String employeeId) {
180
+        // 从末尾开始查找连续的数字
181
+        int numberLength = 0;
182
+        for (int i = employeeId.length() - 1; i >= 0; i--) {
183
+            if (Character.isDigit(employeeId.charAt(i))) {
184
+                numberLength++;
185
+            } else {
186
+                break;
187
+            }
188
+        }
189
+
190
+        if (numberLength == 0) {
191
+            throw new IllegalArgumentException("工号中未找到数字部分");
192
+        }
193
+
194
+        String numberStr = employeeId.substring(employeeId.length() - numberLength);
195
+        String prefix = employeeId.substring(0, employeeId.length() - numberLength);
196
+
197
+        try {
198
+            long number = Long.parseLong(numberStr);
199
+            return new ParseResult(prefix, number, numberLength);
200
+        } catch (NumberFormatException e) {
201
+            throw new IllegalArgumentException("工号数字部分格式错误: " + numberStr);
202
+        }
203
+    }
204
+
205
+    /**
206
+     * 解析结果封装类
207
+     */
208
+    private static class ParseResult {
209
+        final String prefix;
210
+        final long number;
211
+        final int numberLength;
212
+
213
+        ParseResult(String prefix, long number, int numberLength) {
214
+            this.prefix = prefix;
215
+            this.number = number;
216
+            this.numberLength = numberLength;
217
+        }
218
+    }
219
+
220
+    /**
221
+     * 获取前缀
222
+     */
223
+    public String getPrefix() {
224
+        return prefix;
225
+    }
226
+
227
+    /**
228
+     * 获取数字长度
229
+     */
230
+    public int getNumberLength() {
231
+        return numberLength;
232
+    }
233
+
234
+    public static void main(String[] args) {
235
+//        // 示例1:基础使用
236
+//        System.out.println("=== 基础版本示例 ===");
237
+//        EmployeeIdGenerator generator1 = new EmployeeIdGenerator();
238
+//        for (int i = 0; i < 5; i++) {
239
+//            System.out.println("工号: " + generator1.generateNext());
240
+//        }
241
+//
242
+//        // 示例2:根据指定工号继续(自动推断数字长度)
243
+//        System.out.println("\n=== 根据指定工号继续(自动推断) ===");
244
+//        EmployeeIdGenerator generator2 = new EmployeeIdGenerator("EMP000123");
245
+//        for (int i = 0; i < 5; i++) {
246
+//            System.out.println("工号: " + generator2.generateNext());
247
+//        }
248
+//
249
+//        // 示例3:根据指定工号继续(指定数字长度)
250
+//        System.out.println("\n=== 根据指定工号继续(指定数字长度) ===");
251
+//        EmployeeIdGenerator generator3 = new EmployeeIdGenerator("DEPT0123", 4);
252
+//        for (int i = 0; i < 5; i++) {
253
+//            System.out.println("工号: " + generator3.generateNext());
254
+//        }
255
+
256
+//        // 示例4:重置到指定工号
257
+//        System.out.println("\n=== 重置到指定工号 ===");
258
+//        EmployeeIdGenerator generator4 = new EmployeeIdGenerator("TEST", 3, 5);
259
+//        System.out.println("重置前: " + generator4.generateNext());
260
+//        generator4.resetFromEmployeeId("TEST010");
261
+//        System.out.println("重置后: " + generator4.generateNext());
262
+//        System.out.println("下一个: " + generator4.generateNext());
263
+
264
+        // 示例5:无前缀工号
265
+        System.out.println("\n=== 无前缀工号 ===");
266
+        EmployeeIdGenerator generator5 = new EmployeeIdGenerator("000123");
267
+        for (int i = 0; i < 3; i++) {
268
+            System.out.println("工号: " + generator5.generateNext());
269
+        }
270
+    }
271
+}

+ 61 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/cache/CacheUtils.java

@@ -0,0 +1,61 @@
1
+package com.jt.cloud.framework.common.util.cache;
2
+
3
+import com.google.common.cache.CacheBuilder;
4
+import com.google.common.cache.CacheLoader;
5
+import com.google.common.cache.LoadingCache;
6
+
7
+import java.time.Duration;
8
+import java.util.concurrent.Executors;
9
+
10
+/**
11
+ * Cache 工具类
12
+ *
13
+ * @author jt
14
+ */
15
+public class CacheUtils {
16
+
17
+    /**
18
+     * 异步刷新的 LoadingCache 最大缓存数量
19
+     *
20
+     * @see <a href="">本地缓存 CacheUtils 工具类建议</a>
21
+     */
22
+    private static final Integer CACHE_MAX_SIZE = 10000;
23
+
24
+    /**
25
+     * 构建异步刷新的 LoadingCache 对象
26
+     *
27
+     * 注意:如果你的缓存和 ThreadLocal 有关系,要么自己处理 ThreadLocal 的传递,要么使用 {@link #buildCache(Duration, CacheLoader)} 方法
28
+     *
29
+     * 或者简单理解:
30
+     * 1、和“人”相关的,使用 {@link #buildCache(Duration, CacheLoader)} 方法
31
+     * 2、和“全局”、“系统”相关的,使用当前缓存方法
32
+     *
33
+     * @param duration 过期时间
34
+     * @param loader  CacheLoader 对象
35
+     * @return LoadingCache 对象
36
+     */
37
+    public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) {
38
+        return CacheBuilder.newBuilder()
39
+                .maximumSize(CACHE_MAX_SIZE)
40
+                // 只阻塞当前数据加载线程,其他线程返回旧值
41
+                .refreshAfterWrite(duration)
42
+                // 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
43
+                .build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO osy:可能要思考下,未来要不要做成可配置
44
+    }
45
+
46
+    /**
47
+     * 构建同步刷新的 LoadingCache 对象
48
+     *
49
+     * @param duration 过期时间
50
+     * @param loader  CacheLoader 对象
51
+     * @return LoadingCache 对象
52
+     */
53
+    public static <K, V> LoadingCache<K, V> buildCache(Duration duration, CacheLoader<K, V> loader) {
54
+        return CacheBuilder.newBuilder()
55
+                .maximumSize(CACHE_MAX_SIZE)
56
+                // 只阻塞当前数据加载线程,其他线程返回旧值
57
+                .refreshAfterWrite(duration)
58
+                .build(loader);
59
+    }
60
+
61
+}

+ 58 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/collection/ArrayUtils.java

@@ -0,0 +1,58 @@
1
+package com.jt.cloud.framework.common.util.collection;
2
+
3
+import cn.hutool.core.collection.CollectionUtil;
4
+import cn.hutool.core.collection.IterUtil;
5
+import cn.hutool.core.util.ArrayUtil;
6
+
7
+import java.util.Collection;
8
+import java.util.function.Consumer;
9
+import java.util.function.Function;
10
+
11
+import static com.jt.cloud.framework.common.util.collection.CollectionUtils.convertList;
12
+
13
+/**
14
+ * Array 工具类
15
+ *
16
+ * @author jt
17
+ */
18
+public class ArrayUtils {
19
+
20
+    /**
21
+     * 将 object 和 newElements 合并成一个数组
22
+     *
23
+     * @param object 对象
24
+     * @param newElements 数组
25
+     * @param <T> 泛型
26
+     * @return 结果数组
27
+     */
28
+    @SafeVarargs
29
+    public static <T> Consumer<T>[] append(Consumer<T> object, Consumer<T>... newElements) {
30
+        if (object == null) {
31
+            return newElements;
32
+        }
33
+        Consumer<T>[] result = ArrayUtil.newArray(Consumer.class, 1 + newElements.length);
34
+        result[0] = object;
35
+        System.arraycopy(newElements, 0, result, 1, newElements.length);
36
+        return result;
37
+    }
38
+
39
+    public static <T, V> V[] toArray(Collection<T> from, Function<T, V> mapper) {
40
+        return toArray(convertList(from, mapper));
41
+    }
42
+
43
+    @SuppressWarnings("unchecked")
44
+    public static <T> T[] toArray(Collection<T> from) {
45
+        if (CollectionUtil.isEmpty(from)) {
46
+            return (T[]) (new Object[0]);
47
+        }
48
+        return ArrayUtil.toArray(from, (Class<T>) IterUtil.getElementType(from.iterator()));
49
+    }
50
+
51
+    public static <T> T get(T[] array, int index) {
52
+        if (null == array || index >= array.length) {
53
+            return null;
54
+        }
55
+        return array[index];
56
+    }
57
+
58
+}

+ 352 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/collection/CollectionUtils.java

@@ -0,0 +1,352 @@
1
+package com.jt.cloud.framework.common.util.collection;
2
+
3
+import cn.hutool.core.collection.CollUtil;
4
+import cn.hutool.core.collection.CollectionUtil;
5
+import cn.hutool.core.util.ArrayUtil;
6
+import com.jt.cloud.framework.common.pojo.PageResult;
7
+import com.google.common.collect.ImmutableMap;
8
+
9
+import java.util.*;
10
+import java.util.function.*;
11
+import java.util.stream.Collectors;
12
+import java.util.stream.Stream;
13
+
14
+import static cn.hutool.core.convert.Convert.toCollection;
15
+import static java.util.Arrays.asList;
16
+
17
+/**
18
+ * Collection 工具类
19
+ *
20
+ * @author jt
21
+ */
22
+public class CollectionUtils {
23
+
24
+    public static boolean containsAny(Object source, Object... targets) {
25
+        return asList(targets).contains(source);
26
+    }
27
+
28
+    public static boolean isAnyEmpty(Collection<?>... collections) {
29
+        return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty);
30
+    }
31
+
32
+    public static <T> boolean anyMatch(Collection<T> from, Predicate<T> predicate) {
33
+        return from.stream().anyMatch(predicate);
34
+    }
35
+
36
+    public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) {
37
+        if (CollUtil.isEmpty(from)) {
38
+            return new ArrayList<>();
39
+        }
40
+        return from.stream().filter(predicate).collect(Collectors.toList());
41
+    }
42
+
43
+    public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper) {
44
+        if (CollUtil.isEmpty(from)) {
45
+            return new ArrayList<>();
46
+        }
47
+        return distinct(from, keyMapper, (t1, t2) -> t1);
48
+    }
49
+
50
+    public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper, BinaryOperator<T> cover) {
51
+        if (CollUtil.isEmpty(from)) {
52
+            return new ArrayList<>();
53
+        }
54
+        return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values());
55
+    }
56
+
57
+    public static <T, U> List<U> convertList(T[] from, Function<T, U> func) {
58
+        if (ArrayUtil.isEmpty(from)) {
59
+            return new ArrayList<>();
60
+        }
61
+        return convertList(Arrays.asList(from), func);
62
+    }
63
+
64
+    public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) {
65
+        if (CollUtil.isEmpty(from)) {
66
+            return new ArrayList<>();
67
+        }
68
+        return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList());
69
+    }
70
+
71
+    public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
72
+        if (CollUtil.isEmpty(from)) {
73
+            return new ArrayList<>();
74
+        }
75
+        return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());
76
+    }
77
+
78
+    public static <T, U> PageResult<U> convertPage(PageResult<T> from, Function<T, U> func) {
79
+        if (ArrayUtil.isEmpty(from)) {
80
+            return new PageResult<>(from.getTotal());
81
+        }
82
+        return new PageResult<>(convertList(from.getList(), func), from.getTotal());
83
+    }
84
+
85
+    public static <T, U> List<U> convertListByFlatMap(Collection<T> from,
86
+                                                      Function<T, ? extends Stream<? extends U>> func) {
87
+        if (CollUtil.isEmpty(from)) {
88
+            return new ArrayList<>();
89
+        }
90
+        return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
91
+    }
92
+
93
+    public static <T, U, R> List<R> convertListByFlatMap(Collection<T> from,
94
+                                                         Function<? super T, ? extends U> mapper,
95
+                                                         Function<U, ? extends Stream<? extends R>> func) {
96
+        if (CollUtil.isEmpty(from)) {
97
+            return new ArrayList<>();
98
+        }
99
+        return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
100
+    }
101
+
102
+    public static <K, V> List<V> mergeValuesFromMap(Map<K, List<V>> map) {
103
+        return map.values()
104
+                .stream()
105
+                .flatMap(List::stream)
106
+                .collect(Collectors.toList());
107
+    }
108
+
109
+    public static <T> Set<T> convertSet(Collection<T> from) {
110
+        return convertSet(from, v -> v);
111
+    }
112
+
113
+    public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
114
+        if (CollUtil.isEmpty(from)) {
115
+            return new HashSet<>();
116
+        }
117
+        return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet());
118
+    }
119
+
120
+    public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
121
+        if (CollUtil.isEmpty(from)) {
122
+            return new HashSet<>();
123
+        }
124
+        return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet());
125
+    }
126
+
127
+    public static <T, K> Map<K, T> convertMapByFilter(Collection<T> from, Predicate<T> filter, Function<T, K> keyFunc) {
128
+        if (CollUtil.isEmpty(from)) {
129
+            return new HashMap<>();
130
+        }
131
+        return from.stream().filter(filter).collect(Collectors.toMap(keyFunc, v -> v));
132
+    }
133
+
134
+    public static <T, U> Set<U> convertSetByFlatMap(Collection<T> from,
135
+                                                    Function<T, ? extends Stream<? extends U>> func) {
136
+        if (CollUtil.isEmpty(from)) {
137
+            return new HashSet<>();
138
+        }
139
+        return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
140
+    }
141
+
142
+    public static <T, U, R> Set<R> convertSetByFlatMap(Collection<T> from,
143
+                                                       Function<? super T, ? extends U> mapper,
144
+                                                       Function<U, ? extends Stream<? extends R>> func) {
145
+        if (CollUtil.isEmpty(from)) {
146
+            return new HashSet<>();
147
+        }
148
+        return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
149
+    }
150
+
151
+    public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {
152
+        if (CollUtil.isEmpty(from)) {
153
+            return new HashMap<>();
154
+        }
155
+        return convertMap(from, keyFunc, Function.identity());
156
+    }
157
+
158
+    public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc, Supplier<? extends Map<K, T>> supplier) {
159
+        if (CollUtil.isEmpty(from)) {
160
+            return supplier.get();
161
+        }
162
+        return convertMap(from, keyFunc, Function.identity(), supplier);
163
+    }
164
+
165
+    public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
166
+        if (CollUtil.isEmpty(from)) {
167
+            return new HashMap<>();
168
+        }
169
+        return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1);
170
+    }
171
+
172
+    public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction) {
173
+        if (CollUtil.isEmpty(from)) {
174
+            return new HashMap<>();
175
+        }
176
+        return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new);
177
+    }
178
+
179
+    public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, Supplier<? extends Map<K, V>> supplier) {
180
+        if (CollUtil.isEmpty(from)) {
181
+            return supplier.get();
182
+        }
183
+        return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier);
184
+    }
185
+
186
+    public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction, Supplier<? extends Map<K, V>> supplier) {
187
+        if (CollUtil.isEmpty(from)) {
188
+            return new HashMap<>();
189
+        }
190
+        return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier));
191
+    }
192
+
193
+    public static <T, K> Map<K, List<T>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc) {
194
+        if (CollUtil.isEmpty(from)) {
195
+            return new HashMap<>();
196
+        }
197
+        return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList())));
198
+    }
199
+
200
+    public static <T, K, V> Map<K, List<V>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
201
+        if (CollUtil.isEmpty(from)) {
202
+            return new HashMap<>();
203
+        }
204
+        return from.stream()
205
+                .collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList())));
206
+    }
207
+
208
+    // 暂时没想好名字,先以 2 结尾噶
209
+    public static <T, K, V> Map<K, Set<V>> convertMultiMap2(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
210
+        if (CollUtil.isEmpty(from)) {
211
+            return new HashMap<>();
212
+        }
213
+        return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet())));
214
+    }
215
+
216
+    public static <T, K> Map<K, T> convertImmutableMap(Collection<T> from, Function<T, K> keyFunc) {
217
+        if (CollUtil.isEmpty(from)) {
218
+            return Collections.emptyMap();
219
+        }
220
+        ImmutableMap.Builder<K, T> builder = ImmutableMap.builder();
221
+        from.forEach(item -> builder.put(keyFunc.apply(item), item));
222
+        return builder.build();
223
+    }
224
+
225
+    /**
226
+     * 对比老、新两个列表,找出新增、修改、删除的数据
227
+     *
228
+     * @param oldList  老列表
229
+     * @param newList  新列表
230
+     * @param sameFunc 对比函数,返回 true 表示相同,返回 false 表示不同
231
+     *                 注意,same 是通过每个元素的“标识”,判断它们是不是同一个数据
232
+     * @return [新增列表、修改列表、删除列表]
233
+     */
234
+    public static <T> List<List<T>> diffList(Collection<T> oldList, Collection<T> newList,
235
+                                             BiFunction<T, T, Boolean> sameFunc) {
236
+        List<T> createList = new LinkedList<>(newList); // 默认都认为是新增的,后续会进行移除
237
+        List<T> updateList = new ArrayList<>();
238
+        List<T> deleteList = new ArrayList<>();
239
+
240
+        // 通过以 oldList 为主遍历,找出 updateList 和 deleteList
241
+        for (T oldObj : oldList) {
242
+            // 1. 寻找是否有匹配的
243
+            T foundObj = null;
244
+            for (Iterator<T> iterator = createList.iterator(); iterator.hasNext(); ) {
245
+                T newObj = iterator.next();
246
+                // 1.1 不匹配,则直接跳过
247
+                if (!sameFunc.apply(oldObj, newObj)) {
248
+                    continue;
249
+                }
250
+                // 1.2 匹配,则移除,并结束寻找
251
+                iterator.remove();
252
+                foundObj = newObj;
253
+                break;
254
+            }
255
+            // 2. 匹配添加到 updateList;不匹配则添加到 deleteList 中
256
+            if (foundObj != null) {
257
+                updateList.add(foundObj);
258
+            } else {
259
+                deleteList.add(oldObj);
260
+            }
261
+        }
262
+        return asList(createList, updateList, deleteList);
263
+    }
264
+
265
+    public static boolean containsAny(Collection<?> source, Collection<?> candidates) {
266
+        return org.springframework.util.CollectionUtils.containsAny(source, candidates);
267
+    }
268
+
269
+    public static <T> T getFirst(List<T> from) {
270
+        return !CollectionUtil.isEmpty(from) ? from.get(0) : null;
271
+    }
272
+
273
+    public static <T> T findFirst(Collection<T> from, Predicate<T> predicate) {
274
+        return findFirst(from, predicate, Function.identity());
275
+    }
276
+
277
+    public static <T, U> U findFirst(Collection<T> from, Predicate<T> predicate, Function<T, U> func) {
278
+        if (CollUtil.isEmpty(from)) {
279
+            return null;
280
+        }
281
+        return from.stream().filter(predicate).findFirst().map(func).orElse(null);
282
+    }
283
+
284
+    public static <T, V extends Comparable<? super V>> V getMaxValue(Collection<T> from, Function<T, V> valueFunc) {
285
+        if (CollUtil.isEmpty(from)) {
286
+            return null;
287
+        }
288
+        assert !from.isEmpty(); // 断言,避免告警
289
+        T t = from.stream().max(Comparator.comparing(valueFunc)).get();
290
+        return valueFunc.apply(t);
291
+    }
292
+
293
+    public static <T, V extends Comparable<? super V>> V getMinValue(List<T> from, Function<T, V> valueFunc) {
294
+        if (CollUtil.isEmpty(from)) {
295
+            return null;
296
+        }
297
+        assert from.size() > 0; // 断言,避免告警
298
+        T t = from.stream().min(Comparator.comparing(valueFunc)).get();
299
+        return valueFunc.apply(t);
300
+    }
301
+
302
+    public static <T, V extends Comparable<? super V>> T getMinObject(List<T> from, Function<T, V> valueFunc) {
303
+        if (CollUtil.isEmpty(from)) {
304
+            return null;
305
+        }
306
+        assert from.size() > 0; // 断言,避免告警
307
+        return from.stream().min(Comparator.comparing(valueFunc)).get();
308
+    }
309
+
310
+    public static <T, V extends Comparable<? super V>> V getSumValue(Collection<T> from, Function<T, V> valueFunc,
311
+                                                                     BinaryOperator<V> accumulator) {
312
+        return getSumValue(from, valueFunc, accumulator, null);
313
+    }
314
+
315
+    public static <T, V extends Comparable<? super V>> V getSumValue(Collection<T> from, Function<T, V> valueFunc,
316
+                                                                     BinaryOperator<V> accumulator, V defaultValue) {
317
+        if (CollUtil.isEmpty(from)) {
318
+            return defaultValue;
319
+        }
320
+        assert !from.isEmpty(); // 断言,避免告警
321
+        return from.stream().map(valueFunc).filter(Objects::nonNull).reduce(accumulator).orElse(defaultValue);
322
+    }
323
+
324
+    public static <T> void addIfNotNull(Collection<T> coll, T item) {
325
+        if (item == null) {
326
+            return;
327
+        }
328
+        coll.add(item);
329
+    }
330
+
331
+    public static <T> Collection<T> singleton(T obj) {
332
+        return obj == null ? Collections.emptyList() : Collections.singleton(obj);
333
+    }
334
+
335
+    public static <T> List<T> newArrayList(List<List<T>> list) {
336
+        return list.stream().filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList());
337
+    }
338
+
339
+    /**
340
+     * 转换为 LinkedHashSet
341
+     *
342
+     * @param <T>         元素类型
343
+     * @param elementType 集合中元素类型
344
+     * @param value       被转换的值
345
+     * @return {@link LinkedHashSet}
346
+     */
347
+    @SuppressWarnings("unchecked")
348
+    public static <T> LinkedHashSet<T> toLinkedHashSet(Class<T> elementType, Object value) {
349
+        return (LinkedHashSet<T>) toCollection(LinkedHashSet.class, elementType, value);
350
+    }
351
+
352
+}

+ 68 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/collection/MapUtils.java

@@ -0,0 +1,68 @@
1
+package com.jt.cloud.framework.common.util.collection;
2
+
3
+import cn.hutool.core.collection.CollUtil;
4
+import cn.hutool.core.collection.CollectionUtil;
5
+import cn.hutool.core.util.ObjUtil;
6
+import com.jt.cloud.framework.common.core.KeyValue;
7
+import com.google.common.collect.Maps;
8
+import com.google.common.collect.Multimap;
9
+
10
+import java.util.ArrayList;
11
+import java.util.Collection;
12
+import java.util.List;
13
+import java.util.Map;
14
+import java.util.function.Consumer;
15
+
16
+/**
17
+ * Map 工具类
18
+ *
19
+ * @author jt
20
+ */
21
+public class MapUtils {
22
+
23
+    /**
24
+     * 从哈希表表中,获得 keys 对应的所有 value 数组
25
+     *
26
+     * @param multimap 哈希表
27
+     * @param keys keys
28
+     * @return value 数组
29
+     */
30
+    public static <K, V> List<V> getList(Multimap<K, V> multimap, Collection<K> keys) {
31
+        List<V> result = new ArrayList<>();
32
+        keys.forEach(k -> {
33
+            Collection<V> values = multimap.get(k);
34
+            if (CollectionUtil.isEmpty(values)) {
35
+                return;
36
+            }
37
+            result.addAll(values);
38
+        });
39
+        return result;
40
+    }
41
+
42
+    /**
43
+     * 从哈希表查找到 key 对应的 value,然后进一步处理
44
+     * key 为 null 时, 不处理
45
+     * 注意,如果查找到的 value 为 null 时,不进行处理
46
+     *
47
+     * @param map 哈希表
48
+     * @param key key
49
+     * @param consumer 进一步处理的逻辑
50
+     */
51
+    public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) {
52
+        if (ObjUtil.isNull(key) || CollUtil.isEmpty(map)) {
53
+            return;
54
+        }
55
+        V value = map.get(key);
56
+        if (value == null) {
57
+            return;
58
+        }
59
+        consumer.accept(value);
60
+    }
61
+
62
+    public static <K, V> Map<K, V> convertMap(List<KeyValue<K, V>> keyValues) {
63
+        Map<K, V> map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size());
64
+        keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue()));
65
+        return map;
66
+    }
67
+
68
+}

+ 19 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/collection/SetUtils.java

@@ -0,0 +1,19 @@
1
+package com.jt.cloud.framework.common.util.collection;
2
+
3
+import cn.hutool.core.collection.CollUtil;
4
+
5
+import java.util.Set;
6
+
7
+/**
8
+ * Set 工具类
9
+ *
10
+ * @author jt
11
+ */
12
+public class SetUtils {
13
+
14
+    @SafeVarargs
15
+    public static <T> Set<T> asSet(T... objs) {
16
+        return CollUtil.newHashSet(objs);
17
+    }
18
+
19
+}

+ 149 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/date/DateUtils.java

@@ -0,0 +1,149 @@
1
+package com.jt.cloud.framework.common.util.date;
2
+
3
+import cn.hutool.core.date.LocalDateTimeUtil;
4
+
5
+import java.time.*;
6
+import java.util.Calendar;
7
+import java.util.Date;
8
+
9
+/**
10
+ * 时间工具类
11
+ *
12
+ * @author jt
13
+ */
14
+public class DateUtils {
15
+
16
+    /**
17
+     * 时区 - 默认
18
+     */
19
+    public static final String TIME_ZONE_DEFAULT = "GMT+8";
20
+
21
+    /**
22
+     * 秒转换成毫秒
23
+     */
24
+    public static final long SECOND_MILLIS = 1000;
25
+
26
+    public static final String FORMAT_YEAR_MONTH_DAY = "yyyy-MM-dd";
27
+
28
+    public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
29
+
30
+    /**
31
+     * 将 LocalDateTime 转换成 Date
32
+     *
33
+     * @param date LocalDateTime
34
+     * @return LocalDateTime
35
+     */
36
+    public static Date of(LocalDateTime date) {
37
+        if (date == null) {
38
+            return null;
39
+        }
40
+        // 将此日期时间与时区相结合以创建 ZonedDateTime
41
+        ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault());
42
+        // 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳
43
+        Instant instant = zonedDateTime.toInstant();
44
+        // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
45
+        return Date.from(instant);
46
+    }
47
+
48
+    /**
49
+     * 将 Date 转换成 LocalDateTime
50
+     *
51
+     * @param date Date
52
+     * @return LocalDateTime
53
+     */
54
+    public static LocalDateTime of(Date date) {
55
+        if (date == null) {
56
+            return null;
57
+        }
58
+        // 转为时间戳
59
+        Instant instant = date.toInstant();
60
+        // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
61
+        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
62
+    }
63
+
64
+    public static Date addTime(Duration duration) {
65
+        return new Date(System.currentTimeMillis() + duration.toMillis());
66
+    }
67
+
68
+    public static boolean isExpired(LocalDateTime time) {
69
+        LocalDateTime now = LocalDateTime.now();
70
+        return now.isAfter(time);
71
+    }
72
+
73
+    /**
74
+     * 创建指定时间
75
+     *
76
+     * @param year  年
77
+     * @param month 月
78
+     * @param day   日
79
+     * @return 指定时间
80
+     */
81
+    public static Date buildTime(int year, int month, int day) {
82
+        return buildTime(year, month, day, 0, 0, 0);
83
+    }
84
+
85
+    /**
86
+     * 创建指定时间
87
+     *
88
+     * @param year   年
89
+     * @param month  月
90
+     * @param day    日
91
+     * @param hour   小时
92
+     * @param minute 分钟
93
+     * @param second 秒
94
+     * @return 指定时间
95
+     */
96
+    public static Date buildTime(int year, int month, int day,
97
+                                 int hour, int minute, int second) {
98
+        Calendar calendar = Calendar.getInstance();
99
+        calendar.set(Calendar.YEAR, year);
100
+        calendar.set(Calendar.MONTH, month - 1);
101
+        calendar.set(Calendar.DAY_OF_MONTH, day);
102
+        calendar.set(Calendar.HOUR_OF_DAY, hour);
103
+        calendar.set(Calendar.MINUTE, minute);
104
+        calendar.set(Calendar.SECOND, second);
105
+        calendar.set(Calendar.MILLISECOND, 0); // 一般情况下,都是 0 毫秒
106
+        return calendar.getTime();
107
+    }
108
+
109
+    public static Date max(Date a, Date b) {
110
+        if (a == null) {
111
+            return b;
112
+        }
113
+        if (b == null) {
114
+            return a;
115
+        }
116
+        return a.compareTo(b) > 0 ? a : b;
117
+    }
118
+
119
+    public static LocalDateTime max(LocalDateTime a, LocalDateTime b) {
120
+        if (a == null) {
121
+            return b;
122
+        }
123
+        if (b == null) {
124
+            return a;
125
+        }
126
+        return a.isAfter(b) ? a : b;
127
+    }
128
+
129
+    /**
130
+     * 是否今天
131
+     *
132
+     * @param date 日期
133
+     * @return 是否
134
+     */
135
+    public static boolean isToday(LocalDateTime date) {
136
+        return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());
137
+    }
138
+
139
+    /**
140
+     * 是否昨天
141
+     *
142
+     * @param date 日期
143
+     * @return 是否
144
+     */
145
+    public static boolean isYesterday(LocalDateTime date) {
146
+        return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now().minusDays(1));
147
+    }
148
+
149
+}

+ 328 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/date/LocalDateTimeUtils.java

@@ -0,0 +1,328 @@
1
+package com.jt.cloud.framework.common.util.date;
2
+
3
+import cn.hutool.core.collection.CollUtil;
4
+import cn.hutool.core.date.DatePattern;
5
+import cn.hutool.core.date.LocalDateTimeUtil;
6
+import cn.hutool.core.date.TemporalAccessorUtil;
7
+import cn.hutool.core.lang.Assert;
8
+import cn.hutool.core.util.StrUtil;
9
+import com.jt.cloud.framework.common.enums.DateIntervalEnum;
10
+
11
+import java.time.*;
12
+import java.time.format.DateTimeFormatter;
13
+import java.time.format.DateTimeParseException;
14
+import java.time.temporal.ChronoUnit;
15
+import java.time.temporal.TemporalAdjusters;
16
+import java.util.ArrayList;
17
+import java.util.List;
18
+
19
+import static cn.hutool.core.date.DatePattern.UTC_MS_WITH_XXX_OFFSET_PATTERN;
20
+import static cn.hutool.core.date.DatePattern.createFormatter;
21
+
22
+/**
23
+ * 时间工具类,用于 {@link LocalDateTime}
24
+ *
25
+ * @author jt
26
+ */
27
+public class LocalDateTimeUtils {
28
+
29
+    /**
30
+     * 空的 LocalDateTime 对象,主要用于 DB 唯一索引的默认值
31
+     */
32
+    public static LocalDateTime EMPTY = buildTime(1970, 1, 1);
33
+
34
+    public static DateTimeFormatter UTC_MS_WITH_XXX_OFFSET_FORMATTER = createFormatter(UTC_MS_WITH_XXX_OFFSET_PATTERN);
35
+
36
+    /**
37
+     * 解析时间
38
+     *
39
+     * 相比 {@link LocalDateTimeUtil#parse(CharSequence)} 方法来说,会尽量去解析,直到成功
40
+     *
41
+     * @param time 时间
42
+     * @return 时间字符串
43
+     */
44
+    public static LocalDateTime parse(String time) {
45
+        try {
46
+            return LocalDateTimeUtil.parse(time, DatePattern.NORM_DATE_PATTERN);
47
+        } catch (DateTimeParseException e) {
48
+            return LocalDateTimeUtil.parse(time);
49
+        }
50
+    }
51
+
52
+    public static LocalDateTime addTime(Duration duration) {
53
+        return LocalDateTime.now().plus(duration);
54
+    }
55
+
56
+    public static LocalDateTime minusTime(Duration duration) {
57
+        return LocalDateTime.now().minus(duration);
58
+    }
59
+
60
+    public static boolean beforeNow(LocalDateTime date) {
61
+        return date.isBefore(LocalDateTime.now());
62
+    }
63
+
64
+    public static boolean afterNow(LocalDateTime date) {
65
+        return date.isAfter(LocalDateTime.now());
66
+    }
67
+
68
+    /**
69
+     * 创建指定时间
70
+     *
71
+     * @param year  年
72
+     * @param mouth 月
73
+     * @param day   日
74
+     * @return 指定时间
75
+     */
76
+    public static LocalDateTime buildTime(int year, int mouth, int day) {
77
+        return LocalDateTime.of(year, mouth, day, 0, 0, 0);
78
+    }
79
+
80
+    public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1,
81
+                                                   int year2, int mouth2, int day2) {
82
+        return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)};
83
+    }
84
+
85
+    /**
86
+     * 判指定断时间,是否在该时间范围内
87
+     *
88
+     * @param startTime 开始时间
89
+     * @param endTime 结束时间
90
+     * @param time 指定时间
91
+     * @return 是否
92
+     */
93
+    public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) {
94
+        if (startTime == null || endTime == null || time == null) {
95
+            return false;
96
+        }
97
+        return LocalDateTimeUtil.isIn(parse(time), startTime, endTime);
98
+    }
99
+
100
+    /**
101
+     * 判断当前时间是否在该时间范围内
102
+     *
103
+     * @param startTime 开始时间
104
+     * @param endTime   结束时间
105
+     * @return 是否
106
+     */
107
+    public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime) {
108
+        if (startTime == null || endTime == null) {
109
+            return false;
110
+        }
111
+        return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime);
112
+    }
113
+
114
+    /**
115
+     * 判断当前时间是否在该时间范围内
116
+     *
117
+     * @param startTime 开始时间
118
+     * @param endTime   结束时间
119
+     * @return 是否
120
+     */
121
+    public static boolean isBetween(String startTime, String endTime) {
122
+        if (startTime == null || endTime == null) {
123
+            return false;
124
+        }
125
+        LocalDate nowDate = LocalDate.now();
126
+        return LocalDateTimeUtil.isIn(LocalDateTime.now(),
127
+                LocalDateTime.of(nowDate, LocalTime.parse(startTime)),
128
+                LocalDateTime.of(nowDate, LocalTime.parse(endTime)));
129
+    }
130
+
131
+    /**
132
+     * 判断时间段是否重叠
133
+     *
134
+     * @param startTime1 开始 time1
135
+     * @param endTime1   结束 time1
136
+     * @param startTime2 开始 time2
137
+     * @param endTime2   结束 time2
138
+     * @return 重叠:true 不重叠:false
139
+     */
140
+    public static boolean isOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) {
141
+        LocalDate nowDate = LocalDate.now();
142
+        return LocalDateTimeUtil.isOverlap(LocalDateTime.of(nowDate, startTime1), LocalDateTime.of(nowDate, endTime1),
143
+                LocalDateTime.of(nowDate, startTime2), LocalDateTime.of(nowDate, endTime2));
144
+    }
145
+
146
+    /**
147
+     * 获取指定日期所在的月份的开始时间
148
+     * 例如:2023-09-30 00:00:00,000
149
+     *
150
+     * @param date 日期
151
+     * @return 月份的开始时间
152
+     */
153
+    public static LocalDateTime beginOfMonth(LocalDateTime date) {
154
+        return date.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
155
+    }
156
+
157
+    /**
158
+     * 获取指定日期所在的月份的最后时间
159
+     * 例如:2023-09-30 23:59:59,999
160
+     *
161
+     * @param date 日期
162
+     * @return 月份的结束时间
163
+     */
164
+    public static LocalDateTime endOfMonth(LocalDateTime date) {
165
+        return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
166
+    }
167
+
168
+    /**
169
+     * 获得指定日期所在季度
170
+     *
171
+     * @param date 日期
172
+     * @return 所在季度
173
+     */
174
+    public static int getQuarterOfYear(LocalDateTime date) {
175
+        return (date.getMonthValue() - 1) / 3 + 1;
176
+    }
177
+
178
+    /**
179
+     * 获取指定日期到现在过了几天,如果指定日期在当前日期之后,获取结果为负
180
+     *
181
+     * @param dateTime 日期
182
+     * @return 相差天数
183
+     */
184
+    public static Long between(LocalDateTime dateTime) {
185
+        return LocalDateTimeUtil.between(dateTime, LocalDateTime.now(), ChronoUnit.DAYS);
186
+    }
187
+
188
+    /**
189
+     * 获取今天的开始时间
190
+     *
191
+     * @return 今天
192
+     */
193
+    public static LocalDateTime getToday() {
194
+        return LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
195
+    }
196
+
197
+    /**
198
+     * 获取昨天的开始时间
199
+     *
200
+     * @return 昨天
201
+     */
202
+    public static LocalDateTime getYesterday() {
203
+        return LocalDateTimeUtil.beginOfDay(LocalDateTime.now().minusDays(1));
204
+    }
205
+
206
+    /**
207
+     * 获取本月的开始时间
208
+     *
209
+     * @return 本月
210
+     */
211
+    public static LocalDateTime getMonth() {
212
+        return beginOfMonth(LocalDateTime.now());
213
+    }
214
+
215
+    /**
216
+     * 获取本年的开始时间
217
+     *
218
+     * @return 本年
219
+     */
220
+    public static LocalDateTime getYear() {
221
+        return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN);
222
+    }
223
+
224
+    public static List<LocalDateTime[]> getDateRangeList(LocalDateTime startTime,
225
+                                                         LocalDateTime endTime,
226
+                                                         Integer interval) {
227
+        // 1.1 找到枚举
228
+        DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval);
229
+        Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval);
230
+        // 1.2 将时间对齐
231
+        startTime = LocalDateTimeUtil.beginOfDay(startTime);
232
+        endTime = LocalDateTimeUtil.endOfDay(endTime);
233
+
234
+        // 2. 循环,生成时间范围
235
+        List<LocalDateTime[]> timeRanges = new ArrayList<>();
236
+        switch (intervalEnum) {
237
+            case DAY:
238
+                while (startTime.isBefore(endTime)) {
239
+                    timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)});
240
+                    startTime = startTime.plusDays(1);
241
+                }
242
+                break;
243
+            case WEEK:
244
+                while (startTime.isBefore(endTime)) {
245
+                    LocalDateTime endOfWeek = startTime.with(DayOfWeek.SUNDAY).plusDays(1).minusNanos(1);
246
+                    timeRanges.add(new LocalDateTime[]{startTime, endOfWeek});
247
+                    startTime = endOfWeek.plusNanos(1);
248
+                }
249
+                break;
250
+            case MONTH:
251
+                while (startTime.isBefore(endTime)) {
252
+                    LocalDateTime endOfMonth = startTime.with(TemporalAdjusters.lastDayOfMonth()).plusDays(1).minusNanos(1);
253
+                    timeRanges.add(new LocalDateTime[]{startTime, endOfMonth});
254
+                    startTime = endOfMonth.plusNanos(1);
255
+                }
256
+                break;
257
+            case QUARTER:
258
+                while (startTime.isBefore(endTime)) {
259
+                    int quarterOfYear = getQuarterOfYear(startTime);
260
+                    LocalDateTime quarterEnd = quarterOfYear == 4
261
+                            ? startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1)
262
+                            : startTime.withMonth(quarterOfYear * 3 + 1).withDayOfMonth(1).minusNanos(1);
263
+                    timeRanges.add(new LocalDateTime[]{startTime, quarterEnd});
264
+                    startTime = quarterEnd.plusNanos(1);
265
+                }
266
+                break;
267
+            case YEAR:
268
+                while (startTime.isBefore(endTime)) {
269
+                    LocalDateTime endOfYear = startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1);
270
+                    timeRanges.add(new LocalDateTime[]{startTime, endOfYear});
271
+                    startTime = endOfYear.plusNanos(1);
272
+                }
273
+                break;
274
+            default:
275
+                throw new IllegalArgumentException("Invalid interval: " + interval);
276
+        }
277
+        // 3. 兜底,最后一个时间,需要保持在 endTime 之前
278
+        LocalDateTime[] lastTimeRange = CollUtil.getLast(timeRanges);
279
+        if (lastTimeRange != null) {
280
+            lastTimeRange[1] = endTime;
281
+        }
282
+        return timeRanges;
283
+    }
284
+
285
+    /**
286
+     * 格式化时间范围
287
+     *
288
+     * @param startTime 开始时间
289
+     * @param endTime   结束时间
290
+     * @param interval  时间间隔
291
+     * @return 时间范围
292
+     */
293
+    public static String formatDateRange(LocalDateTime startTime, LocalDateTime endTime, Integer interval) {
294
+        // 1. 找到枚举
295
+        DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval);
296
+        Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval);
297
+
298
+        // 2. 循环,生成时间范围
299
+        switch (intervalEnum) {
300
+            case DAY:
301
+                return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN);
302
+            case WEEK:
303
+                return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN)
304
+                        + StrUtil.format("(第 {} 周)", LocalDateTimeUtil.weekOfYear(startTime));
305
+            case MONTH:
306
+                return LocalDateTimeUtil.format(startTime, DatePattern.NORM_MONTH_PATTERN);
307
+            case QUARTER:
308
+                return StrUtil.format("{}-Q{}", startTime.getYear(), getQuarterOfYear(startTime));
309
+            case YEAR:
310
+                return LocalDateTimeUtil.format(startTime, DatePattern.NORM_YEAR_PATTERN);
311
+            default:
312
+                throw new IllegalArgumentException("Invalid interval: " + interval);
313
+        }
314
+    }
315
+
316
+    /**
317
+     * 将给定的 {@link LocalDateTime} 转换为自 Unix 纪元时间(1970-01-01T00:00:00Z)以来的秒数。
318
+     *
319
+     * @param sourceDateTime 需要转换的本地日期时间,不能为空
320
+     * @return 自 1970-01-01T00:00:00Z 起的秒数(epoch second)
321
+     * @throws NullPointerException 如果 {@code sourceDateTime} 为 {@code null}
322
+     * @throws DateTimeException 如果转换过程中发生时间超出范围或其他时间处理异常
323
+     */
324
+    public static Long toEpochSecond(LocalDateTime sourceDateTime) {
325
+        return TemporalAccessorUtil.toInstant(sourceDateTime).getEpochSecond();
326
+    }
327
+
328
+}

+ 175 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/http/HttpUtils.java

@@ -0,0 +1,175 @@
1
+package com.jt.cloud.framework.common.util.http;
2
+
3
+import cn.hutool.core.codec.Base64;
4
+import cn.hutool.core.map.TableMap;
5
+import cn.hutool.core.net.url.UrlBuilder;
6
+import cn.hutool.core.util.ReflectUtil;
7
+import cn.hutool.core.util.StrUtil;
8
+import cn.hutool.http.HttpRequest;
9
+import cn.hutool.http.HttpResponse;
10
+import jakarta.servlet.http.HttpServletRequest;
11
+import org.springframework.util.StringUtils;
12
+import org.springframework.web.util.UriComponents;
13
+import org.springframework.web.util.UriComponentsBuilder;
14
+
15
+import java.net.URI;
16
+import java.net.URLEncoder;
17
+import java.nio.charset.Charset;
18
+import java.nio.charset.StandardCharsets;
19
+import java.util.Map;
20
+
21
+/**
22
+ * HTTP 工具类
23
+ *
24
+ * @author jt
25
+ */
26
+public class HttpUtils {
27
+
28
+    /**
29
+     * 编码 URL 参数
30
+     *
31
+     * @param value 参数
32
+     * @return 编码后的参数
33
+     */
34
+    public static String encodeUtf8(String value) {
35
+        return URLEncoder.encode(value, StandardCharsets.UTF_8);
36
+    }
37
+
38
+    @SuppressWarnings("unchecked")
39
+    public static String replaceUrlQuery(String url, String key, String value) {
40
+        UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset());
41
+        // 先移除
42
+        TableMap<CharSequence, CharSequence> query = (TableMap<CharSequence, CharSequence>)
43
+                ReflectUtil.getFieldValue(builder.getQuery(), "query");
44
+        query.remove(key);
45
+        // 后添加
46
+        builder.addQuery(key, value);
47
+        return builder.build();
48
+    }
49
+
50
+    private String append(String base, Map<String, ?> query, boolean fragment) {
51
+        return append(base, query, null, fragment);
52
+    }
53
+
54
+    /**
55
+     * 拼接 URL
56
+     *
57
+     * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 append 方法
58
+     *
59
+     * @param base 基础 URL
60
+     * @param query 查询参数
61
+     * @param keys query 的 key,对应的原本的 key 的映射。例如说 query 里有个 key 是 xx,实际它的 key 是 extra_xx,则通过 keys 里添加这个映射
62
+     * @param fragment URL 的 fragment,即拼接到 # 中
63
+     * @return 拼接后的 URL
64
+     */
65
+    public static String append(String base, Map<String, ?> query, Map<String, String> keys, boolean fragment) {
66
+        UriComponentsBuilder template = UriComponentsBuilder.newInstance();
67
+        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(base);
68
+        URI redirectUri;
69
+        try {
70
+            // assume it's encoded to start with (if it came in over the wire)
71
+            redirectUri = builder.build(true).toUri();
72
+        } catch (Exception e) {
73
+            // ... but allow client registrations to contain hard-coded non-encoded values
74
+            redirectUri = builder.build().toUri();
75
+            builder = UriComponentsBuilder.fromUri(redirectUri);
76
+        }
77
+        template.scheme(redirectUri.getScheme()).port(redirectUri.getPort()).host(redirectUri.getHost())
78
+                .userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath());
79
+
80
+        if (fragment) {
81
+            StringBuilder values = new StringBuilder();
82
+            if (redirectUri.getFragment() != null) {
83
+                String append = redirectUri.getFragment();
84
+                values.append(append);
85
+            }
86
+            for (String key : query.keySet()) {
87
+                if (values.length() > 0) {
88
+                    values.append("&");
89
+                }
90
+                String name = key;
91
+                if (keys != null && keys.containsKey(key)) {
92
+                    name = keys.get(key);
93
+                }
94
+                values.append(name).append("={").append(key).append("}");
95
+            }
96
+            if (values.length() > 0) {
97
+                template.fragment(values.toString());
98
+            }
99
+            UriComponents encoded = template.build().expand(query).encode();
100
+            builder.fragment(encoded.getFragment());
101
+        } else {
102
+            for (String key : query.keySet()) {
103
+                String name = key;
104
+                if (keys != null && keys.containsKey(key)) {
105
+                    name = keys.get(key);
106
+                }
107
+                template.queryParam(name, "{" + key + "}");
108
+            }
109
+            template.fragment(redirectUri.getFragment());
110
+            UriComponents encoded = template.build().expand(query).encode();
111
+            builder.query(encoded.getQuery());
112
+        }
113
+        return builder.build().toUriString();
114
+    }
115
+
116
+    public static String[] obtainBasicAuthorization(HttpServletRequest request) {
117
+        String clientId;
118
+        String clientSecret;
119
+        // 先从 Header 中获取
120
+        String authorization = request.getHeader("Authorization");
121
+        authorization = StrUtil.subAfter(authorization, "Basic ", true);
122
+        if (StringUtils.hasText(authorization)) {
123
+            authorization = Base64.decodeStr(authorization);
124
+            clientId = StrUtil.subBefore(authorization, ":", false);
125
+            clientSecret = StrUtil.subAfter(authorization, ":", false);
126
+            // 再从 Param 中获取
127
+        } else {
128
+            clientId = request.getParameter("client_id");
129
+            clientSecret = request.getParameter("client_secret");
130
+        }
131
+
132
+        // 如果两者非空,则返回
133
+        if (StrUtil.isNotEmpty(clientId) && StrUtil.isNotEmpty(clientSecret)) {
134
+            return new String[]{clientId, clientSecret};
135
+        }
136
+        return null;
137
+    }
138
+
139
+    /**
140
+     * HTTP post 请求,基于 {@link cn.hutool.http.HttpUtil} 实现
141
+     *
142
+     * 为什么要封装该方法,因为 HttpUtil 默认封装的方法,没有允许传递 headers 参数
143
+     *
144
+     * @param url URL
145
+     * @param headers 请求头
146
+     * @param requestBody 请求体
147
+     * @return 请求结果
148
+     */
149
+    public static String post(String url, Map<String, String> headers, String requestBody) {
150
+        try (HttpResponse response = HttpRequest.post(url)
151
+                .addHeaders(headers)
152
+                .body(requestBody)
153
+                .execute()) {
154
+            return response.body();
155
+        }
156
+    }
157
+
158
+    /**
159
+     * HTTP get 请求,基于 {@link cn.hutool.http.HttpUtil} 实现
160
+     *
161
+     * 为什么要封装该方法,因为 HttpUtil 默认封装的方法,没有允许传递 headers 参数
162
+     *
163
+     * @param url URL
164
+     * @param headers 请求头
165
+     * @return 请求结果
166
+     */
167
+    public static String get(String url, Map<String, String> headers) {
168
+        try (HttpResponse response = HttpRequest.get(url)
169
+                .addHeaders(headers)
170
+                .execute()) {
171
+            return response.body();
172
+        }
173
+    }
174
+
175
+}

+ 61 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/io/FileUtils.java

@@ -0,0 +1,61 @@
1
+package com.jt.cloud.framework.common.util.io;
2
+
3
+import cn.hutool.core.io.FileUtil;
4
+import cn.hutool.core.util.IdUtil;
5
+import lombok.SneakyThrows;
6
+
7
+import java.io.File;
8
+
9
+/**
10
+ * 文件工具类
11
+ *
12
+ * @author jt
13
+ */
14
+public class FileUtils {
15
+
16
+    /**
17
+     * 创建临时文件
18
+     * 该文件会在 JVM 退出时,进行删除
19
+     *
20
+     * @param data 文件内容
21
+     * @return 文件
22
+     */
23
+    @SneakyThrows
24
+    public static File createTempFile(String data) {
25
+        File file = createTempFile();
26
+        // 写入内容
27
+        FileUtil.writeUtf8String(data, file);
28
+        return file;
29
+    }
30
+
31
+    /**
32
+     * 创建临时文件
33
+     * 该文件会在 JVM 退出时,进行删除
34
+     *
35
+     * @param data 文件内容
36
+     * @return 文件
37
+     */
38
+    @SneakyThrows
39
+    public static File createTempFile(byte[] data) {
40
+        File file = createTempFile();
41
+        // 写入内容
42
+        FileUtil.writeBytes(data, file);
43
+        return file;
44
+    }
45
+
46
+    /**
47
+     * 创建临时文件,无内容
48
+     * 该文件会在 JVM 退出时,进行删除
49
+     *
50
+     * @return 文件
51
+     */
52
+    @SneakyThrows
53
+    public static File createTempFile() {
54
+        // 创建文件,通过 UUID 保证唯一
55
+        File file = File.createTempFile(IdUtil.simpleUUID(), null);
56
+        // 标记 JVM 退出时,自动删除
57
+        file.deleteOnExit();
58
+        return file;
59
+    }
60
+
61
+}

+ 28 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/io/IoUtils.java

@@ -0,0 +1,28 @@
1
+package com.jt.cloud.framework.common.util.io;
2
+
3
+import cn.hutool.core.io.IORuntimeException;
4
+import cn.hutool.core.io.IoUtil;
5
+import cn.hutool.core.util.StrUtil;
6
+
7
+import java.io.InputStream;
8
+
9
+/**
10
+ * IO 工具类,用于 {@link cn.hutool.core.io.IoUtil} 缺失的方法
11
+ *
12
+ * @author jt
13
+ */
14
+public class IoUtils {
15
+
16
+    /**
17
+     * 从流中读取 UTF8 编码的内容
18
+     *
19
+     * @param in 输入流
20
+     * @param isClose 是否关闭
21
+     * @return 内容
22
+     * @throws IORuntimeException IO 异常
23
+     */
24
+    public static String readUtf8(InputStream in, boolean isClose) throws IORuntimeException {
25
+        return StrUtil.utf8Str(IoUtil.read(in, isClose));
26
+    }
27
+
28
+}

+ 210 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/json/JsonUtils.java

@@ -0,0 +1,210 @@
1
+package com.jt.cloud.framework.common.util.json;
2
+
3
+import cn.hutool.core.util.ArrayUtil;
4
+import cn.hutool.core.util.StrUtil;
5
+import cn.hutool.json.JSONUtil;
6
+import com.fasterxml.jackson.annotation.JsonInclude;
7
+import com.fasterxml.jackson.core.type.TypeReference;
8
+import com.fasterxml.jackson.databind.DeserializationFeature;
9
+import com.fasterxml.jackson.databind.JsonNode;
10
+import com.fasterxml.jackson.databind.ObjectMapper;
11
+import com.fasterxml.jackson.databind.SerializationFeature;
12
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
13
+import lombok.SneakyThrows;
14
+import lombok.extern.slf4j.Slf4j;
15
+
16
+import java.io.IOException;
17
+import java.lang.reflect.Type;
18
+import java.util.ArrayList;
19
+import java.util.List;
20
+
21
+/**
22
+ * JSON 工具类
23
+ *
24
+ * @author jt
25
+ */
26
+@Slf4j
27
+public class JsonUtils {
28
+
29
+    private static ObjectMapper objectMapper = new ObjectMapper();
30
+
31
+    static {
32
+        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
33
+        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
34
+        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值
35
+        objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化
36
+    }
37
+
38
+    /**
39
+     * 初始化 objectMapper 属性
40
+     * <p>
41
+     * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean
42
+     *
43
+     * @param objectMapper ObjectMapper 对象
44
+     */
45
+    public static void init(ObjectMapper objectMapper) {
46
+        JsonUtils.objectMapper = objectMapper;
47
+    }
48
+
49
+    @SneakyThrows
50
+    public static String toJsonString(Object object) {
51
+        return objectMapper.writeValueAsString(object);
52
+    }
53
+
54
+    @SneakyThrows
55
+    public static byte[] toJsonByte(Object object) {
56
+        return objectMapper.writeValueAsBytes(object);
57
+    }
58
+
59
+    @SneakyThrows
60
+    public static String toJsonPrettyString(Object object) {
61
+        return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
62
+    }
63
+
64
+    public static <T> T parseObject(String text, Class<T> clazz) {
65
+        if (StrUtil.isEmpty(text)) {
66
+            return null;
67
+        }
68
+        try {
69
+            return objectMapper.readValue(text, clazz);
70
+        } catch (IOException e) {
71
+            log.error("json parse err,json:{}", text, e);
72
+            throw new RuntimeException(e);
73
+        }
74
+    }
75
+
76
+    public static <T> T parseObject(String text, String path, Class<T> clazz) {
77
+        if (StrUtil.isEmpty(text)) {
78
+            return null;
79
+        }
80
+        try {
81
+            JsonNode treeNode = objectMapper.readTree(text);
82
+            JsonNode pathNode = treeNode.path(path);
83
+            return objectMapper.readValue(pathNode.toString(), clazz);
84
+        } catch (IOException e) {
85
+            log.error("json parse err,json:{}", text, e);
86
+            throw new RuntimeException(e);
87
+        }
88
+    }
89
+
90
+    public static <T> T parseObject(String text, Type type) {
91
+        if (StrUtil.isEmpty(text)) {
92
+            return null;
93
+        }
94
+        try {
95
+            return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type));
96
+        } catch (IOException e) {
97
+            log.error("json parse err,json:{}", text, e);
98
+            throw new RuntimeException(e);
99
+        }
100
+    }
101
+
102
+    /**
103
+     * 将字符串解析成指定类型的对象
104
+     * 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下,
105
+     * 如果 text 没有 class 属性,则会报错。此时,使用这个方法,可以解决。
106
+     *
107
+     * @param text 字符串
108
+     * @param clazz 类型
109
+     * @return 对象
110
+     */
111
+    public static <T> T parseObject2(String text, Class<T> clazz) {
112
+        if (StrUtil.isEmpty(text)) {
113
+            return null;
114
+        }
115
+        return JSONUtil.toBean(text, clazz);
116
+    }
117
+
118
+    public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
119
+        if (ArrayUtil.isEmpty(bytes)) {
120
+            return null;
121
+        }
122
+        try {
123
+            return objectMapper.readValue(bytes, clazz);
124
+        } catch (IOException e) {
125
+            log.error("json parse err,json:{}", bytes, e);
126
+            throw new RuntimeException(e);
127
+        }
128
+    }
129
+
130
+    public static <T> T parseObject(String text, TypeReference<T> typeReference) {
131
+        try {
132
+            return objectMapper.readValue(text, typeReference);
133
+        } catch (IOException e) {
134
+            log.error("json parse err,json:{}", text, e);
135
+            throw new RuntimeException(e);
136
+        }
137
+    }
138
+
139
+    /**
140
+     * 解析 JSON 字符串成指定类型的对象,如果解析失败,则返回 null
141
+     *
142
+     * @param text 字符串
143
+     * @param typeReference 类型引用
144
+     * @return 指定类型的对象
145
+     */
146
+    public static <T> T parseObjectQuietly(String text, TypeReference<T> typeReference) {
147
+        try {
148
+            return objectMapper.readValue(text, typeReference);
149
+        } catch (IOException e) {
150
+            return null;
151
+        }
152
+    }
153
+
154
+    public static <T> List<T> parseArray(String text, Class<T> clazz) {
155
+        if (StrUtil.isEmpty(text)) {
156
+            return new ArrayList<>();
157
+        }
158
+        try {
159
+            return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
160
+        } catch (IOException e) {
161
+            log.error("json parse err,json:{}", text, e);
162
+            throw new RuntimeException(e);
163
+        }
164
+    }
165
+
166
+    public static <T> List<T> parseArray(String text, String path, Class<T> clazz) {
167
+        if (StrUtil.isEmpty(text)) {
168
+            return null;
169
+        }
170
+        try {
171
+            JsonNode treeNode = objectMapper.readTree(text);
172
+            JsonNode pathNode = treeNode.path(path);
173
+            return objectMapper.readValue(pathNode.toString(), objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
174
+        } catch (IOException e) {
175
+            log.error("json parse err,json:{}", text, e);
176
+            throw new RuntimeException(e);
177
+        }
178
+    }
179
+
180
+    public static JsonNode parseTree(String text) {
181
+        try {
182
+            return objectMapper.readTree(text);
183
+        } catch (IOException e) {
184
+            log.error("json parse err,json:{}", text, e);
185
+            throw new RuntimeException(e);
186
+        }
187
+    }
188
+
189
+    public static JsonNode parseTree(byte[] text) {
190
+        try {
191
+            return objectMapper.readTree(text);
192
+        } catch (IOException e) {
193
+            log.error("json parse err,json:{}", text, e);
194
+            throw new RuntimeException(e);
195
+        }
196
+    }
197
+
198
+    public static boolean isJson(String text) {
199
+        return JSONUtil.isTypeJSON(text);
200
+    }
201
+
202
+    /**
203
+     * 判断字符串是否为 JSON 类型的字符串
204
+     * @param str 字符串
205
+     */
206
+    public static boolean isJsonObject(String str) {
207
+        return JSONUtil.isTypeJSONObject(str);
208
+    }
209
+
210
+}

+ 37 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/json/databind/NumberSerializer.java

@@ -0,0 +1,37 @@
1
+package com.jt.cloud.framework.common.util.json.databind;
2
+
3
+import com.fasterxml.jackson.core.JsonGenerator;
4
+import com.fasterxml.jackson.databind.SerializerProvider;
5
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
6
+
7
+import java.io.IOException;
8
+
9
+/**
10
+ * Long 序列化规则
11
+ *
12
+ * 会将超长 long 值转换为 string,解决前端 JavaScript 最大安全整数是 2^53-1 的问题
13
+ *
14
+ * @author 星语
15
+ */
16
+@JacksonStdImpl
17
+public class NumberSerializer extends com.fasterxml.jackson.databind.ser.std.NumberSerializer {
18
+
19
+    private static final long MAX_SAFE_INTEGER = 9007199254740991L;
20
+    private static final long MIN_SAFE_INTEGER = -9007199254740991L;
21
+
22
+    public static final NumberSerializer INSTANCE = new NumberSerializer(Number.class);
23
+
24
+    public NumberSerializer(Class<? extends Number> rawType) {
25
+        super(rawType);
26
+    }
27
+
28
+    @Override
29
+    public void serialize(Number value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
30
+        // 超出范围 序列化位字符串
31
+        if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
32
+            super.serialize(value, gen, serializers);
33
+        } else {
34
+            gen.writeString(value.toString());
35
+        }
36
+    }
37
+}

+ 27 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java

@@ -0,0 +1,27 @@
1
+package com.jt.cloud.framework.common.util.json.databind;
2
+
3
+import com.fasterxml.jackson.core.JsonParser;
4
+import com.fasterxml.jackson.databind.DeserializationContext;
5
+import com.fasterxml.jackson.databind.JsonDeserializer;
6
+
7
+import java.io.IOException;
8
+import java.time.Instant;
9
+import java.time.LocalDateTime;
10
+import java.time.ZoneId;
11
+
12
+/**
13
+ * 基于时间戳的 LocalDateTime 反序列化器
14
+ *
15
+ * @author 老五
16
+ */
17
+public class TimestampLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
18
+
19
+    public static final TimestampLocalDateTimeDeserializer INSTANCE = new TimestampLocalDateTimeDeserializer();
20
+
21
+    @Override
22
+    public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
23
+        // 将 Long 时间戳,转换为 LocalDateTime 对象
24
+        return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault());
25
+    }
26
+
27
+}

+ 26 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java

@@ -0,0 +1,26 @@
1
+package com.jt.cloud.framework.common.util.json.databind;
2
+
3
+import com.fasterxml.jackson.core.JsonGenerator;
4
+import com.fasterxml.jackson.databind.JsonSerializer;
5
+import com.fasterxml.jackson.databind.SerializerProvider;
6
+
7
+import java.io.IOException;
8
+import java.time.LocalDateTime;
9
+import java.time.ZoneId;
10
+
11
+/**
12
+ * 基于时间戳的 LocalDateTime 序列化器
13
+ *
14
+ * @author 老五
15
+ */
16
+public class TimestampLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
17
+
18
+    public static final TimestampLocalDateTimeSerializer INSTANCE = new TimestampLocalDateTimeSerializer();
19
+
20
+    @Override
21
+    public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
22
+        // 将 LocalDateTime 对象,转换为 Long 时间戳
23
+        gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
24
+    }
25
+
26
+}

+ 30 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/monitor/TracerUtils.java

@@ -0,0 +1,30 @@
1
+package com.jt.cloud.framework.common.util.monitor;
2
+
3
+import org.apache.skywalking.apm.toolkit.trace.TraceContext;
4
+
5
+/**
6
+ * 链路追踪工具类
7
+ *
8
+ * 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 util 包下
9
+ *
10
+ * @author jt
11
+ */
12
+public class TracerUtils {
13
+
14
+    /**
15
+     * 私有化构造方法
16
+     */
17
+    private TracerUtils() {
18
+    }
19
+
20
+    /**
21
+     * 获得链路追踪编号,直接返回 SkyWalking 的 TraceId。
22
+     * 如果不存在的话为空字符串!!!
23
+     *
24
+     * @return 链路追踪编号
25
+     */
26
+    public static String getTraceId() {
27
+        return TraceContext.traceId();
28
+    }
29
+
30
+}

+ 131 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/number/MoneyUtils.java

@@ -0,0 +1,131 @@
1
+package com.jt.cloud.framework.common.util.number;
2
+
3
+import cn.hutool.core.math.Money;
4
+import cn.hutool.core.util.NumberUtil;
5
+
6
+import java.math.BigDecimal;
7
+import java.math.RoundingMode;
8
+
9
+/**
10
+ * 金额工具类
11
+ *
12
+ * @author jt
13
+ */
14
+public class MoneyUtils {
15
+
16
+    /**
17
+     * 金额的小数位数
18
+     */
19
+    private static final int PRICE_SCALE = 2;
20
+
21
+    /**
22
+     * 百分比对应的 BigDecimal 对象
23
+     */
24
+    public static final BigDecimal PERCENT_100 = BigDecimal.valueOf(100);
25
+
26
+    /**
27
+     * 计算百分比金额,四舍五入
28
+     *
29
+     * @param price 金额
30
+     * @param rate  百分比,例如说 56.77% 则传入 56.77
31
+     * @return 百分比金额
32
+     */
33
+    public static Integer calculateRatePrice(Integer price, Double rate) {
34
+        return calculateRatePrice(price, rate, 0, RoundingMode.HALF_UP).intValue();
35
+    }
36
+
37
+    /**
38
+     * 计算百分比金额,向下传入
39
+     *
40
+     * @param price 金额
41
+     * @param rate  百分比,例如说 56.77% 则传入 56.77
42
+     * @return 百分比金额
43
+     */
44
+    public static Integer calculateRatePriceFloor(Integer price, Double rate) {
45
+        return calculateRatePrice(price, rate, 0, RoundingMode.FLOOR).intValue();
46
+    }
47
+
48
+    /**
49
+     * 计算百分比金额
50
+     *
51
+     * @param price   金额(单位分)
52
+     * @param count   数量
53
+     * @param percent 折扣(单位分),列如 60.2%,则传入 6020
54
+     * @return 商品总价
55
+     */
56
+    public static Integer calculator(Integer price, Integer count, Integer percent) {
57
+        price = price * count;
58
+        if (percent == null) {
59
+            return price;
60
+        }
61
+        return MoneyUtils.calculateRatePriceFloor(price, (double) (percent / 100));
62
+    }
63
+
64
+    /**
65
+     * 计算百分比金额
66
+     *
67
+     * @param price        金额
68
+     * @param rate         百分比,例如说 56.77% 则传入 56.77
69
+     * @param scale        保留小数位数
70
+     * @param roundingMode 舍入模式
71
+     */
72
+    public static BigDecimal calculateRatePrice(Number price, Number rate, int scale, RoundingMode roundingMode) {
73
+        return NumberUtil.toBigDecimal(price).multiply(NumberUtil.toBigDecimal(rate)) // 乘以
74
+                .divide(BigDecimal.valueOf(100), scale, roundingMode); // 除以 100
75
+    }
76
+
77
+    /**
78
+     * 分转元
79
+     *
80
+     * @param fen 分
81
+     * @return 元
82
+     */
83
+    public static BigDecimal fenToYuan(int fen) {
84
+        return new Money(0, fen).getAmount();
85
+    }
86
+
87
+    /**
88
+     * 分转元(字符串)
89
+     *
90
+     * 例如说 fen 为 1 时,则结果为 0.01
91
+     *
92
+     * @param fen 分
93
+     * @return 元
94
+     */
95
+    public static String fenToYuanStr(int fen) {
96
+        return new Money(0, fen).toString();
97
+    }
98
+
99
+    /**
100
+     * 金额相乘,默认进行四舍五入
101
+     *
102
+     * 位数:{@link #PRICE_SCALE}
103
+     *
104
+     * @param price 金额
105
+     * @param count 数量
106
+     * @return 金额相乘结果
107
+     */
108
+    public static BigDecimal priceMultiply(BigDecimal price, BigDecimal count) {
109
+        if (price == null || count == null) {
110
+            return null;
111
+        }
112
+        return price.multiply(count).setScale(PRICE_SCALE, RoundingMode.HALF_UP);
113
+    }
114
+
115
+    /**
116
+     * 金额相乘(百分比),默认进行四舍五入
117
+     *
118
+     * 位数:{@link #PRICE_SCALE}
119
+     *
120
+     * @param price  金额
121
+     * @param percent 百分比
122
+     * @return 金额相乘结果
123
+     */
124
+    public static BigDecimal priceMultiplyPercent(BigDecimal price, BigDecimal percent) {
125
+        if (price == null || percent == null) {
126
+            return null;
127
+        }
128
+        return price.multiply(percent).divide(PERCENT_100, PRICE_SCALE, RoundingMode.HALF_UP);
129
+    }
130
+
131
+}

+ 78 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/number/NumberUtils.java

@@ -0,0 +1,78 @@
1
+package com.jt.cloud.framework.common.util.number;
2
+
3
+import cn.hutool.core.collection.CollUtil;
4
+import cn.hutool.core.util.NumberUtil;
5
+import cn.hutool.core.util.StrUtil;
6
+
7
+import java.math.BigDecimal;
8
+import java.util.List;
9
+
10
+/**
11
+ * 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能
12
+ *
13
+ * @author jt
14
+ */
15
+public class NumberUtils {
16
+
17
+    public static Long parseLong(String str) {
18
+        return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null;
19
+    }
20
+
21
+    public static Integer parseInt(String str) {
22
+        return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null;
23
+    }
24
+
25
+    public static boolean isAllNumber(List<String> values) {
26
+        if (CollUtil.isEmpty(values)) {
27
+            return false;
28
+        }
29
+        for (String value : values) {
30
+            if (!NumberUtil.isNumber(value)) {
31
+                return false;
32
+            }
33
+        }
34
+        return true;
35
+    }
36
+
37
+    /**
38
+     * 通过经纬度获取地球上两点之间的距离
39
+     *
40
+     * 参考 <<a href="https://gitee.com/dromara/hutool/blob/1caabb586b1f95aec66a21d039c5695df5e0f4c1/hutool-core/src/main/java/cn/hutool/core/util/DistanceUtil.java">DistanceUtil</a>> 实现,目前它已经被 hutool 删除
41
+     *
42
+     * @param lat1 经度1
43
+     * @param lng1 纬度1
44
+     * @param lat2 经度2
45
+     * @param lng2 纬度2
46
+     * @return 距离,单位:千米
47
+     */
48
+    public static double getDistance(double lat1, double lng1, double lat2, double lng2) {
49
+        double radLat1 = lat1 * Math.PI / 180.0;
50
+        double radLat2 = lat2 * Math.PI / 180.0;
51
+        double a = radLat1 - radLat2;
52
+        double b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
53
+        double distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
54
+                + Math.cos(radLat1) * Math.cos(radLat2)
55
+                * Math.pow(Math.sin(b / 2), 2)));
56
+        distance = distance * 6378.137;
57
+        distance = Math.round(distance * 10000d) / 10000d;
58
+        return distance;
59
+    }
60
+
61
+    /**
62
+     * 提供精确的乘法运算
63
+     *
64
+     * 和 hutool {@link NumberUtil#mul(BigDecimal...)} 的差别是,如果存在 null,则返回 null
65
+     *
66
+     * @param values 多个被乘值
67
+     * @return 积
68
+     */
69
+    public static BigDecimal mul(BigDecimal... values) {
70
+        for (BigDecimal value : values) {
71
+            if (value == null) {
72
+                return null;
73
+            }
74
+        }
75
+        return NumberUtil.mul(values);
76
+    }
77
+
78
+}

+ 69 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/object/BeanUtils.java

@@ -0,0 +1,69 @@
1
+package com.jt.cloud.framework.common.util.object;
2
+
3
+import cn.hutool.core.bean.BeanUtil;
4
+import com.jt.cloud.framework.common.pojo.PageResult;
5
+import com.jt.cloud.framework.common.util.collection.CollectionUtils;
6
+
7
+import java.util.List;
8
+import java.util.function.Consumer;
9
+
10
+/**
11
+ * Bean 工具类
12
+ *
13
+ * 1. 默认使用 {@link cn.hutool.core.bean.BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能
14
+ * 2. 针对复杂的对象转换,可以搜参考 AuthConvert 实现,通过 mapstruct + default 配合实现
15
+ *
16
+ * @author jt
17
+ */
18
+public class BeanUtils {
19
+
20
+    public static <T> T toBean(Object source, Class<T> targetClass) {
21
+        return BeanUtil.toBean(source, targetClass);
22
+    }
23
+
24
+    public static <T> T toBean(Object source, Class<T> targetClass, Consumer<T> peek) {
25
+        T target = toBean(source, targetClass);
26
+        if (target != null) {
27
+            peek.accept(target);
28
+        }
29
+        return target;
30
+    }
31
+
32
+    public static <S, T> List<T> toBean(List<S> source, Class<T> targetType) {
33
+        if (source == null) {
34
+            return null;
35
+        }
36
+        return CollectionUtils.convertList(source, s -> toBean(s, targetType));
37
+    }
38
+
39
+    public static <S, T> List<T> toBean(List<S> source, Class<T> targetType, Consumer<T> peek) {
40
+        List<T> list = toBean(source, targetType);
41
+        if (list != null) {
42
+            list.forEach(peek);
43
+        }
44
+        return list;
45
+    }
46
+
47
+    public static <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType) {
48
+        return toBean(source, targetType, null);
49
+    }
50
+
51
+    public static <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType, Consumer<T> peek) {
52
+        if (source == null) {
53
+            return null;
54
+        }
55
+        List<T> list = toBean(source.getList(), targetType);
56
+        if (peek != null) {
57
+            list.forEach(peek);
58
+        }
59
+        return new PageResult<>(list, source.getTotal());
60
+    }
61
+
62
+    public static void copyProperties(Object source, Object target) {
63
+        if (source == null || target == null) {
64
+            return;
65
+        }
66
+        BeanUtil.copyProperties(source, target, false);
67
+    }
68
+
69
+}

+ 63 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/object/ObjectUtils.java

@@ -0,0 +1,63 @@
1
+package com.jt.cloud.framework.common.util.object;
2
+
3
+import cn.hutool.core.util.ObjectUtil;
4
+import cn.hutool.core.util.ReflectUtil;
5
+
6
+import java.lang.reflect.Field;
7
+import java.util.Arrays;
8
+import java.util.function.Consumer;
9
+
10
+/**
11
+ * Object 工具类
12
+ *
13
+ * @author jt
14
+ */
15
+public class ObjectUtils {
16
+
17
+    /**
18
+     * 复制对象,并忽略 Id 编号
19
+     *
20
+     * @param object 被复制对象
21
+     * @param consumer 消费者,可以二次编辑被复制对象
22
+     * @return 复制后的对象
23
+     */
24
+    public static <T> T cloneIgnoreId(T object, Consumer<T> consumer) {
25
+        T result = ObjectUtil.clone(object);
26
+        // 忽略 id 编号
27
+        Field field = ReflectUtil.getField(object.getClass(), "id");
28
+        if (field != null) {
29
+            ReflectUtil.setFieldValue(result, field, null);
30
+        }
31
+        // 二次编辑
32
+        if (result != null) {
33
+            consumer.accept(result);
34
+        }
35
+        return result;
36
+    }
37
+
38
+    public static <T extends Comparable<T>> T max(T obj1, T obj2) {
39
+        if (obj1 == null) {
40
+            return obj2;
41
+        }
42
+        if (obj2 == null) {
43
+            return obj1;
44
+        }
45
+        return obj1.compareTo(obj2) > 0 ? obj1 : obj2;
46
+    }
47
+
48
+    @SafeVarargs
49
+    public static <T> T defaultIfNull(T... array) {
50
+        for (T item : array) {
51
+            if (item != null) {
52
+                return item;
53
+            }
54
+        }
55
+        return null;
56
+    }
57
+
58
+    @SafeVarargs
59
+    public static <T> boolean equalsAny(T obj, T... array) {
60
+        return Arrays.asList(array).contains(obj);
61
+    }
62
+
63
+}

+ 67 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/object/PageUtils.java

@@ -0,0 +1,67 @@
1
+package com.jt.cloud.framework.common.util.object;
2
+
3
+import cn.hutool.core.collection.CollUtil;
4
+import cn.hutool.core.lang.func.Func1;
5
+import cn.hutool.core.lang.func.LambdaUtil;
6
+import cn.hutool.core.util.ArrayUtil;
7
+import com.jt.cloud.framework.common.pojo.PageParam;
8
+import com.jt.cloud.framework.common.pojo.SortablePageParam;
9
+import com.jt.cloud.framework.common.pojo.SortingField;
10
+import org.springframework.util.Assert;
11
+
12
+import static java.util.Collections.singletonList;
13
+
14
+/**
15
+ * {@link com.jt.cloud.framework.common.pojo.PageParam} 工具类
16
+ *
17
+ * @author jt
18
+ */
19
+public class PageUtils {
20
+
21
+    private static final Object[] ORDER_TYPES = new String[]{SortingField.ORDER_ASC, SortingField.ORDER_DESC};
22
+
23
+    public static int getStart(PageParam pageParam) {
24
+        return (pageParam.getPageNo() - 1) * pageParam.getPageSize();
25
+    }
26
+
27
+    /**
28
+     * 构建排序字段(默认倒序)
29
+     *
30
+     * @param func 排序字段的 Lambda 表达式
31
+     * @param <T>  排序字段所属的类型
32
+     * @return 排序字段
33
+     */
34
+    public static <T> SortingField buildSortingField(Func1<T, ?> func) {
35
+        return buildSortingField(func, SortingField.ORDER_DESC);
36
+    }
37
+
38
+    /**
39
+     * 构建排序字段
40
+     *
41
+     * @param func  排序字段的 Lambda 表达式
42
+     * @param order 排序类型 {@link SortingField#ORDER_ASC} {@link SortingField#ORDER_DESC}
43
+     * @param <T>   排序字段所属的类型
44
+     * @return 排序字段
45
+     */
46
+    public static <T> SortingField buildSortingField(Func1<T, ?> func, String order) {
47
+        Assert.isTrue(ArrayUtil.contains(ORDER_TYPES, order), String.format("字段的排序类型只能是 %s/%s", ORDER_TYPES));
48
+
49
+        String fieldName = LambdaUtil.getFieldName(func);
50
+        return new SortingField(fieldName, order);
51
+    }
52
+
53
+    /**
54
+     * 构建默认的排序字段
55
+     * 如果排序字段为空,则设置排序字段;否则忽略
56
+     *
57
+     * @param sortablePageParam 排序分页查询参数
58
+     * @param func              排序字段的 Lambda 表达式
59
+     * @param <T>               排序字段所属的类型
60
+     */
61
+    public static <T> void buildDefaultSortingField(SortablePageParam sortablePageParam, Func1<T, ?> func) {
62
+        if (sortablePageParam != null && CollUtil.isEmpty(sortablePageParam.getSortingFields())) {
63
+            sortablePageParam.setSortingFields(singletonList(buildSortingField(func)));
64
+        }
65
+    }
66
+
67
+}

+ 7 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/package-info.java

@@ -0,0 +1,7 @@
1
+/**
2
+ * 对于工具类的选择,优先查找 Hutool 中有没对应的方法
3
+ * 如果没有,则自己封装对应的工具类,以 Utils 结尾,用于区分
4
+ *
5
+ * ps:如果担心 Hutool 存在坑的问题,可以阅读 Hutool 的实现源码,以确保可靠性。并且,可以补充相关的单元测试。
6
+ */
7
+package com.jt.cloud.framework.common.util;

+ 105 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/servlet/ServletUtils.java

@@ -0,0 +1,105 @@
1
+package com.jt.cloud.framework.common.util.servlet;
2
+
3
+import cn.hutool.core.util.StrUtil;
4
+import cn.hutool.extra.servlet.JakartaServletUtil;
5
+import com.jt.cloud.framework.common.util.json.JsonUtils;
6
+import jakarta.servlet.ServletRequest;
7
+import jakarta.servlet.http.HttpServletRequest;
8
+import jakarta.servlet.http.HttpServletResponse;
9
+import org.springframework.http.MediaType;
10
+import org.springframework.web.context.request.RequestAttributes;
11
+import org.springframework.web.context.request.RequestContextHolder;
12
+import org.springframework.web.context.request.ServletRequestAttributes;
13
+
14
+import java.util.Map;
15
+
16
+/**
17
+ * 客户端工具类
18
+ *
19
+ * @author jt
20
+ */
21
+public class ServletUtils {
22
+
23
+    /**
24
+     * 返回 JSON 字符串
25
+     *
26
+     * @param response 响应
27
+     * @param object   对象,会序列化成 JSON 字符串
28
+     */
29
+    @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
30
+    public static void writeJSON(HttpServletResponse response, Object object) {
31
+        String content = JsonUtils.toJsonString(object);
32
+        JakartaServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
33
+    }
34
+
35
+    /**
36
+     * @param request 请求
37
+     * @return ua
38
+     */
39
+    public static String getUserAgent(HttpServletRequest request) {
40
+        String ua = request.getHeader("User-Agent");
41
+        return ua != null ? ua : "";
42
+    }
43
+
44
+    /**
45
+     * 获得请求
46
+     *
47
+     * @return HttpServletRequest
48
+     */
49
+    public static HttpServletRequest getRequest() {
50
+        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
51
+        if (!(requestAttributes instanceof ServletRequestAttributes)) {
52
+            return null;
53
+        }
54
+        return ((ServletRequestAttributes) requestAttributes).getRequest();
55
+    }
56
+
57
+    public static String getUserAgent() {
58
+        HttpServletRequest request = getRequest();
59
+        if (request == null) {
60
+            return null;
61
+        }
62
+        return getUserAgent(request);
63
+    }
64
+
65
+    public static String getClientIP() {
66
+        HttpServletRequest request = getRequest();
67
+        if (request == null) {
68
+            return null;
69
+        }
70
+        return JakartaServletUtil.getClientIP(request);
71
+    }
72
+
73
+    public static boolean isJsonRequest(ServletRequest request) {
74
+        return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);
75
+    }
76
+
77
+    public static String getBody(HttpServletRequest request) {
78
+        // 只有在 json 请求在读取,因为只有 CacheRequestBodyFilter 才会进行缓存,支持重复读取
79
+        if (isJsonRequest(request)) {
80
+            return JakartaServletUtil.getBody(request);
81
+        }
82
+        return null;
83
+    }
84
+
85
+    public static byte[] getBodyBytes(HttpServletRequest request) {
86
+        // 只有在 json 请求在读取,因为只有 CacheRequestBodyFilter 才会进行缓存,支持重复读取
87
+        if (isJsonRequest(request)) {
88
+            return JakartaServletUtil.getBodyBytes(request);
89
+        }
90
+        return null;
91
+    }
92
+
93
+    public static String getClientIP(HttpServletRequest request) {
94
+        return JakartaServletUtil.getClientIP(request);
95
+    }
96
+
97
+    public static Map<String, String> getParamMap(HttpServletRequest request) {
98
+        return JakartaServletUtil.getParamMap(request);
99
+    }
100
+
101
+    public static Map<String, String> getHeaderMap(HttpServletRequest request) {
102
+        return JakartaServletUtil.getHeaderMap(request);
103
+    }
104
+
105
+}

+ 123 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/spring/SpringExpressionUtils.java

@@ -0,0 +1,123 @@
1
+package com.jt.cloud.framework.common.util.spring;
2
+
3
+import cn.hutool.core.collection.CollUtil;
4
+import cn.hutool.core.map.MapUtil;
5
+import cn.hutool.core.util.ArrayUtil;
6
+import cn.hutool.core.util.StrUtil;
7
+import cn.hutool.extra.spring.SpringUtil;
8
+import org.aspectj.lang.JoinPoint;
9
+import org.aspectj.lang.reflect.MethodSignature;
10
+import org.springframework.context.expression.BeanFactoryResolver;
11
+import org.springframework.core.DefaultParameterNameDiscoverer;
12
+import org.springframework.core.ParameterNameDiscoverer;
13
+import org.springframework.expression.EvaluationContext;
14
+import org.springframework.expression.Expression;
15
+import org.springframework.expression.ExpressionParser;
16
+import org.springframework.expression.spel.standard.SpelExpressionParser;
17
+import org.springframework.expression.spel.support.StandardEvaluationContext;
18
+
19
+import java.lang.reflect.Method;
20
+import java.util.Collections;
21
+import java.util.List;
22
+import java.util.Map;
23
+
24
+/**
25
+ * Spring EL 表达式的工具类
26
+ *
27
+ * @author mashu
28
+ */
29
+public class SpringExpressionUtils {
30
+
31
+    /**
32
+     * Spring EL 表达式解析器
33
+     */
34
+    private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
35
+    /**
36
+     * 参数名发现器
37
+     */
38
+    private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
39
+
40
+    private SpringExpressionUtils() {
41
+    }
42
+
43
+    /**
44
+     * 从切面中,单个解析 EL 表达式的结果
45
+     *
46
+     * @param joinPoint        切面点
47
+     * @param expressionString EL 表达式数组
48
+     * @return 执行界面
49
+     */
50
+    public static Object parseExpression(JoinPoint joinPoint, String expressionString) {
51
+        Map<String, Object> result = parseExpressions(joinPoint, Collections.singletonList(expressionString));
52
+        return result.get(expressionString);
53
+    }
54
+
55
+    /**
56
+     * 从切面中,批量解析 EL 表达式的结果
57
+     *
58
+     * @param joinPoint         切面点
59
+     * @param expressionStrings EL 表达式数组
60
+     * @return 结果,key 为表达式,value 为对应值
61
+     */
62
+    public static Map<String, Object> parseExpressions(JoinPoint joinPoint, List<String> expressionStrings) {
63
+        // 如果为空,则不进行解析
64
+        if (CollUtil.isEmpty(expressionStrings)) {
65
+            return MapUtil.newHashMap();
66
+        }
67
+
68
+        // 第一步,构建解析的上下文 EvaluationContext
69
+        // 通过 joinPoint 获取被注解方法
70
+        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
71
+        Method method = methodSignature.getMethod();
72
+        // 使用 spring 的 ParameterNameDiscoverer 获取方法形参名数组
73
+        String[] paramNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
74
+        // Spring 的表达式上下文对象
75
+        EvaluationContext context = new StandardEvaluationContext();
76
+        // 给上下文赋值
77
+        if (ArrayUtil.isNotEmpty(paramNames)) {
78
+            Object[] args = joinPoint.getArgs();
79
+            for (int i = 0; i < paramNames.length; i++) {
80
+                context.setVariable(paramNames[i], args[i]);
81
+            }
82
+        }
83
+
84
+        // 第二步,逐个参数解析
85
+        Map<String, Object> result = MapUtil.newHashMap(expressionStrings.size(), true);
86
+        expressionStrings.forEach(key -> {
87
+            Object value = EXPRESSION_PARSER.parseExpression(key).getValue(context);
88
+            result.put(key, value);
89
+        });
90
+        return result;
91
+    }
92
+
93
+    /**
94
+     * 从 Bean 工厂,解析 EL 表达式的结果
95
+     *
96
+     * @param expressionString EL 表达式
97
+     * @return 执行界面
98
+     */
99
+    public static Object parseExpression(String expressionString) {
100
+        return parseExpression(expressionString, null);
101
+    }
102
+
103
+    /**
104
+     * 从 Bean 工厂,解析 EL 表达式的结果
105
+     *
106
+     * @param expressionString EL 表达式
107
+     * @param variables        变量
108
+     * @return 执行界面
109
+     */
110
+    public static Object parseExpression(String expressionString, Map<String, Object> variables) {
111
+        if (StrUtil.isBlank(expressionString)) {
112
+            return null;
113
+        }
114
+        Expression expression = EXPRESSION_PARSER.parseExpression(expressionString);
115
+        StandardEvaluationContext context = new StandardEvaluationContext();
116
+        context.setBeanResolver(new BeanFactoryResolver(SpringUtil.getApplicationContext()));
117
+        if (MapUtil.isNotEmpty(variables)) {
118
+            context.setVariables(variables);
119
+        }
120
+        return expression.getValue(context);
121
+    }
122
+
123
+}

+ 24 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/spring/SpringUtils.java

@@ -0,0 +1,24 @@
1
+package com.jt.cloud.framework.common.util.spring;
2
+
3
+import cn.hutool.extra.spring.SpringUtil;
4
+
5
+import java.util.Objects;
6
+
7
+/**
8
+ * Spring 工具类
9
+ *
10
+ * @author jt
11
+ */
12
+public class SpringUtils extends SpringUtil {
13
+
14
+    /**
15
+     * 是否为生产环境
16
+     *
17
+     * @return 是否生产环境
18
+     */
19
+    public static boolean isProd() {
20
+        String activeProfile = getActiveProfile();
21
+        return Objects.equals("prod", activeProfile);
22
+    }
23
+
24
+}

+ 107 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/string/StrUtils.java

@@ -0,0 +1,107 @@
1
+package com.jt.cloud.framework.common.util.string;
2
+
3
+import cn.hutool.core.text.StrPool;
4
+import cn.hutool.core.util.ArrayUtil;
5
+import cn.hutool.core.util.StrUtil;
6
+import org.aspectj.lang.JoinPoint;
7
+
8
+import java.util.Arrays;
9
+import java.util.Collection;
10
+import java.util.List;
11
+import java.util.Set;
12
+import java.util.stream.Collectors;
13
+
14
+/**
15
+ * 字符串工具类
16
+ *
17
+ * @author jt
18
+ */
19
+public class StrUtils {
20
+
21
+    public static String maxLength(CharSequence str, int maxLength) {
22
+        return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好
23
+    }
24
+
25
+    /**
26
+     * 给定字符串是否以任何一个字符串开始
27
+     * 给定字符串和数组为空都返回 false
28
+     *
29
+     * @param str      给定字符串
30
+     * @param prefixes 需要检测的开始字符串
31
+     * @since 3.0.6
32
+     */
33
+    public static boolean startWithAny(String str, Collection<String> prefixes) {
34
+        if (StrUtil.isEmpty(str) || ArrayUtil.isEmpty(prefixes)) {
35
+            return false;
36
+        }
37
+
38
+        for (CharSequence suffix : prefixes) {
39
+            if (StrUtil.startWith(str, suffix, false)) {
40
+                return true;
41
+            }
42
+        }
43
+        return false;
44
+    }
45
+
46
+    public static List<Long> splitToLong(String value, CharSequence separator) {
47
+        long[] longs = StrUtil.splitToLong(value, separator);
48
+        return Arrays.stream(longs).boxed().collect(Collectors.toList());
49
+    }
50
+
51
+    public static Set<Long> splitToLongSet(String value) {
52
+        return splitToLongSet(value, StrPool.COMMA);
53
+    }
54
+
55
+    public static Set<Long> splitToLongSet(String value, CharSequence separator) {
56
+        long[] longs = StrUtil.splitToLong(value, separator);
57
+        return Arrays.stream(longs).boxed().collect(Collectors.toSet());
58
+    }
59
+
60
+    public static List<Integer> splitToInteger(String value, CharSequence separator) {
61
+        int[] integers = StrUtil.splitToInt(value, separator);
62
+        return Arrays.stream(integers).boxed().collect(Collectors.toList());
63
+    }
64
+
65
+    /**
66
+     * 移除字符串中,包含指定字符串的行
67
+     *
68
+     * @param content 字符串
69
+     * @param sequence 包含的字符串
70
+     * @return 移除后的字符串
71
+     */
72
+    public static String removeLineContains(String content, String sequence) {
73
+        if (StrUtil.isEmpty(content) || StrUtil.isEmpty(sequence)) {
74
+            return content;
75
+        }
76
+        return Arrays.stream(content.split("\n"))
77
+                .filter(line -> !line.contains(sequence))
78
+                .collect(Collectors.joining("\n"));
79
+    }
80
+
81
+    /**
82
+     * 拼接方法的参数
83
+     *
84
+     * 特殊:排除一些无法序列化的参数,如 ServletRequest、ServletResponse、MultipartFile
85
+     *
86
+     * @param joinPoint 连接点
87
+     * @return 拼接后的参数
88
+     */
89
+    public static String joinMethodArgs(JoinPoint joinPoint) {
90
+        Object[] args = joinPoint.getArgs();
91
+        if (ArrayUtil.isEmpty(args)) {
92
+            return "";
93
+        }
94
+        return ArrayUtil.join(args, ",", item -> {
95
+            if (item == null) {
96
+                return "";
97
+            }
98
+            // 讨论可见:https://t.zsxq.com/XUJVk、https://t.zsxq.com/MnKcL
99
+            String clazzName = item.getClass().getName();
100
+            if (StrUtil.startWithAny(clazzName, "javax.servlet", "jakarta.servlet", "org.springframework.web")) {
101
+                return "";
102
+            }
103
+            return item;
104
+        });
105
+    }
106
+
107
+}

+ 61 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/util/validation/ValidationUtils.java

@@ -0,0 +1,61 @@
1
+package com.jt.cloud.framework.common.util.validation;
2
+
3
+import cn.hutool.core.collection.CollUtil;
4
+import cn.hutool.core.lang.Assert;
5
+import lombok.extern.slf4j.Slf4j;
6
+import org.springframework.util.StringUtils;
7
+
8
+import jakarta.validation.ConstraintViolation;
9
+import jakarta.validation.ConstraintViolationException;
10
+import jakarta.validation.Validation;
11
+import jakarta.validation.Validator;
12
+import java.util.Set;
13
+import java.util.regex.Pattern;
14
+
15
+/**
16
+ * 校验工具类
17
+ *
18
+ * @author jt
19
+ */
20
+@Slf4j
21
+public class ValidationUtils {
22
+
23
+    //private static final Pattern PATTERN_MOBILE = Pattern.compile("^(?:(?:\\+|00)86)?1(?:(?:3[\\d])|(?:4[0,1,4-9])|(?:5[0-3,5-9])|(?:6[2,5-7])|(?:7[0-8])|(?:8[\\d])|(?:9[0-3,5-9]))\\d{8}$");
24
+    private static final Pattern PATTERN_MOBILE = Pattern.compile("^\\d{11}$");
25
+    private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
26
+
27
+    private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*");
28
+
29
+    public static boolean isMobile(String mobile) {
30
+        boolean b=StringUtils.hasText(mobile) && PATTERN_MOBILE.matcher(mobile).matches();
31
+        log.info("校验手机号:{},结果:{}}", mobile,b);
32
+        if(!b){
33
+            log.info("校验手机号不通过:{},结果:{}}", mobile,b);
34
+        }
35
+        return b;
36
+    }
37
+
38
+    public static boolean isURL(String url) {
39
+        return StringUtils.hasText(url)
40
+                && PATTERN_URL.matcher(url).matches();
41
+    }
42
+
43
+    public static boolean isXmlNCName(String str) {
44
+        return StringUtils.hasText(str)
45
+                && PATTERN_XML_NCNAME.matcher(str).matches();
46
+    }
47
+
48
+    public static void validate(Object object, Class<?>... groups) {
49
+        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
50
+        Assert.notNull(validator);
51
+        validate(validator, object, groups);
52
+    }
53
+
54
+    public static void validate(Validator validator, Object object, Class<?>... groups) {
55
+        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
56
+        if (CollUtil.isNotEmpty(constraintViolations)) {
57
+            throw new ConstraintViolationException(constraintViolations);
58
+        }
59
+    }
60
+
61
+}

+ 35 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/validation/InEnum.java

@@ -0,0 +1,35 @@
1
+package com.jt.cloud.framework.common.validation;
2
+
3
+import com.jt.cloud.framework.common.core.ArrayValuable;
4
+import jakarta.validation.Constraint;
5
+import jakarta.validation.Payload;
6
+
7
+import java.lang.annotation.*;
8
+
9
+@Target({
10
+        ElementType.METHOD,
11
+        ElementType.FIELD,
12
+        ElementType.ANNOTATION_TYPE,
13
+        ElementType.CONSTRUCTOR,
14
+        ElementType.PARAMETER,
15
+        ElementType.TYPE_USE
16
+})
17
+@Retention(RetentionPolicy.RUNTIME)
18
+@Documented
19
+@Constraint(
20
+        validatedBy = {InEnumValidator.class, InEnumCollectionValidator.class}
21
+)
22
+public @interface InEnum {
23
+
24
+    /**
25
+     * @return 实现 ArrayValuable 接口的类
26
+     */
27
+    Class<? extends ArrayValuable<?>> value();
28
+
29
+    String message() default "必须在指定范围 {value}";
30
+
31
+    Class<?>[] groups() default {};
32
+
33
+    Class<? extends Payload>[] payload() default {};
34
+
35
+}

+ 44 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/validation/InEnumCollectionValidator.java

@@ -0,0 +1,44 @@
1
+package com.jt.cloud.framework.common.validation;
2
+
3
+import cn.hutool.core.collection.CollUtil;
4
+import com.jt.cloud.framework.common.core.ArrayValuable;
5
+import jakarta.validation.ConstraintValidator;
6
+import jakarta.validation.ConstraintValidatorContext;
7
+
8
+import java.util.Arrays;
9
+import java.util.Collection;
10
+import java.util.Collections;
11
+import java.util.List;
12
+
13
+public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<?>> {
14
+
15
+    private List<?> values;
16
+
17
+    @Override
18
+    public void initialize(InEnum annotation) {
19
+        ArrayValuable<?>[] values = annotation.value().getEnumConstants();
20
+        if (values.length == 0) {
21
+            this.values = Collections.emptyList();
22
+        } else {
23
+            this.values = Arrays.asList(values[0].array());
24
+        }
25
+    }
26
+
27
+    @Override
28
+    public boolean isValid(Collection<?> list, ConstraintValidatorContext context) {
29
+        if (list == null) {
30
+            return true;
31
+        }
32
+        // 校验通过
33
+        if (CollUtil.containsAll(values, list)) {
34
+            return true;
35
+        }
36
+        // 校验不通过,自定义提示语句
37
+        context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
38
+        context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
39
+                .replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句
40
+        return false;
41
+    }
42
+
43
+}
44
+

+ 43 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/validation/InEnumValidator.java

@@ -0,0 +1,43 @@
1
+package com.jt.cloud.framework.common.validation;
2
+
3
+import com.jt.cloud.framework.common.core.ArrayValuable;
4
+import jakarta.validation.ConstraintValidator;
5
+import jakarta.validation.ConstraintValidatorContext;
6
+
7
+import java.util.Arrays;
8
+import java.util.Collections;
9
+import java.util.List;
10
+
11
+public class InEnumValidator implements ConstraintValidator<InEnum, Object> {
12
+
13
+    private List<?> values;
14
+
15
+    @Override
16
+    public void initialize(InEnum annotation) {
17
+        ArrayValuable<?>[] values = annotation.value().getEnumConstants();
18
+        if (values.length == 0) {
19
+            this.values = Collections.emptyList();
20
+        } else {
21
+            this.values = Arrays.asList(values[0].array());
22
+        }
23
+    }
24
+
25
+    @Override
26
+    public boolean isValid(Object value, ConstraintValidatorContext context) {
27
+        // 为空时,默认不校验,即认为通过
28
+        if (value == null) {
29
+            return true;
30
+        }
31
+        // 校验通过
32
+        if (values.contains(value)) {
33
+            return true;
34
+        }
35
+        // 校验不通过,自定义提示语句
36
+        context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
37
+        context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
38
+                .replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句
39
+        return false;
40
+    }
41
+
42
+}
43
+

+ 0 - 0
jt-framework/jt-common/src/main/java/com/jt/cloud/framework/common/validation/Mobile.java


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