18339543638 7 months ago
parent
commit
313fdcc5bf
100 changed files with 79320 additions and 0 deletions
  1. 0 0
      init.patch
  2. 108 0
      pom.xml
  3. 73135 0
      sql/data.sql
  4. 845 0
      sql/table.sql
  5. 557 0
      tr-dependencies/pom.xml
  6. 82 0
      tr-framework/pom.xml
  7. 25 0
      tr-framework/src/main/java/cn/tr/core/annotation/ColumnDefaultValue.java
  8. 16 0
      tr-framework/src/main/java/cn/tr/core/annotation/Comment.java
  9. 24 0
      tr-framework/src/main/java/cn/tr/core/annotation/Mobile.java
  10. 25 0
      tr-framework/src/main/java/cn/tr/core/annotation/MobileValidator.java
  11. 18 0
      tr-framework/src/main/java/cn/tr/core/annotation/TenantIgnore.java
  12. 37 0
      tr-framework/src/main/java/cn/tr/core/constant/MybatisConstant.java
  13. 73 0
      tr-framework/src/main/java/cn/tr/core/context/SecurityContextHolder.java
  14. 15 0
      tr-framework/src/main/java/cn/tr/core/enums/CreateEnum.java
  15. 15 0
      tr-framework/src/main/java/cn/tr/core/enums/IEnum.java
  16. 17 0
      tr-framework/src/main/java/cn/tr/core/enums/UserTypeEnum.java
  17. 53 0
      tr-framework/src/main/java/cn/tr/core/enums/WebFilterOrderEnum.java
  18. 23 0
      tr-framework/src/main/java/cn/tr/core/exception/BaseCode.java
  19. 61 0
      tr-framework/src/main/java/cn/tr/core/exception/ServiceException.java
  20. 226 0
      tr-framework/src/main/java/cn/tr/core/exception/TRExcCode.java
  21. 96 0
      tr-framework/src/main/java/cn/tr/core/pojo/CommonResult.java
  22. 22 0
      tr-framework/src/main/java/cn/tr/core/pojo/KeyValue.java
  23. 28 0
      tr-framework/src/main/java/cn/tr/core/pojo/LoginResult.java
  24. 33 0
      tr-framework/src/main/java/cn/tr/core/pojo/PageDomain.java
  25. 41 0
      tr-framework/src/main/java/cn/tr/core/pojo/PageInfo.java
  26. 50 0
      tr-framework/src/main/java/cn/tr/core/pojo/TableDataInfo.java
  27. 55 0
      tr-framework/src/main/java/cn/tr/core/strategy/DeptDataPermissionStrategy.java
  28. 37 0
      tr-framework/src/main/java/cn/tr/core/strategy/ExceptionStrategy.java
  29. 47 0
      tr-framework/src/main/java/cn/tr/core/strategy/LoginUserStrategy.java
  30. 44 0
      tr-framework/src/main/java/cn/tr/core/strategy/PageStrategy.java
  31. 18 0
      tr-framework/src/main/java/cn/tr/core/strategy/auth/ILoginUser.java
  32. 26 0
      tr-framework/src/main/java/cn/tr/core/tenant/TenantConstant.java
  33. 51 0
      tr-framework/src/main/java/cn/tr/core/tenant/TenantContextHolder.java
  34. 77 0
      tr-framework/src/main/java/cn/tr/core/tenant/TenantUtils.java
  35. 49 0
      tr-framework/src/main/java/cn/tr/core/tree/TreeNode.java
  36. 212 0
      tr-framework/src/main/java/cn/tr/core/utils/AvatarUtil.java
  37. 46 0
      tr-framework/src/main/java/cn/tr/core/utils/BizFirstCateUtil.java
  38. 46 0
      tr-framework/src/main/java/cn/tr/core/utils/BizSecondCateUtil.java
  39. 126 0
      tr-framework/src/main/java/cn/tr/core/utils/CommonNetWorkInfoUtil.java
  40. 58 0
      tr-framework/src/main/java/cn/tr/core/utils/IpUtil.java
  41. 146 0
      tr-framework/src/main/java/cn/tr/core/utils/JsonUtils.java
  42. 111 0
      tr-framework/src/main/java/cn/tr/core/utils/MediaTypeUtils.java
  43. 45 0
      tr-framework/src/main/java/cn/tr/core/utils/MenuUtil.java
  44. 164 0
      tr-framework/src/main/java/cn/tr/core/utils/Proxy.java
  45. 46 0
      tr-framework/src/main/java/cn/tr/core/utils/PswUtils.java
  46. 45 0
      tr-framework/src/main/java/cn/tr/core/utils/RoleUtil.java
  47. 149 0
      tr-framework/src/main/java/cn/tr/core/utils/ServletUtils.java
  48. 37 0
      tr-framework/src/main/java/cn/tr/core/utils/SpElUtil.java
  49. 44 0
      tr-framework/src/main/java/cn/tr/core/utils/SysOrgUtil.java
  50. 155 0
      tr-framework/src/main/java/cn/tr/core/utils/TreeUtil.java
  51. 67 0
      tr-framework/src/main/java/cn/tr/core/utils/UserUtil.java
  52. 53 0
      tr-framework/src/main/java/cn/tr/core/utils/ValidationUtils.java
  53. 15 0
      tr-framework/src/main/java/cn/tr/core/validation/Insert.java
  54. 15 0
      tr-framework/src/main/java/cn/tr/core/validation/Update.java
  55. BIN
      tr-framework/src/main/resources/avatarFont.ttf
  56. BIN
      tr-framework/src/main/resources/ip2region.xdb
  57. 22 0
      tr-modules-api/pom.xml
  58. 31 0
      tr-modules-api/tr-module-export-api/pom.xml
  59. 92 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/api/ExcelApi.java
  60. 28 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/constant/ExcelConstant.java
  61. 29 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/annotation/ExcelCellMerge.java
  62. 36 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/annotation/ExcelPropertySupport.java
  63. 44 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/annotation/ExcelSelect.java
  64. 73 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/bean/DynamicFieldDTO.java
  65. 37 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/bean/ExportResult.java
  66. 27 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/converter/BooleanConverter.java
  67. 54 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/converter/DictCascadeSelectConverter.java
  68. 49 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/converter/SysUserCascadeSelectConverter.java
  69. 20 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/enums/ExportType.java
  70. 62 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/enums/InputBoxTypeInterface.java
  71. 179 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/handler/AbstractCascadeSelectConverter.java
  72. 65 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/handler/AbstractDataHandler.java
  73. 20 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/handler/DataHandler.java
  74. 27 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/handler/NoneCascadeSelectConverter.java
  75. 26 0
      tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/register/ExportSampleRegister.java
  76. 14 0
      tr-modules-api/tr-module-quartz-api/pom.xml
  77. 22 0
      tr-modules-api/tr-module-quartz-api/src/main/java/cn/tr/module/api/quartz/JointJobQuartzApi.java
  78. 34 0
      tr-modules-api/tr-module-quartz-api/src/main/java/cn/tr/module/api/quartz/dto/JointJobQuartzDTO.java
  79. 22 0
      tr-modules-api/tr-module-system-api/pom.xml
  80. 32 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/dict/SysDictApi.java
  81. 14 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/dict/SysFirstCateApi.java
  82. 45 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/log/annotation/OperateLog.java
  83. 64 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/log/bo/OperateLogBO.java
  84. 30 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/log/enums/LoginType.java
  85. 14 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/notice/SysNoticeApi.java
  86. 45 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/notice/SysNoticeBO.java
  87. 41 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/storage/StoragePojo.java
  88. 53 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/storage/SysStorageApi.java
  89. 13 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/tenant/SysTenantApi.java
  90. 36 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/tenant/SysTenantPojo.java
  91. 17 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/CbbMenuDTO.java
  92. 29 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysMenuApi.java
  93. 34 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysOrgApi.java
  94. 43 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysOrgDTO.java
  95. 18 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysRoleApi.java
  96. 17 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysRolesDTO.java
  97. 33 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysUserApi.java
  98. 22 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysUserSmallDTO.java
  99. 23 0
      tr-modules/pom.xml
  100. 75 0
      tr-modules/tr-module-export/pom.xml

+ 0 - 0
init.patch


+ 108 - 0
pom.xml

@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>cn.tr</groupId>
+    <artifactId>tr-footstone</artifactId>
+    <version>${revision}</version>
+    <packaging>pom</packaging>
+    <description>驼人通用型管理框架</description>
+
+    <modules>
+        <!--依赖包-->
+        <module>tr-dependencies</module>
+        <!--插件-->
+        <module>tr-plugins</module>
+        <!--一些框架公共服务-->
+        <module>tr-framework</module>
+        <!--系统、业务服务模块-->
+        <module>tr-modules</module>
+        <!--系统、业务服务对外提供api模块-->
+        <module>tr-modules-api</module>
+        <module>tr-test</module>
+    </modules>
+
+    <properties>
+        <revision>0.0.9</revision>
+        <java.version>1.8</java.version>
+        <maven.compiler.source>${java.version}</maven.compiler.source>
+        <maven.compiler.target>${java.version}</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <versions-maven-plugin.version>2.7</versions-maven-plugin.version>
+        <maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
+        <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
+        <skipTests>true</skipTests>
+        <lombok.version>1.18.24</lombok.version>
+        <mapstruct.version>1.5.3.Final</mapstruct.version>
+        <spring-boot.version>2.7.8</spring-boot.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-dependencies</artifactId>
+                <version>${revision}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+
+        </dependencies>
+    </dependencyManagement>
+
+
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <!-- maven-surefire-plugin 插件,用于运行单元测试。 -->
+                <!-- 注意,需要使用 3.0.X+,因为要支持 Junit 5 版本 -->
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>${maven-surefire-plugin.version}</version>
+                </plugin>
+                <!-- maven-compiler-plugin 插件,解决 Lombok + MapStruct 组合 -->
+                <!-- https://stackoverflow.com/questions/33483697/re-run-spring-boot-configuration-annotation-processor-to-update-generated-metada -->
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>${maven-compiler-plugin.version}</version>
+                    <configuration>
+                        <annotationProcessorPaths>
+                            <path>
+                                <groupId>org.springframework.boot</groupId>
+                                <artifactId>spring-boot-configuration-processor</artifactId>
+                                <version>${spring-boot.version}</version>
+                            </path>
+                            <path>
+                                <groupId>org.projectlombok</groupId>
+                                <artifactId>lombok</artifactId>
+                                <version>${lombok.version}</version>
+                            </path>
+                            <path>
+                                <groupId>org.mapstruct</groupId>
+                                <artifactId>mapstruct-processor</artifactId>
+                                <version>${mapstruct.version}</version>
+                            </path>
+                        </annotationProcessorPaths>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+
+    <!-- 使用 aliyun 的 Maven 源,提升下载速度 -->
+    <repositories>
+        <repository>
+            <id>aliyunmaven</id>
+            <name>aliyun</name>
+            <url>https://maven.aliyun.com/repository/public</url>
+        </repository>
+    </repositories>
+
+</project>

File diff suppressed because it is too large
+ 73135 - 0
sql/data.sql


+ 845 - 0
sql/table.sql

@@ -0,0 +1,845 @@
+/*
+ Navicat Premium Data Transfer
+
+ Source Server         : TR 8.0
+ Source Server Type    : MySQL
+ Source Server Version : 80023
+ Source Host           : 192.168.100.32:3307
+ Source Schema         : tr-footstone
+
+ Target Server Type    : MySQL
+ Target Server Version : 80023
+ File Encoding         : 65001
+
+ Date: 13/06/2023 18:18:25
+*/
+
+SET NAMES utf8mb4;
+SET FOREIGN_KEY_CHECKS = 0;
+
+-- ----------------------------
+-- Table structure for gen_basic
+-- ----------------------------
+DROP TABLE IF EXISTS `gen_basic`;
+CREATE TABLE `gen_basic`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `db_table` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `db_table_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `table_prefix` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `generate_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `front_module_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `backend_module_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `menu_pid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `function_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `bus_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `class_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `form_layout` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `grid_whether` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `sort_code` int(0) NULL DEFAULT NULL,
+  `package_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `author_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人',
+  `module` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '代码生成表配置' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for gen_config
+-- ----------------------------
+DROP TABLE IF EXISTS `gen_config`;
+CREATE TABLE `gen_config`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `basic_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `is_table_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `field_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `field_remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `field_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `field_java_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `effect_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `dict_type_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `whether_table` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `whether_retract` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `whether_add_update` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `whether_required` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `query_whether` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `query_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `sort_code` int(0) NULL DEFAULT NULL,
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人',
+  `module_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '代码生成字段配置' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for qrtz_blob_triggers
+-- ----------------------------
+DROP TABLE IF EXISTS `qrtz_blob_triggers`;
+CREATE TABLE `qrtz_blob_triggers`  (
+  `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_group` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `blob_data` blob NULL,
+  PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
+  INDEX `sched_name`(`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
+  CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for qrtz_calendars
+-- ----------------------------
+DROP TABLE IF EXISTS `qrtz_calendars`;
+CREATE TABLE `qrtz_calendars`  (
+  `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `calendar_name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `calendar` blob NOT NULL,
+  PRIMARY KEY (`sched_name`, `calendar_name`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for qrtz_cron_triggers
+-- ----------------------------
+DROP TABLE IF EXISTS `qrtz_cron_triggers`;
+CREATE TABLE `qrtz_cron_triggers`  (
+  `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_group` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `cron_expression` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `time_zone_id` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
+  CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for qrtz_fired_triggers
+-- ----------------------------
+DROP TABLE IF EXISTS `qrtz_fired_triggers`;
+CREATE TABLE `qrtz_fired_triggers`  (
+  `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `entry_id` varchar(95) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_group` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `instance_name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `fired_time` bigint(0) NOT NULL,
+  `sched_time` bigint(0) NOT NULL,
+  `priority` int(0) NOT NULL,
+  `state` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `job_name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `job_group` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `is_nonconcurrent` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `requests_recovery` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  PRIMARY KEY (`sched_name`, `entry_id`) USING BTREE,
+  INDEX `idx_qrtz_ft_trig_inst_name`(`sched_name`, `instance_name`) USING BTREE,
+  INDEX `idx_qrtz_ft_inst_job_req_rcvry`(`sched_name`, `instance_name`, `requests_recovery`) USING BTREE,
+  INDEX `idx_qrtz_ft_j_g`(`sched_name`, `job_name`, `job_group`) USING BTREE,
+  INDEX `idx_qrtz_ft_jg`(`sched_name`, `job_group`) USING BTREE,
+  INDEX `idx_qrtz_ft_t_g`(`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
+  INDEX `idx_qrtz_ft_tg`(`sched_name`, `trigger_group`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for qrtz_job_details
+-- ----------------------------
+DROP TABLE IF EXISTS `qrtz_job_details`;
+CREATE TABLE `qrtz_job_details`  (
+  `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `job_name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `job_group` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `description` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `job_class_name` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `is_durable` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `is_nonconcurrent` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `is_update_data` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `requests_recovery` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `job_data` blob NULL,
+  PRIMARY KEY (`sched_name`, `job_name`, `job_group`) USING BTREE,
+  INDEX `idx_qrtz_j_req_recovery`(`sched_name`, `requests_recovery`) USING BTREE,
+  INDEX `idx_qrtz_j_grp`(`sched_name`, `job_group`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for qrtz_locks
+-- ----------------------------
+DROP TABLE IF EXISTS `qrtz_locks`;
+CREATE TABLE `qrtz_locks`  (
+  `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `lock_name` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  PRIMARY KEY (`sched_name`, `lock_name`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for qrtz_paused_trigger_grps
+-- ----------------------------
+DROP TABLE IF EXISTS `qrtz_paused_trigger_grps`;
+CREATE TABLE `qrtz_paused_trigger_grps`  (
+  `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_group` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  PRIMARY KEY (`sched_name`, `trigger_group`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for qrtz_scheduler_state
+-- ----------------------------
+DROP TABLE IF EXISTS `qrtz_scheduler_state`;
+CREATE TABLE `qrtz_scheduler_state`  (
+  `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `instance_name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `last_checkin_time` bigint(0) NOT NULL,
+  `checkin_interval` bigint(0) NOT NULL,
+  PRIMARY KEY (`sched_name`, `instance_name`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for qrtz_simple_triggers
+-- ----------------------------
+DROP TABLE IF EXISTS `qrtz_simple_triggers`;
+CREATE TABLE `qrtz_simple_triggers`  (
+  `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_group` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `repeat_count` bigint(0) NOT NULL,
+  `repeat_interval` bigint(0) NOT NULL,
+  `times_triggered` bigint(0) NOT NULL,
+  PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
+  CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for qrtz_simprop_triggers
+-- ----------------------------
+DROP TABLE IF EXISTS `qrtz_simprop_triggers`;
+CREATE TABLE `qrtz_simprop_triggers`  (
+  `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_group` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `str_prop_1` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `str_prop_2` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `str_prop_3` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `int_prop_1` int(0) NULL DEFAULT NULL,
+  `int_prop_2` int(0) NULL DEFAULT NULL,
+  `long_prop_1` bigint(0) NULL DEFAULT NULL,
+  `long_prop_2` bigint(0) NULL DEFAULT NULL,
+  `dec_prop_1` decimal(13, 4) NULL DEFAULT NULL,
+  `dec_prop_2` decimal(13, 4) NULL DEFAULT NULL,
+  `bool_prop_1` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `bool_prop_2` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
+  CONSTRAINT `qrtz_simprop_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for qrtz_triggers
+-- ----------------------------
+DROP TABLE IF EXISTS `qrtz_triggers`;
+CREATE TABLE `qrtz_triggers`  (
+  `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_group` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `job_name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `job_group` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `description` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `next_fire_time` bigint(0) NULL DEFAULT NULL,
+  `prev_fire_time` bigint(0) NULL DEFAULT NULL,
+  `priority` int(0) NULL DEFAULT NULL,
+  `trigger_state` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `trigger_type` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `start_time` bigint(0) NOT NULL,
+  `end_time` bigint(0) NULL DEFAULT NULL,
+  `calendar_name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  `misfire_instr` smallint(0) NULL DEFAULT NULL,
+  `job_data` blob NULL,
+  PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
+  INDEX `idx_qrtz_t_j`(`sched_name`, `job_name`, `job_group`) USING BTREE,
+  INDEX `idx_qrtz_t_jg`(`sched_name`, `job_group`) USING BTREE,
+  INDEX `idx_qrtz_t_c`(`sched_name`, `calendar_name`) USING BTREE,
+  INDEX `idx_qrtz_t_g`(`sched_name`, `trigger_group`) USING BTREE,
+  INDEX `idx_qrtz_t_state`(`sched_name`, `trigger_state`) USING BTREE,
+  INDEX `idx_qrtz_t_n_state`(`sched_name`, `trigger_name`, `trigger_group`, `trigger_state`) USING BTREE,
+  INDEX `idx_qrtz_t_n_g_state`(`sched_name`, `trigger_group`, `trigger_state`) USING BTREE,
+  INDEX `idx_qrtz_t_next_fire_time`(`sched_name`, `next_fire_time`) USING BTREE,
+  INDEX `idx_qrtz_t_nft_st`(`sched_name`, `trigger_state`, `next_fire_time`) USING BTREE,
+  INDEX `idx_qrtz_t_nft_misfire`(`sched_name`, `misfire_instr`, `next_fire_time`) USING BTREE,
+  INDEX `idx_qrtz_t_nft_st_misfire`(`sched_name`, `misfire_instr`, `next_fire_time`, `trigger_state`) USING BTREE,
+  INDEX `idx_qrtz_t_nft_st_misfire_grp`(`sched_name`, `misfire_instr`, `next_fire_time`, `trigger_group`, `trigger_state`) USING BTREE,
+  CONSTRAINT `qrtz_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `job_name`, `job_group`) REFERENCES `qrtz_job_details` (`sched_name`, `job_name`, `job_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_constant_config
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_constant_config`;
+CREATE TABLE `sys_constant_config`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '常量名称',
+  `type` tinyint(0) NULL DEFAULT NULL COMMENT '常量类型',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '常量编码',
+  `cate_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '目录id',
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '记录创建时间',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '记录创建人',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '记录更新时间',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '记录更新人',
+  `tenant_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统常量表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_constant_menu
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_constant_menu`;
+CREATE TABLE `sys_constant_menu`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '主键',
+  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '目录类型',
+  `code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '目录编码',
+  `name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '菜单名称',
+  `parent_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '上级菜单',
+  `sort` int(0) NULL DEFAULT NULL COMMENT '排序',
+  `remarks` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `create_time` datetime(3) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(3) NULL DEFAULT NULL COMMENT '更新时间',
+  `disable` tinyint(1) NOT NULL COMMENT '是否可用',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
+  `tenant_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '租户id',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '常量菜单表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_dict
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_dict`;
+CREATE TABLE `sys_dict`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+  `dict_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '字典码',
+  `dict_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '字典名称',
+  `dict_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '字典类型  sys:系统字典 biz:业务字典',
+  `color` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '字典颜色',
+  `parent_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '父级id',
+  `sort` int(0) NULL DEFAULT NULL COMMENT '排序',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
+  PRIMARY KEY (`id`) USING BTREE,
+  INDEX `dict_code`(`dict_code`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字典' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_dict_item
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_dict_item`;
+CREATE TABLE `sys_dict_item`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '主键',
+  `dict_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '字典id',
+  `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '字典值',
+  `label` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '字典显示值',
+  `sort` int(0) NOT NULL COMMENT '排序',
+  `color` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '颜色',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字典项' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_export_row
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_export_row`;
+CREATE TABLE `sys_export_row`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+  `row_index` int(0) NULL DEFAULT NULL COMMENT '行号',
+  `success` tinyint(1) NULL DEFAULT NULL COMMENT '是否成功',
+  `error_msg` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '错误信息',
+  `sheet_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '脚本记录id',
+  `max_column_index` int(0) NULL DEFAULT NULL COMMENT '最大列数',
+  `create_time` datetime(0) NULL DEFAULT NULL,
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
+  `update_time` datetime(0) NULL DEFAULT NULL,
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
+  PRIMARY KEY (`id`) USING BTREE,
+  UNIQUE INDEX `row_index`(`row_index`, `sheet_id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '导入行记录' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_export_sheet
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_export_sheet`;
+CREATE TABLE `sys_export_sheet`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+  `file_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件名称',
+  `import_file_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '导入文件id',
+  `export_file_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '导出文件id',
+  `total_count` int(0) NOT NULL COMMENT '解析总行数',
+  `success_count` int(0) NOT NULL COMMENT '成功行数',
+  `fail_count` int(0) NOT NULL COMMENT '失败行数',
+  `start_time` datetime(0) NOT NULL COMMENT '开始处理时间',
+  `column_count` int(0) NOT NULL COMMENT '列数',
+  `end_time` datetime(0) NULL DEFAULT NULL COMMENT '结束处理时间',
+  `status` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '导入状态',
+  `create_time` datetime(0) NULL DEFAULT NULL,
+  `update_time` datetime(0) NULL DEFAULT NULL,
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '导入工作簿记录表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_job
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_job`;
+CREATE TABLE `sys_job`  (
+  `job_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '任务ID',
+  `job_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '任务名称',
+  `job_group` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'DEFAULT' COMMENT '任务组名',
+  `invoke_target` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '调用目标字符串',
+  `cron_expression` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT 'cron执行表达式',
+  `misfire_policy` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '3' COMMENT '计划执行错误策略(1立即执行 2执行一次 3放弃执行)',
+  `concurrent` tinyint(1) NULL DEFAULT 1 COMMENT '是否并发执行(0允许 1禁止)',
+  `status` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '0' COMMENT '状态(0正常 1暂停)',
+  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '备注信息',
+  PRIMARY KEY (`job_id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '定时任务调度表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_job_log
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_job_log`;
+CREATE TABLE `sys_job_log`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '任务日志ID',
+  `job_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '任务名称',
+  `job_group` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '任务组名',
+  `invoke_target` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '调用目标字符串',
+  `job_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '日志信息',
+  `status` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '0' COMMENT '执行状态(1正常 0失败)',
+  `exception_info` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '异常信息',
+  `start_time` datetime(0) NULL DEFAULT NULL COMMENT '执行开始时间',
+  `end_time` datetime(0) NULL DEFAULT NULL COMMENT '执行结束时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '定时任务调度日志表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_log
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_log`;
+CREATE TABLE `sys_log`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+  `op_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '操作名称',
+  `user_ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户IP',
+  `login_location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户地址',
+  `java_class` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '类名称',
+  `java_method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '方法名称',
+  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '操作日志类型',
+  `op_time` datetime(0) NULL DEFAULT NULL COMMENT '操作时间',
+  `duration` int(0) NULL DEFAULT NULL COMMENT '执行时长',
+  `op_user` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '操作用户',
+  `request_args` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '请求参数',
+  `user_agent` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '浏览器UserAgent',
+  `result_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '响应结果',
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
+  `tenant_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户id',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统日志' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_menu
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_menu`;
+CREATE TABLE `sys_menu`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '主键',
+  `menu_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '菜单类型 dir目录;menu菜单;button按钮',
+  `name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '菜单名称',
+  `parent_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '上级菜单',
+  `route_path` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '路由地址',
+  `component` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '组件路径',
+  `permission` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '权限标识',
+  `icon` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '图标',
+  `keepalive` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '是否缓存 0缓存;1不缓存',
+  `link_external` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '是否外链 0是;1否',
+  `visible` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '是否显示 0显示;1隐藏',
+  `frame` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '是否内嵌 0内嵌;1不内嵌',
+  `link_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '外部链接',
+  `sort` int(0) NULL DEFAULT NULL COMMENT '排序',
+  `remarks` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `status` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '0' COMMENT '状态 0正常;1停用',
+  `create_time` datetime(3) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(3) NULL DEFAULT NULL COMMENT '更新时间',
+  `disable` tinyint(1) NULL DEFAULT NULL COMMENT '是否可用',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '菜单表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_numbering_strategy
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_numbering_strategy`;
+CREATE TABLE `sys_numbering_strategy`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '主键',
+  `code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '策略编码',
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '策略名称',
+  `prefix` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '编号前缀',
+  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '生成模式',
+  `next_num` int(0) NULL DEFAULT NULL COMMENT '下一个编号',
+  `limit_len` int(0) NOT NULL COMMENT '流水号长度',
+  `fill_char` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '填充字符',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
+  `create_time` datetime(0) NULL DEFAULT NULL,
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
+  `update_time` datetime(0) NULL DEFAULT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '编号策略' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_org
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_org`;
+CREATE TABLE `sys_org`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '主键',
+  `parent_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '父级id 顶级目录的父级id为0',
+  `sort` int(0) NULL DEFAULT NULL COMMENT '排序',
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '部门名称',
+  `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '部门电话',
+  `leader_user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '部门领导人',
+  `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '部门邮箱',
+  `disable` tinyint(1) NULL DEFAULT NULL COMMENT '状态 0正常;1停用',
+  `tenant_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户id',
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人'
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '组织' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_portal
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_portal`;
+CREATE TABLE `sys_portal`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '门户id',
+  `code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '门户编码',
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '门户名称',
+  `index_menu_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '门户首页菜单id',
+  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '门户类型',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `tenant_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户id',
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
+  PRIMARY KEY (`id`) USING BTREE,
+  UNIQUE INDEX `code`(`code`, `tenant_id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '门户' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_portal_menu
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_portal_menu`;
+CREATE TABLE `sys_portal_menu`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+  `portal_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '门户id',
+  `menu_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '菜单id',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '门户-菜单关联表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_position
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_position`;
+CREATE TABLE `sys_position`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'id',
+  `pos_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '岗位名称',
+  `pos_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '岗位编码',
+  `sort` int(0) NULL DEFAULT NULL COMMENT '排序码',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
+  `disable` tinyint(1) NULL DEFAULT NULL COMMENT '状态',
+  `tenant_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户id',
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人'
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '岗位表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_role
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_role`;
+CREATE TABLE `sys_role`  (
+  `id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '主键',
+  `code` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色编码',
+  `name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色名称',
+  `data_scope` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '数据范围 1全部数据权限;2自定义数据权限;3本部门数据权限;4本部门及以下数据权限',
+  `remark` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `sort` int(0) NULL DEFAULT NULL COMMENT '排序',
+  `status` tinyint(1) NULL DEFAULT 0 COMMENT '状态 0正常;1停用',
+  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色类型 sys、系统角色 custom、自定义角色',
+  `disable` tinyint(1) NULL DEFAULT NULL COMMENT '是否启用 0、启用 1、关闭',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
+  `tenant_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
+  `update_time` datetime(3) NULL DEFAULT NULL COMMENT '更新时间',
+  `create_time` datetime(3) NULL DEFAULT NULL COMMENT '创建时间',
+  `org_ids` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '数据组织id',
+  PRIMARY KEY (`id`) USING BTREE,
+  UNIQUE INDEX `role_code`(`code`, `tenant_id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_role_menu
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_role_menu`;
+CREATE TABLE `sys_role_menu`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+  `role_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '角色id',
+  `menu_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '菜单id',
+  PRIMARY KEY (`id`) USING BTREE,
+  UNIQUE INDEX `role_id`(`role_id`, `menu_id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色-菜单关联表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_sms_channel
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_sms_channel`;
+CREATE TABLE `sys_sms_channel`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '主键',
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '渠道名称',
+  `signature` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '短信签名',
+  `type` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '渠道类型',
+  `api_key` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '短信 API 的账号',
+  `api_secret` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '短信 API 的秘钥',
+  `callback_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '短信发送回调 URL',
+  `disable` tinyint(1) NOT NULL COMMENT '是否启用',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+  `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
+  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+  `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信渠道' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_sms_log
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_sms_log`;
+CREATE TABLE `sys_sms_log`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+  `channel_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '渠道id',
+  `template_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '模板id',
+  `template_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '模板编码',
+  `template_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '模板类型',
+  `template_params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '模板参数',
+  `template_content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '模板内容',
+  `api_template_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '短信API的模板编号',
+  `mobile` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '手机号',
+  `user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '接收用户id',
+  `user_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户类型',
+  `send_status` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发送状态',
+  `send_time` datetime(0) NULL DEFAULT NULL COMMENT '发送时间',
+  `send_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发送结果编码',
+  `send_msg` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '发送消息',
+  `api_send_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'API 结果编号',
+  `api_send_msg` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'API 错误信息',
+  `api_request_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'API 请求编号',
+  `api_serial_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'API 返回序号',
+  `receive_status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '接收状态',
+  `receive_time` datetime(0) NULL DEFAULT NULL COMMENT '接收时间',
+  `api_receive_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'API 接收结果编码',
+  `api_receive_msg` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'API 接收结果提示',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
+  `create_time` datetime(0) NULL DEFAULT NULL,
+  `update_time` datetime(0) NULL DEFAULT NULL,
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '短信日志' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_sms_temp
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_sms_temp`;
+CREATE TABLE `sys_sms_temp`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '主键',
+  `channel_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '短信渠道id',
+  `create_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建类型',
+  `temp_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板类型',
+  `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '模板内容',
+  `code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板编码',
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板名称',
+  `api_temp_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '短信 API 模板编号',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+  `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
+  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+  `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE,
+  UNIQUE INDEX `code`(`code`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信模板' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_storage_config
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_storage_config`;
+CREATE TABLE `sys_storage_config`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件配置主键',
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '文件配置名称',
+  `master` tinyint(1) NULL DEFAULT NULL COMMENT '主配置',
+  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '存储器类型',
+  `config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '文件配置',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '文件存储配置' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_storage_record
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_storage_record`;
+CREATE TABLE `sys_storage_record`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '存储记录id',
+  `biz_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件业务名称',
+  `real_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件真实名称',
+  `absolute_path` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '绝对路径',
+  `config_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '存储配置id',
+  `size` int(0) NOT NULL COMMENT '文件大小',
+  `suffix` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件后缀',
+  `cate_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '所属目录id',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  `tenant_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '存储记录' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_tenant
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_tenant`;
+CREATE TABLE `sys_tenant`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '租户id',
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户名称',
+  `contract_user` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户联系人',
+  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户类型',
+  `contact_mobile` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户联系人电话',
+  `tenant_user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户所属用户id',
+  `package_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户套餐id',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户备注',
+  `disable` tinyint(1) NULL DEFAULT NULL COMMENT '租户状态',
+  `deleted` tinyint(1) NULL DEFAULT 0 COMMENT '删除标记',
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '租户表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_tenant_package
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_tenant_package`;
+CREATE TABLE `sys_tenant_package`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '套餐id',
+  `package_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '套餐编码',
+  `package_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '套餐名称',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '套餐备注',
+  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '套餐类型',
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
+  PRIMARY KEY (`id`) USING BTREE,
+  UNIQUE INDEX `package_code`(`package_code`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '租户套餐' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_tenant_package_menu
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_tenant_package_menu`;
+CREATE TABLE `sys_tenant_package_menu`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+  `package_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '套餐id',
+  `menu_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '菜单id',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '租户套餐-菜单关联表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_user
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_user`;
+CREATE TABLE `sys_user`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户主键Id',
+  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户名',
+  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密码',
+  `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '昵称',
+  `gender` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '性别',
+  `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '手机号',
+  `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '邮箱地址',
+  `birthday` datetime(0) NULL DEFAULT NULL COMMENT '出生日期',
+  `post_ids` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '岗位id',
+  `org_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '部门id',
+  `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户头像',
+  `status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户状态',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+  `tenant_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户id',
+  `deleted` tinyint(1) NULL DEFAULT 0 COMMENT '删除标记',
+  `last_login_ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '最后登录IP',
+  `last_login_address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '最后登录地址',
+  `last_login_date` datetime(0) NULL DEFAULT NULL COMMENT '最后登录时间',
+  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
+  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
+  PRIMARY KEY (`id`) USING BTREE,
+  INDEX `username`(`username`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_user_portal
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_user_portal`;
+CREATE TABLE `sys_user_portal`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+  `portal_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '门户id',
+  `user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户id',
+  `is_default` tinyint(1) NULL DEFAULT NULL COMMENT '是否为默认门户',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户-门户关联表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_user_position
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_user_position`;
+CREATE TABLE `sys_user_position`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+  `user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
+  `position_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户-岗位表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_user_role
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_user_role`;
+CREATE TABLE `sys_user_role`  (
+  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+  `role_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '角色id',
+  `user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户id',
+  PRIMARY KEY (`id`) USING BTREE,
+  UNIQUE INDEX `role_id`(`role_id`, `user_id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户-角色关联表' ROW_FORMAT = Dynamic;
+
+SET FOREIGN_KEY_CHECKS = 1;

+ 557 - 0
tr-dependencies/pom.xml

@@ -0,0 +1,557 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+
+    <groupId>cn.tr</groupId>
+    <artifactId>tr-dependencies</artifactId>
+    <version>${revision}</version>
+    <packaging>pom</packaging>
+    <modelVersion>4.0.0</modelVersion>
+
+
+    <name>${project.artifactId}</name>
+    <description>基础 bom 文件,管理整个项目的依赖版本</description>
+
+    <properties>
+        <skip>true</skip>
+        <revision>0.0.9</revision>
+
+        <spring-boot.version>2.7.8</spring-boot.version>
+        <hutool.version>5.8.12</hutool.version>
+        <mapstruct.version>1.5.3.Final</mapstruct.version>
+        <lombok.version>1.18.20</lombok.version>
+        <ip2region.version>2.6.6</ip2region.version>
+
+        <!--数据库-->
+        <mybatis-plus.version>3.5.3.1</mybatis-plus.version>
+        <mybatis-plus-generator.version>3.5.3.1</mybatis-plus-generator.version>
+        <mysql-connector.version>8.0.27</mysql-connector.version>
+        <druid.version>1.2.8</druid.version>
+        <pagehelper.boot.version>1.4.5</pagehelper.boot.version>
+        <!--权限相关-->
+        <satoken.version>1.34.0</satoken.version>
+        <!--TEST测试相关-->
+        <podam.version>7.2.11.RELEASE</podam.version>
+        <mockito-inline.version>4.11.0</mockito-inline.version>
+
+        <caffeine.version>2.6.2</caffeine.version>
+        <!--redission-->
+        <redisson.version>3.17.7</redisson.version>
+
+        <!--文件存储-->
+        <aliyun-oss.version>3.15.0</aliyun-oss.version>
+        <minio.version>8.5.1</minio.version>
+        <qiniu.version>7.9.5</qiniu.version>
+        <!--文件导入导出-->
+        <poi.version>5.2.2</poi.version>
+        <!--websocket-->
+        <tio-websocket.version>3.6.0.v20200315-RELEASE</tio-websocket.version>
+
+        <!--字节码增强-->
+        <javassist.version>3.25.0-GA</javassist.version>
+
+        <!--flink版本-->
+        <flink.version>1.16.1</flink.version>
+
+        <!--在线文档-->
+        <knife4j.verison>4.0.0</knife4j.verison>
+
+        <!--阿里云短信-->
+        <ali.sms.version>2.0.23</ali.sms.version>
+
+        <!--渲染模板引擎-->
+        <beetl.version>1.2.40.Beetl.RELEASE</beetl.version>
+
+        <!--硬件信息-->
+        <oshi.core.version>6.2.2</oshi.core.version>
+
+        <stream-rabbit.version>3.2.6</stream-rabbit.version>
+
+        <bus-rabbit.version>3.1.2</bus-rabbit.version>
+
+        <easy-captcha.version>1.6.2</easy-captcha.version>
+
+        <easy-excel.version>3.3.1</easy-excel.version>
+
+        <flyway.version>8.5.13</flyway.version>
+
+        <httpclient.version>4.5.14</httpclient.version>
+
+        <commons-lang3.version>3.12.0</commons-lang3.version>
+
+        <commons-cli.version>1.5.0</commons-cli.version>
+
+        <postgresql.version>42.7.3</postgresql.version>
+
+        <es.version>8.1.0</es.version>
+
+        <jakarta-json.version>2.0.1</jakarta-json.version>
+    </properties>
+
+
+    <dependencyManagement>
+        <dependencies>
+            <!--springboot pom文件-->
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring-boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+
+            <!--redis客户端-->
+            <dependency>
+                <groupId>org.redisson</groupId>
+                <artifactId>redisson-spring-boot-starter</artifactId>
+                <version>${redisson.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.springframework.boot</groupId>
+                        <artifactId>spring-boot-starter-logging</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+
+            <!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
+            <dependency>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-spring-boot-starter</artifactId>
+                <version>${satoken.version}</version>
+            </dependency>
+
+            <!-- Sa-Token 整合 Redis (使用 jdk 默认序列化方式) -->
+            <dependency>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-dao-redis</artifactId>
+                <version>${satoken.version}</version>
+            </dependency>
+
+            <!--使用mapstruct转换-->
+            <dependency>
+                <groupId>org.mapstruct</groupId>
+                <artifactId>mapstruct</artifactId>
+                <version>${mapstruct.version}</version>
+            </dependency>
+
+            <!--hutool工具类包-->
+            <dependency>
+                <groupId>cn.hutool</groupId>
+                <artifactId>hutool-all</artifactId>
+                <version>${hutool.version}</version>
+            </dependency>
+
+            <!--lombok-->
+            <dependency>
+                <groupId>org.projectlombok</groupId>
+                <artifactId>lombok</artifactId>
+                <version>${lombok.version}</version>
+            </dependency>
+
+            <!-- ip地址查询 -->
+            <dependency>
+                <groupId>org.lionsoul</groupId>
+                <artifactId>ip2region</artifactId>
+                <version>${ip2region.version}</version>
+            </dependency>
+
+            <!--mybatis-plus-->
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-boot-starter</artifactId>
+                <version>${mybatis-plus.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-extension</artifactId>
+                <version>${mybatis-plus.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 -->
+                <version>${mybatis-plus-generator.version}</version>
+            </dependency>
+            <!--mybatis-plus-->
+
+            <!--阿里数据库连接池 -->
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>druid-spring-boot-starter</artifactId>
+                <version>${druid.version}</version>
+            </dependency>
+
+            <!-- pagehelper 分页插件 -->
+            <dependency>
+                <groupId>com.github.pagehelper</groupId>
+                <artifactId>pagehelper-spring-boot-starter</artifactId>
+                <version>${pagehelper.boot.version}</version>
+            </dependency>
+
+
+            <!--Test测试自动填充对象属性-->
+            <dependency>
+                <groupId>uk.co.jemos.podam</groupId>
+                <artifactId>podam</artifactId>
+                <version>${podam.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.mockito</groupId>
+                <artifactId>mockito-inline</artifactId>
+                <version>${mockito-inline.version}</version> <!-- 支持 Mockito 的 final 类与 static 方法的 mock -->
+            </dependency>
+
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-starter-test</artifactId>
+                <version>${spring-boot.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <artifactId>asm</artifactId>
+                        <groupId>org.ow2.asm</groupId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>org.mockito</groupId>
+                        <artifactId>mockito-core</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+
+            <!--缓存-->
+            <dependency>
+                <groupId>com.github.ben-manes.caffeine</groupId>
+                <artifactId>caffeine</artifactId>
+                <version>${caffeine.version}</version>
+            </dependency>
+
+
+            <!--OSS模块-->
+            <!--ali OSS存储-->
+            <dependency>
+                <groupId>com.aliyun.oss</groupId>
+                <artifactId>aliyun-sdk-oss</artifactId>
+                <version>${aliyun-oss.version}</version>
+            </dependency>
+            <!-- minio -->
+            <dependency>
+                <groupId>io.minio</groupId>
+                <artifactId>minio</artifactId>
+                <version>${minio.version}</version>
+            </dependency>
+            <!--七牛云-->
+            <dependency>
+                <groupId>com.qiniu</groupId>
+                <artifactId>qiniu-java-sdk</artifactId>
+                <version>${qiniu.version}</version>
+            </dependency>
+            <!--OSS模块-->
+
+
+
+            <!--系统硬件信息-->
+            <dependency>
+                <groupId>com.github.oshi</groupId>
+                <artifactId>oshi-core</artifactId>
+                <version>${oshi.core.version}</version>
+            </dependency>
+
+            <!--&lt;!&ndash; excel导入导出工具 &ndash;&gt;-->
+            <!--<dependency>-->
+            <!--<groupId>org.apache.poi</groupId>-->
+            <!--<artifactId>poi-ooxml</artifactId>-->
+            <!--<version>${poi.version}</version>-->
+            <!--</dependency>-->
+
+            <!--websocket-->
+            <dependency>
+                <groupId>org.t-io</groupId>
+                <artifactId>tio-websocket-spring-boot-starter</artifactId>
+                <version>${tio-websocket.version}</version>
+            </dependency>
+
+            <!--字节码增强技术-->
+            <dependency>
+                <groupId>org.javassist</groupId>
+                <artifactId>javassist</artifactId>
+                <version>${javassist.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.github.whvcse</groupId>
+                <artifactId>easy-captcha</artifactId>
+                <version>${easy-captcha.version}</version>
+            </dependency>
+
+            <!--flink 组件-->
+            <dependency>
+                <groupId>org.apache.flink</groupId>
+                <artifactId>flink-streaming-java</artifactId>
+                <version>${flink.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.apache.flink</groupId>
+                <artifactId>flink-clients</artifactId>
+                <version>${flink.version}</version>
+            </dependency>
+
+            <!--在线文档-->
+            <dependency>
+                <groupId>com.github.xiaoymin</groupId>
+                <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
+                <version>${knife4j.verison}</version>
+            </dependency>
+
+            <!--业务组件-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-framework</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-test</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-lock</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>mysql</groupId>
+                <artifactId>mysql-connector-java</artifactId>
+                <version>${mysql-connector.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-mybatis</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--缓存插件-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-cache</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+
+            <!--文件插件-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-file</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--消息总线-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-eventbus</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+
+            <!--satoken插件-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-satoken</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--编号生成插件-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-numbering-strategy</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--在线文档插件-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-doc</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--租户插件-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-biz-tenant</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--常量业务插件-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-biz-constant</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--websocket插件-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-websocket</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--表结构自动更新插件-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-mp-enhance-actable</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--打印banner插件-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-banner</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+
+            <!--数据对接插件-->
+            <!--<dependency>-->
+                <!--<groupId>cn.tr</groupId>-->
+                <!--<artifactId>tr-spring-boot-starter-plugin-dataX</artifactId>-->
+                <!--<version>${revision}</version>-->
+            <!--</dependency>-->
+
+            <!--敏感词插件-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-desensitize</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--web插件-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-web</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--api模块-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-module-system-api</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--定时任务模块-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-module-quartz-api</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--导出api模块-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-module-export-api</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--短信模块-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-spring-boot-starter-plugin-sms</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--阿里云短信-->
+            <dependency>
+                <groupId>com.aliyun</groupId>
+                <artifactId>dysmsapi20170525</artifactId>
+                <version>${ali.sms.version}</version>
+            </dependency>
+
+            <!--阿里云短信-->
+            <dependency>
+                <groupId>com.ibeetl</groupId>
+                <artifactId>beetl-framework-starter</artifactId>
+                <version>${beetl.version}</version>
+            </dependency>
+
+            <!--驱动式消息 订阅发布-->
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
+                <version>${stream-rabbit.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-starter-bus-amqp</artifactId>
+                <version>${bus-rabbit.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>easyexcel</artifactId>
+                <version>${easy-excel.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.flywaydb</groupId>
+                <artifactId>flyway-core</artifactId>
+                <version>${flyway.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.flywaydb</groupId>
+                <artifactId>flyway-mysql</artifactId>
+                <version>${flyway.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.apache.httpcomponents</groupId>
+                <artifactId>httpclient</artifactId>
+                <version>${httpclient.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-lang3</artifactId>
+                <version>${commons-lang3.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>commons-cli</groupId>
+                <artifactId>commons-cli</artifactId>
+                <version>${commons-cli.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.postgresql</groupId>
+                <artifactId>postgresql</artifactId>
+                <version>${postgresql.version}</version>
+            </dependency>
+
+            <!--spring boot 整合 elasticsearch -->
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
+                <version>${es.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.glassfish</groupId>
+                <artifactId>jakarta.json</artifactId>
+                <version>${jakarta-json.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>co.elastic.clients</groupId>
+                <artifactId>elasticsearch-java</artifactId>
+                <version>${es.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>

+ 82 - 0
tr-framework/pom.xml

@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>tr-footstone</artifactId>
+        <groupId>cn.tr</groupId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>tr-framework</artifactId>
+    <version>${revision}</version>
+
+    <description>系统内置的核心框架功能</description>
+    <modules>
+
+    </modules>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-log4j2</artifactId>
+        </dependency>
+
+        <!-- ip2region -->
+        <dependency>
+            <groupId>org.lionsoul</groupId>
+            <artifactId>ip2region</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.javassist</groupId>
+            <artifactId>javassist</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 25 - 0
tr-framework/src/main/java/cn/tr/core/annotation/ColumnDefaultValue.java

@@ -0,0 +1,25 @@
+package cn.tr.core.annotation;
+
+import java.lang.annotation.*;
+
+
+/**
+ * 字段的默认值
+ *
+ * @author sunchenbin
+ * @version 2020年11月09日 下午6:13:37
+ */
+// 该注解用于方法声明
+@Target(ElementType.FIELD)
+// VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息
+@Retention(RetentionPolicy.RUNTIME)
+// 将此注解包含在javadoc中
+@Documented
+public @interface ColumnDefaultValue {
+
+	/**
+	 * 字段的默认值
+	 * @return 字段的默认值
+	 */
+	String value();
+}

+ 16 - 0
tr-framework/src/main/java/cn/tr/core/annotation/Comment.java

@@ -0,0 +1,16 @@
+package cn.tr.core.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * @@interface : Comment
+ * @Description : 字段注释
+ * @Author : LF
+ * @Date: 2023年03月23日
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Comment {
+    String value() default "";
+}

+ 24 - 0
tr-framework/src/main/java/cn/tr/core/annotation/Mobile.java

@@ -0,0 +1,24 @@
+package cn.tr.core.annotation;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.*;
+
+@Target({
+        ElementType.METHOD,
+        ElementType.FIELD,
+        ElementType.ANNOTATION_TYPE,
+        ElementType.CONSTRUCTOR,
+        ElementType.PARAMETER,
+        ElementType.TYPE_USE
+})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Constraint(
+        validatedBy = MobileValidator.class
+)
+public @interface Mobile {
+
+    String value() default "手机号格式不正确";
+
+}

+ 25 - 0
tr-framework/src/main/java/cn/tr/core/annotation/MobileValidator.java

@@ -0,0 +1,25 @@
+package cn.tr.core.annotation;
+
+import cn.hutool.core.util.PhoneUtil;
+import cn.hutool.core.util.StrUtil;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+public class MobileValidator implements ConstraintValidator<Mobile, String> {
+
+    @Override
+    public void initialize(Mobile annotation) {
+    }
+
+    @Override
+    public boolean isValid(String value, ConstraintValidatorContext context) {
+        // 如果手机号为空,默认不校验,即校验通过
+        if (StrUtil.isEmpty(value)) {
+            return true;
+        }
+        // 校验手机
+        return PhoneUtil.isPhone(value);
+    }
+
+}

+ 18 - 0
tr-framework/src/main/java/cn/tr/core/annotation/TenantIgnore.java

@@ -0,0 +1,18 @@
+package cn.tr.core.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 忽略租户,标记指定方法不进行租户的自动过滤
+ *
+ * 注意,只有 DB 的场景会过滤,其它场景暂时不过滤:
+ * 1、Redis 场景:因为是基于 Key 实现多租户的能力,所以忽略没有意义,不像 DB 是一个 column 实现的
+ * 2、MQ 场景:有点难以抉择,目前可以通过 Consumer 手动在消费的方法上,添加 @TenantIgnore 进行忽略
+ *
+ * @author 芋道源码
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface TenantIgnore {
+}

+ 37 - 0
tr-framework/src/main/java/cn/tr/core/constant/MybatisConstant.java

@@ -0,0 +1,37 @@
+package cn.tr.core.constant;
+
+/**
+ * @ClassName : MybatisConstant
+ * @Description : mybatis常量
+ * @Author : LF
+ * @Date: 2023年02月21日
+ */
+
+public class MybatisConstant {
+    /**
+     * 创建时间字段名称
+     */
+    public static final String CREATE_TIME = "createTime";
+    /**
+     * 更新时间字段名称
+     */
+    public static final String UPDATE_TIME = "updateTime";
+    /**
+     * 创建人字段名称
+     */
+    public static final String CREATE_BY = "createBy";
+    /**
+     * 更新人字段名称
+     */
+    public static final String UPDATE_BY = "updateBy";
+
+    /**
+     * 租户id字段名称
+     */
+    public static final String TENANT_ID = "tenantId";
+
+    /**
+     * 分页信息缓存
+     */
+    public static final String PAGE_CACHE = "page_cache";
+}

+ 73 - 0
tr-framework/src/main/java/cn/tr/core/context/SecurityContextHolder.java

@@ -0,0 +1,73 @@
+package cn.tr.core.context;
+
+import cn.hutool.core.util.ObjectUtil;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 获取当前线程变量中的 用户id、用户名称、Token等信息 
+ * 注意: 必须在网关通过请求头的方法传入,同时在HeaderInterceptor拦截器设置值。 否则这里无法获取
+ *
+ * @author lifang
+ */
+public class SecurityContextHolder {
+    private static final ThreadLocal<Map<String, Object>> THREAD_LOCAL = new ThreadLocal<>();
+
+
+    public static void set(String key, Object value) {
+        if (ObjectUtil.isNull(value)) {
+            return;
+        }
+        Map<String, Object> map = getLocalMap();
+        map.put(key, value);
+    }
+
+    public static String getStr(String key)
+    {
+        return get(key,String.class);
+    }
+
+    public static <T> T get(String key, Class<T> clazz) {
+        Map<String,  Object> map = getLocalMap();
+        Object result = map.getOrDefault(key, null);
+        if(result==null){
+            return null;
+        }
+        if(result.getClass() .equals(clazz) ){
+            return (T) result;
+        }else {
+            return null;
+        }
+    }
+
+
+    public static <T> List<T> getList(String key,Class<T> clazz){
+        return (List<T>) getLocalMap().getOrDefault(key, null);
+    }
+
+    public static <T> Map getMap(String key){
+        return (Map) getLocalMap().getOrDefault(key, null);
+    }
+
+
+
+
+
+    private static Map<String, Object> getLocalMap() {
+        Map<String,  Object> map = THREAD_LOCAL.get();
+        if (map == null)
+        {
+            map = new ConcurrentHashMap<String,Object>();
+            THREAD_LOCAL.set(map);
+        }
+        return map;
+    }
+
+    public static void remove()
+    {
+        THREAD_LOCAL.remove();
+    }
+
+}

+ 15 - 0
tr-framework/src/main/java/cn/tr/core/enums/CreateEnum.java

@@ -0,0 +1,15 @@
+package cn.tr.core.enums;
+
+/**
+ * @ClassName : CreateEnum
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月21日
+ */
+
+public enum  CreateEnum {
+    //系统类型
+    sys,
+    //自定义类型
+    custom
+}

+ 15 - 0
tr-framework/src/main/java/cn/tr/core/enums/IEnum.java

@@ -0,0 +1,15 @@
+package cn.tr.core.enums;
+
+/**
+ * @ClassName : IEnum
+ * @Description :
+ * @Author : JR
+ * @Date: 2022年11月12日
+ */
+
+public interface IEnum<T> {
+    T getValue();
+
+    String getLabel();
+
+}

+ 17 - 0
tr-framework/src/main/java/cn/tr/core/enums/UserTypeEnum.java

@@ -0,0 +1,17 @@
+package cn.tr.core.enums;
+
+/**
+ * 全局用户类型枚举
+ */
+
+public interface UserTypeEnum {
+    /**
+     * 面向 b 端,管理后台
+     */
+    String ADMIN="admin";
+
+    /**
+     * 面向 c 端,普通用户
+     */
+    String MEMBER="member";
+}

+ 53 - 0
tr-framework/src/main/java/cn/tr/core/enums/WebFilterOrderEnum.java

@@ -0,0 +1,53 @@
+package cn.tr.core.enums;
+
+/**
+ * Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
+ *
+ *  考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 enum 包下
+ * order值越小越先执行
+ * @author 芋道源码
+ */
+public interface WebFilterOrderEnum {
+
+    int CORS_FILTER = Integer.MIN_VALUE;
+
+    int EXCEPTION_FILTER=CORS_FILTER+1;
+
+    int TRACE_FILTER = EXCEPTION_FILTER + 1;
+
+    int ENV_TAG_FILTER = TRACE_FILTER + 1;
+
+    int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500;
+
+    /**
+     * 执行租户过滤器
+     */
+    int TENANT_CONTEXT_FILTER = - 104;
+
+    /**
+     * 租户接口安全,在租户过滤器之后
+     */
+    int TENANT_SECURITY_FILTER = -99;
+
+    /**
+     * 请求记录过滤器
+     */
+    int API_ACCESS_LOG_FILTER = -103;
+
+    int XSS_FILTER = -102;  // 需要保证在 RequestBodyCacheFilter 后面
+
+    // Spring Security Filter 默认为 -100,可见 org.springframework.boot.autoconfigure.security.SecurityProperties 配置属性类
+
+
+
+    int ACTIVITI_FILTER = -98; // 需要保证在 Spring Security 过滤后面
+
+    int FLOWABLE_FILTER = -98; // 需要保证在 Spring Security 过滤后面
+
+    /**
+     * 填充登录方式,放在最后
+     */
+    int FILL_LOGIN_TYPE=9999;
+    int DEMO_FILTER = Integer.MAX_VALUE;
+
+}

+ 23 - 0
tr-framework/src/main/java/cn/tr/core/exception/BaseCode.java

@@ -0,0 +1,23 @@
+package cn.tr.core.exception;
+
+/**
+ * @Enum : BaseCode
+ * @Description : 基础错误代码
+ * @Author : JR
+ * @Date: 2022年11月18日
+ */
+
+public interface BaseCode {
+
+    /**
+     * 错误码
+     * @return
+     */
+    String getErrCode();
+
+    /**
+     * 错误信息
+     * @return
+     */
+    String getErrMsg();
+}

+ 61 - 0
tr-framework/src/main/java/cn/tr/core/exception/ServiceException.java

@@ -0,0 +1,61 @@
+package cn.tr.core.exception;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 业务逻辑异常 Exception
+ * @author JR
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public final class ServiceException extends RuntimeException {
+
+    private static final long serialVersionUID = 7657422049507503668L;
+    /**
+     * 业务错误码
+     *
+     */
+    private TRExcCode code;
+
+    /**
+     * 错误提示
+     */
+    private String message;
+
+    /**
+     * 空构造方法,避免反序列化问题
+     */
+    public ServiceException() {
+    }
+
+    public ServiceException(TRExcCode errorCode) {
+        this.code = errorCode;
+        this.message = errorCode.getErrMsg();
+    }
+
+    public ServiceException(TRExcCode errCode, String message) {
+        this.code = errCode;
+        this.message = message;
+    }
+
+    public TRExcCode getCode() {
+        return code;
+    }
+
+    public ServiceException setCode(TRExcCode code) {
+        this.code = code;
+        return this;
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+    }
+
+    public ServiceException setMessage(String message) {
+        this.message = message;
+        return this;
+    }
+
+}

+ 226 - 0
tr-framework/src/main/java/cn/tr/core/exception/TRExcCode.java

@@ -0,0 +1,226 @@
+package cn.tr.core.exception;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @ClassName : TRExcCode
+ * @Description : 根据阿里巴巴编码规约(泰山版)整理的错误码
+ * @Author : JR
+ * @Date: 2022年11月18日
+ */
+@AllArgsConstructor
+@Getter
+public enum TRExcCode implements BaseCode {
+        SUCCESS("00000", "成功"),
+    USER_ERROR_0001("A0001", "用户端错误"),
+    USER_ERROR_A0100("A0100", "用户注册错误"),
+    USER_ERROR_A0101("A0101", "用户未同意隐私协议"),
+    USER_ERROR_A0102("A0102", "注册国家或地区受限"),
+    USER_ERROR_A0110("A0110", "用户名校验失败"),
+    USER_ERROR_A0111("A0111", "用户名已存在"),
+    USER_ERROR_A0112("A0112", "用户名包含敏感词"),
+    USER_ERROR_A0113("A0113", "用户名包含特殊字符"),
+    USER_ERROR_A0120("A0120", "密码校验失败"),
+    USER_ERROR_A0121("A0121", "密码长度不够"),
+    USER_ERROR_A0122("A0122", "密码强度不够"),
+    USER_ERROR_A0130("A0130", "校验码输入错误"),
+    USER_ERROR_A0131("A0131", "短信校验码输入错误"),
+    USER_ERROR_A0132("A0132", "邮件校验码输入错误"),
+    USER_ERROR_A0133("A0133", "语音校验码输入错误"),
+    USER_ERROR_A0140("A0140", "用户证件异常"),
+    USER_ERROR_A0141("A0141", "用户证件类型未选择"),
+    USER_ERROR_A0142("A0142", "大陆身份证编号校验非法"),
+    USER_ERROR_A0143("A0143", "护照编号校验非法"),
+    USER_ERROR_A0144("A0144", "军官证编号校验非法"),
+    USER_ERROR_A0150("A0150", "用户基本信息校验失败"),
+    USER_ERROR_A0151("A0151", "手机格式校验失败"),
+    USER_ERROR_A0152("A0152", "地址格式校验失败"),
+    USER_ERROR_A0153("A0153", "邮箱格式校验失败"),
+
+    /**** 登录 **/
+    USER_ERROR_A0200("A0200", "用户登陆异常"),
+    USER_ERROR_A0201("A0201", "用户账户不存在"),
+    USER_ERROR_A0202("A0202", "用户账户被禁用"),
+    USER_ERROR_A0203("A0203", "用户账户已作废"),
+    USER_ERROR_A0210("A0210", "用户密码错误"),
+    USER_ERROR_A0211("A0211", "用户输入密码次数超限"),
+    USER_ERROR_A0220("A0220", "用户身份校验失败"),
+    USER_ERROR_A0221("A0221", "用户指纹识别失败"),
+    USER_ERROR_A0222("A0222", "用户面容识别失败"),
+    USER_ERROR_A0223("A0223", "用户未获得第三方登陆授权"),
+    USER_ERROR_A0230("A0230", "用户登陆已过期"),
+    USER_ERROR_A0240("A0240", "用户验证码错误"),
+    USER_ERROR_A0241("A0241", "用户验证码尝试次数超限"),
+    USER_ERROR_A0242("A0242", "用户未登录"),
+
+    USER_ERROR_A0243("A0243", "OAuth2 认证失败"),
+    USER_ERROR_A0204("A0244", "租户被禁用"),
+    USER_ERROR_A0205("A0245", "租户已被删除"),
+    /**** 权限 **/
+    USER_ERROR_A0300("A0300", "访问权限异常"),
+    USER_ERROR_A0301("A0301", "访问未授权"),
+    USER_ERROR_A0302("A0302", "正在授权中"),
+    USER_ERROR_A0303("A0303", "用户授权申请被拒绝"),
+    USER_ERROR_A0310("A0310", "因访问对象隐私设置被拦截"),
+    USER_ERROR_A0311("A0311", "授权已过期"),
+    USER_ERROR_A0312("A0312", "无权限使用API"),
+    USER_ERROR_A0320("A0320", "用户访问被拦截"),
+    USER_ERROR_A0321("A0321", "黑名单用户"),
+    USER_ERROR_A0322("A0322", "账号被禁用"),
+    USER_ERROR_A0323("A0323", "非法IP地址"),
+    USER_ERROR_A0324("A0324", "网关访问受限"),
+    USER_ERROR_A0325("A0325", "地域黑名单"),
+    USER_ERROR_A0330("A0330", "服务已欠费"),
+    USER_ERROR_A0340("A0340", "用户签名异常"),
+    USER_ERROR_A0341("A0341", "RSA签名错误"),
+
+
+    /**** 输入错误 **/
+    USER_ERROR_A0400("A0400", "用户请求参数错误"),
+    USER_ERROR_A0401("A0401", "包含非法恶意跳转链接"),
+    USER_ERROR_A0402("A0402", "无效的用户输入"),
+    USER_ERROR_A0403("A0403", "权限不足"),
+    USER_ERROR_A0404("A0404", "用户请求地址不存在"),
+    USER_ERROR_A0410("A0410", "请求必填参数为空"),
+    USER_ERROR_A0411("A0411", "用户订单号为空"),
+    USER_ERROR_A0412("A0412", "订购数量为空"),
+    USER_ERROR_A0413("A0413", "缺少时间戳参数"),
+    USER_ERROR_A0414("A0414", "非法的时间戳参数"),
+    USER_ERROR_A0420("A0420", "请求参数值超出允许的范围"),
+    USER_ERROR_A0421("A0421", "参数格式不匹配"),
+    USER_ERROR_A0422("A0422", "地址不在服务范围"),
+    USER_ERROR_A0423("A0423", "时间不在服务范围"),
+    USER_ERROR_A0424("A0424", "金额超出限制"),
+    USER_ERROR_A0425("A0425", "数量超出限制"),
+    USER_ERROR_A0426("A0426", "请求批量处理总个数超出限制"),
+    USER_ERROR_A0427("A0427", "请求JSON解析失败"),
+    USER_ERROR_A0430("A0430", "用户输入内容非法"),
+    USER_ERROR_A0431("A0431", "包含违禁敏感词"),
+    USER_ERROR_A0432("A0432", "图片包含违禁信息"),
+    USER_ERROR_A0433("A0433", "文件侵犯版权"),
+    USER_ERROR_A0440("A0440", "用户操作异常"),
+    USER_ERROR_A0441("A0441", "用户支付超时"),
+    USER_ERROR_A0442("A0442", "确认订单超时"),
+    USER_ERROR_A0443("A0443", "订单已关闭"),
+    USER_ERROR_A0500("A0500", "用户请求服务异常"),
+    USER_ERROR_A0501("A0501", "请求次数超出限制"),
+    USER_ERROR_A0502("A0502", "请求并发数超出限制"),
+    USER_ERROR_A0503("A0503", "用户操作请等待"),
+    USER_ERROR_A0504("A0504", "WebSocket连接异常"),
+    USER_ERROR_A0505("A0505", "WebSocket连接断开"),
+    USER_ERROR_A0506("A0506", "用户重复请求"),
+    USER_ERROR_A0600("A0600", "用户资源异常"),
+    USER_ERROR_A0601("A0601", "账户余额不足"),
+    USER_ERROR_A0602("A0602", "用户磁盘空间不足"),
+    USER_ERROR_A0603("A0603", "用户内存空间不足"),
+    USER_ERROR_A0604("A0604", "用户OSS容量不足"),
+    USER_ERROR_A0605("A0605", "用户配额已用光"),
+    USER_ERROR_A0700("A0700", "用户上传文件异常"),
+    USER_ERROR_A0701("A0701", "用户上传文件类型不匹配"),
+    USER_ERROR_A0702("A0702", "用户上传文件太大"),
+    USER_ERROR_A0703("A0703", "用户上传图片太大"),
+    USER_ERROR_A0704("A0704", "用户上传视频太大"),
+    USER_ERROR_A0705("A0705", "用户上传压缩文件太大"),
+    USER_ERROR_A0706("A0706", "不支持预览该类型文件预览"),
+    USER_ERROR_A0800("A0800", "用户当前版本异常"),
+    USER_ERROR_A0801("A0801", "用户安装版本与系统不匹配"),
+    USER_ERROR_A0802("A0802", "用户安装版本过低"),
+    USER_ERROR_A0803("A0803", "用户安装版本过高"),
+    USER_ERROR_A0804("A0804", "用户安装版本已过期"),
+    USER_ERROR_A0805("A0805", "用户API请求版本不匹配"),
+    USER_ERROR_A0806("A0806", "用户API请求版本过高"),
+    USER_ERROR_A0807("A0807", "用户API请求版本过低"),
+    USER_ERROR_A0900("A0900", "用户隐私未授权"),
+    USER_ERROR_A0901("A0901", "用户隐私未签署"),
+    USER_ERROR_A0902("A0902", "用户摄像头未授权"),
+    USER_ERROR_A0903("A0903", "用户相机未授权"),
+    USER_ERROR_A0904("A0904", "用户图片库未授权"),
+    USER_ERROR_A0905("A0905", "用户文件未授权"),
+    USER_ERROR_A0906("A0906", "用户位置信息未授权"),
+    USER_ERROR_A0907("A0907", "用户通讯录未授权"),
+    USER_ERROR_A1000("A1000", "用户设备异常"),
+    USER_ERROR_A1001("A1001", "用户相机异常"),
+    USER_ERROR_A1002("A1002", "用户麦克风异常"),
+    USER_ERROR_A1003("A1003", "用户听筒异常"),
+    USER_ERROR_A1004("A1004", "用户扬声器异常"),
+    USER_ERROR_A1005("A1005", "用户GPS定位异常"),
+
+    /**** 服务端错误 **/
+    SYSTEM_ERROR_B0001("B0001", "系统执行出错"),
+    SYSTEM_ERROR_B0100("B0100", "系统执行超时"),
+    SYSTEM_ERROR_B0101("B0101", "系统订单处理超时"),
+    SYSTEM_ERROR_B0200("B0200", "系统容灾功能被触发"),
+    SYSTEM_ERROR_B0210("B0210", "系统限流"),
+    SYSTEM_ERROR_B0220("B0220", "系统功能降级"),
+    SYSTEM_ERROR_B0300("B0300", "系统资源异常"),
+    SYSTEM_ERROR_B0310("B0310", "系统资源耗尽"),
+    SYSTEM_ERROR_B0311("B0311", "系统磁盘空间耗尽"),
+    SYSTEM_ERROR_B0312("B0312", "系统内存耗尽"),
+    SYSTEM_ERROR_B0313("B0313", "文件句柄耗尽"),
+    SYSTEM_ERROR_B0314("B0314", "系统连接池耗尽"),
+    SYSTEM_ERROR_B0315("B0315", "系统线程池耗尽"),
+    SYSTEM_ERROR_B0320("B0320", "系统资源访问异常"),
+    SYSTEM_ERROR_B0321("B0321", "系统读取磁盘文件失败"),
+    SYSTEM_ERROR_B0404("B0321", "资源未找到"),
+
+    /**** 第三方服务错误 **/
+    SERVICE_ERROR_C0001("C0001", "调用第三方服务出错"),
+    SERVICE_ERROR_C0100("C0100", "中间件服务出错"),
+    SERVICE_ERROR_C0110("C0110", "RPC服务出错"),
+    SERVICE_ERROR_C0111("C0111", "RPC服务未找到"),
+    SERVICE_ERROR_C0112("C0112", "RPC服务未注册"),
+    SERVICE_ERROR_C0113("C0113", "接口不存在"),
+    SERVICE_ERROR_C0120("C0120", "消息服务出错"),
+    SERVICE_ERROR_C0121("C0121", "消息投递出错"),
+    SERVICE_ERROR_C0122("C0122", "消息消费出错"),
+    SERVICE_ERROR_C0123("C0123", "消息订阅出错"),
+    SERVICE_ERROR_C0124("C0124", "消息分组未查到"),
+    SERVICE_ERROR_C0130("C0130", "缓存服务出错"),
+    SERVICE_ERROR_C0131("C0131", "key长度超过限制"),
+    SERVICE_ERROR_C0132("C0132", "value长度超过限制"),
+    SERVICE_ERROR_C0133("C0133", "存储容量已满"),
+    SERVICE_ERROR_C0134("C0134", "不支持的数据格式"),
+    SERVICE_ERROR_C0140("C0140", "配置服务出错"),
+    SERVICE_ERROR_C0150("C0150", "网络资源服务出错"),
+    SERVICE_ERROR_C0151("C0151", "VPN服务出错"),
+    SERVICE_ERROR_C0152("C0152", "CDN服务出错"),
+    SERVICE_ERROR_C0153("C0153", "域名解析服务出错"),
+    SERVICE_ERROR_C0154("C0154", "网关服务出错"),
+    SERVICE_ERROR_C0200("C0200", "第三方系统执行超时"),
+    SERVICE_ERROR_C0210("C0210", "RPC执行超时"),
+    SERVICE_ERROR_C0220("C0220", "消息投递超时"),
+    SERVICE_ERROR_C0230("C0230", "缓存服务超时"),
+    SERVICE_ERROR_C0240("C0240", "配置服务超时"),
+    SERVICE_ERROR_C0250("C0250", "数据库服务超时"),
+    SERVICE_ERROR_C0300("C0300", "数据库服务出错"),
+    SERVICE_ERROR_C0311("C0311", "表不存在"),
+    SERVICE_ERROR_C0312("C0312", "列不存在"),
+    SERVICE_ERROR_C0321("C0321", "多表关联中存在多个相同名称的列"),
+    SERVICE_ERROR_C0331("C0331", "数据库死锁"),
+    SERVICE_ERROR_C0341("C0341", "主键冲突"),
+    SERVICE_ERROR_C0400("C0400", "第三方容灾系统被触发"),
+    SERVICE_ERROR_C0401("C0401", "第三方系统限流"),
+    SERVICE_ERROR_C0402("C0402", "第三方功能降级"),
+    SERVICE_ERROR_C0500("C0500", "通知服务出错"),
+    SERVICE_ERROR_C0501("C0501", "短信提醒服务失败"),
+    SERVICE_ERROR_C0502("C0502", "语音提醒服务失败"),
+    SERVICE_ERROR_C0503("C0503", "邮件提醒服务失败"),
+
+
+    //DataX
+    CONFIG_ERROR("Common-00", "您提供的配置文件存在错误信息,请检查您的作业配置 ."),
+    CONVERT_NOT_SUPPORT("Common-01", "同步数据出现业务脏数据情况,数据类型转换错误 ."),
+    CONVERT_OVER_FLOW("Common-02", "同步数据出现业务脏数据情况,数据类型转换溢出 ."),
+    RETRY_FAIL("Common-10", "方法调用多次仍旧失败 ."),
+    RUNTIME_ERROR("Common-11", "运行时内部调用错误 ."),
+    HOOK_INTERNAL_ERROR("Common-12", "Hook运行错误 ."),
+    SHUT_DOWN_TASK("Common-20", "Task收到了shutdown指令,为failover做准备"),
+    WAIT_TIME_EXCEED("Common-21", "等待时间超出范围"),
+    TASK_HUNG_EXPIRED("Common-22", "任务hung住,Expired"),
+    ;
+    @Getter
+    private String errCode;
+    @Getter
+    private String errMsg;
+}

+ 96 - 0
tr-framework/src/main/java/cn/tr/core/pojo/CommonResult.java

@@ -0,0 +1,96 @@
+package cn.tr.core.pojo;
+
+import cn.hutool.core.util.StrUtil;
+import cn.tr.core.exception.BaseCode;
+import cn.tr.core.exception.ServiceException;
+import cn.tr.core.exception.TRExcCode;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.springframework.util.Assert;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * 通用返回
+ *
+ * @param <T> 数据泛型
+ */
+@Data
+@Accessors(chain = true)
+public class CommonResult<T> implements Serializable {
+
+    private static final long serialVersionUID = -2619760277112039632L;
+
+    /**
+     * 错误码
+     *
+     * @see TRExcCode ()
+     */
+    private String code;
+    /**
+     * 返回数据
+     */
+    private T data;
+    /**
+     * 错误提示,用户可阅读
+     *
+     * @see TRExcCode#getErrMsg()
+     */
+    private String errorMsg;
+
+    public static <T> CommonResult<T> error(BaseCode excCode, String detailErrorMsg) {
+        String errCode = excCode.getErrCode();
+        Assert.isTrue(!TRExcCode.SUCCESS.getErrCode().equals(errCode), "type 必须是错误的!");
+        CommonResult<T> result = new CommonResult<>();
+        result.code = errCode;
+        result.errorMsg = StrUtil.isNotBlank(detailErrorMsg)?detailErrorMsg:excCode.getErrMsg();
+        return result;
+    }
+
+    public static <T> CommonResult<T> error(BaseCode errorCode) {
+        return error(errorCode, errorCode.getErrMsg());
+    }
+
+    public static <T> CommonResult<T> success() {
+        return CommonResult.success(null);
+    }
+
+    public static <T> CommonResult<T> success(T data) {
+        CommonResult<T> result = new CommonResult<>();
+        result.code = TRExcCode.SUCCESS.getErrCode();
+        result.data = data;
+        result.errorMsg = "";
+        return result;
+    }
+
+    public static boolean isSuccess(String code) {
+        return Objects.equals(code, TRExcCode.SUCCESS.getErrCode());
+    }
+
+    /**
+     *  避免 jackson 序列化
+     * @return
+     */
+    @JsonIgnore
+    public boolean isSuccess() {
+        return isSuccess(code);
+    }
+
+    /**
+     *  避免 jackson 序列化
+     * @return
+     */
+    @JsonIgnore
+    public boolean isError() {
+        return !isSuccess();
+    }
+
+    // ========= 和 Exception 异常体系集成 =========
+
+    public static <T> CommonResult<T> error(ServiceException serviceException) {
+        return error(TRExcCode.SYSTEM_ERROR_B0001, serviceException.getMessage());
+    }
+
+}

+ 22 - 0
tr-framework/src/main/java/cn/tr/core/pojo/KeyValue.java

@@ -0,0 +1,22 @@
+package cn.tr.core.pojo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * Key Value 的键值对
+ *
+ * @author tr
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor(staticName = "of")
+public class KeyValue<K,V> implements Serializable {
+
+    private K key;
+    private V value;
+
+}

+ 28 - 0
tr-framework/src/main/java/cn/tr/core/pojo/LoginResult.java

@@ -0,0 +1,28 @@
+package cn.tr.core.pojo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @ClassName : LoginResult
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年03月02日
+ */
+@Data
+public class LoginResult implements Serializable {
+    private static final long serialVersionUID = 6736183242095354849L;
+
+    /**
+     * 登录token
+     */
+    private String token;
+
+    /**
+     * token过期时间
+     */
+    private long expireTime;
+
+
+}

+ 33 - 0
tr-framework/src/main/java/cn/tr/core/pojo/PageDomain.java

@@ -0,0 +1,33 @@
+package cn.tr.core.pojo;
+
+import cn.hutool.db.sql.Order;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 分页数据
+ * 
+ * @author lifang
+ */
+@Data
+public class PageDomain
+{
+    /** 当前记录起始索引 */
+    private Integer current;
+
+    /** 每页显示记录数 */
+    private Integer size;
+
+    private List<Order> orders;
+
+    /** 分页参数合理化 */
+    private Boolean reasonable = true;
+
+
+    public PageDomain(Integer current, Integer size,List<Order> orders) {
+        this.current = current;
+        this.size = size;
+        this.orders=orders;
+    }
+}

+ 41 - 0
tr-framework/src/main/java/cn/tr/core/pojo/PageInfo.java

@@ -0,0 +1,41 @@
+package cn.tr.core.pojo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+/**
+ * @ClassName : PageInfo
+ * @Description :
+ * @Author : LF
+ * @Date: 2022年11月25日
+ */
+
+@AllArgsConstructor(staticName = "of")
+@NoArgsConstructor
+@Data
+public class PageInfo<T> implements Serializable {
+
+    /**
+     * 总条数
+     */
+    private Long total;
+
+    /**
+     * 当前记录起始索引
+     */
+    private Integer pageNum;
+
+    /**
+     * 每页显示记录数
+     */
+    private Integer pageSize;
+
+    /**
+     * 数据
+     */
+    private Collection<T> data;
+}

+ 50 - 0
tr-framework/src/main/java/cn/tr/core/pojo/TableDataInfo.java

@@ -0,0 +1,50 @@
+package cn.tr.core.pojo;
+
+
+import cn.tr.core.exception.TRExcCode;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 表格分页数据对象
+ * 
+ * @author lifang
+ */
+@Data
+public class TableDataInfo<T> implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /** 列表数据 */
+    private PageInfo<T> data;
+
+    /** 消息状态码 */
+    private String code;
+
+    /** 消息内容 */
+    private String errorMsg;
+
+
+    /**
+     * 表格数据对象
+     */
+    public TableDataInfo()
+    {
+    }
+
+    /**
+     * 分页
+     *
+     * @param page 分页数据
+     *
+     */
+    public TableDataInfo(PageInfo<T> page)
+    {
+        this.code= TRExcCode.SUCCESS.getErrCode();
+        this.data = page;
+    }
+
+    public String getErrorMsg() {
+        return errorMsg==null?"":errorMsg;
+    }
+}

+ 55 - 0
tr-framework/src/main/java/cn/tr/core/strategy/DeptDataPermissionStrategy.java

@@ -0,0 +1,55 @@
+package cn.tr.core.strategy;
+
+import java.util.*;
+import java.util.function.Supplier;
+
+/**
+ * @ClassName : DeptDataPermissionStrategy
+ * @Description : 数据权限策略
+ * @Author : LF
+ * @Date: 2023年03月03日
+ */
+
+public class DeptDataPermissionStrategy {
+    private DeptDataPermissionStrategy(){
+
+    }
+
+    public static DeptDataPermissionStrategy tr=new DeptDataPermissionStrategy();
+
+
+    /**
+     * 当前用户的部门id
+     */
+    public Supplier<Set<String>> currentUserDeptIdsSupplier=()->null;
+
+    /**
+     * 是否允许查看所有数据
+     */
+    public Supplier<Boolean> allowAllDataSupplier=()->false;
+
+    /**
+     * 是否允许查看自己的数据
+     */
+    public Supplier<Boolean> allowSelfDataSupplier=()->false;
+
+    public Set<String> getCurrentUserDeptIds(){
+        return currentUserDeptIdsSupplier==null?null:currentUserDeptIdsSupplier.get();
+    }
+
+    /**
+     * 查看所有数据
+     * @return
+     */
+    public Boolean getAll(){
+        return allowAllDataSupplier==null?null:allowAllDataSupplier.get();
+    }
+
+    /**
+     * 查看用户自身的数据
+     * @return
+     */
+    public Boolean getSelf(){
+        return allowSelfDataSupplier==null?null:allowSelfDataSupplier.get();
+    }
+}

+ 37 - 0
tr-framework/src/main/java/cn/tr/core/strategy/ExceptionStrategy.java

@@ -0,0 +1,37 @@
+package cn.tr.core.strategy;
+
+import cn.tr.core.exception.TRExcCode;
+import cn.tr.core.pojo.CommonResult;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+/**
+ * @ClassName : ExceptionStrategy
+ * @Description : 异常处理策略
+ * @Author : LF
+ * @Date: 2023年02月28日
+ */
+
+public class ExceptionStrategy {
+    private ExceptionStrategy(){
+
+    }
+    private Map<Class<? extends Throwable>,BiFunction<HttpServletRequest, Throwable,CommonResult>> exceptionHandlerMap=new HashMap<>();
+
+    public static ExceptionStrategy tr =new ExceptionStrategy();
+
+
+    public <T extends Throwable> void  registerThrowableHandler(Class<T> aClass,BiFunction<HttpServletRequest,Throwable,CommonResult> exceptionSupplier){
+        exceptionHandlerMap.put(aClass,exceptionSupplier);
+    }
+
+    public CommonResult<?> exceptionHandle(HttpServletRequest request, Throwable ex){
+        //兜底处理
+        return exceptionHandlerMap
+                .getOrDefault(ex.getClass(), ((re, t) -> CommonResult.error(TRExcCode.SYSTEM_ERROR_B0001, t.getLocalizedMessage())))
+                .apply(request,ex);
+    }
+}

+ 47 - 0
tr-framework/src/main/java/cn/tr/core/strategy/LoginUserStrategy.java

@@ -0,0 +1,47 @@
+package cn.tr.core.strategy;
+
+
+import cn.tr.core.strategy.auth.ILoginUser;
+import java.util.function.Supplier;
+
+/**
+ * @ClassName : LoginUserStrategy
+ * @Description : 登录用户策略
+ * @Author : LF
+ * @Date: 2023年02月21日
+ */
+
+public class LoginUserStrategy {
+    private LoginUserStrategy(){
+
+    }
+
+    public static LoginUserStrategy tr =new LoginUserStrategy();
+
+    public Supplier<? extends ILoginUser> loginUserSupplier;
+
+    /**
+     * 当前地址是否允许匿名登录,默认不允许
+     */
+    public Supplier<Boolean> anonymousLoginSupplier=()->true;
+
+    public String getCurrentUserId(){
+        return loginUserSupplier==null?null:loginUserSupplier.get().getUserId();
+    }
+
+    public String getCurrentUserOrgId(){
+        return loginUserSupplier==null?null:loginUserSupplier.get().getOrgId();
+    }
+
+    public String getCurrentUsername(){
+        return loginUserSupplier.get().getUsername();
+    }
+
+    public String getTenantId(){
+        return loginUserSupplier.get().getTenantId();
+    }
+
+    public boolean isAnonymous(){
+        return anonymousLoginSupplier==null||Boolean.TRUE.equals(anonymousLoginSupplier.get());
+    }
+}

+ 44 - 0
tr-framework/src/main/java/cn/tr/core/strategy/PageStrategy.java

@@ -0,0 +1,44 @@
+package cn.tr.core.strategy;
+
+import cn.tr.core.pojo.PageDomain;
+import com.github.pagehelper.Page;
+import com.github.pagehelper.PageHelper;
+
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * @ClassName : PageStrategy
+ * @Description : 分页策略
+ * @Author : LF
+ * @Date: 2023年02月21日
+ */
+
+public class PageStrategy {
+    private PageStrategy(){
+
+    }
+
+
+    public static final PageStrategy tr = new PageStrategy();
+
+
+    /**
+     * 从请求中创建分页参数
+     */
+    public Supplier<PageDomain> createPage=()-> new PageDomain(1,10,null);
+
+    /**
+     * 开始实现分页
+     */
+    public Consumer<PageDomain> startPage= page->{};
+
+    /**
+     * 获取当前最终分页参数
+     */
+    public Supplier<Page<?>> getPage=()-> new Page<>();
+
+    public void clear(){
+        PageHelper.clearPage();
+    }
+}

+ 18 - 0
tr-framework/src/main/java/cn/tr/core/strategy/auth/ILoginUser.java

@@ -0,0 +1,18 @@
+package cn.tr.core.strategy.auth;
+
+/**
+ * @ClassName : ILoginUser
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年02月21日
+ */
+
+public interface ILoginUser {
+    String getUserId();
+
+    String getUsername();
+
+    String getOrgId();
+
+    String getTenantId();
+}

+ 26 - 0
tr-framework/src/main/java/cn/tr/core/tenant/TenantConstant.java

@@ -0,0 +1,26 @@
+package cn.tr.core.tenant;
+
+/**
+ * @ClassName : TenantConstant
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年02月21日
+ */
+
+public class TenantConstant {
+
+    /**
+     * 租户id
+     */
+    public static final String TENANT_ID="tenant_id";
+
+    /**
+     * 是否为租户缓存
+     */
+    public static final String TENANT_CACHE="tenant_cache";
+
+    /**
+     * 是否忽略租户
+     */
+    public static final String TENANT_IGNORE="tenant_ignore";
+}

+ 51 - 0
tr-framework/src/main/java/cn/tr/core/tenant/TenantContextHolder.java

@@ -0,0 +1,51 @@
+package cn.tr.core.tenant;
+
+import cn.tr.core.context.SecurityContextHolder;
+
+/**
+ * 多租户上下文 Holder
+ *
+ * @author tr
+ */
+public class TenantContextHolder {
+
+    /**
+     * 获得租户编号。
+     *
+     * @return 租户编号
+     */
+    public static String getTenantId() {
+        return SecurityContextHolder.getStr(TenantConstant.TENANT_ID);
+    }
+
+    /**
+     * 获得租户编号。如果不存在,则抛出 NullPointerException 异常
+     *
+     * @return 租户编号
+     */
+    public static String getRequiredTenantId() {
+        String tenantId = getTenantId();
+        if (tenantId == null) {
+            throw new NullPointerException("TenantContextHolder 不存在租户编号!");
+        }
+        return tenantId;
+    }
+
+    public static void setTenantId(String tenantId) {
+        SecurityContextHolder.set(TenantConstant.TENANT_ID,tenantId);
+    }
+
+    public static void setIgnore(boolean ignore) {
+        SecurityContextHolder.set(TenantConstant.TENANT_IGNORE,ignore);
+    }
+
+    /**
+     * 当前是否忽略租户
+     *
+     * @return 是否忽略
+     */
+    public static Boolean isIgnore() {
+        return Boolean.TRUE.equals(SecurityContextHolder.get(TenantConstant.TENANT_IGNORE,Boolean.class));
+    }
+
+}

+ 77 - 0
tr-framework/src/main/java/cn/tr/core/tenant/TenantUtils.java

@@ -0,0 +1,77 @@
+package cn.tr.core.tenant;
+
+
+import cn.tr.core.tenant.TenantContextHolder;
+
+import java.util.function.Supplier;
+
+/**
+ * 多租户 Util
+ *
+ * @author tr
+ */
+public class TenantUtils {
+
+    /**
+     * 使用指定租户,执行对应的逻辑
+     *
+     * 注意,如果当前是忽略租户的情况下,会被强制设置成不忽略租户
+     * 当然,执行完成后,还是会恢复回去
+     *
+     * @param tenantId 租户编号
+     * @param runnable 逻辑
+     */
+    public static void execute(String tenantId, Runnable runnable) {
+        String oldTenantId = TenantContextHolder.getTenantId();
+        Boolean oldIgnore = TenantContextHolder.isIgnore();
+        try {
+            TenantContextHolder.setTenantId(tenantId);
+            TenantContextHolder.setIgnore(false);
+            // 执行逻辑
+            runnable.run();
+        } finally {
+            TenantContextHolder.setTenantId(oldTenantId);
+            TenantContextHolder.setIgnore(oldIgnore);
+        }
+    }
+
+    /**
+     * 忽略租户,执行对应的逻辑
+     *
+     * @param runnable 逻辑
+     */
+    public static void executeIgnore(Runnable runnable) {
+        Boolean oldIgnore = TenantContextHolder.isIgnore();
+        try {
+            TenantContextHolder.setIgnore(true);
+            // 执行逻辑
+            runnable.run();
+        } finally {
+            TenantContextHolder.setIgnore(oldIgnore);
+        }
+    }
+
+    /**
+     * 使用指定租户,执行对应的逻辑
+     *
+     * 注意,如果当前是忽略租户的情况下,会被强制设置成不忽略租户
+     * 当然,执行完成后,还是会恢复回去
+     *
+     * @param tenantId 租户编号
+     * @param supplier 逻辑
+     */
+    public static <T> T execute(String tenantId, Supplier<T> supplier) {
+        String oldTenantId = TenantContextHolder.getTenantId();
+        Boolean oldIgnore = TenantContextHolder.isIgnore();
+        try {
+            TenantContextHolder.setTenantId(tenantId);
+            TenantContextHolder.setIgnore(false);
+            // 执行逻辑
+            return supplier.get();
+        } finally {
+            TenantContextHolder.setTenantId(oldTenantId);
+            TenantContextHolder.setIgnore(oldIgnore);
+        }
+    }
+
+}

+ 49 - 0
tr-framework/src/main/java/cn/tr/core/tree/TreeNode.java

@@ -0,0 +1,49 @@
+package cn.tr.core.tree;
+
+import cn.tr.core.validation.Insert;
+import cn.tr.core.validation.Update;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tree基类
+ * 
+ * @author ruoyi
+ */
+@Data
+public abstract class TreeNode<PK> implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 父节点
+     */
+    @NotNull(message = "父级id不能为空",groups = {Update.class, Insert.class})
+    private PK parentId;
+
+    /**
+     * 顺序
+     */
+    private Integer sort;
+
+    /**
+     * 子节点集合
+     */
+    @ApiModelProperty(readOnly = true)
+    private List<? extends TreeNode<PK>> children = new ArrayList<>();
+
+    /**
+     * 节点路径
+     */
+    private String nodePath;
+
+    public abstract PK getId();
+
+    public abstract String getName();
+}

+ 212 - 0
tr-framework/src/main/java/cn/tr/core/utils/AvatarUtil.java

@@ -0,0 +1,212 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.StrUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.Base64Utils;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Random;
+import java.util.WeakHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @ClassName : AvatarUtil
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年09月04日
+ */
+@Slf4j
+public class AvatarUtil {
+    private final static Color defaultColor=new Color(2, 168,250);
+    private volatile static Map<String, String> avatarMap=new WeakHashMap<>();
+    private static Font singleWordFont;
+
+    static {
+        try {
+            //小米字体MiSans-Medium
+            singleWordFont = Font.createFont(Font.TRUETYPE_FONT, AvatarUtil.class.getClassLoader().getResourceAsStream("avatarFont.ttf")).deriveFont(Font.PLAIN, 50);
+        } catch (FontFormatException e) {
+            log.error("字体加载失败",e);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    ;
+
+    static {
+
+    }
+    /**
+     * 圆角大小
+     */
+    private static final Integer FILLET=150;
+
+    public static String generateAvatar(String name,Color color){
+        if(color==null){
+            color=defaultColor;
+        }
+        String key=new String(name+":"+color.getRGB());
+        String result = avatarMap.get(key);
+        if(StrUtil.isNotBlank(result)){
+            return result;
+        }
+        if(StrUtil.isBlank(name)){
+            name="匿名";
+        }
+        int width = 100;
+        int height = 100;
+//        int nameLen = name.length();
+
+
+        String nameWritten=StrUtil.sub(name,0,1);
+//        // 如果用户输入的姓名少于等于2个字符,不用截取
+//        if (nameLen == 1) {
+//            nameWritten = name;
+//        } else {
+//            // 如果用户输入的姓名大于等于3个字符,截取后面两位
+//            String first = name.substring(0, 1);
+//            if (isChinese(first)) {
+//                // 截取倒数两位汉字
+//                nameWritten = name.substring(nameLen - 2);
+//            } else {
+//                // 截取前面的两个英文字母
+//                nameWritten = name.substring(0, 2).toUpperCase();
+//            }
+//        }
+        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+
+        Graphics2D g2 = (Graphics2D) bi.getGraphics();
+        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+
+        g2.setBackground(color);
+
+        g2.clearRect(0, 0, width, height);
+
+        g2.setPaint(Color.WHITE);
+
+
+        Font font = null;
+        // 两个字及以上
+        if(nameWritten.length() >= 2) {
+            font = singleWordFont;
+            g2.setFont(font);
+
+            String firstWritten = nameWritten.substring(0, 1);
+            String secondWritten = nameWritten.substring(1, 2);
+
+            // 两个中文 如 张三
+            if (isChinese(firstWritten) && isChinese(secondWritten)) {
+                g2.drawString(nameWritten, 20, 60);
+            }
+            // 首中次英 如 张S
+            else if (isChinese(firstWritten) && !isChinese(secondWritten)) {
+                g2.drawString(nameWritten, 27, 60);
+
+            }
+            // 首英,如 ZS
+            else {
+                nameWritten = nameWritten.substring(0,1);
+            }
+
+        }
+        // 一个字
+        if(nameWritten.length() ==1) {
+            // 中文
+            if(isChinese(nameWritten)) {
+//                font = new Font("华文中宋", Font.PLAIN, 50);
+                g2.setFont(singleWordFont);
+                g2.drawString(nameWritten, 25, 67);
+            }
+            // 英文
+            else {
+//                font = new Font("华文中宋", Font.PLAIN, 55);
+                g2.setFont(singleWordFont);
+                g2.drawString(nameWritten.toUpperCase(), 33, 67);
+            }
+        }
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        BufferedImage rounded = makeRoundedCorner(bi, FILLET);
+        try {
+            ImageIO.write(rounded, "png", outputStream);
+            byte[] bytes = outputStream.toByteArray();
+            IoUtil.close(outputStream);
+            result= Base64Utils.encodeToString(bytes);
+        }catch (Exception e){
+            result="";
+        }
+        avatarMap.put(key,"data:image/png;base64,"+result);
+        return "data:image/png;base64,"+result;
+    }
+    /**
+     * 绘制字体头像
+     * 如果是英文名,只显示首字母大写
+     * 如果是中文名,只显示最后两个字
+     * @param name
+     * @throws IOException
+     * @return  头像的base64存储
+     */
+    public static String generateAvatar(String name){
+        return generateAvatar(name,defaultColor);
+    }
+
+
+    /**
+     * 判断字符串是否为中文
+     * @param str
+     * @return
+     */
+    public static boolean isChinese(String str) {
+        String regEx = "[\\u4e00-\\u9fa5]+";
+        Pattern p = Pattern.compile(regEx);
+        Matcher m = p.matcher(str);
+        if (m.find())
+            return true;
+        else
+            return false;
+    }
+
+    /**
+     * 获得随机颜色
+     * @return
+     */
+    private static Color getRandomColor() {
+        String[] beautifulColors = new String[]{"2,168,250"};
+        int len = beautifulColors.length;
+        Random random = new Random();
+        String[] color = beautifulColors[random.nextInt(len)].split(",");
+        return new Color(Integer.parseInt(color[0]), Integer.parseInt(color[1]),
+                Integer.parseInt(color[2]));
+    }
+
+
+    /**
+     * 图片做圆角处理
+     * @param image
+     * @param cornerRadius
+     * @return
+     */
+    public static BufferedImage makeRoundedCorner(BufferedImage image, int cornerRadius){
+        int w = image.getWidth();
+        int h = image.getHeight();
+        BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+        Graphics2D g2 = output.createGraphics();
+        g2.setComposite(AlphaComposite.Src);
+        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        g2.setColor(Color.WHITE);
+        g2.fill(new RoundRectangle2D.Float(0, 0, w, h, cornerRadius, cornerRadius));
+        g2.setComposite(AlphaComposite.SrcAtop);
+        g2.drawImage(image, 0, 0, null);
+        g2.dispose();
+        return output;
+    }
+}

+ 46 - 0
tr-framework/src/main/java/cn/tr/core/utils/BizFirstCateUtil.java

@@ -0,0 +1,46 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.lang.Pair;
+import cn.hutool.core.lang.Tuple;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.util.function.Function;
+
+/**
+ * @Author zzy
+ * @Data 2025/2/7
+ * @Version 1.0
+ * @Description XXX
+ */
+public class BizFirstCateUtil {
+    /**
+     * 根据
+     */
+    public static Function<String, Pair<String, String>> firstCateNameSupplier = firstId -> null;
+
+    public static Function<String, Tuple> firstCateSupplier = firstId -> null;
+
+    public static String getFirstCateName(String firstId) {
+        if (StrUtil.isBlank(firstId)){
+            return null;
+        }
+        Tuple tuple = getFirstCateNameAndGender(firstId);
+        return ObjectUtil.isNull(tuple)?null:tuple.get(0);
+    }
+
+    public static Tuple getFirstCateNameAndGender(String firstId){
+        if(StrUtil.isBlank(firstId)){
+            return null;
+        }
+        return  firstCateSupplier.apply(firstId);
+    }
+
+    public static String getSignature(String firstId){
+        if(StrUtil.isBlank(firstId)){
+            return null;
+        }
+        Tuple tuple = getFirstCateNameAndGender(firstId);
+        return ObjectUtil.isNull(tuple)?null: tuple.get(2);
+    }
+}

+ 46 - 0
tr-framework/src/main/java/cn/tr/core/utils/BizSecondCateUtil.java

@@ -0,0 +1,46 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.lang.Pair;
+import cn.hutool.core.lang.Tuple;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.util.function.Function;
+
+/**
+ * @Author zzy
+ * @Data 2025/2/7
+ * @Version 1.0
+ * @Description XXX
+ */
+public class BizSecondCateUtil {
+    /**
+     * 根据
+     */
+    public static Function<String, Pair<String, String>> secondCateNameSupplier = secondId -> null;
+
+    public static Function<String, Tuple> secondCateSupplier = secondId -> null;
+
+    public static String getSecondCateName(String secondId) {
+        if (StrUtil.isBlank(secondId)){
+            return null;
+        }
+        Tuple tuple = getSecondCateNameAndGender(secondId);
+        return ObjectUtil.isNull(tuple)?null:tuple.get(0);
+    }
+
+    public static Tuple getSecondCateNameAndGender(String secondId){
+        if(StrUtil.isBlank(secondId)){
+            return null;
+        }
+        return  secondCateSupplier.apply(secondId);
+    }
+
+    public static String getSignature(String secondId){
+        if(StrUtil.isBlank(secondId)){
+            return null;
+        }
+        Tuple tuple = getSecondCateNameAndGender(secondId);
+        return ObjectUtil.isNull(tuple)?null: tuple.get(2);
+    }
+}

+ 126 - 0
tr-framework/src/main/java/cn/tr/core/utils/CommonNetWorkInfoUtil.java

@@ -0,0 +1,126 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package cn.tr.core.utils;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.system.SystemUtil;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.*;
+
+/**
+ * 通用获取当前网速工具类
+ *
+ * @author xuyuxiang
+ * @date 2022/9/1 23:45
+ */
+@Slf4j
+public class CommonNetWorkInfoUtil {
+
+    /**
+     * 网速测速时间2s
+     */
+    private static final int SLEEP_SECONDS = 2;
+
+    /**
+     * 获取网络上下行速率,格式{"UP": "123KB/S, "DOWN": "345KB/S"}
+     *
+     * @author xuyuxiang
+     * @date 2022/9/1 23:51
+     */
+    public static Map<String, String> getNetworkUpRate() {
+        Map<String, String> result = new HashMap<>();
+        Process pro = null;
+        Runtime r = Runtime.getRuntime();
+        BufferedReader input = null;
+        try {
+            boolean isWindows = SystemUtil.getOsInfo().isWindows();
+            String command = isWindows ? "netstat -e" : "ss";
+            pro = r.exec(command);
+            input = new BufferedReader(new InputStreamReader(pro.getInputStream()));
+            long[] result1 = readInLine(input, isWindows);
+            Thread.sleep(SLEEP_SECONDS * 1000);
+            pro.destroy();
+            input.close();
+            pro = r.exec(command);
+            input = new BufferedReader(new InputStreamReader(pro.getInputStream()));
+            long[] result2 = readInLine(input, isWindows);
+            String upSpeed = FileUtil.readableFileSize(Convert.toLong(NumberUtil
+                    .div(NumberUtil.sub(result2[0], result1[0]), SLEEP_SECONDS)));
+            String downSpeed = FileUtil.readableFileSize(Convert.toLong(NumberUtil
+                    .div(NumberUtil.sub(result2[1], result1[1]), SLEEP_SECONDS)));
+            result.put("UP", upSpeed + (upSpeed.endsWith("B")?"/S":"B/S"));
+            result.put("DOWN", downSpeed + (downSpeed.endsWith("B")?"/S":"B/S"));
+        } catch (Exception e) {
+            log.info(">>> 网络测速失败,原因:");
+            e.printStackTrace();
+        } finally {
+            if (input != null) {
+                try {
+                    input.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            Optional.ofNullable(pro).ifPresent(Process::destroy);
+        }
+        return result;
+    }
+
+    private static String formatNumber(double f) {
+        return new Formatter().format("%.2f", f).toString();
+    }
+
+    private static long[] readInLine(BufferedReader input, boolean isWindows) {
+        long[] arr = new long[2];
+        StringTokenizer tokenStat;
+        try {
+            if (isWindows) {
+                // 获取windows环境下的网口上下行速率
+                input.readLine();
+                input.readLine();
+                input.readLine();
+                input.readLine();
+                tokenStat = new StringTokenizer(input.readLine());
+                tokenStat.nextToken();
+                arr[0] = Long.parseLong(tokenStat.nextToken());
+                arr[1] = Long.parseLong(tokenStat.nextToken());
+            } else {
+                // 获取linux环境下的网口上下行速率
+                long rx = 0, tx = 0;
+                String line = null;
+                //RX packets:4171603 errors:0 dropped:0 overruns:0 frame:0
+                //TX packets:4171603 errors:0 dropped:0 overruns:0 carrier:0
+                while ((line = input.readLine()) != null) {
+                    if (line.contains("RX packets")) {
+                        rx += Long.parseLong(line.substring(line.indexOf("RX packets") + 11, line.indexOf(" ",
+                                line.indexOf("RX packets") + 11)));
+                    } else if (line.contains("TX packets")) {
+                        tx += Long.parseLong(line.substring(line.indexOf("TX packets") + 11, line.indexOf(" ",
+                                line.indexOf("TX packets") + 11)));
+                    }
+                }
+                arr[0] = rx;
+                arr[1] = tx;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return arr;
+    }
+}

+ 58 - 0
tr-framework/src/main/java/cn/tr/core/utils/IpUtil.java

@@ -0,0 +1,58 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.io.FileUtil;
+import org.lionsoul.ip2region.xdb.Searcher;
+
+import java.io.File;
+import java.io.InputStream;
+
+/**
+ * Ip地址查询类
+ */
+public class IpUtil {
+    private static Searcher searcher;
+
+    static {
+        String fileName = "/ip2region.xdb";
+        File existFile = FileUtil.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
+        if(!FileUtil.exist(existFile)) {
+            InputStream resourceAsStream = IpUtil.class.getResourceAsStream(fileName);
+            FileUtil.writeFromStream(resourceAsStream, existFile);
+        }
+
+        String dbPath = existFile.getPath();
+
+        // 1、从 dbPath 加载整个 xdb 到内存。
+        byte[] cBuff;
+        try {
+            cBuff = Searcher.loadContentFromFile(dbPath);
+        } catch (Exception e) {
+            throw new RuntimeException(String.format("IPUtil初始化失败,原因:%s", e.getMessage()));
+        }
+
+        // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
+        try {
+            searcher = Searcher.newWithBuffer(cBuff);
+        } catch (Exception e) {
+            throw new RuntimeException(String.format("IPUtil初始化失败,原因:%s", e.getMessage()));
+        }
+    }
+
+    /**
+     * 根据IP地址离线获取城市
+     *
+     * @author xuyuxiang
+     * @date 2022/4/27 23:14
+     */
+    public static String getCityInfo(String ip) {
+        try {
+            ip = ip.trim();
+            // 3、执行查询
+            String region = searcher.search(ip);
+            return region.replace("0|", "").replace("|0", "");
+        } catch (Exception e) {
+            return "未知";
+        }
+    }
+
+}

+ 146 - 0
tr-framework/src/main/java/cn/tr/core/utils/JsonUtils.java

@@ -0,0 +1,146 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.experimental.UtilityClass;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * JSON 工具类
+ *
+ * @author 芋道源码
+ */
+@UtilityClass
+public class JsonUtils {
+
+    private static ObjectMapper objectMapper = new ObjectMapper();
+
+    /**
+     * 初始化 objectMapper 属性
+     * <p>
+     * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean
+     *
+     * @param objectMapper ObjectMapper 对象
+     */
+    public static void init(ObjectMapper objectMapper) {
+        JsonUtils.objectMapper = objectMapper;
+    }
+
+    public static String toJsonString(Object object) {
+        try {
+            if(Objects.isNull(object)){
+                return null;
+            }
+            return objectMapper.writeValueAsString(object);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static byte[] toJsonByte(Object object) throws JsonProcessingException {
+        return objectMapper.writeValueAsBytes(object);
+    }
+
+    public static String toJsonPrettyString(Object object) throws JsonProcessingException {
+        return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
+    }
+
+    public static <T> T parseObject(String text, Class<T> clazz) {
+        if (StrUtil.isEmpty(text)) {
+            return null;
+        }
+        try {
+            return objectMapper.readValue(text, clazz);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
+        if (ArrayUtil.isEmpty(bytes)) {
+            return null;
+        }
+        try {
+            return objectMapper.readValue(bytes, clazz);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static <T> T parseObject(String text, TypeReference<T> typeReference) throws JsonProcessingException {
+        if (!isJson(text)) {
+            return null;
+        }
+        try {
+            return objectMapper.readValue(text, typeReference);
+        } catch (IOException e) {
+            throw e;
+        }
+    }
+
+    public static Map<String,Object> parseMap(String text){
+        if (StrUtil.isEmpty(text)) {
+            return new HashMap<String,Object>();
+        }
+        try {
+            return objectMapper.readValue(text, Map.class);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static <T> List<T> parseArray(String text, Class<T> clazz){
+        if (StrUtil.isEmpty(text)) {
+            return new ArrayList<>();
+        }
+        try {
+            return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static JsonNode parseTree(String text) throws JsonProcessingException {
+        try {
+            return objectMapper.readTree(text);
+        } catch (IOException e) {
+            throw e;
+        }
+    }
+
+    public static JsonNode parseTree(byte[] text) throws IOException {
+        try {
+            return objectMapper.readTree(text);
+        } catch (IOException e) {
+            throw e;
+        }
+    }
+
+    public static boolean isJson(String text) {
+        return JSONUtil.isTypeJSON(text);
+    }
+
+    /**
+     * 是否为JSONArray类型的字符串,首尾都为中括号判定为JSONArray字符串
+     *
+     * @param str 字符串
+     * @return 是否为JSONArray类型字符串
+     * @since 5.7.22
+     */
+    public static boolean isTypeJSONArray(String str) {
+        if (StrUtil.isBlank(str)) {
+            return false;
+        }
+        return StrUtil.isWrap(StrUtil.trim(str), '[', ']');
+    }
+}

+ 111 - 0
tr-framework/src/main/java/cn/tr/core/utils/MediaTypeUtils.java

@@ -0,0 +1,111 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @ClassName : MediaTypeUtils
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年03月13日
+ */
+@Slf4j
+public class MediaTypeUtils {
+    private static final Map<String,String> contentTypeMap=new HashMap<>();
+    /**
+     * 默认文件下载
+     */
+    private static final String DEFAULT_CONTENT_TYPE="application/octet-stream ";
+
+    static {
+        for (MediaType value : MediaType.values()) {
+            log.debug("[register]---->fileType:[{}],ContentType:[{}]",value.getFileType(),value.getContentType());
+            contentTypeMap.put(value.getFileType(),value.getContentType());
+        }
+    }
+
+    public static void registerContentType(String fileType,String contentType){
+        log.debug("[register]---->fileType:[{}],ContentType:[{}]",fileType,contentType);
+        contentTypeMap.put(fileType,contentType);
+    }
+
+    /**
+     *  解析文件锁对应的Content-Type
+     * @param fileName
+     * @return
+     */
+    public static String parseContentType(String fileName) {
+        if(!StrUtil.contains(fileName,".")){
+            return DEFAULT_CONTENT_TYPE;
+        }
+        String fileType= CollectionUtil.getLast(StrUtil.split(fileName,"."));
+        return contentTypeMap.getOrDefault(fileType,DEFAULT_CONTENT_TYPE);
+    }
+
+    @AllArgsConstructor
+    @Getter
+    private static enum MediaType{
+        DOC("doc","application/msword"),
+        DOCX("docx","application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
+
+        RTF("rtf","application/rtf"),
+
+        XLS("xls","application/vnd.ms-excel"),
+        XLSX("xlsx","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
+
+        PPT("ppt","application/vnd.ms-powerpoint"),
+        PPTX("pptx","application/vnd.openxmlformats-officedocument.presentationml.presentation"),
+
+        PPS("pps","application/vnd.ms-powerpoint"),
+        PPSX("ppsx","application/vnd.openxmlformats-officedocument.presentationml.slideshow"),
+
+        PDF("pdf","application/pdf"),
+
+        SWF("swf","application/x-shockwave-flash"),
+
+        DDL("ddl","application/x-msdownload"),
+
+        TAR("tar","application/x-tar"),
+
+        TGZ("tgz","application/x-compressed"),
+
+        ZIP("zip","application/x-zip-compressed"),
+
+        z("z","application/x-compress"),
+
+        WAV("wav","audio/wav"),
+
+        WMA("wma","audio/x-ms-wma"),
+
+        WMV("wmv","video/x-ms-wmv"),
+
+
+        BMP("bmp","image/bmp"),
+
+        GIF("gif","image/gif"),
+
+        JPEG("jpeg","image/jpg"),
+        JPG("jpg","image/jpg"),
+        PNG("png","image/jpg"),
+
+        HTML("html","text/html"),
+
+        TXT("txt","text/plain"),
+
+        VSD("vsd","application/vnd.visio"),
+
+        XML("xml","text/xml"),
+
+
+
+        ;
+        private String fileType;
+        private String contentType;
+    }
+}

+ 45 - 0
tr-framework/src/main/java/cn/tr/core/utils/MenuUtil.java

@@ -0,0 +1,45 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.lang.Pair;
+import cn.hutool.core.lang.Tuple;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.util.function.Function;
+
+/**
+ * @Author zzy
+ * @Data 2024/12/17
+ * @Version 1.0
+ */
+public class MenuUtil {
+
+    /**
+     * 根据
+     */
+    public static Function<String, Pair<String,String>> menuNameSupplier = menuId -> null;
+
+    /**
+     * 根据 tuple[名称]
+     */
+
+    public static Function<String, Tuple> menuSupplier = menuId -> null;
+
+    public static String getMenuName(String menuId){
+        if (StrUtil.isBlank(menuId)){
+            return null;
+        }
+        Tuple tuple = getMenuNameAndGender(menuId);
+        return ObjectUtil.isNull(tuple)?null:tuple.get(0);
+    }
+
+    private static Tuple getMenuNameAndGender(String menuId) {
+        if (StrUtil.isBlank(menuId)){
+            return null;
+        }
+        return menuSupplier.apply(menuId);
+
+    }
+
+
+}

+ 164 - 0
tr-framework/src/main/java/cn/tr/core/utils/Proxy.java

@@ -0,0 +1,164 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.util.StrUtil;
+import javassist.*;
+import javassist.bytecode.AnnotationsAttribute;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.annotation.*;
+import lombok.Getter;
+import lombok.SneakyThrows;
+import org.springframework.util.ClassUtils;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+public class Proxy<I> {
+    private static final AtomicLong counter = new AtomicLong(1);
+
+    public final CtClass ctClass;
+    @Getter
+    private final Class<I> superClass;
+    @Getter
+    private final String className;
+    @Getter
+    private final String classFullName;
+
+    private Class<I> targetClass;
+
+    @SneakyThrows
+    public static <I> Proxy<I> create(Class<I> superClass, String... classPathString) {
+        return new Proxy<>(superClass, classPathString);
+    }
+
+    public Proxy(Class<I> superClass, String... classPathString) throws Exception {
+        if (superClass == null) {
+            throw new NullPointerException("superClass can not be null");
+        }
+
+        className = superClass.getSimpleName() + "FastBeanCopier" + counter.getAndAdd(1);
+        classFullName = "cn.tr.plugin.eventbus"+ "." + className;
+
+        this.superClass = superClass;
+        ClassPool classPool = ClassPool.getDefault();
+
+        classPool.insertClassPath(new ClassClassPath(this.getClass()));
+        classPool.insertClassPath(new LoaderClassPath(ClassUtils.getDefaultClassLoader()));
+
+        if (classPathString != null) {
+            for (String path : classPathString) {
+                classPool.insertClassPath(path);
+            }
+        }
+
+
+        ctClass = classPool.makeClass(classFullName);
+        if (superClass != Object.class) {
+            if (superClass.isInterface()) {
+                ctClass.setInterfaces(new CtClass[]{classPool.get(superClass.getName())});
+            } else {
+                ctClass.setSuperclass(classPool.get(superClass.getName()));
+            }
+        }
+        addConstructor("public " + className + "(){}");
+    }
+
+    public Proxy<I> addMethod(String code) {
+        return handleException(() -> ctClass.addMethod(CtNewMethod.make(code, ctClass)));
+    }
+
+    public Proxy<I> addConstructor(String code) {
+        return handleException(() -> ctClass.addConstructor(CtNewConstructor.make(code, ctClass)));
+    }
+
+    public Proxy<I> addField(String code) {
+        return addField(code, null);
+    }
+
+    public Proxy<I> addField(String code, Class<? extends java.lang.annotation.Annotation> annotation) {
+        return addField(code, annotation, null);
+    }
+
+    @SuppressWarnings("all")
+    public static MemberValue createMemberValue(Object value, ConstPool constPool) {
+        MemberValue memberValue = null;
+        if (value instanceof Integer) {
+            memberValue = new IntegerMemberValue(constPool, ((Integer) value));
+        } else if (value instanceof Boolean) {
+            memberValue = new BooleanMemberValue((Boolean) value, constPool);
+        } else if (value instanceof Long) {
+            memberValue = new LongMemberValue((Long) value, constPool);
+        } else if (value instanceof String) {
+            memberValue = new StringMemberValue((String) value, constPool);
+        } else if (value instanceof Class) {
+            memberValue = new ClassMemberValue(((Class) value).getName(), constPool);
+        } else if (value instanceof Object[]) {
+            Object[] arr = ((Object[]) value);
+            ArrayMemberValue arrayMemberValue = new ArrayMemberValue(new ClassMemberValue(arr[0].getClass().getName(), constPool), constPool);
+            arrayMemberValue.setValue(Arrays.stream(arr)
+                    .map(o -> createMemberValue(o, constPool))
+                    .toArray(MemberValue[]::new));
+            memberValue = arrayMemberValue;
+
+        }
+        return memberValue;
+    }
+
+    public Proxy<I> custom(Consumer<CtClass> ctClassConsumer) {
+        ctClassConsumer.accept(ctClass);
+        return this;
+    }
+
+    @SneakyThrows
+    public Proxy<I> addField(String code, Class<? extends java.lang.annotation.Annotation> annotation, Map<String, Object> annotationProperties) {
+        return handleException(() -> {
+            CtField ctField = CtField.make(code, ctClass);
+            if (null != annotation) {
+                ConstPool constPool = ctClass.getClassFile().getConstPool();
+                AnnotationsAttribute attributeInfo = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
+                Annotation ann = new javassist.bytecode.annotation.Annotation(annotation.getName(), constPool);
+                if (null != annotationProperties) {
+                    annotationProperties.forEach((key, value) -> {
+                        MemberValue memberValue = createMemberValue(value, constPool);
+                        if (memberValue != null) {
+                            ann.addMemberValue(key, memberValue);
+                        }
+                    });
+                }
+                attributeInfo.addAnnotation(ann);
+                ctField.getFieldInfo().addAttribute(attributeInfo);
+            }
+            ctClass.addField(ctField);
+        });
+    }
+
+    @SneakyThrows
+    private Proxy<I> handleException(Task task) {
+        task.run();
+        return this;
+    }
+
+
+    @SneakyThrows
+    public I newInstance() {
+        return getTargetClass().newInstance();
+    }
+
+    @SneakyThrows
+    public Class<I> getTargetClass() {
+        if (targetClass == null) {
+            targetClass = (Class<I>) ctClass.toClass(ClassUtils.getDefaultClassLoader(), null);
+        }
+        return targetClass;
+    }
+
+    interface Task {
+        void run() throws Exception;
+    }
+}

+ 46 - 0
tr-framework/src/main/java/cn/tr/core/utils/PswUtils.java

@@ -0,0 +1,46 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.digest.BCrypt;
+import lombok.experimental.UtilityClass;
+
+/**
+ * @ClassName : PswUtils
+ * @Description : 密码工具类
+ * @Author : LF
+ * @Date: 2023年03月24日
+ */
+@UtilityClass
+public class PswUtils {
+    /**
+     * 生成BCryptPasswordEncoder密码
+     *
+     * @param password 密码
+     * @return 加密字符串
+     */
+    public static String encryptPassword(String password) {
+        return BCrypt.hashpw(password);
+    }
+
+    /**
+     * 判断密码是否相同
+     *
+     * @param rawPassword     真实密码
+     * @param encodedPassword 加密后字符
+     * @return 结果
+     */
+    public static boolean matchesPassword(String rawPassword, String encodedPassword) {
+        return BCrypt.checkpw(rawPassword, encodedPassword);
+    }
+
+    /**
+     * 校验密码
+     *
+     * @param password 密码
+     */
+    public static void validatePsw(String password) {
+        if(StrUtil.length(password)<6){
+            throw new UnsupportedOperationException("密码长度不得少于6位");
+        }
+    }
+}

+ 45 - 0
tr-framework/src/main/java/cn/tr/core/utils/RoleUtil.java

@@ -0,0 +1,45 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.lang.Pair;
+import cn.hutool.core.lang.Tuple;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.util.function.Function;
+
+/**
+ * @Author zzy
+ * @Data 2024/12/17
+ * @Version 1.0
+ */
+public class RoleUtil {
+
+    /**
+     * 根据
+     */
+    public static Function<String, Pair<String,String>> RoleNameSupplier = roleId -> null;
+
+    /**
+     * 根据 tuple[名称]
+     */
+
+    public static Function<String, Tuple> roleSupplier = roleId -> null;
+
+    public static String getRoleName(String roleId){
+        if (StrUtil.isBlank(roleId)){
+            return null;
+        }
+        Tuple tuple = getRoleNameAndGender(roleId);
+        return ObjectUtil.isNull(tuple)?null:tuple.get(0);
+    }
+
+    private static Tuple getRoleNameAndGender(String roleId) {
+        if (StrUtil.isBlank(roleId)){
+            return null;
+        }
+        return roleSupplier.apply(roleId);
+
+    }
+
+
+}

+ 149 - 0
tr-framework/src/main/java/cn/tr/core/utils/ServletUtils.java

@@ -0,0 +1,149 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.servlet.ServletUtil;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
+
+/**
+ * 客户端工具类
+ *
+ * @author 芋道源码
+ */
+public class ServletUtils {
+
+    /**
+     * 获取String参数
+     */
+    public static String getParameter(String name)
+    {
+        return getRequest().getParameter(name);
+    }
+
+    /**
+     * 获取String参数
+     */
+    public static String getHeader(String name)
+    {
+        return getRequest().getHeader(name);
+    }
+    /**
+     * 返回 JSON 字符串
+     *
+     * @param response 响应
+     * @param object 对象,会序列化成 JSON 字符串
+     */
+    @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
+    public static void writeJSON(HttpServletResponse response, Object object) throws JsonProcessingException {
+        response.setStatus(HttpStatus.OK.value());
+        String content = JsonUtils.toJsonString(object);
+        ServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
+    }
+
+    /**
+     * 返回附件
+     *
+     * @param response 响应
+     * @param filename 文件名
+     * @param content 附件内容
+     * @throws IOException
+     */
+    public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
+        writeAttachment(response,filename,content,false);
+    }
+
+    /**
+     * 返回附件
+     *
+     * @param response 响应
+     * @param filename 文件名
+     * @param content 附件内容
+     * @param close 是否关闭流
+     * @throws IOException
+     */
+    public static void writeAttachment(HttpServletResponse response, String filename, byte[] content, boolean close) throws IOException {
+        // 设置 header 和 contentType
+        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
+        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
+        // 输出附件
+        IoUtil.write(response.getOutputStream(), close, content);
+    }
+
+    /**
+     * @param request 请求
+     * @return ua
+     */
+    public static String getUserAgent(HttpServletRequest request) {
+        String ua = request.getHeader("User-Agent");
+        return ua != null ? ua : "";
+    }
+
+    /**
+     * 获得请求
+     *
+     * @return HttpServletRequest
+     */
+    public static HttpServletRequest getRequest() {
+        try {
+            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+            if (!(requestAttributes instanceof ServletRequestAttributes)) {
+                return null;
+            }
+            return ((ServletRequestAttributes) requestAttributes).getRequest();
+        }catch (Exception e){
+            return null;
+        }
+    }
+
+    /**
+     * 获取ua
+     * @return
+     */
+    public static String getUserAgent() {
+        HttpServletRequest request = getRequest();
+        if (request == null) {
+            return null;
+        }
+        return getUserAgent(request);
+    }
+
+    /**
+     * 获取客户端ip地址
+     * @return
+     */
+    public static String getClientIP() {
+        return getClientIP(getRequest());
+    }
+
+    /**
+     * 获取客户端ip地址
+     * @return
+     */
+    public static String getClientIP(  HttpServletRequest request ) {
+        if (request == null) {
+            return null;
+        }
+        return ServletUtil.getClientIP(request);
+    }
+
+    /**
+     * 判断请求内容类型是为json
+     * @param request
+     * @return
+     */
+    public static boolean isJsonRequest(ServletRequest request) {
+        return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);
+    }
+
+}

+ 37 - 0
tr-framework/src/main/java/cn/tr/core/utils/SpElUtil.java

@@ -0,0 +1,37 @@
+package cn.tr.core.utils;
+
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+
+import java.lang.reflect.Method;
+
+/**
+ * @ClassName : SpELUtil
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年06月05日
+ */
+
+public class SpElUtil {
+    /**
+     * 解析
+     * @param expressionString el表达式
+     * @param method           拦截方法
+     * @param args             拦截参数
+     * @return
+     */
+    public static String parseExpression(String expressionString, Method method, Object[] args) {
+        //获取被拦截方法参数名列表
+        LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
+        String[] paramNameArr = discoverer.getParameterNames(method);
+        //SPEL解析
+        ExpressionParser parser = new SpelExpressionParser();
+        StandardEvaluationContext context = new StandardEvaluationContext();
+        for (int i = 0; i < paramNameArr.length; i++) {
+            context.setVariable(paramNameArr[i], args[i]);
+        }
+        return parser.parseExpression(expressionString).getValue(context, String.class);
+    }
+}

+ 44 - 0
tr-framework/src/main/java/cn/tr/core/utils/SysOrgUtil.java

@@ -0,0 +1,44 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.lang.Pair;
+import cn.hutool.core.lang.Tuple;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.util.function.Function;
+
+/**
+ * @Author zzy
+ * @Data 2025/1/9
+ * @Version 1.0
+ * @Description XXX
+ */
+public class SysOrgUtil {
+
+    /**
+     * 根据
+     */
+    public static Function<String, Pair<String,String>> sysOrgNameSupplier = sysOrgId -> null;
+
+    /**
+     * 根据 tuple[名称]
+     */
+
+    public static Function<String, Tuple> sysOrgSupplier = sysOrgId -> null;
+
+    public static String getSysOrgName(String sysOrgId){
+        if (StrUtil.isBlank(sysOrgId)){
+            return null;
+        }
+        Tuple tuple = getSysOrgNameAndGender(sysOrgId);
+        return ObjectUtil.isNull(tuple)?null:tuple.get(0);
+    }
+
+    private static Tuple getSysOrgNameAndGender(String sysOrgId) {
+        if (StrUtil.isBlank(sysOrgId)){
+            return null;
+        }
+        return sysOrgSupplier.apply(sysOrgId);
+
+    }
+}

+ 155 - 0
tr-framework/src/main/java/cn/tr/core/utils/TreeUtil.java

@@ -0,0 +1,155 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.tr.core.tree.TreeNode;
+import lombok.experimental.UtilityClass;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName : TreeUtil
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月01日
+ */
+@UtilityClass
+public class TreeUtil {
+    /**
+     * 获取ID对应的节点,如果有多个ID相同的节点,只返回第一个。<br>
+     * 此方法只查找此节点及子节点,采用递归深度优先遍历。
+     *
+     * @param <T>  ID类型
+     * @param node 节点
+     * @param id   ID
+     * @return 节点
+     * @since 5.2.4
+     */
+    public static <T> TreeNode<T> getNode(TreeNode<T> node, T id) {
+        if (ObjectUtil.equal(id, node.getId())) {
+            return node;
+        }
+        final List<? extends TreeNode<T>> children = node.getChildren();
+        if (null == children) {
+            return null;
+        }
+
+        // 查找子节点
+        TreeNode<T> childNode;
+        for (TreeNode<T> child : children) {
+            childNode = getNode(child, id);
+            if(childNode!=null){
+                return childNode;
+            }
+        }
+
+        // 未找到节点
+        return null;
+    }
+    /**
+     * 将传入的数组转变为树状结构
+     * @param sources
+     * @return
+     */
+    public static <Node extends TreeNode> List<Node> buildTree(Collection<Node> sources){
+        return buildTree(sources,null);
+    };
+
+    public static  <Node extends TreeNode> List<Node> buildTree(Collection<Node > sources, List<String> excludeNodeIds){
+        if(CollectionUtil.isNotEmpty(excludeNodeIds)){
+            sources=sources.stream()
+                    .filter(source->!excludeNodeIds.contains(source.getId()))
+                    .collect(Collectors.toList());
+        }
+        //找到顶级节点
+        sources.stream()
+                .peek(node->node.setParentId(ObjectUtil.isEmpty(node.getParentId())?"0":node.getParentId()))
+                .filter(source-> ObjectUtil.isNull(source.getSort())).forEach(source->source.setSort(999));
+        List<Node> result = sources.stream()
+                .filter(node -> StrUtil.equals("0",String.valueOf(node.getParentId())))
+                .collect(Collectors.toList());
+        if(CollectionUtil.isEmpty(result)){
+            return result;
+        }
+        Map<Object, List<TreeNode>> groupByParentId = sources.stream()
+                .filter(node ->!StrUtil.equals("0",String.valueOf(node.getParentId())))
+                .collect(Collectors.groupingBy(TreeNode::getParentId));
+        result.forEach(topNode->{
+            topNode.setNodePath(topNode.getName());
+            fillChildNode(topNode,groupByParentId);
+        });
+        CollectionUtil.sort(result, Comparator.comparing(TreeNode::getSort));
+        return result;
+    }
+
+    /**
+     * 填充叶子子节点
+     */
+    private static void fillChildNode(TreeNode source, Map<Object, List<TreeNode>> groupByParentIdMap){
+        Object parentId = source.getId();
+        List<TreeNode> children = groupByParentIdMap.get(parentId);
+        if(CollectionUtil.isEmpty(children)){
+            return;
+        }
+        source.setChildren(children);
+        children.forEach(child->{
+            child.setNodePath(Optional.ofNullable(source.getName()).orElse("")+"/"+child.getName());
+            fillChildNode(child,groupByParentIdMap);
+        });
+        CollectionUtil.sort(children,Comparator.comparing(TreeNode::getSort));
+    }
+
+    /**
+     * 校验节点配置是否合理
+     */
+    public static <T extends TreeNode>  void validateNode(T node,Collection<T> tree){
+        if(ObjectUtil.equals(node.getId(),node.getParentId())){
+            throw new UnsupportedOperationException("不能将自身作为上级节点");
+        }
+        Object parentId = node.getParentId();
+        List<T> flatTree = flatTree(tree);
+        for (T treeNode : flatTree) {
+            if(ObjectUtil.equals(treeNode.getId(),node.getId())){
+                node=treeNode;
+                break;
+            }
+        }
+
+        List<T> children = flatTree(Collections.singleton(node));
+        for (T child : children) {
+            if(ObjectUtil.equals(child.getId(),parentId)){
+                throw new UnsupportedOperationException("不能将子节点作为上级节点");
+            }
+        }
+    }
+
+    public static <T extends TreeNode> List<T> flatTree(Collection<T> sources) {
+        List<T> result = new ArrayList<>();
+        for (T source : sources) {
+            flatTree(source,result);
+        }
+        return result;
+    }
+
+    /**
+     * 将树结构展开
+     * @param source 树节点
+     * @param result 展开结果
+     * @param <T>
+     */
+    private static  <T extends TreeNode>  void flatTree(T source,Collection<T> result){
+        if(source==null){
+            return;
+        }
+        result.add(source);
+        if(CollectionUtil.isNotEmpty(source.getChildren())){
+            for (Object child : source.getChildren()) {
+                flatTree((T) child,result);
+            }
+        }
+    }
+
+
+}

+ 67 - 0
tr-framework/src/main/java/cn/tr/core/utils/UserUtil.java

@@ -0,0 +1,67 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.lang.Pair;
+import cn.hutool.core.lang.Tuple;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.awt.*;
+import java.util.function.Function;
+
+/**
+ * @ClassName : UserUtil
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年09月05日
+ */
+
+public class UserUtil {
+    /**
+     * 根据
+     */
+    public static Function<String, Pair<String,String>> userNickNameSupplier= userId->null;
+    /**
+     * 根据 tuple[昵称、性别、个性签名]
+     */
+    public static Function<String,Tuple> userSupplier= userId->null;
+
+    public static String getNickName(String userId){
+        if(StrUtil.isBlank(userId)){
+            return null;
+        }
+        Tuple tuple = getNickNameAndGender(userId);
+        return ObjectUtil.isNull(tuple)?null: tuple.get(0);
+    }
+
+    public static Tuple getNickNameAndGender(String userId){
+        if(StrUtil.isBlank(userId)){
+            return null;
+        }
+        return  userSupplier.apply(userId);
+    }
+
+    public static String getAvatar(String userId){
+        if(StrUtil.isBlank(userId)){
+            return null;
+        }
+        Tuple tuple = getNickNameAndGender(userId);
+        if(tuple==null){
+            return getAvatar("匿名",null);
+        }else {
+            return getAvatar(tuple.get(0),tuple.get(1));
+        }
+    }
+
+    public static String getAvatar(String nickname,String gender){
+        return StrUtil.equals(gender,"WOMAN")? AvatarUtil.generateAvatar(nickname, Color.PINK):AvatarUtil.generateAvatar(nickname,null);
+    }
+
+    public static String getSignature(String userId){
+        if(StrUtil.isBlank(userId)){
+            return null;
+        }
+        Tuple tuple = getNickNameAndGender(userId);
+        return ObjectUtil.isNull(tuple)?null: tuple.get(2);
+    }
+}

+ 53 - 0
tr-framework/src/main/java/cn/tr/core/utils/ValidationUtils.java

@@ -0,0 +1,53 @@
+package cn.tr.core.utils;
+
+import cn.hutool.core.collection.CollUtil;
+import org.springframework.util.StringUtils;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * 校验工具类
+ *
+ * @author 芋道源码
+ */
+public class ValidationUtils {
+    private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
+
+    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}$");
+
+    private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
+
+    private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*");
+
+    public static boolean isMobile(String mobile) {
+        return StringUtils.hasText(mobile)
+                && PATTERN_MOBILE.matcher(mobile).matches();
+    }
+
+    public static boolean isURL(String url) {
+        return StringUtils.hasText(url)
+                && PATTERN_URL.matcher(url).matches();
+    }
+
+    public static boolean isXmlNCName(String str) {
+        return StringUtils.hasText(str)
+                && PATTERN_XML_NCNAME.matcher(str).matches();
+    }
+
+    public static void validate(Object object, Class<?>... groups) {
+        validate(validator,object,groups);
+    }
+
+    public static void validate(Validator validator, Object object, Class<?>... groups) {
+        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
+        if (CollUtil.isNotEmpty(constraintViolations)) {
+            throw new ConstraintViolationException(constraintViolations);
+        }
+    }
+
+}

+ 15 - 0
tr-framework/src/main/java/cn/tr/core/validation/Insert.java

@@ -0,0 +1,15 @@
+package cn.tr.core.validation;
+
+import java.lang.annotation.*;
+
+/**
+ * @@interface : Insert
+ * @Description : 数据新增时校验
+ * @Author : LF
+ * @Date: 2023年03月27日
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Insert {
+}

+ 15 - 0
tr-framework/src/main/java/cn/tr/core/validation/Update.java

@@ -0,0 +1,15 @@
+package cn.tr.core.validation;
+
+import java.lang.annotation.*;
+
+/**
+ * @@interface : Update
+ * @Description : 数据更新时校验
+ * @Author : LF
+ * @Date: 2023年03月27日
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Update {
+}

BIN
tr-framework/src/main/resources/avatarFont.ttf


BIN
tr-framework/src/main/resources/ip2region.xdb


+ 22 - 0
tr-modules-api/pom.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>tr-footstone</artifactId>
+        <groupId>cn.tr</groupId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <version>${revision}</version>
+    <artifactId>tr-modules-api</artifactId>
+    <packaging>pom</packaging>
+    <modules>
+        <module>tr-module-system-api</module>
+        <module>tr-module-export-api</module>
+        <module>tr-module-quartz-api</module>
+    </modules>
+
+
+</project>

+ 31 - 0
tr-modules-api/tr-module-export-api/pom.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>tr-modules-api</artifactId>
+        <groupId>cn.tr</groupId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>tr-module-export-api</artifactId>
+    <version>${revision}</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-framework</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-module-system-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 92 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/api/ExcelApi.java

@@ -0,0 +1,92 @@
+package cn.tr.module.api;
+
+import cn.tr.module.export.bean.DynamicFieldDTO;
+import org.springframework.util.Base64Utils;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @ClassName : ExcelApi
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年06月05日
+ */
+
+public interface ExcelApi {
+    /**
+     * 导出文件
+     * @param aClass 导出类
+     * @param data   导出数据
+     * @return 返回base64
+     */
+    default <T> String exportExcel(Class<T> aClass, Collection<T> data){
+        return "data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,"+Base64Utils.encodeToString(doExportExcel(aClass, data));
+    };
+
+    /**
+     * 导出文件
+     * @param aClass 导出类
+     * @param data   导出数据
+     * @return 文件字节码数组
+     */
+    <T> byte[] doExportExcel(Class<T> aClass, Collection<T> data);
+
+    default  <T> String importExcel(String filename, InputStream inputStream, Class<T> aClass) throws Exception {
+        return importExcel(filename,inputStream,aClass,null);
+    }
+
+    /**
+     * 导入文件
+     * @param filename    文件名称
+     * @param inputStream 数据流
+     * @param aClass      文件类
+     * @param  customObj  自定义参数
+     * @return            导入记录id
+     * @throws Exception
+     */
+    <T> String importExcel(String filename, InputStream inputStream, Class<T> aClass, Map<String,Object> customObj,Map<String, String> headers) throws Exception;
+
+    /**
+     * 导入文件
+     * @param filename    文件名称
+     * @param inputStream 数据流
+     * @param aClass      文件类
+     * @param  customObj  自定义参数
+     * @return            导入记录id
+     * @throws Exception
+     */
+    default <T> String importExcel(String filename, InputStream inputStream, Class<T> aClass, Map<String,Object> customObj) throws Exception{
+        return importExcel(filename,inputStream,aClass,customObj,null);
+    };
+
+    /**
+     * 动态表单导出
+     * @param
+     * @param fields 动态表单字段
+     * @return
+     * @param <T>
+     */
+    default <T> String exportExcelDynamic(String sheetName, List<DynamicFieldDTO> fields, Collection<Map<Integer, Object>> data){
+        return "data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,"+Base64Utils.encodeToString(doExportExcelDynamic(sheetName,fields,data));
+    };
+
+    /**
+     * 动态表单导出
+     * @param
+     * @param fields 动态表单字段
+     * @return
+     * @param <T>
+     */
+    default <T> String exportExcelsDynamic(List<String> sheetNames,List<List<DynamicFieldDTO>> fields,List<Collection<Map<Integer, Object>>> datas){
+        return "data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,"+Base64Utils.encodeToString(doExportExcelsDynamic(sheetNames,fields,datas));
+    };
+
+    <T> byte[] doExportExcelDynamic(String sheetName, List<DynamicFieldDTO> fields, Collection<Map<Integer, Object>> data);
+
+
+
+    <T> byte[] doExportExcelsDynamic(List<String> sheetNames,List<List<DynamicFieldDTO>> fields,List<Collection<Map<Integer, Object>>> datas);
+}

+ 28 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/constant/ExcelConstant.java

@@ -0,0 +1,28 @@
+package cn.tr.module.constant;
+
+/**
+ * @ClassName : ExportConstant
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年05月20日
+ */
+
+public interface ExcelConstant {
+    /**
+     * 表格最大行数
+     */
+    Integer MAX_ROW=65535;
+
+
+    String ROW_PARSE_TOPIC="/export/biz/excel";
+
+    String SHEET_FINISH_TOPIC="/export/biz/excel/callback";
+
+    String SHEET_TOTAL_COUNT="sheet_total_count";
+    String SHEET_SUCCESS_COUNT="sheet_success_count";
+    String SHEET_FAIL_COUNT="sheet_fail_count";
+
+    String CUSTOM_OBJ_SHEET_ID="sheetId";
+    String CUSTOM_OBJ_ORG_ID="orgId";
+    String CUSTOM_OBJ_fields_ID="fields";
+}

+ 29 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/annotation/ExcelCellMerge.java

@@ -0,0 +1,29 @@
+package cn.tr.module.export.annotation;
+
+import java.lang.annotation.*;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.ElementType.TYPE_USE;
+
+/**
+ * @Interface : ExcelCellMerge
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年11月08日
+ */
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ExcelCellMerge {
+    /**
+     * 是否自动合并
+     * @return
+     */
+    boolean autoMerge() default true;
+
+    /**
+     * 当单元格内容为空时是否合并
+     * @return
+     */
+    boolean mergeBlank() default false;
+}

+ 36 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/annotation/ExcelPropertySupport.java

@@ -0,0 +1,36 @@
+package cn.tr.module.export.annotation;
+
+
+import cn.tr.module.export.handler.NoneCascadeSelectConverter;
+
+import java.lang.annotation.*;
+
+
+/**
+ * @@interface : ExcelDict
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年05月19日
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ExcelPropertySupport {
+    /**
+     * 字典码
+     * 1、导出时会根据字典码自动生成下拉菜单
+     * 2、导出时会根据字段值自动返回字典值
+     */
+    String dictCode() default "";
+
+
+    ExcelSelect select() default @ExcelSelect(converter = NoneCascadeSelectConverter.class);
+    /**
+     * 导出模板批注
+     */
+    String comment() default "";
+
+    ExcelCellMerge merge()default @ExcelCellMerge(autoMerge = false,mergeBlank = false);
+
+    String columnType() default "";
+}

+ 44 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/annotation/ExcelSelect.java

@@ -0,0 +1,44 @@
+package cn.tr.module.export.annotation;
+
+import cn.tr.module.export.handler.AbstractCascadeSelectConverter;
+import cn.tr.module.export.handler.NoneCascadeSelectConverter;
+
+import java.lang.annotation.*;
+
+/**
+ * @ClassName : ExcelCombo
+ * @Description : 对于excel转入转出集合的支持
+ * @Author : LF
+ * @Date: 2023年06月01日
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ExcelSelect {
+    /**
+     * 集合处理参数
+     */
+    String[] param() default "";
+
+    /**
+     * 集合转换类
+     */
+    Class<? extends AbstractCascadeSelectConverter<?>> converter() default NoneCascadeSelectConverter.class;
+
+    /**
+     * 级联时的级联属性名称
+     * <pre>
+     *     省-> 市-> 区
+     *     省的 cascade 为 市
+     *     市的 cascade 为 区
+     * <pre/>
+     */
+    String cascade() default "";
+
+    /**
+     * 是否允许字典值自定义
+     * 即是否用户输入的内容在字典中不存在
+     */
+    boolean allowCustom() default false;
+
+}

+ 73 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/bean/DynamicFieldDTO.java

@@ -0,0 +1,73 @@
+package cn.tr.module.export.bean;
+
+
+import cn.hutool.core.util.StrUtil;
+import cn.tr.core.utils.JsonUtils;
+import lombok.Data;
+import lombok.ToString;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+import java.util.*;
+/**
+ * 任务单字段实体
+ *
+ * @author lf
+ * @date  2025/01/18 16:45
+ **/
+@Data
+@ToString
+@Slf4j
+public class DynamicFieldDTO  implements Serializable {
+
+    /** id */
+    private String id;
+
+    /** 字段唯一标识 */
+    private String fieldKey;
+
+    /** 字段名称 */
+    private String fieldName;
+
+    /** 字段类型,字典(input_box_type) */
+    private String inputBoxType;
+
+    /** options */
+    private List<String> options;
+
+    /** 是否必填 */
+    private Boolean required;
+
+    /** 排序 */
+    private Integer sort;
+
+    /**
+     * 字段的正则校验
+     */
+    private String regexExp;
+
+    /**
+     * 数字框限制
+     */
+    private List<Double> numOptions;
+
+    public void setOptions(String options) {
+        if (StrUtil.isNotEmpty(options)) {
+            try {
+                this.options = JsonUtils.parseArray(options,String.class);
+            }catch (Exception e){
+                log.info("[动态表单] 下拉框 字符串:{},解析json失败",options);
+            }
+        }
+    }
+
+    public void setNumOptions(String numOptions) {
+        if (StrUtil.isNotEmpty(numOptions)) {
+            try {
+                this.numOptions = JsonUtils.parseArray(numOptions,Double.class);
+            }catch (Exception e){
+                log.info("[动态表单] 下拉框 数字限制:{},解析json失败",options);
+            }
+        }
+    }
+}

+ 37 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/bean/ExportResult.java

@@ -0,0 +1,37 @@
+package cn.tr.module.export.bean;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.tr.module.export.enums.ExportType;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @ClassName : ExportResult
+ * @Description : 文件导出结果
+ * @Author : LF
+ * @Date: 2023年06月15日
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ExportResult {
+    private String fileName;
+    private String base64;
+    private ExportType type;
+    public String getFileName() {
+        if(CollectionUtil.size(StrUtil.split(fileName,"."))>2){
+            return fileName;
+        }
+        return fileName+type.getSuffix();
+    }
+
+    public static ExportResult of(String fileName,String base64){
+        return new ExportResult(fileName,base64,ExportType.EXCEL);
+    }
+
+    public static ExportResult of(String fileName,String base64,ExportType type){
+        return new ExportResult(fileName,base64,type);
+    }
+}

+ 27 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/converter/BooleanConverter.java

@@ -0,0 +1,27 @@
+package cn.tr.module.export.converter;
+
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * @ClassName : BooleanConverter
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年05月19日
+ */
+
+public class BooleanConverter implements Converter<Boolean> {
+    @Override
+    public Class<Boolean> supportJavaTypeKey() {
+        return Boolean.class;
+    }
+
+    @Override
+    public WriteCellData<?> convertToExcelData(Boolean value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
+        WriteCellData<Object> cellData = new WriteCellData<>("");
+        cellData.setStringValue(Boolean.TRUE.equals(value)?"是":"否");
+        return cellData;
+    }
+}

+ 54 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/converter/DictCascadeSelectConverter.java

@@ -0,0 +1,54 @@
+package cn.tr.module.export.converter;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.lang.Pair;
+import cn.hutool.extra.spring.SpringUtil;
+import cn.tr.module.export.handler.AbstractCascadeSelectConverter;
+import cn.tr.module.api.sys.dict.SysDictApi;
+
+import java.util.*;
+
+/**
+ * @ClassName : DictComboConverter
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年06月01日
+ */
+
+public class DictCascadeSelectConverter extends AbstractCascadeSelectConverter<String> {
+    private final SysDictApi dictApi;
+
+    public DictCascadeSelectConverter() {
+        this.dictApi = SpringUtil.getBean(SysDictApi.class);
+    }
+
+    @Override
+    public Map<String, List<String>> buildCascadeSelectList(Collection<String> params) {
+        String dictCode = CollectionUtil.getFirst(params);
+        Map<String, List<String>> result = new HashMap<>();
+        List<Map<String, Map<String, List<String>>>> dicts = dictApi.findChildrenDictsByCode(dictCode);
+        if(CollectionUtil.isNotEmpty(dicts)){
+            for (Map<String, Map<String, List<String>>> dict : dicts) {
+                for (Map<String, List<String>> value : dict.values()) {
+                    value.forEach((k,v)->{
+                        if(CollectionUtil.isNotEmpty(value)){
+                            result.put(k,v);
+                        }
+                    });
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public  List<Pair<String, String>>  doGetSelectPairs(Collection<String> params) {
+        String dictCode = CollectionUtil.getFirst(params);
+        return dictApi.findAllChildrenDictsByCode(dictCode);
+    }
+
+    @Override
+    public Class<String> doSupportJavaTypeKey() {
+        return String.class;
+    }
+}

+ 49 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/converter/SysUserCascadeSelectConverter.java

@@ -0,0 +1,49 @@
+package cn.tr.module.export.converter;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.lang.Pair;
+import cn.hutool.extra.spring.SpringUtil;
+import cn.tr.module.api.sys.user.SysUserApi;
+import cn.tr.module.export.handler.AbstractCascadeSelectConverter;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @ClassName : DictComboConverter
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年06月01日
+ */
+
+public class SysUserCascadeSelectConverter extends AbstractCascadeSelectConverter<String> {
+    private final SysUserApi sysUserApi;
+
+    public SysUserCascadeSelectConverter() {
+        this.sysUserApi = SpringUtil.getBean(SysUserApi.class);
+    }
+
+    @Override
+    public Map<String, List<String>> buildCascadeSelectList(Collection<String> params) {
+        Map<String, List<String>> result = new HashMap<>();
+        List<Pair<String, String>> userIdAndNickName = sysUserApi.selectAllUserIdAndNickName();
+        if(CollectionUtil.isNotEmpty(userIdAndNickName)){
+            for (Pair<String, String> pair : userIdAndNickName) {
+                result.put(pair.getValue(),null);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public  List<Pair<String, String>>  doGetSelectPairs(Collection<String> params) {
+        return sysUserApi.selectAllUserIdAndNickName();
+    }
+
+    @Override
+    public Class<String> doSupportJavaTypeKey() {
+        return String.class;
+    }
+}

+ 20 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/enums/ExportType.java

@@ -0,0 +1,20 @@
+package cn.tr.module.export.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @Enum : ExportType
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年09月25日
+ */
+@AllArgsConstructor
+@Getter
+public enum ExportType {
+    DOCX(".docx"),
+    EXCEL(".xlsx"),
+    ZIP(".zip"),;
+    @Getter
+    private String suffix;
+}

+ 62 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/enums/InputBoxTypeInterface.java

@@ -0,0 +1,62 @@
+package cn.tr.module.export.enums;
+
+/**
+ * 前端输入框类型
+ */
+public interface InputBoxTypeInterface {
+    /**
+     * 文本域
+     */
+    String TEXTAREA = "textarea";
+
+    /**
+     * 普通文本输入框类型。
+     */
+    String TEXT = "input";
+
+    /**
+     * 数字输入框类型。
+     */
+    String NUMBER = "number";
+
+    /**
+     * 多选框类型。
+     */
+    String MULTIPLE_SELECT = "multipleSelect";
+
+    /**
+     * 单选框类型。
+     */
+    String RADIO = "radio";
+
+    /**
+     * 单选框类型。
+     */
+    String SELECT = "select";
+
+    /**
+     * 日期输入框类型。
+     */
+    String DATE = "date";
+
+    /**
+     * 日期段
+     */
+    String DATE_RANGE="dateRange";
+
+    /**
+     * 附件上传
+     * {@link cn.tr.module.api.sys.storage.StoragePojo}
+     */
+    String FILE_STORAGE="fileStorage";
+
+    /**
+     * 超链接
+     */
+    String HYPER_LINKS="hyperlinks";
+
+    /**
+     * 平台引用
+     */
+    String REFERENCE="reference";
+}

+ 179 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/handler/AbstractCascadeSelectConverter.java

@@ -0,0 +1,179 @@
+package cn.tr.module.export.handler;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.lang.Pair;
+import cn.hutool.core.thread.ThreadUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.tr.module.export.annotation.ExcelPropertySupport;
+import cn.tr.module.export.annotation.ExcelSelect;
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.Cell;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.data.ReadCellData;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+import java.util.*;
+
+/**
+ * @ClassName : AbstractCascadeSelectConverter
+ * @Description : 级联选择框构建
+ * @Author : LF
+ * @Date: 2023年06月01日
+ */
+
+public abstract class AbstractCascadeSelectConverter<T> implements Converter<T> {
+    public static final ThreadLocal<Map<String,List<Pair<String, String>>>> threadLocal = ThreadUtil.createThreadLocal(false);
+    @Override
+    public Class<T> supportJavaTypeKey() {
+        return doSupportJavaTypeKey();
+    }
+
+    @Override
+    public T convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
+        if(cellData==null||(StrUtil.isEmpty(cellData.getStringValue())&&ObjectUtil.isNull(cellData.getNumberValue()))){
+            return null;
+        }
+        CellDataTypeEnum type = cellData.getType();
+        ExcelPropertySupport excelPropertySupport = contentProperty.getField().getAnnotation(ExcelPropertySupport.class);
+        String content = null;
+        if(CellDataTypeEnum.NUMBER.equals(type)){
+            content=String.valueOf(cellData.getNumberValue());
+        }else{
+            content=cellData.getStringValue();
+        }
+        //校验是否存在
+        T javaValue = excelConverterJavaValue(content,getSelectPairs(getParams(excelPropertySupport)));
+        if(ObjectUtil.isNull(javaValue)){
+            ExcelSelect select = excelPropertySupport.select();
+            if(select.allowCustom()){
+                return (T) content;
+            }
+            throw new UnsupportedOperationException("所填值{"+content+"}不合法,请重新下载模板导入");
+        }
+        return javaValue;
+    }
+
+
+    @Override
+    public WriteCellData<?> convertToExcelData(T value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
+        WriteCellData<String> cellData = new WriteCellData<>("");
+        //java 转execl,java中填写的是key值,填写value(key)放入execl中
+        ExcelPropertySupport excelPropertySupport = contentProperty.getField().getAnnotation(ExcelPropertySupport.class);
+        if(excelPropertySupport!=null){
+            cellData.setStringValue(javaConverterExcelCellValue(value,getSelectPairs(getParams(excelPropertySupport)),excelPropertySupport.select()));
+        }
+        return cellData;
+    }
+
+    private Collection<String> getParams(ExcelPropertySupport propertySupport){
+        if(propertySupport!=null){
+            return Arrays.asList(propertySupport.select().param());
+        }
+        return null;
+    }
+
+    /**
+     * 根据所给的值将java的值转变为execl所展示的值
+     * @param content execl内容
+     * @param selectValues 选择框内容
+     * @return
+     */
+    public T excelConverterJavaValue(String content,List<Pair<String, String>> selectValues){
+        if(doSupportJavaTypeKey().equals(Boolean.class)){
+            if(StrUtil.equals(content,"1")||StrUtil.equals(content,"true")){
+                return Convert.convert(doSupportJavaTypeKey(),Boolean.TRUE);
+            }else {
+                return Convert.convert(doSupportJavaTypeKey(),Boolean.FALSE);
+            }
+        }
+        for (Pair<String, String> pair : selectValues) {
+            if (ObjectUtil.equals(content, pair.getValue())) {
+                return (T) pair.getKey();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 构建菜单获取级联列表
+     * @param params 查询列表参数集合
+     * @return List<Pair<String,List<String>>> 级联列表
+     * <pre>
+     * [{"河南省",["郑州市","新乡市","平顶山市"]},
+     * {"河北省",["石家庄市","邯郸市"]}]
+     *
+     * Pair中的value值为当前字段所需要的列表,若不存在上级级联,返回一个Pair即可,Key为''或null
+     * [{"",["是","否"]}]
+     * <pre/>
+     */
+    public Map<String, List<String>>  buildCascadeSelectList(Collection<String> params){
+        HashMap<String, List<String>> result = new HashMap<>();
+        List<Pair<String, String>> selectPairs = getSelectPairs(params);
+        for (Pair<String, String> selectPair : selectPairs) {
+            result.put(selectPair.getValue(),null);
+        }
+        return result;
+    };
+
+
+    /**
+     * 获取选择框中的键值对
+     * @param params
+     * @return  在excel转java 和 java转excel时使用
+     */
+    public List<Pair<String, String>>  getSelectPairs(Collection<String> params){
+        String key=this.getClass().getSimpleName();
+        if(CollectionUtil.isNotEmpty(params)){
+            key=key+"-"+CollectionUtil.join(params,"-");
+        }
+        Map<String,List<Pair<String, String>>> selectPairsMap = threadLocal.get();
+        if(selectPairsMap==null){
+            selectPairsMap=new WeakHashMap<>();
+        }
+        List<Pair<String, String>> result=selectPairsMap.get(key);
+        if(ObjectUtil.isNull(result)){
+            result=doGetSelectPairs(params);
+        }
+        selectPairsMap.put(key,result);
+        threadLocal.set(selectPairsMap);
+        return result;
+    };
+
+
+    /**
+     * 获取选择框中的键值对
+     * @param params
+     * @return
+     * <pre>
+     *     java : excel
+     *     key:  value
+     *     <pre/>
+     */
+    public abstract List<Pair<String, String>> doGetSelectPairs(Collection<String> params);
+
+    /**
+     * 根据所给的值将java的值转变为execl所展示的值
+     * @param value  java所给的value值
+     * @param selectValues 选择框键值对
+     * @return
+     */
+    public String javaConverterExcelCellValue(T value,List<Pair<String, String>> selectValues,ExcelSelect excelSelect){
+        if(ObjectUtil.isNull(value)||CollectionUtil.isEmpty(selectValues)){
+            return "";
+        }
+        for (Pair<String, String> pair : selectValues) {
+            if (ObjectUtil.equals(value, pair.getKey())) {
+                return pair.getValue();
+            }
+        }
+        if(excelSelect!=null&&excelSelect.allowCustom()){
+            return String.valueOf(value);
+        }
+        return null;
+    };
+
+    public abstract Class<T> doSupportJavaTypeKey();
+}

+ 65 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/handler/AbstractDataHandler.java

@@ -0,0 +1,65 @@
+package cn.tr.module.export.handler;
+
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.ReflectUtil;
+import cn.tr.core.utils.ValidationUtils;
+import cn.tr.module.register.ExportSampleRegister;
+import lombok.Getter;
+import org.springframework.util.ReflectionUtils;
+
+import java.util.*;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import java.util.Map;
+
+/**
+ * @ClassName : AbstractDataHandler
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年05月23日
+ */
+
+public abstract class AbstractDataHandler<T> implements DataHandler<T> {
+    public static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
+    @Getter
+    private Class<T> aClass;
+
+    public AbstractDataHandler(Class<T> aClass) {
+        this.aClass = aClass;
+        List<T> sample = getSample();
+        if(CollectionUtil.isEmpty(sample)){
+            sample=new ArrayList<>();
+        }else {
+            sample=new ArrayList<>(sample);
+        }
+        sample.add(buildDefaultOb());
+        ExportSampleRegister.registerSample(aClass,sample);
+    }
+
+    private T buildDefaultOb(){
+        T t = ReflectUtil.newInstance(aClass);
+        ReflectionUtils.doWithFields(aClass,field -> {
+            if(String.class.equals(field.getType())){
+                field.setAccessible(true);
+                field.set(t,"……");
+            }
+        });
+        return t;
+    }
+    /**
+     * 模板中的样例数组
+     * @return
+     */
+    public List<T> getSample(){
+        return null;
+    }
+
+    @Override
+    public void handle(T obj, Map<String,Object> customObj) {
+        ValidationUtils.validate(validator,obj);
+        doHandle(obj);
+    }
+
+    public abstract void doHandle(T obj);
+}

+ 20 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/handler/DataHandler.java

@@ -0,0 +1,20 @@
+package cn.tr.module.export.handler;
+
+import java.util.Map;
+
+/**
+ * @ClassName : DataHandler
+ * @Description : 导入数据的处理器
+ * @Author : LF
+ * @Date: 2023年05月23日
+ */
+
+public interface DataHandler<T> {
+
+    /**
+     * 对导入对象进行处理
+     * @param obj       解析对象
+     * @param customObj 自定义参数
+     */
+    void handle(T obj, Map<String,Object> customObj);
+}

+ 27 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/export/handler/NoneCascadeSelectConverter.java

@@ -0,0 +1,27 @@
+package cn.tr.module.export.handler;
+
+
+import cn.hutool.core.lang.Pair;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @ClassName : NoneComboConverter
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年06月01日
+ */
+
+public class NoneCascadeSelectConverter extends AbstractCascadeSelectConverter<Void> {
+
+    @Override
+    public List<Pair<String, String>> doGetSelectPairs(Collection<String> params) {
+        return null;
+    }
+
+    @Override
+    public Class<Void> doSupportJavaTypeKey() {
+        return Void.class;
+    }
+}

+ 26 - 0
tr-modules-api/tr-module-export-api/src/main/java/cn/tr/module/register/ExportSampleRegister.java

@@ -0,0 +1,26 @@
+package cn.tr.module.register;
+import java.util.*;
+/**
+ * @ClassName : ExportSampleRegister
+ * @Description; 导出的样例注册
+ * @Author : LF
+ * @Date: 2023年09月20日
+ */
+
+public class ExportSampleRegister {
+
+    private static Map<Class<?>,Collection<?>> sampleMap=new HashMap<>();
+
+    public static <T>  void registerSample(Class<T>  tClass,Collection<T>  sample){
+        sampleMap.put(tClass,sample);
+    }
+
+    public static <T> Collection<T> getSample(Class<T> tClass){
+        return (Collection<T>) sampleMap.get(tClass);
+    }
+
+
+    public static List<List<String>> getSample(List<String> aClass) {
+        return (List<List<String>>) sampleMap.get(aClass);
+    }
+}

+ 14 - 0
tr-modules-api/tr-module-quartz-api/pom.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>tr-modules-api</artifactId>
+        <groupId>cn.tr</groupId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>tr-module-quartz-api</artifactId>
+
+</project>

+ 22 - 0
tr-modules-api/tr-module-quartz-api/src/main/java/cn/tr/module/api/quartz/JointJobQuartzApi.java

@@ -0,0 +1,22 @@
+package cn.tr.module.api.quartz;
+
+import cn.tr.module.api.quartz.dto.JointJobQuartzDTO;
+
+import java.util.Collection;
+
+/**
+ * @ClassName : JointJobQuartzApi
+ * @Description : 数据对接所使用的定时任务的api
+ * @Author : LF
+ * @Date: 2024年05月08日
+ */
+
+public interface JointJobQuartzApi {
+    void addOrUpdateJointJob(JointJobQuartzDTO source) throws Exception;
+
+    void startJointJob(String jointJobId)throws Exception;
+
+    void pauseJointJob(String jointJobId) throws Exception;
+
+    void delJointJobs(Collection<String> jointJobIds) throws Exception;
+}

+ 34 - 0
tr-modules-api/tr-module-quartz-api/src/main/java/cn/tr/module/api/quartz/dto/JointJobQuartzDTO.java

@@ -0,0 +1,34 @@
+package cn.tr.module.api.quartz.dto;
+
+/**
+ * @ClassName : JointJobQuartzDTO
+ * @Description :
+ * @Author : LF
+ * @Date: 2024年05月08日
+ */
+
+public class JointJobQuartzDTO {
+    private String jointJobId;
+    private String cron;
+
+    public JointJobQuartzDTO(String jointJobId, String cron) {
+        this.jointJobId = jointJobId;
+        this.cron = cron;
+    }
+
+    public String getJointJobId() {
+        return jointJobId;
+    }
+
+    public void setJointJobId(String jointJobId) {
+        this.jointJobId = jointJobId;
+    }
+
+    public String getCron() {
+        return cron;
+    }
+
+    public void setCron(String cron) {
+        this.cron = cron;
+    }
+}

+ 22 - 0
tr-modules-api/tr-module-system-api/pom.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>tr-modules-api</artifactId>
+        <groupId>cn.tr</groupId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>tr-module-system-api</artifactId>
+    <version>${revision}</version>
+    <dependencies>
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-framework</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 32 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/dict/SysDictApi.java

@@ -0,0 +1,32 @@
+package cn.tr.module.api.sys.dict;
+
+import cn.hutool.core.lang.Pair;
+
+import java.util.*;
+/**
+ * @ClassName : SysDictApi
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年05月19日
+ */
+
+public interface SysDictApi {
+
+    /**
+     * 查找字典值
+     * @param dictCode 字典编码
+     * @return
+     */
+    String findDictItem(String dictCode);
+
+    /**
+     * 找到字典码下的所有字典值
+     * @param dictCode
+     * @return
+     */
+    List<Pair<String,String>> findAllChildrenDictsByCode(String dictCode);
+
+    List<Map<String, Map<String,List<String>>>> findChildrenDictsByCode(String dictCode);
+
+    Map<String, String> findAllChildrenDictsMapByCode(String dictCode);
+}

+ 14 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/dict/SysFirstCateApi.java

@@ -0,0 +1,14 @@
+package cn.tr.module.api.sys.dict;
+
+import java.util.List;
+
+/**
+ * @Author zzy
+ * @Data 2025/3/11
+ * @Version 1.0
+ * @Description XXX
+ */
+public interface SysFirstCateApi {
+
+
+}

+ 45 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/log/annotation/OperateLog.java

@@ -0,0 +1,45 @@
+package cn.tr.module.api.sys.log.annotation;
+
+import cn.tr.module.api.sys.log.enums.LoginType;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 操作日志注解
+ *
+ * @author 芋道源码
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface OperateLog {
+
+    // ========== 模块字段 ==========
+
+    /**
+     * 操作名
+     *
+     * 为空时,会尝试读取 {@link io.swagger.annotations.ApiOperation#value()} ()} 属性
+     */
+    String name() default "";
+
+    LoginType loginType() default  LoginType.oper;
+
+    // ========== 开关字段 ==========
+
+    /**
+     * 是否记录操作日志
+     */
+    boolean enable() default true;
+    /**
+     * 是否记录方法参数
+     */
+    boolean logArgs() default true;
+    /**
+     * 是否记录方法结果的数据
+     */
+    boolean logResultData() default true;
+
+}

+ 64 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/log/bo/OperateLogBO.java

@@ -0,0 +1,64 @@
+package cn.tr.module.api.sys.log.bo;
+
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 操作日志
+ *
+ * @author 芋道源码
+ */
+@Data
+public class OperateLogBO {
+    /**
+     * 操作名称
+     */
+    private String opName;
+    /**
+     * 用户IP
+     */
+    private String userIp;
+    /**
+     * 用户地址
+     */
+    private String loginLocation;
+    /**
+     * 类名称
+     */
+    private String javaClass;
+    /**
+     * 方法名称
+     */
+    private String javaMethod;
+    /**
+     * 操作时间
+     */
+    private Date opTime;
+    /**
+     * 执行时长
+     */
+    private Integer duration;
+    /**
+     * 操作用户
+     */
+    private String opUser;
+    /**
+     * 请求参数
+     */
+    private String requestArgs;
+    /**
+     * 浏览器UserAgent
+     */
+    private String userAgent;
+    /**
+     * 响应结果
+     */
+    private String resultJson;
+
+    /**
+     * 日志类型
+     * {@link cn.tr.module.api.sys.log.enums.LoginType}
+     */
+    private String type;
+}

+ 30 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/log/enums/LoginType.java

@@ -0,0 +1,30 @@
+package cn.tr.module.api.sys.log.enums;
+
+import lombok.Getter;
+
+/**
+ * 操作日志的操作类型
+ *
+ * @author ruoyi
+ */
+@Getter
+public enum LoginType {
+
+    /**
+     * 操作日志
+     */
+    oper,
+    /**
+     * 异常日志
+     */
+    exception,
+    /**
+     * 登录日志
+     */
+    login,
+    /**
+     * 登出日志
+     */
+    logout;
+
+}

+ 14 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/notice/SysNoticeApi.java

@@ -0,0 +1,14 @@
+package cn.tr.module.api.sys.notice;
+
+
+import java.util.*;
+/**
+ * @ClassName : SysNoticeApi
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年11月06日
+ */
+
+public interface SysNoticeApi {
+    List<SysNoticeBO> selectList();
+}

+ 45 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/notice/SysNoticeBO.java

@@ -0,0 +1,45 @@
+package cn.tr.module.api.sys.notice;
+
+import lombok.Data;
+import lombok.ToString;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 系统公告传输对象
+ *
+ * @author lf
+ * @date  2023/11/06 10:12
+ **/
+@Data
+@ToString
+public class SysNoticeBO implements Serializable {
+    private static final long serialVersionUID = 1L;
+    private String id;
+
+    /**
+     * 标题
+     */
+    private String title;
+
+    /**
+     * 类型
+     */
+    private String type;
+
+    /**
+     * 内容
+     */
+    private String content;
+
+
+    /**
+     * 发布人昵称
+     */
+    private String updateNickName;
+
+    /**
+     * 发布时间
+     */
+    private Date updateTime;
+}

+ 41 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/storage/StoragePojo.java

@@ -0,0 +1,41 @@
+package cn.tr.module.api.sys.storage;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @ClassName : StoragePojo
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年07月19日
+ */
+@Data
+public class StoragePojo implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 存储记录id
+     */
+    private String id;
+
+    /**
+     * 文件名称
+     */
+    private String realName;
+
+    /**
+     * 绝对路径
+     */
+    private String absolutePath;
+
+
+    /**
+     * 文件大小(KB)
+     */
+    private Integer size;
+
+    /**
+     * 文件后缀
+     */
+    private String suffix;
+}

+ 53 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/storage/SysStorageApi.java

@@ -0,0 +1,53 @@
+package cn.tr.module.api.sys.storage;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @ClassName : SysStorageApi
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年05月18日
+ */
+
+public interface SysStorageApi {
+    /**
+     * 上传文件
+     * @param content 文件内容
+     * @param filename 上传文件名称
+     * @return  文件id
+     */
+    default String upload(byte[] content,String filename) throws Exception{
+        return upload(content, filename, null);
+    };
+
+    /**
+     * 上传文件
+     * @param content 文件内容
+     * @param filename 上传文件名称
+     * @param millis 过期时间(毫秒)
+     * @return  文件id
+     */
+    String upload(byte[] content, String filename,Long millis) throws Exception;
+
+    /**
+     * 删除文件
+     * @param fileId
+     */
+    void removeById(String fileId);
+
+    /**
+     * 获取文件
+     * @param recordId 文件记录id
+     * @return
+     */
+    byte[] obtainContent(String recordId) throws Exception;
+
+    /**
+     * 批量查询存储文件信息
+     * @param storageIds 存储文件id集合
+     * @return
+     */
+    Map<String,StoragePojo> findStorage(Collection<String> storageIds);
+}

+ 13 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/tenant/SysTenantApi.java

@@ -0,0 +1,13 @@
+package cn.tr.module.api.sys.tenant;
+
+import java.util.*;
+/**
+ * @ClassName : SysTenantApi
+ * @Description :
+ * @Author : LF
+ * @Date: 2024年03月11日
+ */
+
+public interface SysTenantApi {
+    List<SysTenantPojo> selectAll();
+}

+ 36 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/tenant/SysTenantPojo.java

@@ -0,0 +1,36 @@
+package cn.tr.module.api.sys.tenant;
+
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @ClassName : SysTenantPO
+ * @Description : 租户
+ * @Author : LF
+ * @Date: 2023年04月05日
+ */
+@Data
+public class SysTenantPojo implements Serializable {
+    private static final long serialVersionUID = -4624795887273192599L;
+    /**
+     * 租户id
+     */
+    private String tenantId;
+
+    /**
+     * 租户名称
+     */
+    private String name;
+
+    /**
+     * 租户类型
+     */
+    private String type;
+
+    /**
+     * 租户状态 0、开启 1、关闭
+     */
+    private Boolean disable;
+}

+ 17 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/CbbMenuDTO.java

@@ -0,0 +1,17 @@
+package cn.tr.module.api.sys.user;
+
+import lombok.Data;
+
+/**
+ * @ClassName : BizCbbMenuDTO
+ * @Description :
+ * @Author : LF
+ * @Date: 2024年11月13日
+ */
+@Data
+public class CbbMenuDTO {
+    private String id;
+    private String name;
+    private Integer count=0;
+    private String remark;
+}

+ 29 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysMenuApi.java

@@ -0,0 +1,29 @@
+package cn.tr.module.api.sys.user;
+
+
+import cn.hutool.core.lang.Pair;
+import java.util.*;
+/**
+ * @Interface : SysMenuApi
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月14日
+ */
+
+public interface SysMenuApi {
+    /**
+     * 代码生成菜单插入
+     * @param parentId          父级菜单id
+     * @param busName           业务名称
+     * @param frontModuleName   前端模块名称
+     * @param functionName      功能名称
+     * @param permissionPrefix  权限前缀
+     * @return <menuId,componentPath> <菜单id,组件路径>
+     */
+    Pair<String,String> addForGenMenu(String parentId, String busName, String frontModuleName,String backModuleName,String permissionPrefix, String functionName);
+
+
+    List<CbbMenuDTO> selectCbbMenu(String parentId);
+
+    List<Pair<String,String>> selectAllMenuIdAndName();
+}

+ 34 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysOrgApi.java

@@ -0,0 +1,34 @@
+package cn.tr.module.api.sys.user;
+
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * @Interface : SysMenuApi
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月14日
+ */
+
+public interface SysOrgApi {
+    /**
+     * orgIds 组织id
+     * @return <orgId,orgName> <组织id,组织名称>
+     */
+    Map<String,String> selectOrgNamesById(Collection<String> orgIds);
+
+    SysOrgDTO selectOrgNamesById(String orgId);
+
+    /**
+     * 查询所有组织机构
+     * @return <orgId,orgName> <组织id,组织名称>
+     */
+    Map<String,String> selectAll();
+
+    String selectOrgIdByName(String orgIdName);
+
+    String selectOrgIdByNodeName(String orgNodeName);
+
+    String selectOrgLeaderId(String orgId);
+}

+ 43 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysOrgDTO.java

@@ -0,0 +1,43 @@
+package cn.tr.module.api.sys.user;
+
+import cn.tr.core.annotation.Comment;
+import lombok.Data;
+import lombok.ToString;
+
+/**
+ * 部门对象 sys_menu
+ *
+ * @author tr
+ * @date 2022-11-23 15:34:37
+ */
+@Data
+@ToString
+public class SysOrgDTO  {
+    @Comment(value = "主键")
+    private String id;
+
+    @Comment(value = "父级id 顶级目录的父级id为0")
+    private String parentId;
+
+    @Comment("排序")
+    private Integer sort;
+
+    @Comment(value = "部门名称")
+    private String name;
+
+    @Comment(value = "部门电话")
+    private String phone;
+
+    @Comment(value = "部门领导人")
+    private String leaderUserId;
+    
+    @Comment(value = "部门邮箱")
+    private String email;
+
+    @Comment(value = "状态 0正常;1停用")
+    private Boolean disable;
+
+    @Comment("节点路径")
+    private String nodePath;
+
+}

+ 18 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysRoleApi.java

@@ -0,0 +1,18 @@
+package cn.tr.module.api.sys.user;
+import cn.hutool.core.lang.Pair;
+
+import java.util.*;
+
+/**
+ * @Author zzy
+ * @Data 2024/12/18
+ * @Version 1.0
+ * @Description XXX
+ */
+public interface SysRoleApi {
+
+    /**
+     * 查询所有角色id和名称
+     */
+    List<Pair<String,String>> selectAllRoleIdAndName();
+}

+ 17 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysRolesDTO.java

@@ -0,0 +1,17 @@
+package cn.tr.module.api.sys.user;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * @ClassName : SysRolePO
+ * @Description : 系统角色
+ * @Author : LF
+ * @Date: 2023年04月03日
+ */
+@Data
+@AllArgsConstructor(staticName = "of")
+public class SysRolesDTO {
+    private String id;
+    private String code;
+}

+ 33 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysUserApi.java

@@ -0,0 +1,33 @@
+package cn.tr.module.api.sys.user;
+
+import cn.hutool.core.lang.Pair;
+
+import java.util.*;
+/**
+ * @ClassName : SysUserApi
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年09月28日
+ */
+
+public interface SysUserApi {
+    /**
+     * 查询所有用户的id和昵称
+     * @return
+     */
+    List<Pair<String,String >> selectAllUserIdAndNickName();
+
+    Collection<String> findUserPermission(String userId);
+
+    List<SysUserSmallDTO> findUsers(Collection<String> userIds);
+
+    List<SysRolesDTO> findAllRoleByUserId(String userId);
+
+    Collection<String> findAllOrgByUserId(String userId);
+
+    Collection<String> findAllOwnerOrgByUserId(String userId);
+
+    List<SysUserSmallDTO> findUsersByOrgId(String orgId);
+
+    String selectUserIdByNickName(String nickName);
+}

+ 22 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysUserSmallDTO.java

@@ -0,0 +1,22 @@
+package cn.tr.module.api.sys.user;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @ClassName : SysUserSmallDTO
+ * @Description :
+ * @Author : LF
+ * @Date: 2025年01月14日
+ */
+@Data
+@AllArgsConstructor(staticName = "of")
+@NoArgsConstructor
+public class SysUserSmallDTO {
+    private String id;
+
+    private String username;
+
+    private String nickname;
+}

+ 23 - 0
tr-modules/pom.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>tr-footstone</artifactId>
+        <groupId>cn.tr</groupId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>tr-modules</artifactId>
+    <packaging>pom</packaging>
+    <version>${revision}</version>
+    <modules>
+        <module>tr-module-system</module>
+        <module>tr-module-gen</module>
+        <module>tr-module-export</module>
+        <module>tr-module-quartz</module>
+    </modules>
+
+
+</project>

+ 75 - 0
tr-modules/tr-module-export/pom.xml

@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>tr-modules</artifactId>
+        <groupId>cn.tr</groupId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>tr-module-export</artifactId>
+    <version>${revision}</version>
+
+    <description>文件导出中心</description>
+
+    <dependencies>
+
+        <!--<dependency>-->
+            <!--<groupId>cn.tr</groupId>-->
+            <!--<artifactId>tr-spring-boot-starter-plugin-biz-tenant</artifactId>-->
+        <!--</dependency>-->
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-spring-boot-starter-plugin-eventbus</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-spring-boot-starter-plugin-mybatis</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-spring-boot-starter-plugin-satoken</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-spring-boot-starter-plugin-doc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-spring-boot-starter-plugin-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-module-export-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-module-system-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-spring-boot-starter-plugin-lock</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-spring-boot-starter-plugin-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>

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