mirror of
				https://github.com/dromara/RuoYi-Vue-Plus.git
				synced 2025-11-04 00:03:51 +08:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			c054029cfc
			...
			v5.2.2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					098d3347a0 | ||
| 
						 | 
					08d4493994 | ||
| 
						 | 
					367d739e2d | ||
| 
						 | 
					d6688a367d | ||
| 
						 | 
					0b331796e2 | ||
| 
						 | 
					456620b638 | 
@@ -2,7 +2,7 @@
 | 
			
		||||
  <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
 | 
			
		||||
    <deployment type="dockerfile">
 | 
			
		||||
      <settings>
 | 
			
		||||
        <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.3.1" />
 | 
			
		||||
        <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.2.2" />
 | 
			
		||||
        <option name="buildOnly" value="true" />
 | 
			
		||||
        <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
 | 
			
		||||
      </settings>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
<component name="ProjectRunConfigurationManager">
 | 
			
		||||
  <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
 | 
			
		||||
  <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="演示机">
 | 
			
		||||
    <deployment type="dockerfile">
 | 
			
		||||
      <settings>
 | 
			
		||||
        <option name="imageTag" value="ruoyi/ruoyi-server:5.3.1" />
 | 
			
		||||
        <option name="imageTag" value="ruoyi/ruoyi-server:5.2.2" />
 | 
			
		||||
        <option name="buildOnly" value="true" />
 | 
			
		||||
        <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
 | 
			
		||||
      </settings>
 | 
			
		||||
    </deployment>
 | 
			
		||||
    <method v="2" />
 | 
			
		||||
  </configuration>
 | 
			
		||||
</component>
 | 
			
		||||
</component>
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
  <configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
 | 
			
		||||
    <deployment type="dockerfile">
 | 
			
		||||
      <settings>
 | 
			
		||||
        <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.3.1" />
 | 
			
		||||
        <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.2.2" />
 | 
			
		||||
        <option name="buildOnly" value="true" />
 | 
			
		||||
        <option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
 | 
			
		||||
      </settings>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								README.md
									
									
									
									
									
								
							@@ -6,28 +6,24 @@
 | 
			
		||||
 | 
			
		||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
 | 
			
		||||
[](https://github.com/dromara/RuoYi-Vue-Plus)
 | 
			
		||||
[](https://gitcode.com/dromara/RuoYi-Vue-Plus)
 | 
			
		||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE)
 | 
			
		||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
 | 
			
		||||
[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
 | 
			
		||||
<br>
 | 
			
		||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
 | 
			
		||||
[]()
 | 
			
		||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
 | 
			
		||||
[]()
 | 
			
		||||
[]()
 | 
			
		||||
[]()
 | 
			
		||||
 | 
			
		||||
> Dromara RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架)
 | 
			
		||||
> RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架)
 | 
			
		||||
 | 
			
		||||
> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br>
 | 
			
		||||
活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源
 | 
			
		||||
 | 
			
		||||
> 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
 | 
			
		||||
 | 
			
		||||
> 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)<br>
 | 
			
		||||
> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)<br>
 | 
			
		||||
> 成员前端项目地址: 基于soybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)<br>
 | 
			
		||||
> 成员项目地址: 删除多租户与工作流 [RuoYi-Vue-Plus-Single](https://gitee.com/ColorDreams/RuoYi-Vue-Plus-Single)<br>
 | 
			
		||||
> 前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)
 | 
			
		||||
 | 
			
		||||
> 文档地址: [plus-doc](https://plus-doc.dromara.org) 文档在华为云上如果打不开大概率是DNS问题 可以尝试切换网络等方式(或者科学上网)
 | 
			
		||||
> 文档地址: [plus-doc](https://plus-doc.dromara.org)
 | 
			
		||||
 | 
			
		||||
## 赞助商
 | 
			
		||||
 | 
			
		||||
@@ -35,8 +31,6 @@ MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey <br>
 | 
			
		||||
CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
 | 
			
		||||
数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/ <br>
 | 
			
		||||
引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc <br>
 | 
			
		||||
<font color="red">**启山商城系统 多租户商城源码可免费商用可二次开发 - https://www.73app.cn/** </font><br>
 | 
			
		||||
Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11 <br>
 | 
			
		||||
[如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group)
 | 
			
		||||
 | 
			
		||||
# 本框架与RuoYi的功能差异
 | 
			
		||||
@@ -78,7 +72,7 @@ Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11 <br>
 | 
			
		||||
| 邮件          | 采用 mail-api 通用协议支持大部分邮件厂商                                                                                         | 不支持                                                                                |
 | 
			
		||||
| 接口文档        | 采用 SpringDoc、javadoc 无注解零入侵基于java注释<br/>只需把注释写好 无需再写一大堆的文档注解了                                                     | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成                                                | 
 | 
			
		||||
| 校验框架        | 采用 Validation 支持注解与工具类校验 注解支持国际化                                                                                  | 仅支持注解 且注解不支持国际化                                                                    |
 | 
			
		||||
| Excel框架     | 采用 FastExcel(原Alibaba EasyExcel) 基于插件化<br/>框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等                                   | 基于 POI 手写实现 功能有限 复杂 扩展性差                                                           |
 | 
			
		||||
| Excel框架     | 采用 Alibaba EasyExcel 基于插件化<br/>框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等                                               | 基于 POI 手写实现 功能有限 复杂 扩展性差                                                           |
 | 
			
		||||
| 工作流支持       | 支持各种复杂审批 转办 委派 加减签 会签 或签 票签 等功能                                                                                   | 无                                                                                  |
 | 
			
		||||
| 工具类框架       | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码                                                       | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等                                            | 
 | 
			
		||||
| 监控框架        | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制<br/>实时监控服务状态 框架还为其扩展了在线日志查看监控                                    | 无                                                                                  | 
 | 
			
		||||
@@ -116,6 +110,7 @@ Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11 <br>
 | 
			
		||||
| 系统接口   | 根据业务代码自动生成相关的api接口文档                                                 | 支持  | 支持               |
 | 
			
		||||
| 服务监控   | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等                                  | 支持  | 仅支持单机CPU、内存、磁盘监控 |
 | 
			
		||||
| 缓存监控   | 对系统的缓存信息查询,命令统计等。                                                    | 支持  | 支持               |
 | 
			
		||||
| 在线构建器  | 拖动表单元素生成相应的HTML代码。                                                   | 支持  | 支持               |
 | 
			
		||||
| 使用案例   | 系统的一些功能案例                                                            | 支持  | 不支持              |
 | 
			
		||||
 | 
			
		||||
## 参考文档
 | 
			
		||||
@@ -170,8 +165,8 @@ Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11 <br>
 | 
			
		||||
|  |  |
 | 
			
		||||
|  |  |
 | 
			
		||||
|  |  |
 | 
			
		||||
|  |  |
 | 
			
		||||
|  |  |
 | 
			
		||||
|  |  |
 | 
			
		||||
|  |  |
 | 
			
		||||
|  |  |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										121
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										121
									
								
								pom.xml
									
									
									
									
									
								
							@@ -10,55 +10,56 @@
 | 
			
		||||
 | 
			
		||||
    <name>RuoYi-Vue-Plus</name>
 | 
			
		||||
    <url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
 | 
			
		||||
    <description>Dromara RuoYi-Vue-Plus多租户管理系统</description>
 | 
			
		||||
    <description>RuoYi-Vue-Plus多租户管理系统</description>
 | 
			
		||||
 | 
			
		||||
    <properties>
 | 
			
		||||
        <revision>5.3.1</revision>
 | 
			
		||||
        <spring-boot.version>3.4.5</spring-boot.version>
 | 
			
		||||
        <revision>5.2.2</revision>
 | 
			
		||||
        <spring-boot.version>3.2.9</spring-boot.version>
 | 
			
		||||
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 | 
			
		||||
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 | 
			
		||||
        <java.version>17</java.version>
 | 
			
		||||
        <mybatis.version>3.5.16</mybatis.version>
 | 
			
		||||
        <springdoc.version>2.8.5</springdoc.version>
 | 
			
		||||
        <springdoc.version>2.6.0</springdoc.version>
 | 
			
		||||
        <therapi-javadoc.version>0.15.0</therapi-javadoc.version>
 | 
			
		||||
        <fastexcel.version>1.2.0</fastexcel.version>
 | 
			
		||||
        <easyexcel.version>4.0.2</easyexcel.version>
 | 
			
		||||
        <velocity.version>2.3</velocity.version>
 | 
			
		||||
        <satoken.version>1.42.0</satoken.version>
 | 
			
		||||
        <mybatis-plus.version>3.5.11</mybatis-plus.version>
 | 
			
		||||
        <satoken.version>1.38.0</satoken.version>
 | 
			
		||||
        <mybatis-plus.version>3.5.7</mybatis-plus.version>
 | 
			
		||||
        <p6spy.version>3.9.1</p6spy.version>
 | 
			
		||||
        <hutool.version>5.8.35</hutool.version>
 | 
			
		||||
        <spring-boot-admin.version>3.4.5</spring-boot-admin.version>
 | 
			
		||||
        <redisson.version>3.45.1</redisson.version>
 | 
			
		||||
        <hutool.version>5.8.31</hutool.version>
 | 
			
		||||
        <okhttp.version>4.10.0</okhttp.version>
 | 
			
		||||
        <spring-boot-admin.version>3.2.3</spring-boot-admin.version>
 | 
			
		||||
        <redisson.version>3.34.1</redisson.version>
 | 
			
		||||
        <lock4j.version>2.2.7</lock4j.version>
 | 
			
		||||
        <dynamic-ds.version>4.3.1</dynamic-ds.version>
 | 
			
		||||
        <snailjob.version>1.5.0</snailjob.version>
 | 
			
		||||
        <mapstruct-plus.version>1.4.6</mapstruct-plus.version>
 | 
			
		||||
        <snailjob.version>1.1.2</snailjob.version>
 | 
			
		||||
        <mapstruct-plus.version>1.4.4</mapstruct-plus.version>
 | 
			
		||||
        <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
 | 
			
		||||
        <lombok.version>1.18.36</lombok.version>
 | 
			
		||||
        <bouncycastle.version>1.80</bouncycastle.version>
 | 
			
		||||
        <justauth.version>1.16.7</justauth.version>
 | 
			
		||||
        <lombok.version>1.18.34</lombok.version>
 | 
			
		||||
        <bouncycastle.version>1.76</bouncycastle.version>
 | 
			
		||||
        <justauth.version>1.16.6</justauth.version>
 | 
			
		||||
        <!-- 离线IP地址定位库 -->
 | 
			
		||||
        <ip2region.version>2.7.0</ip2region.version>
 | 
			
		||||
        <undertow.version>2.3.15.Final</undertow.version>
 | 
			
		||||
 | 
			
		||||
        <!-- OSS 配置 -->
 | 
			
		||||
        <aws.sdk.version>2.28.22</aws.sdk.version>
 | 
			
		||||
        <aws.sdk.version>2.25.15</aws.sdk.version>
 | 
			
		||||
        <aws.crt.version>0.29.13</aws.crt.version>
 | 
			
		||||
        <!-- SMS 配置 -->
 | 
			
		||||
        <sms4j.version>3.3.4</sms4j.version>
 | 
			
		||||
        <sms4j.version>3.3.2</sms4j.version>
 | 
			
		||||
        <!-- 限制框架中的fastjson版本 -->
 | 
			
		||||
        <fastjson.version>1.2.83</fastjson.version>
 | 
			
		||||
        <!-- 面向运行时的D-ORM依赖 -->
 | 
			
		||||
        <anyline.version>8.7.2-20250101</anyline.version>
 | 
			
		||||
        <!-- 工作流配置 -->
 | 
			
		||||
        <warm-flow.version>1.7.2</warm-flow.version>
 | 
			
		||||
        <anyline.version>8.7.2-20240808</anyline.version>
 | 
			
		||||
        <!--工作流配置-->
 | 
			
		||||
        <flowable.version>7.0.1</flowable.version>
 | 
			
		||||
 | 
			
		||||
        <!-- 插件版本 -->
 | 
			
		||||
        <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
 | 
			
		||||
        <maven-war-plugin.version>3.2.2</maven-war-plugin.version>
 | 
			
		||||
        <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
 | 
			
		||||
        <maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
 | 
			
		||||
        <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
 | 
			
		||||
        <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
 | 
			
		||||
        <!-- 打包默认跳过测试 -->
 | 
			
		||||
        <skipTests>true</skipTests>
 | 
			
		||||
    </properties>
 | 
			
		||||
 | 
			
		||||
    <profiles>
 | 
			
		||||
@@ -68,8 +69,6 @@
 | 
			
		||||
                <!-- 环境标识,需要与配置文件的名称相对应 -->
 | 
			
		||||
                <profiles.active>local</profiles.active>
 | 
			
		||||
                <logging.level>info</logging.level>
 | 
			
		||||
                <monitor.username>ruoyi</monitor.username>
 | 
			
		||||
                <monitor.password>123456</monitor.password>
 | 
			
		||||
            </properties>
 | 
			
		||||
        </profile>
 | 
			
		||||
        <profile>
 | 
			
		||||
@@ -78,8 +77,6 @@
 | 
			
		||||
                <!-- 环境标识,需要与配置文件的名称相对应 -->
 | 
			
		||||
                <profiles.active>dev</profiles.active>
 | 
			
		||||
                <logging.level>info</logging.level>
 | 
			
		||||
                <monitor.username>ruoyi</monitor.username>
 | 
			
		||||
                <monitor.password>123456</monitor.password>
 | 
			
		||||
            </properties>
 | 
			
		||||
            <activation>
 | 
			
		||||
                <!-- 默认环境 -->
 | 
			
		||||
@@ -91,8 +88,6 @@
 | 
			
		||||
            <properties>
 | 
			
		||||
                <profiles.active>prod</profiles.active>
 | 
			
		||||
                <logging.level>warn</logging.level>
 | 
			
		||||
                <monitor.username>ruoyi</monitor.username>
 | 
			
		||||
                <monitor.password>123456</monitor.password>
 | 
			
		||||
            </properties>
 | 
			
		||||
        </profile>
 | 
			
		||||
    </profiles>
 | 
			
		||||
@@ -119,16 +114,12 @@
 | 
			
		||||
                <scope>import</scope>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <!-- Warm-Flow国产工作流引擎, 在线文档:http://warm-flow.cn/ -->
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>org.dromara.warm</groupId>
 | 
			
		||||
                <artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId>
 | 
			
		||||
                <version>${warm-flow.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>org.dromara.warm</groupId>
 | 
			
		||||
                <artifactId>warm-flow-plugin-ui-sb-web</artifactId>
 | 
			
		||||
                <version>${warm-flow.version}</version>
 | 
			
		||||
                <groupId>org.flowable</groupId>
 | 
			
		||||
                <artifactId>flowable-bom</artifactId>
 | 
			
		||||
                <version>${flowable.version}</version>
 | 
			
		||||
                <type>pom</type>
 | 
			
		||||
                <scope>import</scope>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <!-- JustAuth 的依赖配置-->
 | 
			
		||||
@@ -166,9 +157,9 @@
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>cn.idev.excel</groupId>
 | 
			
		||||
                <artifactId>fastexcel</artifactId>
 | 
			
		||||
                <version>${fastexcel.version}</version>
 | 
			
		||||
                <groupId>com.alibaba</groupId>
 | 
			
		||||
                <artifactId>easyexcel</artifactId>
 | 
			
		||||
                <version>${easyexcel.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <!-- velocity代码生成使用模板 -->
 | 
			
		||||
@@ -221,12 +212,6 @@
 | 
			
		||||
                <version>${mybatis-plus.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>com.baomidou</groupId>
 | 
			
		||||
                <artifactId>mybatis-plus-jsqlparser</artifactId>
 | 
			
		||||
                <version>${mybatis-plus.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>com.baomidou</groupId>
 | 
			
		||||
                <artifactId>mybatis-plus-annotation</artifactId>
 | 
			
		||||
@@ -240,24 +225,30 @@
 | 
			
		||||
                <version>${p6spy.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>com.squareup.okhttp3</groupId>
 | 
			
		||||
                <artifactId>okhttp</artifactId>
 | 
			
		||||
                <version>${okhttp.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <!--  AWS SDK for Java 2.x  -->
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>software.amazon.awssdk</groupId>
 | 
			
		||||
                <artifactId>s3</artifactId>
 | 
			
		||||
                <version>${aws.sdk.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <!-- 使用AWS基于 CRT 的 S3 客户端 -->
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>software.amazon.awssdk.crt</groupId>
 | 
			
		||||
                <artifactId>aws-crt</artifactId>
 | 
			
		||||
                <version>${aws.crt.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <!-- 基于 AWS CRT 的 S3 客户端的性能增强的 S3 传输管理器 -->
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>software.amazon.awssdk</groupId>
 | 
			
		||||
                <artifactId>s3-transfer-manager</artifactId>
 | 
			
		||||
                <version>${aws.sdk.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <!-- 将基于 Netty 的 HTTP 客户端从类路径中移除 -->
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>software.amazon.awssdk</groupId>
 | 
			
		||||
                <artifactId>netty-nio-client</artifactId>
 | 
			
		||||
                <version>${aws.sdk.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <!--短信sms4j-->
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>org.dromara.sms4j</groupId>
 | 
			
		||||
@@ -321,6 +312,28 @@
 | 
			
		||||
                <version>${ip2region.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>io.undertow</groupId>
 | 
			
		||||
                <artifactId>undertow-core</artifactId>
 | 
			
		||||
                <version>${undertow.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>io.undertow</groupId>
 | 
			
		||||
                <artifactId>undertow-servlet</artifactId>
 | 
			
		||||
                <version>${undertow.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>io.undertow</groupId>
 | 
			
		||||
                <artifactId>undertow-websockets-jsr</artifactId>
 | 
			
		||||
                <version>${undertow.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <artifactId>commons-compress</artifactId>
 | 
			
		||||
                <groupId>org.apache.commons</groupId>
 | 
			
		||||
                <version>1.26.2</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>com.alibaba</groupId>
 | 
			
		||||
                <artifactId>fastjson</artifactId>
 | 
			
		||||
@@ -374,7 +387,7 @@
 | 
			
		||||
            <plugin>
 | 
			
		||||
                <groupId>org.apache.maven.plugins</groupId>
 | 
			
		||||
                <artifactId>maven-compiler-plugin</artifactId>
 | 
			
		||||
                <version>${maven-compiler-plugin.version}</version>
 | 
			
		||||
                <version>${maven-compiler-plugin.verison}</version>
 | 
			
		||||
                <configuration>
 | 
			
		||||
                    <source>${java.version}</source>
 | 
			
		||||
                    <target>${java.version}</target>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
 | 
			
		||||
FROM bellsoft/liberica-openjdk-debian:17.0.11-cds
 | 
			
		||||
#FROM bellsoft/liberica-openjdk-debian:21.0.5-cds
 | 
			
		||||
#FROM bellsoft/liberica-openjdk-debian:21.0.3-cds
 | 
			
		||||
#FROM findepi/graalvm:java17-native
 | 
			
		||||
 | 
			
		||||
LABEL maintainer="Lion Li"
 | 
			
		||||
@@ -11,20 +11,13 @@ RUN mkdir -p /ruoyi/server/logs \
 | 
			
		||||
 | 
			
		||||
WORKDIR /ruoyi/server
 | 
			
		||||
 | 
			
		||||
ENV SERVER_PORT=8080 SNAIL_PORT=28080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
 | 
			
		||||
ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
 | 
			
		||||
 | 
			
		||||
EXPOSE ${SERVER_PORT}
 | 
			
		||||
# 暴露 snail job 客户端端口 用于定时任务调度中心通信
 | 
			
		||||
EXPOSE ${SNAIL_PORT}
 | 
			
		||||
 | 
			
		||||
ADD ./target/ruoyi-admin.jar ./app.jar
 | 
			
		||||
# 工作流字体文件
 | 
			
		||||
ADD ./zhFonts/ /usr/share/fonts/zhFonts/
 | 
			
		||||
 | 
			
		||||
SHELL ["/bin/bash", "-c"]
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
 | 
			
		||||
           -Dsnail-job.port=${SNAIL_PORT} \
 | 
			
		||||
           # 应用名称 如果想区分集群节点监控 改成不同的名称即可
 | 
			
		||||
           #-Dskywalking.agent.service_name=ruoyi-server \
 | 
			
		||||
           #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ package org.dromara.web.controller;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.annotation.SaIgnore;
 | 
			
		||||
import cn.dev33.satoken.exception.NotLoginException;
 | 
			
		||||
import cn.dev33.satoken.stp.StpUtil;
 | 
			
		||||
import cn.hutool.core.codec.Base64;
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
@@ -13,7 +12,7 @@ import me.zhyd.oauth.model.AuthResponse;
 | 
			
		||||
import me.zhyd.oauth.model.AuthUser;
 | 
			
		||||
import me.zhyd.oauth.request.AuthRequest;
 | 
			
		||||
import me.zhyd.oauth.utils.AuthStateUtils;
 | 
			
		||||
import org.dromara.common.core.constant.SystemConstants;
 | 
			
		||||
import org.dromara.common.core.constant.UserConstants;
 | 
			
		||||
import org.dromara.common.core.domain.R;
 | 
			
		||||
import org.dromara.common.core.domain.model.LoginBody;
 | 
			
		||||
import org.dromara.common.core.domain.model.RegisterBody;
 | 
			
		||||
@@ -93,7 +92,7 @@ public class AuthController {
 | 
			
		||||
        if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
 | 
			
		||||
            log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
 | 
			
		||||
            return R.fail(MessageUtils.message("auth.grant.type.error"));
 | 
			
		||||
        } else if (!SystemConstants.NORMAL.equals(client.getStatus())) {
 | 
			
		||||
        } else if (!UserConstants.NORMAL.equals(client.getStatus())) {
 | 
			
		||||
            return R.fail(MessageUtils.message("auth.grant.type.blocked"));
 | 
			
		||||
        }
 | 
			
		||||
        // 校验租户
 | 
			
		||||
@@ -112,7 +111,7 @@ public class AuthController {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取跳转URL
 | 
			
		||||
     * 第三方登录请求
 | 
			
		||||
     *
 | 
			
		||||
     * @param source 登录来源
 | 
			
		||||
     * @return 结果
 | 
			
		||||
@@ -134,15 +133,13 @@ public class AuthController {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 前端回调绑定授权(需要token)
 | 
			
		||||
     * 第三方登录回调业务处理 绑定授权
 | 
			
		||||
     *
 | 
			
		||||
     * @param loginBody 请求体
 | 
			
		||||
     * @return 结果
 | 
			
		||||
     */
 | 
			
		||||
    @PostMapping("/social/callback")
 | 
			
		||||
    public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
 | 
			
		||||
        // 校验token
 | 
			
		||||
        StpUtil.checkLogin();
 | 
			
		||||
        // 获取第三方登录信息
 | 
			
		||||
        AuthResponse<AuthUser> response = SocialUtils.loginAuth(
 | 
			
		||||
                loginBody.getSource(), loginBody.getSocialCode(),
 | 
			
		||||
@@ -158,14 +155,12 @@ public class AuthController {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 取消授权(需要token)
 | 
			
		||||
     * 取消授权
 | 
			
		||||
     *
 | 
			
		||||
     * @param socialId socialId
 | 
			
		||||
     */
 | 
			
		||||
    @DeleteMapping(value = "/unlock/{socialId}")
 | 
			
		||||
    public R<Void> unlockSocial(@PathVariable Long socialId) {
 | 
			
		||||
        // 校验token
 | 
			
		||||
        StpUtil.checkLogin();
 | 
			
		||||
        Boolean rows = socialUserService.deleteWithValidById(socialId);
 | 
			
		||||
        return rows ? R.ok() : R.fail("取消授权失败");
 | 
			
		||||
    }
 | 
			
		||||
@@ -231,7 +226,7 @@ public class AuthController {
 | 
			
		||||
        }
 | 
			
		||||
        // 根据域名进行筛选
 | 
			
		||||
        List<TenantListVo> list = StreamUtils.filter(voList, vo ->
 | 
			
		||||
            StringUtils.equalsIgnoreCase(vo.getDomain(), host));
 | 
			
		||||
                StringUtils.equals(vo.getDomain(), host));
 | 
			
		||||
        result.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
 | 
			
		||||
        return R.ok(result);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@ import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.dromara.common.core.constant.Constants;
 | 
			
		||||
import org.dromara.common.core.constant.GlobalConstants;
 | 
			
		||||
import org.dromara.common.core.domain.R;
 | 
			
		||||
import org.dromara.common.core.exception.ServiceException;
 | 
			
		||||
import org.dromara.common.core.utils.SpringUtils;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import org.dromara.common.core.utils.reflect.ReflectUtils;
 | 
			
		||||
@@ -80,21 +79,12 @@ public class CaptchaController {
 | 
			
		||||
     *
 | 
			
		||||
     * @param email 邮箱
 | 
			
		||||
     */
 | 
			
		||||
    @RateLimiter(key = "#email", time = 60, count = 1)
 | 
			
		||||
    @GetMapping("/resource/email/code")
 | 
			
		||||
    public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
 | 
			
		||||
        if (!mailProperties.getEnabled()) {
 | 
			
		||||
            return R.fail("当前系统没有开启邮箱功能!");
 | 
			
		||||
        }
 | 
			
		||||
        SpringUtils.getAopProxy(this).emailCodeImpl(email);
 | 
			
		||||
        return R.ok();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 邮箱验证码
 | 
			
		||||
     * 独立方法避免验证码关闭之后仍然走限流
 | 
			
		||||
     */
 | 
			
		||||
    @RateLimiter(key = "#email", time = 60, count = 1)
 | 
			
		||||
    public void emailCodeImpl(String email) {
 | 
			
		||||
        String key = GlobalConstants.CAPTCHA_CODE_KEY + email;
 | 
			
		||||
        String code = RandomUtil.randomNumbers(4);
 | 
			
		||||
        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
 | 
			
		||||
@@ -102,30 +92,23 @@ public class CaptchaController {
 | 
			
		||||
            MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.error("验证码短信发送异常 => {}", e.getMessage());
 | 
			
		||||
            throw new ServiceException(e.getMessage());
 | 
			
		||||
            return R.fail(e.getMessage());
 | 
			
		||||
        }
 | 
			
		||||
        return R.ok();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成验证码
 | 
			
		||||
     */
 | 
			
		||||
    @RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
 | 
			
		||||
    @GetMapping("/auth/code")
 | 
			
		||||
    public R<CaptchaVo> getCode() {
 | 
			
		||||
        CaptchaVo captchaVo = new CaptchaVo();
 | 
			
		||||
        boolean captchaEnabled = captchaProperties.getEnable();
 | 
			
		||||
        if (!captchaEnabled) {
 | 
			
		||||
            CaptchaVo captchaVo = new CaptchaVo();
 | 
			
		||||
            captchaVo.setCaptchaEnabled(false);
 | 
			
		||||
            return R.ok(captchaVo);
 | 
			
		||||
        }
 | 
			
		||||
        return R.ok(SpringUtils.getAopProxy(this).getCodeImpl());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成验证码
 | 
			
		||||
     * 独立方法避免验证码关闭之后仍然走限流
 | 
			
		||||
     */
 | 
			
		||||
    @RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
 | 
			
		||||
    public CaptchaVo getCodeImpl() {
 | 
			
		||||
        // 保存验证码信息
 | 
			
		||||
        String uuid = IdUtil.simpleUUID();
 | 
			
		||||
        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
 | 
			
		||||
@@ -145,10 +128,9 @@ public class CaptchaController {
 | 
			
		||||
            code = exp.getValue(String.class);
 | 
			
		||||
        }
 | 
			
		||||
        RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
 | 
			
		||||
        CaptchaVo captchaVo = new CaptchaVo();
 | 
			
		||||
        captchaVo.setUuid(uuid);
 | 
			
		||||
        captchaVo.setImg(captcha.getImageBase64());
 | 
			
		||||
        return captchaVo;
 | 
			
		||||
        return R.ok(captchaVo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
package org.dromara.web.controller;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.annotation.SaIgnore;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.dromara.common.core.utils.SpringUtils;
 | 
			
		||||
import org.dromara.common.core.config.RuoYiConfig;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
 | 
			
		||||
@@ -17,12 +17,16 @@ import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
@RestController
 | 
			
		||||
public class IndexController {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 系统基础配置
 | 
			
		||||
     */
 | 
			
		||||
    private final RuoYiConfig ruoyiConfig;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 访问首页,提示语
 | 
			
		||||
     */
 | 
			
		||||
    @GetMapping("/")
 | 
			
		||||
    public String index() {
 | 
			
		||||
        return StringUtils.format("欢迎使用{}后台管理框架,请通过前端地址访问。", SpringUtils.getApplicationName());
 | 
			
		||||
        return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,9 @@
 | 
			
		||||
package org.dromara.web.listener;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.config.SaTokenConfig;
 | 
			
		||||
import cn.dev33.satoken.listener.SaTokenListener;
 | 
			
		||||
import cn.dev33.satoken.stp.SaLoginModel;
 | 
			
		||||
import cn.dev33.satoken.stp.StpUtil;
 | 
			
		||||
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
 | 
			
		||||
import cn.hutool.core.convert.Convert;
 | 
			
		||||
import cn.hutool.http.useragent.UserAgent;
 | 
			
		||||
import cn.hutool.http.useragent.UserAgentUtil;
 | 
			
		||||
@@ -34,13 +35,14 @@ import java.time.Duration;
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class UserActionListener implements SaTokenListener {
 | 
			
		||||
 | 
			
		||||
    private final SaTokenConfig tokenConfig;
 | 
			
		||||
    private final SysLoginService loginService;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 每次登录时触发
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) {
 | 
			
		||||
    public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
 | 
			
		||||
        UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
 | 
			
		||||
        String ip = ServletUtils.getClientIP();
 | 
			
		||||
        UserOnlineDTO dto = new UserOnlineDTO();
 | 
			
		||||
@@ -50,17 +52,17 @@ public class UserActionListener implements SaTokenListener {
 | 
			
		||||
        dto.setOs(userAgent.getOs().getName());
 | 
			
		||||
        dto.setLoginTime(System.currentTimeMillis());
 | 
			
		||||
        dto.setTokenId(tokenValue);
 | 
			
		||||
        String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY);
 | 
			
		||||
        String tenantId = (String) loginParameter.getExtra(LoginHelper.TENANT_KEY);
 | 
			
		||||
        String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY);
 | 
			
		||||
        String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY);
 | 
			
		||||
        dto.setUserName(username);
 | 
			
		||||
        dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY));
 | 
			
		||||
        dto.setDeviceType(loginParameter.getDeviceType());
 | 
			
		||||
        dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY));
 | 
			
		||||
        dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY));
 | 
			
		||||
        dto.setDeviceType(loginModel.getDevice());
 | 
			
		||||
        dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY));
 | 
			
		||||
        TenantHelper.dynamic(tenantId, () -> {
 | 
			
		||||
            if(loginParameter.getTimeout() == -1) {
 | 
			
		||||
            if(tokenConfig.getTimeout() == -1) {
 | 
			
		||||
                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
 | 
			
		||||
            } else {
 | 
			
		||||
                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout()));
 | 
			
		||||
                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        // 记录登录日志
 | 
			
		||||
@@ -72,7 +74,7 @@ public class UserActionListener implements SaTokenListener {
 | 
			
		||||
        logininforEvent.setRequest(ServletUtils.getRequest());
 | 
			
		||||
        SpringUtils.context().publishEvent(logininforEvent);
 | 
			
		||||
        // 更新登录信息
 | 
			
		||||
        loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip);
 | 
			
		||||
        loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip);
 | 
			
		||||
        log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,12 +12,11 @@ import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import me.zhyd.oauth.model.AuthUser;
 | 
			
		||||
import org.dromara.common.core.constant.CacheConstants;
 | 
			
		||||
import org.dromara.common.core.constant.Constants;
 | 
			
		||||
import org.dromara.common.core.constant.SystemConstants;
 | 
			
		||||
import org.dromara.common.core.constant.TenantConstants;
 | 
			
		||||
import org.dromara.common.core.domain.dto.PostDTO;
 | 
			
		||||
import org.dromara.common.core.domain.dto.RoleDTO;
 | 
			
		||||
import org.dromara.common.core.domain.model.LoginUser;
 | 
			
		||||
import org.dromara.common.core.enums.LoginType;
 | 
			
		||||
import org.dromara.common.core.enums.TenantStatus;
 | 
			
		||||
import org.dromara.common.core.exception.ServiceException;
 | 
			
		||||
import org.dromara.common.core.exception.user.UserException;
 | 
			
		||||
import org.dromara.common.core.utils.*;
 | 
			
		||||
@@ -61,7 +60,6 @@ public class SysLoginService {
 | 
			
		||||
    private final ISysSocialService sysSocialService;
 | 
			
		||||
    private final ISysRoleService roleService;
 | 
			
		||||
    private final ISysDeptService deptService;
 | 
			
		||||
    private final ISysPostService postService;
 | 
			
		||||
    private final SysUserMapper userMapper;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -150,24 +148,21 @@ public class SysLoginService {
 | 
			
		||||
     */
 | 
			
		||||
    public LoginUser buildLoginUser(SysUserVo user) {
 | 
			
		||||
        LoginUser loginUser = new LoginUser();
 | 
			
		||||
        Long userId = user.getUserId();
 | 
			
		||||
        loginUser.setTenantId(user.getTenantId());
 | 
			
		||||
        loginUser.setUserId(userId);
 | 
			
		||||
        loginUser.setUserId(user.getUserId());
 | 
			
		||||
        loginUser.setDeptId(user.getDeptId());
 | 
			
		||||
        loginUser.setUsername(user.getUserName());
 | 
			
		||||
        loginUser.setNickname(user.getNickName());
 | 
			
		||||
        loginUser.setUserType(user.getUserType());
 | 
			
		||||
        loginUser.setMenuPermission(permissionService.getMenuPermission(userId));
 | 
			
		||||
        loginUser.setRolePermission(permissionService.getRolePermission(userId));
 | 
			
		||||
        loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId()));
 | 
			
		||||
        loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId()));
 | 
			
		||||
        if (ObjectUtil.isNotNull(user.getDeptId())) {
 | 
			
		||||
            Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById);
 | 
			
		||||
            loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY));
 | 
			
		||||
            loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY));
 | 
			
		||||
        }
 | 
			
		||||
        List<SysRoleVo> roles = roleService.selectRolesByUserId(userId);
 | 
			
		||||
        List<SysPostVo> posts = postService.selectPostsByUserId(userId);
 | 
			
		||||
        List<SysRoleVo> roles = roleService.selectRolesByUserId(user.getUserId());
 | 
			
		||||
        loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
 | 
			
		||||
        loginUser.setPosts(BeanUtil.copyToList(posts, PostDTO.class));
 | 
			
		||||
        return loginUser;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -228,17 +223,17 @@ public class SysLoginService {
 | 
			
		||||
        if (!TenantHelper.isEnable()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (StringUtils.isBlank(tenantId)) {
 | 
			
		||||
            throw new TenantException("tenant.number.not.blank");
 | 
			
		||||
        }
 | 
			
		||||
        if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (StringUtils.isBlank(tenantId)) {
 | 
			
		||||
            throw new TenantException("tenant.number.not.blank");
 | 
			
		||||
        }
 | 
			
		||||
        SysTenantVo tenant = tenantService.queryByTenantId(tenantId);
 | 
			
		||||
        if (ObjectUtil.isNull(tenant)) {
 | 
			
		||||
            log.info("登录租户:{} 不存在.", tenantId);
 | 
			
		||||
            throw new TenantException("tenant.not.exists");
 | 
			
		||||
        } else if (SystemConstants.DISABLE.equals(tenant.getStatus())) {
 | 
			
		||||
        } else if (TenantStatus.DISABLE.getCode().equals(tenant.getStatus())) {
 | 
			
		||||
            log.info("登录租户:{} 已被停用.", tenantId);
 | 
			
		||||
            throw new TenantException("tenant.blocked");
 | 
			
		||||
        } else if (ObjectUtil.isNotNull(tenant.getExpireTime())
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
package org.dromara.web.service;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.crypto.digest.BCrypt;
 | 
			
		||||
import cn.dev33.satoken.secure.BCrypt;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.dromara.common.core.constant.Constants;
 | 
			
		||||
@@ -84,11 +84,11 @@ public class SysRegisterService {
 | 
			
		||||
        String captcha = RedisUtils.getCacheObject(verifyKey);
 | 
			
		||||
        RedisUtils.deleteObject(verifyKey);
 | 
			
		||||
        if (captcha == null) {
 | 
			
		||||
            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
 | 
			
		||||
            recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire"));
 | 
			
		||||
            throw new CaptchaExpireException();
 | 
			
		||||
        }
 | 
			
		||||
        if (!code.equalsIgnoreCase(captcha)) {
 | 
			
		||||
            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
 | 
			
		||||
            recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error"));
 | 
			
		||||
            throw new CaptchaException();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,17 @@
 | 
			
		||||
package org.dromara.web.service.impl;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.stp.SaLoginModel;
 | 
			
		||||
import cn.dev33.satoken.stp.StpUtil;
 | 
			
		||||
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.dromara.common.core.constant.Constants;
 | 
			
		||||
import org.dromara.common.core.constant.GlobalConstants;
 | 
			
		||||
import org.dromara.common.core.constant.SystemConstants;
 | 
			
		||||
import org.dromara.common.core.domain.model.EmailLoginBody;
 | 
			
		||||
import org.dromara.common.core.domain.model.LoginUser;
 | 
			
		||||
import org.dromara.common.core.enums.LoginType;
 | 
			
		||||
import org.dromara.common.core.enums.UserStatus;
 | 
			
		||||
import org.dromara.common.core.exception.user.CaptchaExpireException;
 | 
			
		||||
import org.dromara.common.core.exception.user.UserException;
 | 
			
		||||
import org.dromara.common.core.utils.MessageUtils;
 | 
			
		||||
@@ -58,8 +58,8 @@ public class EmailAuthStrategy implements IAuthStrategy {
 | 
			
		||||
        });
 | 
			
		||||
        loginUser.setClientKey(client.getClientKey());
 | 
			
		||||
        loginUser.setDeviceType(client.getDeviceType());
 | 
			
		||||
        SaLoginParameter model = new SaLoginParameter();
 | 
			
		||||
        model.setDeviceType(client.getDeviceType());
 | 
			
		||||
        SaLoginModel model = new SaLoginModel();
 | 
			
		||||
        model.setDevice(client.getDeviceType());
 | 
			
		||||
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
			
		||||
        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
			
		||||
        model.setTimeout(client.getTimeout());
 | 
			
		||||
@@ -92,7 +92,7 @@ public class EmailAuthStrategy implements IAuthStrategy {
 | 
			
		||||
        if (ObjectUtil.isNull(user)) {
 | 
			
		||||
            log.info("登录用户:{} 不存在.", email);
 | 
			
		||||
            throw new UserException("user.not.exists", email);
 | 
			
		||||
        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
 | 
			
		||||
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
 | 
			
		||||
            log.info("登录用户:{} 已被停用.", email);
 | 
			
		||||
            throw new UserException("user.blocked", email);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,18 @@
 | 
			
		||||
package org.dromara.web.service.impl;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.secure.BCrypt;
 | 
			
		||||
import cn.dev33.satoken.stp.SaLoginModel;
 | 
			
		||||
import cn.dev33.satoken.stp.StpUtil;
 | 
			
		||||
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import cn.hutool.crypto.digest.BCrypt;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.dromara.common.core.constant.Constants;
 | 
			
		||||
import org.dromara.common.core.constant.GlobalConstants;
 | 
			
		||||
import org.dromara.common.core.constant.SystemConstants;
 | 
			
		||||
import org.dromara.common.core.domain.model.LoginUser;
 | 
			
		||||
import org.dromara.common.core.domain.model.PasswordLoginBody;
 | 
			
		||||
import org.dromara.common.core.enums.LoginType;
 | 
			
		||||
import org.dromara.common.core.enums.UserStatus;
 | 
			
		||||
import org.dromara.common.core.exception.user.CaptchaException;
 | 
			
		||||
import org.dromara.common.core.exception.user.CaptchaExpireException;
 | 
			
		||||
import org.dromara.common.core.exception.user.UserException;
 | 
			
		||||
@@ -70,8 +70,8 @@ public class PasswordAuthStrategy implements IAuthStrategy {
 | 
			
		||||
        });
 | 
			
		||||
        loginUser.setClientKey(client.getClientKey());
 | 
			
		||||
        loginUser.setDeviceType(client.getDeviceType());
 | 
			
		||||
        SaLoginParameter model = new SaLoginParameter();
 | 
			
		||||
        model.setDeviceType(client.getDeviceType());
 | 
			
		||||
        SaLoginModel model = new SaLoginModel();
 | 
			
		||||
        model.setDevice(client.getDeviceType());
 | 
			
		||||
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
			
		||||
        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
			
		||||
        model.setTimeout(client.getTimeout());
 | 
			
		||||
@@ -113,7 +113,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
 | 
			
		||||
        if (ObjectUtil.isNull(user)) {
 | 
			
		||||
            log.info("登录用户:{} 不存在.", username);
 | 
			
		||||
            throw new UserException("user.not.exists", username);
 | 
			
		||||
        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
 | 
			
		||||
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
 | 
			
		||||
            log.info("登录用户:{} 已被停用.", username);
 | 
			
		||||
            throw new UserException("user.blocked", username);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,17 @@
 | 
			
		||||
package org.dromara.web.service.impl;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.stp.SaLoginModel;
 | 
			
		||||
import cn.dev33.satoken.stp.StpUtil;
 | 
			
		||||
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.dromara.common.core.constant.Constants;
 | 
			
		||||
import org.dromara.common.core.constant.GlobalConstants;
 | 
			
		||||
import org.dromara.common.core.constant.SystemConstants;
 | 
			
		||||
import org.dromara.common.core.domain.model.LoginUser;
 | 
			
		||||
import org.dromara.common.core.domain.model.SmsLoginBody;
 | 
			
		||||
import org.dromara.common.core.enums.LoginType;
 | 
			
		||||
import org.dromara.common.core.enums.UserStatus;
 | 
			
		||||
import org.dromara.common.core.exception.user.CaptchaExpireException;
 | 
			
		||||
import org.dromara.common.core.exception.user.UserException;
 | 
			
		||||
import org.dromara.common.core.utils.MessageUtils;
 | 
			
		||||
@@ -58,8 +58,8 @@ public class SmsAuthStrategy implements IAuthStrategy {
 | 
			
		||||
        });
 | 
			
		||||
        loginUser.setClientKey(client.getClientKey());
 | 
			
		||||
        loginUser.setDeviceType(client.getDeviceType());
 | 
			
		||||
        SaLoginParameter model = new SaLoginParameter();
 | 
			
		||||
        model.setDeviceType(client.getDeviceType());
 | 
			
		||||
        SaLoginModel model = new SaLoginModel();
 | 
			
		||||
        model.setDevice(client.getDeviceType());
 | 
			
		||||
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
			
		||||
        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
			
		||||
        model.setTimeout(client.getTimeout());
 | 
			
		||||
@@ -92,7 +92,7 @@ public class SmsAuthStrategy implements IAuthStrategy {
 | 
			
		||||
        if (ObjectUtil.isNull(user)) {
 | 
			
		||||
            log.info("登录用户:{} 不存在.", phonenumber);
 | 
			
		||||
            throw new UserException("user.not.exists", phonenumber);
 | 
			
		||||
        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
 | 
			
		||||
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
 | 
			
		||||
            log.info("登录用户:{} 已被停用.", phonenumber);
 | 
			
		||||
            throw new UserException("user.blocked", phonenumber);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
package org.dromara.web.service.impl;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.stp.SaLoginModel;
 | 
			
		||||
import cn.dev33.satoken.stp.StpUtil;
 | 
			
		||||
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.map.MapUtil;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
@@ -11,9 +11,9 @@ import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import me.zhyd.oauth.model.AuthResponse;
 | 
			
		||||
import me.zhyd.oauth.model.AuthUser;
 | 
			
		||||
import org.dromara.common.core.constant.SystemConstants;
 | 
			
		||||
import org.dromara.common.core.domain.model.LoginUser;
 | 
			
		||||
import org.dromara.common.core.domain.model.SocialLoginBody;
 | 
			
		||||
import org.dromara.common.core.enums.UserStatus;
 | 
			
		||||
import org.dromara.common.core.exception.ServiceException;
 | 
			
		||||
import org.dromara.common.core.exception.user.UserException;
 | 
			
		||||
import org.dromara.common.core.utils.StreamUtils;
 | 
			
		||||
@@ -99,8 +99,8 @@ public class SocialAuthStrategy implements IAuthStrategy {
 | 
			
		||||
        });
 | 
			
		||||
        loginUser.setClientKey(client.getClientKey());
 | 
			
		||||
        loginUser.setDeviceType(client.getDeviceType());
 | 
			
		||||
        SaLoginParameter model = new SaLoginParameter();
 | 
			
		||||
        model.setDeviceType(client.getDeviceType());
 | 
			
		||||
        SaLoginModel model = new SaLoginModel();
 | 
			
		||||
        model.setDevice(client.getDeviceType());
 | 
			
		||||
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
			
		||||
        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
			
		||||
        model.setTimeout(client.getTimeout());
 | 
			
		||||
@@ -121,7 +121,7 @@ public class SocialAuthStrategy implements IAuthStrategy {
 | 
			
		||||
        if (ObjectUtil.isNull(user)) {
 | 
			
		||||
            log.info("登录用户:{} 不存在.", "");
 | 
			
		||||
            throw new UserException("user.not.exists", "");
 | 
			
		||||
        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
 | 
			
		||||
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
 | 
			
		||||
            log.info("登录用户:{} 已被停用.", "");
 | 
			
		||||
            throw new UserException("user.blocked", "");
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +1,17 @@
 | 
			
		||||
package org.dromara.web.service.impl;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.stp.SaLoginModel;
 | 
			
		||||
import cn.dev33.satoken.stp.StpUtil;
 | 
			
		||||
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import me.zhyd.oauth.config.AuthConfig;
 | 
			
		||||
import me.zhyd.oauth.model.AuthCallback;
 | 
			
		||||
import me.zhyd.oauth.model.AuthResponse;
 | 
			
		||||
import me.zhyd.oauth.model.AuthToken;
 | 
			
		||||
import me.zhyd.oauth.model.AuthUser;
 | 
			
		||||
import me.zhyd.oauth.request.AuthRequest;
 | 
			
		||||
import me.zhyd.oauth.request.AuthWechatMiniProgramRequest;
 | 
			
		||||
import org.dromara.common.core.constant.SystemConstants;
 | 
			
		||||
import org.dromara.common.core.domain.model.XcxLoginBody;
 | 
			
		||||
import org.dromara.common.core.domain.model.XcxLoginUser;
 | 
			
		||||
import org.dromara.common.core.exception.ServiceException;
 | 
			
		||||
import org.dromara.common.core.enums.UserStatus;
 | 
			
		||||
import org.dromara.common.core.utils.ValidatorUtils;
 | 
			
		||||
import org.dromara.common.json.utils.JsonUtils;
 | 
			
		||||
import org.dromara.common.satoken.utils.LoginHelper;
 | 
			
		||||
import org.dromara.system.domain.SysClient;
 | 
			
		||||
import org.dromara.system.domain.vo.SysClientVo;
 | 
			
		||||
import org.dromara.system.domain.vo.SysUserVo;
 | 
			
		||||
import org.dromara.web.domain.vo.LoginVo;
 | 
			
		||||
@@ -47,24 +40,12 @@ public class XcxAuthStrategy implements IAuthStrategy {
 | 
			
		||||
        // 多个小程序识别使用
 | 
			
		||||
        String appid = loginBody.getAppid();
 | 
			
		||||
 | 
			
		||||
        // todo 以下自行实现
 | 
			
		||||
        // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
 | 
			
		||||
        AuthRequest authRequest = new AuthWechatMiniProgramRequest(AuthConfig.builder()
 | 
			
		||||
            .clientId(appid).clientSecret("自行填写密钥 可根据不同appid填入不同密钥")
 | 
			
		||||
            .ignoreCheckRedirectUri(true).ignoreCheckState(true).build());
 | 
			
		||||
        AuthCallback authCallback = new AuthCallback();
 | 
			
		||||
        authCallback.setCode(xcxCode);
 | 
			
		||||
        AuthResponse<AuthUser> resp = authRequest.login(authCallback);
 | 
			
		||||
        String openid, unionId;
 | 
			
		||||
        if (resp.ok()) {
 | 
			
		||||
            AuthToken token = resp.getData().getToken();
 | 
			
		||||
            openid = token.getOpenId();
 | 
			
		||||
            // 微信小程序只有关联到微信开放平台下之后才能获取到 unionId,因此unionId不一定能返回。
 | 
			
		||||
            unionId = token.getUnionId();
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new ServiceException(resp.getMsg());
 | 
			
		||||
        }
 | 
			
		||||
        String openid = "";
 | 
			
		||||
        // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
 | 
			
		||||
        SysUserVo user = loadUserByOpenid(openid);
 | 
			
		||||
 | 
			
		||||
        // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
 | 
			
		||||
        XcxLoginUser loginUser = new XcxLoginUser();
 | 
			
		||||
        loginUser.setTenantId(user.getTenantId());
 | 
			
		||||
@@ -76,8 +57,8 @@ public class XcxAuthStrategy implements IAuthStrategy {
 | 
			
		||||
        loginUser.setDeviceType(client.getDeviceType());
 | 
			
		||||
        loginUser.setOpenid(openid);
 | 
			
		||||
 | 
			
		||||
        SaLoginParameter model = new SaLoginParameter();
 | 
			
		||||
        model.setDeviceType(client.getDeviceType());
 | 
			
		||||
        SaLoginModel model = new SaLoginModel();
 | 
			
		||||
        model.setDevice(client.getDeviceType());
 | 
			
		||||
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
			
		||||
        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
			
		||||
        model.setTimeout(client.getTimeout());
 | 
			
		||||
@@ -101,7 +82,7 @@ public class XcxAuthStrategy implements IAuthStrategy {
 | 
			
		||||
        if (ObjectUtil.isNull(user)) {
 | 
			
		||||
            log.info("登录用户:{} 不存在.", openid);
 | 
			
		||||
            // todo 用户不存在 业务逻辑自行实现
 | 
			
		||||
        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
 | 
			
		||||
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
 | 
			
		||||
            log.info("登录用户:{} 已被停用.", openid);
 | 
			
		||||
            // todo 用户已被停用 业务逻辑自行实现
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -8,27 +8,23 @@ spring.boot.admin.client:
 | 
			
		||||
    metadata:
 | 
			
		||||
      username: ${spring.boot.admin.client.username}
 | 
			
		||||
      userpassword: ${spring.boot.admin.client.password}
 | 
			
		||||
  username: @monitor.username@
 | 
			
		||||
  password: @monitor.password@
 | 
			
		||||
  username: ruoyi
 | 
			
		||||
  password: 123456
 | 
			
		||||
 | 
			
		||||
--- # snail-job 配置
 | 
			
		||||
snail-job:
 | 
			
		||||
  enabled: true
 | 
			
		||||
  # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
 | 
			
		||||
  group: "ruoyi_group"
 | 
			
		||||
  # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表
 | 
			
		||||
  # SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表
 | 
			
		||||
  token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
 | 
			
		||||
  server:
 | 
			
		||||
    host: 127.0.0.1
 | 
			
		||||
    port: 17888
 | 
			
		||||
  # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
 | 
			
		||||
  # 详见 script/sql/snail_job.sql `sj_namespace` 表
 | 
			
		||||
  namespace: ${spring.profiles.active}
 | 
			
		||||
  # 随主应用端口漂移
 | 
			
		||||
  # 随主应用端口飘逸
 | 
			
		||||
  port: 2${server.port}
 | 
			
		||||
  # 客户端ip指定
 | 
			
		||||
  host:
 | 
			
		||||
  # RPC类型: netty, grpc
 | 
			
		||||
  rpc-type: grpc
 | 
			
		||||
 | 
			
		||||
--- # 数据源配置
 | 
			
		||||
spring:
 | 
			
		||||
@@ -52,14 +48,14 @@ spring:
 | 
			
		||||
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
 | 
			
		||||
          username: root
 | 
			
		||||
          password: root
 | 
			
		||||
#        # 从库数据源
 | 
			
		||||
#        slave:
 | 
			
		||||
#          lazy: true
 | 
			
		||||
#          type: ${spring.datasource.type}
 | 
			
		||||
#          driverClassName: com.mysql.cj.jdbc.Driver
 | 
			
		||||
#          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
 | 
			
		||||
#          username:
 | 
			
		||||
#          password:
 | 
			
		||||
        # 从库数据源
 | 
			
		||||
        slave:
 | 
			
		||||
          lazy: true
 | 
			
		||||
          type: ${spring.datasource.type}
 | 
			
		||||
          driverClassName: com.mysql.cj.jdbc.Driver
 | 
			
		||||
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
 | 
			
		||||
          username:
 | 
			
		||||
          password:
 | 
			
		||||
#        oracle:
 | 
			
		||||
#          type: ${spring.datasource.type}
 | 
			
		||||
#          driverClassName: oracle.jdbc.OracleDriver
 | 
			
		||||
@@ -120,8 +116,8 @@ redisson:
 | 
			
		||||
  nettyThreads: 8
 | 
			
		||||
  # 单节点配置
 | 
			
		||||
  singleServerConfig:
 | 
			
		||||
    # 客户端名称 不能用中文
 | 
			
		||||
    clientName: RuoYi-Vue-Plus
 | 
			
		||||
    # 客户端名称
 | 
			
		||||
    clientName: ${ruoyi.name}
 | 
			
		||||
    # 最小空闲连接数
 | 
			
		||||
    connectionMinimumIdleSize: 8
 | 
			
		||||
    # 连接池大小
 | 
			
		||||
@@ -202,7 +198,7 @@ justauth:
 | 
			
		||||
      redirect-uri: ${justauth.address}/social-callback?source=maxkey
 | 
			
		||||
    topiam:
 | 
			
		||||
      # topiam 服务器地址
 | 
			
		||||
      server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol
 | 
			
		||||
      server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
 | 
			
		||||
      client-id: 449c4*********937************759
 | 
			
		||||
      client-secret: ac7***********1e0************28d
 | 
			
		||||
      redirect-uri: ${justauth.address}/social-callback?source=topiam
 | 
			
		||||
@@ -263,10 +259,3 @@ justauth:
 | 
			
		||||
      client-id: 10**********6
 | 
			
		||||
      client-secret: 1f7d08**********5b7**********29e
 | 
			
		||||
      redirect-uri: ${justauth.address}/social-callback?source=gitlab
 | 
			
		||||
    gitea:
 | 
			
		||||
      # 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204
 | 
			
		||||
      # gitea 服务器地址
 | 
			
		||||
      server-url: https://demo.gitea.com
 | 
			
		||||
      client-id: 10**********6
 | 
			
		||||
      client-secret: 1f7d08**********5b7**********29e
 | 
			
		||||
      redirect-uri: ${justauth.address}/social-callback?source=gitea
 | 
			
		||||
 
 | 
			
		||||
@@ -11,27 +11,23 @@ spring.boot.admin.client:
 | 
			
		||||
    metadata:
 | 
			
		||||
      username: ${spring.boot.admin.client.username}
 | 
			
		||||
      userpassword: ${spring.boot.admin.client.password}
 | 
			
		||||
  username: @monitor.username@
 | 
			
		||||
  password: @monitor.password@
 | 
			
		||||
  username: ruoyi
 | 
			
		||||
  password: 123456
 | 
			
		||||
 | 
			
		||||
--- # snail-job 配置
 | 
			
		||||
snail-job:
 | 
			
		||||
  enabled: true
 | 
			
		||||
  # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
 | 
			
		||||
  group: "ruoyi_group"
 | 
			
		||||
  # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表
 | 
			
		||||
  # SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表
 | 
			
		||||
  token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
 | 
			
		||||
  server:
 | 
			
		||||
    host: 127.0.0.1
 | 
			
		||||
    port: 17888
 | 
			
		||||
  # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
 | 
			
		||||
  # 详见 script/sql/snail_job.sql `sj_namespace` 表
 | 
			
		||||
  namespace: ${spring.profiles.active}
 | 
			
		||||
  # 随主应用端口漂移
 | 
			
		||||
  # 随主应用端口飘逸
 | 
			
		||||
  port: 2${server.port}
 | 
			
		||||
  # 客户端ip指定
 | 
			
		||||
  host:
 | 
			
		||||
  # RPC类型: netty, grpc
 | 
			
		||||
  rpc-type: grpc
 | 
			
		||||
 | 
			
		||||
--- # 数据源配置
 | 
			
		||||
spring:
 | 
			
		||||
@@ -55,14 +51,14 @@ spring:
 | 
			
		||||
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
 | 
			
		||||
          username: root
 | 
			
		||||
          password: root
 | 
			
		||||
#        # 从库数据源
 | 
			
		||||
#        slave:
 | 
			
		||||
#          lazy: true
 | 
			
		||||
#          type: ${spring.datasource.type}
 | 
			
		||||
#          driverClassName: com.mysql.cj.jdbc.Driver
 | 
			
		||||
#          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
 | 
			
		||||
#          username:
 | 
			
		||||
#          password:
 | 
			
		||||
        # 从库数据源
 | 
			
		||||
        slave:
 | 
			
		||||
          lazy: true
 | 
			
		||||
          type: ${spring.datasource.type}
 | 
			
		||||
          driverClassName: com.mysql.cj.jdbc.Driver
 | 
			
		||||
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
 | 
			
		||||
          username:
 | 
			
		||||
          password:
 | 
			
		||||
#        oracle:
 | 
			
		||||
#          type: ${spring.datasource.type}
 | 
			
		||||
#          driverClassName: oracle.jdbc.OracleDriver
 | 
			
		||||
@@ -123,8 +119,8 @@ redisson:
 | 
			
		||||
  nettyThreads: 32
 | 
			
		||||
  # 单节点配置
 | 
			
		||||
  singleServerConfig:
 | 
			
		||||
    # 客户端名称 不能用中文
 | 
			
		||||
    clientName: RuoYi-Vue-Plus
 | 
			
		||||
    # 客户端名称
 | 
			
		||||
    clientName: ${ruoyi.name}
 | 
			
		||||
    # 最小空闲连接数
 | 
			
		||||
    connectionMinimumIdleSize: 32
 | 
			
		||||
    # 连接池大小
 | 
			
		||||
@@ -265,10 +261,3 @@ justauth:
 | 
			
		||||
      client-id: 10**********6
 | 
			
		||||
      client-secret: 1f7d08**********5b7**********29e
 | 
			
		||||
      redirect-uri: ${justauth.address}/social-callback?source=gitlab
 | 
			
		||||
    gitea:
 | 
			
		||||
      # 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204
 | 
			
		||||
      # gitea 服务器地址
 | 
			
		||||
      server-url: https://demo.gitea.com
 | 
			
		||||
      client-id: 10**********6
 | 
			
		||||
      client-secret: 1f7d08**********5b7**********29e
 | 
			
		||||
      redirect-uri: ${justauth.address}/social-callback?source=gitea
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,24 @@
 | 
			
		||||
# 项目相关配置
 | 
			
		||||
ruoyi:
 | 
			
		||||
  # 名称
 | 
			
		||||
  name: RuoYi-Vue-Plus
 | 
			
		||||
  # 版本
 | 
			
		||||
  version: ${revision}
 | 
			
		||||
  # 版权年份
 | 
			
		||||
  copyrightYear: 2024
 | 
			
		||||
 | 
			
		||||
captcha:
 | 
			
		||||
  enable: true
 | 
			
		||||
  # 页面 <参数设置> 可开启关闭 验证码校验
 | 
			
		||||
  # 验证码类型 math 数组计算 char 字符验证
 | 
			
		||||
  type: MATH
 | 
			
		||||
  # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
 | 
			
		||||
  category: CIRCLE
 | 
			
		||||
  # 数字验证码位数
 | 
			
		||||
  numberLength: 1
 | 
			
		||||
  # 字符验证码长度
 | 
			
		||||
  charLength: 4
 | 
			
		||||
 | 
			
		||||
# 开发环境配置
 | 
			
		||||
server:
 | 
			
		||||
  # 服务器的HTTP端口,默认为8080
 | 
			
		||||
@@ -20,25 +41,12 @@ server:
 | 
			
		||||
      # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
 | 
			
		||||
      worker: 256
 | 
			
		||||
 | 
			
		||||
captcha:
 | 
			
		||||
  # 是否启用验证码校验
 | 
			
		||||
  enable: true
 | 
			
		||||
  # 验证码类型 math 数组计算 char 字符验证
 | 
			
		||||
  type: MATH
 | 
			
		||||
  # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
 | 
			
		||||
  category: CIRCLE
 | 
			
		||||
  # 数字验证码位数
 | 
			
		||||
  numberLength: 1
 | 
			
		||||
  # 字符验证码长度
 | 
			
		||||
  charLength: 4
 | 
			
		||||
 | 
			
		||||
# 日志配置
 | 
			
		||||
logging:
 | 
			
		||||
  level:
 | 
			
		||||
    org.dromara: @logging.level@
 | 
			
		||||
    org.springframework: warn
 | 
			
		||||
    org.mybatis.spring.mapper: error
 | 
			
		||||
    org.apache.fury: warn
 | 
			
		||||
  config: classpath:logback-plus.xml
 | 
			
		||||
 | 
			
		||||
# 用户配置
 | 
			
		||||
@@ -52,7 +60,7 @@ user:
 | 
			
		||||
# Spring配置
 | 
			
		||||
spring:
 | 
			
		||||
  application:
 | 
			
		||||
    name: RuoYi-Vue-Plus
 | 
			
		||||
    name: ${ruoyi.name}
 | 
			
		||||
  threads:
 | 
			
		||||
    # 开启虚拟线程 仅jdk21可用
 | 
			
		||||
    virtual:
 | 
			
		||||
@@ -102,15 +110,17 @@ sa-token:
 | 
			
		||||
security:
 | 
			
		||||
  # 排除路径
 | 
			
		||||
  excludes:
 | 
			
		||||
    # 静态资源
 | 
			
		||||
    - /*.html
 | 
			
		||||
    - /**/*.html
 | 
			
		||||
    - /**/*.css
 | 
			
		||||
    - /**/*.js
 | 
			
		||||
    # 公共路径
 | 
			
		||||
    - /favicon.ico
 | 
			
		||||
    - /error
 | 
			
		||||
    # swagger 文档配置
 | 
			
		||||
    - /*/api-docs
 | 
			
		||||
    - /*/api-docs/**
 | 
			
		||||
    - /warm-flow-ui/token-name
 | 
			
		||||
 | 
			
		||||
# 多租户配置
 | 
			
		||||
tenant:
 | 
			
		||||
@@ -131,8 +141,6 @@ tenant:
 | 
			
		||||
# MyBatisPlus配置
 | 
			
		||||
# https://baomidou.com/config/
 | 
			
		||||
mybatis-plus:
 | 
			
		||||
  # 自定义配置 是否全局开启逻辑删除 关闭后 所有逻辑删除功能将失效
 | 
			
		||||
  enableLogicDelete: true
 | 
			
		||||
  # 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper
 | 
			
		||||
  mapperPackage: org.dromara.**.mapper
 | 
			
		||||
  # 对应的 XML 文件位置
 | 
			
		||||
@@ -177,9 +185,12 @@ springdoc:
 | 
			
		||||
  api-docs:
 | 
			
		||||
    # 是否开启接口文档
 | 
			
		||||
    enabled: true
 | 
			
		||||
#  swagger-ui:
 | 
			
		||||
#    # 持久化认证数据
 | 
			
		||||
#    persistAuthorization: true
 | 
			
		||||
  info:
 | 
			
		||||
    # 标题
 | 
			
		||||
    title: '标题:RuoYi-Vue-Plus多租户管理系统_接口文档'
 | 
			
		||||
    title: '标题:${ruoyi.name}多租户管理系统_接口文档'
 | 
			
		||||
    # 描述
 | 
			
		||||
    description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
 | 
			
		||||
    # 版本
 | 
			
		||||
@@ -206,16 +217,15 @@ springdoc:
 | 
			
		||||
      packages-to-scan: org.dromara.system
 | 
			
		||||
    - group: 4.代码生成模块
 | 
			
		||||
      packages-to-scan: org.dromara.generator
 | 
			
		||||
    - group: 5.工作流模块
 | 
			
		||||
      packages-to-scan: org.dromara.workflow
 | 
			
		||||
 | 
			
		||||
# 防止XSS攻击
 | 
			
		||||
xss:
 | 
			
		||||
  # 过滤开关
 | 
			
		||||
  enabled: true
 | 
			
		||||
  # 排除链接(多个用逗号分隔)
 | 
			
		||||
  excludeUrls:
 | 
			
		||||
    - /system/notice
 | 
			
		||||
  excludes: /system/notice
 | 
			
		||||
  # 匹配链接
 | 
			
		||||
  urlPatterns: /system/*,/monitor/*,/tool/*
 | 
			
		||||
 | 
			
		||||
# 全局线程池相关配置
 | 
			
		||||
# 如使用JDK21请直接使用虚拟线程 不要开启此配置
 | 
			
		||||
@@ -260,19 +270,24 @@ websocket:
 | 
			
		||||
  # 设置访问源地址
 | 
			
		||||
  allowedOrigins: '*'
 | 
			
		||||
 | 
			
		||||
--- # warm-flow工作流配置
 | 
			
		||||
warm-flow:
 | 
			
		||||
  # 是否开启工作流,默认true
 | 
			
		||||
--- #flowable配置
 | 
			
		||||
flowable:
 | 
			
		||||
  # 开关 用于启动/停用工作流
 | 
			
		||||
  enabled: true
 | 
			
		||||
  # 是否开启设计器ui
 | 
			
		||||
  ui: true
 | 
			
		||||
  # 默认Authorization,如果有多个token,用逗号分隔
 | 
			
		||||
  token-name: ${sa-token.token-name},clientid
 | 
			
		||||
  # 流程状态对应的三元色
 | 
			
		||||
  chart-status-color:
 | 
			
		||||
    ## 未办理
 | 
			
		||||
    - 62,62,62
 | 
			
		||||
    ## 待办理
 | 
			
		||||
    - 255,205,23
 | 
			
		||||
    ## 已办理
 | 
			
		||||
    - 157,255,0
 | 
			
		||||
  process.enabled: ${flowable.enabled}
 | 
			
		||||
  eventregistry.enabled: ${flowable.enabled}
 | 
			
		||||
  async-executor-activate: false #关闭定时任务JOB
 | 
			
		||||
  #  将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
 | 
			
		||||
  database-schema-update: true
 | 
			
		||||
  activity-font-name: 宋体
 | 
			
		||||
  label-font-name: 宋体
 | 
			
		||||
  annotation-font-name: 宋体
 | 
			
		||||
  # 关闭各个模块生成表,目前只使用工作流基础表
 | 
			
		||||
  idm:
 | 
			
		||||
    enabled: false
 | 
			
		||||
  cmmn:
 | 
			
		||||
    enabled: false
 | 
			
		||||
  dmn:
 | 
			
		||||
    enabled: false
 | 
			
		||||
  app:
 | 
			
		||||
    enabled: false
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
<configuration>
 | 
			
		||||
    <property name="log.path" value="./logs"/>
 | 
			
		||||
    <property name="console.log.pattern"
 | 
			
		||||
              value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
 | 
			
		||||
              value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
 | 
			
		||||
    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
 | 
			
		||||
 | 
			
		||||
    <!-- 控制台输出 -->
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
package org.dromara.test;
 | 
			
		||||
 | 
			
		||||
import org.dromara.common.web.config.properties.CaptchaProperties;
 | 
			
		||||
import org.dromara.common.core.config.RuoYiConfig;
 | 
			
		||||
import org.junit.jupiter.api.*;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.boot.test.context.SpringBootTest;
 | 
			
		||||
@@ -17,19 +17,19 @@ import java.util.concurrent.TimeUnit;
 | 
			
		||||
public class DemoUnitTest {
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private CaptchaProperties captchaProperties;
 | 
			
		||||
    private RuoYiConfig ruoYiConfig;
 | 
			
		||||
 | 
			
		||||
    @DisplayName("测试 @SpringBootTest @Test @DisplayName 注解")
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testTest() {
 | 
			
		||||
        System.out.println(captchaProperties);
 | 
			
		||||
        System.out.println(ruoYiConfig);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Disabled
 | 
			
		||||
    @DisplayName("测试 @Disabled 注解")
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testDisabled() {
 | 
			
		||||
        System.out.println(captchaProperties);
 | 
			
		||||
        System.out.println(ruoYiConfig);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Timeout(value = 2L, unit = TimeUnit.SECONDS)
 | 
			
		||||
@@ -37,7 +37,7 @@ public class DemoUnitTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testTimeout() throws InterruptedException {
 | 
			
		||||
        Thread.sleep(3000);
 | 
			
		||||
        System.out.println(captchaProperties);
 | 
			
		||||
        System.out.println(ruoYiConfig);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
3f2ee348-0303-40ca-bf03-03f48d2d2141
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@@ -1,4 +0,0 @@
 | 
			
		||||
3
 | 
			
		||||
SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso10646-1
 | 
			
		||||
SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso8859-1
 | 
			
		||||
SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-koi8-r
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
3
 | 
			
		||||
SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso10646-1
 | 
			
		||||
SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso8859-1
 | 
			
		||||
SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-koi8-r
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
    </description>
 | 
			
		||||
 | 
			
		||||
    <properties>
 | 
			
		||||
        <revision>5.3.1</revision>
 | 
			
		||||
        <revision>5.2.2</revision>
 | 
			
		||||
    </properties>
 | 
			
		||||
 | 
			
		||||
    <dependencyManagement>
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,33 @@
 | 
			
		||||
package org.dromara.common.core.config;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 读取项目相关配置
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@Component
 | 
			
		||||
@ConfigurationProperties(prefix = "ruoyi")
 | 
			
		||||
public class RuoYiConfig {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 项目名称
 | 
			
		||||
     */
 | 
			
		||||
    private String name;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 版本
 | 
			
		||||
     */
 | 
			
		||||
    private String version;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 版权年份
 | 
			
		||||
     */
 | 
			
		||||
    private String copyrightYear;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -4,13 +4,11 @@ import jakarta.annotation.PreDestroy;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
 | 
			
		||||
import org.dromara.common.core.config.properties.ThreadPoolProperties;
 | 
			
		||||
import org.dromara.common.core.utils.SpringUtils;
 | 
			
		||||
import org.dromara.common.core.utils.Threads;
 | 
			
		||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 | 
			
		||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.core.task.VirtualThreadTaskExecutor;
 | 
			
		||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.ScheduledExecutorService;
 | 
			
		||||
@@ -51,15 +49,8 @@ public class ThreadPoolConfig {
 | 
			
		||||
     */
 | 
			
		||||
    @Bean(name = "scheduledExecutorService")
 | 
			
		||||
    protected ScheduledExecutorService scheduledExecutorService() {
 | 
			
		||||
        // daemon 必须为 true
 | 
			
		||||
        BasicThreadFactory.Builder builder = new BasicThreadFactory.Builder().daemon(true);
 | 
			
		||||
        if (SpringUtils.isVirtual()) {
 | 
			
		||||
            builder.namingPattern("virtual-schedule-pool-%d").wrappedFactory(new VirtualThreadTaskExecutor().getVirtualThreadFactory());
 | 
			
		||||
        } else {
 | 
			
		||||
            builder.namingPattern("schedule-pool-%d");
 | 
			
		||||
        }
 | 
			
		||||
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core,
 | 
			
		||||
            builder.build(),
 | 
			
		||||
            new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
 | 
			
		||||
            new ThreadPoolExecutor.CallerRunsPolicy()) {
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void afterExecute(Runnable r, Throwable t) {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@ package org.dromara.common.core.config;
 | 
			
		||||
import jakarta.validation.Validator;
 | 
			
		||||
import org.hibernate.validator.HibernateValidator;
 | 
			
		||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
			
		||||
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
 | 
			
		||||
import org.springframework.context.MessageSource;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
 | 
			
		||||
@@ -15,11 +14,11 @@ import java.util.Properties;
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@AutoConfiguration(before = ValidationAutoConfiguration.class)
 | 
			
		||||
@AutoConfiguration
 | 
			
		||||
public class ValidatorConfig {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 配置校验框架 快速失败模式
 | 
			
		||||
     * 配置校验框架 快速返回模式
 | 
			
		||||
     */
 | 
			
		||||
    @Bean
 | 
			
		||||
    public Validator validator(MessageSource messageSource) {
 | 
			
		||||
@@ -29,7 +28,7 @@ public class ValidatorConfig {
 | 
			
		||||
            // 设置使用 HibernateValidator 校验器
 | 
			
		||||
            factoryBean.setProviderClass(HibernateValidator.class);
 | 
			
		||||
            Properties properties = new Properties();
 | 
			
		||||
            // 设置快速失败模式(fail-fast),即校验过程中一旦遇到失败,立即停止并返回错误
 | 
			
		||||
            // 设置 快速异常返回
 | 
			
		||||
            properties.setProperty("hibernate.validator.fail_fast", "true");
 | 
			
		||||
            factoryBean.setValidationProperties(properties);
 | 
			
		||||
            // 加载配置
 | 
			
		||||
 
 | 
			
		||||
@@ -3,14 +3,13 @@ package org.dromara.common.core.constant;
 | 
			
		||||
/**
 | 
			
		||||
 * 缓存组名称常量
 | 
			
		||||
 * <p>
 | 
			
		||||
 * key 格式为 cacheNames#ttl#maxIdleTime#maxSize#local
 | 
			
		||||
 * key 格式为 cacheNames#ttl#maxIdleTime#maxSize
 | 
			
		||||
 * <p>
 | 
			
		||||
 * ttl 过期时间 如果设置为0则不过期 默认为0
 | 
			
		||||
 * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0
 | 
			
		||||
 * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0
 | 
			
		||||
 * local 默认开启本地缓存为1 关闭本地缓存为0
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500、test#1h#0#500#0
 | 
			
		||||
 * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@@ -31,11 +30,6 @@ public interface CacheNames {
 | 
			
		||||
     */
 | 
			
		||||
    String SYS_DICT = "sys_dict";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 数据字典类型
 | 
			
		||||
     */
 | 
			
		||||
    String SYS_DICT_TYPE = "sys_dict_type";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户
 | 
			
		||||
     */
 | 
			
		||||
@@ -66,16 +60,6 @@ public interface CacheNames {
 | 
			
		||||
     */
 | 
			
		||||
    String SYS_OSS = "sys_oss#30d";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 角色自定义权限
 | 
			
		||||
     */
 | 
			
		||||
    String SYS_ROLE_CUSTOM = "sys_role_custom#30d";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 部门及以下权限
 | 
			
		||||
     */
 | 
			
		||||
    String SYS_DEPT_AND_CHILD = "sys_dept_and_child#30d";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * OSS配置
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -68,7 +68,12 @@ public interface Constants {
 | 
			
		||||
    Integer CAPTCHA_EXPIRATION = 2;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 顶级父级id
 | 
			
		||||
     * 令牌
 | 
			
		||||
     */
 | 
			
		||||
    String TOKEN = "token";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 顶级部门id
 | 
			
		||||
     */
 | 
			
		||||
    Long TOP_PARENT_ID = 0L;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,14 +17,9 @@ public interface RegexConstants extends RegexPool {
 | 
			
		||||
    String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 权限标识必须符合以下格式:
 | 
			
		||||
     * 1. 标准格式:xxx:yyy:zzz
 | 
			
		||||
     * - 第一部分(xxx):只能包含字母、数字和下划线(_),不能使用 `*`
 | 
			
		||||
     * - 第二部分(yyy):可以包含字母、数字、下划线(_)和 `*`
 | 
			
		||||
     * - 第三部分(zzz):可以包含字母、数字、下划线(_)和 `*`
 | 
			
		||||
     * 2. 允许空字符串(""),表示没有权限标识
 | 
			
		||||
     * 权限标识必须符合 tool:build:list 格式,或者空字符串
 | 
			
		||||
     */
 | 
			
		||||
    String PERMISSION_STRING = "^$|^[a-zA-Z0-9_]+:[a-zA-Z0-9_*]+:[a-zA-Z0-9_*]+$";
 | 
			
		||||
    String PERMISSION_STRING = "^(|^[a-zA-Z0-9_]+:[a-zA-Z0-9_]+:[a-zA-Z0-9_]+)$";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 身份证号码(后6位)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,80 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.constant;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 系统常量信息
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
public interface SystemConstants {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 正常状态
 | 
			
		||||
     */
 | 
			
		||||
    String NORMAL = "0";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 异常状态
 | 
			
		||||
     */
 | 
			
		||||
    String DISABLE = "1";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否为系统默认(是)
 | 
			
		||||
     */
 | 
			
		||||
    String YES = "Y";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否为系统默认(否)
 | 
			
		||||
     */
 | 
			
		||||
    String NO = "N";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否菜单外链(是)
 | 
			
		||||
     */
 | 
			
		||||
    String YES_FRAME = "0";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否菜单外链(否)
 | 
			
		||||
     */
 | 
			
		||||
    String NO_FRAME = "1";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 菜单类型(目录)
 | 
			
		||||
     */
 | 
			
		||||
    String TYPE_DIR = "M";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 菜单类型(菜单)
 | 
			
		||||
     */
 | 
			
		||||
    String TYPE_MENU = "C";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 菜单类型(按钮)
 | 
			
		||||
     */
 | 
			
		||||
    String TYPE_BUTTON = "F";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Layout组件标识
 | 
			
		||||
     */
 | 
			
		||||
    String LAYOUT = "Layout";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * ParentView组件标识
 | 
			
		||||
     */
 | 
			
		||||
    String PARENT_VIEW = "ParentView";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * InnerLink组件标识
 | 
			
		||||
     */
 | 
			
		||||
    String INNER_LINK = "InnerLink";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 超级管理员ID
 | 
			
		||||
     */
 | 
			
		||||
    Long SUPER_ADMIN_ID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根部门祖级列表
 | 
			
		||||
     */
 | 
			
		||||
    String ROOT_DEPT_ANCESTORS = "0";
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -7,6 +7,16 @@ package org.dromara.common.core.constant;
 | 
			
		||||
 */
 | 
			
		||||
public interface TenantConstants {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户正常状态
 | 
			
		||||
     */
 | 
			
		||||
    String NORMAL = "0";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户封禁状态
 | 
			
		||||
     */
 | 
			
		||||
    String DISABLE = "1";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 超级管理员ID
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,152 @@
 | 
			
		||||
package org.dromara.common.core.constant;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 用户常量信息
 | 
			
		||||
 *
 | 
			
		||||
 * @author ruoyi
 | 
			
		||||
 */
 | 
			
		||||
public interface UserConstants {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 平台内系统用户的唯一标志
 | 
			
		||||
     */
 | 
			
		||||
    String SYS_USER = "SYS_USER";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 正常状态
 | 
			
		||||
     */
 | 
			
		||||
    String NORMAL = "0";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 异常状态
 | 
			
		||||
     */
 | 
			
		||||
    String EXCEPTION = "1";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户正常状态
 | 
			
		||||
     */
 | 
			
		||||
    String USER_NORMAL = "0";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户封禁状态
 | 
			
		||||
     */
 | 
			
		||||
    String USER_DISABLE = "1";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 角色正常状态
 | 
			
		||||
     */
 | 
			
		||||
    String ROLE_NORMAL = "0";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 角色封禁状态
 | 
			
		||||
     */
 | 
			
		||||
    String ROLE_DISABLE = "1";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 部门正常状态
 | 
			
		||||
     */
 | 
			
		||||
    String DEPT_NORMAL = "0";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 部门停用状态
 | 
			
		||||
     */
 | 
			
		||||
    String DEPT_DISABLE = "1";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 岗位正常状态
 | 
			
		||||
     */
 | 
			
		||||
    String POST_NORMAL = "0";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 岗位停用状态
 | 
			
		||||
     */
 | 
			
		||||
    String POST_DISABLE = "1";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 字典正常状态
 | 
			
		||||
     */
 | 
			
		||||
    String DICT_NORMAL = "0";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 通用存在标志
 | 
			
		||||
     */
 | 
			
		||||
    String DEL_FLAG_NORMAL = "0";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 通用删除标志
 | 
			
		||||
     */
 | 
			
		||||
    String DEL_FLAG_REMOVED  = "2";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否为系统默认(是)
 | 
			
		||||
     */
 | 
			
		||||
    String YES = "Y";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否菜单外链(是)
 | 
			
		||||
     */
 | 
			
		||||
    String YES_FRAME = "0";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否菜单外链(否)
 | 
			
		||||
     */
 | 
			
		||||
    String NO_FRAME = "1";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 菜单正常状态
 | 
			
		||||
     */
 | 
			
		||||
    String MENU_NORMAL = "0";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 菜单停用状态
 | 
			
		||||
     */
 | 
			
		||||
    String MENU_DISABLE = "1";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 菜单类型(目录)
 | 
			
		||||
     */
 | 
			
		||||
    String TYPE_DIR = "M";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 菜单类型(菜单)
 | 
			
		||||
     */
 | 
			
		||||
    String TYPE_MENU = "C";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 菜单类型(按钮)
 | 
			
		||||
     */
 | 
			
		||||
    String TYPE_BUTTON = "F";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Layout组件标识
 | 
			
		||||
     */
 | 
			
		||||
    String LAYOUT = "Layout";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * ParentView组件标识
 | 
			
		||||
     */
 | 
			
		||||
    String PARENT_VIEW = "ParentView";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * InnerLink组件标识
 | 
			
		||||
     */
 | 
			
		||||
    String INNER_LINK = "InnerLink";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户名长度限制
 | 
			
		||||
     */
 | 
			
		||||
    int USERNAME_MIN_LENGTH = 2;
 | 
			
		||||
    int USERNAME_MAX_LENGTH = 20;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 密码长度限制
 | 
			
		||||
     */
 | 
			
		||||
    int PASSWORD_MIN_LENGTH = 5;
 | 
			
		||||
    int PASSWORD_MAX_LENGTH = 20;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 超级管理员ID
 | 
			
		||||
     */
 | 
			
		||||
    Long SUPER_ADMIN_ID = 1L;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,71 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.domain.dto;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 办理任务请求对象
 | 
			
		||||
 *
 | 
			
		||||
 * @author may
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
public class CompleteTaskDTO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 任务id
 | 
			
		||||
     */
 | 
			
		||||
    private Long taskId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 附件id
 | 
			
		||||
     */
 | 
			
		||||
    private String fileId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 抄送人员
 | 
			
		||||
     */
 | 
			
		||||
    private List<FlowCopyDTO> flowCopyList;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 消息类型
 | 
			
		||||
     */
 | 
			
		||||
    private List<String> messageType;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 办理意见
 | 
			
		||||
     */
 | 
			
		||||
    private String message;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 消息通知
 | 
			
		||||
     */
 | 
			
		||||
    private String notice;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 流程变量
 | 
			
		||||
     */
 | 
			
		||||
    private Map<String, Object> variables;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 扩展变量(此处为逗号分隔的ossId)
 | 
			
		||||
     */
 | 
			
		||||
    private String ext;
 | 
			
		||||
 | 
			
		||||
    public Map<String, Object> getVariables() {
 | 
			
		||||
        if (variables == null) {
 | 
			
		||||
            return new HashMap<>(16);
 | 
			
		||||
        }
 | 
			
		||||
        variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
 | 
			
		||||
        return variables;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,36 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.domain.dto;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 部门
 | 
			
		||||
 *
 | 
			
		||||
 * @author AprilWind
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
public class DeptDTO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 部门ID
 | 
			
		||||
     */
 | 
			
		||||
    private Long deptId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 父部门ID
 | 
			
		||||
     */
 | 
			
		||||
    private Long parentId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 部门名称
 | 
			
		||||
     */
 | 
			
		||||
    private String deptName;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.domain.dto;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 字典数据DTO
 | 
			
		||||
 *
 | 
			
		||||
 * @author AprilWind
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
public class DictDataDTO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 字典标签
 | 
			
		||||
     */
 | 
			
		||||
    private String dictLabel;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 字典键值
 | 
			
		||||
     */
 | 
			
		||||
    private String dictValue;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否默认(Y是 N否)
 | 
			
		||||
     */
 | 
			
		||||
    private String isDefault;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 备注
 | 
			
		||||
     */
 | 
			
		||||
    private String remark;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.domain.dto;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 字典类型DTO
 | 
			
		||||
 *
 | 
			
		||||
 * @author AprilWind
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
public class DictTypeDTO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 字典主键
 | 
			
		||||
     */
 | 
			
		||||
    private Long dictId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 字典名称
 | 
			
		||||
     */
 | 
			
		||||
    private String dictName;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 字典类型
 | 
			
		||||
     */
 | 
			
		||||
    private String dictType;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 备注
 | 
			
		||||
     */
 | 
			
		||||
    private String remark;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,46 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.domain.dto;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 岗位
 | 
			
		||||
 *
 | 
			
		||||
 * @author AprilWind
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
public class PostDTO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 岗位ID
 | 
			
		||||
     */
 | 
			
		||||
    private Long postId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 部门id
 | 
			
		||||
     */
 | 
			
		||||
    private Long deptId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 岗位编码
 | 
			
		||||
     */
 | 
			
		||||
    private String postCode;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 岗位名称
 | 
			
		||||
     */
 | 
			
		||||
    private String postName;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 岗位类别编码
 | 
			
		||||
     */
 | 
			
		||||
    private String postCategory;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -35,7 +35,7 @@ public class RoleDTO implements Serializable {
 | 
			
		||||
    private String roleKey;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限)
 | 
			
		||||
     * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限)
 | 
			
		||||
     */
 | 
			
		||||
    private String dataScope;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,45 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.domain.dto;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 启动流程对象
 | 
			
		||||
 *
 | 
			
		||||
 * @author may
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
public class StartProcessDTO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 业务唯一值id
 | 
			
		||||
     */
 | 
			
		||||
    private String businessId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 流程定义编码
 | 
			
		||||
     */
 | 
			
		||||
    private String flowCode;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}}
 | 
			
		||||
     */
 | 
			
		||||
    private Map<String, Object> variables;
 | 
			
		||||
 | 
			
		||||
    public Map<String, Object> getVariables() {
 | 
			
		||||
        if (variables == null) {
 | 
			
		||||
            return new HashMap<>(16);
 | 
			
		||||
        }
 | 
			
		||||
        variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
 | 
			
		||||
        return variables;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.domain.dto;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 启动流程返回对象
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
public class StartProcessReturnDTO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 流程实例id
 | 
			
		||||
     */
 | 
			
		||||
    private Long processInstanceId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 任务id
 | 
			
		||||
     */
 | 
			
		||||
    private Long taskId;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,101 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.domain.dto;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 任务受让人
 | 
			
		||||
 *
 | 
			
		||||
 * @author AprilWind
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
public class TaskAssigneeDTO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 总大小
 | 
			
		||||
     */
 | 
			
		||||
    private Long total = 0L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
    private List<TaskHandler> list;
 | 
			
		||||
 | 
			
		||||
    public TaskAssigneeDTO(Long total, List<TaskHandler> list) {
 | 
			
		||||
        this.total = total;
 | 
			
		||||
        this.list = list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将源列表转换为 TaskHandler 列表
 | 
			
		||||
     *
 | 
			
		||||
     * @param <T>              通用类型
 | 
			
		||||
     * @param sourceList       待转换的源列表
 | 
			
		||||
     * @param storageId        提取 storageId 的函数
 | 
			
		||||
     * @param handlerCode      提取 handlerCode 的函数
 | 
			
		||||
     * @param handlerName      提取 handlerName 的函数
 | 
			
		||||
     * @param groupName        提取 groupName 的函数
 | 
			
		||||
     * @param createTimeMapper 提取 createTime 的函数
 | 
			
		||||
     * @return 转换后的 TaskHandler 列表
 | 
			
		||||
     */
 | 
			
		||||
    public static <T> List<TaskHandler> convertToHandlerList(
 | 
			
		||||
        List<T> sourceList,
 | 
			
		||||
        Function<T, Long> storageId,
 | 
			
		||||
        Function<T, String> handlerCode,
 | 
			
		||||
        Function<T, String> handlerName,
 | 
			
		||||
        Function<T, Long> groupName,
 | 
			
		||||
        Function<T, Date> createTimeMapper) {
 | 
			
		||||
        return sourceList.stream()
 | 
			
		||||
            .map(item -> new TaskHandler(
 | 
			
		||||
                String.valueOf(storageId.apply(item)),
 | 
			
		||||
                handlerCode.apply(item),
 | 
			
		||||
                handlerName.apply(item),
 | 
			
		||||
                groupName != null ? String.valueOf(groupName.apply(item)) : null,
 | 
			
		||||
                createTimeMapper.apply(item)
 | 
			
		||||
            )).collect(Collectors.toList());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Data
 | 
			
		||||
    @NoArgsConstructor
 | 
			
		||||
    @AllArgsConstructor
 | 
			
		||||
    public static class TaskHandler {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 主键
 | 
			
		||||
         */
 | 
			
		||||
        private String storageId;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 权限编码
 | 
			
		||||
         */
 | 
			
		||||
        private String handlerCode;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 权限名称
 | 
			
		||||
         */
 | 
			
		||||
        private String handlerName;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 权限分组
 | 
			
		||||
         */
 | 
			
		||||
        private String groupName;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 创建时间
 | 
			
		||||
         */
 | 
			
		||||
        private Date createTime;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,54 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.domain.event;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 流程创建任务监听
 | 
			
		||||
 *
 | 
			
		||||
 * @author may
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
public class ProcessCreateTaskEvent implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户ID
 | 
			
		||||
     */
 | 
			
		||||
    private String tenantId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 流程定义编码
 | 
			
		||||
     */
 | 
			
		||||
    private String flowCode;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
 | 
			
		||||
     */
 | 
			
		||||
    private Integer nodeType;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 流程节点编码
 | 
			
		||||
     */
 | 
			
		||||
    private String nodeCode;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 流程节点名称
 | 
			
		||||
     */
 | 
			
		||||
    private String nodeName;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 任务id
 | 
			
		||||
     */
 | 
			
		||||
    private Long taskId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 业务id
 | 
			
		||||
     */
 | 
			
		||||
    private String businessId;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.domain.event;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 删除流程监听
 | 
			
		||||
 *
 | 
			
		||||
 * @author AprilWind
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
public class ProcessDeleteEvent implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户ID
 | 
			
		||||
     */
 | 
			
		||||
    private String tenantId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 流程定义编码
 | 
			
		||||
     */
 | 
			
		||||
    private String flowCode;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 业务id
 | 
			
		||||
     */
 | 
			
		||||
    private String businessId;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -4,13 +4,13 @@ import lombok.Data;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 总体流程监听
 | 
			
		||||
 *
 | 
			
		||||
 * @author may
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
public class ProcessEvent implements Serializable {
 | 
			
		||||
 | 
			
		||||
@@ -18,48 +18,24 @@ public class ProcessEvent implements Serializable {
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户ID
 | 
			
		||||
     * 流程定义key
 | 
			
		||||
     */
 | 
			
		||||
    private String tenantId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 流程定义编码
 | 
			
		||||
     */
 | 
			
		||||
    private String flowCode;
 | 
			
		||||
    private String key;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 业务id
 | 
			
		||||
     */
 | 
			
		||||
    private String businessId;
 | 
			
		||||
    private String businessKey;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
 | 
			
		||||
     */
 | 
			
		||||
    private Integer nodeType;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 流程节点编码
 | 
			
		||||
     */
 | 
			
		||||
    private String nodeCode;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 流程节点名称
 | 
			
		||||
     */
 | 
			
		||||
    private String nodeName;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 流程状态
 | 
			
		||||
     * 状态
 | 
			
		||||
     */
 | 
			
		||||
    private String status;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 办理参数
 | 
			
		||||
     */
 | 
			
		||||
    private Map<String, Object> params;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 当为true时为申请人节点办理
 | 
			
		||||
     */
 | 
			
		||||
    private Boolean submit;
 | 
			
		||||
    private boolean submit;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,40 @@
 | 
			
		||||
package org.dromara.common.core.domain.event;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 流程办理监听
 | 
			
		||||
 *
 | 
			
		||||
 * @author may
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
public class ProcessTaskEvent implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 流程定义key
 | 
			
		||||
     */
 | 
			
		||||
    private String key;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 审批节点key
 | 
			
		||||
     */
 | 
			
		||||
    private String taskDefinitionKey;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 任务id
 | 
			
		||||
     */
 | 
			
		||||
    private String taskId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 业务id
 | 
			
		||||
     */
 | 
			
		||||
    private String businessKey;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,8 @@
 | 
			
		||||
package org.dromara.common.core.domain.model;
 | 
			
		||||
 | 
			
		||||
import org.dromara.common.core.domain.dto.RoleDTO;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.dromara.common.core.domain.dto.PostDTO;
 | 
			
		||||
import org.dromara.common.core.domain.dto.RoleDTO;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
@@ -112,11 +111,6 @@ public class LoginUser implements Serializable {
 | 
			
		||||
     */
 | 
			
		||||
    private List<RoleDTO> roles;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 岗位对象
 | 
			
		||||
     */
 | 
			
		||||
    private List<PostDTO> posts;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 数据权限 当前角色ID
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@ import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import org.hibernate.validator.constraints.Length;
 | 
			
		||||
 | 
			
		||||
import static org.dromara.common.core.constant.UserConstants.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 密码登录对象
 | 
			
		||||
 *
 | 
			
		||||
@@ -18,14 +20,14 @@ public class PasswordLoginBody extends LoginBody {
 | 
			
		||||
     * 用户名
 | 
			
		||||
     */
 | 
			
		||||
    @NotBlank(message = "{user.username.not.blank}")
 | 
			
		||||
    @Length(min = 2, max = 30, message = "{user.username.length.valid}")
 | 
			
		||||
    @Length(min = USERNAME_MIN_LENGTH, max = USERNAME_MAX_LENGTH, message = "{user.username.length.valid}")
 | 
			
		||||
    private String username;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户密码
 | 
			
		||||
     */
 | 
			
		||||
    @NotBlank(message = "{user.password.not.blank}")
 | 
			
		||||
    @Length(min = 5, max = 30, message = "{user.password.length.valid}")
 | 
			
		||||
    @Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
 | 
			
		||||
    private String password;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@ import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import org.hibernate.validator.constraints.Length;
 | 
			
		||||
 | 
			
		||||
import static org.dromara.common.core.constant.UserConstants.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 用户注册对象
 | 
			
		||||
 *
 | 
			
		||||
@@ -18,14 +20,14 @@ public class RegisterBody extends LoginBody {
 | 
			
		||||
     * 用户名
 | 
			
		||||
     */
 | 
			
		||||
    @NotBlank(message = "{user.username.not.blank}")
 | 
			
		||||
    @Length(min = 2, max = 30, message = "{user.username.length.valid}")
 | 
			
		||||
    @Length(min = USERNAME_MIN_LENGTH, max = USERNAME_MAX_LENGTH, message = "{user.username.length.valid}")
 | 
			
		||||
    private String username;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户密码
 | 
			
		||||
     */
 | 
			
		||||
    @NotBlank(message = "{user.password.not.blank}")
 | 
			
		||||
    @Length(min = 5, max = 30, message = "{user.password.length.valid}")
 | 
			
		||||
    @Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
 | 
			
		||||
    private String password;
 | 
			
		||||
 | 
			
		||||
    private String userType;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,56 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.domain.model;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 任务受让人
 | 
			
		||||
 *
 | 
			
		||||
 * @author AprilWind
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
public class TaskAssigneeBody implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 权限编码
 | 
			
		||||
     */
 | 
			
		||||
    private String handlerCode;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 权限名称
 | 
			
		||||
     */
 | 
			
		||||
    private String handlerName;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 权限分组
 | 
			
		||||
     */
 | 
			
		||||
    private String groupId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 开始时间
 | 
			
		||||
     */
 | 
			
		||||
    private String beginTime;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 结束时间
 | 
			
		||||
     */
 | 
			
		||||
    private String endTime;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 当前页
 | 
			
		||||
     */
 | 
			
		||||
    private Integer pageNum = 1;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 每页显示条数
 | 
			
		||||
     */
 | 
			
		||||
    private Integer pageSize = 10;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -7,10 +7,6 @@ import org.dromara.common.core.exception.ServiceException;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 业务状态枚举
 | 
			
		||||
@@ -20,37 +16,30 @@ import java.util.stream.Collectors;
 | 
			
		||||
@Getter
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public enum BusinessStatusEnum {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 已撤销
 | 
			
		||||
     */
 | 
			
		||||
    CANCEL("cancel", "已撤销"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 草稿
 | 
			
		||||
     */
 | 
			
		||||
    DRAFT("draft", "草稿"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 待审核
 | 
			
		||||
     */
 | 
			
		||||
    WAITING("waiting", "待审核"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 已完成
 | 
			
		||||
     */
 | 
			
		||||
    FINISH("finish", "已完成"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 已作废
 | 
			
		||||
     */
 | 
			
		||||
    INVALID("invalid", "已作废"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 已退回
 | 
			
		||||
     */
 | 
			
		||||
    BACK("back", "已退回"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 已终止
 | 
			
		||||
     */
 | 
			
		||||
@@ -66,72 +55,20 @@ public enum BusinessStatusEnum {
 | 
			
		||||
     */
 | 
			
		||||
    private final String desc;
 | 
			
		||||
 | 
			
		||||
    private static final Map<String, BusinessStatusEnum> STATUS_MAP = Arrays.stream(BusinessStatusEnum.values())
 | 
			
		||||
        .collect(Collectors.toConcurrentMap(BusinessStatusEnum::getStatus, Function.identity()));
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据状态获取对应的 BusinessStatusEnum 枚举
 | 
			
		||||
     * 获取业务状态
 | 
			
		||||
     *
 | 
			
		||||
     * @param status 业务状态码
 | 
			
		||||
     * @return 对应的 BusinessStatusEnum 枚举,如果找不到则返回 null
 | 
			
		||||
     */
 | 
			
		||||
    public static BusinessStatusEnum getByStatus(String status) {
 | 
			
		||||
        // 使用 STATUS_MAP 获取对应的枚举,若找不到则返回 null
 | 
			
		||||
        return STATUS_MAP.get(status);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据状态获取对应的业务状态描述信息
 | 
			
		||||
     *
 | 
			
		||||
     * @param status 业务状态码
 | 
			
		||||
     * @return 返回业务状态描述,若状态码为空或未找到对应的枚举,返回空字符串
 | 
			
		||||
     * @param status 状态
 | 
			
		||||
     */
 | 
			
		||||
    public static String findByStatus(String status) {
 | 
			
		||||
        if (StringUtils.isBlank(status)) {
 | 
			
		||||
            return StrUtil.EMPTY;
 | 
			
		||||
        }
 | 
			
		||||
        BusinessStatusEnum statusEnum = STATUS_MAP.get(status);
 | 
			
		||||
        return (statusEnum != null) ? statusEnum.getDesc() : StrUtil.EMPTY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 判断是否为指定的状态之一:草稿、已撤销或已退回
 | 
			
		||||
     *
 | 
			
		||||
     * @param status 要检查的状态
 | 
			
		||||
     * @return 如果状态为草稿、已撤销或已退回之一,则返回 true;否则返回 false
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isDraftOrCancelOrBack(String status) {
 | 
			
		||||
        return DRAFT.status.equals(status) || CANCEL.status.equals(status) || BACK.status.equals(status);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 判断是否为撤销,退回,作废,终止
 | 
			
		||||
     *
 | 
			
		||||
     * @param status status
 | 
			
		||||
     * @return 结果
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean initialState(String status) {
 | 
			
		||||
        return CANCEL.status.equals(status) || BACK.status.equals(status) || INVALID.status.equals(status) || TERMINATION.status.equals(status);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取运行中的实例状态列表
 | 
			
		||||
     *
 | 
			
		||||
     * @return 包含运行中实例状态的不可变列表
 | 
			
		||||
     * (包含 DRAFT、WAITING、BACK 和 CANCEL 状态)
 | 
			
		||||
     */
 | 
			
		||||
    public static List<String> runningStatus() {
 | 
			
		||||
        return Arrays.asList(DRAFT.status, WAITING.status, BACK.status, CANCEL.status);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取结束实例的状态列表
 | 
			
		||||
     *
 | 
			
		||||
     * @return 包含结束实例状态的不可变列表
 | 
			
		||||
     * (包含 FINISH、INVALID 和 TERMINATION 状态)
 | 
			
		||||
     */
 | 
			
		||||
    public static List<String> finishStatus() {
 | 
			
		||||
        return Arrays.asList(FINISH.status, INVALID.status, TERMINATION.status);
 | 
			
		||||
        return Arrays.stream(BusinessStatusEnum.values())
 | 
			
		||||
            .filter(statusEnum -> statusEnum.getStatus().equals(status))
 | 
			
		||||
            .findFirst()
 | 
			
		||||
            .map(BusinessStatusEnum::getDesc)
 | 
			
		||||
            .orElse(StrUtil.EMPTY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -211,5 +148,5 @@ public enum BusinessStatusEnum {
 | 
			
		||||
            throw new ServiceException("流程状态为空!");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import lombok.Getter;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 设备类型
 | 
			
		||||
 * 针对一套 用户体系
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@@ -28,12 +29,9 @@ public enum DeviceType {
 | 
			
		||||
    XCX("xcx"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 第三方社交登录平台
 | 
			
		||||
     * social第三方端
 | 
			
		||||
     */
 | 
			
		||||
    SOCIAL("social");
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设备标识
 | 
			
		||||
     */
 | 
			
		||||
    private final String device;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,146 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.enums;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 日期格式
 | 
			
		||||
 * "yyyy":4位数的年份,例如:2023年表示为"2023"。
 | 
			
		||||
 * "yy":2位数的年份,例如:2023年表示为"23"。
 | 
			
		||||
 * "MM":2位数的月份,取值范围为01到12,例如:7月表示为"07"。
 | 
			
		||||
 * "M":不带前导零的月份,取值范围为1到12,例如:7月表示为"7"。
 | 
			
		||||
 * "dd":2位数的日期,取值范围为01到31,例如:22日表示为"22"。
 | 
			
		||||
 * "d":不带前导零的日期,取值范围为1到31,例如:22日表示为"22"。
 | 
			
		||||
 * "EEEE":星期的全名,例如:星期三表示为"Wednesday"。
 | 
			
		||||
 * "E":星期的缩写,例如:星期三表示为"Wed"。
 | 
			
		||||
 * "DDD" 或 "D":一年中的第几天,取值范围为001到366,例如:第200天表示为"200"。
 | 
			
		||||
 * 时间格式
 | 
			
		||||
 * "HH":24小时制的小时数,取值范围为00到23,例如:下午5点表示为"17"。
 | 
			
		||||
 * "hh":12小时制的小时数,取值范围为01到12,例如:下午5点表示为"05"。
 | 
			
		||||
 * "mm":分钟数,取值范围为00到59,例如:30分钟表示为"30"。
 | 
			
		||||
 * "ss":秒数,取值范围为00到59,例如:45秒表示为"45"。
 | 
			
		||||
 * "SSS":毫秒数,取值范围为000到999,例如:123毫秒表示为"123"。
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 日期格式与时间格式枚举
 | 
			
		||||
 */
 | 
			
		||||
@Getter
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public enum FormatsType {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如:2023年表示为"23"
 | 
			
		||||
     */
 | 
			
		||||
    YY("yy"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如:2023年表示为"2023"
 | 
			
		||||
     */
 | 
			
		||||
    YYYY("yyyy"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例例如,2023年7月可以表示为 "2023-07"
 | 
			
		||||
     */
 | 
			
		||||
    YYYY_MM("yyyy-MM"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,日期 "2023年7月22日" 可以表示为 "2023-07-22"
 | 
			
		||||
     */
 | 
			
		||||
    YYYY_MM_DD("yyyy-MM-dd"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023-07-22 15:30"
 | 
			
		||||
     */
 | 
			
		||||
    YYYY_MM_DD_HH_MM("yyyy-MM-dd HH:mm"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023-07-22 15:30:45"
 | 
			
		||||
     */
 | 
			
		||||
    YYYY_MM_DD_HH_MM_SS("yyyy-MM-dd HH:mm:ss"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如:下午3点30分45秒,表示为 "15:30:45"
 | 
			
		||||
     */
 | 
			
		||||
    HH_MM_SS("HH:mm:ss"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例例如,2023年7月可以表示为 "2023/07"
 | 
			
		||||
     */
 | 
			
		||||
    YYYY_MM_SLASH("yyyy/MM"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,日期 "2023年7月22日" 可以表示为 "2023/07/22"
 | 
			
		||||
     */
 | 
			
		||||
    YYYY_MM_DD_SLASH("yyyy/MM/dd"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45"
 | 
			
		||||
     */
 | 
			
		||||
    YYYY_MM_DD_HH_MM_SLASH("yyyy/MM/dd HH:mm"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45"
 | 
			
		||||
     */
 | 
			
		||||
    YYYY_MM_DD_HH_MM_SS_SLASH("yyyy/MM/dd HH:mm:ss"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例例如,2023年7月可以表示为 "2023.07"
 | 
			
		||||
     */
 | 
			
		||||
    YYYY_MM_DOT("yyyy.MM"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,日期 "2023年7月22日" 可以表示为 "2023.07.22"
 | 
			
		||||
     */
 | 
			
		||||
    YYYY_MM_DD_DOT("yyyy.MM.dd"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023.07.22 15:30"
 | 
			
		||||
     */
 | 
			
		||||
    YYYY_MM_DD_HH_MM_DOT("yyyy.MM.dd HH:mm"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023.07.22 15:30:45"
 | 
			
		||||
     */
 | 
			
		||||
    YYYY_MM_DD_HH_MM_SS_DOT("yyyy.MM.dd HH:mm:ss"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,2023年7月可以表示为 "202307"
 | 
			
		||||
     */
 | 
			
		||||
    YYYYMM("yyyyMM"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,2023年7月22日可以表示为 "20230722"
 | 
			
		||||
     */
 | 
			
		||||
    YYYYMMDD("yyyyMMdd"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,2023年7月22日下午3点可以表示为 "2023072215"
 | 
			
		||||
     */
 | 
			
		||||
    YYYYMMDDHH("yyyyMMddHH"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,2023年7月22日下午3点30分可以表示为 "202307221530"
 | 
			
		||||
     */
 | 
			
		||||
    YYYYMMDDHHMM("yyyyMMddHHmm"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 例如,2023年7月22日下午3点30分45秒可以表示为 "20230722153045"
 | 
			
		||||
     */
 | 
			
		||||
    YYYYMMDDHHMMSS("yyyyMMddHHmmss");
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 时间格式
 | 
			
		||||
     */
 | 
			
		||||
    private final String timeFormat;
 | 
			
		||||
 | 
			
		||||
    public static FormatsType getFormatsType(String str) {
 | 
			
		||||
        for (FormatsType value : values()) {
 | 
			
		||||
            if (StringUtils.contains(str, value.getTimeFormat())) {
 | 
			
		||||
                return value;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        throw new RuntimeException("'FormatsType' not found By " + str);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,30 @@
 | 
			
		||||
package org.dromara.common.core.enums;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 用户状态
 | 
			
		||||
 *
 | 
			
		||||
 * @author LionLi
 | 
			
		||||
 */
 | 
			
		||||
@Getter
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public enum TenantStatus {
 | 
			
		||||
    /**
 | 
			
		||||
     * 正常
 | 
			
		||||
     */
 | 
			
		||||
    OK("0", "正常"),
 | 
			
		||||
    /**
 | 
			
		||||
     * 停用
 | 
			
		||||
     */
 | 
			
		||||
    DISABLE("1", "停用"),
 | 
			
		||||
    /**
 | 
			
		||||
     * 删除
 | 
			
		||||
     */
 | 
			
		||||
    DELETED("2", "删除");
 | 
			
		||||
 | 
			
		||||
    private final String code;
 | 
			
		||||
    private final String info;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,11 +1,12 @@
 | 
			
		||||
package org.dromara.common.core.enums;
 | 
			
		||||
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 用户类型
 | 
			
		||||
 * 设备类型
 | 
			
		||||
 * 针对多套 用户体系
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@@ -14,18 +15,15 @@ import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
public enum UserType {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 后台系统用户
 | 
			
		||||
     * pc端
 | 
			
		||||
     */
 | 
			
		||||
    SYS_USER("sys_user"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 移动客户端用户
 | 
			
		||||
     * app端
 | 
			
		||||
     */
 | 
			
		||||
    APP_USER("app_user");
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户类型标识(用于 token、权限识别等)
 | 
			
		||||
     */
 | 
			
		||||
    private final String userType;
 | 
			
		||||
 | 
			
		||||
    public static UserType getUserType(String str) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,62 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.exception;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * sse 特制异常
 | 
			
		||||
 *
 | 
			
		||||
 * @author LionLi
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public final class SseException extends RuntimeException {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 错误码
 | 
			
		||||
     */
 | 
			
		||||
    private Integer code;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 错误提示
 | 
			
		||||
     */
 | 
			
		||||
    private String message;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 错误明细,内部调试错误
 | 
			
		||||
     */
 | 
			
		||||
    private String detailMessage;
 | 
			
		||||
 | 
			
		||||
    public SseException(String message) {
 | 
			
		||||
        this.message = message;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SseException(String message, Integer code) {
 | 
			
		||||
        this.message = message;
 | 
			
		||||
        this.code = code;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getMessage() {
 | 
			
		||||
        return message;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SseException setMessage(String message) {
 | 
			
		||||
        this.message = message;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SseException setDetailMessage(String detailMessage) {
 | 
			
		||||
        this.detailMessage = detailMessage;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
package org.dromara.common.core.service;
 | 
			
		||||
 | 
			
		||||
import org.dromara.common.core.domain.dto.DeptDTO;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 通用 部门服务
 | 
			
		||||
 *
 | 
			
		||||
@@ -19,19 +15,4 @@ public interface DeptService {
 | 
			
		||||
     */
 | 
			
		||||
    String selectDeptNameByIds(String deptIds);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据部门ID查询部门负责人
 | 
			
		||||
     *
 | 
			
		||||
     * @param deptId 部门ID,用于指定需要查询的部门
 | 
			
		||||
     * @return 返回该部门的负责人ID
 | 
			
		||||
     */
 | 
			
		||||
    Long selectDeptLeaderById(Long deptId);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询部门
 | 
			
		||||
     *
 | 
			
		||||
     * @return 部门列表
 | 
			
		||||
     */
 | 
			
		||||
    List<DeptDTO> selectDeptsByList();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
package org.dromara.common.core.service;
 | 
			
		||||
 | 
			
		||||
import org.dromara.common.core.domain.dto.DictDataDTO;
 | 
			
		||||
import org.dromara.common.core.domain.dto.DictTypeDTO;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -68,20 +64,4 @@ public interface DictService {
 | 
			
		||||
     */
 | 
			
		||||
    Map<String, String> getAllDictByDictType(String dictType);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据字典类型查询详细信息
 | 
			
		||||
     *
 | 
			
		||||
     * @param dictType 字典类型
 | 
			
		||||
     * @return 字典类型详细信息
 | 
			
		||||
     */
 | 
			
		||||
    DictTypeDTO getDictType(String dictType);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据字典类型查询字典数据列表
 | 
			
		||||
     *
 | 
			
		||||
     * @param dictType 字典类型
 | 
			
		||||
     * @return 字典数据列表
 | 
			
		||||
     */
 | 
			
		||||
    List<DictDataDTO> getDictData(String dictType);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.service;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 通用 岗位服务
 | 
			
		||||
 *
 | 
			
		||||
 * @author AprilWind
 | 
			
		||||
 */
 | 
			
		||||
public interface PostService {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.service;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 通用 角色服务
 | 
			
		||||
 *
 | 
			
		||||
 * @author AprilWind
 | 
			
		||||
 */
 | 
			
		||||
public interface RoleService {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,45 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.service;
 | 
			
		||||
 | 
			
		||||
import org.dromara.common.core.domain.dto.TaskAssigneeDTO;
 | 
			
		||||
import org.dromara.common.core.domain.model.TaskAssigneeBody;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 工作流设计器获取任务执行人
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
public interface TaskAssigneeService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询角色并返回任务指派的列表,支持分页
 | 
			
		||||
     *
 | 
			
		||||
     * @param taskQuery 查询条件
 | 
			
		||||
     * @return 办理人
 | 
			
		||||
     */
 | 
			
		||||
    TaskAssigneeDTO selectRolesByTaskAssigneeList(TaskAssigneeBody taskQuery);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询岗位并返回任务指派的列表,支持分页
 | 
			
		||||
     *
 | 
			
		||||
     * @param taskQuery 查询条件
 | 
			
		||||
     * @return 办理人
 | 
			
		||||
     */
 | 
			
		||||
    TaskAssigneeDTO selectPostsByTaskAssigneeList(TaskAssigneeBody taskQuery);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询部门并返回任务指派的列表,支持分页
 | 
			
		||||
     *
 | 
			
		||||
     * @param taskQuery 查询条件
 | 
			
		||||
     * @return 办理人
 | 
			
		||||
     */
 | 
			
		||||
    TaskAssigneeDTO selectDeptsByTaskAssigneeList(TaskAssigneeBody taskQuery);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询用户并返回任务指派的列表,支持分页
 | 
			
		||||
     *
 | 
			
		||||
     * @param taskQuery 查询条件
 | 
			
		||||
     * @return 办理人
 | 
			
		||||
     */
 | 
			
		||||
    TaskAssigneeDTO selectUsersByTaskAssigneeList(TaskAssigneeBody taskQuery);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -3,7 +3,6 @@ package org.dromara.common.core.service;
 | 
			
		||||
import org.dromara.common.core.domain.dto.UserDTO;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 通用 用户服务
 | 
			
		||||
@@ -83,45 +82,4 @@ public interface UserService {
 | 
			
		||||
     * @return 用户
 | 
			
		||||
     */
 | 
			
		||||
    List<UserDTO> selectUsersByDeptIds(List<Long> deptIds);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 通过岗位ID查询用户
 | 
			
		||||
     *
 | 
			
		||||
     * @param postIds 岗位ids
 | 
			
		||||
     * @return 用户
 | 
			
		||||
     */
 | 
			
		||||
    List<UserDTO> selectUsersByPostIds(List<Long> postIds);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据用户 ID 列表查询用户名称映射关系
 | 
			
		||||
     *
 | 
			
		||||
     * @param userIds 用户 ID 列表
 | 
			
		||||
     * @return Map,其中 key 为用户 ID,value 为对应的用户名称
 | 
			
		||||
     */
 | 
			
		||||
    Map<Long, String> selectUserNamesByIds(List<Long> userIds);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据角色 ID 列表查询角色名称映射关系
 | 
			
		||||
     *
 | 
			
		||||
     * @param roleIds 角色 ID 列表
 | 
			
		||||
     * @return Map,其中 key 为角色 ID,value 为对应的角色名称
 | 
			
		||||
     */
 | 
			
		||||
    Map<Long, String> selectRoleNamesByIds(List<Long> roleIds);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据部门 ID 列表查询部门名称映射关系
 | 
			
		||||
     *
 | 
			
		||||
     * @param deptIds 部门 ID 列表
 | 
			
		||||
     * @return Map,其中 key 为部门 ID,value 为对应的部门名称
 | 
			
		||||
     */
 | 
			
		||||
    Map<Long, String> selectDeptNamesByIds(List<Long> deptIds);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据岗位 ID 列表查询岗位名称映射关系
 | 
			
		||||
     *
 | 
			
		||||
     * @param postIds 岗位 ID 列表
 | 
			
		||||
     * @return Map,其中 key 为岗位 ID,value 为对应的岗位名称
 | 
			
		||||
     */
 | 
			
		||||
    Map<Long, String> selectPostNamesByIds(List<Long> postIds);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
package org.dromara.common.core.service;
 | 
			
		||||
 | 
			
		||||
import org.dromara.common.core.domain.dto.CompleteTaskDTO;
 | 
			
		||||
import org.dromara.common.core.domain.dto.StartProcessDTO;
 | 
			
		||||
import org.dromara.common.core.domain.dto.StartProcessReturnDTO;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
@@ -17,79 +13,64 @@ public interface WorkflowService {
 | 
			
		||||
    /**
 | 
			
		||||
     * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息
 | 
			
		||||
     *
 | 
			
		||||
     * @param businessIds 业务id
 | 
			
		||||
     * @param businessKeys 业务id
 | 
			
		||||
     * @return 结果
 | 
			
		||||
     */
 | 
			
		||||
    boolean deleteInstance(List<Long> businessIds);
 | 
			
		||||
    boolean deleteRunAndHisInstance(List<String> businessKeys);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前流程状态
 | 
			
		||||
     *
 | 
			
		||||
     * @param taskId 任务id
 | 
			
		||||
     * @return 状态
 | 
			
		||||
     */
 | 
			
		||||
    String getBusinessStatusByTaskId(Long taskId);
 | 
			
		||||
    String getBusinessStatusByTaskId(String taskId);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前流程状态
 | 
			
		||||
     *
 | 
			
		||||
     * @param businessId 业务id
 | 
			
		||||
     * @return 状态
 | 
			
		||||
     * @param businessKey 业务id
 | 
			
		||||
     */
 | 
			
		||||
    String getBusinessStatus(String businessId);
 | 
			
		||||
    String getBusinessStatus(String businessKey);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置流程变量
 | 
			
		||||
     * 设置流程变量(全局变量)
 | 
			
		||||
     *
 | 
			
		||||
     * @param instanceId 流程实例id
 | 
			
		||||
     * @param variable   流程变量
 | 
			
		||||
     * @param taskId       任务id
 | 
			
		||||
     * @param variableName 变量名称
 | 
			
		||||
     * @param value        变量值
 | 
			
		||||
     */
 | 
			
		||||
    void setVariable(Long instanceId, Map<String, Object> variable);
 | 
			
		||||
    void setVariable(String taskId, String variableName, Object value);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取流程变量
 | 
			
		||||
     * 设置流程变量(全局变量)
 | 
			
		||||
     *
 | 
			
		||||
     * @param instanceId 流程实例id
 | 
			
		||||
     * @param taskId    任务id
 | 
			
		||||
     * @param variables 流程变量
 | 
			
		||||
     */
 | 
			
		||||
    Map<String, Object> instanceVariable(Long instanceId);
 | 
			
		||||
    void setVariables(String taskId, Map<String, Object> variables);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置流程变量(本地变量,非全局变量)
 | 
			
		||||
     *
 | 
			
		||||
     * @param taskId       任务id
 | 
			
		||||
     * @param variableName 变量名称
 | 
			
		||||
     * @param value        变量值
 | 
			
		||||
     */
 | 
			
		||||
    void setVariableLocal(String taskId, String variableName, Object value);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置流程变量(本地变量,非全局变量)
 | 
			
		||||
     *
 | 
			
		||||
     * @param taskId    任务id
 | 
			
		||||
     * @param variables 流程变量
 | 
			
		||||
     */
 | 
			
		||||
    void setVariablesLocal(String taskId, Map<String, Object> variables);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 按照业务id查询流程实例id
 | 
			
		||||
     *
 | 
			
		||||
     * @param businessId 业务id
 | 
			
		||||
     * @param businessKey 业务id
 | 
			
		||||
     * @return 结果
 | 
			
		||||
     */
 | 
			
		||||
    Long getInstanceIdByBusinessId(String businessId);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 新增租户流程定义
 | 
			
		||||
     *
 | 
			
		||||
     * @param tenantId 租户id
 | 
			
		||||
     */
 | 
			
		||||
    void syncDef(String tenantId);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 启动流程
 | 
			
		||||
     *
 | 
			
		||||
     * @param startProcess 参数
 | 
			
		||||
     * @return 结果
 | 
			
		||||
     */
 | 
			
		||||
    StartProcessReturnDTO startWorkFlow(StartProcessDTO startProcess);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 办理任务
 | 
			
		||||
     * 系统后台发起审批 无用户信息 需要忽略权限
 | 
			
		||||
     * completeTask.getVariables().put("ignore", true);
 | 
			
		||||
     *
 | 
			
		||||
     * @param completeTask 参数
 | 
			
		||||
     */
 | 
			
		||||
    boolean completeTask(CompleteTaskDTO completeTask);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 办理任务
 | 
			
		||||
     *
 | 
			
		||||
     * @param taskId  任务ID
 | 
			
		||||
     * @param message 办理意见
 | 
			
		||||
     */
 | 
			
		||||
    boolean completeTask(Long taskId, String message);
 | 
			
		||||
    String getInstanceIdByBusinessKey(String businessKey);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,157 +1,106 @@
 | 
			
		||||
package org.dromara.common.core.utils;
 | 
			
		||||
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.apache.commons.lang3.time.DateFormatUtils;
 | 
			
		||||
import org.dromara.common.core.enums.FormatsType;
 | 
			
		||||
import org.dromara.common.core.exception.ServiceException;
 | 
			
		||||
 | 
			
		||||
import java.lang.management.ManagementFactory;
 | 
			
		||||
import java.text.ParseException;
 | 
			
		||||
import java.text.SimpleDateFormat;
 | 
			
		||||
import java.time.*;
 | 
			
		||||
import java.time.LocalDate;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.time.LocalTime;
 | 
			
		||||
import java.time.ZoneId;
 | 
			
		||||
import java.time.ZonedDateTime;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 时间工具类
 | 
			
		||||
 *
 | 
			
		||||
 * @author ruoyi
 | 
			
		||||
 */
 | 
			
		||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
			
		||||
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
 | 
			
		||||
 | 
			
		||||
    public static final String YYYY = "yyyy";
 | 
			
		||||
 | 
			
		||||
    public static final String YYYY_MM = "yyyy-MM";
 | 
			
		||||
 | 
			
		||||
    public static final String YYYY_MM_DD = "yyyy-MM-dd";
 | 
			
		||||
 | 
			
		||||
    public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
 | 
			
		||||
 | 
			
		||||
    public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
 | 
			
		||||
 | 
			
		||||
    private static final String[] PARSE_PATTERNS = {
 | 
			
		||||
        "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
 | 
			
		||||
        "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
 | 
			
		||||
        "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
 | 
			
		||||
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    private DateUtils() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前日期和时间
 | 
			
		||||
     * 获取当前Date型日期
 | 
			
		||||
     *
 | 
			
		||||
     * @return 当前日期和时间的 Date 对象表示
 | 
			
		||||
     * @return Date() 当前日期
 | 
			
		||||
     */
 | 
			
		||||
    public static Date getNowDate() {
 | 
			
		||||
        return new Date();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前日期的字符串表示,格式为YYYY-MM-DD
 | 
			
		||||
     * 获取当前日期, 默认格式为yyyy-MM-dd
 | 
			
		||||
     *
 | 
			
		||||
     * @return 当前日期的字符串表示
 | 
			
		||||
     * @return String
 | 
			
		||||
     */
 | 
			
		||||
    public static String getDate() {
 | 
			
		||||
        return dateTimeNow(FormatsType.YYYY_MM_DD);
 | 
			
		||||
        return dateTimeNow(YYYY_MM_DD);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前日期的字符串表示,格式为yyyyMMdd
 | 
			
		||||
     *
 | 
			
		||||
     * @return 当前日期的字符串表示
 | 
			
		||||
     */
 | 
			
		||||
    public static String getCurrentDate() {
 | 
			
		||||
        return DateFormatUtils.format(new Date(), FormatsType.YYYYMMDD.getTimeFormat());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前日期的路径格式字符串,格式为"yyyy/MM/dd"
 | 
			
		||||
     *
 | 
			
		||||
     * @return 当前日期的路径格式字符串
 | 
			
		||||
     */
 | 
			
		||||
    public static String datePath() {
 | 
			
		||||
        Date now = new Date();
 | 
			
		||||
        return DateFormatUtils.format(now, FormatsType.YYYY_MM_DD_SLASH.getTimeFormat());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前时间的字符串表示,格式为YYYY-MM-DD HH:MM:SS
 | 
			
		||||
     *
 | 
			
		||||
     * @return 当前时间的字符串表示
 | 
			
		||||
     */
 | 
			
		||||
    public static String getTime() {
 | 
			
		||||
        return dateTimeNow(FormatsType.YYYY_MM_DD_HH_MM_SS);
 | 
			
		||||
        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前时间的字符串表示,格式为 "HH:MM:SS"
 | 
			
		||||
     *
 | 
			
		||||
     * @return 当前时间的字符串表示,格式为 "HH:MM:SS"
 | 
			
		||||
     */
 | 
			
		||||
    public static String getTimeWithHourMinuteSecond() {
 | 
			
		||||
        return dateTimeNow(FormatsType.HH_MM_SS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前日期和时间的字符串表示,格式为YYYYMMDDHHMMSS
 | 
			
		||||
     *
 | 
			
		||||
     * @return 当前日期和时间的字符串表示
 | 
			
		||||
     */
 | 
			
		||||
    public static String dateTimeNow() {
 | 
			
		||||
        return dateTimeNow(FormatsType.YYYYMMDDHHMMSS);
 | 
			
		||||
        return dateTimeNow(YYYYMMDDHHMMSS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前日期和时间的指定格式的字符串表示
 | 
			
		||||
     *
 | 
			
		||||
     * @param format 日期时间格式,例如"YYYY-MM-DD HH:MM:SS"
 | 
			
		||||
     * @return 当前日期和时间的字符串表示
 | 
			
		||||
     */
 | 
			
		||||
    public static String dateTimeNow(final FormatsType format) {
 | 
			
		||||
    public static String dateTimeNow(final String format) {
 | 
			
		||||
        return parseDateToStr(format, new Date());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将指定日期格式化为 YYYY-MM-DD 格式的字符串
 | 
			
		||||
     *
 | 
			
		||||
     * @param date 要格式化的日期对象
 | 
			
		||||
     * @return 格式化后的日期字符串
 | 
			
		||||
     */
 | 
			
		||||
    public static String formatDate(final Date date) {
 | 
			
		||||
        return parseDateToStr(FormatsType.YYYY_MM_DD, date);
 | 
			
		||||
    public static String dateTime(final Date date) {
 | 
			
		||||
        return parseDateToStr(YYYY_MM_DD, date);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将指定日期格式化为 YYYY-MM-DD HH:MM:SS 格式的字符串
 | 
			
		||||
     *
 | 
			
		||||
     * @param date 要格式化的日期对象
 | 
			
		||||
     * @return 格式化后的日期时间字符串
 | 
			
		||||
     */
 | 
			
		||||
    public static String formatDateTime(final Date date) {
 | 
			
		||||
        return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, date);
 | 
			
		||||
    public static String parseDateToStr(final String format, final Date date) {
 | 
			
		||||
        return new SimpleDateFormat(format).format(date);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将指定日期按照指定格式进行格式化
 | 
			
		||||
     *
 | 
			
		||||
     * @param format 要使用的日期时间格式,例如"YYYY-MM-DD HH:MM:SS"
 | 
			
		||||
     * @param date   要格式化的日期对象
 | 
			
		||||
     * @return 格式化后的日期时间字符串
 | 
			
		||||
     */
 | 
			
		||||
    public static String parseDateToStr(final FormatsType format, final Date date) {
 | 
			
		||||
        return new SimpleDateFormat(format.getTimeFormat()).format(date);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将指定格式的日期时间字符串转换为 Date 对象
 | 
			
		||||
     *
 | 
			
		||||
     * @param format 要解析的日期时间格式,例如"YYYY-MM-DD HH:MM:SS"
 | 
			
		||||
     * @param ts     要解析的日期时间字符串
 | 
			
		||||
     * @return 解析后的 Date 对象
 | 
			
		||||
     * @throws RuntimeException 如果解析过程中发生异常
 | 
			
		||||
     */
 | 
			
		||||
    public static Date parseDateTime(final FormatsType format, final String ts) {
 | 
			
		||||
    public static Date dateTime(final String format, final String ts) {
 | 
			
		||||
        try {
 | 
			
		||||
            return new SimpleDateFormat(format.getTimeFormat()).parse(ts);
 | 
			
		||||
            return new SimpleDateFormat(format).parse(ts);
 | 
			
		||||
        } catch (ParseException e) {
 | 
			
		||||
            throw new RuntimeException(e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将对象转换为日期对象
 | 
			
		||||
     *
 | 
			
		||||
     * @param str 要转换的对象,通常是字符串
 | 
			
		||||
     * @return 转换后的日期对象,如果转换失败或输入为null,则返回null
 | 
			
		||||
     * 日期路径 即年/月/日 如2018/08/08
 | 
			
		||||
     */
 | 
			
		||||
    public static String datePath() {
 | 
			
		||||
        Date now = new Date();
 | 
			
		||||
        return DateFormatUtils.format(now, "yyyy/MM/dd");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 日期路径 即年/月/日 如20180808
 | 
			
		||||
     */
 | 
			
		||||
    public static String dateTime() {
 | 
			
		||||
        Date now = new Date();
 | 
			
		||||
        return DateFormatUtils.format(now, "yyyyMMdd");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 日期型字符串转化为日期 格式
 | 
			
		||||
     */
 | 
			
		||||
    public static Date parseDate(Object str) {
 | 
			
		||||
        if (str == null) {
 | 
			
		||||
@@ -166,8 +115,6 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取服务器启动时间
 | 
			
		||||
     *
 | 
			
		||||
     * @return 服务器启动时间的 Date 对象表示
 | 
			
		||||
     */
 | 
			
		||||
    public static Date getServerStartDate() {
 | 
			
		||||
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
 | 
			
		||||
@@ -175,79 +122,35 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 计算两个时间之间的时间差,并以指定单位返回(绝对值)
 | 
			
		||||
     *
 | 
			
		||||
     * @param start 起始时间
 | 
			
		||||
     * @param end   结束时间
 | 
			
		||||
     * @param unit  所需返回的时间单位(DAYS、HOURS、MINUTES、SECONDS、MILLISECONDS、MICROSECONDS、NANOSECONDS)
 | 
			
		||||
     * @return 时间差的绝对值,以指定单位表示
 | 
			
		||||
     * 计算相差天数
 | 
			
		||||
     */
 | 
			
		||||
    public static long difference(Date start, Date end, TimeUnit unit) {
 | 
			
		||||
        // 计算时间差,单位为毫秒,取绝对值避免负数
 | 
			
		||||
        long diffInMillis = Math.abs(end.getTime() - start.getTime());
 | 
			
		||||
 | 
			
		||||
        // 根据目标单位转换时间差
 | 
			
		||||
        return switch (unit) {
 | 
			
		||||
            case DAYS -> diffInMillis / TimeUnit.DAYS.toMillis(1);
 | 
			
		||||
            case HOURS -> diffInMillis / TimeUnit.HOURS.toMillis(1);
 | 
			
		||||
            case MINUTES -> diffInMillis / TimeUnit.MINUTES.toMillis(1);
 | 
			
		||||
            case SECONDS -> diffInMillis / TimeUnit.SECONDS.toMillis(1);
 | 
			
		||||
            case MILLISECONDS -> diffInMillis;
 | 
			
		||||
            case MICROSECONDS -> TimeUnit.MILLISECONDS.toMicros(diffInMillis);
 | 
			
		||||
            case NANOSECONDS -> TimeUnit.MILLISECONDS.toNanos(diffInMillis);
 | 
			
		||||
        };
 | 
			
		||||
    public static int differentDaysByMillisecond(Date date1, Date date2) {
 | 
			
		||||
        return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 计算两个日期之间的时间差,并以天、小时和分钟的格式返回
 | 
			
		||||
     *
 | 
			
		||||
     * @param endDate 结束日期
 | 
			
		||||
     * @param nowDate 当前日期
 | 
			
		||||
     * @return 表示时间差的字符串,格式为"天 小时 分钟"
 | 
			
		||||
     * 计算两个时间差
 | 
			
		||||
     */
 | 
			
		||||
    public static String getDatePoor(Date endDate, Date nowDate) {
 | 
			
		||||
        long diffInMillis = endDate.getTime() - nowDate.getTime();
 | 
			
		||||
        long day = TimeUnit.MILLISECONDS.toDays(diffInMillis);
 | 
			
		||||
        long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24;
 | 
			
		||||
        long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60;
 | 
			
		||||
        return String.format("%d天 %d小时 %d分钟", day, hour, min);
 | 
			
		||||
        long nd = 1000 * 24 * 60 * 60;
 | 
			
		||||
        long nh = 1000 * 60 * 60;
 | 
			
		||||
        long nm = 1000 * 60;
 | 
			
		||||
        // long ns = 1000;
 | 
			
		||||
        // 获得两个时间的毫秒时间差异
 | 
			
		||||
        long diff = endDate.getTime() - nowDate.getTime();
 | 
			
		||||
        // 计算差多少天
 | 
			
		||||
        long day = diff / nd;
 | 
			
		||||
        // 计算差多少小时
 | 
			
		||||
        long hour = diff % nd / nh;
 | 
			
		||||
        // 计算差多少分钟
 | 
			
		||||
        long min = diff % nd % nh / nm;
 | 
			
		||||
        // 计算差多少秒//输出结果
 | 
			
		||||
        // long sec = diff % nd % nh % nm / ns;
 | 
			
		||||
        return day + "天" + hour + "小时" + min + "分钟";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 计算两个时间点的差值(天、小时、分钟、秒),当值为0时不显示该单位
 | 
			
		||||
     *
 | 
			
		||||
     * @param endDate 结束时间
 | 
			
		||||
     * @param nowDate 当前时间
 | 
			
		||||
     * @return 时间差字符串,格式为 "x天 x小时 x分钟 x秒",若为 0 则不显示
 | 
			
		||||
     */
 | 
			
		||||
    public static String getTimeDifference(Date endDate, Date nowDate) {
 | 
			
		||||
        long diffInMillis = endDate.getTime() - nowDate.getTime();
 | 
			
		||||
        long day = TimeUnit.MILLISECONDS.toDays(diffInMillis);
 | 
			
		||||
        long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24;
 | 
			
		||||
        long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60;
 | 
			
		||||
        long sec = TimeUnit.MILLISECONDS.toSeconds(diffInMillis) % 60;
 | 
			
		||||
        // 构建时间差字符串,条件是值不为0才显示
 | 
			
		||||
        StringBuilder result = new StringBuilder();
 | 
			
		||||
        if (day > 0) {
 | 
			
		||||
            result.append(String.format("%d天 ", day));
 | 
			
		||||
        }
 | 
			
		||||
        if (hour > 0) {
 | 
			
		||||
            result.append(String.format("%d小时 ", hour));
 | 
			
		||||
        }
 | 
			
		||||
        if (min > 0) {
 | 
			
		||||
            result.append(String.format("%d分钟 ", min));
 | 
			
		||||
        }
 | 
			
		||||
        if (sec > 0) {
 | 
			
		||||
            result.append(String.format("%d秒", sec));
 | 
			
		||||
        }
 | 
			
		||||
        return result.length() > 0 ? result.toString().trim() : "0秒";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将 LocalDateTime 对象转换为 Date 对象
 | 
			
		||||
     *
 | 
			
		||||
     * @param temporalAccessor 要转换的 LocalDateTime 对象
 | 
			
		||||
     * @return 转换后的 Date 对象
 | 
			
		||||
     * 增加 LocalDateTime ==> Date
 | 
			
		||||
     */
 | 
			
		||||
    public static Date toDate(LocalDateTime temporalAccessor) {
 | 
			
		||||
        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
 | 
			
		||||
@@ -255,46 +158,11 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将 LocalDate 对象转换为 Date 对象
 | 
			
		||||
     *
 | 
			
		||||
     * @param temporalAccessor 要转换的 LocalDate 对象
 | 
			
		||||
     * @return 转换后的 Date 对象
 | 
			
		||||
     * 增加 LocalDate ==> Date
 | 
			
		||||
     */
 | 
			
		||||
    public static Date toDate(LocalDate temporalAccessor) {
 | 
			
		||||
        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
 | 
			
		||||
        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
 | 
			
		||||
        return Date.from(zdt.toInstant());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 校验日期范围
 | 
			
		||||
     *
 | 
			
		||||
     * @param startDate 开始日期
 | 
			
		||||
     * @param endDate   结束日期
 | 
			
		||||
     * @param maxValue  最大时间跨度的限制值
 | 
			
		||||
     * @param unit      时间跨度的单位,可选择 "DAYS"、"HOURS" 或 "MINUTES"
 | 
			
		||||
     */
 | 
			
		||||
    public static void validateDateRange(Date startDate, Date endDate, int maxValue, TimeUnit unit) {
 | 
			
		||||
        // 校验结束日期不能早于开始日期
 | 
			
		||||
        if (endDate.before(startDate)) {
 | 
			
		||||
            throw new ServiceException("结束日期不能早于开始日期");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 计算时间跨度
 | 
			
		||||
        long diffInMillis = endDate.getTime() - startDate.getTime();
 | 
			
		||||
 | 
			
		||||
        // 根据单位转换时间跨度
 | 
			
		||||
        long diff = switch (unit) {
 | 
			
		||||
            case DAYS -> TimeUnit.MILLISECONDS.toDays(diffInMillis);
 | 
			
		||||
            case HOURS -> TimeUnit.MILLISECONDS.toHours(diffInMillis);
 | 
			
		||||
            case MINUTES -> TimeUnit.MILLISECONDS.toMinutes(diffInMillis);
 | 
			
		||||
            default -> throw new IllegalArgumentException("不支持的时间单位");
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // 校验时间跨度不超过最大限制
 | 
			
		||||
        if (diff > maxValue) {
 | 
			
		||||
            throw new ServiceException("最大时间跨度为 " + maxValue + " " + unit.toString().toLowerCase());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,84 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.utils;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.lang.PatternPool;
 | 
			
		||||
import cn.hutool.core.net.NetUtil;
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.dromara.common.core.utils.regex.RegexUtils;
 | 
			
		||||
 | 
			
		||||
import java.net.Inet6Address;
 | 
			
		||||
import java.net.InetAddress;
 | 
			
		||||
import java.net.UnknownHostException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 增强网络相关工具类
 | 
			
		||||
 *
 | 
			
		||||
 * @author 秋辞未寒
 | 
			
		||||
 */
 | 
			
		||||
@Slf4j
 | 
			
		||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
			
		||||
public class NetUtils extends NetUtil {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 判断是否为IPv6地址
 | 
			
		||||
     *
 | 
			
		||||
     * @param ip IP地址
 | 
			
		||||
     * @return 是否为IPv6地址
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isIPv6(String ip) {
 | 
			
		||||
        try {
 | 
			
		||||
            // 判断是否为IPv6地址
 | 
			
		||||
            return InetAddress.getByName(ip) instanceof Inet6Address;
 | 
			
		||||
        } catch (UnknownHostException e) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 判断IPv6地址是否为内网地址
 | 
			
		||||
     * <br><br>
 | 
			
		||||
     * 以下地址将归类为本地地址,如有业务场景有需要,请根据需求自行处理:
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * 通配符地址 0:0:0:0:0:0:0:0
 | 
			
		||||
     * 链路本地地址 fe80::/10
 | 
			
		||||
     * 唯一本地地址 fec0::/10
 | 
			
		||||
     * 环回地址 ::1
 | 
			
		||||
     * </pre>
 | 
			
		||||
     *
 | 
			
		||||
     * @param ip IP地址
 | 
			
		||||
     * @return 是否为内网地址
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isInnerIPv6(String ip) {
 | 
			
		||||
        try {
 | 
			
		||||
            // 判断是否为IPv6地址
 | 
			
		||||
            if (InetAddress.getByName(ip) instanceof Inet6Address inet6Address) {
 | 
			
		||||
                // isAnyLocalAddress 判断是否为通配符地址,通常不会将其视为内网地址,根据业务场景自行处理判断
 | 
			
		||||
                // isLinkLocalAddress 判断是否为链路本地地址,通常不算内网地址,是否划分归属于内网需要根据业务场景自行处理判断
 | 
			
		||||
                // isLoopbackAddress 判断是否为环回地址,与IPv4的 127.0.0.1 同理,用于表示本机
 | 
			
		||||
                // isSiteLocalAddress 判断是否为本地站点地址,IPv6唯一本地地址(Unique Local Addresses,简称ULA)
 | 
			
		||||
                if (inet6Address.isAnyLocalAddress()
 | 
			
		||||
                    || inet6Address.isLinkLocalAddress()
 | 
			
		||||
                    || inet6Address.isLoopbackAddress()
 | 
			
		||||
                    || inet6Address.isSiteLocalAddress()) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (UnknownHostException e) {
 | 
			
		||||
            // 注意,isInnerIPv6方法和isIPv6方法的适用范围不同,所以此处不能忽略其异常信息。
 | 
			
		||||
            throw new IllegalArgumentException("Invalid IPv6 address!", e);
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 判断是否为IPv4地址
 | 
			
		||||
     *
 | 
			
		||||
     * @param ip IP地址
 | 
			
		||||
     * @return 是否为IPv4地址
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isIPv4(String ip) {
 | 
			
		||||
        return RegexUtils.isMatch(PatternPool.IPV4, ip);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,60 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.utils;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 对象工具类
 | 
			
		||||
 *
 | 
			
		||||
 * @author 秋辞未寒
 | 
			
		||||
 */
 | 
			
		||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
			
		||||
public class ObjectUtils extends ObjectUtil {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 如果对象不为空,则获取对象中的某个字段 ObjectUtils.notNullGetter(user, User::getName);
 | 
			
		||||
     *
 | 
			
		||||
     * @param obj 对象
 | 
			
		||||
     * @param func 获取方法
 | 
			
		||||
     * @return 对象字段
 | 
			
		||||
     */
 | 
			
		||||
    public static <T, E> E notNullGetter(T obj, Function<T, E> func) {
 | 
			
		||||
        if (isNotNull(obj) && isNotNull(func)) {
 | 
			
		||||
            return func.apply(obj);
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 如果对象不为空,则获取对象中的某个字段,否则返回默认值
 | 
			
		||||
     *
 | 
			
		||||
     * @param obj          对象
 | 
			
		||||
     * @param func         获取方法
 | 
			
		||||
     * @param defaultValue 默认值
 | 
			
		||||
     * @return 对象字段
 | 
			
		||||
     */
 | 
			
		||||
    public static <T, E> E notNullGetter(T obj, Function<T, E> func, E defaultValue) {
 | 
			
		||||
        if (isNotNull(obj) && isNotNull(func)) {
 | 
			
		||||
            return func.apply(obj);
 | 
			
		||||
        }
 | 
			
		||||
        return defaultValue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 如果值不为空,则返回值,否则返回默认值
 | 
			
		||||
     *
 | 
			
		||||
     * @param obj          对象
 | 
			
		||||
     * @param defaultValue 默认值
 | 
			
		||||
     * @return 对象字段
 | 
			
		||||
     */
 | 
			
		||||
    public static <T> T notNull(T obj, T defaultValue) {
 | 
			
		||||
        if (isNotNull(obj)) {
 | 
			
		||||
            return obj;
 | 
			
		||||
        }
 | 
			
		||||
        return defaultValue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -25,7 +25,7 @@ import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 客户端工具类,提供获取请求参数、响应处理、头部信息等常用操作
 | 
			
		||||
 * 客户端工具类
 | 
			
		||||
 *
 | 
			
		||||
 * @author ruoyi
 | 
			
		||||
 */
 | 
			
		||||
@@ -33,73 +33,52 @@ import java.util.Map;
 | 
			
		||||
public class ServletUtils extends JakartaServletUtil {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取指定名称的 String 类型的请求参数
 | 
			
		||||
     *
 | 
			
		||||
     * @param name 参数名
 | 
			
		||||
     * @return 参数值
 | 
			
		||||
     * 获取String参数
 | 
			
		||||
     */
 | 
			
		||||
    public static String getParameter(String name) {
 | 
			
		||||
        return getRequest().getParameter(name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取指定名称的 String 类型的请求参数,若参数不存在,则返回默认值
 | 
			
		||||
     *
 | 
			
		||||
     * @param name         参数名
 | 
			
		||||
     * @param defaultValue 默认值
 | 
			
		||||
     * @return 参数值或默认值
 | 
			
		||||
     * 获取String参数
 | 
			
		||||
     */
 | 
			
		||||
    public static String getParameter(String name, String defaultValue) {
 | 
			
		||||
        return Convert.toStr(getRequest().getParameter(name), defaultValue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取指定名称的 Integer 类型的请求参数
 | 
			
		||||
     *
 | 
			
		||||
     * @param name 参数名
 | 
			
		||||
     * @return 参数值
 | 
			
		||||
     * 获取Integer参数
 | 
			
		||||
     */
 | 
			
		||||
    public static Integer getParameterToInt(String name) {
 | 
			
		||||
        return Convert.toInt(getRequest().getParameter(name));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取指定名称的 Integer 类型的请求参数,若参数不存在,则返回默认值
 | 
			
		||||
     *
 | 
			
		||||
     * @param name         参数名
 | 
			
		||||
     * @param defaultValue 默认值
 | 
			
		||||
     * @return 参数值或默认值
 | 
			
		||||
     * 获取Integer参数
 | 
			
		||||
     */
 | 
			
		||||
    public static Integer getParameterToInt(String name, Integer defaultValue) {
 | 
			
		||||
        return Convert.toInt(getRequest().getParameter(name), defaultValue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取指定名称的 Boolean 类型的请求参数
 | 
			
		||||
     *
 | 
			
		||||
     * @param name 参数名
 | 
			
		||||
     * @return 参数值
 | 
			
		||||
     * 获取Boolean参数
 | 
			
		||||
     */
 | 
			
		||||
    public static Boolean getParameterToBool(String name) {
 | 
			
		||||
        return Convert.toBool(getRequest().getParameter(name));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取指定名称的 Boolean 类型的请求参数,若参数不存在,则返回默认值
 | 
			
		||||
     *
 | 
			
		||||
     * @param name         参数名
 | 
			
		||||
     * @param defaultValue 默认值
 | 
			
		||||
     * @return 参数值或默认值
 | 
			
		||||
     * 获取Boolean参数
 | 
			
		||||
     */
 | 
			
		||||
    public static Boolean getParameterToBool(String name, Boolean defaultValue) {
 | 
			
		||||
        return Convert.toBool(getRequest().getParameter(name), defaultValue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取所有请求参数(以 Map 的形式返回)
 | 
			
		||||
     * 获得所有请求参数
 | 
			
		||||
     *
 | 
			
		||||
     * @param request 请求对象{@link ServletRequest}
 | 
			
		||||
     * @return 请求参数的 Map,键为参数名,值为参数值数组
 | 
			
		||||
     * @return Map
 | 
			
		||||
     */
 | 
			
		||||
    public static Map<String, String[]> getParams(ServletRequest request) {
 | 
			
		||||
        final Map<String, String[]> map = request.getParameterMap();
 | 
			
		||||
@@ -107,10 +86,10 @@ public class ServletUtils extends JakartaServletUtil {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取所有请求参数(以 Map 的形式返回,值为字符串形式的拼接)
 | 
			
		||||
     * 获得所有请求参数
 | 
			
		||||
     *
 | 
			
		||||
     * @param request 请求对象{@link ServletRequest}
 | 
			
		||||
     * @return 请求参数的 Map,键为参数名,值为拼接后的字符串
 | 
			
		||||
     * @return Map
 | 
			
		||||
     */
 | 
			
		||||
    public static Map<String, String> getParamMap(ServletRequest request) {
 | 
			
		||||
        Map<String, String> params = new HashMap<>();
 | 
			
		||||
@@ -121,9 +100,7 @@ public class ServletUtils extends JakartaServletUtil {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前 HTTP 请求对象
 | 
			
		||||
     *
 | 
			
		||||
     * @return 当前 HTTP 请求对象
 | 
			
		||||
     * 获取request
 | 
			
		||||
     */
 | 
			
		||||
    public static HttpServletRequest getRequest() {
 | 
			
		||||
        try {
 | 
			
		||||
@@ -134,9 +111,7 @@ public class ServletUtils extends JakartaServletUtil {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前 HTTP 响应对象
 | 
			
		||||
     *
 | 
			
		||||
     * @return 当前 HTTP 响应对象
 | 
			
		||||
     * 获取response
 | 
			
		||||
     */
 | 
			
		||||
    public static HttpServletResponse getResponse() {
 | 
			
		||||
        try {
 | 
			
		||||
@@ -147,25 +122,12 @@ public class ServletUtils extends JakartaServletUtil {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前请求的 HttpSession 对象
 | 
			
		||||
     * <p>
 | 
			
		||||
     * 如果当前请求已经关联了一个会话(即已经存在有效的 session ID),
 | 
			
		||||
     * 则返回该会话对象;如果没有关联会话,则会创建一个新的会话对象并返回。
 | 
			
		||||
     * <p>
 | 
			
		||||
     * HttpSession 用于存储会话级别的数据,如用户登录信息、购物车内容等,
 | 
			
		||||
     * 可以在多个请求之间共享会话数据
 | 
			
		||||
     *
 | 
			
		||||
     * @return 当前请求的 HttpSession 对象
 | 
			
		||||
     * 获取session
 | 
			
		||||
     */
 | 
			
		||||
    public static HttpSession getSession() {
 | 
			
		||||
        return getRequest().getSession();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前请求的请求属性
 | 
			
		||||
     *
 | 
			
		||||
     * @return {@link ServletRequestAttributes} 请求属性对象
 | 
			
		||||
     */
 | 
			
		||||
    public static ServletRequestAttributes getRequestAttributes() {
 | 
			
		||||
        try {
 | 
			
		||||
            RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
 | 
			
		||||
@@ -175,13 +137,6 @@ public class ServletUtils extends JakartaServletUtil {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取指定请求头的值,如果头部为空则返回空字符串
 | 
			
		||||
     *
 | 
			
		||||
     * @param request 请求对象
 | 
			
		||||
     * @param name    头部名称
 | 
			
		||||
     * @return 头部值
 | 
			
		||||
     */
 | 
			
		||||
    public static String getHeader(HttpServletRequest request, String name) {
 | 
			
		||||
        String value = request.getHeader(name);
 | 
			
		||||
        if (StringUtils.isEmpty(value)) {
 | 
			
		||||
@@ -190,12 +145,6 @@ public class ServletUtils extends JakartaServletUtil {
 | 
			
		||||
        return urlDecode(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取所有请求头的 Map,键为头部名称,值为头部值
 | 
			
		||||
     *
 | 
			
		||||
     * @param request 请求对象
 | 
			
		||||
     * @return 请求头的 Map
 | 
			
		||||
     */
 | 
			
		||||
    public static Map<String, String> getHeaders(HttpServletRequest request) {
 | 
			
		||||
        Map<String, String> map = new LinkedCaseInsensitiveMap<>();
 | 
			
		||||
        Enumeration<String> enumeration = request.getHeaderNames();
 | 
			
		||||
@@ -210,7 +159,7 @@ public class ServletUtils extends JakartaServletUtil {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将字符串渲染到客户端(以 JSON 格式返回)
 | 
			
		||||
     * 将字符串渲染到客户端
 | 
			
		||||
     *
 | 
			
		||||
     * @param response 渲染对象
 | 
			
		||||
     * @param string   待渲染的字符串
 | 
			
		||||
@@ -227,47 +176,37 @@ public class ServletUtils extends JakartaServletUtil {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 判断当前请求是否为 Ajax 异步请求
 | 
			
		||||
     * 是否是Ajax异步请求
 | 
			
		||||
     *
 | 
			
		||||
     * @param request 请求对象
 | 
			
		||||
     * @return 是否为 Ajax 请求
 | 
			
		||||
     * @param request
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isAjaxRequest(HttpServletRequest request) {
 | 
			
		||||
 | 
			
		||||
        // 判断 Accept 头部是否包含 application/json
 | 
			
		||||
        String accept = request.getHeader("accept");
 | 
			
		||||
        if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 判断 X-Requested-With 头部是否包含 XMLHttpRequest
 | 
			
		||||
        String xRequestedWith = request.getHeader("X-Requested-With");
 | 
			
		||||
        if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 判断 URI 后缀是否为 .json 或 .xml
 | 
			
		||||
        String uri = request.getRequestURI();
 | 
			
		||||
        if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 判断请求参数 __ajax 是否为 json 或 xml
 | 
			
		||||
        String ajax = request.getParameter("__ajax");
 | 
			
		||||
        return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取客户端 IP 地址
 | 
			
		||||
     *
 | 
			
		||||
     * @return 客户端 IP 地址
 | 
			
		||||
     */
 | 
			
		||||
    public static String getClientIP() {
 | 
			
		||||
        return getClientIP(getRequest());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 对内容进行 URL 编码
 | 
			
		||||
     * 内容编码
 | 
			
		||||
     *
 | 
			
		||||
     * @param str 内容
 | 
			
		||||
     * @return 编码后的内容
 | 
			
		||||
@@ -277,7 +216,7 @@ public class ServletUtils extends JakartaServletUtil {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 对内容进行 URL 解码
 | 
			
		||||
     * 内容解码
 | 
			
		||||
     *
 | 
			
		||||
     * @param str 内容
 | 
			
		||||
     * @return 解码后的内容
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,10 @@ import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.convert.Convert;
 | 
			
		||||
import cn.hutool.core.lang.Validator;
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.springframework.util.AntPathMatcher;
 | 
			
		||||
 | 
			
		||||
import java.nio.charset.Charset;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
@@ -16,16 +17,13 @@ import java.util.stream.Collectors;
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
			
		||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
 | 
			
		||||
 | 
			
		||||
    public static final String SEPARATOR = ",";
 | 
			
		||||
 | 
			
		||||
    public static final String SLASH = "/";
 | 
			
		||||
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    private StringUtils() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取参数不为空值
 | 
			
		||||
     *
 | 
			
		||||
@@ -319,47 +317,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
 | 
			
		||||
            .stream()
 | 
			
		||||
            .filter(Objects::nonNull)
 | 
			
		||||
            .map(mapper)
 | 
			
		||||
            .filter(Objects::nonNull)
 | 
			
		||||
            .collect(Collectors.toList());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 不区分大小写检查 CharSequence 是否以指定的前缀开头。
 | 
			
		||||
     *
 | 
			
		||||
     * @param str     要检查的 CharSequence 可能为 null
 | 
			
		||||
     * @param prefixs 要查找的前缀可能为 null
 | 
			
		||||
     * @return 是否包含
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean startWithAnyIgnoreCase(CharSequence str, CharSequence... prefixs) {
 | 
			
		||||
        // 判断是否是以指定字符串开头
 | 
			
		||||
        for (CharSequence prefix : prefixs) {
 | 
			
		||||
            if (StringUtils.startsWithIgnoreCase(str, prefix)) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将字符串从源字符集转换为目标字符集
 | 
			
		||||
     *
 | 
			
		||||
     * @param input       原始字符串
 | 
			
		||||
     * @param fromCharset 源字符集
 | 
			
		||||
     * @param toCharset   目标字符集
 | 
			
		||||
     * @return 转换后的字符串
 | 
			
		||||
     */
 | 
			
		||||
    public static String convert(String input, Charset fromCharset, Charset toCharset) {
 | 
			
		||||
        if (isBlank(input)) {
 | 
			
		||||
            return input;
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            // 从源字符集获取字节
 | 
			
		||||
            byte[] bytes = input.getBytes(fromCharset);
 | 
			
		||||
            // 使用目标字符集解码
 | 
			
		||||
            return new String(bytes, toCharset);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            return input;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,18 @@ import java.util.concurrent.*;
 | 
			
		||||
@Slf4j
 | 
			
		||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
			
		||||
public class Threads {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * sleep等待,单位为毫秒
 | 
			
		||||
     */
 | 
			
		||||
    public static void sleep(long milliseconds) {
 | 
			
		||||
        try {
 | 
			
		||||
            Thread.sleep(milliseconds);
 | 
			
		||||
        } catch (InterruptedException e) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 停止线程池
 | 
			
		||||
     * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
 | 
			
		||||
 
 | 
			
		||||
@@ -5,13 +5,11 @@ import cn.hutool.core.lang.tree.Tree;
 | 
			
		||||
import cn.hutool.core.lang.tree.TreeNodeConfig;
 | 
			
		||||
import cn.hutool.core.lang.tree.TreeUtil;
 | 
			
		||||
import cn.hutool.core.lang.tree.parser.NodeParser;
 | 
			
		||||
import org.dromara.common.core.utils.reflect.ReflectUtils;
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.dromara.common.core.utils.reflect.ReflectUtils;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 扩展 hutool TreeUtil 封装系统树构建
 | 
			
		||||
@@ -26,71 +24,12 @@ public class TreeBuildUtils extends TreeUtil {
 | 
			
		||||
     */
 | 
			
		||||
    public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label");
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 构建树形结构
 | 
			
		||||
     *
 | 
			
		||||
     * @param <T>        输入节点的类型
 | 
			
		||||
     * @param <K>        节点ID的类型
 | 
			
		||||
     * @param list       节点列表,其中包含了要构建树形结构的所有节点
 | 
			
		||||
     * @param nodeParser 解析器,用于将输入节点转换为树节点
 | 
			
		||||
     * @return 构建好的树形结构列表
 | 
			
		||||
     */
 | 
			
		||||
    public static <T, K> List<Tree<K>> build(List<T> list, NodeParser<T, K> nodeParser) {
 | 
			
		||||
        if (CollUtil.isEmpty(list)) {
 | 
			
		||||
            return CollUtil.newArrayList();
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        K k = ReflectUtils.invokeGetter(list.get(0), "parentId");
 | 
			
		||||
        return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 构建树形结构
 | 
			
		||||
     *
 | 
			
		||||
     * @param <T>        输入节点的类型
 | 
			
		||||
     * @param <K>        节点ID的类型
 | 
			
		||||
     * @param parentId   顶级节点
 | 
			
		||||
     * @param list       节点列表,其中包含了要构建树形结构的所有节点
 | 
			
		||||
     * @param nodeParser 解析器,用于将输入节点转换为树节点
 | 
			
		||||
     * @return 构建好的树形结构列表
 | 
			
		||||
     */
 | 
			
		||||
    public static <T, K> List<Tree<K>> build(List<T> list, K parentId, NodeParser<T, K> nodeParser) {
 | 
			
		||||
        if (CollUtil.isEmpty(list)) {
 | 
			
		||||
            return CollUtil.newArrayList();
 | 
			
		||||
        }
 | 
			
		||||
        return TreeUtil.build(list, parentId, DEFAULT_CONFIG, nodeParser);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取节点列表中所有节点的叶子节点
 | 
			
		||||
     *
 | 
			
		||||
     * @param <K>   节点ID的类型
 | 
			
		||||
     * @param nodes 节点列表
 | 
			
		||||
     * @return 包含所有叶子节点的列表
 | 
			
		||||
     */
 | 
			
		||||
    public static <K> List<Tree<K>> getLeafNodes(List<Tree<K>> nodes) {
 | 
			
		||||
        if (CollUtil.isEmpty(nodes)) {
 | 
			
		||||
            return CollUtil.newArrayList();
 | 
			
		||||
        }
 | 
			
		||||
        return nodes.stream()
 | 
			
		||||
            .flatMap(TreeBuildUtils::extractLeafNodes)
 | 
			
		||||
            .collect(Collectors.toList());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取指定节点下的所有叶子节点
 | 
			
		||||
     *
 | 
			
		||||
     * @param <K>  节点ID的类型
 | 
			
		||||
     * @param node 要查找叶子节点的根节点
 | 
			
		||||
     * @return 包含所有叶子节点的列表
 | 
			
		||||
     */
 | 
			
		||||
    private static <K> Stream<Tree<K>> extractLeafNodes(Tree<K> node) {
 | 
			
		||||
        if (!node.hasChild()) {
 | 
			
		||||
            return Stream.of(node);
 | 
			
		||||
        } else {
 | 
			
		||||
            // 递归调用,获取所有子节点的叶子节点
 | 
			
		||||
            return node.getChildren().stream()
 | 
			
		||||
                .flatMap(TreeBuildUtils::extractLeafNodes);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
package org.dromara.common.core.utils.ip;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.net.NetUtil;
 | 
			
		||||
import cn.hutool.http.HtmlUtil;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.dromara.common.core.utils.NetUtils;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取地址类
 | 
			
		||||
@@ -20,24 +20,14 @@ public class AddressUtils {
 | 
			
		||||
    public static final String UNKNOWN = "XX XX";
 | 
			
		||||
 | 
			
		||||
    public static String getRealAddressByIP(String ip) {
 | 
			
		||||
        // 处理空串并过滤HTML标签
 | 
			
		||||
        ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,""));
 | 
			
		||||
        boolean isIPv6 = NetUtils.isIPv6(ip);
 | 
			
		||||
        // 判断是否为IPv4或IPv6,如果不是则返回未知地址
 | 
			
		||||
        if (!NetUtils.isIPv4(ip) && !isIPv6) {
 | 
			
		||||
        if (StringUtils.isBlank(ip)) {
 | 
			
		||||
            return UNKNOWN;
 | 
			
		||||
        }
 | 
			
		||||
        // 内网不查询
 | 
			
		||||
        if (NetUtils.isInnerIPv6(ip) || NetUtils.isInnerIP(ip)) {
 | 
			
		||||
        ip = StringUtils.contains(ip, "0:0:0:0:0:0:0:1") ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
 | 
			
		||||
        if (NetUtil.isInnerIP(ip)) {
 | 
			
		||||
            return "内网IP";
 | 
			
		||||
        }
 | 
			
		||||
        // 不支持IPv6,不再进行没有必要的IP地址信息的解析,直接返回
 | 
			
		||||
        if (isIPv6) {
 | 
			
		||||
            log.warn("ip2region不支持IPV6地址解析:{}", ip);
 | 
			
		||||
            // 如有需要,可自行实现IPv6地址信息解析逻辑,并在这里返回
 | 
			
		||||
            return "未知";
 | 
			
		||||
        }
 | 
			
		||||
        return RegionUtils.getCityInfo(ip);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,15 @@
 | 
			
		||||
package org.dromara.common.core.utils.ip;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.io.resource.NoResourceException;
 | 
			
		||||
import cn.hutool.core.io.resource.ResourceUtil;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import cn.hutool.core.io.FileUtil;
 | 
			
		||||
import cn.hutool.core.io.resource.ClassPathResource;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import org.dromara.common.core.exception.ServiceException;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import org.dromara.common.core.utils.file.FileUtils;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.lionsoul.ip2region.xdb.Searcher;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 根据ip地址定位工具类,离线方式
 | 
			
		||||
 * 参考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">集成 ip2region 实现离线IP地址定位库</a>
 | 
			
		||||
@@ -16,19 +19,31 @@ import org.lionsoul.ip2region.xdb.Searcher;
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class RegionUtils {
 | 
			
		||||
 | 
			
		||||
    // IP地址库文件名称
 | 
			
		||||
    public static final String IP_XDB_FILENAME = "ip2region.xdb";
 | 
			
		||||
 | 
			
		||||
    private static final Searcher SEARCHER;
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        String fileName = "/ip2region.xdb";
 | 
			
		||||
        File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
 | 
			
		||||
        if (!FileUtils.exist(existFile)) {
 | 
			
		||||
            ClassPathResource fileStream = new ClassPathResource(fileName);
 | 
			
		||||
            if (ObjectUtil.isEmpty(fileStream.getStream())) {
 | 
			
		||||
                throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!");
 | 
			
		||||
            }
 | 
			
		||||
            FileUtils.writeFromStream(fileStream.getStream(), existFile);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String dbPath = existFile.getPath();
 | 
			
		||||
 | 
			
		||||
        // 1、从 dbPath 加载整个 xdb 到内存。
 | 
			
		||||
        byte[] cBuff;
 | 
			
		||||
        try {
 | 
			
		||||
            // 1、将 ip2region 数据库文件 xdb 从 ClassPath 加载到内存。
 | 
			
		||||
            // 2、基于加载到内存的 xdb 数据创建一个 Searcher 查询对象。
 | 
			
		||||
            SEARCHER = Searcher.newWithBuffer(ResourceUtil.readBytes(IP_XDB_FILENAME));
 | 
			
		||||
            log.info("RegionUtils初始化成功,加载IP地址库数据成功!");
 | 
			
		||||
        } catch (NoResourceException e) {
 | 
			
		||||
            throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!");
 | 
			
		||||
            cBuff = Searcher.loadContentFromFile(dbPath);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            throw new ServiceException("RegionUtils初始化失败,原因:从ip2region.xdb文件加载内容失败!" + e.getMessage());
 | 
			
		||||
        }
 | 
			
		||||
        // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
 | 
			
		||||
        try {
 | 
			
		||||
            SEARCHER = Searcher.newWithBuffer(cBuff);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage());
 | 
			
		||||
        }
 | 
			
		||||
@@ -39,8 +54,9 @@ public class RegionUtils {
 | 
			
		||||
     */
 | 
			
		||||
    public static String getCityInfo(String ip) {
 | 
			
		||||
        try {
 | 
			
		||||
            ip = ip.trim();
 | 
			
		||||
            // 3、执行查询
 | 
			
		||||
            String region = SEARCHER.search(StringUtils.trim(ip));
 | 
			
		||||
            String region = SEARCHER.search(ip);
 | 
			
		||||
            return region.replace("0|", "").replace("|0", "");
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.error("IP地址离线获取城市异常 {}", ip);
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,7 @@ public final class RegexUtils extends ReUtil {
 | 
			
		||||
     */
 | 
			
		||||
    public static String extractFromString(String input, String regex, String defaultInput) {
 | 
			
		||||
        try {
 | 
			
		||||
            String str = ReUtil.get(regex, input, 1);
 | 
			
		||||
            return str == null ? defaultInput : str;
 | 
			
		||||
            return ReUtil.get(regex, input, 1);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            return defaultInput;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ public class SqlUtil {
 | 
			
		||||
    /**
 | 
			
		||||
     * 定义常用的 sql关键字
 | 
			
		||||
     */
 | 
			
		||||
    public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
 | 
			
		||||
    public static final String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,48 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.validate.enumd;
 | 
			
		||||
 | 
			
		||||
import jakarta.validation.Constraint;
 | 
			
		||||
import jakarta.validation.Payload;
 | 
			
		||||
 | 
			
		||||
import java.lang.annotation.*;
 | 
			
		||||
 | 
			
		||||
import static java.lang.annotation.ElementType.*;
 | 
			
		||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 自定义枚举校验
 | 
			
		||||
 *
 | 
			
		||||
 * @author 秋辞未寒
 | 
			
		||||
 * @date 2024-12-09
 | 
			
		||||
 */
 | 
			
		||||
@Documented
 | 
			
		||||
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
 | 
			
		||||
@Retention(RUNTIME)
 | 
			
		||||
@Repeatable(EnumPattern.List.class) // 允许在同一元素上多次使用该注解
 | 
			
		||||
@Constraint(validatedBy = {EnumPatternValidator.class})
 | 
			
		||||
public @interface EnumPattern {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 需要校验的枚举类型
 | 
			
		||||
     */
 | 
			
		||||
    Class<? extends Enum<?>> type();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 枚举类型校验值字段名称
 | 
			
		||||
     * 需确保该字段实现了 getter 方法
 | 
			
		||||
     */
 | 
			
		||||
    String fieldName();
 | 
			
		||||
 | 
			
		||||
    String message() default "输入值不在枚举范围内";
 | 
			
		||||
 | 
			
		||||
    Class<?>[] groups() default {};
 | 
			
		||||
 | 
			
		||||
    Class<? extends Payload>[] payload() default {};
 | 
			
		||||
 | 
			
		||||
    @Documented
 | 
			
		||||
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
 | 
			
		||||
    @Retention(RUNTIME)
 | 
			
		||||
    @interface List {
 | 
			
		||||
        EnumPattern[] value();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
package org.dromara.common.core.validate.enumd;
 | 
			
		||||
 | 
			
		||||
import jakarta.validation.ConstraintValidator;
 | 
			
		||||
import jakarta.validation.ConstraintValidatorContext;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import org.dromara.common.core.utils.reflect.ReflectUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 自定义枚举校验注解实现
 | 
			
		||||
 *
 | 
			
		||||
 * @author 秋辞未寒
 | 
			
		||||
 * @date 2024-12-09
 | 
			
		||||
 */
 | 
			
		||||
public class EnumPatternValidator implements ConstraintValidator<EnumPattern, String> {
 | 
			
		||||
 | 
			
		||||
    private EnumPattern annotation;;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void initialize(EnumPattern annotation) {
 | 
			
		||||
        ConstraintValidator.super.initialize(annotation);
 | 
			
		||||
        this.annotation = annotation;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
 | 
			
		||||
        if (StringUtils.isNotBlank(value)) {
 | 
			
		||||
            String fieldName = annotation.fieldName();
 | 
			
		||||
            for (Object e : annotation.type().getEnumConstants()) {
 | 
			
		||||
                if (value.equals(ReflectUtils.invokeGetter(e, fieldName))) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
org.dromara.common.core.config.ApplicationConfig
 | 
			
		||||
org.dromara.common.core.config.AsyncConfig
 | 
			
		||||
org.dromara.common.core.config.RuoYiConfig
 | 
			
		||||
org.dromara.common.core.config.ThreadPoolConfig
 | 
			
		||||
org.dromara.common.core.config.ValidatorConfig
 | 
			
		||||
org.dromara.common.core.utils.SpringUtils
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 接口文档配置
 | 
			
		||||
 * Swagger 文档配置
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
package org.dromara.common.encrypt.core;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import cn.hutool.core.util.ReflectUtil;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.apache.ibatis.io.Resources;
 | 
			
		||||
import org.dromara.common.core.utils.ObjectUtils;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import org.dromara.common.encrypt.annotation.EncryptField;
 | 
			
		||||
import org.springframework.context.ConfigurableApplicationContext;
 | 
			
		||||
@@ -17,10 +17,7 @@ import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
 | 
			
		||||
import org.springframework.util.ClassUtils;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Field;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
@@ -37,7 +34,7 @@ public class EncryptorManager {
 | 
			
		||||
    /**
 | 
			
		||||
     * 缓存加密器
 | 
			
		||||
     */
 | 
			
		||||
    Map<Integer, IEncryptor> encryptorMap = new ConcurrentHashMap<>();
 | 
			
		||||
    Map<EncryptContext, IEncryptor> encryptorMap = new ConcurrentHashMap<>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 类加密字段缓存
 | 
			
		||||
@@ -58,7 +55,10 @@ public class EncryptorManager {
 | 
			
		||||
     * 获取类加密字段缓存
 | 
			
		||||
     */
 | 
			
		||||
    public Set<Field> getFieldCache(Class<?> sourceClazz) {
 | 
			
		||||
        return ObjectUtils.notNullGetter(fieldCache, f -> f.get(sourceClazz));
 | 
			
		||||
        if (ObjectUtil.isNotNull(fieldCache)) {
 | 
			
		||||
            return fieldCache.get(sourceClazz);
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -67,12 +67,11 @@ public class EncryptorManager {
 | 
			
		||||
     * @param encryptContext 加密执行者需要的相关配置参数
 | 
			
		||||
     */
 | 
			
		||||
    public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) {
 | 
			
		||||
        int key = encryptContext.hashCode();
 | 
			
		||||
        if (encryptorMap.containsKey(key)) {
 | 
			
		||||
            return encryptorMap.get(key);
 | 
			
		||||
        if (encryptorMap.containsKey(encryptContext)) {
 | 
			
		||||
            return encryptorMap.get(encryptContext);
 | 
			
		||||
        }
 | 
			
		||||
        IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext);
 | 
			
		||||
        encryptorMap.put(key, encryptor);
 | 
			
		||||
        encryptorMap.put(encryptContext, encryptor);
 | 
			
		||||
        return encryptor;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -82,7 +81,7 @@ public class EncryptorManager {
 | 
			
		||||
     * @param encryptContext 加密执行者需要的相关配置参数
 | 
			
		||||
     */
 | 
			
		||||
    public void removeEncryptor(EncryptContext encryptContext) {
 | 
			
		||||
        this.encryptorMap.remove(encryptContext.hashCode());
 | 
			
		||||
        this.encryptorMap.remove(encryptContext);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -99,7 +99,7 @@ public class CryptoFilter implements Filter {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            return null;
 | 
			
		||||
            throw new RuntimeException(e);
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -76,14 +76,12 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
 | 
			
		||||
        String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey);
 | 
			
		||||
 | 
			
		||||
        // 设置响应头
 | 
			
		||||
        // vue版本需要设置
 | 
			
		||||
        servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag);
 | 
			
		||||
        servletResponse.setHeader(headerFlag, encryptPassword);
 | 
			
		||||
        servletResponse.setHeader("Access-Control-Allow-Origin", "*");
 | 
			
		||||
        servletResponse.setHeader("Access-Control-Allow-Methods", "*");
 | 
			
		||||
        servletResponse.setHeader(headerFlag, encryptPassword);
 | 
			
		||||
        servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // 获取原始内容
 | 
			
		||||
        String originalBody = this.getContent();
 | 
			
		||||
        // 对内容进行加密
 | 
			
		||||
 
 | 
			
		||||
@@ -19,12 +19,10 @@ import java.util.Map;
 | 
			
		||||
 * @author 老马
 | 
			
		||||
 */
 | 
			
		||||
public class EncryptUtils {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 公钥
 | 
			
		||||
     */
 | 
			
		||||
    public static final String PUBLIC_KEY = "publicKey";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 私钥
 | 
			
		||||
     */
 | 
			
		||||
@@ -53,7 +51,7 @@ public class EncryptUtils {
 | 
			
		||||
    /**
 | 
			
		||||
     * AES加密
 | 
			
		||||
     *
 | 
			
		||||
     * @param data     待加密数据
 | 
			
		||||
     * @param data     待解密数据
 | 
			
		||||
     * @param password 秘钥字符串
 | 
			
		||||
     * @return 加密后字符串, 采用Base64编码
 | 
			
		||||
     */
 | 
			
		||||
@@ -72,7 +70,7 @@ public class EncryptUtils {
 | 
			
		||||
    /**
 | 
			
		||||
     * AES加密
 | 
			
		||||
     *
 | 
			
		||||
     * @param data     待加密数据
 | 
			
		||||
     * @param data     待解密数据
 | 
			
		||||
     * @param password 秘钥字符串
 | 
			
		||||
     * @return 加密后字符串, 采用Hex编码
 | 
			
		||||
     */
 | 
			
		||||
@@ -108,7 +106,7 @@ public class EncryptUtils {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * SM4加密(Base64编码)
 | 
			
		||||
     * sm4加密
 | 
			
		||||
     *
 | 
			
		||||
     * @param data     待加密数据
 | 
			
		||||
     * @param password 秘钥字符串
 | 
			
		||||
@@ -127,11 +125,11 @@ public class EncryptUtils {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * SM4加密(Hex编码)
 | 
			
		||||
     * sm4加密
 | 
			
		||||
     *
 | 
			
		||||
     * @param data     待加密数据
 | 
			
		||||
     * @param password 秘钥字符串
 | 
			
		||||
     * @return 加密后字符串, 采用Hex编码
 | 
			
		||||
     * @return 加密后字符串, 采用Base64编码
 | 
			
		||||
     */
 | 
			
		||||
    public static String encryptBySm4Hex(String data, String password) {
 | 
			
		||||
        if (StrUtil.isBlank(password)) {
 | 
			
		||||
@@ -148,7 +146,7 @@ public class EncryptUtils {
 | 
			
		||||
    /**
 | 
			
		||||
     * sm4解密
 | 
			
		||||
     *
 | 
			
		||||
     * @param data     待解密数据(可以是Base64或Hex编码)
 | 
			
		||||
     * @param data     待解密数据
 | 
			
		||||
     * @param password 秘钥字符串
 | 
			
		||||
     * @return 解密后字符串
 | 
			
		||||
     */
 | 
			
		||||
@@ -210,7 +208,7 @@ public class EncryptUtils {
 | 
			
		||||
    /**
 | 
			
		||||
     * sm2私钥解密
 | 
			
		||||
     *
 | 
			
		||||
     * @param data       待解密数据
 | 
			
		||||
     * @param data       待加密数据
 | 
			
		||||
     * @param privateKey 私钥
 | 
			
		||||
     * @return 解密后字符串
 | 
			
		||||
     */
 | 
			
		||||
@@ -268,7 +266,7 @@ public class EncryptUtils {
 | 
			
		||||
    /**
 | 
			
		||||
     * rsa私钥解密
 | 
			
		||||
     *
 | 
			
		||||
     * @param data       待解密数据
 | 
			
		||||
     * @param data       待加密数据
 | 
			
		||||
     * @param privateKey 私钥
 | 
			
		||||
     * @return 解密后字符串
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,13 @@
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>cn.idev.excel</groupId>
 | 
			
		||||
            <artifactId>fastexcel</artifactId>
 | 
			
		||||
            <groupId>com.alibaba</groupId>
 | 
			
		||||
            <artifactId>easyexcel</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <artifactId>commons-compress</artifactId>
 | 
			
		||||
            <groupId>org.apache.commons</groupId>
 | 
			
		||||
            <version>1.26.2</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
package org.dromara.common.excel.annotation;
 | 
			
		||||
 | 
			
		||||
import java.lang.annotation.ElementType;
 | 
			
		||||
import java.lang.annotation.Retention;
 | 
			
		||||
import java.lang.annotation.RetentionPolicy;
 | 
			
		||||
import java.lang.annotation.Target;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 批注
 | 
			
		||||
 * @author guzhouyanyu
 | 
			
		||||
 */
 | 
			
		||||
@Target({ElementType.FIELD})
 | 
			
		||||
@Retention(RetentionPolicy.RUNTIME)
 | 
			
		||||
public @interface ExcelNotation {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * col index
 | 
			
		||||
     */
 | 
			
		||||
    int index() default -1;
 | 
			
		||||
    /**
 | 
			
		||||
     * 批注内容
 | 
			
		||||
     */
 | 
			
		||||
    String value() default "";
 | 
			
		||||
}
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
package org.dromara.common.excel.annotation;
 | 
			
		||||
 | 
			
		||||
import org.apache.poi.ss.usermodel.IndexedColors;
 | 
			
		||||
 | 
			
		||||
import java.lang.annotation.ElementType;
 | 
			
		||||
import java.lang.annotation.Retention;
 | 
			
		||||
import java.lang.annotation.RetentionPolicy;
 | 
			
		||||
import java.lang.annotation.Target;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 是否必填
 | 
			
		||||
 * @author guzhouyanyu
 | 
			
		||||
 */
 | 
			
		||||
@Target({ElementType.FIELD})
 | 
			
		||||
@Retention(RetentionPolicy.RUNTIME)
 | 
			
		||||
public @interface ExcelRequired {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * col index
 | 
			
		||||
     */
 | 
			
		||||
    int index() default -1;
 | 
			
		||||
    /**
 | 
			
		||||
     * 字体颜色
 | 
			
		||||
     */
 | 
			
		||||
    IndexedColors fontColor() default IndexedColors.RED;
 | 
			
		||||
}
 | 
			
		||||
@@ -2,12 +2,12 @@ package org.dromara.common.excel.convert;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.convert.Convert;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import cn.idev.excel.converters.Converter;
 | 
			
		||||
import cn.idev.excel.enums.CellDataTypeEnum;
 | 
			
		||||
import cn.idev.excel.metadata.GlobalConfiguration;
 | 
			
		||||
import cn.idev.excel.metadata.data.ReadCellData;
 | 
			
		||||
import cn.idev.excel.metadata.data.WriteCellData;
 | 
			
		||||
import cn.idev.excel.metadata.property.ExcelContentProperty;
 | 
			
		||||
import com.alibaba.excel.converters.Converter;
 | 
			
		||||
import com.alibaba.excel.enums.CellDataTypeEnum;
 | 
			
		||||
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 lombok.extern.slf4j.Slf4j;
 | 
			
		||||
 | 
			
		||||
import java.math.BigDecimal;
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,12 @@ package org.dromara.common.excel.convert;
 | 
			
		||||
import cn.hutool.core.annotation.AnnotationUtil;
 | 
			
		||||
import cn.hutool.core.convert.Convert;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import cn.idev.excel.converters.Converter;
 | 
			
		||||
import cn.idev.excel.enums.CellDataTypeEnum;
 | 
			
		||||
import cn.idev.excel.metadata.GlobalConfiguration;
 | 
			
		||||
import cn.idev.excel.metadata.data.ReadCellData;
 | 
			
		||||
import cn.idev.excel.metadata.data.WriteCellData;
 | 
			
		||||
import cn.idev.excel.metadata.property.ExcelContentProperty;
 | 
			
		||||
import com.alibaba.excel.converters.Converter;
 | 
			
		||||
import com.alibaba.excel.enums.CellDataTypeEnum;
 | 
			
		||||
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 org.dromara.common.excel.annotation.ExcelDictFormat;
 | 
			
		||||
import org.dromara.common.core.service.DictService;
 | 
			
		||||
import org.dromara.common.core.utils.SpringUtils;
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,12 @@ package org.dromara.common.excel.convert;
 | 
			
		||||
import cn.hutool.core.annotation.AnnotationUtil;
 | 
			
		||||
import cn.hutool.core.convert.Convert;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import cn.idev.excel.converters.Converter;
 | 
			
		||||
import cn.idev.excel.enums.CellDataTypeEnum;
 | 
			
		||||
import cn.idev.excel.metadata.GlobalConfiguration;
 | 
			
		||||
import cn.idev.excel.metadata.data.ReadCellData;
 | 
			
		||||
import cn.idev.excel.metadata.data.WriteCellData;
 | 
			
		||||
import cn.idev.excel.metadata.property.ExcelContentProperty;
 | 
			
		||||
import com.alibaba.excel.converters.Converter;
 | 
			
		||||
import com.alibaba.excel.enums.CellDataTypeEnum;
 | 
			
		||||
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 org.dromara.common.core.utils.reflect.ReflectUtils;
 | 
			
		||||
import org.dromara.common.excel.annotation.ExcelEnumFormat;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,11 @@ package org.dromara.common.excel.core;
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.util.ReflectUtil;
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import cn.idev.excel.annotation.ExcelProperty;
 | 
			
		||||
import cn.idev.excel.metadata.Head;
 | 
			
		||||
import cn.idev.excel.write.handler.WorkbookWriteHandler;
 | 
			
		||||
import cn.idev.excel.write.handler.context.WorkbookWriteHandlerContext;
 | 
			
		||||
import cn.idev.excel.write.merge.AbstractMergeStrategy;
 | 
			
		||||
import com.alibaba.excel.annotation.ExcelProperty;
 | 
			
		||||
import com.alibaba.excel.metadata.Head;
 | 
			
		||||
import com.alibaba.excel.write.handler.WorkbookWriteHandler;
 | 
			
		||||
import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext;
 | 
			
		||||
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.SneakyThrows;
 | 
			
		||||
@@ -112,13 +112,7 @@ public class CellMergeStrategy extends AbstractMergeStrategy implements Workbook
 | 
			
		||||
                        }
 | 
			
		||||
                        map.put(field, new RepeatCell(val, i));
 | 
			
		||||
                    } else if (i == list.size() - 1) {
 | 
			
		||||
                        if (!isMerge(list, i, field)) {
 | 
			
		||||
                            // 如果最后一行不能合并,检查之前的数据是否需要合并
 | 
			
		||||
                            if (i - repeatCell.getCurrent() > 1) {
 | 
			
		||||
                                cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
 | 
			
		||||
                            }
 | 
			
		||||
                        } else if (i > repeatCell.getCurrent()) {
 | 
			
		||||
                            // 如果最后一行可以合并,则直接合并到最后
 | 
			
		||||
                        if (i > repeatCell.getCurrent() && isMerge(list, i, field)) {
 | 
			
		||||
                            cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (!isMerge(list, i, field)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
package org.dromara.common.excel.core;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import cn.idev.excel.context.AnalysisContext;
 | 
			
		||||
import cn.idev.excel.event.AnalysisEventListener;
 | 
			
		||||
import cn.idev.excel.exception.ExcelAnalysisException;
 | 
			
		||||
import cn.idev.excel.exception.ExcelDataConvertException;
 | 
			
		||||
import com.alibaba.excel.context.AnalysisContext;
 | 
			
		||||
import com.alibaba.excel.event.AnalysisEventListener;
 | 
			
		||||
import com.alibaba.excel.exception.ExcelAnalysisException;
 | 
			
		||||
import com.alibaba.excel.exception.ExcelDataConvertException;
 | 
			
		||||
import org.dromara.common.core.utils.StreamUtils;
 | 
			
		||||
import org.dromara.common.core.utils.ValidatorUtils;
 | 
			
		||||
import org.dromara.common.json.utils.JsonUtils;
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,12 @@ import cn.hutool.core.util.ArrayUtil;
 | 
			
		||||
import cn.hutool.core.util.EnumUtil;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import cn.idev.excel.metadata.FieldCache;
 | 
			
		||||
import cn.idev.excel.metadata.FieldWrapper;
 | 
			
		||||
import cn.idev.excel.util.ClassUtils;
 | 
			
		||||
import cn.idev.excel.write.handler.SheetWriteHandler;
 | 
			
		||||
import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
 | 
			
		||||
import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder;
 | 
			
		||||
import com.alibaba.excel.metadata.FieldCache;
 | 
			
		||||
import com.alibaba.excel.metadata.FieldWrapper;
 | 
			
		||||
import com.alibaba.excel.util.ClassUtils;
 | 
			
		||||
import com.alibaba.excel.write.handler.SheetWriteHandler;
 | 
			
		||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
 | 
			
		||||
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.apache.poi.ss.usermodel.*;
 | 
			
		||||
import org.apache.poi.ss.util.CellRangeAddressList;
 | 
			
		||||
@@ -55,7 +55,6 @@ public class ExcelDownHandler implements SheetWriteHandler {
 | 
			
		||||
     * 下拉可选项
 | 
			
		||||
     */
 | 
			
		||||
    private final List<DropDownOptions> dropDownOptions;
 | 
			
		||||
    private final DictService dictService;
 | 
			
		||||
    /**
 | 
			
		||||
     * 当前单选进度
 | 
			
		||||
     */
 | 
			
		||||
@@ -64,6 +63,7 @@ public class ExcelDownHandler implements SheetWriteHandler {
 | 
			
		||||
     * 当前联动选择进度
 | 
			
		||||
     */
 | 
			
		||||
    private int currentLinkedOptionsSheetIndex;
 | 
			
		||||
    private final DictService dictService;
 | 
			
		||||
 | 
			
		||||
    public ExcelDownHandler(List<DropDownOptions> options) {
 | 
			
		||||
        this.dropDownOptions = options;
 | 
			
		||||
@@ -139,8 +139,8 @@ public class ExcelDownHandler implements SheetWriteHandler {
 | 
			
		||||
            } else if (everyOptions.getOptions().size() > 10) {
 | 
			
		||||
                // 当一级选项参数个数大于10,使用额外表的形式
 | 
			
		||||
                dropDownWithSheet(helper, workbook, sheet, everyOptions.getIndex(), everyOptions.getOptions());
 | 
			
		||||
            } else {
 | 
			
		||||
                // 否则使用默认形式
 | 
			
		||||
            } else if (everyOptions.getOptions().size() != 0) {
 | 
			
		||||
                // 当一级选项个数不为空,使用默认形式
 | 
			
		||||
                dropDownWithSimple(helper, sheet, everyOptions.getIndex(), everyOptions.getOptions());
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
@@ -171,24 +171,10 @@ public class ExcelDownHandler implements SheetWriteHandler {
 | 
			
		||||
        Sheet linkedOptionsDataSheet = workbook.createSheet(WorkbookUtil.createSafeSheetName(linkedOptionsSheetName));
 | 
			
		||||
        // 将下拉表隐藏
 | 
			
		||||
        workbook.setSheetHidden(workbook.getSheetIndex(linkedOptionsDataSheet), true);
 | 
			
		||||
        // 选项数据
 | 
			
		||||
        // 完善横向的一级选项数据表
 | 
			
		||||
        List<String> firstOptions = options.getOptions();
 | 
			
		||||
        Map<String, List<String>> secoundOptionsMap = options.getNextOptions();
 | 
			
		||||
 | 
			
		||||
        // 采用按行填充数据的方式,避免出现数据无法写入的问题
 | 
			
		||||
        // Attempting to write a row in the range that is already written to disk
 | 
			
		||||
 | 
			
		||||
        // 使用ArrayList记载数据,防止乱序
 | 
			
		||||
        List<String> columnNames = new ArrayList<>();
 | 
			
		||||
        // 写入第一行,即第一级的数据
 | 
			
		||||
        Row firstRow = linkedOptionsDataSheet.createRow(0);
 | 
			
		||||
        for (int columnIndex = 0; columnIndex < firstOptions.size(); columnIndex++) {
 | 
			
		||||
            String columnName = firstOptions.get(columnIndex);
 | 
			
		||||
            firstRow.createCell(columnIndex)
 | 
			
		||||
                .setCellValue(columnName);
 | 
			
		||||
            columnNames.add(columnName);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 创建名称管理器
 | 
			
		||||
        Name name = workbook.createName();
 | 
			
		||||
        // 设置名称管理器的别名
 | 
			
		||||
@@ -204,12 +190,28 @@ public class ExcelDownHandler implements SheetWriteHandler {
 | 
			
		||||
        // 设置数据校验为序列模式,引用的是名称管理器中的别名
 | 
			
		||||
        this.markOptionsToSheet(helper, sheet, options.getIndex(), helper.createFormulaListConstraint(linkedOptionsSheetName));
 | 
			
		||||
 | 
			
		||||
        // 创建二级选项的名称管理器
 | 
			
		||||
        for (int columIndex = 0; columIndex < columnNames.size(); columIndex++) {
 | 
			
		||||
            // 列名
 | 
			
		||||
        for (int columIndex = 0; columIndex < firstOptions.size(); columIndex++) {
 | 
			
		||||
            // 先提取主表中一级下拉的列名
 | 
			
		||||
            String firstOptionsColumnName = getExcelColumnName(columIndex);
 | 
			
		||||
            // 对应的一级值
 | 
			
		||||
            String thisFirstOptionsValue = columnNames.get(columIndex);
 | 
			
		||||
            // 一次循环是每一个一级选项
 | 
			
		||||
            int finalI = columIndex;
 | 
			
		||||
            // 本次循环的一级选项值
 | 
			
		||||
            String thisFirstOptionsValue = firstOptions.get(columIndex);
 | 
			
		||||
            // 创建第一行的数据
 | 
			
		||||
            Optional.ofNullable(linkedOptionsDataSheet.getRow(0))
 | 
			
		||||
                // 如果不存在则创建第一行
 | 
			
		||||
                .orElseGet(() -> linkedOptionsDataSheet.createRow(finalI))
 | 
			
		||||
                // 第一行当前列
 | 
			
		||||
                .createCell(columIndex)
 | 
			
		||||
                // 设置值为当前一级选项值
 | 
			
		||||
                .setCellValue(thisFirstOptionsValue);
 | 
			
		||||
 | 
			
		||||
            // 第二行开始,设置第二级别选项参数
 | 
			
		||||
            List<String> secondOptions = secoundOptionsMap.get(thisFirstOptionsValue);
 | 
			
		||||
            if (CollUtil.isEmpty(secondOptions)) {
 | 
			
		||||
                // 必须保证至少有一个关联选项,否则将导致Excel解析错误
 | 
			
		||||
                secondOptions = Collections.singletonList("暂无_0");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 以该一级选项值创建子名称管理器
 | 
			
		||||
            Name sonName = workbook.createName();
 | 
			
		||||
@@ -220,9 +222,7 @@ public class ExcelDownHandler implements SheetWriteHandler {
 | 
			
		||||
                linkedOptionsSheetName,
 | 
			
		||||
                firstOptionsColumnName,
 | 
			
		||||
                firstOptionsColumnName,
 | 
			
		||||
                // 二级选项存在则设置为(选项个数+1)行,否则设置为2行
 | 
			
		||||
                Math.max(Optional.ofNullable(secoundOptionsMap.get(thisFirstOptionsValue))
 | 
			
		||||
                    .orElseGet(ArrayList::new).size(), 1) + 1
 | 
			
		||||
                secondOptions.size() + 1
 | 
			
		||||
            );
 | 
			
		||||
            // 设置名称管理器的引用位置
 | 
			
		||||
            sonName.setRefersToFormula(sonFunction);
 | 
			
		||||
@@ -235,51 +235,25 @@ public class ExcelDownHandler implements SheetWriteHandler {
 | 
			
		||||
                // 二级只能主表每一行的每一列添加二级校验
 | 
			
		||||
                markLinkedOptionsToSheet(helper, sheet, i, options.getNextIndex(), helper.createFormulaListConstraint(secondOptionsFunction));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 将二级数据处理为按行区分
 | 
			
		||||
        Map<Integer, List<String>> columnValueMap = new HashMap<>();
 | 
			
		||||
        int currentRow = 1;
 | 
			
		||||
        while (currentRow >= 0) {
 | 
			
		||||
            boolean flag = false;
 | 
			
		||||
            List<String> rowData = new ArrayList<>();
 | 
			
		||||
            for (String columnName : columnNames) {
 | 
			
		||||
                List<String> data = secoundOptionsMap.get(columnName);
 | 
			
		||||
                if (CollUtil.isEmpty(data)) {
 | 
			
		||||
                    // 添加空字符串填充位置
 | 
			
		||||
                    rowData.add(" ");
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                // 取第一个
 | 
			
		||||
                String str = data.get(0);
 | 
			
		||||
                rowData.add(str);
 | 
			
		||||
                // 通过移除的方式避免重复
 | 
			
		||||
                data.remove(0);
 | 
			
		||||
                // 设置可以继续
 | 
			
		||||
                flag = true;
 | 
			
		||||
            }
 | 
			
		||||
            columnValueMap.put(currentRow, rowData);
 | 
			
		||||
            // 可以继续,则增加行数,否则置为负数跳出循环
 | 
			
		||||
            if (flag) {
 | 
			
		||||
                currentRow++;
 | 
			
		||||
            } else {
 | 
			
		||||
                currentRow = -1;
 | 
			
		||||
            for (int rowIndex = 0; rowIndex < secondOptions.size(); rowIndex++) {
 | 
			
		||||
                // 从第二行开始填充二级选项
 | 
			
		||||
                int finalRowIndex = rowIndex + 1;
 | 
			
		||||
                int finalColumIndex = columIndex;
 | 
			
		||||
 | 
			
		||||
                Row row = Optional.ofNullable(linkedOptionsDataSheet.getRow(finalRowIndex))
 | 
			
		||||
                    // 没有则创建
 | 
			
		||||
                    .orElseGet(() -> linkedOptionsDataSheet.createRow(finalRowIndex));
 | 
			
		||||
                Optional
 | 
			
		||||
                    // 在本级一级选项所在的列
 | 
			
		||||
                    .ofNullable(row.getCell(finalColumIndex))
 | 
			
		||||
                    // 不存在则创建
 | 
			
		||||
                    .orElseGet(() -> row.createCell(finalColumIndex))
 | 
			
		||||
                    // 设置二级选项值
 | 
			
		||||
                    .setCellValue(secondOptions.get(rowIndex));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 填充第二级选项数据
 | 
			
		||||
        columnValueMap.forEach((rowIndex, rowValues) -> {
 | 
			
		||||
            Row row = linkedOptionsDataSheet.createRow(rowIndex);
 | 
			
		||||
            for (int columnIndex = 0; columnIndex < rowValues.size(); columnIndex++) {
 | 
			
		||||
                String rowValue = rowValues.get(columnIndex);
 | 
			
		||||
                // 填充位置的部分不渲染
 | 
			
		||||
                if (StrUtil.isNotBlank(rowValue)) {
 | 
			
		||||
                    row.createCell(columnIndex)
 | 
			
		||||
                        .setCellValue(rowValue);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        currentLinkedOptionsSheetIndex++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -291,11 +265,9 @@ public class ExcelDownHandler implements SheetWriteHandler {
 | 
			
		||||
     * @param value    下拉选可选值
 | 
			
		||||
     */
 | 
			
		||||
    private void dropDownWithSheet(DataValidationHelper helper, Workbook workbook, Sheet sheet, Integer celIndex, List<String> value) {
 | 
			
		||||
        //由于poi的写出相关问题,超过100个会被临时写进硬盘,导致后续内存合并会出Attempting to write a row[] in the range [] that is already written to disk
 | 
			
		||||
        String tmpOptionsSheetName = OPTIONS_SHEET_NAME + "_" + currentOptionsColumnIndex;
 | 
			
		||||
        // 创建下拉数据表
 | 
			
		||||
        Sheet simpleDataSheet = Optional.ofNullable(workbook.getSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName)))
 | 
			
		||||
            .orElseGet(() -> workbook.createSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName)));
 | 
			
		||||
        Sheet simpleDataSheet = Optional.ofNullable(workbook.getSheet(WorkbookUtil.createSafeSheetName(OPTIONS_SHEET_NAME)))
 | 
			
		||||
            .orElseGet(() -> workbook.createSheet(WorkbookUtil.createSafeSheetName(OPTIONS_SHEET_NAME)));
 | 
			
		||||
        // 将下拉表隐藏
 | 
			
		||||
        workbook.setSheetHidden(workbook.getSheetIndex(simpleDataSheet), true);
 | 
			
		||||
        // 完善纵向的一级选项数据表
 | 
			
		||||
@@ -304,9 +276,9 @@ public class ExcelDownHandler implements SheetWriteHandler {
 | 
			
		||||
            // 获取每一选项行,如果没有则创建
 | 
			
		||||
            Row row = Optional.ofNullable(simpleDataSheet.getRow(i))
 | 
			
		||||
                .orElseGet(() -> simpleDataSheet.createRow(finalI));
 | 
			
		||||
            // 获取本级选项对应的选项列,如果没有则创建。上述采用多个sheet,默认索引为1列
 | 
			
		||||
            Cell cell = Optional.ofNullable(row.getCell(0))
 | 
			
		||||
                .orElseGet(() -> row.createCell(0));
 | 
			
		||||
            // 获取本级选项对应的选项列,如果没有则创建
 | 
			
		||||
            Cell cell = Optional.ofNullable(row.getCell(currentOptionsColumnIndex))
 | 
			
		||||
                .orElseGet(() -> row.createCell(currentOptionsColumnIndex));
 | 
			
		||||
            // 设置值
 | 
			
		||||
            cell.setCellValue(value.get(i));
 | 
			
		||||
        }
 | 
			
		||||
@@ -314,13 +286,13 @@ public class ExcelDownHandler implements SheetWriteHandler {
 | 
			
		||||
        // 创建名称管理器
 | 
			
		||||
        Name name = workbook.createName();
 | 
			
		||||
        // 设置名称管理器的别名
 | 
			
		||||
        String nameName = String.format("%s_%d", tmpOptionsSheetName, celIndex);
 | 
			
		||||
        String nameName = String.format("%s_%d", OPTIONS_SHEET_NAME, celIndex);
 | 
			
		||||
        name.setNameName(nameName);
 | 
			
		||||
        // 以纵向第一列创建一级下拉拼接引用位置
 | 
			
		||||
        String function = String.format("%s!$%s$1:$%s$%d",
 | 
			
		||||
            tmpOptionsSheetName,
 | 
			
		||||
            getExcelColumnName(0),
 | 
			
		||||
            getExcelColumnName(0),
 | 
			
		||||
            OPTIONS_SHEET_NAME,
 | 
			
		||||
            getExcelColumnName(currentOptionsColumnIndex),
 | 
			
		||||
            getExcelColumnName(currentOptionsColumnIndex),
 | 
			
		||||
            value.size());
 | 
			
		||||
        // 设置名称管理器的引用位置
 | 
			
		||||
        name.setRefersToFormula(function);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
package org.dromara.common.excel.core;
 | 
			
		||||
 | 
			
		||||
import cn.idev.excel.read.listener.ReadListener;
 | 
			
		||||
import com.alibaba.excel.read.listener.ReadListener;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Excel 导入监听
 | 
			
		||||
 
 | 
			
		||||
@@ -1,135 +0,0 @@
 | 
			
		||||
package org.dromara.common.excel.handler;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.idev.excel.metadata.data.DataFormatData;
 | 
			
		||||
import cn.idev.excel.metadata.data.WriteCellData;
 | 
			
		||||
import cn.idev.excel.util.StyleUtil;
 | 
			
		||||
import cn.idev.excel.write.handler.CellWriteHandler;
 | 
			
		||||
import cn.idev.excel.write.handler.SheetWriteHandler;
 | 
			
		||||
import cn.idev.excel.write.handler.context.CellWriteHandlerContext;
 | 
			
		||||
import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
 | 
			
		||||
import cn.idev.excel.write.metadata.style.WriteCellStyle;
 | 
			
		||||
import cn.idev.excel.write.metadata.style.WriteFont;
 | 
			
		||||
import org.apache.poi.ss.usermodel.*;
 | 
			
		||||
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
 | 
			
		||||
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
 | 
			
		||||
import org.dromara.common.core.utils.reflect.ReflectUtils;
 | 
			
		||||
import org.dromara.common.excel.annotation.ExcelNotation;
 | 
			
		||||
import org.dromara.common.excel.annotation.ExcelRequired;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Field;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 批注、必填
 | 
			
		||||
 *
 | 
			
		||||
 * @author guzhouyanyu
 | 
			
		||||
 */
 | 
			
		||||
public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 批注
 | 
			
		||||
     */
 | 
			
		||||
    private final Map<Integer, String> notationMap;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 头列字体颜色
 | 
			
		||||
     */
 | 
			
		||||
    private final Map<Integer, Short> headColumnMap;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public DataWriteHandler(Class<?> clazz) {
 | 
			
		||||
        notationMap = getNotationMap(clazz);
 | 
			
		||||
        headColumnMap = getRequiredMap(clazz);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void afterCellDispose(CellWriteHandlerContext context) {
 | 
			
		||||
        if (CollUtil.isEmpty(notationMap) && CollUtil.isEmpty(headColumnMap)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        WriteCellData<?> cellData = context.getFirstCellData();
 | 
			
		||||
        WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();
 | 
			
		||||
 | 
			
		||||
        DataFormatData dataFormatData = new DataFormatData();
 | 
			
		||||
        // 单元格设置为文本格式
 | 
			
		||||
        dataFormatData.setIndex((short) 49);
 | 
			
		||||
        writeCellStyle.setDataFormatData(dataFormatData);
 | 
			
		||||
 | 
			
		||||
        if (context.getHead()) {
 | 
			
		||||
            Cell cell = context.getCell();
 | 
			
		||||
            WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder();
 | 
			
		||||
            Sheet sheet = writeSheetHolder.getSheet();
 | 
			
		||||
            Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
 | 
			
		||||
            Drawing<?> drawing = sheet.createDrawingPatriarch();
 | 
			
		||||
            // 设置标题字体样式
 | 
			
		||||
            WriteFont headWriteFont = new WriteFont();
 | 
			
		||||
            // 加粗
 | 
			
		||||
            headWriteFont.setBold(true);
 | 
			
		||||
            if (CollUtil.isNotEmpty(headColumnMap) && headColumnMap.containsKey(cell.getColumnIndex())) {
 | 
			
		||||
                // 设置字体颜色
 | 
			
		||||
                headWriteFont.setColor(headColumnMap.get(cell.getColumnIndex()));
 | 
			
		||||
            }
 | 
			
		||||
            writeCellStyle.setWriteFont(headWriteFont);
 | 
			
		||||
            CellStyle cellStyle = StyleUtil.buildCellStyle(workbook, null, writeCellStyle);
 | 
			
		||||
            cell.setCellStyle(cellStyle);
 | 
			
		||||
 | 
			
		||||
            if (CollUtil.isNotEmpty(notationMap) && notationMap.containsKey(cell.getColumnIndex())) {
 | 
			
		||||
                // 批注内容
 | 
			
		||||
                String notationContext = notationMap.get(cell.getColumnIndex());
 | 
			
		||||
                // 创建绘图对象
 | 
			
		||||
                Comment comment = drawing.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), 0, (short) 5, 5));
 | 
			
		||||
                comment.setString(new XSSFRichTextString(notationContext));
 | 
			
		||||
                cell.setCellComment(comment);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取必填列
 | 
			
		||||
     */
 | 
			
		||||
    private static Map<Integer, Short> getRequiredMap(Class<?> clazz) {
 | 
			
		||||
        Map<Integer, Short> requiredMap = new HashMap<>();
 | 
			
		||||
        Field[] fields = clazz.getDeclaredFields();
 | 
			
		||||
        // 检查 fields 数组是否为空
 | 
			
		||||
        if (fields.length == 0) {
 | 
			
		||||
            return requiredMap;
 | 
			
		||||
        }
 | 
			
		||||
        Field[] filteredFields = ReflectUtils.getFields(clazz, field -> !"serialVersionUID".equals(field.getName()));
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < filteredFields.length; i++) {
 | 
			
		||||
            Field field = filteredFields[i];
 | 
			
		||||
            if (!field.isAnnotationPresent(ExcelRequired.class)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            ExcelRequired excelRequired = field.getAnnotation(ExcelRequired.class);
 | 
			
		||||
            int columnIndex =  excelRequired.index() == -1 ? i : excelRequired.index();
 | 
			
		||||
            requiredMap.put(columnIndex, excelRequired.fontColor().getIndex());
 | 
			
		||||
        }
 | 
			
		||||
        return requiredMap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取批注
 | 
			
		||||
     */
 | 
			
		||||
    private static Map<Integer, String> getNotationMap(Class<?> clazz) {
 | 
			
		||||
        Map<Integer, String> notationMap = new HashMap<>();
 | 
			
		||||
        Field[] fields = clazz.getDeclaredFields();
 | 
			
		||||
        // 检查 fields 数组是否为空
 | 
			
		||||
        if (fields.length == 0) {
 | 
			
		||||
            return notationMap;
 | 
			
		||||
        }
 | 
			
		||||
        Field[] filteredFields = ReflectUtils.getFields(clazz, field -> !"serialVersionUID".equals(field.getName()));
 | 
			
		||||
        for (int i = 0; i < filteredFields.length; i++) {
 | 
			
		||||
            Field field = filteredFields[i];
 | 
			
		||||
            if (!field.isAnnotationPresent(ExcelNotation.class)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            ExcelNotation excelNotation = field.getAnnotation(ExcelNotation.class);
 | 
			
		||||
            int columnIndex =  excelNotation.index() == -1 ? i : excelNotation.index();
 | 
			
		||||
            notationMap.put(columnIndex, excelNotation.value());
 | 
			
		||||
        }
 | 
			
		||||
        return notationMap;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,13 +3,13 @@ package org.dromara.common.excel.utils;
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.io.resource.ClassPathResource;
 | 
			
		||||
import cn.hutool.core.util.IdUtil;
 | 
			
		||||
import cn.idev.excel.FastExcel;
 | 
			
		||||
import cn.idev.excel.ExcelWriter;
 | 
			
		||||
import cn.idev.excel.write.builder.ExcelWriterSheetBuilder;
 | 
			
		||||
import cn.idev.excel.write.metadata.WriteSheet;
 | 
			
		||||
import cn.idev.excel.write.metadata.fill.FillConfig;
 | 
			
		||||
import cn.idev.excel.write.metadata.fill.FillWrapper;
 | 
			
		||||
import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
 | 
			
		||||
import com.alibaba.excel.EasyExcel;
 | 
			
		||||
import com.alibaba.excel.ExcelWriter;
 | 
			
		||||
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
 | 
			
		||||
import com.alibaba.excel.write.metadata.WriteSheet;
 | 
			
		||||
import com.alibaba.excel.write.metadata.fill.FillConfig;
 | 
			
		||||
import com.alibaba.excel.write.metadata.fill.FillWrapper;
 | 
			
		||||
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
 | 
			
		||||
import jakarta.servlet.ServletOutputStream;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
@@ -18,7 +18,6 @@ import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import org.dromara.common.core.utils.file.FileUtils;
 | 
			
		||||
import org.dromara.common.excel.convert.ExcelBigNumberConvert;
 | 
			
		||||
import org.dromara.common.excel.core.*;
 | 
			
		||||
import org.dromara.common.excel.handler.DataWriteHandler;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
@@ -43,7 +42,7 @@ public class ExcelUtil {
 | 
			
		||||
     * @return 转换后集合
 | 
			
		||||
     */
 | 
			
		||||
    public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {
 | 
			
		||||
        return FastExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync();
 | 
			
		||||
        return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -57,7 +56,7 @@ public class ExcelUtil {
 | 
			
		||||
     */
 | 
			
		||||
    public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, boolean isValidate) {
 | 
			
		||||
        DefaultExcelListener<T> listener = new DefaultExcelListener<>(isValidate);
 | 
			
		||||
        FastExcel.read(is, clazz, listener).sheet().doRead();
 | 
			
		||||
        EasyExcel.read(is, clazz, listener).sheet().doRead();
 | 
			
		||||
        return listener.getExcelResult();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -70,7 +69,7 @@ public class ExcelUtil {
 | 
			
		||||
     * @return 转换后集合
 | 
			
		||||
     */
 | 
			
		||||
    public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, ExcelListener<T> listener) {
 | 
			
		||||
        FastExcel.read(is, clazz, listener).sheet().doRead();
 | 
			
		||||
        EasyExcel.read(is, clazz, listener).sheet().doRead();
 | 
			
		||||
        return listener.getExcelResult();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -186,13 +185,12 @@ public class ExcelUtil {
 | 
			
		||||
     */
 | 
			
		||||
    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge,
 | 
			
		||||
                                       OutputStream os, List<DropDownOptions> options) {
 | 
			
		||||
        ExcelWriterSheetBuilder builder = FastExcel.write(os, clazz)
 | 
			
		||||
        ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
 | 
			
		||||
            .autoCloseStream(false)
 | 
			
		||||
            // 自动适配
 | 
			
		||||
            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
 | 
			
		||||
            // 大数值自动转换 防止失真
 | 
			
		||||
            .registerConverter(new ExcelBigNumberConvert())
 | 
			
		||||
            .registerWriteHandler(new DataWriteHandler(clazz))
 | 
			
		||||
            .sheet(sheetName);
 | 
			
		||||
        if (merge) {
 | 
			
		||||
            // 合并处理器
 | 
			
		||||
@@ -213,11 +211,8 @@ public class ExcelUtil {
 | 
			
		||||
     * @param data         模板需要的数据
 | 
			
		||||
     * @param response     响应体
 | 
			
		||||
     */
 | 
			
		||||
    public static <T> void exportTemplate(List<T> data, String filename, String templatePath, HttpServletResponse response) {
 | 
			
		||||
    public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) {
 | 
			
		||||
        try {
 | 
			
		||||
            if (CollUtil.isEmpty(data)) {
 | 
			
		||||
                throw new IllegalArgumentException("数据为空");
 | 
			
		||||
            }
 | 
			
		||||
            resetResponse(filename, response);
 | 
			
		||||
            ServletOutputStream os = response.getOutputStream();
 | 
			
		||||
            exportTemplate(data, templatePath, os);
 | 
			
		||||
@@ -235,20 +230,21 @@ public class ExcelUtil {
 | 
			
		||||
     * @param data         模板需要的数据
 | 
			
		||||
     * @param os           输出流
 | 
			
		||||
     */
 | 
			
		||||
    public static <T> void exportTemplate(List<T> data, String templatePath, OutputStream os) {
 | 
			
		||||
    public static void exportTemplate(List<Object> data, String templatePath, OutputStream os) {
 | 
			
		||||
        ClassPathResource templateResource = new ClassPathResource(templatePath);
 | 
			
		||||
        ExcelWriter excelWriter = FastExcel.write(os)
 | 
			
		||||
        ExcelWriter excelWriter = EasyExcel.write(os)
 | 
			
		||||
            .withTemplate(templateResource.getStream())
 | 
			
		||||
            .autoCloseStream(false)
 | 
			
		||||
            // 大数值自动转换 防止失真
 | 
			
		||||
            .registerConverter(new ExcelBigNumberConvert())
 | 
			
		||||
            .registerWriteHandler(new DataWriteHandler(data.get(0).getClass()))
 | 
			
		||||
            .build();
 | 
			
		||||
        WriteSheet writeSheet = FastExcel.writerSheet().build();
 | 
			
		||||
        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
 | 
			
		||||
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
 | 
			
		||||
        if (CollUtil.isEmpty(data)) {
 | 
			
		||||
            throw new IllegalArgumentException("数据为空");
 | 
			
		||||
        }
 | 
			
		||||
        // 单表多数据导出 模板格式为 {.属性}
 | 
			
		||||
        for (T d : data) {
 | 
			
		||||
            excelWriter.fill(d, fillConfig, writeSheet);
 | 
			
		||||
        for (Object d : data) {
 | 
			
		||||
            excelWriter.fill(d, writeSheet);
 | 
			
		||||
        }
 | 
			
		||||
        excelWriter.finish();
 | 
			
		||||
    }
 | 
			
		||||
@@ -265,9 +261,6 @@ public class ExcelUtil {
 | 
			
		||||
     */
 | 
			
		||||
    public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) {
 | 
			
		||||
        try {
 | 
			
		||||
            if (CollUtil.isEmpty(data)) {
 | 
			
		||||
                throw new IllegalArgumentException("数据为空");
 | 
			
		||||
            }
 | 
			
		||||
            resetResponse(filename, response);
 | 
			
		||||
            ServletOutputStream os = response.getOutputStream();
 | 
			
		||||
            exportTemplateMultiList(data, templatePath, os);
 | 
			
		||||
@@ -288,9 +281,6 @@ public class ExcelUtil {
 | 
			
		||||
     */
 | 
			
		||||
    public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String filename, String templatePath, HttpServletResponse response) {
 | 
			
		||||
        try {
 | 
			
		||||
            if (CollUtil.isEmpty(data)) {
 | 
			
		||||
                throw new IllegalArgumentException("数据为空");
 | 
			
		||||
            }
 | 
			
		||||
            resetResponse(filename, response);
 | 
			
		||||
            ServletOutputStream os = response.getOutputStream();
 | 
			
		||||
            exportTemplateMultiSheet(data, templatePath, os);
 | 
			
		||||
@@ -310,13 +300,16 @@ public class ExcelUtil {
 | 
			
		||||
     */
 | 
			
		||||
    public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) {
 | 
			
		||||
        ClassPathResource templateResource = new ClassPathResource(templatePath);
 | 
			
		||||
        ExcelWriter excelWriter = FastExcel.write(os)
 | 
			
		||||
        ExcelWriter excelWriter = EasyExcel.write(os)
 | 
			
		||||
            .withTemplate(templateResource.getStream())
 | 
			
		||||
            .autoCloseStream(false)
 | 
			
		||||
            // 大数值自动转换 防止失真
 | 
			
		||||
            .registerConverter(new ExcelBigNumberConvert())
 | 
			
		||||
            .build();
 | 
			
		||||
        WriteSheet writeSheet = FastExcel.writerSheet().build();
 | 
			
		||||
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
 | 
			
		||||
        if (CollUtil.isEmpty(data)) {
 | 
			
		||||
            throw new IllegalArgumentException("数据为空");
 | 
			
		||||
        }
 | 
			
		||||
        for (Map.Entry<String, Object> map : data.entrySet()) {
 | 
			
		||||
            // 设置列表后续还有数据
 | 
			
		||||
            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
 | 
			
		||||
@@ -324,7 +317,7 @@ public class ExcelUtil {
 | 
			
		||||
                // 多表导出必须使用 FillWrapper
 | 
			
		||||
                excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);
 | 
			
		||||
            } else {
 | 
			
		||||
                excelWriter.fill(map.getValue(), fillConfig, writeSheet);
 | 
			
		||||
                excelWriter.fill(map.getValue(), writeSheet);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        excelWriter.finish();
 | 
			
		||||
@@ -341,14 +334,17 @@ public class ExcelUtil {
 | 
			
		||||
     */
 | 
			
		||||
    public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String templatePath, OutputStream os) {
 | 
			
		||||
        ClassPathResource templateResource = new ClassPathResource(templatePath);
 | 
			
		||||
        ExcelWriter excelWriter = FastExcel.write(os)
 | 
			
		||||
        ExcelWriter excelWriter = EasyExcel.write(os)
 | 
			
		||||
            .withTemplate(templateResource.getStream())
 | 
			
		||||
            .autoCloseStream(false)
 | 
			
		||||
            // 大数值自动转换 防止失真
 | 
			
		||||
            .registerConverter(new ExcelBigNumberConvert())
 | 
			
		||||
            .build();
 | 
			
		||||
        if (CollUtil.isEmpty(data)) {
 | 
			
		||||
            throw new IllegalArgumentException("数据为空");
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 0; i < data.size(); i++) {
 | 
			
		||||
            WriteSheet writeSheet = FastExcel.writerSheet(i).build();
 | 
			
		||||
            WriteSheet writeSheet = EasyExcel.writerSheet(i).build();
 | 
			
		||||
            for (Map.Entry<String, Object> map : data.get(i).entrySet()) {
 | 
			
		||||
                // 设置列表后续还有数据
 | 
			
		||||
                FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,8 @@ import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 | 
			
		||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
 | 
			
		||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
 | 
			
		||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.dromara.common.json.handler.BigNumberSerializer;
 | 
			
		||||
import org.dromara.common.json.handler.CustomDateDeserializer;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
			
		||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
 | 
			
		||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
 | 
			
		||||
@@ -16,7 +15,6 @@ import java.math.BigDecimal;
 | 
			
		||||
import java.math.BigInteger;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.time.format.DateTimeFormatter;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.TimeZone;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -40,7 +38,6 @@ public class JacksonConfig {
 | 
			
		||||
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
 | 
			
		||||
            javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
 | 
			
		||||
            javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
 | 
			
		||||
            javaTimeModule.addDeserializer(Date.class, new CustomDateDeserializer());
 | 
			
		||||
            builder.modules(javaTimeModule);
 | 
			
		||||
            builder.timeZone(TimeZone.getDefault());
 | 
			
		||||
            log.info("初始化 jackson 配置");
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ public class BigNumberSerializer extends NumberSerializer {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
 | 
			
		||||
        // 超出范围 序列化为字符串
 | 
			
		||||
        // 超出范围 序列化位字符串
 | 
			
		||||
        if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
 | 
			
		||||
            super.serialize(value, gen, provider);
 | 
			
		||||
        } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,31 +0,0 @@
 | 
			
		||||
package org.dromara.common.json.handler;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.date.DateUtil;
 | 
			
		||||
import com.fasterxml.jackson.core.JsonParser;
 | 
			
		||||
import com.fasterxml.jackson.databind.DeserializationContext;
 | 
			
		||||
import com.fasterxml.jackson.databind.JsonDeserializer;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 自定义 Date 类型反序列化处理器(支持多种格式)
 | 
			
		||||
 *
 | 
			
		||||
 * @author AprilWind
 | 
			
		||||
 */
 | 
			
		||||
public class CustomDateDeserializer extends JsonDeserializer<Date> {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 反序列化逻辑:将字符串转换为 Date 对象
 | 
			
		||||
     *
 | 
			
		||||
     * @param p    JSON 解析器,用于获取字符串值
 | 
			
		||||
     * @param ctxt 上下文环境(可用于获取更多配置)
 | 
			
		||||
     * @return 转换后的 Date 对象,若为空字符串返回 null
 | 
			
		||||
     * @throws IOException 当字符串格式非法或转换失败时抛出
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
 | 
			
		||||
        return DateUtil.parse(p.getText());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user