mirror of
https://github.com/dromara/RuoYi-Vue-Plus.git
synced 2025-09-23 11:46:39 +08:00
commit
3c8d864b5f
@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
<configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||||
<deployment type="dockerfile">
|
<deployment type="dockerfile">
|
||||||
<settings>
|
<settings>
|
||||||
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.2.3" />
|
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.3.0-BETA" />
|
||||||
<option name="buildOnly" value="true" />
|
<option name="buildOnly" value="true" />
|
||||||
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
|
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
|
||||||
</settings>
|
</settings>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="演示机">
|
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||||
<deployment type="dockerfile">
|
<deployment type="dockerfile">
|
||||||
<settings>
|
<settings>
|
||||||
<option name="imageTag" value="ruoyi/ruoyi-server:5.2.3" />
|
<option name="imageTag" value="ruoyi/ruoyi-server:5.3.0-BETA" />
|
||||||
<option name="buildOnly" value="true" />
|
<option name="buildOnly" value="true" />
|
||||||
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
|
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
|
||||||
</settings>
|
</settings>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
<configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||||
<deployment type="dockerfile">
|
<deployment type="dockerfile">
|
||||||
<settings>
|
<settings>
|
||||||
<option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.2.3" />
|
<option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.3.0-BETA" />
|
||||||
<option name="buildOnly" value="true" />
|
<option name="buildOnly" value="true" />
|
||||||
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
|
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
|
||||||
</settings>
|
</settings>
|
||||||
|
16
README.md
16
README.md
@ -6,22 +6,25 @@
|
|||||||
|
|
||||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
||||||
[](https://github.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/master/LICENSE)
|
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
|
||||||
[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
|
[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
|
||||||
<br>
|
<br>
|
||||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
||||||
[]()
|
[]()
|
||||||
[]()
|
[]()
|
||||||
[]()
|
[]()
|
||||||
|
|
||||||
> RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架)
|
> Dromara RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架)
|
||||||
|
|
||||||
> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br>
|
> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br>
|
||||||
活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源
|
活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源
|
||||||
|
|
||||||
> 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
|
> 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
|
||||||
|
|
||||||
> 前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)
|
> 官方前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)<br>
|
||||||
|
> 成员前端项目地址: 基于vben [ruoyi-plus-vben](https://gitee.com/dapppp/ruoyi-plus-vben)<br>
|
||||||
|
> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)
|
||||||
|
|
||||||
> 文档地址: [plus-doc](https://plus-doc.dromara.org)
|
> 文档地址: [plus-doc](https://plus-doc.dromara.org)
|
||||||
|
|
||||||
@ -31,6 +34,7 @@ MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey <br>
|
|||||||
CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
|
CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
|
||||||
数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/ <br>
|
数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/ <br>
|
||||||
引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc <br>
|
引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc <br>
|
||||||
|
<font color="red">**启山商城系统 多租户商城源码可免费商用可二次开发 - https://www.73app.cn/** </font><br>
|
||||||
[如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group)
|
[如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group)
|
||||||
|
|
||||||
# 本框架与RuoYi的功能差异
|
# 本框架与RuoYi的功能差异
|
||||||
@ -165,8 +169,8 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
|
|||||||
|  |  |
|
|  |  |
|
||||||
|  |  |
|
|  |  |
|
||||||
|  |  |
|
|  |  |
|
||||||
|  |  |
|
|  |  |
|
||||||
|  |  |
|
|  |  |
|
||||||
|  |  |
|
|  |  |
|
||||||
|
|
||||||
|
|
||||||
|
52
pom.xml
52
pom.xml
@ -10,33 +10,33 @@
|
|||||||
|
|
||||||
<name>RuoYi-Vue-Plus</name>
|
<name>RuoYi-Vue-Plus</name>
|
||||||
<url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
|
<url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
|
||||||
<description>RuoYi-Vue-Plus多租户管理系统</description>
|
<description>Dromara RuoYi-Vue-Plus多租户管理系统</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>5.2.3</revision>
|
<revision>5.3.0-BETA</revision>
|
||||||
<spring-boot.version>3.2.11</spring-boot.version>
|
<spring-boot.version>3.4.1</spring-boot.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<java.version>17</java.version>
|
<java.version>17</java.version>
|
||||||
<mybatis.version>3.5.16</mybatis.version>
|
<mybatis.version>3.5.16</mybatis.version>
|
||||||
<springdoc.version>2.6.0</springdoc.version>
|
<springdoc.version>2.8.3</springdoc.version>
|
||||||
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
|
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
|
||||||
<easyexcel.version>4.0.3</easyexcel.version>
|
<easyexcel.version>4.0.3</easyexcel.version>
|
||||||
<velocity.version>2.3</velocity.version>
|
<velocity.version>2.3</velocity.version>
|
||||||
<satoken.version>1.39.0</satoken.version>
|
<satoken.version>1.39.0</satoken.version>
|
||||||
<mybatis-plus.version>3.5.8</mybatis-plus.version>
|
<mybatis-plus.version>3.5.10</mybatis-plus.version>
|
||||||
<p6spy.version>3.9.1</p6spy.version>
|
<p6spy.version>3.9.1</p6spy.version>
|
||||||
<hutool.version>5.8.31</hutool.version>
|
<hutool.version>5.8.35</hutool.version>
|
||||||
<spring-boot-admin.version>3.2.3</spring-boot-admin.version>
|
<spring-boot-admin.version>3.4.1</spring-boot-admin.version>
|
||||||
<redisson.version>3.37.0</redisson.version>
|
<redisson.version>3.43.0</redisson.version>
|
||||||
<lock4j.version>2.2.7</lock4j.version>
|
<lock4j.version>2.2.7</lock4j.version>
|
||||||
<dynamic-ds.version>4.3.1</dynamic-ds.version>
|
<dynamic-ds.version>4.3.1</dynamic-ds.version>
|
||||||
<snailjob.version>1.1.2</snailjob.version>
|
<snailjob.version>1.3.0-beta1.1</snailjob.version>
|
||||||
<mapstruct-plus.version>1.4.5</mapstruct-plus.version>
|
<mapstruct-plus.version>1.4.6</mapstruct-plus.version>
|
||||||
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
|
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
|
||||||
<lombok.version>1.18.34</lombok.version>
|
<lombok.version>1.18.36</lombok.version>
|
||||||
<bouncycastle.version>1.76</bouncycastle.version>
|
<bouncycastle.version>1.76</bouncycastle.version>
|
||||||
<justauth.version>1.16.6</justauth.version>
|
<justauth.version>1.16.7</justauth.version>
|
||||||
<!-- 离线IP地址定位库 -->
|
<!-- 离线IP地址定位库 -->
|
||||||
<ip2region.version>2.7.0</ip2region.version>
|
<ip2region.version>2.7.0</ip2region.version>
|
||||||
|
|
||||||
@ -48,14 +48,14 @@
|
|||||||
<!-- 限制框架中的fastjson版本 -->
|
<!-- 限制框架中的fastjson版本 -->
|
||||||
<fastjson.version>1.2.83</fastjson.version>
|
<fastjson.version>1.2.83</fastjson.version>
|
||||||
<!-- 面向运行时的D-ORM依赖 -->
|
<!-- 面向运行时的D-ORM依赖 -->
|
||||||
<anyline.version>8.7.2-20241022</anyline.version>
|
<anyline.version>8.7.2-20250101</anyline.version>
|
||||||
<!--工作流配置-->
|
<!--工作流配置-->
|
||||||
<flowable.version>7.0.1</flowable.version>
|
<warm-flow.version>1.6.0-m5</warm-flow.version>
|
||||||
|
|
||||||
<!-- 插件版本 -->
|
<!-- 插件版本 -->
|
||||||
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
|
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
|
||||||
<maven-war-plugin.version>3.2.2</maven-war-plugin.version>
|
<maven-war-plugin.version>3.2.2</maven-war-plugin.version>
|
||||||
<maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
|
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
|
||||||
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
|
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
|
||||||
<flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
|
<flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
|
||||||
</properties>
|
</properties>
|
||||||
@ -118,12 +118,16 @@
|
|||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Warm-Flow国产工作流引擎, 在线文档:http://warm-flow.cn/ -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.flowable</groupId>
|
<groupId>org.dromara.warm</groupId>
|
||||||
<artifactId>flowable-bom</artifactId>
|
<artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId>
|
||||||
<version>${flowable.version}</version>
|
<version>${warm-flow.version}</version>
|
||||||
<type>pom</type>
|
</dependency>
|
||||||
<scope>import</scope>
|
<dependency>
|
||||||
|
<groupId>org.dromara.warm</groupId>
|
||||||
|
<artifactId>warm-flow-plugin-ui-sb-web</artifactId>
|
||||||
|
<version>${warm-flow.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- JustAuth 的依赖配置-->
|
<!-- JustAuth 的依赖配置-->
|
||||||
@ -216,6 +220,12 @@
|
|||||||
<version>${mybatis-plus.version}</version>
|
<version>${mybatis-plus.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-jsqlparser</artifactId>
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
<artifactId>mybatis-plus-annotation</artifactId>
|
<artifactId>mybatis-plus-annotation</artifactId>
|
||||||
@ -369,7 +379,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>${maven-compiler-plugin.verison}</version>
|
<version>${maven-compiler-plugin.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>${java.version}</source>
|
<source>${java.version}</source>
|
||||||
<target>${java.version}</target>
|
<target>${java.version}</target>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
|
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
|
||||||
FROM bellsoft/liberica-openjdk-debian:17.0.11-cds
|
FROM bellsoft/liberica-openjdk-debian:17.0.11-cds
|
||||||
#FROM bellsoft/liberica-openjdk-debian:21.0.3-cds
|
#FROM bellsoft/liberica-openjdk-debian:21.0.5-cds
|
||||||
#FROM findepi/graalvm:java17-native
|
#FROM findepi/graalvm:java17-native
|
||||||
|
|
||||||
LABEL maintainer="Lion Li"
|
LABEL maintainer="Lion Li"
|
||||||
@ -16,6 +16,10 @@ ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
|
|||||||
EXPOSE ${SERVER_PORT}
|
EXPOSE ${SERVER_PORT}
|
||||||
|
|
||||||
ADD ./target/ruoyi-admin.jar ./app.jar
|
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} \
|
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
|
||||||
# 应用名称 如果想区分集群节点监控 改成不同的名称即可
|
# 应用名称 如果想区分集群节点监控 改成不同的名称即可
|
||||||
|
@ -2,6 +2,7 @@ package org.dromara.web.controller;
|
|||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaIgnore;
|
import cn.dev33.satoken.annotation.SaIgnore;
|
||||||
import cn.dev33.satoken.exception.NotLoginException;
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.core.codec.Base64;
|
import cn.hutool.core.codec.Base64;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
@ -12,7 +13,7 @@ import me.zhyd.oauth.model.AuthResponse;
|
|||||||
import me.zhyd.oauth.model.AuthUser;
|
import me.zhyd.oauth.model.AuthUser;
|
||||||
import me.zhyd.oauth.request.AuthRequest;
|
import me.zhyd.oauth.request.AuthRequest;
|
||||||
import me.zhyd.oauth.utils.AuthStateUtils;
|
import me.zhyd.oauth.utils.AuthStateUtils;
|
||||||
import org.dromara.common.core.constant.UserConstants;
|
import org.dromara.common.core.constant.SystemConstants;
|
||||||
import org.dromara.common.core.domain.R;
|
import org.dromara.common.core.domain.R;
|
||||||
import org.dromara.common.core.domain.model.LoginBody;
|
import org.dromara.common.core.domain.model.LoginBody;
|
||||||
import org.dromara.common.core.domain.model.RegisterBody;
|
import org.dromara.common.core.domain.model.RegisterBody;
|
||||||
@ -92,7 +93,7 @@ public class AuthController {
|
|||||||
if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
|
if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
|
||||||
log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
|
log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
|
||||||
return R.fail(MessageUtils.message("auth.grant.type.error"));
|
return R.fail(MessageUtils.message("auth.grant.type.error"));
|
||||||
} else if (!UserConstants.NORMAL.equals(client.getStatus())) {
|
} else if (!SystemConstants.NORMAL.equals(client.getStatus())) {
|
||||||
return R.fail(MessageUtils.message("auth.grant.type.blocked"));
|
return R.fail(MessageUtils.message("auth.grant.type.blocked"));
|
||||||
}
|
}
|
||||||
// 校验租户
|
// 校验租户
|
||||||
@ -111,7 +112,7 @@ public class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 第三方登录请求
|
* 获取跳转URL
|
||||||
*
|
*
|
||||||
* @param source 登录来源
|
* @param source 登录来源
|
||||||
* @return 结果
|
* @return 结果
|
||||||
@ -133,13 +134,15 @@ public class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 第三方登录回调业务处理 绑定授权
|
* 前端回调绑定授权(需要token)
|
||||||
*
|
*
|
||||||
* @param loginBody 请求体
|
* @param loginBody 请求体
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@PostMapping("/social/callback")
|
@PostMapping("/social/callback")
|
||||||
public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
|
public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
|
||||||
|
// 校验token
|
||||||
|
StpUtil.checkLogin();
|
||||||
// 获取第三方登录信息
|
// 获取第三方登录信息
|
||||||
AuthResponse<AuthUser> response = SocialUtils.loginAuth(
|
AuthResponse<AuthUser> response = SocialUtils.loginAuth(
|
||||||
loginBody.getSource(), loginBody.getSocialCode(),
|
loginBody.getSource(), loginBody.getSocialCode(),
|
||||||
@ -155,12 +158,14 @@ public class AuthController {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消授权
|
* 取消授权(需要token)
|
||||||
*
|
*
|
||||||
* @param socialId socialId
|
* @param socialId socialId
|
||||||
*/
|
*/
|
||||||
@DeleteMapping(value = "/unlock/{socialId}")
|
@DeleteMapping(value = "/unlock/{socialId}")
|
||||||
public R<Void> unlockSocial(@PathVariable Long socialId) {
|
public R<Void> unlockSocial(@PathVariable Long socialId) {
|
||||||
|
// 校验token
|
||||||
|
StpUtil.checkLogin();
|
||||||
Boolean rows = socialUserService.deleteWithValidById(socialId);
|
Boolean rows = socialUserService.deleteWithValidById(socialId);
|
||||||
return rows ? R.ok() : R.fail("取消授权失败");
|
return rows ? R.ok() : R.fail("取消授权失败");
|
||||||
}
|
}
|
||||||
@ -226,7 +231,7 @@ public class AuthController {
|
|||||||
}
|
}
|
||||||
// 根据域名进行筛选
|
// 根据域名进行筛选
|
||||||
List<TenantListVo> list = StreamUtils.filter(voList, vo ->
|
List<TenantListVo> list = StreamUtils.filter(voList, vo ->
|
||||||
StringUtils.equals(vo.getDomain(), host));
|
StringUtils.equalsIgnoreCase(vo.getDomain(), host));
|
||||||
result.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
|
result.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
|
||||||
return R.ok(result);
|
return R.ok(result);
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,12 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import me.zhyd.oauth.model.AuthUser;
|
import me.zhyd.oauth.model.AuthUser;
|
||||||
import org.dromara.common.core.constant.CacheConstants;
|
import org.dromara.common.core.constant.CacheConstants;
|
||||||
import org.dromara.common.core.constant.Constants;
|
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.constant.TenantConstants;
|
||||||
|
import org.dromara.common.core.domain.dto.PostDTO;
|
||||||
import org.dromara.common.core.domain.dto.RoleDTO;
|
import org.dromara.common.core.domain.dto.RoleDTO;
|
||||||
import org.dromara.common.core.domain.model.LoginUser;
|
import org.dromara.common.core.domain.model.LoginUser;
|
||||||
import org.dromara.common.core.enums.LoginType;
|
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.ServiceException;
|
||||||
import org.dromara.common.core.exception.user.UserException;
|
import org.dromara.common.core.exception.user.UserException;
|
||||||
import org.dromara.common.core.utils.*;
|
import org.dromara.common.core.utils.*;
|
||||||
@ -60,6 +61,7 @@ public class SysLoginService {
|
|||||||
private final ISysSocialService sysSocialService;
|
private final ISysSocialService sysSocialService;
|
||||||
private final ISysRoleService roleService;
|
private final ISysRoleService roleService;
|
||||||
private final ISysDeptService deptService;
|
private final ISysDeptService deptService;
|
||||||
|
private final ISysPostService postService;
|
||||||
private final SysUserMapper userMapper;
|
private final SysUserMapper userMapper;
|
||||||
|
|
||||||
|
|
||||||
@ -148,21 +150,24 @@ public class SysLoginService {
|
|||||||
*/
|
*/
|
||||||
public LoginUser buildLoginUser(SysUserVo user) {
|
public LoginUser buildLoginUser(SysUserVo user) {
|
||||||
LoginUser loginUser = new LoginUser();
|
LoginUser loginUser = new LoginUser();
|
||||||
|
Long userId = user.getUserId();
|
||||||
loginUser.setTenantId(user.getTenantId());
|
loginUser.setTenantId(user.getTenantId());
|
||||||
loginUser.setUserId(user.getUserId());
|
loginUser.setUserId(userId);
|
||||||
loginUser.setDeptId(user.getDeptId());
|
loginUser.setDeptId(user.getDeptId());
|
||||||
loginUser.setUsername(user.getUserName());
|
loginUser.setUsername(user.getUserName());
|
||||||
loginUser.setNickname(user.getNickName());
|
loginUser.setNickname(user.getNickName());
|
||||||
loginUser.setUserType(user.getUserType());
|
loginUser.setUserType(user.getUserType());
|
||||||
loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId()));
|
loginUser.setMenuPermission(permissionService.getMenuPermission(userId));
|
||||||
loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId()));
|
loginUser.setRolePermission(permissionService.getRolePermission(userId));
|
||||||
if (ObjectUtil.isNotNull(user.getDeptId())) {
|
if (ObjectUtil.isNotNull(user.getDeptId())) {
|
||||||
Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById);
|
Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById);
|
||||||
loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY));
|
loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY));
|
||||||
loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY));
|
loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY));
|
||||||
}
|
}
|
||||||
List<SysRoleVo> roles = roleService.selectRolesByUserId(user.getUserId());
|
List<SysRoleVo> roles = roleService.selectRolesByUserId(userId);
|
||||||
|
List<SysPostVo> posts = postService.selectPostsByUserId(userId);
|
||||||
loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
|
loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
|
||||||
|
loginUser.setPosts(BeanUtil.copyToList(posts, PostDTO.class));
|
||||||
return loginUser;
|
return loginUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,17 +228,17 @@ public class SysLoginService {
|
|||||||
if (!TenantHelper.isEnable()) {
|
if (!TenantHelper.isEnable()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (StringUtils.isBlank(tenantId)) {
|
if (StringUtils.isBlank(tenantId)) {
|
||||||
throw new TenantException("tenant.number.not.blank");
|
throw new TenantException("tenant.number.not.blank");
|
||||||
}
|
}
|
||||||
|
if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
SysTenantVo tenant = tenantService.queryByTenantId(tenantId);
|
SysTenantVo tenant = tenantService.queryByTenantId(tenantId);
|
||||||
if (ObjectUtil.isNull(tenant)) {
|
if (ObjectUtil.isNull(tenant)) {
|
||||||
log.info("登录租户:{} 不存在.", tenantId);
|
log.info("登录租户:{} 不存在.", tenantId);
|
||||||
throw new TenantException("tenant.not.exists");
|
throw new TenantException("tenant.not.exists");
|
||||||
} else if (TenantStatus.DISABLE.getCode().equals(tenant.getStatus())) {
|
} else if (SystemConstants.DISABLE.equals(tenant.getStatus())) {
|
||||||
log.info("登录租户:{} 已被停用.", tenantId);
|
log.info("登录租户:{} 已被停用.", tenantId);
|
||||||
throw new TenantException("tenant.blocked");
|
throw new TenantException("tenant.blocked");
|
||||||
} else if (ObjectUtil.isNotNull(tenant.getExpireTime())
|
} else if (ObjectUtil.isNotNull(tenant.getExpireTime())
|
||||||
|
@ -84,11 +84,11 @@ public class SysRegisterService {
|
|||||||
String captcha = RedisUtils.getCacheObject(verifyKey);
|
String captcha = RedisUtils.getCacheObject(verifyKey);
|
||||||
RedisUtils.deleteObject(verifyKey);
|
RedisUtils.deleteObject(verifyKey);
|
||||||
if (captcha == null) {
|
if (captcha == null) {
|
||||||
recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire"));
|
recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
||||||
throw new CaptchaExpireException();
|
throw new CaptchaExpireException();
|
||||||
}
|
}
|
||||||
if (!code.equalsIgnoreCase(captcha)) {
|
if (!code.equalsIgnoreCase(captcha)) {
|
||||||
recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error"));
|
recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
|
||||||
throw new CaptchaException();
|
throw new CaptchaException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,10 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.common.core.constant.Constants;
|
import org.dromara.common.core.constant.Constants;
|
||||||
import org.dromara.common.core.constant.GlobalConstants;
|
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.EmailLoginBody;
|
||||||
import org.dromara.common.core.domain.model.LoginUser;
|
import org.dromara.common.core.domain.model.LoginUser;
|
||||||
import org.dromara.common.core.enums.LoginType;
|
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.CaptchaExpireException;
|
||||||
import org.dromara.common.core.exception.user.UserException;
|
import org.dromara.common.core.exception.user.UserException;
|
||||||
import org.dromara.common.core.utils.MessageUtils;
|
import org.dromara.common.core.utils.MessageUtils;
|
||||||
@ -92,7 +92,7 @@ public class EmailAuthStrategy implements IAuthStrategy {
|
|||||||
if (ObjectUtil.isNull(user)) {
|
if (ObjectUtil.isNull(user)) {
|
||||||
log.info("登录用户:{} 不存在.", email);
|
log.info("登录用户:{} 不存在.", email);
|
||||||
throw new UserException("user.not.exists", email);
|
throw new UserException("user.not.exists", email);
|
||||||
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
} else if (SystemConstants.DISABLE.equals(user.getStatus())) {
|
||||||
log.info("登录用户:{} 已被停用.", email);
|
log.info("登录用户:{} 已被停用.", email);
|
||||||
throw new UserException("user.blocked", email);
|
throw new UserException("user.blocked", email);
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,10 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.common.core.constant.Constants;
|
import org.dromara.common.core.constant.Constants;
|
||||||
import org.dromara.common.core.constant.GlobalConstants;
|
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.LoginUser;
|
||||||
import org.dromara.common.core.domain.model.PasswordLoginBody;
|
import org.dromara.common.core.domain.model.PasswordLoginBody;
|
||||||
import org.dromara.common.core.enums.LoginType;
|
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.CaptchaException;
|
||||||
import org.dromara.common.core.exception.user.CaptchaExpireException;
|
import org.dromara.common.core.exception.user.CaptchaExpireException;
|
||||||
import org.dromara.common.core.exception.user.UserException;
|
import org.dromara.common.core.exception.user.UserException;
|
||||||
@ -113,7 +113,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
|
|||||||
if (ObjectUtil.isNull(user)) {
|
if (ObjectUtil.isNull(user)) {
|
||||||
log.info("登录用户:{} 不存在.", username);
|
log.info("登录用户:{} 不存在.", username);
|
||||||
throw new UserException("user.not.exists", username);
|
throw new UserException("user.not.exists", username);
|
||||||
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
} else if (SystemConstants.DISABLE.equals(user.getStatus())) {
|
||||||
log.info("登录用户:{} 已被停用.", username);
|
log.info("登录用户:{} 已被停用.", username);
|
||||||
throw new UserException("user.blocked", username);
|
throw new UserException("user.blocked", username);
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,10 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.common.core.constant.Constants;
|
import org.dromara.common.core.constant.Constants;
|
||||||
import org.dromara.common.core.constant.GlobalConstants;
|
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.LoginUser;
|
||||||
import org.dromara.common.core.domain.model.SmsLoginBody;
|
import org.dromara.common.core.domain.model.SmsLoginBody;
|
||||||
import org.dromara.common.core.enums.LoginType;
|
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.CaptchaExpireException;
|
||||||
import org.dromara.common.core.exception.user.UserException;
|
import org.dromara.common.core.exception.user.UserException;
|
||||||
import org.dromara.common.core.utils.MessageUtils;
|
import org.dromara.common.core.utils.MessageUtils;
|
||||||
@ -92,7 +92,7 @@ public class SmsAuthStrategy implements IAuthStrategy {
|
|||||||
if (ObjectUtil.isNull(user)) {
|
if (ObjectUtil.isNull(user)) {
|
||||||
log.info("登录用户:{} 不存在.", phonenumber);
|
log.info("登录用户:{} 不存在.", phonenumber);
|
||||||
throw new UserException("user.not.exists", phonenumber);
|
throw new UserException("user.not.exists", phonenumber);
|
||||||
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
} else if (SystemConstants.DISABLE.equals(user.getStatus())) {
|
||||||
log.info("登录用户:{} 已被停用.", phonenumber);
|
log.info("登录用户:{} 已被停用.", phonenumber);
|
||||||
throw new UserException("user.blocked", phonenumber);
|
throw new UserException("user.blocked", phonenumber);
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,9 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import me.zhyd.oauth.model.AuthResponse;
|
import me.zhyd.oauth.model.AuthResponse;
|
||||||
import me.zhyd.oauth.model.AuthUser;
|
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.LoginUser;
|
||||||
import org.dromara.common.core.domain.model.SocialLoginBody;
|
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.ServiceException;
|
||||||
import org.dromara.common.core.exception.user.UserException;
|
import org.dromara.common.core.exception.user.UserException;
|
||||||
import org.dromara.common.core.utils.StreamUtils;
|
import org.dromara.common.core.utils.StreamUtils;
|
||||||
@ -121,7 +121,7 @@ public class SocialAuthStrategy implements IAuthStrategy {
|
|||||||
if (ObjectUtil.isNull(user)) {
|
if (ObjectUtil.isNull(user)) {
|
||||||
log.info("登录用户:{} 不存在.", "");
|
log.info("登录用户:{} 不存在.", "");
|
||||||
throw new UserException("user.not.exists", "");
|
throw new UserException("user.not.exists", "");
|
||||||
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
} else if (SystemConstants.DISABLE.equals(user.getStatus())) {
|
||||||
log.info("登录用户:{} 已被停用.", "");
|
log.info("登录用户:{} 已被停用.", "");
|
||||||
throw new UserException("user.blocked", "");
|
throw new UserException("user.blocked", "");
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,20 @@ import cn.dev33.satoken.stp.StpUtil;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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.XcxLoginBody;
|
||||||
import org.dromara.common.core.domain.model.XcxLoginUser;
|
import org.dromara.common.core.domain.model.XcxLoginUser;
|
||||||
import org.dromara.common.core.enums.UserStatus;
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
import org.dromara.common.core.utils.ValidatorUtils;
|
import org.dromara.common.core.utils.ValidatorUtils;
|
||||||
import org.dromara.common.json.utils.JsonUtils;
|
import org.dromara.common.json.utils.JsonUtils;
|
||||||
import org.dromara.common.satoken.utils.LoginHelper;
|
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.SysClientVo;
|
||||||
import org.dromara.system.domain.vo.SysUserVo;
|
import org.dromara.system.domain.vo.SysUserVo;
|
||||||
import org.dromara.web.domain.vo.LoginVo;
|
import org.dromara.web.domain.vo.LoginVo;
|
||||||
@ -40,12 +47,24 @@ public class XcxAuthStrategy implements IAuthStrategy {
|
|||||||
// 多个小程序识别使用
|
// 多个小程序识别使用
|
||||||
String appid = loginBody.getAppid();
|
String appid = loginBody.getAppid();
|
||||||
|
|
||||||
// todo 以下自行实现
|
|
||||||
// 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
|
// 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
|
||||||
String 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());
|
||||||
|
}
|
||||||
// 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
|
// 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
|
||||||
SysUserVo user = loadUserByOpenid(openid);
|
SysUserVo user = loadUserByOpenid(openid);
|
||||||
|
|
||||||
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
|
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
|
||||||
XcxLoginUser loginUser = new XcxLoginUser();
|
XcxLoginUser loginUser = new XcxLoginUser();
|
||||||
loginUser.setTenantId(user.getTenantId());
|
loginUser.setTenantId(user.getTenantId());
|
||||||
@ -82,7 +101,7 @@ public class XcxAuthStrategy implements IAuthStrategy {
|
|||||||
if (ObjectUtil.isNull(user)) {
|
if (ObjectUtil.isNull(user)) {
|
||||||
log.info("登录用户:{} 不存在.", openid);
|
log.info("登录用户:{} 不存在.", openid);
|
||||||
// todo 用户不存在 业务逻辑自行实现
|
// todo 用户不存在 业务逻辑自行实现
|
||||||
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
} else if (SystemConstants.DISABLE.equals(user.getStatus())) {
|
||||||
log.info("登录用户:{} 已被停用.", openid);
|
log.info("登录用户:{} 已被停用.", openid);
|
||||||
// todo 用户已被停用 业务逻辑自行实现
|
// todo 用户已被停用 业务逻辑自行实现
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
spring.boot.admin.client:
|
spring.boot.admin.client:
|
||||||
# 增加客户端开关
|
# 增加客户端开关
|
||||||
enabled: true
|
enabled: true
|
||||||
url: http://localhost:9090/admin
|
url: http://localhost:9090
|
||||||
instance:
|
instance:
|
||||||
service-host-type: IP
|
service-host-type: IP
|
||||||
metadata:
|
metadata:
|
||||||
@ -16,17 +16,19 @@ snail-job:
|
|||||||
enabled: true
|
enabled: true
|
||||||
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
|
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
|
||||||
group: "ruoyi_group"
|
group: "ruoyi_group"
|
||||||
# SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表
|
# SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表
|
||||||
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
|
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
|
||||||
server:
|
server:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 17888
|
port: 17888
|
||||||
# 详见 script/sql/snail_job.sql `sj_namespace` 表
|
# 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
|
||||||
namespace: ${spring.profiles.active}
|
namespace: ${spring.profiles.active}
|
||||||
# 随主应用端口飘逸
|
# 随主应用端口漂移
|
||||||
port: 2${server.port}
|
port: 2${server.port}
|
||||||
# 客户端ip指定
|
# 客户端ip指定
|
||||||
host:
|
host:
|
||||||
|
# RPC类型: netty, grpc
|
||||||
|
rpc-type: grpc
|
||||||
|
|
||||||
--- # 数据源配置
|
--- # 数据源配置
|
||||||
spring:
|
spring:
|
||||||
@ -50,14 +52,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
|
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
|
username: root
|
||||||
password: root
|
password: root
|
||||||
# 从库数据源
|
# # 从库数据源
|
||||||
slave:
|
# slave:
|
||||||
lazy: true
|
# lazy: true
|
||||||
type: ${spring.datasource.type}
|
# type: ${spring.datasource.type}
|
||||||
driverClassName: com.mysql.cj.jdbc.Driver
|
# 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
|
# 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:
|
# username:
|
||||||
password:
|
# password:
|
||||||
# oracle:
|
# oracle:
|
||||||
# type: ${spring.datasource.type}
|
# type: ${spring.datasource.type}
|
||||||
# driverClassName: oracle.jdbc.OracleDriver
|
# driverClassName: oracle.jdbc.OracleDriver
|
||||||
@ -200,7 +202,7 @@ justauth:
|
|||||||
redirect-uri: ${justauth.address}/social-callback?source=maxkey
|
redirect-uri: ${justauth.address}/social-callback?source=maxkey
|
||||||
topiam:
|
topiam:
|
||||||
# topiam 服务器地址
|
# topiam 服务器地址
|
||||||
server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
|
server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol
|
||||||
client-id: 449c4*********937************759
|
client-id: 449c4*********937************759
|
||||||
client-secret: ac7***********1e0************28d
|
client-secret: ac7***********1e0************28d
|
||||||
redirect-uri: ${justauth.address}/social-callback?source=topiam
|
redirect-uri: ${justauth.address}/social-callback?source=topiam
|
||||||
|
@ -5,7 +5,7 @@ spring.servlet.multipart.location: /ruoyi/server/temp
|
|||||||
spring.boot.admin.client:
|
spring.boot.admin.client:
|
||||||
# 增加客户端开关
|
# 增加客户端开关
|
||||||
enabled: true
|
enabled: true
|
||||||
url: http://localhost:9090/admin
|
url: http://localhost:9090
|
||||||
instance:
|
instance:
|
||||||
service-host-type: IP
|
service-host-type: IP
|
||||||
metadata:
|
metadata:
|
||||||
@ -19,17 +19,19 @@ snail-job:
|
|||||||
enabled: true
|
enabled: true
|
||||||
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
|
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
|
||||||
group: "ruoyi_group"
|
group: "ruoyi_group"
|
||||||
# SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表
|
# SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表
|
||||||
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
|
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
|
||||||
server:
|
server:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 17888
|
port: 17888
|
||||||
# 详见 script/sql/snail_job.sql `sj_namespace` 表
|
# 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
|
||||||
namespace: ${spring.profiles.active}
|
namespace: ${spring.profiles.active}
|
||||||
# 随主应用端口飘逸
|
# 随主应用端口漂移
|
||||||
port: 2${server.port}
|
port: 2${server.port}
|
||||||
# 客户端ip指定
|
# 客户端ip指定
|
||||||
host:
|
host:
|
||||||
|
# RPC类型: netty, grpc
|
||||||
|
rpc-type: grpc
|
||||||
|
|
||||||
--- # 数据源配置
|
--- # 数据源配置
|
||||||
spring:
|
spring:
|
||||||
@ -53,14 +55,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
|
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
|
username: root
|
||||||
password: root
|
password: root
|
||||||
# 从库数据源
|
# # 从库数据源
|
||||||
slave:
|
# slave:
|
||||||
lazy: true
|
# lazy: true
|
||||||
type: ${spring.datasource.type}
|
# type: ${spring.datasource.type}
|
||||||
driverClassName: com.mysql.cj.jdbc.Driver
|
# 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
|
# 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:
|
# username:
|
||||||
password:
|
# password:
|
||||||
# oracle:
|
# oracle:
|
||||||
# type: ${spring.datasource.type}
|
# type: ${spring.datasource.type}
|
||||||
# driverClassName: oracle.jdbc.OracleDriver
|
# driverClassName: oracle.jdbc.OracleDriver
|
||||||
|
@ -47,6 +47,7 @@ logging:
|
|||||||
org.dromara: @logging.level@
|
org.dromara: @logging.level@
|
||||||
org.springframework: warn
|
org.springframework: warn
|
||||||
org.mybatis.spring.mapper: error
|
org.mybatis.spring.mapper: error
|
||||||
|
org.apache.fury: warn
|
||||||
config: classpath:logback-plus.xml
|
config: classpath:logback-plus.xml
|
||||||
|
|
||||||
# 用户配置
|
# 用户配置
|
||||||
@ -110,17 +111,15 @@ sa-token:
|
|||||||
security:
|
security:
|
||||||
# 排除路径
|
# 排除路径
|
||||||
excludes:
|
excludes:
|
||||||
# 静态资源
|
|
||||||
- /*.html
|
- /*.html
|
||||||
- /**/*.html
|
- /**/*.html
|
||||||
- /**/*.css
|
- /**/*.css
|
||||||
- /**/*.js
|
- /**/*.js
|
||||||
# 公共路径
|
|
||||||
- /favicon.ico
|
- /favicon.ico
|
||||||
- /error
|
- /error
|
||||||
# swagger 文档配置
|
|
||||||
- /*/api-docs
|
- /*/api-docs
|
||||||
- /*/api-docs/**
|
- /*/api-docs/**
|
||||||
|
- /warm-flow-ui/token-name
|
||||||
|
|
||||||
# 多租户配置
|
# 多租户配置
|
||||||
tenant:
|
tenant:
|
||||||
@ -141,6 +140,8 @@ tenant:
|
|||||||
# MyBatisPlus配置
|
# MyBatisPlus配置
|
||||||
# https://baomidou.com/config/
|
# https://baomidou.com/config/
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
|
# 自定义配置 是否全局开启逻辑删除 关闭后 所有逻辑删除功能将失效
|
||||||
|
enableLogicDelete: true
|
||||||
# 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper
|
# 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper
|
||||||
mapperPackage: org.dromara.**.mapper
|
mapperPackage: org.dromara.**.mapper
|
||||||
# 对应的 XML 文件位置
|
# 对应的 XML 文件位置
|
||||||
@ -217,6 +218,8 @@ springdoc:
|
|||||||
packages-to-scan: org.dromara.system
|
packages-to-scan: org.dromara.system
|
||||||
- group: 4.代码生成模块
|
- group: 4.代码生成模块
|
||||||
packages-to-scan: org.dromara.generator
|
packages-to-scan: org.dromara.generator
|
||||||
|
- group: 5.工作流模块
|
||||||
|
packages-to-scan: org.dromara.workflow
|
||||||
|
|
||||||
# 防止XSS攻击
|
# 防止XSS攻击
|
||||||
xss:
|
xss:
|
||||||
@ -225,8 +228,7 @@ xss:
|
|||||||
# 排除链接(多个用逗号分隔)
|
# 排除链接(多个用逗号分隔)
|
||||||
excludeUrls:
|
excludeUrls:
|
||||||
- /system/notice
|
- /system/notice
|
||||||
- /workflow/model/save
|
- /warm-flow/save-xml
|
||||||
- /workflow/model/editModelXml
|
|
||||||
|
|
||||||
# 全局线程池相关配置
|
# 全局线程池相关配置
|
||||||
# 如使用JDK21请直接使用虚拟线程 不要开启此配置
|
# 如使用JDK21请直接使用虚拟线程 不要开启此配置
|
||||||
@ -271,24 +273,11 @@ websocket:
|
|||||||
# 设置访问源地址
|
# 设置访问源地址
|
||||||
allowedOrigins: '*'
|
allowedOrigins: '*'
|
||||||
|
|
||||||
--- #flowable配置
|
--- # warm-flow工作流配置
|
||||||
flowable:
|
warm-flow:
|
||||||
# 开关 用于启动/停用工作流
|
# 是否开启工作流,默认true
|
||||||
enabled: true
|
enabled: true
|
||||||
process.enabled: ${flowable.enabled}
|
# 是否开启设计器ui
|
||||||
eventregistry.enabled: ${flowable.enabled}
|
ui: true
|
||||||
async-executor-activate: false #关闭定时任务JOB
|
# 默认Authorization,如果有多个token,用逗号分隔
|
||||||
# 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
|
token-name: ${sa-token.token-name},clientid
|
||||||
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>
|
<configuration>
|
||||||
<property name="log.path" value="./logs"/>
|
<property name="log.path" value="./logs"/>
|
||||||
<property name="console.log.pattern"
|
<property name="console.log.pattern"
|
||||||
value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
|
value="%cyan(%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"/>
|
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
|
||||||
|
|
||||||
<!-- 控制台输出 -->
|
<!-- 控制台输出 -->
|
||||||
|
1
ruoyi-admin/zhFonts/.uuid
Normal file
1
ruoyi-admin/zhFonts/.uuid
Normal file
@ -0,0 +1 @@
|
|||||||
|
3f2ee348-0303-40ca-bf03-03f48d2d2141
|
BIN
ruoyi-admin/zhFonts/SIMSUN.TTC
Normal file
BIN
ruoyi-admin/zhFonts/SIMSUN.TTC
Normal file
Binary file not shown.
4
ruoyi-admin/zhFonts/fonts.dir
Normal file
4
ruoyi-admin/zhFonts/fonts.dir
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
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
|
4
ruoyi-admin/zhFonts/fonts.scale
Normal file
4
ruoyi-admin/zhFonts/fonts.scale
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
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>
|
</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>5.2.3</revision>
|
<revision>5.3.0-BETA</revision>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
@ -4,11 +4,13 @@ import jakarta.annotation.PreDestroy;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||||
import org.dromara.common.core.config.properties.ThreadPoolProperties;
|
import org.dromara.common.core.config.properties.ThreadPoolProperties;
|
||||||
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
import org.dromara.common.core.utils.Threads;
|
import org.dromara.common.core.utils.Threads;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.core.task.VirtualThreadTaskExecutor;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
@ -49,8 +51,15 @@ public class ThreadPoolConfig {
|
|||||||
*/
|
*/
|
||||||
@Bean(name = "scheduledExecutorService")
|
@Bean(name = "scheduledExecutorService")
|
||||||
protected ScheduledExecutorService 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,
|
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core,
|
||||||
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
|
builder.build(),
|
||||||
new ThreadPoolExecutor.CallerRunsPolicy()) {
|
new ThreadPoolExecutor.CallerRunsPolicy()) {
|
||||||
@Override
|
@Override
|
||||||
protected void afterExecute(Runnable r, Throwable t) {
|
protected void afterExecute(Runnable r, Throwable t) {
|
||||||
|
@ -60,6 +60,16 @@ public interface CacheNames {
|
|||||||
*/
|
*/
|
||||||
String SYS_OSS = "sys_oss#30d";
|
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配置
|
* OSS配置
|
||||||
*/
|
*/
|
||||||
|
@ -68,12 +68,7 @@ public interface Constants {
|
|||||||
Integer CAPTCHA_EXPIRATION = 2;
|
Integer CAPTCHA_EXPIRATION = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 令牌
|
* 顶级父级id
|
||||||
*/
|
|
||||||
String TOKEN = "token";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 顶级部门id
|
|
||||||
*/
|
*/
|
||||||
Long TOP_PARENT_ID = 0L;
|
Long TOP_PARENT_ID = 0L;
|
||||||
|
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
}
|
@ -7,16 +7,6 @@ package org.dromara.common.core.constant;
|
|||||||
*/
|
*/
|
||||||
public interface TenantConstants {
|
public interface TenantConstants {
|
||||||
|
|
||||||
/**
|
|
||||||
* 租户正常状态
|
|
||||||
*/
|
|
||||||
String NORMAL = "0";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 租户封禁状态
|
|
||||||
*/
|
|
||||||
String DISABLE = "1";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 超级管理员ID
|
* 超级管理员ID
|
||||||
*/
|
*/
|
||||||
|
@ -1,152 +0,0 @@
|
|||||||
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;
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,71 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package org.dromara.common.core.domain.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抄送
|
||||||
|
*
|
||||||
|
* @author may
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class FlowCopyDTO implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名称
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
}
|
@ -10,7 +10,6 @@ import java.io.Serializable;
|
|||||||
*
|
*
|
||||||
* @author may
|
* @author may
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class ProcessEvent implements Serializable {
|
public class ProcessEvent implements Serializable {
|
||||||
|
|
||||||
@ -18,14 +17,19 @@ public class ProcessEvent implements Serializable {
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程定义key
|
* 租户ID
|
||||||
*/
|
*/
|
||||||
private String key;
|
private String tenantId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程定义编码
|
||||||
|
*/
|
||||||
|
private String flowCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 业务id
|
* 业务id
|
||||||
*/
|
*/
|
||||||
private String businessKey;
|
private String businessId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 状态
|
* 状态
|
||||||
@ -37,5 +41,4 @@ public class ProcessEvent implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private boolean submit;
|
private boolean submit;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import java.io.Serializable;
|
|||||||
*
|
*
|
||||||
* @author may
|
* @author may
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class ProcessTaskEvent implements Serializable {
|
public class ProcessTaskEvent implements Serializable {
|
||||||
|
|
||||||
@ -18,23 +17,28 @@ public class ProcessTaskEvent implements Serializable {
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程定义key
|
* 租户ID
|
||||||
*/
|
*/
|
||||||
private String key;
|
private String tenantId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 审批节点key
|
* 流程定义编码
|
||||||
*/
|
*/
|
||||||
private String taskDefinitionKey;
|
private String flowCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审批节点编码
|
||||||
|
*/
|
||||||
|
private String nodeCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任务id
|
* 任务id
|
||||||
*/
|
*/
|
||||||
private String taskId;
|
private Long taskId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 业务id
|
* 业务id
|
||||||
*/
|
*/
|
||||||
private String businessKey;
|
private String businessId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package org.dromara.common.core.domain.model;
|
package org.dromara.common.core.domain.model;
|
||||||
|
|
||||||
import org.dromara.common.core.domain.dto.RoleDTO;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
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.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@ -111,6 +112,11 @@ public class LoginUser implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private List<RoleDTO> roles;
|
private List<RoleDTO> roles;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 岗位对象
|
||||||
|
*/
|
||||||
|
private List<PostDTO> posts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据权限 当前角色ID
|
* 数据权限 当前角色ID
|
||||||
*/
|
*/
|
||||||
|
@ -5,8 +5,6 @@ import lombok.Data;
|
|||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import org.hibernate.validator.constraints.Length;
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
import static org.dromara.common.core.constant.UserConstants.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 密码登录对象
|
* 密码登录对象
|
||||||
*
|
*
|
||||||
@ -20,14 +18,14 @@ public class PasswordLoginBody extends LoginBody {
|
|||||||
* 用户名
|
* 用户名
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "{user.username.not.blank}")
|
@NotBlank(message = "{user.username.not.blank}")
|
||||||
@Length(min = USERNAME_MIN_LENGTH, max = USERNAME_MAX_LENGTH, message = "{user.username.length.valid}")
|
@Length(min = 2, max = 20, message = "{user.username.length.valid}")
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户密码
|
* 用户密码
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "{user.password.not.blank}")
|
@NotBlank(message = "{user.password.not.blank}")
|
||||||
@Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
|
@Length(min = 5, max = 20, message = "{user.password.length.valid}")
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,6 @@ import lombok.Data;
|
|||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import org.hibernate.validator.constraints.Length;
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
import static org.dromara.common.core.constant.UserConstants.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户注册对象
|
* 用户注册对象
|
||||||
*
|
*
|
||||||
@ -20,14 +18,14 @@ public class RegisterBody extends LoginBody {
|
|||||||
* 用户名
|
* 用户名
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "{user.username.not.blank}")
|
@NotBlank(message = "{user.username.not.blank}")
|
||||||
@Length(min = USERNAME_MIN_LENGTH, max = USERNAME_MAX_LENGTH, message = "{user.username.length.valid}")
|
@Length(min = 2, max = 20, message = "{user.username.length.valid}")
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户密码
|
* 用户密码
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "{user.password.not.blank}")
|
@NotBlank(message = "{user.password.not.blank}")
|
||||||
@Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
|
@Length(min = 5, max = 20, message = "{user.password.length.valid}")
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
private String userType;
|
private String userType;
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
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,6 +7,10 @@ import org.dromara.common.core.exception.ServiceException;
|
|||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 业务状态枚举
|
* 业务状态枚举
|
||||||
@ -16,30 +20,37 @@ import java.util.Arrays;
|
|||||||
@Getter
|
@Getter
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public enum BusinessStatusEnum {
|
public enum BusinessStatusEnum {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已撤销
|
* 已撤销
|
||||||
*/
|
*/
|
||||||
CANCEL("cancel", "已撤销"),
|
CANCEL("cancel", "已撤销"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 草稿
|
* 草稿
|
||||||
*/
|
*/
|
||||||
DRAFT("draft", "草稿"),
|
DRAFT("draft", "草稿"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 待审核
|
* 待审核
|
||||||
*/
|
*/
|
||||||
WAITING("waiting", "待审核"),
|
WAITING("waiting", "待审核"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已完成
|
* 已完成
|
||||||
*/
|
*/
|
||||||
FINISH("finish", "已完成"),
|
FINISH("finish", "已完成"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已作废
|
* 已作废
|
||||||
*/
|
*/
|
||||||
INVALID("invalid", "已作废"),
|
INVALID("invalid", "已作废"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已退回
|
* 已退回
|
||||||
*/
|
*/
|
||||||
BACK("back", "已退回"),
|
BACK("back", "已退回"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已终止
|
* 已终止
|
||||||
*/
|
*/
|
||||||
@ -55,20 +66,72 @@ public enum BusinessStatusEnum {
|
|||||||
*/
|
*/
|
||||||
private final String desc;
|
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 状态
|
* @param status 业务状态码
|
||||||
|
* @return 对应的 BusinessStatusEnum 枚举,如果找不到则返回 null
|
||||||
|
*/
|
||||||
|
public static BusinessStatusEnum getByStatus(String status) {
|
||||||
|
// 使用 STATUS_MAP 获取对应的枚举,若找不到则返回 null
|
||||||
|
return STATUS_MAP.get(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据状态获取对应的业务状态描述信息
|
||||||
|
*
|
||||||
|
* @param status 业务状态码
|
||||||
|
* @return 返回业务状态描述,若状态码为空或未找到对应的枚举,返回空字符串
|
||||||
*/
|
*/
|
||||||
public static String findByStatus(String status) {
|
public static String findByStatus(String status) {
|
||||||
if (StringUtils.isBlank(status)) {
|
if (StringUtils.isBlank(status)) {
|
||||||
return StrUtil.EMPTY;
|
return StrUtil.EMPTY;
|
||||||
}
|
}
|
||||||
return Arrays.stream(BusinessStatusEnum.values())
|
BusinessStatusEnum statusEnum = STATUS_MAP.get(status);
|
||||||
.filter(statusEnum -> statusEnum.getStatus().equals(status))
|
return (statusEnum != null) ? statusEnum.getDesc() : StrUtil.EMPTY;
|
||||||
.findFirst()
|
}
|
||||||
.map(BusinessStatusEnum::getDesc)
|
|
||||||
.orElse(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,5 +211,5 @@ public enum BusinessStatusEnum {
|
|||||||
throw new ServiceException("流程状态为空!");
|
throw new ServiceException("流程状态为空!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -0,0 +1,146 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
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,5 +1,9 @@
|
|||||||
package org.dromara.common.core.service;
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
import org.dromara.common.core.domain.dto.DeptDTO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用 部门服务
|
* 通用 部门服务
|
||||||
*
|
*
|
||||||
@ -15,4 +19,19 @@ public interface DeptService {
|
|||||||
*/
|
*/
|
||||||
String selectDeptNameByIds(String deptIds);
|
String selectDeptNameByIds(String deptIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据部门ID查询部门负责人
|
||||||
|
*
|
||||||
|
* @param deptId 部门ID,用于指定需要查询的部门
|
||||||
|
* @return 返回该部门的负责人ID
|
||||||
|
*/
|
||||||
|
Long selectDeptLeaderById(Long deptId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询部门
|
||||||
|
*
|
||||||
|
* @return 部门列表
|
||||||
|
*/
|
||||||
|
List<DeptDTO> selectDeptsByList();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用 岗位服务
|
||||||
|
*
|
||||||
|
* @author AprilWind
|
||||||
|
*/
|
||||||
|
public interface PostService {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用 角色服务
|
||||||
|
*
|
||||||
|
* @author AprilWind
|
||||||
|
*/
|
||||||
|
public interface RoleService {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
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);
|
||||||
|
|
||||||
|
}
|
@ -82,4 +82,13 @@ public interface UserService {
|
|||||||
* @return 用户
|
* @return 用户
|
||||||
*/
|
*/
|
||||||
List<UserDTO> selectUsersByDeptIds(List<Long> deptIds);
|
List<UserDTO> selectUsersByDeptIds(List<Long> deptIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过岗位ID查询用户
|
||||||
|
*
|
||||||
|
* @param postIds 岗位ids
|
||||||
|
* @return 用户
|
||||||
|
*/
|
||||||
|
List<UserDTO> selectUsersByPostIds(List<Long> postIds);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package org.dromara.common.core.service;
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
import org.dromara.common.core.domain.dto.CompleteTaskDTO;
|
||||||
|
import org.dromara.common.core.domain.dto.StartProcessDTO;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -13,64 +16,63 @@ public interface WorkflowService {
|
|||||||
/**
|
/**
|
||||||
* 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息
|
* 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息
|
||||||
*
|
*
|
||||||
* @param businessKeys 业务id
|
* @param businessIds 业务id
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
boolean deleteRunAndHisInstance(List<String> businessKeys);
|
boolean deleteInstance(List<Long> businessIds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前流程状态
|
* 获取当前流程状态
|
||||||
*
|
*
|
||||||
* @param taskId 任务id
|
* @param taskId 任务id
|
||||||
|
* @return 状态
|
||||||
*/
|
*/
|
||||||
String getBusinessStatusByTaskId(String taskId);
|
String getBusinessStatusByTaskId(Long taskId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前流程状态
|
* 获取当前流程状态
|
||||||
*
|
*
|
||||||
* @param businessKey 业务id
|
* @param businessId 业务id
|
||||||
|
* @return 状态
|
||||||
*/
|
*/
|
||||||
String getBusinessStatus(String businessKey);
|
String getBusinessStatus(String businessId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置流程变量(全局变量)
|
* 设置流程变量
|
||||||
*
|
*
|
||||||
* @param taskId 任务id
|
* @param instanceId 流程实例id
|
||||||
* @param variableName 变量名称
|
* @param variable 流程变量
|
||||||
* @param value 变量值
|
|
||||||
*/
|
*/
|
||||||
void setVariable(String taskId, String variableName, Object value);
|
void setVariable(Long instanceId, Map<String, Object> variable);
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置流程变量(全局变量)
|
|
||||||
*
|
|
||||||
* @param taskId 任务id
|
|
||||||
* @param variables 流程变量
|
|
||||||
*/
|
|
||||||
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
|
* 按照业务id查询流程实例id
|
||||||
*
|
*
|
||||||
* @param businessKey 业务id
|
* @param businessId 业务id
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
String getInstanceIdByBusinessKey(String businessKey);
|
Long getInstanceIdByBusinessId(String businessId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增租户流程定义
|
||||||
|
*
|
||||||
|
* @param tenantId 租户id
|
||||||
|
*/
|
||||||
|
void syncDef(String tenantId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动流程
|
||||||
|
*
|
||||||
|
* @param startProcess 参数
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
Map<String, Object> startWorkFlow(StartProcessDTO startProcess);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 办理任务
|
||||||
|
*
|
||||||
|
* @param completeTask 参数
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
boolean completeTask(CompleteTaskDTO completeTask);
|
||||||
}
|
}
|
||||||
|
@ -1,106 +1,157 @@
|
|||||||
package org.dromara.common.core.utils;
|
package org.dromara.common.core.utils;
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
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.lang.management.ManagementFactory;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.time.LocalDate;
|
import java.time.*;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.LocalTime;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.ZonedDateTime;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 时间工具类
|
* 时间工具类
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
|
||||||
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
|
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 = {
|
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",
|
"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() {
|
public static Date getNowDate() {
|
||||||
return new Date();
|
return new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前日期, 默认格式为yyyy-MM-dd
|
* 获取当前日期的字符串表示,格式为YYYY-MM-DD
|
||||||
*
|
*
|
||||||
* @return String
|
* @return 当前日期的字符串表示
|
||||||
*/
|
*/
|
||||||
public static String getDate() {
|
public static String getDate() {
|
||||||
return dateTimeNow(YYYY_MM_DD);
|
return dateTimeNow(FormatsType.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() {
|
public static String getTime() {
|
||||||
return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
|
return dateTimeNow(FormatsType.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() {
|
public static String dateTimeNow() {
|
||||||
return dateTimeNow(YYYYMMDDHHMMSS);
|
return dateTimeNow(FormatsType.YYYYMMDDHHMMSS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String dateTimeNow(final String format) {
|
/**
|
||||||
|
* 获取当前日期和时间的指定格式的字符串表示
|
||||||
|
*
|
||||||
|
* @param format 日期时间格式,例如"YYYY-MM-DD HH:MM:SS"
|
||||||
|
* @return 当前日期和时间的字符串表示
|
||||||
|
*/
|
||||||
|
public static String dateTimeNow(final FormatsType format) {
|
||||||
return parseDateToStr(format, new Date());
|
return parseDateToStr(format, new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String dateTime(final Date date) {
|
/**
|
||||||
return parseDateToStr(YYYY_MM_DD, date);
|
* 将指定日期格式化为 YYYY-MM-DD 格式的字符串
|
||||||
|
*
|
||||||
|
* @param date 要格式化的日期对象
|
||||||
|
* @return 格式化后的日期字符串
|
||||||
|
*/
|
||||||
|
public static String formatDate(final Date date) {
|
||||||
|
return parseDateToStr(FormatsType.YYYY_MM_DD, date);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String parseDateToStr(final String format, final Date date) {
|
/**
|
||||||
return new SimpleDateFormat(format).format(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 Date dateTime(final String format, final String ts) {
|
/**
|
||||||
|
* 将指定日期按照指定格式进行格式化
|
||||||
|
*
|
||||||
|
* @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) {
|
||||||
try {
|
try {
|
||||||
return new SimpleDateFormat(format).parse(ts);
|
return new SimpleDateFormat(format.getTimeFormat()).parse(ts);
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日期路径 即年/月/日 如2018/08/08
|
* 将对象转换为日期对象
|
||||||
*/
|
*
|
||||||
public static String datePath() {
|
* @param str 要转换的对象,通常是字符串
|
||||||
Date now = new Date();
|
* @return 转换后的日期对象,如果转换失败或输入为null,则返回null
|
||||||
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) {
|
public static Date parseDate(Object str) {
|
||||||
if (str == null) {
|
if (str == null) {
|
||||||
@ -115,6 +166,8 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取服务器启动时间
|
* 获取服务器启动时间
|
||||||
|
*
|
||||||
|
* @return 服务器启动时间的 Date 对象表示
|
||||||
*/
|
*/
|
||||||
public static Date getServerStartDate() {
|
public static Date getServerStartDate() {
|
||||||
long time = ManagementFactory.getRuntimeMXBean().getStartTime();
|
long time = ManagementFactory.getRuntimeMXBean().getStartTime();
|
||||||
@ -122,35 +175,66 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算相差天数
|
* 计算两个日期之间的天数差(以毫秒为单位)
|
||||||
|
*
|
||||||
|
* @param date1 第一个日期
|
||||||
|
* @param date2 第二个日期
|
||||||
|
* @return 两个日期之间的天数差的绝对值
|
||||||
*/
|
*/
|
||||||
public static int differentDaysByMillisecond(Date date1, Date date2) {
|
public static int differentDaysByMillisecond(Date date1, Date date2) {
|
||||||
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
|
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算两个时间差
|
* 计算两个日期之间的时间差,并以天、小时和分钟的格式返回
|
||||||
|
*
|
||||||
|
* @param endDate 结束日期
|
||||||
|
* @param nowDate 当前日期
|
||||||
|
* @return 表示时间差的字符串,格式为"天 小时 分钟"
|
||||||
*/
|
*/
|
||||||
public static String getDatePoor(Date endDate, Date nowDate) {
|
public static String getDatePoor(Date endDate, Date nowDate) {
|
||||||
long nd = 1000 * 24 * 60 * 60;
|
long diffInMillis = endDate.getTime() - nowDate.getTime();
|
||||||
long nh = 1000 * 60 * 60;
|
long day = TimeUnit.MILLISECONDS.toDays(diffInMillis);
|
||||||
long nm = 1000 * 60;
|
long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24;
|
||||||
// long ns = 1000;
|
long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60;
|
||||||
// 获得两个时间的毫秒时间差异
|
return String.format("%d天 %d小时 %d分钟", day, hour, min);
|
||||||
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 + "分钟";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 增加 LocalDateTime ==> Date
|
* 计算两个时间点的差值(天、小时、分钟、秒),当值为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 对象
|
||||||
*/
|
*/
|
||||||
public static Date toDate(LocalDateTime temporalAccessor) {
|
public static Date toDate(LocalDateTime temporalAccessor) {
|
||||||
ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
|
ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
|
||||||
@ -158,11 +242,46 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 增加 LocalDate ==> Date
|
* 将 LocalDate 对象转换为 Date 对象
|
||||||
|
*
|
||||||
|
* @param temporalAccessor 要转换的 LocalDate 对象
|
||||||
|
* @return 转换后的 Date 对象
|
||||||
*/
|
*/
|
||||||
public static Date toDate(LocalDate temporalAccessor) {
|
public static Date toDate(LocalDate temporalAccessor) {
|
||||||
LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
|
LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
|
||||||
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
|
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
|
||||||
return Date.from(zdt.toInstant());
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
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;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 客户端工具类
|
* 客户端工具类,提供获取请求参数、响应处理、头部信息等常用操作
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@ -33,52 +33,73 @@ import java.util.Map;
|
|||||||
public class ServletUtils extends JakartaServletUtil {
|
public class ServletUtils extends JakartaServletUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取String参数
|
* 获取指定名称的 String 类型的请求参数
|
||||||
|
*
|
||||||
|
* @param name 参数名
|
||||||
|
* @return 参数值
|
||||||
*/
|
*/
|
||||||
public static String getParameter(String name) {
|
public static String getParameter(String name) {
|
||||||
return getRequest().getParameter(name);
|
return getRequest().getParameter(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取String参数
|
* 获取指定名称的 String 类型的请求参数,若参数不存在,则返回默认值
|
||||||
|
*
|
||||||
|
* @param name 参数名
|
||||||
|
* @param defaultValue 默认值
|
||||||
|
* @return 参数值或默认值
|
||||||
*/
|
*/
|
||||||
public static String getParameter(String name, String defaultValue) {
|
public static String getParameter(String name, String defaultValue) {
|
||||||
return Convert.toStr(getRequest().getParameter(name), defaultValue);
|
return Convert.toStr(getRequest().getParameter(name), defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取Integer参数
|
* 获取指定名称的 Integer 类型的请求参数
|
||||||
|
*
|
||||||
|
* @param name 参数名
|
||||||
|
* @return 参数值
|
||||||
*/
|
*/
|
||||||
public static Integer getParameterToInt(String name) {
|
public static Integer getParameterToInt(String name) {
|
||||||
return Convert.toInt(getRequest().getParameter(name));
|
return Convert.toInt(getRequest().getParameter(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取Integer参数
|
* 获取指定名称的 Integer 类型的请求参数,若参数不存在,则返回默认值
|
||||||
|
*
|
||||||
|
* @param name 参数名
|
||||||
|
* @param defaultValue 默认值
|
||||||
|
* @return 参数值或默认值
|
||||||
*/
|
*/
|
||||||
public static Integer getParameterToInt(String name, Integer defaultValue) {
|
public static Integer getParameterToInt(String name, Integer defaultValue) {
|
||||||
return Convert.toInt(getRequest().getParameter(name), defaultValue);
|
return Convert.toInt(getRequest().getParameter(name), defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取Boolean参数
|
* 获取指定名称的 Boolean 类型的请求参数
|
||||||
|
*
|
||||||
|
* @param name 参数名
|
||||||
|
* @return 参数值
|
||||||
*/
|
*/
|
||||||
public static Boolean getParameterToBool(String name) {
|
public static Boolean getParameterToBool(String name) {
|
||||||
return Convert.toBool(getRequest().getParameter(name));
|
return Convert.toBool(getRequest().getParameter(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取Boolean参数
|
* 获取指定名称的 Boolean 类型的请求参数,若参数不存在,则返回默认值
|
||||||
|
*
|
||||||
|
* @param name 参数名
|
||||||
|
* @param defaultValue 默认值
|
||||||
|
* @return 参数值或默认值
|
||||||
*/
|
*/
|
||||||
public static Boolean getParameterToBool(String name, Boolean defaultValue) {
|
public static Boolean getParameterToBool(String name, Boolean defaultValue) {
|
||||||
return Convert.toBool(getRequest().getParameter(name), defaultValue);
|
return Convert.toBool(getRequest().getParameter(name), defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得所有请求参数
|
* 获取所有请求参数(以 Map 的形式返回)
|
||||||
*
|
*
|
||||||
* @param request 请求对象{@link ServletRequest}
|
* @param request 请求对象{@link ServletRequest}
|
||||||
* @return Map
|
* @return 请求参数的 Map,键为参数名,值为参数值数组
|
||||||
*/
|
*/
|
||||||
public static Map<String, String[]> getParams(ServletRequest request) {
|
public static Map<String, String[]> getParams(ServletRequest request) {
|
||||||
final Map<String, String[]> map = request.getParameterMap();
|
final Map<String, String[]> map = request.getParameterMap();
|
||||||
@ -86,10 +107,10 @@ public class ServletUtils extends JakartaServletUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得所有请求参数
|
* 获取所有请求参数(以 Map 的形式返回,值为字符串形式的拼接)
|
||||||
*
|
*
|
||||||
* @param request 请求对象{@link ServletRequest}
|
* @param request 请求对象{@link ServletRequest}
|
||||||
* @return Map
|
* @return 请求参数的 Map,键为参数名,值为拼接后的字符串
|
||||||
*/
|
*/
|
||||||
public static Map<String, String> getParamMap(ServletRequest request) {
|
public static Map<String, String> getParamMap(ServletRequest request) {
|
||||||
Map<String, String> params = new HashMap<>();
|
Map<String, String> params = new HashMap<>();
|
||||||
@ -100,7 +121,9 @@ public class ServletUtils extends JakartaServletUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取request
|
* 获取当前 HTTP 请求对象
|
||||||
|
*
|
||||||
|
* @return 当前 HTTP 请求对象
|
||||||
*/
|
*/
|
||||||
public static HttpServletRequest getRequest() {
|
public static HttpServletRequest getRequest() {
|
||||||
try {
|
try {
|
||||||
@ -111,7 +134,9 @@ public class ServletUtils extends JakartaServletUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取response
|
* 获取当前 HTTP 响应对象
|
||||||
|
*
|
||||||
|
* @return 当前 HTTP 响应对象
|
||||||
*/
|
*/
|
||||||
public static HttpServletResponse getResponse() {
|
public static HttpServletResponse getResponse() {
|
||||||
try {
|
try {
|
||||||
@ -122,12 +147,25 @@ public class ServletUtils extends JakartaServletUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取session
|
* 获取当前请求的 HttpSession 对象
|
||||||
|
* <p>
|
||||||
|
* 如果当前请求已经关联了一个会话(即已经存在有效的 session ID),
|
||||||
|
* 则返回该会话对象;如果没有关联会话,则会创建一个新的会话对象并返回。
|
||||||
|
* <p>
|
||||||
|
* HttpSession 用于存储会话级别的数据,如用户登录信息、购物车内容等,
|
||||||
|
* 可以在多个请求之间共享会话数据
|
||||||
|
*
|
||||||
|
* @return 当前请求的 HttpSession 对象
|
||||||
*/
|
*/
|
||||||
public static HttpSession getSession() {
|
public static HttpSession getSession() {
|
||||||
return getRequest().getSession();
|
return getRequest().getSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前请求的请求属性
|
||||||
|
*
|
||||||
|
* @return {@link ServletRequestAttributes} 请求属性对象
|
||||||
|
*/
|
||||||
public static ServletRequestAttributes getRequestAttributes() {
|
public static ServletRequestAttributes getRequestAttributes() {
|
||||||
try {
|
try {
|
||||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||||
@ -137,6 +175,13 @@ public class ServletUtils extends JakartaServletUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定请求头的值,如果头部为空则返回空字符串
|
||||||
|
*
|
||||||
|
* @param request 请求对象
|
||||||
|
* @param name 头部名称
|
||||||
|
* @return 头部值
|
||||||
|
*/
|
||||||
public static String getHeader(HttpServletRequest request, String name) {
|
public static String getHeader(HttpServletRequest request, String name) {
|
||||||
String value = request.getHeader(name);
|
String value = request.getHeader(name);
|
||||||
if (StringUtils.isEmpty(value)) {
|
if (StringUtils.isEmpty(value)) {
|
||||||
@ -145,6 +190,12 @@ public class ServletUtils extends JakartaServletUtil {
|
|||||||
return urlDecode(value);
|
return urlDecode(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有请求头的 Map,键为头部名称,值为头部值
|
||||||
|
*
|
||||||
|
* @param request 请求对象
|
||||||
|
* @return 请求头的 Map
|
||||||
|
*/
|
||||||
public static Map<String, String> getHeaders(HttpServletRequest request) {
|
public static Map<String, String> getHeaders(HttpServletRequest request) {
|
||||||
Map<String, String> map = new LinkedCaseInsensitiveMap<>();
|
Map<String, String> map = new LinkedCaseInsensitiveMap<>();
|
||||||
Enumeration<String> enumeration = request.getHeaderNames();
|
Enumeration<String> enumeration = request.getHeaderNames();
|
||||||
@ -159,7 +210,7 @@ public class ServletUtils extends JakartaServletUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将字符串渲染到客户端
|
* 将字符串渲染到客户端(以 JSON 格式返回)
|
||||||
*
|
*
|
||||||
* @param response 渲染对象
|
* @param response 渲染对象
|
||||||
* @param string 待渲染的字符串
|
* @param string 待渲染的字符串
|
||||||
@ -176,37 +227,47 @@ public class ServletUtils extends JakartaServletUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否是Ajax异步请求
|
* 判断当前请求是否为 Ajax 异步请求
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request 请求对象
|
||||||
|
* @return 是否为 Ajax 请求
|
||||||
*/
|
*/
|
||||||
public static boolean isAjaxRequest(HttpServletRequest request) {
|
public static boolean isAjaxRequest(HttpServletRequest request) {
|
||||||
|
|
||||||
|
// 判断 Accept 头部是否包含 application/json
|
||||||
String accept = request.getHeader("accept");
|
String accept = request.getHeader("accept");
|
||||||
if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) {
|
if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 判断 X-Requested-With 头部是否包含 XMLHttpRequest
|
||||||
String xRequestedWith = request.getHeader("X-Requested-With");
|
String xRequestedWith = request.getHeader("X-Requested-With");
|
||||||
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
|
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 判断 URI 后缀是否为 .json 或 .xml
|
||||||
String uri = request.getRequestURI();
|
String uri = request.getRequestURI();
|
||||||
if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
|
if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 判断请求参数 __ajax 是否为 json 或 xml
|
||||||
String ajax = request.getParameter("__ajax");
|
String ajax = request.getParameter("__ajax");
|
||||||
return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml");
|
return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取客户端 IP 地址
|
||||||
|
*
|
||||||
|
* @return 客户端 IP 地址
|
||||||
|
*/
|
||||||
public static String getClientIP() {
|
public static String getClientIP() {
|
||||||
return getClientIP(getRequest());
|
return getClientIP(getRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内容编码
|
* 对内容进行 URL 编码
|
||||||
*
|
*
|
||||||
* @param str 内容
|
* @param str 内容
|
||||||
* @return 编码后的内容
|
* @return 编码后的内容
|
||||||
@ -216,7 +277,7 @@ public class ServletUtils extends JakartaServletUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内容解码
|
* 对内容进行 URL 解码
|
||||||
*
|
*
|
||||||
* @param str 内容
|
* @param str 内容
|
||||||
* @return 解码后的内容
|
* @return 解码后的内容
|
||||||
|
@ -4,8 +4,6 @@ import cn.hutool.core.collection.CollUtil;
|
|||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.core.lang.Validator;
|
import cn.hutool.core.lang.Validator;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import org.springframework.util.AntPathMatcher;
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -17,13 +15,16 @@ import java.util.stream.Collectors;
|
|||||||
*
|
*
|
||||||
* @author Lion Li
|
* @author Lion Li
|
||||||
*/
|
*/
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
|
||||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||||
|
|
||||||
public static final String SEPARATOR = ",";
|
public static final String SEPARATOR = ",";
|
||||||
|
|
||||||
public static final String SLASH = "/";
|
public static final String SLASH = "/";
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
private StringUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取参数不为空值
|
* 获取参数不为空值
|
||||||
*
|
*
|
||||||
@ -320,4 +321,21 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
|||||||
.collect(Collectors.toList());
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,18 +14,6 @@ import java.util.concurrent.*;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public class Threads {
|
public class Threads {
|
||||||
|
|
||||||
/**
|
|
||||||
* sleep等待,单位为毫秒
|
|
||||||
*/
|
|
||||||
public static void sleep(long milliseconds) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(milliseconds);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 停止线程池
|
* 停止线程池
|
||||||
* 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
|
* 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
|
||||||
|
@ -43,6 +43,23 @@ public class TreeBuildUtils extends TreeUtil {
|
|||||||
return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取节点列表中所有节点的叶子节点
|
* 获取节点列表中所有节点的叶子节点
|
||||||
*
|
*
|
||||||
|
@ -15,7 +15,7 @@ public class SqlUtil {
|
|||||||
/**
|
/**
|
||||||
* 定义常用的 sql关键字
|
* 定义常用的 sql关键字
|
||||||
*/
|
*/
|
||||||
public static String SQL_REGEX = "and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
|
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()";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
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,11 +1,11 @@
|
|||||||
package org.dromara.common.encrypt.core;
|
package org.dromara.common.encrypt.core;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
|
||||||
import cn.hutool.core.util.ReflectUtil;
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.ibatis.io.Resources;
|
import org.apache.ibatis.io.Resources;
|
||||||
|
import org.dromara.common.core.utils.ObjectUtils;
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
import org.dromara.common.encrypt.annotation.EncryptField;
|
import org.dromara.common.encrypt.annotation.EncryptField;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
@ -58,10 +58,7 @@ public class EncryptorManager {
|
|||||||
* 获取类加密字段缓存
|
* 获取类加密字段缓存
|
||||||
*/
|
*/
|
||||||
public Set<Field> getFieldCache(Class<?> sourceClazz) {
|
public Set<Field> getFieldCache(Class<?> sourceClazz) {
|
||||||
if (ObjectUtil.isNotNull(fieldCache)) {
|
return ObjectUtils.notNullGetter(fieldCache, f -> f.get(sourceClazz));
|
||||||
return fieldCache.get(sourceClazz);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,10 +19,12 @@ import java.util.Map;
|
|||||||
* @author 老马
|
* @author 老马
|
||||||
*/
|
*/
|
||||||
public class EncryptUtils {
|
public class EncryptUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 公钥
|
* 公钥
|
||||||
*/
|
*/
|
||||||
public static final String PUBLIC_KEY = "publicKey";
|
public static final String PUBLIC_KEY = "publicKey";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 私钥
|
* 私钥
|
||||||
*/
|
*/
|
||||||
@ -51,7 +53,7 @@ public class EncryptUtils {
|
|||||||
/**
|
/**
|
||||||
* AES加密
|
* AES加密
|
||||||
*
|
*
|
||||||
* @param data 待解密数据
|
* @param data 待加密数据
|
||||||
* @param password 秘钥字符串
|
* @param password 秘钥字符串
|
||||||
* @return 加密后字符串, 采用Base64编码
|
* @return 加密后字符串, 采用Base64编码
|
||||||
*/
|
*/
|
||||||
@ -70,7 +72,7 @@ public class EncryptUtils {
|
|||||||
/**
|
/**
|
||||||
* AES加密
|
* AES加密
|
||||||
*
|
*
|
||||||
* @param data 待解密数据
|
* @param data 待加密数据
|
||||||
* @param password 秘钥字符串
|
* @param password 秘钥字符串
|
||||||
* @return 加密后字符串, 采用Hex编码
|
* @return 加密后字符串, 采用Hex编码
|
||||||
*/
|
*/
|
||||||
@ -208,7 +210,7 @@ public class EncryptUtils {
|
|||||||
/**
|
/**
|
||||||
* sm2私钥解密
|
* sm2私钥解密
|
||||||
*
|
*
|
||||||
* @param data 待加密数据
|
* @param data 待解密数据
|
||||||
* @param privateKey 私钥
|
* @param privateKey 私钥
|
||||||
* @return 解密后字符串
|
* @return 解密后字符串
|
||||||
*/
|
*/
|
||||||
@ -266,7 +268,7 @@ public class EncryptUtils {
|
|||||||
/**
|
/**
|
||||||
* rsa私钥解密
|
* rsa私钥解密
|
||||||
*
|
*
|
||||||
* @param data 待加密数据
|
* @param data 待解密数据
|
||||||
* @param privateKey 私钥
|
* @param privateKey 私钥
|
||||||
* @return 解密后字符串
|
* @return 解密后字符串
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
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 "";
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
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;
|
||||||
|
}
|
@ -55,6 +55,7 @@ public class ExcelDownHandler implements SheetWriteHandler {
|
|||||||
* 下拉可选项
|
* 下拉可选项
|
||||||
*/
|
*/
|
||||||
private final List<DropDownOptions> dropDownOptions;
|
private final List<DropDownOptions> dropDownOptions;
|
||||||
|
private final DictService dictService;
|
||||||
/**
|
/**
|
||||||
* 当前单选进度
|
* 当前单选进度
|
||||||
*/
|
*/
|
||||||
@ -63,7 +64,6 @@ public class ExcelDownHandler implements SheetWriteHandler {
|
|||||||
* 当前联动选择进度
|
* 当前联动选择进度
|
||||||
*/
|
*/
|
||||||
private int currentLinkedOptionsSheetIndex;
|
private int currentLinkedOptionsSheetIndex;
|
||||||
private final DictService dictService;
|
|
||||||
|
|
||||||
public ExcelDownHandler(List<DropDownOptions> options) {
|
public ExcelDownHandler(List<DropDownOptions> options) {
|
||||||
this.dropDownOptions = options;
|
this.dropDownOptions = options;
|
||||||
@ -139,8 +139,8 @@ public class ExcelDownHandler implements SheetWriteHandler {
|
|||||||
} else if (everyOptions.getOptions().size() > 10) {
|
} else if (everyOptions.getOptions().size() > 10) {
|
||||||
// 当一级选项参数个数大于10,使用额外表的形式
|
// 当一级选项参数个数大于10,使用额外表的形式
|
||||||
dropDownWithSheet(helper, workbook, sheet, everyOptions.getIndex(), everyOptions.getOptions());
|
dropDownWithSheet(helper, workbook, sheet, everyOptions.getIndex(), everyOptions.getOptions());
|
||||||
} else if (everyOptions.getOptions().size() != 0) {
|
} else {
|
||||||
// 当一级选项个数不为空,使用默认形式
|
// 否则使用默认形式
|
||||||
dropDownWithSimple(helper, sheet, everyOptions.getIndex(), everyOptions.getOptions());
|
dropDownWithSimple(helper, sheet, everyOptions.getIndex(), everyOptions.getOptions());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -171,10 +171,24 @@ public class ExcelDownHandler implements SheetWriteHandler {
|
|||||||
Sheet linkedOptionsDataSheet = workbook.createSheet(WorkbookUtil.createSafeSheetName(linkedOptionsSheetName));
|
Sheet linkedOptionsDataSheet = workbook.createSheet(WorkbookUtil.createSafeSheetName(linkedOptionsSheetName));
|
||||||
// 将下拉表隐藏
|
// 将下拉表隐藏
|
||||||
workbook.setSheetHidden(workbook.getSheetIndex(linkedOptionsDataSheet), true);
|
workbook.setSheetHidden(workbook.getSheetIndex(linkedOptionsDataSheet), true);
|
||||||
// 完善横向的一级选项数据表
|
// 选项数据
|
||||||
List<String> firstOptions = options.getOptions();
|
List<String> firstOptions = options.getOptions();
|
||||||
Map<String, List<String>> secoundOptionsMap = options.getNextOptions();
|
Map<String, List<String>> secoundOptionsMap = options.getNextOptions();
|
||||||
|
|
||||||
|
// 采用按行填充数据的方式,避免EasyExcel出现数据无法写入的问题
|
||||||
|
// 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();
|
Name name = workbook.createName();
|
||||||
// 设置名称管理器的别名
|
// 设置名称管理器的别名
|
||||||
@ -190,28 +204,12 @@ public class ExcelDownHandler implements SheetWriteHandler {
|
|||||||
// 设置数据校验为序列模式,引用的是名称管理器中的别名
|
// 设置数据校验为序列模式,引用的是名称管理器中的别名
|
||||||
this.markOptionsToSheet(helper, sheet, options.getIndex(), helper.createFormulaListConstraint(linkedOptionsSheetName));
|
this.markOptionsToSheet(helper, sheet, options.getIndex(), helper.createFormulaListConstraint(linkedOptionsSheetName));
|
||||||
|
|
||||||
for (int columIndex = 0; columIndex < firstOptions.size(); columIndex++) {
|
// 创建二级选项的名称管理器
|
||||||
// 先提取主表中一级下拉的列名
|
for (int columIndex = 0; columIndex < columnNames.size(); columIndex++) {
|
||||||
|
// 列名
|
||||||
String firstOptionsColumnName = getExcelColumnName(columIndex);
|
String firstOptionsColumnName = getExcelColumnName(columIndex);
|
||||||
// 一次循环是每一个一级选项
|
// 对应的一级值
|
||||||
int finalI = columIndex;
|
String thisFirstOptionsValue = columnNames.get(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();
|
Name sonName = workbook.createName();
|
||||||
@ -222,7 +220,9 @@ public class ExcelDownHandler implements SheetWriteHandler {
|
|||||||
linkedOptionsSheetName,
|
linkedOptionsSheetName,
|
||||||
firstOptionsColumnName,
|
firstOptionsColumnName,
|
||||||
firstOptionsColumnName,
|
firstOptionsColumnName,
|
||||||
secondOptions.size() + 1
|
// 二级选项存在则设置为(选项个数+1)行,否则设置为2行
|
||||||
|
Math.max(Optional.ofNullable(secoundOptionsMap.get(thisFirstOptionsValue))
|
||||||
|
.orElseGet(ArrayList::new).size(), 1) + 1
|
||||||
);
|
);
|
||||||
// 设置名称管理器的引用位置
|
// 设置名称管理器的引用位置
|
||||||
sonName.setRefersToFormula(sonFunction);
|
sonName.setRefersToFormula(sonFunction);
|
||||||
@ -235,25 +235,51 @@ public class ExcelDownHandler implements SheetWriteHandler {
|
|||||||
// 二级只能主表每一行的每一列添加二级校验
|
// 二级只能主表每一行的每一列添加二级校验
|
||||||
markLinkedOptionsToSheet(helper, sheet, i, options.getNextIndex(), helper.createFormulaListConstraint(secondOptionsFunction));
|
markLinkedOptionsToSheet(helper, sheet, i, options.getNextIndex(), helper.createFormulaListConstraint(secondOptionsFunction));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int rowIndex = 0; rowIndex < secondOptions.size(); rowIndex++) {
|
// 将二级数据处理为按行区分
|
||||||
// 从第二行开始填充二级选项
|
Map<Integer, List<String>> columnValueMap = new HashMap<>();
|
||||||
int finalRowIndex = rowIndex + 1;
|
int currentRow = 1;
|
||||||
int finalColumIndex = columIndex;
|
while (currentRow >= 0) {
|
||||||
|
boolean flag = false;
|
||||||
Row row = Optional.ofNullable(linkedOptionsDataSheet.getRow(finalRowIndex))
|
List<String> rowData = new ArrayList<>();
|
||||||
// 没有则创建
|
for (String columnName : columnNames) {
|
||||||
.orElseGet(() -> linkedOptionsDataSheet.createRow(finalRowIndex));
|
List<String> data = secoundOptionsMap.get(columnName);
|
||||||
Optional
|
if (CollUtil.isEmpty(data)) {
|
||||||
// 在本级一级选项所在的列
|
// 添加空字符串填充位置
|
||||||
.ofNullable(row.getCell(finalColumIndex))
|
rowData.add(" ");
|
||||||
// 不存在则创建
|
continue;
|
||||||
.orElseGet(() -> row.createCell(finalColumIndex))
|
}
|
||||||
// 设置二级选项值
|
// 取第一个
|
||||||
.setCellValue(secondOptions.get(rowIndex));
|
String str = data.get(0);
|
||||||
|
rowData.add(str);
|
||||||
|
// 通过移除的方式避免重复
|
||||||
|
data.remove(0);
|
||||||
|
// 设置可以继续
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
columnValueMap.put(currentRow, rowData);
|
||||||
|
// 可以继续,则增加行数,否则置为负数跳出循环
|
||||||
|
if (flag) {
|
||||||
|
currentRow++;
|
||||||
|
} else {
|
||||||
|
currentRow = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 填充第二级选项数据
|
||||||
|
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++;
|
currentLinkedOptionsSheetIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,135 @@
|
|||||||
|
package org.dromara.common.excel.handler;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.alibaba.excel.metadata.data.DataFormatData;
|
||||||
|
import com.alibaba.excel.metadata.data.WriteCellData;
|
||||||
|
import com.alibaba.excel.util.StyleUtil;
|
||||||
|
import com.alibaba.excel.write.handler.CellWriteHandler;
|
||||||
|
import com.alibaba.excel.write.handler.SheetWriteHandler;
|
||||||
|
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
|
||||||
|
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||||
|
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
|
||||||
|
import com.alibaba.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;
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ import org.dromara.common.core.utils.StringUtils;
|
|||||||
import org.dromara.common.core.utils.file.FileUtils;
|
import org.dromara.common.core.utils.file.FileUtils;
|
||||||
import org.dromara.common.excel.convert.ExcelBigNumberConvert;
|
import org.dromara.common.excel.convert.ExcelBigNumberConvert;
|
||||||
import org.dromara.common.excel.core.*;
|
import org.dromara.common.excel.core.*;
|
||||||
|
import org.dromara.common.excel.handler.DataWriteHandler;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -191,6 +192,7 @@ public class ExcelUtil {
|
|||||||
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
||||||
// 大数值自动转换 防止失真
|
// 大数值自动转换 防止失真
|
||||||
.registerConverter(new ExcelBigNumberConvert())
|
.registerConverter(new ExcelBigNumberConvert())
|
||||||
|
.registerWriteHandler(new DataWriteHandler(clazz))
|
||||||
.sheet(sheetName);
|
.sheet(sheetName);
|
||||||
if (merge) {
|
if (merge) {
|
||||||
// 合并处理器
|
// 合并处理器
|
||||||
@ -211,7 +213,7 @@ public class ExcelUtil {
|
|||||||
* @param data 模板需要的数据
|
* @param data 模板需要的数据
|
||||||
* @param response 响应体
|
* @param response 响应体
|
||||||
*/
|
*/
|
||||||
public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) {
|
public static <T> void exportTemplate(List<T> data, String filename, String templatePath, HttpServletResponse response) {
|
||||||
try {
|
try {
|
||||||
resetResponse(filename, response);
|
resetResponse(filename, response);
|
||||||
ServletOutputStream os = response.getOutputStream();
|
ServletOutputStream os = response.getOutputStream();
|
||||||
@ -230,20 +232,21 @@ public class ExcelUtil {
|
|||||||
* @param data 模板需要的数据
|
* @param data 模板需要的数据
|
||||||
* @param os 输出流
|
* @param os 输出流
|
||||||
*/
|
*/
|
||||||
public static void exportTemplate(List<Object> data, String templatePath, OutputStream os) {
|
public static <T> void exportTemplate(List<T> data, String templatePath, OutputStream os) {
|
||||||
|
if (CollUtil.isEmpty(data)) {
|
||||||
|
throw new IllegalArgumentException("数据为空");
|
||||||
|
}
|
||||||
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
||||||
ExcelWriter excelWriter = EasyExcel.write(os)
|
ExcelWriter excelWriter = EasyExcel.write(os)
|
||||||
.withTemplate(templateResource.getStream())
|
.withTemplate(templateResource.getStream())
|
||||||
.autoCloseStream(false)
|
.autoCloseStream(false)
|
||||||
// 大数值自动转换 防止失真
|
// 大数值自动转换 防止失真
|
||||||
.registerConverter(new ExcelBigNumberConvert())
|
.registerConverter(new ExcelBigNumberConvert())
|
||||||
|
.registerWriteHandler(new DataWriteHandler(data.get(0).getClass()))
|
||||||
.build();
|
.build();
|
||||||
WriteSheet writeSheet = EasyExcel.writerSheet().build();
|
WriteSheet writeSheet = EasyExcel.writerSheet().build();
|
||||||
if (CollUtil.isEmpty(data)) {
|
|
||||||
throw new IllegalArgumentException("数据为空");
|
|
||||||
}
|
|
||||||
// 单表多数据导出 模板格式为 {.属性}
|
// 单表多数据导出 模板格式为 {.属性}
|
||||||
for (Object d : data) {
|
for (T d : data) {
|
||||||
excelWriter.fill(d, writeSheet);
|
excelWriter.fill(d, writeSheet);
|
||||||
}
|
}
|
||||||
excelWriter.finish();
|
excelWriter.finish();
|
||||||
@ -299,6 +302,9 @@ public class ExcelUtil {
|
|||||||
* @param os 输出流
|
* @param os 输出流
|
||||||
*/
|
*/
|
||||||
public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) {
|
public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) {
|
||||||
|
if (CollUtil.isEmpty(data)) {
|
||||||
|
throw new IllegalArgumentException("数据为空");
|
||||||
|
}
|
||||||
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
||||||
ExcelWriter excelWriter = EasyExcel.write(os)
|
ExcelWriter excelWriter = EasyExcel.write(os)
|
||||||
.withTemplate(templateResource.getStream())
|
.withTemplate(templateResource.getStream())
|
||||||
@ -307,9 +313,6 @@ public class ExcelUtil {
|
|||||||
.registerConverter(new ExcelBigNumberConvert())
|
.registerConverter(new ExcelBigNumberConvert())
|
||||||
.build();
|
.build();
|
||||||
WriteSheet writeSheet = EasyExcel.writerSheet().build();
|
WriteSheet writeSheet = EasyExcel.writerSheet().build();
|
||||||
if (CollUtil.isEmpty(data)) {
|
|
||||||
throw new IllegalArgumentException("数据为空");
|
|
||||||
}
|
|
||||||
for (Map.Entry<String, Object> map : data.entrySet()) {
|
for (Map.Entry<String, Object> map : data.entrySet()) {
|
||||||
// 设置列表后续还有数据
|
// 设置列表后续还有数据
|
||||||
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
|
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
|
||||||
@ -333,6 +336,9 @@ public class ExcelUtil {
|
|||||||
* @param os 输出流
|
* @param os 输出流
|
||||||
*/
|
*/
|
||||||
public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String templatePath, OutputStream os) {
|
public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String templatePath, OutputStream os) {
|
||||||
|
if (CollUtil.isEmpty(data)) {
|
||||||
|
throw new IllegalArgumentException("数据为空");
|
||||||
|
}
|
||||||
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
||||||
ExcelWriter excelWriter = EasyExcel.write(os)
|
ExcelWriter excelWriter = EasyExcel.write(os)
|
||||||
.withTemplate(templateResource.getStream())
|
.withTemplate(templateResource.getStream())
|
||||||
@ -340,9 +346,6 @@ public class ExcelUtil {
|
|||||||
// 大数值自动转换 防止失真
|
// 大数值自动转换 防止失真
|
||||||
.registerConverter(new ExcelBigNumberConvert())
|
.registerConverter(new ExcelBigNumberConvert())
|
||||||
.build();
|
.build();
|
||||||
if (CollUtil.isEmpty(data)) {
|
|
||||||
throw new IllegalArgumentException("数据为空");
|
|
||||||
}
|
|
||||||
for (int i = 0; i < data.size(); i++) {
|
for (int i = 0; i < data.size(); i++) {
|
||||||
WriteSheet writeSheet = EasyExcel.writerSheet(i).build();
|
WriteSheet writeSheet = EasyExcel.writerSheet(i).build();
|
||||||
for (Map.Entry<String, Object> map : data.get(i).entrySet()) {
|
for (Map.Entry<String, Object> map : data.get(i).entrySet()) {
|
||||||
|
@ -32,7 +32,7 @@ public class BigNumberSerializer extends NumberSerializer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||||
// 超出范围 序列化位字符串
|
// 超出范围 序列化为字符串
|
||||||
if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
|
if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
|
||||||
super.serialize(value, gen, provider);
|
super.serialize(value, gen, provider);
|
||||||
} else {
|
} else {
|
||||||
|
@ -100,7 +100,7 @@ public class LogAspect {
|
|||||||
|
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
operLog.setStatus(BusinessStatus.FAIL.ordinal());
|
operLog.setStatus(BusinessStatus.FAIL.ordinal());
|
||||||
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
|
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 3800));
|
||||||
}
|
}
|
||||||
// 设置方法名称
|
// 设置方法名称
|
||||||
String className = joinPoint.getTarget().getClass().getName();
|
String className = joinPoint.getTarget().getClass().getName();
|
||||||
@ -113,13 +113,12 @@ public class LogAspect {
|
|||||||
// 设置消耗时间
|
// 设置消耗时间
|
||||||
StopWatch stopWatch = KEY_CACHE.get();
|
StopWatch stopWatch = KEY_CACHE.get();
|
||||||
stopWatch.stop();
|
stopWatch.stop();
|
||||||
operLog.setCostTime(stopWatch.getTime());
|
operLog.setCostTime(stopWatch.getDuration().toMillis());
|
||||||
// 发布事件保存数据库
|
// 发布事件保存数据库
|
||||||
SpringUtils.context().publishEvent(operLog);
|
SpringUtils.context().publishEvent(operLog);
|
||||||
} catch (Exception exp) {
|
} catch (Exception exp) {
|
||||||
// 记录本地异常日志
|
// 记录本地异常日志
|
||||||
log.error("异常信息:{}", exp.getMessage());
|
log.error("异常信息:{}", exp.getMessage());
|
||||||
exp.printStackTrace();
|
|
||||||
} finally {
|
} finally {
|
||||||
KEY_CACHE.remove();
|
KEY_CACHE.remove();
|
||||||
}
|
}
|
||||||
@ -146,7 +145,7 @@ public class LogAspect {
|
|||||||
}
|
}
|
||||||
// 是否需要保存response,参数和值
|
// 是否需要保存response,参数和值
|
||||||
if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) {
|
if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) {
|
||||||
operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 2000));
|
operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 3800));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,11 +160,11 @@ public class LogAspect {
|
|||||||
String requestMethod = operLog.getRequestMethod();
|
String requestMethod = operLog.getRequestMethod();
|
||||||
if (MapUtil.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) {
|
if (MapUtil.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) {
|
||||||
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
|
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
|
||||||
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
|
operLog.setOperParam(StringUtils.substring(params, 0, 3800));
|
||||||
} else {
|
} else {
|
||||||
MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES);
|
MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES);
|
||||||
MapUtil.removeAny(paramsMap, excludeParamNames);
|
MapUtil.removeAny(paramsMap, excludeParamNames);
|
||||||
operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 2000));
|
operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 3800));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,13 @@ public class MailProperties {
|
|||||||
private String pass;
|
private String pass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送方,遵循RFC-822标准
|
* 发送方,遵循RFC-822标准<br>
|
||||||
|
* 发件人可以是以下形式:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1. user@xxx.xx
|
||||||
|
* 2. name <user@xxx.xx>
|
||||||
|
* </pre>
|
||||||
*/
|
*/
|
||||||
private String from;
|
private String from;
|
||||||
|
|
||||||
|
@ -37,6 +37,11 @@
|
|||||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-jsqlparser</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- sql性能分析插件 -->
|
<!-- sql性能分析插件 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>p6spy</groupId>
|
<groupId>p6spy</groupId>
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package org.dromara.common.mybatis.aspect;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.AfterReturning;
|
||||||
|
import org.aspectj.lang.annotation.AfterThrowing;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Before;
|
||||||
|
import org.dromara.common.mybatis.annotation.DataPermission;
|
||||||
|
import org.dromara.common.mybatis.helper.DataPermissionHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据权限处理
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Aspect
|
||||||
|
public class DataPermissionAspect {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理请求前执行
|
||||||
|
*/
|
||||||
|
@Before(value = "@annotation(dataPermission)")
|
||||||
|
public void doBefore(JoinPoint joinPoint, DataPermission dataPermission) {
|
||||||
|
DataPermissionHelper.setPermission(dataPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理完请求后执行
|
||||||
|
*
|
||||||
|
* @param joinPoint 切点
|
||||||
|
*/
|
||||||
|
@AfterReturning(pointcut = "@annotation(dataPermission)")
|
||||||
|
public void doAfterReturning(JoinPoint joinPoint, DataPermission dataPermission) {
|
||||||
|
DataPermissionHelper.removePermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拦截异常操作
|
||||||
|
*
|
||||||
|
* @param joinPoint 切点
|
||||||
|
* @param e 异常
|
||||||
|
*/
|
||||||
|
@AfterThrowing(value = "@annotation(dataPermission)", throwing = "e")
|
||||||
|
public void doAfterThrowing(JoinPoint joinPoint, DataPermission dataPermission, Exception e) {
|
||||||
|
DataPermissionHelper.removePermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,6 +2,7 @@ package org.dromara.common.mybatis.config;
|
|||||||
|
|
||||||
import cn.hutool.core.net.NetUtil;
|
import cn.hutool.core.net.NetUtil;
|
||||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||||
|
import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler;
|
||||||
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
|
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
|
||||||
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
|
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
@ -10,8 +11,10 @@ import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerIntercept
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||||
import org.dromara.common.core.factory.YmlPropertySourceFactory;
|
import org.dromara.common.core.factory.YmlPropertySourceFactory;
|
||||||
import org.dromara.common.core.utils.SpringUtils;
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
|
import org.dromara.common.mybatis.aspect.DataPermissionAspect;
|
||||||
import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler;
|
import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler;
|
||||||
import org.dromara.common.mybatis.handler.MybatisExceptionHandler;
|
import org.dromara.common.mybatis.handler.MybatisExceptionHandler;
|
||||||
|
import org.dromara.common.mybatis.handler.PlusPostInitTableInfoHandler;
|
||||||
import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor;
|
import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor;
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
@ -54,6 +57,14 @@ public class MybatisPlusConfig {
|
|||||||
return new PlusDataPermissionInterceptor(SpringUtils.getProperty("mybatis-plus.mapperPackage"));
|
return new PlusDataPermissionInterceptor(SpringUtils.getProperty("mybatis-plus.mapperPackage"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据权限切面处理器
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public DataPermissionAspect dataPermissionAspect() {
|
||||||
|
return new DataPermissionAspect();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页插件,自动识别数据库类型
|
* 分页插件,自动识别数据库类型
|
||||||
*/
|
*/
|
||||||
@ -96,6 +107,14 @@ public class MybatisPlusConfig {
|
|||||||
return new MybatisExceptionHandler();
|
return new MybatisExceptionHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化表对象处理器
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public PostInitTableInfoHandler postInitTableInfoHandler() {
|
||||||
|
return new PlusPostInitTableInfoHandler();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PaginationInnerInterceptor 分页插件,自动识别数据库类型
|
* PaginationInnerInterceptor 分页插件,自动识别数据库类型
|
||||||
* https://baomidou.com/pages/97710a/
|
* https://baomidou.com/pages/97710a/
|
||||||
|
@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.dromara.common.core.exception.ServiceException;
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
@ -113,8 +114,14 @@ public class PageQuery implements Serializable {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
public Integer getFirstNum() {
|
public Integer getFirstNum() {
|
||||||
return (pageNum - 1) * pageSize;
|
return (pageNum - 1) * pageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PageQuery(Integer pageSize, Integer pageNum) {
|
||||||
|
this.pageSize = pageSize;
|
||||||
|
this.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,8 @@ public class TableDataInfo<T> implements Serializable {
|
|||||||
public TableDataInfo(List<T> list, long total) {
|
public TableDataInfo(List<T> list, long total) {
|
||||||
this.rows = list;
|
this.rows = list;
|
||||||
this.total = total;
|
this.total = total;
|
||||||
|
this.code = HttpStatus.HTTP_OK;
|
||||||
|
this.msg = "查询成功";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,7 +48,12 @@ public enum DataScopeType {
|
|||||||
/**
|
/**
|
||||||
* 仅本人数据权限
|
* 仅本人数据权限
|
||||||
*/
|
*/
|
||||||
SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 ");
|
SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 "),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门及以下或本人数据权限
|
||||||
|
*/
|
||||||
|
DEPT_AND_CHILD_OR_SELF("6", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} ) OR #{#userName} = #{#user.userId} ", " 1 = 0 ");
|
||||||
|
|
||||||
private final String code;
|
private final String code;
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.apache.ibatis.reflection.MetaObject;
|
import org.apache.ibatis.reflection.MetaObject;
|
||||||
import org.dromara.common.core.domain.model.LoginUser;
|
import org.dromara.common.core.domain.model.LoginUser;
|
||||||
import org.dromara.common.core.exception.ServiceException;
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
|
import org.dromara.common.core.utils.ObjectUtils;
|
||||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||||
import org.dromara.common.satoken.utils.LoginHelper;
|
import org.dromara.common.satoken.utils.LoginHelper;
|
||||||
|
|
||||||
@ -31,8 +32,7 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler {
|
|||||||
try {
|
try {
|
||||||
if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
|
if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
|
||||||
// 获取当前时间作为创建时间和更新时间,如果创建时间不为空,则使用创建时间,否则使用当前时间
|
// 获取当前时间作为创建时间和更新时间,如果创建时间不为空,则使用创建时间,否则使用当前时间
|
||||||
Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime())
|
Date current = ObjectUtils.notNull(baseEntity.getCreateTime(), new Date());
|
||||||
? baseEntity.getCreateTime() : new Date();
|
|
||||||
baseEntity.setCreateTime(current);
|
baseEntity.setCreateTime(current);
|
||||||
baseEntity.setUpdateTime(current);
|
baseEntity.setUpdateTime(current);
|
||||||
|
|
||||||
@ -44,8 +44,7 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler {
|
|||||||
// 填充创建人、更新人和创建部门信息
|
// 填充创建人、更新人和创建部门信息
|
||||||
baseEntity.setCreateBy(userId);
|
baseEntity.setCreateBy(userId);
|
||||||
baseEntity.setUpdateBy(userId);
|
baseEntity.setUpdateBy(userId);
|
||||||
baseEntity.setCreateDept(ObjectUtil.isNotNull(baseEntity.getCreateDept())
|
baseEntity.setCreateDept(ObjectUtils.notNull(baseEntity.getCreateDept(), loginUser.getDeptId()));
|
||||||
? baseEntity.getCreateDept() : loginUser.getDeptId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -3,6 +3,7 @@ package org.dromara.common.mybatis.handler;
|
|||||||
import cn.hutool.core.annotation.AnnotationUtil;
|
import cn.hutool.core.annotation.AnnotationUtil;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.sf.jsqlparser.JSQLParserException;
|
import net.sf.jsqlparser.JSQLParserException;
|
||||||
import net.sf.jsqlparser.expression.Expression;
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
@ -28,19 +29,13 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
|||||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||||
import org.springframework.core.type.ClassMetadata;
|
import org.springframework.core.type.ClassMetadata;
|
||||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||||
import org.springframework.expression.BeanResolver;
|
import org.springframework.expression.*;
|
||||||
import org.springframework.expression.ExpressionParser;
|
|
||||||
import org.springframework.expression.ParserContext;
|
|
||||||
import org.springframework.expression.common.TemplateParserContext;
|
import org.springframework.expression.common.TemplateParserContext;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@ -54,7 +49,7 @@ import java.util.function.Function;
|
|||||||
public class PlusDataPermissionHandler {
|
public class PlusDataPermissionHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 方法或类(名称) 与 注解的映射关系缓存
|
* 类名称与注解的映射关系缓存(由于aop无法拦截mybatis接口类上的注解 只能通过启动预扫描的方式进行)
|
||||||
*/
|
*/
|
||||||
private final Map<String, DataPermission> dataPermissionCacheMap = new ConcurrentHashMap<>();
|
private final Map<String, DataPermission> dataPermissionCacheMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@ -86,24 +81,24 @@ public class PlusDataPermissionHandler {
|
|||||||
* @return 数据过滤条件的 SQL 片段
|
* @return 数据过滤条件的 SQL 片段
|
||||||
*/
|
*/
|
||||||
public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) {
|
public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) {
|
||||||
// 获取数据权限配置
|
|
||||||
DataPermission dataPermission = getDataPermission(mappedStatementId);
|
|
||||||
// 获取当前登录用户信息
|
|
||||||
LoginUser currentUser = DataPermissionHelper.getVariable("user");
|
|
||||||
if (ObjectUtil.isNull(currentUser)) {
|
|
||||||
currentUser = LoginHelper.getLoginUser();
|
|
||||||
DataPermissionHelper.setVariable("user", currentUser);
|
|
||||||
}
|
|
||||||
// 如果是超级管理员或租户管理员,则不过滤数据
|
|
||||||
if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) {
|
|
||||||
return where;
|
|
||||||
}
|
|
||||||
// 构造数据过滤条件的 SQL 片段
|
|
||||||
String dataFilterSql = buildDataFilter(dataPermission, isSelect);
|
|
||||||
if (StringUtils.isBlank(dataFilterSql)) {
|
|
||||||
return where;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
|
// 获取数据权限配置
|
||||||
|
DataPermission dataPermission = getDataPermission(mappedStatementId);
|
||||||
|
// 获取当前登录用户信息
|
||||||
|
LoginUser currentUser = DataPermissionHelper.getVariable("user");
|
||||||
|
if (ObjectUtil.isNull(currentUser)) {
|
||||||
|
currentUser = LoginHelper.getLoginUser();
|
||||||
|
DataPermissionHelper.setVariable("user", currentUser);
|
||||||
|
}
|
||||||
|
// 如果是超级管理员或租户管理员,则不过滤数据
|
||||||
|
if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) {
|
||||||
|
return where;
|
||||||
|
}
|
||||||
|
// 构造数据过滤条件的 SQL 片段
|
||||||
|
String dataFilterSql = buildDataFilter(dataPermission, isSelect);
|
||||||
|
if (StringUtils.isBlank(dataFilterSql)) {
|
||||||
|
return where;
|
||||||
|
}
|
||||||
Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql);
|
Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql);
|
||||||
// 数据权限使用单独的括号 防止与其他条件冲突
|
// 数据权限使用单独的括号 防止与其他条件冲突
|
||||||
ParenthesedExpressionList<Expression> parenthesis = new ParenthesedExpressionList<>(expression);
|
ParenthesedExpressionList<Expression> parenthesis = new ParenthesedExpressionList<>(expression);
|
||||||
@ -114,6 +109,8 @@ public class PlusDataPermissionHandler {
|
|||||||
}
|
}
|
||||||
} catch (JSQLParserException e) {
|
} catch (JSQLParserException e) {
|
||||||
throw new ServiceException("数据权限解析异常 => " + e.getMessage());
|
throw new ServiceException("数据权限解析异常 => " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
DataPermissionHelper.removePermission();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,10 +129,33 @@ public class PlusDataPermissionHandler {
|
|||||||
joinStr = " " + dataPermission.joinStr() + " ";
|
joinStr = " " + dataPermission.joinStr() + " ";
|
||||||
}
|
}
|
||||||
LoginUser user = DataPermissionHelper.getVariable("user");
|
LoginUser user = DataPermissionHelper.getVariable("user");
|
||||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
Object defaultValue = "-1";
|
||||||
|
NullSafeStandardEvaluationContext context = new NullSafeStandardEvaluationContext(defaultValue);
|
||||||
|
context.addPropertyAccessor(new NullSafePropertyAccessor(context.getPropertyAccessors().get(0), defaultValue));
|
||||||
context.setBeanResolver(beanResolver);
|
context.setBeanResolver(beanResolver);
|
||||||
DataPermissionHelper.getContext().forEach(context::setVariable);
|
DataPermissionHelper.getContext().forEach(context::setVariable);
|
||||||
Set<String> conditions = new HashSet<>();
|
Set<String> conditions = new HashSet<>();
|
||||||
|
// 优先设置变量
|
||||||
|
List<String> keys = new ArrayList<>();
|
||||||
|
Map<DataColumn, Boolean> ignoreMap = new HashMap<>();
|
||||||
|
for (DataColumn dataColumn : dataPermission.value()) {
|
||||||
|
if (dataColumn.key().length != dataColumn.value().length) {
|
||||||
|
throw new ServiceException("角色数据范围异常 => key与value长度不匹配");
|
||||||
|
}
|
||||||
|
// 包含权限标识符 这直接跳过
|
||||||
|
if (StringUtils.isNotBlank(dataColumn.permission()) &&
|
||||||
|
CollUtil.contains(user.getMenuPermission(), dataColumn.permission())
|
||||||
|
) {
|
||||||
|
ignoreMap.put(dataColumn, Boolean.TRUE);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 设置注解变量 key 为表达式变量 value 为变量值
|
||||||
|
for (int i = 0; i < dataColumn.key().length; i++) {
|
||||||
|
context.setVariable(dataColumn.key()[i], dataColumn.value()[i]);
|
||||||
|
}
|
||||||
|
keys.addAll(Arrays.stream(dataColumn.key()).map(key -> "#" + key).toList());
|
||||||
|
}
|
||||||
|
|
||||||
for (RoleDTO role : user.getRoles()) {
|
for (RoleDTO role : user.getRoles()) {
|
||||||
user.setRoleId(role.getRoleId());
|
user.setRoleId(role.getRoleId());
|
||||||
// 获取角色权限泛型
|
// 获取角色权限泛型
|
||||||
@ -145,33 +165,30 @@ public class PlusDataPermissionHandler {
|
|||||||
}
|
}
|
||||||
// 全部数据权限直接返回
|
// 全部数据权限直接返回
|
||||||
if (type == DataScopeType.ALL) {
|
if (type == DataScopeType.ALL) {
|
||||||
return "";
|
return StringUtils.EMPTY;
|
||||||
}
|
}
|
||||||
boolean isSuccess = false;
|
boolean isSuccess = false;
|
||||||
for (DataColumn dataColumn : dataPermission.value()) {
|
for (DataColumn dataColumn : dataPermission.value()) {
|
||||||
if (dataColumn.key().length != dataColumn.value().length) {
|
|
||||||
throw new ServiceException("角色数据范围异常 => key与value长度不匹配");
|
|
||||||
}
|
|
||||||
// 不包含 key 变量 则不处理
|
|
||||||
if (!StringUtils.containsAny(type.getSqlTemplate(),
|
|
||||||
Arrays.stream(dataColumn.key()).map(key -> "#" + key).toArray(String[]::new)
|
|
||||||
)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 包含权限标识符 这直接跳过
|
// 包含权限标识符 这直接跳过
|
||||||
if (StringUtils.isNotBlank(dataColumn.permission()) &&
|
if (ignoreMap.containsKey(dataColumn)) {
|
||||||
CollUtil.contains(user.getMenuPermission(), dataColumn.permission())
|
// 修复多角色与权限标识符共用问题 https://gitee.com/dromara/RuoYi-Vue-Plus/issues/IB4CS4
|
||||||
) {
|
conditions.add(joinStr + " 1 = 1 ");
|
||||||
isSuccess = true;
|
isSuccess = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 设置注解变量 key 为表达式变量 value 为变量值
|
// 不包含 key 变量 则不处理
|
||||||
for (int i = 0; i < dataColumn.key().length; i++) {
|
if (!StringUtils.containsAny(type.getSqlTemplate(), keys.toArray(String[]::new))) {
|
||||||
context.setVariable(dataColumn.key()[i], dataColumn.value()[i]);
|
continue;
|
||||||
}
|
}
|
||||||
|
// 当前注解不满足模板 不处理
|
||||||
|
if (!StringUtils.containsAny(type.getSqlTemplate(), dataColumn.key())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 忽略数据权限 防止spel表达式内有其他sql查询导致死循环调用
|
||||||
|
String sql = DataPermissionHelper.ignore(() ->
|
||||||
|
parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class)
|
||||||
|
);
|
||||||
// 解析sql模板并填充
|
// 解析sql模板并填充
|
||||||
String sql = parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class);
|
|
||||||
conditions.add(joinStr + sql);
|
conditions.add(joinStr + sql);
|
||||||
isSuccess = true;
|
isSuccess = true;
|
||||||
}
|
}
|
||||||
@ -185,7 +202,7 @@ public class PlusDataPermissionHandler {
|
|||||||
String sql = StreamUtils.join(conditions, Function.identity(), "");
|
String sql = StreamUtils.join(conditions, Function.identity(), "");
|
||||||
return sql.substring(joinStr.length());
|
return sql.substring(joinStr.length());
|
||||||
}
|
}
|
||||||
return "";
|
return StringUtils.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -212,7 +229,10 @@ public class PlusDataPermissionHandler {
|
|||||||
// 获取资源对应的类对象
|
// 获取资源对应的类对象
|
||||||
Class<?> clazz = Resources.classForName(classMetadata.getClassName());
|
Class<?> clazz = Resources.classForName(classMetadata.getClassName());
|
||||||
// 查找类中的特定注解
|
// 查找类中的特定注解
|
||||||
findAnnotation(clazz);
|
if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) {
|
||||||
|
DataPermission dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class);
|
||||||
|
dataPermissionCacheMap.put(clazz.getName(), dataPermission);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -220,29 +240,6 @@ public class PlusDataPermissionHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 在指定的类中查找特定的注解 DataPermission,并将带有这个注解的方法或类存储到 dataPermissionCacheMap 中
|
|
||||||
*
|
|
||||||
* @param clazz 要查找的类
|
|
||||||
*/
|
|
||||||
private void findAnnotation(Class<?> clazz) {
|
|
||||||
DataPermission dataPermission;
|
|
||||||
for (Method method : clazz.getMethods()) {
|
|
||||||
if (method.isDefault() || method.isVarArgs()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String mappedStatementId = clazz.getName() + "." + method.getName();
|
|
||||||
if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) {
|
|
||||||
dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class);
|
|
||||||
dataPermissionCacheMap.put(mappedStatementId, dataPermission);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) {
|
|
||||||
dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class);
|
|
||||||
dataPermissionCacheMap.put(clazz.getName(), dataPermission);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据映射语句 ID 或类名获取对应的 DataPermission 注解对象
|
* 根据映射语句 ID 或类名获取对应的 DataPermission 注解对象
|
||||||
*
|
*
|
||||||
@ -250,9 +247,9 @@ public class PlusDataPermissionHandler {
|
|||||||
* @return DataPermission 注解对象,如果不存在则返回 null
|
* @return DataPermission 注解对象,如果不存在则返回 null
|
||||||
*/
|
*/
|
||||||
public DataPermission getDataPermission(String mapperId) {
|
public DataPermission getDataPermission(String mapperId) {
|
||||||
// 检查缓存中是否包含映射语句 ID 对应的 DataPermission 注解对象
|
// 检查上下文中是否包含映射语句 ID 对应的 DataPermission 注解对象
|
||||||
if (dataPermissionCacheMap.containsKey(mapperId)) {
|
if (DataPermissionHelper.getPermission() != null) {
|
||||||
return dataPermissionCacheMap.get(mapperId);
|
return DataPermissionHelper.getPermission();
|
||||||
}
|
}
|
||||||
// 如果缓存中不包含映射语句 ID 对应的 DataPermission 注解对象,则尝试使用类名作为键查找
|
// 如果缓存中不包含映射语句 ID 对应的 DataPermission 注解对象,则尝试使用类名作为键查找
|
||||||
String clazzName = mapperId.substring(0, mapperId.lastIndexOf("."));
|
String clazzName = mapperId.substring(0, mapperId.lastIndexOf("."));
|
||||||
@ -271,4 +268,65 @@ public class PlusDataPermissionHandler {
|
|||||||
public boolean invalid(String mapperId) {
|
public boolean invalid(String mapperId) {
|
||||||
return getDataPermission(mapperId) == null;
|
return getDataPermission(mapperId) == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对所有null变量找不到的变量返回默认值
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
private static class NullSafeStandardEvaluationContext extends StandardEvaluationContext {
|
||||||
|
|
||||||
|
private final Object defaultValue;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object lookupVariable(String name) {
|
||||||
|
Object obj = super.lookupVariable(name);
|
||||||
|
// 如果读取到的值是 null,则返回默认值
|
||||||
|
if (obj == null) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对所有null变量找不到的变量返回默认值 委托模式 将不需要处理的方法委托给原处理器
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
private static class NullSafePropertyAccessor implements PropertyAccessor {
|
||||||
|
|
||||||
|
private final PropertyAccessor delegate;
|
||||||
|
private final Object defaultValue;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?>[] getSpecificTargetClasses() {
|
||||||
|
return delegate.getSpecificTargetClasses();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
|
||||||
|
return delegate.canRead(context, target, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
|
||||||
|
TypedValue value = delegate.read(context, target, name);
|
||||||
|
// 如果读取到的值是 null,则返回默认值
|
||||||
|
if (value.getValue() == null) {
|
||||||
|
return new TypedValue(defaultValue);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
|
||||||
|
return delegate.canWrite(context, target, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
|
||||||
|
delegate.write(context, target, name, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package org.dromara.common.mybatis.handler;
|
||||||
|
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||||
|
import org.apache.ibatis.session.Configuration;
|
||||||
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
|
import org.dromara.common.core.utils.reflect.ReflectUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改表信息初始化方式
|
||||||
|
* 目前用于全局修改是否使用逻辑删除
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
public class PlusPostInitTableInfoHandler implements PostInitTableInfoHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postTableInfo(TableInfo tableInfo, Configuration configuration) {
|
||||||
|
String flag = SpringUtils.getProperty("mybatis-plus.enableLogicDelete", "true");
|
||||||
|
// 只有关闭时 统一设置false 为true时mp自动判断不处理
|
||||||
|
if (!Convert.toBool(flag)) {
|
||||||
|
ReflectUtils.setFieldValue(tableInfo, "withLogicDelete", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
|
|||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.dromara.common.core.utils.reflect.ReflectUtils;
|
import org.dromara.common.core.utils.reflect.ReflectUtils;
|
||||||
|
import org.dromara.common.mybatis.annotation.DataPermission;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -29,6 +30,33 @@ public class DataPermissionHelper {
|
|||||||
|
|
||||||
private static final ThreadLocal<Stack<Integer>> REENTRANT_IGNORE = ThreadLocal.withInitial(Stack::new);
|
private static final ThreadLocal<Stack<Integer>> REENTRANT_IGNORE = ThreadLocal.withInitial(Stack::new);
|
||||||
|
|
||||||
|
private static final ThreadLocal<DataPermission> PERMISSION_CACHE = new ThreadLocal<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前执行mapper权限注解
|
||||||
|
*
|
||||||
|
* @return 返回当前执行mapper权限注解
|
||||||
|
*/
|
||||||
|
public static DataPermission getPermission() {
|
||||||
|
return PERMISSION_CACHE.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前执行mapper权限注解
|
||||||
|
*
|
||||||
|
* @param dataPermission 数据权限注解
|
||||||
|
*/
|
||||||
|
public static void setPermission(DataPermission dataPermission) {
|
||||||
|
PERMISSION_CACHE.set(dataPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除当前执行mapper权限注解
|
||||||
|
*/
|
||||||
|
public static void removePermission() {
|
||||||
|
PERMISSION_CACHE.remove();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从上下文中获取指定键的变量值,并将其转换为指定的类型
|
* 从上下文中获取指定键的变量值,并将其转换为指定的类型
|
||||||
*
|
*
|
||||||
|
@ -24,8 +24,8 @@ mybatis-plus:
|
|||||||
# 主键类型
|
# 主键类型
|
||||||
# AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID
|
# AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID
|
||||||
idType: ASSIGN_ID
|
idType: ASSIGN_ID
|
||||||
# 逻辑已删除值(框架表均使用此值 禁止随意修改)
|
# 逻辑已删除值(可按需求随意修改)
|
||||||
logicDeleteValue: 2
|
logicDeleteValue: 1
|
||||||
# 逻辑未删除值
|
# 逻辑未删除值
|
||||||
logicNotDeleteValue: 0
|
logicNotDeleteValue: 0
|
||||||
insertStrategy: NOT_NULL
|
insertStrategy: NOT_NULL
|
||||||
|
@ -9,7 +9,6 @@ import org.dromara.common.core.utils.file.FileUtils;
|
|||||||
import org.dromara.common.oss.constant.OssConstant;
|
import org.dromara.common.oss.constant.OssConstant;
|
||||||
import org.dromara.common.oss.entity.UploadResult;
|
import org.dromara.common.oss.entity.UploadResult;
|
||||||
import org.dromara.common.oss.enumd.AccessPolicyType;
|
import org.dromara.common.oss.enumd.AccessPolicyType;
|
||||||
import org.dromara.common.oss.enumd.PolicyType;
|
|
||||||
import org.dromara.common.oss.exception.OssException;
|
import org.dromara.common.oss.exception.OssException;
|
||||||
import org.dromara.common.oss.properties.OssProperties;
|
import org.dromara.common.oss.properties.OssProperties;
|
||||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||||
@ -22,8 +21,6 @@ import software.amazon.awssdk.services.s3.S3AsyncClient;
|
|||||||
import software.amazon.awssdk.services.s3.S3Configuration;
|
import software.amazon.awssdk.services.s3.S3Configuration;
|
||||||
import software.amazon.awssdk.services.s3.crt.S3CrtHttpConfiguration;
|
import software.amazon.awssdk.services.s3.crt.S3CrtHttpConfiguration;
|
||||||
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
|
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
|
||||||
import software.amazon.awssdk.services.s3.model.NoSuchBucketException;
|
|
||||||
import software.amazon.awssdk.services.s3.model.S3Exception;
|
|
||||||
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
|
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
|
||||||
import software.amazon.awssdk.transfer.s3.S3TransferManager;
|
import software.amazon.awssdk.transfer.s3.S3TransferManager;
|
||||||
import software.amazon.awssdk.transfer.s3.model.*;
|
import software.amazon.awssdk.transfer.s3.model.*;
|
||||||
@ -35,6 +32,7 @@ import java.net.URL;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* S3 存储协议 所有兼容S3协议的云厂商均支持
|
* S3 存储协议 所有兼容S3协议的云厂商均支持
|
||||||
@ -115,8 +113,6 @@ public class OssClient {
|
|||||||
.serviceConfiguration(config)
|
.serviceConfiguration(config)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// 创建存储桶
|
|
||||||
createBucket();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (e instanceof OssException) {
|
if (e instanceof OssException) {
|
||||||
throw e;
|
throw e;
|
||||||
@ -125,43 +121,6 @@ public class OssClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步创建存储桶
|
|
||||||
* 如果存储桶不存在,会进行创建;如果存储桶存在,不执行任何操作
|
|
||||||
*
|
|
||||||
* @throws OssException 当创建存储桶时发生异常时抛出
|
|
||||||
*/
|
|
||||||
public void createBucket() {
|
|
||||||
String bucketName = properties.getBucketName();
|
|
||||||
try {
|
|
||||||
// 尝试获取存储桶的信息
|
|
||||||
client.headBucket(
|
|
||||||
x -> x.bucket(bucketName)
|
|
||||||
.build())
|
|
||||||
.join();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
if (ex.getCause() instanceof NoSuchBucketException) {
|
|
||||||
try {
|
|
||||||
// 存储桶不存在,尝试创建存储桶
|
|
||||||
client.createBucket(
|
|
||||||
x -> x.bucket(bucketName))
|
|
||||||
.join();
|
|
||||||
|
|
||||||
// 设置存储桶的访问策略(Bucket Policy)
|
|
||||||
client.putBucketPolicy(
|
|
||||||
x -> x.bucket(bucketName)
|
|
||||||
.policy(getPolicy(bucketName, getAccessPolicy().getPolicyType())))
|
|
||||||
.join();
|
|
||||||
} catch (S3Exception e) {
|
|
||||||
// 存储桶创建或策略设置失败
|
|
||||||
throw new OssException("创建Bucket失败, 请核对配置信息:[" + e.getMessage() + "]");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new OssException("判断Bucket是否存在失败,请核对配置信息:[" + ex.getMessage() + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传文件到 Amazon S3,并返回上传结果
|
* 上传文件到 Amazon S3,并返回上传结果
|
||||||
*
|
*
|
||||||
@ -281,10 +240,11 @@ public class OssClient {
|
|||||||
*
|
*
|
||||||
* @param key 文件在 Amazon S3 中的对象键
|
* @param key 文件在 Amazon S3 中的对象键
|
||||||
* @param out 输出流
|
* @param out 输出流
|
||||||
|
* @param consumer 自定义处理逻辑
|
||||||
* @return 输出流中写入的字节数(长度)
|
* @return 输出流中写入的字节数(长度)
|
||||||
* @throws OssException 如果下载失败,抛出自定义异常
|
* @throws OssException 如果下载失败,抛出自定义异常
|
||||||
*/
|
*/
|
||||||
public long download(String key, OutputStream out) {
|
public void download(String key, OutputStream out, Consumer<Long> consumer) {
|
||||||
try {
|
try {
|
||||||
// 构建下载请求
|
// 构建下载请求
|
||||||
DownloadRequest<ResponseInputStream<GetObjectResponse>> downloadRequest = DownloadRequest.builder()
|
DownloadRequest<ResponseInputStream<GetObjectResponse>> downloadRequest = DownloadRequest.builder()
|
||||||
@ -300,7 +260,10 @@ public class OssClient {
|
|||||||
Download<ResponseInputStream<GetObjectResponse>> responseFuture = transferManager.download(downloadRequest);
|
Download<ResponseInputStream<GetObjectResponse>> responseFuture = transferManager.download(downloadRequest);
|
||||||
// 输出到流中
|
// 输出到流中
|
||||||
try (ResponseInputStream<GetObjectResponse> responseStream = responseFuture.completionFuture().join().result()) { // auto-closeable stream
|
try (ResponseInputStream<GetObjectResponse> responseStream = responseFuture.completionFuture().join().result()) { // auto-closeable stream
|
||||||
return responseStream.transferTo(out); // 阻塞调用线程 blocks the calling thread
|
if (consumer != null) {
|
||||||
|
consumer.accept(responseStream.response().contentLength());
|
||||||
|
}
|
||||||
|
responseStream.transferTo(out); // 阻塞调用线程 blocks the calling thread
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new OssException("文件下载失败,错误信息:[" + e.getMessage() + "]");
|
throw new OssException("文件下载失败,错误信息:[" + e.getMessage() + "]");
|
||||||
@ -326,13 +289,13 @@ public class OssClient {
|
|||||||
/**
|
/**
|
||||||
* 获取私有URL链接
|
* 获取私有URL链接
|
||||||
*
|
*
|
||||||
* @param objectKey 对象KEY
|
* @param objectKey 对象KEY
|
||||||
* @param second 授权时间
|
* @param expiredTime 链接授权到期时间
|
||||||
*/
|
*/
|
||||||
public String getPrivateUrl(String objectKey, Integer second) {
|
public String getPrivateUrl(String objectKey, Duration expiredTime) {
|
||||||
// 使用 AWS S3 预签名 URL 的生成器 获取对象的预签名 URL
|
// 使用 AWS S3 预签名 URL 的生成器 获取对象的预签名 URL
|
||||||
URL url = presigner.presignGetObject(
|
URL url = presigner.presignGetObject(
|
||||||
x -> x.signatureDuration(Duration.ofSeconds(second))
|
x -> x.signatureDuration(expiredTime)
|
||||||
.getObjectRequest(
|
.getObjectRequest(
|
||||||
y -> y.bucket(properties.getBucketName())
|
y -> y.bucket(properties.getBucketName())
|
||||||
.key(objectKey)
|
.key(objectKey)
|
||||||
@ -529,77 +492,4 @@ public class OssClient {
|
|||||||
return AccessPolicyType.getByType(properties.getAccessPolicy());
|
return AccessPolicyType.getByType(properties.getAccessPolicy());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成 AWS S3 存储桶访问策略
|
|
||||||
*
|
|
||||||
* @param bucketName 存储桶
|
|
||||||
* @param policyType 桶策略类型
|
|
||||||
* @return 符合 AWS S3 存储桶访问策略格式的字符串
|
|
||||||
*/
|
|
||||||
private static String getPolicy(String bucketName, PolicyType policyType) {
|
|
||||||
String policy = switch (policyType) {
|
|
||||||
case WRITE -> """
|
|
||||||
{
|
|
||||||
"Version": "2012-10-17",
|
|
||||||
"Statement": []
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
case READ_WRITE -> """
|
|
||||||
{
|
|
||||||
"Version": "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": "*",
|
|
||||||
"Action": [
|
|
||||||
"s3:GetBucketLocation",
|
|
||||||
"s3:ListBucket",
|
|
||||||
"s3:ListBucketMultipartUploads"
|
|
||||||
],
|
|
||||||
"Resource": "arn:aws:s3:::bucketName"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": "*",
|
|
||||||
"Action": [
|
|
||||||
"s3:AbortMultipartUpload",
|
|
||||||
"s3:DeleteObject",
|
|
||||||
"s3:GetObject",
|
|
||||||
"s3:ListMultipartUploadParts",
|
|
||||||
"s3:PutObject"
|
|
||||||
],
|
|
||||||
"Resource": "arn:aws:s3:::bucketName/*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
case READ -> """
|
|
||||||
{
|
|
||||||
"Version": "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": "*",
|
|
||||||
"Action": ["s3:GetBucketLocation"],
|
|
||||||
"Resource": "arn:aws:s3:::bucketName"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Effect": "Deny",
|
|
||||||
"Principal": "*",
|
|
||||||
"Action": ["s3:ListBucket"],
|
|
||||||
"Resource": "arn:aws:s3:::bucketName"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": "*",
|
|
||||||
"Action": "s3:GetObject",
|
|
||||||
"Resource": "arn:aws:s3:::bucketName/*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
};
|
|
||||||
return policy.replaceAll("bucketName", bucketName);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,17 +17,17 @@ public enum AccessPolicyType {
|
|||||||
/**
|
/**
|
||||||
* private
|
* private
|
||||||
*/
|
*/
|
||||||
PRIVATE("0", BucketCannedACL.PRIVATE, ObjectCannedACL.PRIVATE, PolicyType.WRITE),
|
PRIVATE("0", BucketCannedACL.PRIVATE, ObjectCannedACL.PRIVATE),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* public
|
* public
|
||||||
*/
|
*/
|
||||||
PUBLIC("1", BucketCannedACL.PUBLIC_READ_WRITE, ObjectCannedACL.PUBLIC_READ_WRITE, PolicyType.READ_WRITE),
|
PUBLIC("1", BucketCannedACL.PUBLIC_READ_WRITE, ObjectCannedACL.PUBLIC_READ_WRITE),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* custom
|
* custom
|
||||||
*/
|
*/
|
||||||
CUSTOM("2", BucketCannedACL.PUBLIC_READ, ObjectCannedACL.PUBLIC_READ, PolicyType.READ);
|
CUSTOM("2", BucketCannedACL.PUBLIC_READ, ObjectCannedACL.PUBLIC_READ);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 桶 权限类型(数据库值)
|
* 桶 权限类型(数据库值)
|
||||||
@ -44,11 +44,6 @@ public enum AccessPolicyType {
|
|||||||
*/
|
*/
|
||||||
private final ObjectCannedACL objectCannedACL;
|
private final ObjectCannedACL objectCannedACL;
|
||||||
|
|
||||||
/**
|
|
||||||
* 桶策略类型
|
|
||||||
*/
|
|
||||||
private final PolicyType policyType;
|
|
||||||
|
|
||||||
public static AccessPolicyType getByType(String type) {
|
public static AccessPolicyType getByType(String type) {
|
||||||
for (AccessPolicyType value : values()) {
|
for (AccessPolicyType value : values()) {
|
||||||
if (value.getType().equals(type)) {
|
if (value.getType().equals(type)) {
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
package org.dromara.common.oss.enumd;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* minio策略配置
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@AllArgsConstructor
|
|
||||||
public enum PolicyType {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 只读
|
|
||||||
*/
|
|
||||||
READ("read-only"),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 只写
|
|
||||||
*/
|
|
||||||
WRITE("write-only"),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读写
|
|
||||||
*/
|
|
||||||
READ_WRITE("read-write");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 类型
|
|
||||||
*/
|
|
||||||
private final String type;
|
|
||||||
|
|
||||||
}
|
|
@ -38,4 +38,10 @@ public @interface RateLimiter {
|
|||||||
* 提示消息 支持国际化 格式为 {code}
|
* 提示消息 支持国际化 格式为 {code}
|
||||||
*/
|
*/
|
||||||
String message() default "{rate.limiter.message}";
|
String message() default "{rate.limiter.message}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限流策略超时时间 默认一天(策略存活时间 会清除已存在的策略数据)
|
||||||
|
*/
|
||||||
|
int timeout() default 86400;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -54,13 +54,14 @@ public class RateLimiterAspect {
|
|||||||
public void doBefore(JoinPoint point, RateLimiter rateLimiter) {
|
public void doBefore(JoinPoint point, RateLimiter rateLimiter) {
|
||||||
int time = rateLimiter.time();
|
int time = rateLimiter.time();
|
||||||
int count = rateLimiter.count();
|
int count = rateLimiter.count();
|
||||||
|
int timeout = rateLimiter.timeout();
|
||||||
try {
|
try {
|
||||||
String combineKey = getCombineKey(rateLimiter, point);
|
String combineKey = getCombineKey(rateLimiter, point);
|
||||||
RateType rateType = RateType.OVERALL;
|
RateType rateType = RateType.OVERALL;
|
||||||
if (rateLimiter.limitType() == LimitType.CLUSTER) {
|
if (rateLimiter.limitType() == LimitType.CLUSTER) {
|
||||||
rateType = RateType.PER_CLIENT;
|
rateType = RateType.PER_CLIENT;
|
||||||
}
|
}
|
||||||
long number = RedisUtils.rateLimiter(combineKey, rateType, count, time);
|
long number = RedisUtils.rateLimiter(combineKey, rateType, count, time, timeout);
|
||||||
if (number == -1) {
|
if (number == -1) {
|
||||||
String message = rateLimiter.message();
|
String message = rateLimiter.message();
|
||||||
if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) {
|
if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) {
|
||||||
|
@ -42,6 +42,18 @@
|
|||||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- <!– redis序列化替代方案 比json快无数的跨语言二进制序列化 –>-->
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>org.apache.fury</groupId>-->
|
||||||
|
<!-- <artifactId>fury-core</artifactId>-->
|
||||||
|
<!-- <version>0.9.0</version>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>org.slf4j</groupId>-->
|
||||||
|
<!-- <artifactId>slf4j-api</artifactId>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -53,6 +53,9 @@ public class RedisConfig {
|
|||||||
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||||
// 指定序列化输入的类型,类必须是非final修饰的。序列化时将对象全类名一起保存下来
|
// 指定序列化输入的类型,类必须是非final修饰的。序列化时将对象全类名一起保存下来
|
||||||
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
|
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
|
||||||
|
// LoggerFactory.useSlf4jLogging(true);
|
||||||
|
// FuryCodec furyCodec = new FuryCodec();
|
||||||
|
// CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, furyCodec, furyCodec);
|
||||||
TypedJsonJacksonCodec jsonCodec = new TypedJsonJacksonCodec(Object.class, om);
|
TypedJsonJacksonCodec jsonCodec = new TypedJsonJacksonCodec(Object.class, om);
|
||||||
// 组合序列化 key 使用 String 内容使用通用 json 格式
|
// 组合序列化 key 使用 String 内容使用通用 json 格式
|
||||||
CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, jsonCodec, jsonCodec);
|
CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, jsonCodec, jsonCodec);
|
||||||
|
@ -174,12 +174,12 @@ public class QueueUtils {
|
|||||||
*
|
*
|
||||||
* @param queueName 队列名
|
* @param queueName 队列名
|
||||||
* @param capacity 容量
|
* @param capacity 容量
|
||||||
* @param destroy 已存在是否销毁
|
* @param destroy 是否销毁
|
||||||
*/
|
*/
|
||||||
public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity, boolean destroy) {
|
public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity, boolean destroy) {
|
||||||
RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
|
RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
|
||||||
if (boundedBlockingQueue.isExists() && destroy) {
|
if (destroy) {
|
||||||
destroyQueue(queueName);
|
boundedBlockingQueue.delete();
|
||||||
}
|
}
|
||||||
return boundedBlockingQueue.trySetCapacity(capacity);
|
return boundedBlockingQueue.trySetCapacity(capacity);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import lombok.AccessLevel;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.dromara.common.core.utils.SpringUtils;
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
import org.redisson.api.*;
|
import org.redisson.api.*;
|
||||||
|
import org.redisson.api.options.KeysScanOptions;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -36,8 +37,22 @@ public class RedisUtils {
|
|||||||
* @return -1 表示失败
|
* @return -1 表示失败
|
||||||
*/
|
*/
|
||||||
public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) {
|
public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) {
|
||||||
|
return rateLimiter(key, rateType, rate, rateInterval, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限流
|
||||||
|
*
|
||||||
|
* @param key 限流key
|
||||||
|
* @param rateType 限流类型
|
||||||
|
* @param rate 速率
|
||||||
|
* @param rateInterval 速率间隔
|
||||||
|
* @param timeout 超时时间
|
||||||
|
* @return -1 表示失败
|
||||||
|
*/
|
||||||
|
public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval, int timeout) {
|
||||||
RRateLimiter rateLimiter = CLIENT.getRateLimiter(key);
|
RRateLimiter rateLimiter = CLIENT.getRateLimiter(key);
|
||||||
rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS);
|
rateLimiter.trySetRate(rateType, rate, Duration.ofSeconds(rateInterval), Duration.ofSeconds(timeout));
|
||||||
if (rateLimiter.tryAcquire()) {
|
if (rateLimiter.tryAcquire()) {
|
||||||
return rateLimiter.availablePermits();
|
return rateLimiter.availablePermits();
|
||||||
} else {
|
} else {
|
||||||
@ -518,13 +533,34 @@ public class RedisUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得缓存的基本对象列表(全局匹配忽略租户 自行拼接租户id)
|
* 获得缓存的基本对象列表(全局匹配忽略租户 自行拼接租户id)
|
||||||
*
|
* <P>
|
||||||
|
* limit-设置扫描的限制数量(默认为0,查询全部)
|
||||||
|
* pattern-设置键的匹配模式(默认为null)
|
||||||
|
* chunkSize-设置每次扫描的块大小(默认为0,本方法设置为1000)
|
||||||
|
* type-设置键的类型(默认为null,查询全部类型)
|
||||||
|
* </P>
|
||||||
|
* @see KeysScanOptions
|
||||||
* @param pattern 字符串前缀
|
* @param pattern 字符串前缀
|
||||||
* @return 对象列表
|
* @return 对象列表
|
||||||
*/
|
*/
|
||||||
public static Collection<String> keys(final String pattern) {
|
public static Collection<String> keys(final String pattern) {
|
||||||
Stream<String> stream = CLIENT.getKeys().getKeysStreamByPattern(pattern);
|
return keys(KeysScanOptions.defaults().pattern(pattern).chunkSize(1000));
|
||||||
return stream.collect(Collectors.toList());
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过扫描参数获取缓存的基本对象列表
|
||||||
|
* @param keysScanOptions 扫描参数
|
||||||
|
* <P>
|
||||||
|
* limit-设置扫描的限制数量(默认为0,查询全部)
|
||||||
|
* pattern-设置键的匹配模式(默认为null)
|
||||||
|
* chunkSize-设置每次扫描的块大小(默认为0)
|
||||||
|
* type-设置键的类型(默认为null,查询全部类型)
|
||||||
|
* </P>
|
||||||
|
* @see KeysScanOptions
|
||||||
|
*/
|
||||||
|
public static Collection<String> keys(final KeysScanOptions keysScanOptions) {
|
||||||
|
Stream<String> keysStream = CLIENT.getKeys().getKeysStream(keysScanOptions);
|
||||||
|
return keysStream.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,165 @@
|
|||||||
|
package org.dromara.common.redis.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.redisson.api.RIdGenerator;
|
||||||
|
import org.redisson.api.RedissonClient;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发号器工具类
|
||||||
|
*
|
||||||
|
* @author 秋辞未寒
|
||||||
|
* @date 2024-12-10
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class SequenceUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认初始值
|
||||||
|
*/
|
||||||
|
public static final Long DEFAULT_INIT_VALUE = 1L;
|
||||||
|
/**
|
||||||
|
* 默认步长
|
||||||
|
*/
|
||||||
|
public static final Long DEFAULT_STEP_VALUE = 1L;
|
||||||
|
/**
|
||||||
|
* 默认过期时间-天
|
||||||
|
*/
|
||||||
|
public static final Duration DEFAULT_EXPIRE_TIME_DAY = Duration.ofDays(1);
|
||||||
|
/**
|
||||||
|
* 默认过期时间-分钟
|
||||||
|
*/
|
||||||
|
public static final Duration DEFAULT_EXPIRE_TIME_MINUTE = Duration.ofMinutes(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Redisson客户端实例
|
||||||
|
*/
|
||||||
|
private static final RedissonClient REDISSON_CLIENT = SpringUtils.getBean(RedissonClient.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取ID生成器
|
||||||
|
*
|
||||||
|
* @param key 业务key
|
||||||
|
* @param expireTime 过期时间
|
||||||
|
* @param initValue ID初始值
|
||||||
|
* @param stepValue ID步长
|
||||||
|
* @return ID生成器
|
||||||
|
*/
|
||||||
|
private static RIdGenerator getIdGenerator(String key, Duration expireTime, Long initValue, Long stepValue) {
|
||||||
|
if (initValue == null || initValue <= 0) {
|
||||||
|
initValue = DEFAULT_INIT_VALUE;
|
||||||
|
}
|
||||||
|
if (stepValue == null || stepValue <= 0) {
|
||||||
|
stepValue = DEFAULT_STEP_VALUE;
|
||||||
|
}
|
||||||
|
RIdGenerator idGenerator = REDISSON_CLIENT.getIdGenerator(key);
|
||||||
|
// 设置初始值和步长
|
||||||
|
idGenerator.tryInit(initValue, stepValue);
|
||||||
|
// 设置过期时间
|
||||||
|
idGenerator.expire(expireTime);
|
||||||
|
return idGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定业务key的唯一id
|
||||||
|
*
|
||||||
|
* @param key 业务key
|
||||||
|
* @param expireTime 过期时间
|
||||||
|
* @param initValue ID初始值
|
||||||
|
* @param stepValue ID步长
|
||||||
|
* @return 唯一id
|
||||||
|
*/
|
||||||
|
public static long nextId(String key, Duration expireTime, Long initValue, Long stepValue) {
|
||||||
|
return getIdGenerator(key, expireTime, initValue, stepValue).nextId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定业务key的唯一id字符串
|
||||||
|
*
|
||||||
|
* @param key 业务key
|
||||||
|
* @param expireTime 过期时间
|
||||||
|
* @param initValue ID初始值
|
||||||
|
* @param stepValue ID步长
|
||||||
|
* @return 唯一id
|
||||||
|
*/
|
||||||
|
public static String nextIdStr(String key, Duration expireTime, Long initValue, Long stepValue) {
|
||||||
|
return String.valueOf(nextId(key, expireTime, initValue, stepValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定业务key的唯一id (ID初始值=1,ID步长=1)
|
||||||
|
*
|
||||||
|
* @param key 业务key
|
||||||
|
* @param expireTime 过期时间
|
||||||
|
* @return 唯一id
|
||||||
|
*/
|
||||||
|
public static long nextId(String key, Duration expireTime) {
|
||||||
|
return getIdGenerator(key, expireTime, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定业务key的唯一id字符串 (ID初始值=1,ID步长=1)
|
||||||
|
*
|
||||||
|
* @param key 业务key
|
||||||
|
* @param expireTime 过期时间
|
||||||
|
* @return 唯一id
|
||||||
|
*/
|
||||||
|
public static String nextIdStr(String key, Duration expireTime) {
|
||||||
|
return String.valueOf(nextId(key, expireTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 yyyyMMdd 开头的唯一id
|
||||||
|
*
|
||||||
|
* @return 唯一id
|
||||||
|
*/
|
||||||
|
public static String nextIdDate() {
|
||||||
|
return nextIdDate("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 prefix + yyyyMMdd 开头的唯一id
|
||||||
|
*
|
||||||
|
* @param prefix 业务前缀
|
||||||
|
* @return 唯一id
|
||||||
|
*/
|
||||||
|
public static String nextIdDate(String prefix) {
|
||||||
|
// 前缀+日期 构建 prefixKey
|
||||||
|
String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMATTER));
|
||||||
|
// 获取下一个id
|
||||||
|
long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_DAY, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId();
|
||||||
|
// 返回完整id
|
||||||
|
return StringUtils.format("{}{}", prefixKey, nextId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 yyyyMMddHHmmss 开头的唯一id
|
||||||
|
*
|
||||||
|
* @return 唯一id
|
||||||
|
*/
|
||||||
|
public static String nextIdDateTime() {
|
||||||
|
return nextIdDateTime("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 prefix + yyyyMMddHHmmss 开头的唯一id
|
||||||
|
*
|
||||||
|
* @param prefix 业务前缀
|
||||||
|
* @return 唯一id
|
||||||
|
*/
|
||||||
|
public static String nextIdDateTime(String prefix) {
|
||||||
|
// 前缀+日期时间 构建 prefixKey
|
||||||
|
String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_FORMATTER));
|
||||||
|
// 获取下一个id
|
||||||
|
long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_MINUTE, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId();
|
||||||
|
// 返回完整id
|
||||||
|
return StringUtils.format("{}{}", prefixKey, nextId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -51,7 +51,11 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||||||
if (timeout == NEVER_EXPIRE) {
|
if (timeout == NEVER_EXPIRE) {
|
||||||
RedisUtils.setCacheObject(key, value);
|
RedisUtils.setCacheObject(key, value);
|
||||||
} else {
|
} else {
|
||||||
RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
|
if (RedisUtils.hasKey(key)) {
|
||||||
|
RedisUtils.setCacheObject(key, value, true);
|
||||||
|
} else {
|
||||||
|
RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
CAFFEINE.invalidate(key);
|
CAFFEINE.invalidate(key);
|
||||||
}
|
}
|
||||||
@ -114,7 +118,11 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||||||
if (timeout == NEVER_EXPIRE) {
|
if (timeout == NEVER_EXPIRE) {
|
||||||
RedisUtils.setCacheObject(key, object);
|
RedisUtils.setCacheObject(key, object);
|
||||||
} else {
|
} else {
|
||||||
RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));
|
if (RedisUtils.hasKey(key)) {
|
||||||
|
RedisUtils.setCacheObject(key, object, true);
|
||||||
|
} else {
|
||||||
|
RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
CAFFEINE.invalidate(key);
|
CAFFEINE.invalidate(key);
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,14 @@ import cn.hutool.core.convert.Convert;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.dromara.common.core.constant.SystemConstants;
|
||||||
import org.dromara.common.core.constant.TenantConstants;
|
import org.dromara.common.core.constant.TenantConstants;
|
||||||
import org.dromara.common.core.constant.UserConstants;
|
|
||||||
import org.dromara.common.core.domain.model.LoginUser;
|
import org.dromara.common.core.domain.model.LoginUser;
|
||||||
import org.dromara.common.core.enums.UserType;
|
import org.dromara.common.core.enums.UserType;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录鉴权助手
|
* 登录鉴权助手
|
||||||
* <p>
|
* <p>
|
||||||
@ -88,6 +89,13 @@ public class LoginHelper {
|
|||||||
return Convert.toLong(getExtra(USER_KEY));
|
return Convert.toLong(getExtra(USER_KEY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户id
|
||||||
|
*/
|
||||||
|
public static String getUserIdStr() {
|
||||||
|
return Convert.toStr(getExtra(USER_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户账户
|
* 获取用户账户
|
||||||
*/
|
*/
|
||||||
@ -152,7 +160,7 @@ public class LoginHelper {
|
|||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
public static boolean isSuperAdmin(Long userId) {
|
public static boolean isSuperAdmin(Long userId) {
|
||||||
return UserConstants.SUPER_ADMIN_ID.equals(userId);
|
return SystemConstants.SUPER_ADMIN_ID.equals(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,7 +22,13 @@ import java.lang.annotation.Target;
|
|||||||
public @interface Sensitive {
|
public @interface Sensitive {
|
||||||
SensitiveStrategy strategy();
|
SensitiveStrategy strategy();
|
||||||
|
|
||||||
String roleKey() default "";
|
/**
|
||||||
|
* 角色标识符 多个角色满足一个即可
|
||||||
|
*/
|
||||||
|
String[] roleKey() default {};
|
||||||
|
|
||||||
String perms() default "";
|
/**
|
||||||
|
* 权限标识符 多个权限满足一个即可
|
||||||
|
*/
|
||||||
|
String[] perms() default {};
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,6 @@ public interface SensitiveService {
|
|||||||
/**
|
/**
|
||||||
* 是否脱敏
|
* 是否脱敏
|
||||||
*/
|
*/
|
||||||
boolean isSensitive(String roleKey, String perms);
|
boolean isSensitive(String[] roleKey, String[] perms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -80,12 +80,12 @@ public enum SensitiveStrategy {
|
|||||||
FIRST_MASK(DesensitizedUtil::firstMask),
|
FIRST_MASK(DesensitizedUtil::firstMask),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清空为null
|
* 清空为""
|
||||||
*/
|
*/
|
||||||
CLEAR(s -> DesensitizedUtil.clear()),
|
CLEAR(s -> DesensitizedUtil.clear()),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清空为""
|
* 清空为null
|
||||||
*/
|
*/
|
||||||
CLEAR_TO_NULL(s -> DesensitizedUtil.clearToNull());
|
CLEAR_TO_NULL(s -> DesensitizedUtil.clearToNull());
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@ import java.util.Objects;
|
|||||||
public class SensitiveHandler extends JsonSerializer<String> implements ContextualSerializer {
|
public class SensitiveHandler extends JsonSerializer<String> implements ContextualSerializer {
|
||||||
|
|
||||||
private SensitiveStrategy strategy;
|
private SensitiveStrategy strategy;
|
||||||
private String roleKey;
|
private String[] roleKey;
|
||||||
private String perms;
|
private String[] perms;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||||
|
@ -66,7 +66,7 @@ public class PlusSmsDao implements SmsDao {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void clean() {
|
public void clean() {
|
||||||
RedisUtils.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + "sms:");
|
RedisUtils.deleteKeys(GlobalConstants.GLOBAL_REDIS_KEY + "sms:*");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ public class AuthMaxKeyRequest extends AuthDefaultRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||||
String body = doPostAuthorizationCode(authCallback.getCode());
|
String body = doPostAuthorizationCode(authCallback.getCode());
|
||||||
Dict object = JsonUtils.parseMap(body);
|
Dict object = JsonUtils.parseMap(body);
|
||||||
// oauth/token 验证异常
|
// oauth/token 验证异常
|
||||||
@ -51,7 +51,7 @@ public class AuthMaxKeyRequest extends AuthDefaultRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
public AuthUser getUserInfo(AuthToken authToken) {
|
||||||
String body = doGetUserInfo(authToken);
|
String body = doGetUserInfo(authToken);
|
||||||
Dict object = JsonUtils.parseMap(body);
|
Dict object = JsonUtils.parseMap(body);
|
||||||
// oauth/token 验证异常
|
// oauth/token 验证异常
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package org.dromara.common.social.topiam;
|
package org.dromara.common.social.topiam;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
import cn.hutool.core.lang.Dict;
|
import cn.hutool.core.lang.Dict;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.http.HttpRequest;
|
||||||
|
import cn.hutool.http.HttpResponse;
|
||||||
import com.xkcoding.http.support.HttpHeader;
|
import com.xkcoding.http.support.HttpHeader;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import me.zhyd.oauth.cache.AuthStateCache;
|
import me.zhyd.oauth.cache.AuthStateCache;
|
||||||
@ -16,7 +19,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
|||||||
import org.dromara.common.core.utils.SpringUtils;
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
import org.dromara.common.json.utils.JsonUtils;
|
import org.dromara.common.json.utils.JsonUtils;
|
||||||
|
|
||||||
import static org.dromara.common.social.topiam.AuthTopiamSource.TOPIAM;
|
import static org.dromara.common.social.topiam.AuthTopIamSource.TOPIAM;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TopIAM 认证请求
|
* TopIAM 认证请求
|
||||||
@ -41,7 +44,7 @@ public class AuthTopIamRequest extends AuthDefaultRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
public AuthToken getAccessToken(AuthCallback authCallback) {
|
||||||
String body = doPostAuthorizationCode(authCallback.getCode());
|
String body = doPostAuthorizationCode(authCallback.getCode());
|
||||||
Dict object = JsonUtils.parseMap(body);
|
Dict object = JsonUtils.parseMap(body);
|
||||||
checkResponse(object);
|
checkResponse(object);
|
||||||
@ -55,7 +58,7 @@ public class AuthTopIamRequest extends AuthDefaultRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
public AuthUser getUserInfo(AuthToken authToken) {
|
||||||
String body = doGetUserInfo(authToken);
|
String body = doGetUserInfo(authToken);
|
||||||
Dict object = JsonUtils.parseMap(body);
|
Dict object = JsonUtils.parseMap(body);
|
||||||
checkResponse(object);
|
checkResponse(object);
|
||||||
@ -70,6 +73,16 @@ public class AuthTopIamRequest extends AuthDefaultRequest {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String doPostAuthorizationCode(String code) {
|
||||||
|
HttpRequest request = HttpRequest.post(source.accessToken())
|
||||||
|
.header("Authorization", "Basic " + Base64.encode("%s:%s".formatted(config.getClientId(), config.getClientSecret())))
|
||||||
|
.form("grant_type", "authorization_code")
|
||||||
|
.form("code", code)
|
||||||
|
.form("redirect_uri", config.getRedirectUri());
|
||||||
|
HttpResponse response = request.execute();
|
||||||
|
return response.body();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String doGetUserInfo(AuthToken authToken) {
|
protected String doGetUserInfo(AuthToken authToken) {
|
||||||
@ -86,7 +99,7 @@ public class AuthTopIamRequest extends AuthDefaultRequest {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void checkResponse(Dict object) {
|
private static void checkResponse(Dict object) {
|
||||||
// oauth/token 验证异常
|
// oauth/token 验证异常
|
||||||
if (object.containsKey("error")) {
|
if (object.containsKey("error")) {
|
||||||
throw new AuthException(object.getStr("error_description"));
|
throw new AuthException(object.getStr("error_description"));
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user