Compare commits
127 Commits
master
...
dev.caojia
Author | SHA1 | Date |
---|---|---|
|
761138a6b3 | |
|
40c6e3b3cb | |
|
ba6fa6dc98 | |
|
fb1870328e | |
|
0d337286b1 | |
|
ba2eec3a0c | |
|
059148d97b | |
|
f756d27590 | |
|
4b2bc5ccc5 | |
|
255c486740 | |
|
c0e8f3a222 | |
|
69cdc27527 | |
|
0a61a6e8cf | |
|
d80480336b | |
|
dc9a8f52b6 | |
|
a286f91ef5 | |
|
ed2c9bfbf9 | |
|
e3e4e9baaf | |
|
cd3191ab60 | |
|
f72c1c6495 | |
|
4c1ced76dc | |
|
0ed28f01ec | |
|
3bc53a7d4b | |
|
74df0be997 | |
|
9a41a5ee41 | |
|
6cfb02aafb | |
|
64287e85eb | |
|
9785256c80 | |
|
38320d8acb | |
|
34b479fe1a | |
|
96d690580a | |
|
6267fd5b5a | |
|
03c44645c5 | |
|
8361200034 | |
|
2fd7323d79 | |
|
1ad8dec41e | |
|
9bff9d672e | |
|
abfc92405c | |
|
dc3ad5a99c | |
|
61488b5969 | |
|
770220bd10 | |
|
5a1c3a7c14 | |
|
516a6aa641 | |
|
64063390bd | |
|
1f72880806 | |
|
546e4be756 | |
|
c72b8f2aba | |
|
a786d1b81a | |
|
aefc1dd9a0 | |
|
75af3a8e8f | |
|
288fc89bd6 | |
|
9a67b94219 | |
|
a83bdf69ab | |
|
f3316fc400 | |
|
27e22efbb6 | |
|
837c6cb01c | |
|
84453dac06 | |
|
e9285a11e0 | |
|
9cad37f9be | |
|
430af9d852 | |
|
d8d28a6302 | |
|
7c9343d6a6 | |
|
c4a4fadfb4 | |
|
177b5e8a69 | |
|
c058749afd | |
|
965ef50b86 | |
|
88f21a5d9d | |
|
efd38c1c4d | |
|
125a8be83e | |
|
230db55a99 | |
|
0d1f98811b | |
|
24c77bf396 | |
|
3d1c9bc618 | |
|
c71757101b | |
|
546656c01f | |
|
213766b48c | |
|
c2d42da958 | |
|
6de3aa9475 | |
|
07b2122871 | |
|
e359147876 | |
|
4012572b52 | |
|
de11772200 | |
|
4ba8064b64 | |
|
7ccc5d4dfb | |
|
39d2e1e27c | |
|
2cc30e4315 | |
|
f007ff0ff2 | |
|
312d307772 | |
|
506a7a2dc8 | |
|
245350d233 | |
|
cbe266392c | |
|
50b5651e74 | |
|
00626819c6 | |
|
0cff3ff8bd | |
|
4eefdb279d | |
|
3d65a43faf | |
|
f7ae4752e2 | |
|
688b7ef0df | |
|
c3a46b9577 | |
|
3272abb854 | |
|
a16b942937 | |
|
239b93e421 | |
|
94ed7562ea | |
|
54a1c30e3d | |
|
e095e4e5a2 | |
|
505f440af8 | |
|
7f71d6b2cf | |
|
26bce15c72 | |
|
7c8281a35e | |
|
36a7b6e8fb | |
|
9c088c91d1 | |
|
4cd97ae5e3 | |
|
5e0629e74a | |
|
af496fcb33 | |
|
769de3cf6a | |
|
18cfb79131 | |
|
ea0e4a0261 | |
|
6892aecb56 | |
|
ae7c9bb06d | |
|
68c65cde33 | |
|
f19b1fb480 | |
|
a337791762 | |
|
8d9d1b9f67 | |
|
1d4299cd23 | |
|
77fec3d7a2 | |
|
3ff9fef182 | |
|
afed932070 |
|
@ -9,8 +9,8 @@
|
|||
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
|
||||
[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
|
||||
<br>
|
||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
||||
[]()
|
||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
|
||||
|
@ -56,7 +56,7 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
|
|||
| 数据加解密 | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密<br/>支持多种策略 如BASE64、AES、RSA、SM2、SM4等 | 无 |
|
||||
| 接口传输加密 | 采用 动态 AES + RSA 加密请求 body 每一次请求秘钥都不同大幅度降低可破解性 | 无 |
|
||||
| 数据翻译 | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译<br/>支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现 | 无 |
|
||||
| 多数据源框架 | 采用 dynamic-datasource 支持世面大部分数据库<br/>通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源<br/>支持spel表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 |
|
||||
| 多数据源框架 | 采用 dynamic-datasource 支持市面大部分数据库<br/>通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源<br/>支持spel表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 |
|
||||
| 多数据源事务 | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚 | 不支持 |
|
||||
| 数据库连接池 | 采用 HikariCP Spring官方内置连接池 配置简单 以性能与稳定性闻名天下 | 采用 druid bug众多 社区维护差 活跃度低 配置众多繁琐性能一般 |
|
||||
| 数据库主键 | 采用 雪花ID 基于时间戳的 有序增长 唯一ID 再也不用为分库分表 数据合并主键冲突重复而发愁 | 采用 数据库自增ID 支持数据量有限 不支持多数据源主键唯一 |
|
||||
|
|
50
pom.xml
50
pom.xml
|
@ -9,12 +9,11 @@
|
|||
<version>${revision}</version>
|
||||
|
||||
<name>RuoYi-Vue-Plus</name>
|
||||
<url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
|
||||
<description>RuoYi-Vue-Plus多租户管理系统</description>
|
||||
<description>校本资源管理系统</description>
|
||||
|
||||
<properties>
|
||||
<revision>5.2.0-BETA</revision>
|
||||
<spring-boot.version>3.2.5</spring-boot.version>
|
||||
<revision>5.2.1</revision>
|
||||
<spring-boot.version>3.2.6</spring-boot.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>17</java.version>
|
||||
|
@ -25,16 +24,16 @@
|
|||
<easyexcel.version>3.3.4</easyexcel.version>
|
||||
<velocity.version>2.3</velocity.version>
|
||||
<satoken.version>1.38.0</satoken.version>
|
||||
<mybatis-plus.version>3.5.6</mybatis-plus.version>
|
||||
<mybatis-plus.version>3.5.7</mybatis-plus.version>
|
||||
<p6spy.version>3.9.1</p6spy.version>
|
||||
<hutool.version>5.8.27</hutool.version>
|
||||
<okhttp.version>4.10.0</okhttp.version>
|
||||
<spring-boot-admin.version>3.2.3</spring-boot-admin.version>
|
||||
<redisson.version>3.29.0</redisson.version>
|
||||
<redisson.version>3.31.0</redisson.version>
|
||||
<lock4j.version>2.2.7</lock4j.version>
|
||||
<dynamic-ds.version>4.3.0</dynamic-ds.version>
|
||||
<dynamic-ds.version>4.3.1</dynamic-ds.version>
|
||||
<alibaba-ttl.version>2.14.4</alibaba-ttl.version>
|
||||
<snailjob.version>1.0.0-beta1</snailjob.version>
|
||||
<snailjob.version>1.0.1</snailjob.version>
|
||||
<mapstruct-plus.version>1.3.6</mapstruct-plus.version>
|
||||
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
|
||||
<lombok.version>1.18.32</lombok.version>
|
||||
|
@ -50,6 +49,8 @@
|
|||
<sms4j.version>3.2.1</sms4j.version>
|
||||
<!-- 限制框架中的fastjson版本 -->
|
||||
<fastjson.version>1.2.83</fastjson.version>
|
||||
<!--工作流配置-->
|
||||
<flowable.version>7.0.0</flowable.version>
|
||||
|
||||
<!-- 插件版本 -->
|
||||
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
|
||||
|
@ -57,18 +58,9 @@
|
|||
<maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
|
||||
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
|
||||
<flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
|
||||
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>local</id>
|
||||
<properties>
|
||||
<!-- 环境标识,需要与配置文件的名称相对应 -->
|
||||
<profiles.active>local</profiles.active>
|
||||
<logging.level>info</logging.level>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>dev</id>
|
||||
<properties>
|
||||
|
@ -81,6 +73,13 @@
|
|||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>test</id>
|
||||
<properties>
|
||||
<profiles.active>test</profiles.active>
|
||||
<logging.level>info</logging.level>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>prod</id>
|
||||
<properties>
|
||||
|
@ -112,6 +111,14 @@
|
|||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-bom</artifactId>
|
||||
<version>${flowable.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JustAuth 的依赖配置-->
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
|
@ -347,6 +354,14 @@
|
|||
<artifactId>ruoyi-generator</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 工作流模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-workflow</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
@ -355,7 +370,6 @@
|
|||
<module>ruoyi-common</module>
|
||||
<module>ruoyi-extend</module>
|
||||
<module>ruoyi-modules</module>
|
||||
<module>ruoyi-modules/ruoyi-file</module>
|
||||
</modules>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
|
||||
FROM bellsoft/liberica-openjdk-debian:17.0.11-cds
|
||||
#FROM bellsoft/liberica-openjdk-debian:21.0.3-cds
|
||||
#FROM findepi/graalvm:java17-native
|
||||
FROM openjdk:17.0.2-oraclelinux8
|
||||
|
||||
MAINTAINER Lion Li
|
||||
LABEL maintainer="Lion Li"
|
||||
|
||||
RUN mkdir -p /ruoyi/server/logs \
|
||||
/ruoyi/server/temp \
|
||||
|
|
|
@ -28,21 +28,26 @@
|
|||
<artifactId>ojdbc8</artifactId>
|
||||
</dependency>
|
||||
<!-- PostgreSql -->
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.postgresql</groupId>-->
|
||||
<!-- <artifactId>postgresql</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- SqlServer -->
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.microsoft.sqlserver</groupId>-->
|
||||
<!-- <artifactId>mssql-jdbc</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-doc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-social</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-ratelimiter</artifactId>
|
||||
|
@ -80,21 +85,6 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SnailJob client -->
|
||||
<dependency>
|
||||
<groupId>com.aizuda</groupId>
|
||||
<artifactId>snail-job-client-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aizuda</groupId>
|
||||
<artifactId>snail-job-client-job-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- skywalking 整合 logback -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.apache.skywalking</groupId>-->
|
||||
|
|
|
@ -1,22 +1,34 @@
|
|||
package org.dromara.web.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.request.AuthRequest;
|
||||
import me.zhyd.oauth.utils.AuthStateUtils;
|
||||
import org.dromara.common.core.constant.UserConstants;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.domain.model.LoginBody;
|
||||
import org.dromara.common.core.domain.model.PasswordLoginBody;
|
||||
import org.dromara.common.core.domain.model.RegisterBody;
|
||||
import org.dromara.common.core.domain.model.SocialLoginBody;
|
||||
import org.dromara.common.core.utils.*;
|
||||
import org.dromara.common.encrypt.annotation.ApiEncrypt;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
|
||||
import org.dromara.common.social.config.properties.SocialProperties;
|
||||
import org.dromara.common.social.utils.SocialUtils;
|
||||
import org.dromara.common.tenant.helper.TenantHelper;
|
||||
import org.dromara.common.websocket.dto.WebSocketMessageDto;
|
||||
import org.dromara.common.websocket.utils.WebSocketUtils;
|
||||
import org.dromara.system.domain.SysClient;
|
||||
import org.dromara.system.domain.bo.SysTenantBo;
|
||||
import org.dromara.system.domain.vo.SysClientVo;
|
||||
import org.dromara.system.domain.vo.SysTenantVo;
|
||||
|
@ -34,7 +46,10 @@ import org.springframework.validation.annotation.Validated;
|
|||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -50,6 +65,7 @@ import java.util.concurrent.TimeUnit;
|
|||
@RequestMapping("/auth")
|
||||
public class AuthController {
|
||||
|
||||
private final SocialProperties socialProperties;
|
||||
private final SysLoginService loginService;
|
||||
private final SysRegisterService registerService;
|
||||
private final ISysConfigService configService;
|
||||
|
@ -89,13 +105,75 @@ public class AuthController {
|
|||
Long userId = LoginHelper.getUserId();
|
||||
scheduledExecutorService.schedule(() -> {
|
||||
WebSocketMessageDto dto = new WebSocketMessageDto();
|
||||
dto.setMessage("欢迎登录RuoYi-Vue-Plus后台管理系统");
|
||||
dto.setMessage("欢迎登录校本资源平台");
|
||||
dto.setSessionKeys(List.of(userId));
|
||||
WebSocketUtils.publishMessage(dto);
|
||||
}, 3, TimeUnit.SECONDS);
|
||||
return R.ok(loginVo);
|
||||
}
|
||||
|
||||
@SaIgnore
|
||||
@PostMapping("/passwordLogin")
|
||||
public R<LoginVo> loginClient(@RequestBody PasswordLoginBody loginBody) {
|
||||
// 授权类型和客户端id
|
||||
String clientId = loginBody.getClientId();
|
||||
String grantType = loginBody.getGrantType();
|
||||
SysClientVo client = clientService.queryByClientId(clientId);
|
||||
// 查询不到 client 或 client 内不包含 grantType
|
||||
if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
|
||||
log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
|
||||
return R.fail(MessageUtils.message("auth.grant.type.error"));
|
||||
} else if (!UserConstants.NORMAL.equals(client.getStatus())) {
|
||||
return R.fail(MessageUtils.message("auth.grant.type.blocked"));
|
||||
}
|
||||
// 登录
|
||||
LoginVo loginVo = IAuthStrategy.login(JsonUtils.toJsonString(loginBody), client, grantType);
|
||||
return R.ok(loginVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方登录请求
|
||||
*
|
||||
* @param source 登录来源
|
||||
* @return 结果
|
||||
*/
|
||||
@GetMapping("/binding/{source}")
|
||||
public R<String> authBinding(@PathVariable("source") String source,
|
||||
@RequestParam String tenantId, @RequestParam String domain) {
|
||||
SocialLoginConfigProperties obj = socialProperties.getType().get(source);
|
||||
if (ObjectUtil.isNull(obj)) {
|
||||
return R.fail(source + "平台账号暂不支持");
|
||||
}
|
||||
AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties);
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("tenantId", tenantId);
|
||||
map.put("domain", domain);
|
||||
map.put("state", AuthStateUtils.createState());
|
||||
String authorizeUrl = authRequest.authorize(Base64.encode(JsonUtils.toJsonString(map), StandardCharsets.UTF_8));
|
||||
return R.ok("操作成功", authorizeUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方登录回调业务处理 绑定授权
|
||||
*
|
||||
* @param loginBody 请求体
|
||||
* @return 结果
|
||||
*/
|
||||
@PostMapping("/social/callback")
|
||||
public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
|
||||
// 获取第三方登录信息
|
||||
AuthResponse<AuthUser> response = SocialUtils.loginAuth(
|
||||
loginBody.getSource(), loginBody.getSocialCode(),
|
||||
loginBody.getSocialState(), socialProperties);
|
||||
AuthUser authUserData = response.getData();
|
||||
// 判断授权响应是否成功
|
||||
if (!response.ok()) {
|
||||
return R.fail(response.getMsg());
|
||||
}
|
||||
loginService.socialRegister(authUserData);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 取消授权
|
||||
|
@ -138,8 +216,26 @@ public class AuthController {
|
|||
*/
|
||||
@GetMapping("/tenant/list")
|
||||
public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
|
||||
// 返回对象
|
||||
LoginTenantVo result = new LoginTenantVo();
|
||||
boolean enable = TenantHelper.isEnable();
|
||||
result.setTenantEnabled(enable);
|
||||
// 如果未开启租户这直接返回
|
||||
if (!enable) {
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo());
|
||||
List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class);
|
||||
try {
|
||||
// 如果只超管返回所有租户
|
||||
if (LoginHelper.isSuperAdmin()) {
|
||||
result.setVoList(voList);
|
||||
return R.ok(result);
|
||||
}
|
||||
} catch (NotLoginException ignored) {
|
||||
}
|
||||
|
||||
// 获取域名
|
||||
String host;
|
||||
String referer = request.getHeader("referer");
|
||||
|
@ -152,11 +248,8 @@ public class AuthController {
|
|||
// 根据域名进行筛选
|
||||
List<TenantListVo> list = StreamUtils.filter(voList, vo ->
|
||||
StringUtils.equals(vo.getDomain(), host));
|
||||
// 返回对象
|
||||
LoginTenantVo vo = new LoginTenantVo();
|
||||
vo.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
|
||||
vo.setTenantEnabled(TenantHelper.isEnable());
|
||||
return R.ok(vo);
|
||||
result.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.dromara.web.service;
|
|||
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.system.domain.SysClient;
|
||||
import org.dromara.system.domain.vo.SysClientVo;
|
||||
import org.dromara.web.domain.vo.LoginVo;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.dromara.common.json.utils.JsonUtils;
|
|||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.tenant.helper.TenantHelper;
|
||||
import org.dromara.system.domain.SysClient;
|
||||
import org.dromara.system.domain.SysUser;
|
||||
import org.dromara.system.domain.vo.SysClientVo;
|
||||
import org.dromara.system.domain.vo.SysUserVo;
|
||||
|
|
|
@ -4,6 +4,7 @@ import cn.dev33.satoken.secure.BCrypt;
|
|||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -59,7 +60,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
|
|||
|
||||
boolean captchaEnabled = captchaProperties.getEnable();
|
||||
// 验证码开关
|
||||
if (captchaEnabled) {
|
||||
if (captchaEnabled && StrUtil.isNotBlank(code)) {
|
||||
validateCaptcha(tenantId, username, code, uuid);
|
||||
}
|
||||
|
||||
|
@ -94,7 +95,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
|
|||
* @param uuid 唯一标识
|
||||
*/
|
||||
private void validateCaptcha(String tenantId, String username, String code, String uuid) {
|
||||
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
|
||||
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
|
||||
String captcha = RedisUtils.getCacheObject(verifyKey);
|
||||
RedisUtils.deleteObject(verifyKey);
|
||||
if (captcha == null) {
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.dromara.common.json.utils.JsonUtils;
|
|||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.tenant.helper.TenantHelper;
|
||||
import org.dromara.system.domain.SysClient;
|
||||
import org.dromara.system.domain.SysUser;
|
||||
import org.dromara.system.domain.vo.SysClientVo;
|
||||
import org.dromara.system.domain.vo.SysUserVo;
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
package org.dromara.web.service.impl;
|
||||
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.Method;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.core.domain.model.SocialLoginBody;
|
||||
import org.dromara.common.core.enums.UserStatus;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.exception.user.UserException;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.core.utils.ValidatorUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.social.config.properties.SocialProperties;
|
||||
import org.dromara.common.social.utils.SocialUtils;
|
||||
import org.dromara.common.tenant.helper.TenantHelper;
|
||||
import org.dromara.system.domain.vo.SysClientVo;
|
||||
import org.dromara.system.domain.vo.SysSocialVo;
|
||||
import org.dromara.system.domain.vo.SysUserVo;
|
||||
import org.dromara.system.mapper.SysUserMapper;
|
||||
import org.dromara.system.service.ISysSocialService;
|
||||
import org.dromara.web.domain.vo.LoginVo;
|
||||
import org.dromara.web.service.IAuthStrategy;
|
||||
import org.dromara.web.service.SysLoginService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 第三方授权策略
|
||||
*
|
||||
* @author thiszhc is 三三
|
||||
*/
|
||||
@Slf4j
|
||||
@Service("social" + IAuthStrategy.BASE_NAME)
|
||||
@RequiredArgsConstructor
|
||||
public class SocialAuthStrategy implements IAuthStrategy {
|
||||
|
||||
private final SocialProperties socialProperties;
|
||||
private final ISysSocialService sysSocialService;
|
||||
private final SysUserMapper userMapper;
|
||||
private final SysLoginService loginService;
|
||||
|
||||
/**
|
||||
* 登录-第三方授权登录
|
||||
*
|
||||
* @param body 登录信息
|
||||
* @param client 客户端信息
|
||||
*/
|
||||
@Override
|
||||
public LoginVo login(String body, SysClientVo client) {
|
||||
SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class);
|
||||
ValidatorUtils.validate(loginBody);
|
||||
AuthResponse<AuthUser> response = SocialUtils.loginAuth(
|
||||
loginBody.getSource(), loginBody.getSocialCode(),
|
||||
loginBody.getSocialState(), socialProperties);
|
||||
if (!response.ok()) {
|
||||
throw new ServiceException(response.getMsg());
|
||||
}
|
||||
AuthUser authUserData = response.getData();
|
||||
if ("GITEE".equals(authUserData.getSource())) {
|
||||
// 如用户使用 gitee 登录顺手 star 给作者一点支持 拒绝白嫖
|
||||
HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Vue-Plus")
|
||||
.formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
|
||||
.executeAsync();
|
||||
HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Cloud-Plus")
|
||||
.formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
|
||||
.executeAsync();
|
||||
}
|
||||
|
||||
List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
|
||||
}
|
||||
SysSocialVo social;
|
||||
if (TenantHelper.isEnable()) {
|
||||
Optional<SysSocialVo> opt = StreamUtils.findAny(list, x -> x.getTenantId().equals(loginBody.getTenantId()));
|
||||
if (opt.isEmpty()) {
|
||||
throw new ServiceException("对不起,你没有权限登录当前租户!");
|
||||
}
|
||||
social = opt.get();
|
||||
} else {
|
||||
social = list.get(0);
|
||||
}
|
||||
// 查找用户
|
||||
SysUserVo user = loadUser(social.getTenantId(), social.getUserId());
|
||||
|
||||
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
|
||||
LoginUser loginUser = loginService.buildLoginUser(user);
|
||||
loginUser.setClientKey(client.getClientKey());
|
||||
loginUser.setDeviceType(client.getDeviceType());
|
||||
SaLoginModel model = new SaLoginModel();
|
||||
model.setDevice(client.getDeviceType());
|
||||
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||
// 例如: 后台用户30分钟过期 app用户1天过期
|
||||
model.setTimeout(client.getTimeout());
|
||||
model.setActiveTimeout(client.getActiveTimeout());
|
||||
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
|
||||
// 生成token
|
||||
LoginHelper.login(loginUser, model);
|
||||
|
||||
LoginVo loginVo = new LoginVo();
|
||||
loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||
loginVo.setClientId(client.getClientId());
|
||||
return loginVo;
|
||||
}
|
||||
|
||||
private SysUserVo loadUser(String tenantId, Long userId) {
|
||||
return TenantHelper.dynamic(tenantId, () -> {
|
||||
SysUserVo user = userMapper.selectVoById(userId);
|
||||
if (ObjectUtil.isNull(user)) {
|
||||
log.info("登录用户:{} 不存在.", "");
|
||||
throw new UserException("user.not.exists", "");
|
||||
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
||||
log.info("登录用户:{} 已被停用.", "");
|
||||
throw new UserException("user.blocked", "");
|
||||
}
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -11,6 +11,7 @@ import org.dromara.common.core.enums.UserStatus;
|
|||
import org.dromara.common.core.utils.ValidatorUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.system.domain.SysClient;
|
||||
import org.dromara.system.domain.vo.SysClientVo;
|
||||
import org.dromara.system.domain.vo.SysUserVo;
|
||||
import org.dromara.web.domain.vo.LoginVo;
|
||||
|
|
|
@ -6,21 +6,22 @@ spring.boot.admin.client:
|
|||
instance:
|
||||
service-host-type: IP
|
||||
username: ruoyi
|
||||
password: 123456
|
||||
password: Mz123456!
|
||||
|
||||
--- # snail-job 配置
|
||||
snail-job:
|
||||
enabled: true
|
||||
enabled: false
|
||||
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
|
||||
group-name: "ruoyi_group"
|
||||
group: "ruoyi_group"
|
||||
# SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表
|
||||
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
|
||||
server:
|
||||
host: 127.0.0.1
|
||||
port: 1788
|
||||
port: 17888
|
||||
# 详见 script/sql/snail_job.sql `sj_namespace` 表
|
||||
namespace: ${spring.profiles.active}
|
||||
|
||||
# 随主应用端口飘逸
|
||||
port: 2${server.port}
|
||||
|
||||
--- # 数据源配置
|
||||
spring:
|
||||
|
@ -95,13 +96,14 @@ spring.data:
|
|||
port: 6379
|
||||
# 数据库索引
|
||||
database: 1
|
||||
# 密码(如没有密码请注释掉)
|
||||
# redis 密码必须配置
|
||||
password: Mz123456*
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
# 是否开启ssl
|
||||
ssl.enabled: false
|
||||
|
||||
# redisson 配置
|
||||
redisson:
|
||||
# redis key前缀
|
||||
keyPrefix:
|
||||
|
@ -177,3 +179,80 @@ sms:
|
|||
access-key-secret: 您的accessKeySecret
|
||||
signature: 您的短信签名
|
||||
sdk-app-id: 您的sdkAppId
|
||||
|
||||
|
||||
--- # 三方授权
|
||||
justauth:
|
||||
# 前端外网访问地址
|
||||
address: http://localhost:80
|
||||
type:
|
||||
maxkey:
|
||||
# maxkey 服务器地址
|
||||
# 注意 如下均配置均不需要修改 maxkey 已经内置好了数据
|
||||
server-url: http://sso.maxkey.top
|
||||
client-id: 876892492581044224
|
||||
client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
|
||||
redirect-uri: ${justauth.address}/social-callback?source=maxkey
|
||||
topiam:
|
||||
# topiam 服务器地址
|
||||
server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
|
||||
client-id: 449c4*********937************759
|
||||
client-secret: ac7***********1e0************28d
|
||||
redirect-uri: ${justauth.address}/social-callback?source=topiam
|
||||
scopes: [openid, email, phone, profile]
|
||||
qq:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=qq
|
||||
union-id: false
|
||||
weibo:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=weibo
|
||||
gitee:
|
||||
client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
|
||||
client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
|
||||
redirect-uri: ${justauth.address}/social-callback?source=gitee
|
||||
dingtalk:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=dingtalk
|
||||
baidu:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=baidu
|
||||
csdn:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=csdn
|
||||
coding:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=coding
|
||||
coding-group-name: xx
|
||||
oschina:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=oschina
|
||||
alipay_wallet:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
|
||||
alipay-public-key: MIIB**************DAQAB
|
||||
wechat_open:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=wechat_open
|
||||
wechat_mp:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
|
||||
wechat_enterprise:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
|
||||
agent-id: 1000002
|
||||
gitlab:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=gitlab
|
||||
|
|
|
@ -9,20 +9,22 @@ spring.boot.admin.client:
|
|||
instance:
|
||||
service-host-type: IP
|
||||
username: ruoyi
|
||||
password: 123456
|
||||
password: Mz123456!
|
||||
|
||||
--- # snail-job 配置
|
||||
snail-job:
|
||||
enabled: false
|
||||
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
|
||||
group-name: "ruoyi_group"
|
||||
group: "ruoyi_group"
|
||||
# SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表
|
||||
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
|
||||
server:
|
||||
host: 127.0.0.1
|
||||
port: 1788
|
||||
port: 17888
|
||||
# 详见 script/sql/snail_job.sql `sj_namespace` 表
|
||||
namespace: ${spring.profiles.active}
|
||||
# 随主应用端口飘逸
|
||||
port: 2${server.port}
|
||||
|
||||
--- # 数据源配置
|
||||
spring:
|
||||
|
@ -97,13 +99,14 @@ spring.data:
|
|||
port: 6379
|
||||
# 数据库索引
|
||||
database: 0
|
||||
# 密码(如没有密码请注释掉)
|
||||
# password:
|
||||
# redis 密码必须配置
|
||||
password: ruoyi123
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
# 是否开启ssl
|
||||
ssl.enabled: false
|
||||
|
||||
# redisson 配置
|
||||
redisson:
|
||||
# redis key前缀
|
||||
keyPrefix:
|
||||
|
|
|
@ -0,0 +1,256 @@
|
|||
--- # 监控中心配置
|
||||
spring.boot.admin.client:
|
||||
# 增加客户端开关
|
||||
enabled: false
|
||||
url: http://localhost:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
username: ruoyi
|
||||
password: Mz123456!
|
||||
|
||||
--- # snail-job 配置
|
||||
snail-job:
|
||||
enabled: false
|
||||
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
|
||||
group-name: "ruoyi_group"
|
||||
# SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表
|
||||
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
|
||||
server:
|
||||
host: 127.0.0.1
|
||||
port: 1788
|
||||
# 详见 script/sql/snail_job.sql `sj_namespace` 表
|
||||
namespace: ${spring.profiles.active}
|
||||
|
||||
|
||||
--- # 数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
|
||||
dynamic:
|
||||
# 性能分析插件(有性能损耗 不建议生产环境使用)
|
||||
p6spy: true
|
||||
# 设置默认的数据源或者数据源组,默认值即为 master
|
||||
primary: master
|
||||
# 严格模式 匹配不到数据源则报错
|
||||
strict: true
|
||||
datasource:
|
||||
# 主库数据源
|
||||
master:
|
||||
type: ${spring.datasource.type}
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
|
||||
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
||||
url: jdbc:mysql://127.0.0.1:3306/school_file?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||
username: root
|
||||
password: Mz123456*
|
||||
# 从库数据源
|
||||
slave:
|
||||
lazy: true
|
||||
type: ${spring.datasource.type}
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://127.0.0.1:3306/school_file?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||
username:
|
||||
password:
|
||||
# oracle:
|
||||
# type: ${spring.datasource.type}
|
||||
# driverClassName: oracle.jdbc.OracleDriver
|
||||
# url: jdbc:oracle:thin:@//localhost:1521/XE
|
||||
# username: ROOT
|
||||
# password: root
|
||||
# postgres:
|
||||
# type: ${spring.datasource.type}
|
||||
# driverClassName: org.postgresql.Driver
|
||||
# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
|
||||
# username: root
|
||||
# password: root
|
||||
# sqlserver:
|
||||
# type: ${spring.datasource.type}
|
||||
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
|
||||
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
|
||||
# username: SA
|
||||
# password: root
|
||||
hikari:
|
||||
# 最大连接池数量
|
||||
maxPoolSize: 20
|
||||
# 最小空闲线程数量
|
||||
minIdle: 10
|
||||
# 配置获取连接等待超时的时间
|
||||
connectionTimeout: 30000
|
||||
# 校验超时时间
|
||||
validationTimeout: 5000
|
||||
# 空闲连接存活最大时间,默认10分钟
|
||||
idleTimeout: 600000
|
||||
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
|
||||
maxLifetime: 1800000
|
||||
# 多久检查一次连接的活性
|
||||
keepaliveTime: 30000
|
||||
|
||||
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
||||
spring.data:
|
||||
redis:
|
||||
# 地址
|
||||
host: 127.0.0.1
|
||||
# 端口,默认为6379
|
||||
port: 6379
|
||||
# 数据库索引
|
||||
database: 1
|
||||
# 密码(如没有密码请注释掉)
|
||||
password: Mz123456*
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
# 是否开启ssl
|
||||
ssl.enabled: false
|
||||
|
||||
redisson:
|
||||
# redis key前缀
|
||||
keyPrefix:
|
||||
# 线程池数量
|
||||
threads: 4
|
||||
# Netty线程池数量
|
||||
nettyThreads: 8
|
||||
# 单节点配置
|
||||
singleServerConfig:
|
||||
# 客户端名称
|
||||
clientName: ${ruoyi.name}
|
||||
# 最小空闲连接数
|
||||
connectionMinimumIdleSize: 8
|
||||
# 连接池大小
|
||||
connectionPoolSize: 32
|
||||
# 连接空闲超时,单位:毫秒
|
||||
idleConnectionTimeout: 10000
|
||||
# 命令等待超时,单位:毫秒
|
||||
timeout: 3000
|
||||
# 发布和订阅连接池大小
|
||||
subscriptionConnectionPoolSize: 50
|
||||
|
||||
--- # mail 邮件发送
|
||||
mail:
|
||||
enabled: false
|
||||
host: smtp.163.com
|
||||
port: 465
|
||||
# 是否需要用户名密码验证
|
||||
auth: true
|
||||
# 发送方,遵循RFC-822标准
|
||||
from: xxx@163.com
|
||||
# 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
|
||||
user: xxx@163.com
|
||||
# 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助)
|
||||
pass: xxxxxxxxxx
|
||||
# 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
|
||||
starttlsEnable: true
|
||||
# 使用SSL安全连接
|
||||
sslEnable: true
|
||||
# SMTP超时时长,单位毫秒,缺省值不超时
|
||||
timeout: 0
|
||||
# Socket连接超时值,单位毫秒,缺省值不超时
|
||||
connectionTimeout: 0
|
||||
|
||||
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
|
||||
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
|
||||
sms:
|
||||
# 配置源类型用于标定配置来源(interface,yaml)
|
||||
config-type: yaml
|
||||
# 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
|
||||
restricted: true
|
||||
# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
|
||||
minute-max: 1
|
||||
# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
|
||||
account-max: 30
|
||||
# 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
|
||||
blends:
|
||||
# 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
|
||||
# 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
|
||||
config1:
|
||||
# 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
|
||||
supplier: alibaba
|
||||
# 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。
|
||||
access-key-id: 您的accessKey
|
||||
# 称为accessSecret有些称之为apiSecret
|
||||
access-key-secret: 您的accessKeySecret
|
||||
signature: 您的短信签名
|
||||
sdk-app-id: 您的sdkAppId
|
||||
config2:
|
||||
# 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
|
||||
supplier: tencent
|
||||
access-key-id: 您的accessKey
|
||||
access-key-secret: 您的accessKeySecret
|
||||
signature: 您的短信签名
|
||||
sdk-app-id: 您的sdkAppId
|
||||
|
||||
|
||||
--- # 三方授权
|
||||
justauth:
|
||||
# 前端外网访问地址
|
||||
address: http://localhost:80
|
||||
type:
|
||||
maxkey:
|
||||
# maxkey 服务器地址
|
||||
# 注意 如下均配置均不需要修改 maxkey 已经内置好了数据
|
||||
server-url: http://sso.maxkey.top
|
||||
client-id: 876892492581044224
|
||||
client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
|
||||
redirect-uri: ${justauth.address}/social-callback?source=maxkey
|
||||
topiam:
|
||||
# topiam 服务器地址
|
||||
server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
|
||||
client-id: 449c4*********937************759
|
||||
client-secret: ac7***********1e0************28d
|
||||
redirect-uri: ${justauth.address}/social-callback?source=topiam
|
||||
scopes: [openid, email, phone, profile]
|
||||
qq:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=qq
|
||||
union-id: false
|
||||
weibo:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=weibo
|
||||
gitee:
|
||||
client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
|
||||
client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
|
||||
redirect-uri: ${justauth.address}/social-callback?source=gitee
|
||||
dingtalk:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=dingtalk
|
||||
baidu:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=baidu
|
||||
csdn:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=csdn
|
||||
coding:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=coding
|
||||
coding-group-name: xx
|
||||
oschina:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=oschina
|
||||
alipay_wallet:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
|
||||
alipay-public-key: MIIB**************DAQAB
|
||||
wechat_open:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=wechat_open
|
||||
wechat_mp:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
|
||||
wechat_enterprise:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
|
||||
agent-id: 1000002
|
||||
gitlab:
|
||||
client-id: 10**********6
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${justauth.address}/social-callback?source=gitlab
|
|
@ -22,7 +22,7 @@ captcha:
|
|||
# 开发环境配置
|
||||
server:
|
||||
# 服务器的HTTP端口,默认为8080
|
||||
port: 8080
|
||||
port: 8090
|
||||
servlet:
|
||||
# 应用的访问路径
|
||||
context-path: /
|
||||
|
@ -139,9 +139,8 @@ tenant:
|
|||
- sys_user_post
|
||||
- sys_user_role
|
||||
- sys_client
|
||||
- sys_oss
|
||||
- sys_oss_config
|
||||
- sys_oss_resource
|
||||
- sys_oss_textbook
|
||||
|
||||
# MyBatisPlus配置
|
||||
# https://baomidou.com/config/
|
||||
|
@ -189,7 +188,7 @@ api-decrypt:
|
|||
springdoc:
|
||||
api-docs:
|
||||
# 是否开启接口文档
|
||||
enabled: true
|
||||
enabled: false
|
||||
# swagger-ui:
|
||||
# # 持久化认证数据
|
||||
# persistAuthorization: true
|
||||
|
@ -270,3 +269,20 @@ websocket:
|
|||
# 设置访问源地址
|
||||
allowedOrigins: '*'
|
||||
|
||||
#--- #flowable配置
|
||||
#flowable:
|
||||
# async-executor-activate: false #关闭定时任务JOB
|
||||
# # 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
|
||||
# database-schema-update: true
|
||||
# activity-font-name: 宋体
|
||||
# label-font-name: 宋体
|
||||
# annotation-font-name: 宋体
|
||||
# # 关闭各个模块生成表,目前只使用工作流基础表
|
||||
# idm:
|
||||
# enabled: false
|
||||
# cmmn:
|
||||
# enabled: false
|
||||
# dmn:
|
||||
# enabled: false
|
||||
# app:
|
||||
# enabled: false
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
<modules>
|
||||
<module>ruoyi-common-bom</module>
|
||||
<module>ruoyi-common-social</module>
|
||||
<module>ruoyi-common-core</module>
|
||||
<module>ruoyi-common-doc</module>
|
||||
<module>ruoyi-common-excel</module>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
</description>
|
||||
|
||||
<properties>
|
||||
<revision>5.2.0-BETA</revision>
|
||||
<revision>5.2.1</revision>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
|
|
@ -10,8 +10,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
|
|||
* @author Lion Li
|
||||
*/
|
||||
@AutoConfiguration
|
||||
// 表示通过aop框架暴露该代理对象,AopContext能够访问
|
||||
@EnableAspectJAutoProxy(exposeProxy = true)
|
||||
@EnableAspectJAutoProxy
|
||||
@EnableAsync(proxyTargetClass = true)
|
||||
public class ApplicationConfig {
|
||||
|
||||
|
|
|
@ -55,6 +55,11 @@ public interface CacheNames {
|
|||
*/
|
||||
String SYS_DEPT = "sys_dept#30d";
|
||||
|
||||
/**
|
||||
* 租户部门
|
||||
*/
|
||||
String SYS_TENANT_DEPT = "sys_tenant_dept#30d";
|
||||
|
||||
/**
|
||||
* OSS内容
|
||||
*/
|
||||
|
@ -70,4 +75,7 @@ public interface CacheNames {
|
|||
*/
|
||||
String ONLINE_TOKEN = "online_tokens";
|
||||
|
||||
String SYS_CATALOG_TEXTBOOK = "sys_catalog_textbook#30d";
|
||||
String SYS_CATALOG_RESOURCE = "sys_catalog_resource#30d";
|
||||
String SYS_CATALOG_PERSON = "sys_catalog_person#30d";
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public interface RegexConstants extends RegexPool {
|
|||
/**
|
||||
* 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符
|
||||
*/
|
||||
String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$";
|
||||
String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&;])[A-Za-z\\d@$!%*?&;]{8,}$";
|
||||
|
||||
/**
|
||||
* 通用状态(0表示正常,1表示停用)
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package org.dromara.common.core.domain.event;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 总体流程监听
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class ProcessEvent implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 流程定义key
|
||||
*/
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
private String businessKey;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 当为true时为申请人节点办理
|
||||
*/
|
||||
private boolean submit;
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package org.dromara.common.core.domain.event;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 流程办理监听
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class ProcessTaskEvent implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 流程定义key
|
||||
*/
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 审批节点key
|
||||
*/
|
||||
private String taskDefinitionKey;
|
||||
|
||||
/**
|
||||
* 任务id
|
||||
*/
|
||||
private String taskId;
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
private String businessKey;
|
||||
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
package org.dromara.common.core.enums;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 业务状态枚举
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum BusinessStatusEnum {
|
||||
/**
|
||||
* 已撤销
|
||||
*/
|
||||
CANCEL("cancel", "已撤销"),
|
||||
/**
|
||||
* 草稿
|
||||
*/
|
||||
DRAFT("draft", "草稿"),
|
||||
/**
|
||||
* 待审核
|
||||
*/
|
||||
WAITING("waiting", "待审核"),
|
||||
/**
|
||||
* 已完成
|
||||
*/
|
||||
FINISH("finish", "已完成"),
|
||||
/**
|
||||
* 已作废
|
||||
*/
|
||||
INVALID("invalid", "已作废"),
|
||||
/**
|
||||
* 已退回
|
||||
*/
|
||||
BACK("back", "已退回"),
|
||||
/**
|
||||
* 已终止
|
||||
*/
|
||||
TERMINATION("termination", "已终止");
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private final String status;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String desc;
|
||||
|
||||
/**
|
||||
* 获取业务状态
|
||||
*
|
||||
* @param status 状态
|
||||
*/
|
||||
public static String findByStatus(String status) {
|
||||
if (StringUtils.isBlank(status)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
return Arrays.stream(BusinessStatusEnum.values())
|
||||
.filter(statusEnum -> statusEnum.getStatus().equals(status))
|
||||
.findFirst()
|
||||
.map(BusinessStatusEnum::getDesc)
|
||||
.orElse(StrUtil.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动流程校验
|
||||
*
|
||||
* @param status 状态
|
||||
*/
|
||||
public static void checkStartStatus(String status) {
|
||||
if (WAITING.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已提交过申请,正在审批中!");
|
||||
} else if (FINISH.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已完成申请!");
|
||||
} else if (INVALID.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已作废!");
|
||||
} else if (TERMINATION.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已终止!");
|
||||
} else if (StringUtils.isBlank(status)) {
|
||||
throw new ServiceException("流程状态为空!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销流程校验
|
||||
*
|
||||
* @param status 状态
|
||||
*/
|
||||
public static void checkCancelStatus(String status) {
|
||||
if (CANCEL.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已撤销!");
|
||||
} else if (FINISH.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已完成申请!");
|
||||
} else if (INVALID.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已作废!");
|
||||
} else if (TERMINATION.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已终止!");
|
||||
} else if (BACK.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已退回!");
|
||||
} else if (StringUtils.isBlank(status)) {
|
||||
throw new ServiceException("流程状态为空!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 驳回流程校验
|
||||
*
|
||||
* @param status 状态
|
||||
*/
|
||||
public static void checkBackStatus(String status) {
|
||||
if (BACK.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已退回!");
|
||||
} else if (FINISH.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已完成申请!");
|
||||
} else if (INVALID.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已作废!");
|
||||
} else if (TERMINATION.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已终止!");
|
||||
} else if (CANCEL.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已撤销!");
|
||||
} else if (StringUtils.isBlank(status)) {
|
||||
throw new ServiceException("流程状态为空!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 作废,终止流程校验
|
||||
*
|
||||
* @param status 状态
|
||||
*/
|
||||
public static void checkInvalidStatus(String status) {
|
||||
if (FINISH.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已完成申请!");
|
||||
} else if (INVALID.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已作废!");
|
||||
} else if (TERMINATION.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已终止!");
|
||||
} else if (StringUtils.isBlank(status)) {
|
||||
throw new ServiceException("流程状态为空!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,4 +15,12 @@ public interface DeptService {
|
|||
*/
|
||||
String selectDeptNameByIds(String deptIds);
|
||||
|
||||
/**
|
||||
* 通过部门名称查询租户部门Id
|
||||
*
|
||||
* @param deptName 部门名称
|
||||
* @return 部门ID
|
||||
*/
|
||||
Long selectDeptIdByName(String deptName);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package org.dromara.common.core.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 通用 工作流服务
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
public interface WorkflowService {
|
||||
|
||||
/**
|
||||
* 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息
|
||||
*
|
||||
* @param businessKeys 业务id
|
||||
* @return 结果
|
||||
*/
|
||||
boolean deleteRunAndHisInstance(List<String> businessKeys);
|
||||
|
||||
/**
|
||||
* 获取当前流程状态
|
||||
*
|
||||
* @param taskId 任务id
|
||||
*/
|
||||
String getBusinessStatusByTaskId(String taskId);
|
||||
|
||||
/**
|
||||
* 获取当前流程状态
|
||||
*
|
||||
* @param businessKey 业务id
|
||||
*/
|
||||
String getBusinessStatus(String businessKey);
|
||||
|
||||
/**
|
||||
* 设置流程变量(全局变量)
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @param variableName 变量名称
|
||||
* @param value 变量值
|
||||
*/
|
||||
void setVariable(String taskId, String variableName, Object value);
|
||||
|
||||
/**
|
||||
* 设置流程变量(全局变量)
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @param businessKey 业务id
|
||||
* @return 结果
|
||||
*/
|
||||
String getInstanceIdByBusinessKey(String businessKey);
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package org.dromara.common.core.utils;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import org.springframework.aop.framework.AopContext;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.autoconfigure.thread.Threading;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
@ -50,7 +49,7 @@ public final class SpringUtils extends SpringUtil {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getAopProxy(T invoker) {
|
||||
return (T) AopContext.currentProxy();
|
||||
return (T) getBean(invoker.getClass());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import lombok.NoArgsConstructor;
|
|||
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -34,6 +35,34 @@ public class StreamUtils {
|
|||
return collection.stream().filter(function).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到流中满足条件的第一个元素
|
||||
*
|
||||
* @param collection 需要查询的集合
|
||||
* @param function 过滤方法
|
||||
* @return 找到符合条件的第一个元素,没有则返回null
|
||||
*/
|
||||
public static <E> E findFirst(Collection<E> collection, Predicate<E> function) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return null;
|
||||
}
|
||||
return collection.stream().filter(function).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到流中任意一个满足条件的元素
|
||||
*
|
||||
* @param collection 需要查询的集合
|
||||
* @param function 过滤方法
|
||||
* @return 找到符合条件的任意一个元素,没有则返回null
|
||||
*/
|
||||
public static <E> Optional<E> findAny(Collection<E> collection, Predicate<E> function) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return collection.stream().filter(function).findAny();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection拼接
|
||||
*
|
||||
|
|
|
@ -60,6 +60,16 @@ public class RegexValidator extends Validator {
|
|||
return isMatchRegex(ACCOUNT, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查输入的密码是否匹配预定义的规则
|
||||
*
|
||||
* @param value 要验证的密码
|
||||
* @return 如果密码符合规则,返回 true;否则,返回 false。
|
||||
*/
|
||||
public static boolean isPassword(CharSequence value) {
|
||||
return isMatchRegex(PASSWORD, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证输入的账号是否符合规则,如果不符合,则抛出 ValidateException 异常
|
||||
*
|
||||
|
|
|
@ -11,6 +11,7 @@ import io.swagger.v3.oas.models.Paths;
|
|||
import io.swagger.v3.oas.models.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
||||
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
|
||||
import org.springdoc.core.properties.SpringDocConfigProperties;
|
||||
|
@ -230,7 +231,7 @@ public class OpenApiHandler extends OpenAPIService {
|
|||
.flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet());
|
||||
methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class));
|
||||
if (!CollectionUtils.isEmpty(methodTags)) {
|
||||
tagsStr.addAll(methodTags.stream().map(tag -> propertyResolverUtils.resolve(tag.name(), locale)).collect(Collectors.toSet()));
|
||||
tagsStr.addAll(StreamUtils.toSet(methodTags, tag -> propertyResolverUtils.resolve(tag.name(), locale)));
|
||||
List<io.swagger.v3.oas.annotations.tags.Tag> allTags = new ArrayList<>(methodTags);
|
||||
addTags(allTags, tags, locale);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
|||
@AutoConfiguration
|
||||
@ConditionalOnProperty(prefix = "snail-job", name = "enabled", havingValue = "true")
|
||||
@EnableScheduling
|
||||
@EnableSnailJob(group = "${snail-job.group-name}")
|
||||
@EnableSnailJob
|
||||
public class SnailJobConfig {
|
||||
|
||||
@EventListener(SnailClientStartingEvent.class)
|
||||
|
|
|
@ -56,7 +56,7 @@ public class LogAspect {
|
|||
* 处理请求前执行
|
||||
*/
|
||||
@Before(value = "@annotation(controllerLog)")
|
||||
public void boBefore(JoinPoint joinPoint, Log controllerLog) {
|
||||
public void doBefore(JoinPoint joinPoint, Log controllerLog) {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
KEY_CACHE.set(stopWatch);
|
||||
stopWatch.start();
|
||||
|
|
|
@ -3,9 +3,10 @@ package org.dromara.common.mybatis.annotation;
|
|||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 数据权限
|
||||
*
|
||||
* 数据权限注解,用于标记数据权限的占位符关键字和替换值
|
||||
* <p>
|
||||
* 一个注解只能对应一个模板
|
||||
* </p>
|
||||
*
|
||||
* @author Lion Li
|
||||
* @version 3.5.0
|
||||
|
@ -16,12 +17,16 @@ import java.lang.annotation.*;
|
|||
public @interface DataColumn {
|
||||
|
||||
/**
|
||||
* 占位符关键字
|
||||
* 数据权限模板的占位符关键字,默认为 "deptName"
|
||||
*
|
||||
* @return 占位符关键字数组
|
||||
*/
|
||||
String[] key() default "deptName";
|
||||
|
||||
/**
|
||||
* 占位符替换值
|
||||
* 数据权限模板的占位符替换值,默认为 "dept_id"
|
||||
*
|
||||
* @return 占位符替换值数组
|
||||
*/
|
||||
String[] value() default "dept_id";
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package org.dromara.common.mybatis.annotation;
|
|||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 数据权限组
|
||||
* 数据权限组注解,用于标记数据权限配置数组
|
||||
*
|
||||
* @author Lion Li
|
||||
* @version 3.5.0
|
||||
|
@ -13,6 +13,11 @@ import java.lang.annotation.*;
|
|||
@Documented
|
||||
public @interface DataPermission {
|
||||
|
||||
/**
|
||||
* 数据权限配置数组,用于指定数据权限的占位符关键字和替换值
|
||||
*
|
||||
* @return 数据权限配置数组
|
||||
*/
|
||||
DataColumn[] value();
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import java.util.Map;
|
|||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class BaseEntity implements Serializable {
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.baomidou.mybatisplus.extension.toolkit.Db;
|
|||
import org.apache.ibatis.logging.Log;
|
||||
import org.apache.ibatis.logging.LogFactory;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
@ -34,73 +35,125 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
|
|||
|
||||
Log log = LogFactory.getLog(BaseMapperPlus.class);
|
||||
|
||||
/**
|
||||
* 获取当前实例对象关联的泛型类型 V 的 Class 对象
|
||||
*
|
||||
* @return 返回当前实例对象关联的泛型类型 V 的 Class 对象
|
||||
*/
|
||||
default Class<V> currentVoClass() {
|
||||
return (Class<V>) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前实例对象关联的泛型类型 T 的 Class 对象
|
||||
*
|
||||
* @return 返回当前实例对象关联的泛型类型 T 的 Class 对象
|
||||
*/
|
||||
default Class<T> currentModelClass() {
|
||||
return (Class<T>) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用默认的查询条件查询并返回结果列表
|
||||
*
|
||||
* @return 返回查询结果的列表
|
||||
*/
|
||||
default List<T> selectList() {
|
||||
return this.selectList(new QueryWrapper<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入
|
||||
* 批量插入实体对象集合
|
||||
*
|
||||
* @param entityList 实体对象集合
|
||||
* @return 插入操作是否成功的布尔值
|
||||
*/
|
||||
default boolean insertBatch(Collection<T> entityList) {
|
||||
return Db.saveBatch(entityList);
|
||||
Db.saveBatch(entityList);
|
||||
// 临时解决 新版本 mp 插入状态判断错误问题
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新
|
||||
* 批量根据ID更新实体对象集合
|
||||
*
|
||||
* @param entityList 实体对象集合
|
||||
* @return 更新操作是否成功的布尔值
|
||||
*/
|
||||
default boolean updateBatchById(Collection<T> entityList) {
|
||||
return Db.updateBatchById(entityList);
|
||||
Db.updateBatchById(entityList);
|
||||
// 临时解决 新版本 mp 插入状态判断错误问题
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入或更新
|
||||
* 批量插入或更新实体对象集合
|
||||
*
|
||||
* @param entityList 实体对象集合
|
||||
* @return 插入或更新操作是否成功的布尔值
|
||||
*/
|
||||
default boolean insertOrUpdateBatch(Collection<T> entityList) {
|
||||
return Db.saveOrUpdateBatch(entityList);
|
||||
Db.saveOrUpdateBatch(entityList);
|
||||
// 临时解决 新版本 mp 插入状态判断错误问题
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入(包含限制条数)
|
||||
* 批量插入实体对象集合并指定批处理大小
|
||||
*
|
||||
* @param entityList 实体对象集合
|
||||
* @param batchSize 批处理大小
|
||||
* @return 插入操作是否成功的布尔值
|
||||
*/
|
||||
default boolean insertBatch(Collection<T> entityList, int batchSize) {
|
||||
return Db.saveBatch(entityList, batchSize);
|
||||
Db.saveBatch(entityList, batchSize);
|
||||
// 临时解决 新版本 mp 插入状态判断错误问题
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新(包含限制条数)
|
||||
* 批量根据ID更新实体对象集合并指定批处理大小
|
||||
*
|
||||
* @param entityList 实体对象集合
|
||||
* @param batchSize 批处理大小
|
||||
* @return 更新操作是否成功的布尔值
|
||||
*/
|
||||
default boolean updateBatchById(Collection<T> entityList, int batchSize) {
|
||||
return Db.updateBatchById(entityList, batchSize);
|
||||
Db.updateBatchById(entityList, batchSize);
|
||||
// 临时解决 新版本 mp 插入状态判断错误问题
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入或更新(包含限制条数)
|
||||
* 批量插入或更新实体对象集合并指定批处理大小
|
||||
*
|
||||
* @param entityList 实体对象集合
|
||||
* @param batchSize 批处理大小
|
||||
* @return 插入或更新操作是否成功的布尔值
|
||||
*/
|
||||
default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {
|
||||
return Db.saveOrUpdateBatch(entityList, batchSize);
|
||||
Db.saveOrUpdateBatch(entityList, batchSize);
|
||||
// 临时解决 新版本 mp 插入状态判断错误问题
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入或更新(包含限制条数)
|
||||
* 根据ID查询单个VO对象
|
||||
*
|
||||
* @param id 主键ID
|
||||
* @return 查询到的单个VO对象
|
||||
*/
|
||||
default boolean insertOrUpdate(T entity) {
|
||||
return Db.saveOrUpdate(entity);
|
||||
}
|
||||
|
||||
default V selectVoById(Serializable id) {
|
||||
return selectVoById(id, this.currentVoClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ID 查询
|
||||
* 根据ID查询单个VO对象并将其转换为指定的VO类
|
||||
*
|
||||
* @param id 主键ID
|
||||
* @param voClass 要转换的VO类的Class对象
|
||||
* @param <C> VO类的类型
|
||||
* @return 查询到的单个VO对象,经过转换为指定的VO类后返回
|
||||
*/
|
||||
default <C> C selectVoById(Serializable id, Class<C> voClass) {
|
||||
T obj = this.selectById(id);
|
||||
|
@ -110,12 +163,23 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
|
|||
return MapstructUtils.convert(obj, voClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID集合批量查询VO对象列表
|
||||
*
|
||||
* @param idList 主键ID集合
|
||||
* @return 查询到的VO对象列表
|
||||
*/
|
||||
default List<V> selectVoBatchIds(Collection<? extends Serializable> idList) {
|
||||
return selectVoBatchIds(idList, this.currentVoClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询(根据ID 批量查询)
|
||||
* 根据ID集合批量查询实体对象列表,并将其转换为指定的VO对象列表
|
||||
*
|
||||
* @param idList 主键ID集合
|
||||
* @param voClass 要转换的VO类的Class对象
|
||||
* @param <C> VO类的类型
|
||||
* @return 查询到的VO对象列表,经过转换为指定的VO类后返回
|
||||
*/
|
||||
default <C> List<C> selectVoBatchIds(Collection<? extends Serializable> idList, Class<C> voClass) {
|
||||
List<T> list = this.selectBatchIds(idList);
|
||||
|
@ -125,12 +189,23 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
|
|||
return MapstructUtils.convert(list, voClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据查询条件Map查询VO对象列表
|
||||
*
|
||||
* @param map 查询条件Map
|
||||
* @return 查询到的VO对象列表
|
||||
*/
|
||||
default List<V> selectVoByMap(Map<String, Object> map) {
|
||||
return selectVoByMap(map, this.currentVoClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询(根据 columnMap 条件)
|
||||
* 根据查询条件Map查询实体对象列表,并将其转换为指定的VO对象列表
|
||||
*
|
||||
* @param map 查询条件Map
|
||||
* @param voClass 要转换的VO类的Class对象
|
||||
* @param <C> VO类的类型
|
||||
* @return 查询到的VO对象列表,经过转换为指定的VO类后返回
|
||||
*/
|
||||
default <C> List<C> selectVoByMap(Map<String, Object> map, Class<C> voClass) {
|
||||
List<T> list = this.selectByMap(map);
|
||||
|
@ -140,23 +215,47 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
|
|||
return MapstructUtils.convert(list, voClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询单个VO对象
|
||||
*
|
||||
* @param wrapper 查询条件Wrapper
|
||||
* @return 查询到的单个VO对象
|
||||
*/
|
||||
default V selectVoOne(Wrapper<T> wrapper) {
|
||||
return selectVoOne(wrapper, this.currentVoClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询单个VO对象,并根据需要决定是否抛出异常
|
||||
*
|
||||
* @param wrapper 查询条件Wrapper
|
||||
* @param throwEx 是否抛出异常的标志
|
||||
* @return 查询到的单个VO对象
|
||||
*/
|
||||
default V selectVoOne(Wrapper<T> wrapper, boolean throwEx) {
|
||||
return selectVoOne(wrapper, this.currentVoClass(), throwEx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 entity 条件,查询一条记录
|
||||
* 根据条件查询单个VO对象,并指定返回的VO对象的类型
|
||||
*
|
||||
* @param wrapper 查询条件Wrapper
|
||||
* @param voClass 返回的VO对象的Class对象
|
||||
* @param <C> 返回的VO对象的类型
|
||||
* @return 查询到的单个VO对象,经过类型转换为指定的VO类后返回
|
||||
*/
|
||||
default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass) {
|
||||
return selectVoOne(wrapper, voClass, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 entity 条件,查询一条记录
|
||||
* 根据条件查询单个实体对象,并将其转换为指定的VO对象
|
||||
*
|
||||
* @param wrapper 查询条件Wrapper
|
||||
* @param voClass 要转换的VO类的Class对象
|
||||
* @param throwEx 是否抛出异常的标志
|
||||
* @param <C> VO类的类型
|
||||
* @return 查询到的单个VO对象,经过转换为指定的VO类后返回
|
||||
*/
|
||||
default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass, boolean throwEx) {
|
||||
T obj = this.selectOne(wrapper, throwEx);
|
||||
|
@ -166,16 +265,32 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
|
|||
return MapstructUtils.convert(obj, voClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有VO对象列表
|
||||
*
|
||||
* @return 查询到的VO对象列表
|
||||
*/
|
||||
default List<V> selectVoList() {
|
||||
return selectVoList(new QueryWrapper<>(), this.currentVoClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询VO对象列表
|
||||
*
|
||||
* @param wrapper 查询条件Wrapper
|
||||
* @return 查询到的VO对象列表
|
||||
*/
|
||||
default List<V> selectVoList(Wrapper<T> wrapper) {
|
||||
return selectVoList(wrapper, this.currentVoClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 entity 条件,查询全部记录
|
||||
* 根据条件查询实体对象列表,并将其转换为指定的VO对象列表
|
||||
*
|
||||
* @param wrapper 查询条件Wrapper
|
||||
* @param voClass 要转换的VO类的Class对象
|
||||
* @param <C> VO类的类型
|
||||
* @return 查询到的VO对象列表,经过转换为指定的VO类后返回
|
||||
*/
|
||||
default <C> List<C> selectVoList(Wrapper<T> wrapper, Class<C> voClass) {
|
||||
List<T> list = this.selectList(wrapper);
|
||||
|
@ -185,15 +300,31 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
|
|||
return MapstructUtils.convert(list, voClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件分页查询VO对象列表
|
||||
*
|
||||
* @param page 分页信息
|
||||
* @param wrapper 查询条件Wrapper
|
||||
* @return 查询到的VO对象分页列表
|
||||
*/
|
||||
default <P extends IPage<V>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper) {
|
||||
return selectVoPage(page, wrapper, this.currentVoClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询VO
|
||||
* 根据条件分页查询实体对象列表,并将其转换为指定的VO对象分页列表
|
||||
*
|
||||
* @param page 分页信息
|
||||
* @param wrapper 查询条件Wrapper
|
||||
* @param voClass 要转换的VO类的Class对象
|
||||
* @param <C> VO类的类型
|
||||
* @param <P> VO对象分页列表的类型
|
||||
* @return 查询到的VO对象分页列表,经过转换为指定的VO类后返回
|
||||
*/
|
||||
default <C, P extends IPage<C>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper, Class<C> voClass) {
|
||||
// 根据条件分页查询实体对象列表
|
||||
List<T> list = this.selectList(page, wrapper);
|
||||
// 创建一个新的VO对象分页列表,并设置分页信息
|
||||
IPage<C> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return (P) voPage;
|
||||
|
@ -202,8 +333,16 @@ public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
|
|||
return (P) voPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询符合条件的对象,并将其转换为指定类型的对象列表
|
||||
*
|
||||
* @param wrapper 查询条件Wrapper
|
||||
* @param mapper 转换函数,用于将查询到的对象转换为指定类型的对象
|
||||
* @param <C> 要转换的对象的类型
|
||||
* @return 查询到的符合条件的对象列表,经过转换为指定类型的对象后返回
|
||||
*/
|
||||
default <C> List<C> selectObjs(Wrapper<T> wrapper, Function<? super Object, C> mapper) {
|
||||
return this.selectObjs(wrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());
|
||||
return StreamUtils.toList(this.selectObjs(wrapper), mapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@ import cn.hutool.core.collection.CollUtil;
|
|||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.sql.SqlUtil;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
@ -19,7 +19,6 @@ import java.util.List;
|
|||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class PageQuery implements Serializable {
|
||||
|
||||
|
@ -56,6 +55,9 @@ public class PageQuery implements Serializable {
|
|||
*/
|
||||
public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* 构建分页对象
|
||||
*/
|
||||
public <T> Page<T> build() {
|
||||
Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM);
|
||||
Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE);
|
||||
|
|
|
@ -14,7 +14,6 @@ import java.util.List;
|
|||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class TableDataInfo<T> implements Serializable {
|
||||
|
@ -53,6 +52,9 @@ public class TableDataInfo<T> implements Serializable {
|
|||
this.total = total;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据分页对象构建表格分页数据对象
|
||||
*/
|
||||
public static <T> TableDataInfo<T> build(IPage<T> page) {
|
||||
TableDataInfo<T> rspData = new TableDataInfo<>();
|
||||
rspData.setCode(HttpStatus.HTTP_OK);
|
||||
|
@ -62,6 +64,9 @@ public class TableDataInfo<T> implements Serializable {
|
|||
return rspData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数据列表构建表格分页数据对象
|
||||
*/
|
||||
public static <T> TableDataInfo<T> build(List<T> list) {
|
||||
TableDataInfo<T> rspData = new TableDataInfo<>();
|
||||
rspData.setCode(HttpStatus.HTTP_OK);
|
||||
|
@ -71,6 +76,9 @@ public class TableDataInfo<T> implements Serializable {
|
|||
return rspData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建表格分页数据对象
|
||||
*/
|
||||
public static <T> TableDataInfo<T> build() {
|
||||
TableDataInfo<T> rspData = new TableDataInfo<>();
|
||||
rspData.setCode(HttpStatus.HTTP_OK);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package org.dromara.common.mybatis.enums;
|
||||
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 数据库类型
|
||||
|
@ -33,8 +33,17 @@ public enum DataBaseType {
|
|||
*/
|
||||
SQL_SERVER("Microsoft SQL Server");
|
||||
|
||||
/**
|
||||
* 数据库类型
|
||||
*/
|
||||
private final String type;
|
||||
|
||||
/**
|
||||
* 根据数据库产品名称查找对应的数据库类型
|
||||
*
|
||||
* @param databaseProductName 数据库产品名称
|
||||
* @return 对应的数据库类型枚举值,如果未找到则返回 null
|
||||
*/
|
||||
public static DataBaseType find(String databaseProductName) {
|
||||
if (StringUtils.isBlank(databaseProductName)) {
|
||||
return null;
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
package org.dromara.common.mybatis.enums;
|
||||
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.helper.DataPermissionHelper;
|
||||
|
||||
/**
|
||||
* 数据权限类型
|
||||
* 数据权限类型枚举
|
||||
* <p>
|
||||
* 语法支持 spel 模板表达式
|
||||
* <p>
|
||||
* 内置数据 user 当前用户 内容参考 LoginUser
|
||||
* 如需扩展数据 可使用 {@link DataPermissionHelper} 操作
|
||||
* 内置服务 sdss 系统数据权限服务 内容参考 SysDataScopeService
|
||||
* 如需扩展更多自定义服务 可以参考 sdss 自行编写
|
||||
* 支持使用 SpEL 模板表达式定义 SQL 查询条件
|
||||
* 内置数据:
|
||||
* - {@code user}: 当前登录用户信息,参考 {@link LoginUser}
|
||||
* 内置服务:
|
||||
* - {@code sdss}: 系统数据权限服务,参考 {@link ISysDataScopeService}
|
||||
* 如需扩展数据,可以通过 {@link DataPermissionHelper} 进行操作
|
||||
* 如需扩展服务,可以通过 {@link ISysDataScopeService} 自行编写
|
||||
* </p>
|
||||
*
|
||||
* @author Lion Li
|
||||
* @version 3.5.0
|
||||
|
@ -29,36 +32,50 @@ public enum DataScopeType {
|
|||
|
||||
/**
|
||||
* 自定数据权限
|
||||
* 使用 SpEL 表达式:`#{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} )`
|
||||
* 如果不满足条件,则使用默认 SQL 表达式:`1 = 0`
|
||||
*/
|
||||
CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", " 1 = 0 "),
|
||||
|
||||
/**
|
||||
* 部门数据权限
|
||||
* 使用 SpEL 表达式:`#{#deptName} = #{#user.deptId}`
|
||||
* 如果不满足条件,则使用默认 SQL 表达式:`1 = 0`
|
||||
*/
|
||||
DEPT("3", " #{#deptName} = #{#user.deptId} ", " 1 = 0 "),
|
||||
|
||||
/**
|
||||
* 部门及以下数据权限
|
||||
* 使用 SpEL 表达式:`#{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )}`
|
||||
* 如果不满足条件,则使用默认 SQL 表达式:`1 = 0`
|
||||
*/
|
||||
DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", " 1 = 0 "),
|
||||
|
||||
/**
|
||||
* 仅本人数据权限
|
||||
* 使用 SpEL 表达式:`#{#userName} = #{#user.userId}`
|
||||
* 如果不满足条件,则使用默认 SQL 表达式:`1 = 0`
|
||||
*/
|
||||
SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 ");
|
||||
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* 语法 采用 spel 模板表达式
|
||||
* SpEL 模板表达式,用于构建 SQL 查询条件
|
||||
*/
|
||||
private final String sqlTemplate;
|
||||
|
||||
/**
|
||||
* 不满足 sqlTemplate 则填充
|
||||
* 如果不满足 {@code sqlTemplate} 的条件,则使用此默认 SQL 表达式
|
||||
*/
|
||||
private final String elseSql;
|
||||
|
||||
/**
|
||||
* 根据枚举代码查找对应的枚举值
|
||||
*
|
||||
* @param code 枚举代码
|
||||
* @return 对应的枚举值,如果未找到则返回 null
|
||||
*/
|
||||
public static DataScopeType findCode(String code) {
|
||||
if (StringUtils.isBlank(code)) {
|
||||
return null;
|
||||
|
|
|
@ -3,12 +3,12 @@ package org.dromara.common.mybatis.handler;
|
|||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
|
@ -21,21 +21,28 @@ import java.util.Date;
|
|||
@Slf4j
|
||||
public class InjectionMetaObjectHandler implements MetaObjectHandler {
|
||||
|
||||
/**
|
||||
* 插入填充方法,用于在插入数据时自动填充实体对象中的创建时间、更新时间、创建人、更新人等信息
|
||||
*
|
||||
* @param metaObject 元对象,用于获取原始对象并进行填充
|
||||
*/
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
try {
|
||||
if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
|
||||
// 获取当前时间作为创建时间和更新时间,如果创建时间不为空,则使用创建时间,否则使用当前时间
|
||||
Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime())
|
||||
? baseEntity.getCreateTime() : new Date();
|
||||
baseEntity.setCreateTime(current);
|
||||
baseEntity.setUpdateTime(current);
|
||||
|
||||
// 如果创建人为空,则填充当前登录用户的信息
|
||||
if (ObjectUtil.isNull(baseEntity.getCreateBy())) {
|
||||
LoginUser loginUser = getLoginUser();
|
||||
if (ObjectUtil.isNotNull(loginUser)) {
|
||||
Long userId = loginUser.getUserId();
|
||||
// 当前已登录 且 创建人为空 则填充
|
||||
// 填充创建人、更新人和创建部门信息
|
||||
baseEntity.setCreateBy(userId);
|
||||
// 当前已登录 且 更新人为空 则填充
|
||||
baseEntity.setUpdateBy(userId);
|
||||
baseEntity.setCreateDept(ObjectUtil.isNotNull(baseEntity.getCreateDept())
|
||||
? baseEntity.getCreateDept() : loginUser.getDeptId());
|
||||
|
@ -47,19 +54,24 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新填充方法,用于在更新数据时自动填充实体对象中的更新时间和更新人信息
|
||||
*
|
||||
* @param metaObject 元对象,用于获取原始对象并进行填充
|
||||
*/
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
try {
|
||||
if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
|
||||
// 获取当前时间作为更新时间,无论原始对象中的更新时间是否为空都填充
|
||||
Date current = new Date();
|
||||
// 更新时间填充(不管为不为空)
|
||||
baseEntity.setUpdateTime(current);
|
||||
// 当前已登录 更新人填充(不管为不为空)
|
||||
|
||||
// 获取当前登录用户的ID,并填充更新人信息
|
||||
Long userId = LoginHelper.getUserId();
|
||||
if (ObjectUtil.isNotNull(userId)) {
|
||||
baseEntity.setUpdateBy(userId);
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
|
||||
|
@ -67,7 +79,9 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取登录用户名
|
||||
* 获取当前登录用户信息
|
||||
*
|
||||
* @return 当前登录用户的信息,如果用户未登录则返回 null
|
||||
*/
|
||||
private LoginUser getLoginUser() {
|
||||
LoginUser loginUser;
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
package org.dromara.common.mybatis.handler;
|
||||
|
||||
import org.dromara.common.core.domain.R;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.mybatis.spring.MyBatisSystemException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Mybatis异常处理器
|
||||
*
|
||||
|
|
|
@ -68,13 +68,27 @@ public class PlusDataPermissionHandler {
|
|||
*/
|
||||
private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory());
|
||||
|
||||
/**
|
||||
* 构造方法,扫描指定包下的 Mapper 类并初始化缓存
|
||||
*
|
||||
* @param mapperPackage Mapper 类所在的包路径
|
||||
*/
|
||||
public PlusDataPermissionHandler(String mapperPackage) {
|
||||
scanMapperClasses(mapperPackage);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取数据过滤条件的 SQL 片段
|
||||
*
|
||||
* @param where 原始的查询条件表达式
|
||||
* @param mappedStatementId Mapper 方法的 ID
|
||||
* @param isSelect 是否为查询语句
|
||||
* @return 数据过滤条件的 SQL 片段
|
||||
*/
|
||||
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();
|
||||
|
@ -84,6 +98,7 @@ public class PlusDataPermissionHandler {
|
|||
if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) {
|
||||
return where;
|
||||
}
|
||||
// 构造数据过滤条件的 SQL 片段
|
||||
String dataFilterSql = buildDataFilter(dataPermission.value(), isSelect);
|
||||
if (StringUtils.isBlank(dataFilterSql)) {
|
||||
return where;
|
||||
|
@ -103,7 +118,12 @@ public class PlusDataPermissionHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* 构造数据过滤sql
|
||||
* 构建数据过滤条件的 SQL 语句
|
||||
*
|
||||
* @param dataColumns 数据权限注解中的列信息
|
||||
* @param isSelect 标志当前操作是否为查询操作,查询操作和更新或删除操作在处理过滤条件时会有不同的处理方式
|
||||
* @return 构建的数据过滤条件的 SQL 语句
|
||||
* @throws ServiceException 如果角色的数据范围异常或者 key 与 value 的长度不匹配,则抛出 ServiceException 异常
|
||||
*/
|
||||
private String buildDataFilter(DataColumn[] dataColumns, boolean isSelect) {
|
||||
// 更新或删除需满足所有条件
|
||||
|
@ -159,20 +179,29 @@ public class PlusDataPermissionHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* 通过 mapperPackage 设置的扫描包 扫描缓存有注解的方法与类
|
||||
* 扫描指定包下的 Mapper 类,并查找其中带有特定注解的方法或类
|
||||
*
|
||||
* @param mapperPackage Mapper 类所在的包路径
|
||||
*/
|
||||
private void scanMapperClasses(String mapperPackage) {
|
||||
// 创建资源解析器和元数据读取工厂
|
||||
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
|
||||
// 将 Mapper 包路径按分隔符拆分为数组
|
||||
String[] packagePatternArray = StringUtils.splitPreserveAllTokens(mapperPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
||||
String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
|
||||
try {
|
||||
for (String packagePattern : packagePatternArray) {
|
||||
// 将包路径转换为资源路径
|
||||
String path = ClassUtils.convertClassNameToResourcePath(packagePattern);
|
||||
// 获取指定路径下的所有 .class 文件资源
|
||||
Resource[] resources = resolver.getResources(classpath + path + "/*.class");
|
||||
for (Resource resource : resources) {
|
||||
// 获取资源的类元数据
|
||||
ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata();
|
||||
// 获取资源对应的类对象
|
||||
Class<?> clazz = Resources.classForName(classMetadata.getClassName());
|
||||
// 查找类中的特定注解
|
||||
findAnnotation(clazz);
|
||||
}
|
||||
}
|
||||
|
@ -181,9 +210,13 @@ 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;
|
||||
|
@ -194,17 +227,24 @@ public class PlusDataPermissionHandler {
|
|||
dataPermissionCacheMap.put(mappedStatementId, dataPermission);
|
||||
}
|
||||
}
|
||||
// 获取类注解
|
||||
if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) {
|
||||
dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class);
|
||||
dataPermissionCacheMap.put(clazz.getName(), dataPermission);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据映射语句 ID 或类名获取对应的 DataPermission 注解对象
|
||||
*
|
||||
* @param mapperId 映射语句 ID
|
||||
* @return DataPermission 注解对象,如果不存在则返回 null
|
||||
*/
|
||||
public DataPermission getDataPermission(String mapperId) {
|
||||
// 检查缓存中是否包含映射语句 ID 对应的 DataPermission 注解对象
|
||||
if (dataPermissionCacheMap.containsKey(mapperId)) {
|
||||
return dataPermissionCacheMap.get(mapperId);
|
||||
}
|
||||
// 如果缓存中不包含映射语句 ID 对应的 DataPermission 注解对象,则尝试使用类名作为键查找
|
||||
String clazzName = mapperId.substring(0, mapperId.lastIndexOf("."));
|
||||
if (dataPermissionCacheMap.containsKey(clazzName)) {
|
||||
return dataPermissionCacheMap.get(clazzName);
|
||||
|
@ -213,7 +253,10 @@ public class PlusDataPermissionHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* 是否无效
|
||||
* 检查给定的映射语句 ID 是否有效,即是否能够找到对应的 DataPermission 注解对象
|
||||
*
|
||||
* @param mapperId 映射语句 ID
|
||||
* @return 如果找到对应的 DataPermission 注解对象,则返回 false;否则返回 true
|
||||
*/
|
||||
public boolean invalid(String mapperId) {
|
||||
return getDataPermission(mapperId) == null;
|
||||
|
|
|
@ -2,11 +2,11 @@ package org.dromara.common.mybatis.helper;
|
|||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.mybatis.enums.DataBaseType;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
|
@ -14,7 +14,6 @@ import java.sql.DatabaseMetaData;
|
|||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 数据库助手
|
||||
|
|
|
@ -24,17 +24,35 @@ public class DataPermissionHelper {
|
|||
|
||||
private static final String DATA_PERMISSION_KEY = "data:permission";
|
||||
|
||||
/**
|
||||
* 从上下文中获取指定键的变量值,并将其转换为指定的类型
|
||||
*
|
||||
* @param key 变量的键
|
||||
* @param <T> 变量值的类型
|
||||
* @return 指定键的变量值,如果不存在则返回 null
|
||||
*/
|
||||
public static <T> T getVariable(String key) {
|
||||
Map<String, Object> context = getContext();
|
||||
return (T) context.get(key);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 向上下文中设置指定键的变量值
|
||||
*
|
||||
* @param key 要设置的变量的键
|
||||
* @param value 要设置的变量值
|
||||
*/
|
||||
public static void setVariable(String key, Object value) {
|
||||
Map<String, Object> context = getContext();
|
||||
context.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据权限上下文
|
||||
*
|
||||
* @return 存储在SaStorage中的Map对象,用于存储数据权限相关的上下文信息
|
||||
* @throws NullPointerException 如果数据权限上下文类型异常,则抛出NullPointerException
|
||||
*/
|
||||
public static Map<String, Object> getContext() {
|
||||
SaStorage saStorage = SaHolder.getStorage();
|
||||
Object attribute = saStorage.get(DATA_PERMISSION_KEY);
|
||||
|
@ -64,6 +82,7 @@ public class DataPermissionHelper {
|
|||
|
||||
/**
|
||||
* 在忽略数据权限中执行
|
||||
* <p>禁止在忽略数据权限中执行忽略数据权限</p>
|
||||
*
|
||||
* @param handle 处理执行方法
|
||||
*/
|
||||
|
@ -78,6 +97,7 @@ public class DataPermissionHelper {
|
|||
|
||||
/**
|
||||
* 在忽略数据权限中执行
|
||||
* <p>禁止在忽略数据权限中执行忽略数据权限</p>
|
||||
*
|
||||
* @param handle 处理执行方法
|
||||
*/
|
||||
|
|
|
@ -37,17 +37,33 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto
|
|||
|
||||
private final PlusDataPermissionHandler dataPermissionHandler;
|
||||
|
||||
/**
|
||||
* 构造函数,初始化 PlusDataPermissionHandler 实例
|
||||
*
|
||||
* @param mapperPackage 扫描的映射器包
|
||||
*/
|
||||
public PlusDataPermissionInterceptor(String mapperPackage) {
|
||||
this.dataPermissionHandler = new PlusDataPermissionHandler(mapperPackage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在执行查询之前,检查并处理数据权限相关逻辑
|
||||
*
|
||||
* @param executor MyBatis 执行器对象
|
||||
* @param ms 映射语句对象
|
||||
* @param parameter 方法参数
|
||||
* @param rowBounds 分页对象
|
||||
* @param resultHandler 结果处理器
|
||||
* @param boundSql 绑定的 SQL 对象
|
||||
* @throws SQLException 如果发生 SQL 异常
|
||||
*/
|
||||
@Override
|
||||
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
|
||||
// 检查忽略注解
|
||||
// 检查是否需要忽略数据权限处理
|
||||
if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
|
||||
return;
|
||||
}
|
||||
// 检查是否无效 无数据权限注解
|
||||
// 检查是否缺少有效的数据权限注解
|
||||
if (dataPermissionHandler.invalid(ms.getId())) {
|
||||
return;
|
||||
}
|
||||
|
@ -56,16 +72,26 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto
|
|||
mpBs.sql(parserSingle(mpBs.sql(), ms.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 在准备 SQL 语句之前,检查并处理更新和删除操作的数据权限相关逻辑
|
||||
*
|
||||
* @param sh MyBatis StatementHandler 对象
|
||||
* @param connection 数据库连接对象
|
||||
* @param transactionTimeout 事务超时时间
|
||||
*/
|
||||
@Override
|
||||
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
|
||||
PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
|
||||
MappedStatement ms = mpSh.mappedStatement();
|
||||
// 获取 SQL 命令类型(增、删、改、查)
|
||||
SqlCommandType sct = ms.getSqlCommandType();
|
||||
|
||||
// 只处理更新和删除操作的 SQL 语句
|
||||
if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
|
||||
if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
|
||||
return;
|
||||
}
|
||||
// 检查是否无效 无数据权限注解
|
||||
// 检查是否缺少有效的数据权限注解
|
||||
if (dataPermissionHandler.invalid(ms.getId())) {
|
||||
return;
|
||||
}
|
||||
|
@ -74,6 +100,14 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 SELECT 查询语句中的 WHERE 条件
|
||||
*
|
||||
* @param select SELECT 查询对象
|
||||
* @param index 查询语句的索引
|
||||
* @param sql 查询语句
|
||||
* @param obj WHERE 条件参数
|
||||
*/
|
||||
@Override
|
||||
protected void processSelect(Select select, int index, String sql, Object obj) {
|
||||
if (select instanceof PlainSelect) {
|
||||
|
@ -84,6 +118,14 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 UPDATE 语句中的 WHERE 条件
|
||||
*
|
||||
* @param update UPDATE 查询对象
|
||||
* @param index 查询语句的索引
|
||||
* @param sql 查询语句
|
||||
* @param obj WHERE 条件参数
|
||||
*/
|
||||
@Override
|
||||
protected void processUpdate(Update update, int index, String sql, Object obj) {
|
||||
Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), (String) obj, false);
|
||||
|
@ -92,6 +134,14 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 DELETE 语句中的 WHERE 条件
|
||||
*
|
||||
* @param delete DELETE 查询对象
|
||||
* @param index 查询语句的索引
|
||||
* @param sql 查询语句
|
||||
* @param obj WHERE 条件参数
|
||||
*/
|
||||
@Override
|
||||
protected void processDelete(Delete delete, int index, String sql, Object obj) {
|
||||
Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), (String) obj, false);
|
||||
|
@ -101,10 +151,10 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto
|
|||
}
|
||||
|
||||
/**
|
||||
* 设置 where 条件
|
||||
* 设置 SELECT 语句的 WHERE 条件
|
||||
*
|
||||
* @param plainSelect 查询对象
|
||||
* @param mappedStatementId 执行方法id
|
||||
* @param plainSelect SELECT 查询对象
|
||||
* @param mappedStatementId 映射语句的 ID
|
||||
*/
|
||||
protected void setWhere(PlainSelect plainSelect, String mappedStatementId) {
|
||||
Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), mappedStatementId, true);
|
||||
|
@ -113,6 +163,14 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建表达式,用于处理表的数据权限
|
||||
*
|
||||
* @param table 表对象
|
||||
* @param where WHERE 条件表达式
|
||||
* @param whereSegment WHERE 条件片段
|
||||
* @return 构建的表达式
|
||||
*/
|
||||
@Override
|
||||
public Expression buildTableExpression(Table table, Expression where, String whereSegment) {
|
||||
// 只有新版数据权限处理器才会执行到这里
|
||||
|
|
|
@ -17,4 +17,4 @@ databaseDialectTimestampFormat=yyyy-MM-dd HH:mm:ss
|
|||
# 是否过滤 Log
|
||||
filter=true
|
||||
# 过滤 Log 时所排除的 sql 关键字,以逗号分隔
|
||||
exclude=SELECT 1
|
||||
exclude=
|
||||
|
|
|
@ -162,13 +162,14 @@ public class OssClient {
|
|||
/**
|
||||
* 上传文件到 Amazon S3,并返回上传结果
|
||||
*
|
||||
* @param filePath 本地文件路径
|
||||
* @param key 在 Amazon S3 中的对象键
|
||||
* @param md5Digest 本地文件的 MD5 哈希值(可选)
|
||||
* @param filePath 本地文件路径
|
||||
* @param key 在 Amazon S3 中的对象键
|
||||
* @param md5Digest 本地文件的 MD5 哈希值(可选)
|
||||
* @param contentType 文件内容类型
|
||||
* @return UploadResult 包含上传后的文件信息
|
||||
* @throws OssException 如果上传失败,抛出自定义异常
|
||||
*/
|
||||
public UploadResult upload(Path filePath, String key, String md5Digest) {
|
||||
public UploadResult upload(Path filePath, String key, String md5Digest, String contentType) {
|
||||
try {
|
||||
// 构建上传请求对象
|
||||
FileUpload fileUpload = transferManager.uploadFile(
|
||||
|
@ -176,6 +177,8 @@ public class OssClient {
|
|||
y -> y.bucket(properties.getBucketName())
|
||||
.key(key)
|
||||
.contentMD5(StringUtils.isNotEmpty(md5Digest) ? md5Digest : null)
|
||||
.contentType(contentType)
|
||||
.acl(getAccessPolicy().getObjectCannedACL())
|
||||
.build())
|
||||
.addTransferListener(LoggingTransferListener.create())
|
||||
.source(filePath).build());
|
||||
|
@ -201,10 +204,11 @@ public class OssClient {
|
|||
* @param inputStream 要上传的输入流
|
||||
* @param key 在 Amazon S3 中的对象键
|
||||
* @param length 输入流的长度
|
||||
* @param contentType 文件内容类型
|
||||
* @return UploadResult 包含上传后的文件信息
|
||||
* @throws OssException 如果上传失败,抛出自定义异常
|
||||
*/
|
||||
public UploadResult upload(InputStream inputStream, String key, Long length) {
|
||||
public UploadResult upload(InputStream inputStream, String key, Long length, String contentType) {
|
||||
// 如果输入流不是 ByteArrayInputStream,则将其读取为字节数组再创建 ByteArrayInputStream
|
||||
if (!(inputStream instanceof ByteArrayInputStream)) {
|
||||
inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream));
|
||||
|
@ -219,6 +223,8 @@ public class OssClient {
|
|||
.putObjectRequest(
|
||||
y -> y.bucket(properties.getBucketName())
|
||||
.key(key)
|
||||
.contentType(contentType)
|
||||
.acl(getAccessPolicy().getObjectCannedACL())
|
||||
.build())
|
||||
.build());
|
||||
|
||||
|
@ -311,12 +317,12 @@ public class OssClient {
|
|||
* 获取私有URL链接
|
||||
*
|
||||
* @param objectKey 对象KEY
|
||||
* @param second 授权时间
|
||||
* @param day 授权时间
|
||||
*/
|
||||
public String getPrivateUrl(String objectKey, Integer second) {
|
||||
public String getPrivateUrl(String objectKey, Integer day) {
|
||||
// 使用 AWS S3 预签名 URL 的生成器 获取对象的预签名 URL
|
||||
URL url = presigner.presignGetObject(
|
||||
x -> x.signatureDuration(Duration.ofSeconds(second))
|
||||
x -> x.signatureDuration(Duration.ofDays(day))
|
||||
.getObjectRequest(
|
||||
y -> y.bucket(properties.getBucketName())
|
||||
.key(objectKey)
|
||||
|
@ -335,7 +341,7 @@ public class OssClient {
|
|||
* @throws OssException 如果上传失败,抛出自定义异常
|
||||
*/
|
||||
public UploadResult uploadSuffix(byte[] data, String suffix) {
|
||||
return upload(new ByteArrayInputStream(data), getPath(properties.getPrefix(), suffix), Long.valueOf(data.length));
|
||||
return upload(new ByteArrayInputStream(data), getPath(properties.getPrefix(), suffix), Long.valueOf(data.length), FileUtils.getMimeType(suffix));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -348,7 +354,7 @@ public class OssClient {
|
|||
* @throws OssException 如果上传失败,抛出自定义异常
|
||||
*/
|
||||
public UploadResult uploadSuffix(InputStream inputStream, String suffix, Long length) {
|
||||
return upload(inputStream, getPath(properties.getPrefix(), suffix), length);
|
||||
return upload(inputStream, getPath(properties.getPrefix(), suffix), length, FileUtils.getMimeType(suffix));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -360,7 +366,7 @@ public class OssClient {
|
|||
* @throws OssException 如果上传失败,抛出自定义异常
|
||||
*/
|
||||
public UploadResult uploadSuffix(File file, String suffix) {
|
||||
return upload(file.toPath(), getPath(properties.getPrefix(), suffix), null);
|
||||
return upload(file.toPath(), getPath(properties.getPrefix(), suffix), null, FileUtils.getMimeType(suffix));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -48,7 +48,10 @@ public class OssFactory {
|
|||
}
|
||||
OssProperties properties = JsonUtils.parseObject(json, OssProperties.class);
|
||||
// 使用租户标识避免多个租户相同key实例覆盖
|
||||
String key = properties.getTenantId() + ":" + configKey;
|
||||
String key = configKey;
|
||||
if (StringUtils.isNotBlank(properties.getTenantId())) {
|
||||
key = properties.getTenantId() + ":" + configKey;
|
||||
}
|
||||
OssClient client = CLIENT_CACHE.get(key);
|
||||
// 客户端不存在或配置不相同则重新构建
|
||||
if (client == null || !client.checkPropertiesSame(properties)) {
|
||||
|
|
|
@ -23,7 +23,7 @@ public class RedisExceptionHandler {
|
|||
@ExceptionHandler(LockFailureException.class)
|
||||
public R<Void> handleLockFailureException(LockFailureException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("获取锁失败了'{}',发生Lock4j异常." + requestURI, e.getMessage());
|
||||
log.error("获取锁失败了'{}',发生Lock4j异常.", requestURI, e);
|
||||
return R.fail(HttpStatus.HTTP_UNAVAILABLE, "业务处理中,请稍后再试...");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package org.dromara.common.redis.utils;
|
||||
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.redisson.api.*;
|
||||
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 分布式队列工具
|
||||
|
@ -224,7 +225,7 @@ public class QueueUtils {
|
|||
/**
|
||||
* 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等)
|
||||
*/
|
||||
public static <T> void subscribeBlockingQueue(String queueName, Consumer<T> consumer, boolean isDelayed) {
|
||||
public static <T> void subscribeBlockingQueue(String queueName, Function<T, CompletionStage<Void>> consumer, boolean isDelayed) {
|
||||
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
|
||||
if (isDelayed) {
|
||||
// 订阅延迟队列
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.dromara.common.satoken.core.dao;
|
|||
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
|
@ -54,7 +53,7 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||
} else {
|
||||
RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
|
||||
}
|
||||
CAFFEINE.put(key, value);
|
||||
CAFFEINE.invalidate(key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,7 +63,7 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||
public void update(String key, String value) {
|
||||
if (RedisUtils.hasKey(key)) {
|
||||
RedisUtils.setCacheObject(key, value, true);
|
||||
CAFFEINE.put(key, value);
|
||||
CAFFEINE.invalidate(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,7 +116,7 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||
} else {
|
||||
RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));
|
||||
}
|
||||
CAFFEINE.put(key, object);
|
||||
CAFFEINE.invalidate(key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,7 +126,7 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||
public void updateObject(String key, Object object) {
|
||||
if (RedisUtils.hasKey(key)) {
|
||||
RedisUtils.setCacheObject(key, object, true);
|
||||
CAFFEINE.put(key, object);
|
||||
CAFFEINE.invalidate(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.dromara.common.satoken.utils;
|
|||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.AccessLevel;
|
||||
|
@ -87,6 +88,13 @@ public class LoginHelper {
|
|||
return Convert.toLong(getExtra(USER_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户账户
|
||||
*/
|
||||
public static String getUsername() {
|
||||
return Convert.toStr(getExtra(USER_NAME_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户ID
|
||||
*/
|
||||
|
@ -129,13 +137,6 @@ public class LoginHelper {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户账户
|
||||
*/
|
||||
public static String getUsername() {
|
||||
return getLoginUser().getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户类型
|
||||
*/
|
||||
|
@ -170,6 +171,9 @@ public class LoginHelper {
|
|||
* @return 结果
|
||||
*/
|
||||
public static boolean isTenantAdmin(Set<String> rolePermission) {
|
||||
if (CollUtil.isEmpty(rolePermission)) {
|
||||
return false;
|
||||
}
|
||||
return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY);
|
||||
}
|
||||
|
||||
|
@ -188,7 +192,11 @@ public class LoginHelper {
|
|||
* @return 结果
|
||||
*/
|
||||
public static boolean isLogin() {
|
||||
return getLoginUser() != null;
|
||||
try {
|
||||
return getLoginUser() != null;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.dromara.common.sms.config;
|
||||
|
||||
import org.dromara.common.sms.core.dao.PlusSmsDao;
|
||||
import org.dromara.common.sms.handler.SmsExceptionHandler;
|
||||
import org.dromara.sms4j.api.dao.SmsDao;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
|
@ -21,4 +22,12 @@ public class SmsAutoConfiguration {
|
|||
return new PlusSmsDao();
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常处理器
|
||||
*/
|
||||
@Bean
|
||||
public SmsExceptionHandler smsExceptionHandler() {
|
||||
return new SmsExceptionHandler();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package org.dromara.common.sms.handler;
|
||||
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* SMS异常处理器
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class SmsExceptionHandler {
|
||||
|
||||
/**
|
||||
* sms异常
|
||||
*/
|
||||
@ExceptionHandler(SmsBlendException.class)
|
||||
public R<Void> handleSmsBlendException(SmsBlendException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',发生sms短信异常.", requestURI, e);
|
||||
return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, "短信发送失败,请稍后再试...");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-common-social</artifactId>
|
||||
|
||||
<description>
|
||||
ruoyi-common-social 授权认证
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-json</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-redis</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,23 @@
|
|||
package org.dromara.common.social.config;
|
||||
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import org.dromara.common.social.config.properties.SocialProperties;
|
||||
import org.dromara.common.social.utils.AuthRedisStateCache;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* Social 配置属性
|
||||
* @author thiszhc
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(SocialProperties.class)
|
||||
public class SocialAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public AuthStateCache authStateCache() {
|
||||
return new AuthRedisStateCache();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package org.dromara.common.social.config.properties;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 社交登录配置
|
||||
*
|
||||
* @author thiszhc
|
||||
*/
|
||||
@Data
|
||||
public class SocialLoginConfigProperties {
|
||||
|
||||
/**
|
||||
* 应用 ID
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* 应用密钥
|
||||
*/
|
||||
private String clientSecret;
|
||||
|
||||
/**
|
||||
* 回调地址
|
||||
*/
|
||||
private String redirectUri;
|
||||
|
||||
/**
|
||||
* 是否获取unionId
|
||||
*/
|
||||
private boolean unionId;
|
||||
|
||||
/**
|
||||
* Coding 企业名称
|
||||
*/
|
||||
private String codingGroupName;
|
||||
|
||||
/**
|
||||
* 支付宝公钥
|
||||
*/
|
||||
private String alipayPublicKey;
|
||||
|
||||
/**
|
||||
* 企业微信应用ID
|
||||
*/
|
||||
private String agentId;
|
||||
|
||||
/**
|
||||
* stackoverflow api key
|
||||
*/
|
||||
private String stackOverflowKey;
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 客户端系统类型
|
||||
*/
|
||||
private String clientOsType;
|
||||
|
||||
/**
|
||||
* maxkey 服务器地址
|
||||
*/
|
||||
private String serverUrl;
|
||||
|
||||
/**
|
||||
* 请求范围
|
||||
*/
|
||||
private List<String> scopes;
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package org.dromara.common.social.config.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Social 配置属性
|
||||
*
|
||||
* @author thiszhc
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "justauth")
|
||||
public class SocialProperties {
|
||||
|
||||
/**
|
||||
* 授权类型
|
||||
*/
|
||||
private Map<String, SocialLoginConfigProperties> type;
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package org.dromara.common.social.maxkey;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.request.AuthDefaultRequest;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
|
||||
/**
|
||||
* @author 长春叭哥 2023年03月26日
|
||||
*/
|
||||
public class AuthMaxKeyRequest extends AuthDefaultRequest {
|
||||
|
||||
public static final String SERVER_URL = SpringUtils.getProperty("justauth.type.maxkey.server-url");
|
||||
|
||||
/**
|
||||
* 设定归属域
|
||||
*/
|
||||
public AuthMaxKeyRequest(AuthConfig config) {
|
||||
super(config, AuthMaxKeySource.MAXKEY);
|
||||
}
|
||||
|
||||
public AuthMaxKeyRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthMaxKeySource.MAXKEY, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
String body = doPostAuthorizationCode(authCallback.getCode());
|
||||
Dict object = JsonUtils.parseMap(body);
|
||||
// oauth/token 验证异常
|
||||
if (object.containsKey("error")) {
|
||||
throw new AuthException(object.getStr("error_description"));
|
||||
}
|
||||
// user 验证异常
|
||||
if (object.containsKey("message")) {
|
||||
throw new AuthException(object.getStr("message"));
|
||||
}
|
||||
return AuthToken.builder()
|
||||
.accessToken(object.getStr("access_token"))
|
||||
.refreshToken(object.getStr("refresh_token"))
|
||||
.idToken(object.getStr("id_token"))
|
||||
.tokenType(object.getStr("token_type"))
|
||||
.scope(object.getStr("scope"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
String body = doGetUserInfo(authToken);
|
||||
Dict object = JsonUtils.parseMap(body);
|
||||
// oauth/token 验证异常
|
||||
if (object.containsKey("error")) {
|
||||
throw new AuthException(object.getStr("error_description"));
|
||||
}
|
||||
// user 验证异常
|
||||
if (object.containsKey("message")) {
|
||||
throw new AuthException(object.getStr("message"));
|
||||
}
|
||||
return AuthUser.builder()
|
||||
.uuid(object.getStr("userId"))
|
||||
.username(object.getStr("username"))
|
||||
.nickname(object.getStr("displayName"))
|
||||
.avatar(object.getStr("avatar_url"))
|
||||
.blog(object.getStr("web_url"))
|
||||
.company(object.getStr("organization"))
|
||||
.location(object.getStr("location"))
|
||||
.email(object.getStr("email"))
|
||||
.remark(object.getStr("bio"))
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package org.dromara.common.social.maxkey;
|
||||
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.request.AuthDefaultRequest;
|
||||
|
||||
/**
|
||||
* Oauth2 默认接口说明
|
||||
*
|
||||
* @author 长春叭哥 2023年03月26日
|
||||
*
|
||||
*/
|
||||
public enum AuthMaxKeySource implements AuthSource {
|
||||
|
||||
/**
|
||||
* 自己搭建的 maxkey 私服
|
||||
*/
|
||||
MAXKEY {
|
||||
|
||||
/**
|
||||
* 授权的api
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
return AuthMaxKeyRequest.SERVER_URL + "/sign/authz/oauth/v20/authorize";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取accessToken的api
|
||||
*/
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return AuthMaxKeyRequest.SERVER_URL + "/sign/authz/oauth/v20/token";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息的api
|
||||
*/
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return AuthMaxKeyRequest.SERVER_URL + "/sign/api/oauth/v20/me";
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台对应的 AuthRequest 实现类,必须继承自 {@link AuthDefaultRequest}
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthMaxKeyRequest.class;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package org.dromara.common.social.topiam;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.xkcoding.http.support.HttpHeader;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.request.AuthDefaultRequest;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
|
||||
import static org.dromara.common.social.topiam.AuthTopiamSource.TOPIAM;
|
||||
|
||||
/**
|
||||
* TopIAM 认证请求
|
||||
*
|
||||
* @author xlsea
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Slf4j
|
||||
public class AuthTopIamRequest extends AuthDefaultRequest {
|
||||
|
||||
public static final String SERVER_URL = SpringUtils.getProperty("justauth.type.topiam.server-url");
|
||||
|
||||
/**
|
||||
* 设定归属域
|
||||
*/
|
||||
public AuthTopIamRequest(AuthConfig config) {
|
||||
super(config, TOPIAM);
|
||||
}
|
||||
|
||||
public AuthTopIamRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, TOPIAM, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
String body = doPostAuthorizationCode(authCallback.getCode());
|
||||
Dict object = JsonUtils.parseMap(body);
|
||||
checkResponse(object);
|
||||
return AuthToken.builder()
|
||||
.accessToken(object.getStr("access_token"))
|
||||
.refreshToken(object.getStr("refresh_token"))
|
||||
.idToken(object.getStr("id_token"))
|
||||
.tokenType(object.getStr("token_type"))
|
||||
.scope(object.getStr("scope"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
String body = doGetUserInfo(authToken);
|
||||
Dict object = JsonUtils.parseMap(body);
|
||||
checkResponse(object);
|
||||
return AuthUser.builder()
|
||||
.uuid(object.getStr("sub"))
|
||||
.username(object.getStr("preferred_username"))
|
||||
.nickname(object.getStr("nickname"))
|
||||
.avatar(object.getStr("picture"))
|
||||
.email(object.getStr("email"))
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String doGetUserInfo(AuthToken authToken) {
|
||||
return new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, new HttpHeader()
|
||||
.add("Content-Type", "application/json")
|
||||
.add("Authorization", "Bearer " + authToken.getAccessToken()), false).getBody();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(super.authorize(state))
|
||||
.queryParam("scope", StrUtil.join("%20", config.getScopes()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static void checkResponse(Dict object) {
|
||||
// oauth/token 验证异常
|
||||
if (object.containsKey("error")) {
|
||||
throw new AuthException(object.getStr("error_description"));
|
||||
}
|
||||
// user 验证异常
|
||||
if (object.containsKey("message")) {
|
||||
throw new AuthException(object.getStr("message"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package org.dromara.common.social.topiam;
|
||||
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.request.AuthDefaultRequest;
|
||||
|
||||
/**
|
||||
* Oauth2 默认接口说明
|
||||
*
|
||||
* @author xlsea
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
public enum AuthTopiamSource implements AuthSource {
|
||||
|
||||
/**
|
||||
* 测试
|
||||
*/
|
||||
TOPIAM {
|
||||
/**
|
||||
* 授权的api
|
||||
*/
|
||||
@Override
|
||||
public String authorize() {
|
||||
return AuthTopIamRequest.SERVER_URL + "/oauth2/auth";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取accessToken的api
|
||||
*/
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return AuthTopIamRequest.SERVER_URL + "/oauth2/token";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息的api
|
||||
*/
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return AuthTopIamRequest.SERVER_URL + "/oauth2/userinfo";
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台对应的 AuthRequest 实现类,必须继承自 {@link AuthDefaultRequest}
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends AuthDefaultRequest> getTargetClass() {
|
||||
return AuthTopIamRequest.class;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package org.dromara.common.social.utils;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import org.dromara.common.core.constant.GlobalConstants;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 授权状态缓存
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class AuthRedisStateCache implements AuthStateCache {
|
||||
|
||||
/**
|
||||
* 存入缓存
|
||||
*
|
||||
* @param key 缓存key
|
||||
* @param value 缓存内容
|
||||
*/
|
||||
@Override
|
||||
public void cache(String key, String value) {
|
||||
// 授权超时时间 默认三分钟
|
||||
RedisUtils.setCacheObject(GlobalConstants.SOCIAL_AUTH_CODE_KEY + key, value, Duration.ofMinutes(3));
|
||||
}
|
||||
|
||||
/**
|
||||
* 存入缓存
|
||||
*
|
||||
* @param key 缓存key
|
||||
* @param value 缓存内容
|
||||
* @param timeout 指定缓存过期时间(毫秒)
|
||||
*/
|
||||
@Override
|
||||
public void cache(String key, String value, long timeout) {
|
||||
RedisUtils.setCacheObject(GlobalConstants.SOCIAL_AUTH_CODE_KEY + key, value, Duration.ofMillis(timeout));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存内容
|
||||
*
|
||||
* @param key 缓存key
|
||||
* @return 缓存内容
|
||||
*/
|
||||
@Override
|
||||
public String get(String key) {
|
||||
return RedisUtils.getCacheObject(GlobalConstants.SOCIAL_AUTH_CODE_KEY + key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在key,如果对应key的value值已过期,也返回false
|
||||
*
|
||||
* @param key 缓存key
|
||||
* @return true:存在key,并且value没过期;false:key不存在或者已过期
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(String key) {
|
||||
return RedisUtils.hasKey(GlobalConstants.SOCIAL_AUTH_CODE_KEY + key);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package org.dromara.common.social.utils;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.request.*;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
|
||||
import org.dromara.common.social.config.properties.SocialProperties;
|
||||
import org.dromara.common.social.maxkey.AuthMaxKeyRequest;
|
||||
import org.dromara.common.social.topiam.AuthTopIamRequest;
|
||||
|
||||
/**
|
||||
* 认证授权工具类
|
||||
*
|
||||
* @author thiszhc
|
||||
*/
|
||||
public class SocialUtils {
|
||||
|
||||
private static final AuthRedisStateCache STATE_CACHE = SpringUtils.getBean(AuthRedisStateCache.class);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static AuthResponse<AuthUser> loginAuth(String source, String code, String state, SocialProperties socialProperties) throws AuthException {
|
||||
AuthRequest authRequest = getAuthRequest(source, socialProperties);
|
||||
AuthCallback callback = new AuthCallback();
|
||||
callback.setCode(code);
|
||||
callback.setState(state);
|
||||
return authRequest.login(callback);
|
||||
}
|
||||
|
||||
public static AuthRequest getAuthRequest(String source, SocialProperties socialProperties) throws AuthException {
|
||||
SocialLoginConfigProperties obj = socialProperties.getType().get(source);
|
||||
if (ObjectUtil.isNull(obj)) {
|
||||
throw new AuthException("不支持的第三方登录类型");
|
||||
}
|
||||
AuthConfig.AuthConfigBuilder builder = AuthConfig.builder()
|
||||
.clientId(obj.getClientId())
|
||||
.clientSecret(obj.getClientSecret())
|
||||
.redirectUri(obj.getRedirectUri())
|
||||
.scopes(obj.getScopes());
|
||||
return switch (source.toLowerCase()) {
|
||||
case "dingtalk" -> new AuthDingTalkRequest(builder.build(), STATE_CACHE);
|
||||
case "baidu" -> new AuthBaiduRequest(builder.build(), STATE_CACHE);
|
||||
case "github" -> new AuthGithubRequest(builder.build(), STATE_CACHE);
|
||||
case "gitee" -> new AuthGiteeRequest(builder.build(), STATE_CACHE);
|
||||
case "weibo" -> new AuthWeiboRequest(builder.build(), STATE_CACHE);
|
||||
case "coding" -> new AuthCodingRequest(builder.build(), STATE_CACHE);
|
||||
case "oschina" -> new AuthOschinaRequest(builder.build(), STATE_CACHE);
|
||||
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1,所以这儿的回调地址使用的局域网内的ip
|
||||
case "alipay_wallet" -> new AuthAlipayRequest(builder.build(), socialProperties.getType().get("alipay_wallet").getAlipayPublicKey(), STATE_CACHE);
|
||||
case "qq" -> new AuthQqRequest(builder.build(), STATE_CACHE);
|
||||
case "wechat_open" -> new AuthWeChatOpenRequest(builder.build(), STATE_CACHE);
|
||||
case "taobao" -> new AuthTaobaoRequest(builder.build(), STATE_CACHE);
|
||||
case "douyin" -> new AuthDouyinRequest(builder.build(), STATE_CACHE);
|
||||
case "linkedin" -> new AuthLinkedinRequest(builder.build(), STATE_CACHE);
|
||||
case "microsoft" -> new AuthMicrosoftRequest(builder.build(), STATE_CACHE);
|
||||
case "renren" -> new AuthRenrenRequest(builder.build(), STATE_CACHE);
|
||||
case "stack_overflow" -> new AuthStackOverflowRequest(builder.stackOverflowKey("").build(), STATE_CACHE);
|
||||
case "huawei" -> new AuthHuaweiRequest(builder.build(), STATE_CACHE);
|
||||
case "wechat_enterprise" -> new AuthWeChatEnterpriseQrcodeRequest(builder.agentId("").build(), STATE_CACHE);
|
||||
case "gitlab" -> new AuthGitlabRequest(builder.build(), STATE_CACHE);
|
||||
case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE);
|
||||
case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE);
|
||||
case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE);
|
||||
case "topiam" -> new AuthTopIamRequest(builder.build(), STATE_CACHE);
|
||||
default -> throw new AuthException("未获取到有效的Auth配置");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
org.dromara.common.social.config.SocialAutoConfiguration
|
|
@ -35,7 +35,7 @@ public class TenantConfig {
|
|||
|
||||
@ConditionalOnBean(MybatisPlusConfig.class)
|
||||
@AutoConfiguration(after = {MybatisPlusConfig.class})
|
||||
static class MybatisPlusConfigation {
|
||||
static class MybatisPlusConfiguration {
|
||||
|
||||
/**
|
||||
* 多租户插件
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.dromara.common.tenant.helper;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
|
@ -79,22 +78,28 @@ public class TenantHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static void setDynamic(String tenantId) {
|
||||
setDynamic(tenantId, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置动态租户(一直有效 需要手动清理)
|
||||
* <p>
|
||||
* 如果为未登录状态下 那么只在当前线程内生效
|
||||
*
|
||||
* @param tenantId 租户id
|
||||
* @param global 是否全局生效
|
||||
*/
|
||||
public static void setDynamic(String tenantId) {
|
||||
public static void setDynamic(String tenantId, boolean global) {
|
||||
if (!isEnable()) {
|
||||
return;
|
||||
}
|
||||
if (!isLogin()) {
|
||||
if (!isLogin() || !global) {
|
||||
TEMP_DYNAMIC_TENANT.set(tenantId);
|
||||
return;
|
||||
}
|
||||
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
|
||||
RedisUtils.setCacheObject(cacheKey, tenantId);
|
||||
SaHolder.getStorage().set(cacheKey, tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,13 +114,13 @@ public class TenantHelper {
|
|||
if (!isLogin()) {
|
||||
return TEMP_DYNAMIC_TENANT.get();
|
||||
}
|
||||
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
|
||||
String tenantId = (String) SaHolder.getStorage().get(cacheKey);
|
||||
// 如果线程内有值 优先返回
|
||||
String tenantId = TEMP_DYNAMIC_TENANT.get();
|
||||
if (StringUtils.isNotBlank(tenantId)) {
|
||||
return tenantId;
|
||||
}
|
||||
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
|
||||
tenantId = RedisUtils.getCacheObject(cacheKey);
|
||||
SaHolder.getStorage().set(cacheKey, tenantId);
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
|
@ -130,9 +135,9 @@ public class TenantHelper {
|
|||
TEMP_DYNAMIC_TENANT.remove();
|
||||
return;
|
||||
}
|
||||
TEMP_DYNAMIC_TENANT.remove();
|
||||
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
|
||||
RedisUtils.deleteObject(cacheKey);
|
||||
SaHolder.getStorage().delete(cacheKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,15 +16,11 @@ import org.springframework.core.task.VirtualThreadTaskExecutor;
|
|||
@AutoConfiguration
|
||||
public class UndertowConfig implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
|
||||
|
||||
/**
|
||||
* 设置 Undertow 的 websocket 缓冲池
|
||||
*/
|
||||
@Override
|
||||
public void customize(UndertowServletWebServerFactory factory) {
|
||||
// 默认不直接分配内存 如果项目中使用了 websocket 建议直接分配
|
||||
factory.addDeploymentInfoCustomizers(deploymentInfo -> {
|
||||
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
|
||||
webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 512));
|
||||
webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(true, 1024));
|
||||
deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
|
||||
// 使用虚拟线程
|
||||
if (SpringUtils.isVirtual()) {
|
||||
|
|
|
@ -27,17 +27,20 @@ public class WebSocketConfig {
|
|||
|
||||
@Bean
|
||||
public WebSocketConfigurer webSocketConfigurer(HandshakeInterceptor handshakeInterceptor,
|
||||
WebSocketHandler webSocketHandler,
|
||||
WebSocketProperties webSocketProperties) {
|
||||
WebSocketHandler webSocketHandler, WebSocketProperties webSocketProperties) {
|
||||
// 如果WebSocket的路径为空,则设置默认路径为 "/websocket"
|
||||
if (StrUtil.isBlank(webSocketProperties.getPath())) {
|
||||
webSocketProperties.setPath("/websocket");
|
||||
}
|
||||
|
||||
// 如果允许跨域访问的地址为空,则设置为 "*",表示允许所有来源的跨域请求
|
||||
if (StrUtil.isBlank(webSocketProperties.getAllowedOrigins())) {
|
||||
webSocketProperties.setAllowedOrigins("*");
|
||||
}
|
||||
|
||||
// 返回一个WebSocketConfigurer对象,用于配置WebSocket
|
||||
return registry -> registry
|
||||
// 添加WebSocket处理程序和拦截器到指定路径,设置允许的跨域来源
|
||||
.addHandler(webSocketHandler, webSocketProperties.getPath())
|
||||
.addInterceptors(handshakeInterceptor)
|
||||
.setAllowedOrigins(webSocketProperties.getAllowedOrigins());
|
||||
|
|
|
@ -6,6 +6,7 @@ package org.dromara.common.websocket.constant;
|
|||
* @author zendwang
|
||||
*/
|
||||
public interface WebSocketConstants {
|
||||
|
||||
/**
|
||||
* websocketSession中的参数的key
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.dromara.common.websocket.handler;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.websocket.dto.WebSocketMessageDto;
|
||||
|
@ -8,6 +9,7 @@ import org.dromara.common.websocket.utils.WebSocketUtils;
|
|||
import org.springframework.web.socket.*;
|
||||
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.dromara.common.websocket.constant.WebSocketConstants.LOGIN_USER_KEY;
|
||||
|
@ -24,40 +26,54 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler {
|
|||
* 连接成功后
|
||||
*/
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) {
|
||||
public void afterConnectionEstablished(WebSocketSession session) throws IOException {
|
||||
LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
session.close(CloseStatus.BAD_DATA);
|
||||
log.info("[connect] invalid token received. sessionId: {}", session.getId());
|
||||
return;
|
||||
}
|
||||
WebSocketSessionHolder.addSession(loginUser.getUserId(), session);
|
||||
log.info("[connect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理发送来的文本消息
|
||||
* 处理接收到的文本消息
|
||||
*
|
||||
* @param session
|
||||
* @param message
|
||||
* @throws Exception
|
||||
* @param session WebSocket会话
|
||||
* @param message 接收到的文本消息
|
||||
* @throws Exception 处理消息过程中可能抛出的异常
|
||||
*/
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
|
||||
// 从WebSocket会话中获取登录用户信息
|
||||
LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
|
||||
List<Long> userIds = List.of(loginUser.getUserId());
|
||||
|
||||
// 创建WebSocket消息DTO对象
|
||||
WebSocketMessageDto webSocketMessageDto = new WebSocketMessageDto();
|
||||
webSocketMessageDto.setSessionKeys(userIds);
|
||||
webSocketMessageDto.setSessionKeys(List.of(loginUser.getUserId()));
|
||||
webSocketMessageDto.setMessage(message.getPayload());
|
||||
WebSocketUtils.publishMessage(webSocketMessageDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理接收到的二进制消息
|
||||
*
|
||||
* @param session WebSocket会话
|
||||
* @param message 接收到的二进制消息
|
||||
* @throws Exception 处理消息过程中可能抛出的异常
|
||||
*/
|
||||
@Override
|
||||
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
|
||||
super.handleBinaryMessage(session, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 心跳监测的回复
|
||||
* 处理接收到的Pong消息(心跳监测)
|
||||
*
|
||||
* @param session
|
||||
* @param message
|
||||
* @throws Exception
|
||||
* @param session WebSocket会话
|
||||
* @param message 接收到的Pong消息
|
||||
* @throws Exception 处理消息过程中可能抛出的异常
|
||||
*/
|
||||
@Override
|
||||
protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
|
||||
|
@ -65,11 +81,11 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* 连接出错时
|
||||
* 处理WebSocket传输错误
|
||||
*
|
||||
* @param session
|
||||
* @param exception
|
||||
* @throws Exception
|
||||
* @param session WebSocket会话
|
||||
* @param exception 发生的异常
|
||||
* @throws Exception 处理过程中可能抛出的异常
|
||||
*/
|
||||
@Override
|
||||
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
|
||||
|
@ -77,22 +93,26 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* 连接关闭后
|
||||
* 在WebSocket连接关闭后执行清理操作
|
||||
*
|
||||
* @param session
|
||||
* @param status
|
||||
* @param session WebSocket会话
|
||||
* @param status 关闭状态信息
|
||||
*/
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
|
||||
LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
log.info("[disconnect] invalid token received. sessionId: {}", session.getId());
|
||||
return;
|
||||
}
|
||||
WebSocketSessionHolder.removeSession(loginUser.getUserId());
|
||||
log.info("[disconnect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否支持分片消息
|
||||
* 指示处理程序是否支持接收部分消息
|
||||
*
|
||||
* @return
|
||||
* @return 如果支持接收部分消息,则返回true;否则返回false
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsPartialMessages() {
|
||||
|
|
|
@ -18,24 +18,52 @@ public class WebSocketSessionHolder {
|
|||
|
||||
private static final Map<Long, WebSocketSession> USER_SESSION_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 将WebSocket会话添加到用户会话Map中
|
||||
*
|
||||
* @param sessionKey 会话键,用于检索会话
|
||||
* @param session 要添加的WebSocket会话
|
||||
*/
|
||||
public static void addSession(Long sessionKey, WebSocketSession session) {
|
||||
USER_SESSION_MAP.put(sessionKey, session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从用户会话Map中移除指定会话键对应的WebSocket会话
|
||||
*
|
||||
* @param sessionKey 要移除的会话键
|
||||
*/
|
||||
public static void removeSession(Long sessionKey) {
|
||||
if (USER_SESSION_MAP.containsKey(sessionKey)) {
|
||||
USER_SESSION_MAP.remove(sessionKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会话键从用户会话Map中获取WebSocket会话
|
||||
*
|
||||
* @param sessionKey 要获取的会话键
|
||||
* @return 与给定会话键对应的WebSocket会话,如果不存在则返回null
|
||||
*/
|
||||
public static WebSocketSession getSessions(Long sessionKey) {
|
||||
return USER_SESSION_MAP.get(sessionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储在用户会话Map中所有WebSocket会话的会话键集合
|
||||
*
|
||||
* @return 所有WebSocket会话的会话键集合
|
||||
*/
|
||||
public static Set<Long> getSessionsAll() {
|
||||
return USER_SESSION_MAP.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查给定的会话键是否存在于用户会话Map中
|
||||
*
|
||||
* @param sessionKey 要检查的会话键
|
||||
* @return 如果存在对应的会话键,则返回true;否则返回false
|
||||
*/
|
||||
public static Boolean existSession(Long sessionKey) {
|
||||
return USER_SESSION_MAP.containsKey(sessionKey);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
package org.dromara.common.websocket.interceptor;
|
||||
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.web.socket.WebSocketHandler;
|
||||
|
@ -21,31 +25,51 @@ import static org.dromara.common.websocket.constant.WebSocketConstants.LOGIN_USE
|
|||
public class PlusWebSocketInterceptor implements HandshakeInterceptor {
|
||||
|
||||
/**
|
||||
* 握手前
|
||||
* WebSocket握手之前执行的前置处理方法
|
||||
*
|
||||
* @param request request
|
||||
* @param response response
|
||||
* @param wsHandler wsHandler
|
||||
* @param attributes attributes
|
||||
* @return 是否握手成功
|
||||
* @param request WebSocket握手请求
|
||||
* @param response WebSocket握手响应
|
||||
* @param wsHandler WebSocket处理程序
|
||||
* @param attributes 与WebSocket会话关联的属性
|
||||
* @return 如果允许握手继续进行,则返回true;否则返回false
|
||||
*/
|
||||
@Override
|
||||
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
attributes.put(LOGIN_USER_KEY, loginUser);
|
||||
return true;
|
||||
try {
|
||||
// 检查是否登录 是否有token
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
|
||||
// 解决 ws 不走 mvc 拦截器问题(cloud 版本不受影响)
|
||||
// 检查 header 与 param 里的 clientid 与 token 里的是否一致
|
||||
String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY);
|
||||
String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY);
|
||||
String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString();
|
||||
if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) {
|
||||
// token 无效
|
||||
throw NotLoginException.newInstance(StpUtil.getLoginType(),
|
||||
"-100", "客户端ID与Token不匹配",
|
||||
StpUtil.getTokenValue());
|
||||
}
|
||||
|
||||
attributes.put(LOGIN_USER_KEY, loginUser);
|
||||
return true;
|
||||
} catch (NotLoginException e) {
|
||||
log.error("WebSocket 认证失败'{}',无法访问系统资源", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 握手后
|
||||
* WebSocket握手成功后执行的后置处理方法
|
||||
*
|
||||
* @param request request
|
||||
* @param response response
|
||||
* @param wsHandler wsHandler
|
||||
* @param exception 异常
|
||||
* @param request WebSocket握手请求
|
||||
* @param response WebSocket握手响应
|
||||
* @param wsHandler WebSocket处理程序
|
||||
* @param exception 握手过程中可能出现的异常
|
||||
*/
|
||||
@Override
|
||||
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
|
||||
|
||||
// 在这个方法中可以执行一些握手成功后的后续处理逻辑,比如记录日志或者其他操作
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package org.dromara.common.websocket.listener;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.websocket.holder.WebSocketSessionHolder;
|
||||
import org.dromara.common.websocket.utils.WebSocketUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.Ordered;
|
||||
|
@ -16,8 +16,15 @@ import org.springframework.core.Ordered;
|
|||
@Slf4j
|
||||
public class WebSocketTopicListener implements ApplicationRunner, Ordered {
|
||||
|
||||
/**
|
||||
* 在Spring Boot应用程序启动时初始化WebSocket主题订阅监听器
|
||||
*
|
||||
* @param args 应用程序参数
|
||||
* @throws Exception 初始化过程中可能抛出的异常
|
||||
*/
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
// 订阅WebSocket消息
|
||||
WebSocketUtils.subscribeMessage((message) -> {
|
||||
log.info("WebSocket主题订阅收到消息session keys={} message={}", message.getSessionKeys(), message.getMessage());
|
||||
// 如果key不为空就按照key发消息 如果为空就群发
|
||||
|
|
|
@ -29,10 +29,10 @@ import static org.dromara.common.websocket.constant.WebSocketConstants.WEB_SOCKE
|
|||
public class WebSocketUtils {
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* 向指定的WebSocket会话发送消息
|
||||
*
|
||||
* @param sessionKey session主键 一般为用户id
|
||||
* @param message 消息文本
|
||||
* @param sessionKey 要发送消息的用户id
|
||||
* @param message 要发送的消息内容
|
||||
*/
|
||||
public static void sendMessage(Long sessionKey, String message) {
|
||||
WebSocketSession session = WebSocketSessionHolder.getSessions(sessionKey);
|
||||
|
@ -40,18 +40,18 @@ public class WebSocketUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* 订阅消息
|
||||
* 订阅WebSocket消息主题,并提供一个消费者函数来处理接收到的消息
|
||||
*
|
||||
* @param consumer 自定义处理
|
||||
* @param consumer 处理WebSocket消息的消费者函数
|
||||
*/
|
||||
public static void subscribeMessage(Consumer<WebSocketMessageDto> consumer) {
|
||||
RedisUtils.subscribe(WEB_SOCKET_TOPIC, WebSocketMessageDto.class, consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布订阅的消息
|
||||
* 发布WebSocket订阅消息
|
||||
*
|
||||
* @param webSocketMessage 消息对象
|
||||
* @param webSocketMessage 要发布的WebSocket消息对象
|
||||
*/
|
||||
public static void publishMessage(WebSocketMessageDto webSocketMessage) {
|
||||
List<Long> unsentSessionKeys = new ArrayList<>();
|
||||
|
@ -76,9 +76,9 @@ public class WebSocketUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* 发布订阅的消息(群发)
|
||||
* 向所有的WebSocket会话发布订阅的消息(群发)
|
||||
*
|
||||
* @param message 消息内容
|
||||
* @param message 要发布的消息内容
|
||||
*/
|
||||
public static void publishAll(String message) {
|
||||
WebSocketMessageDto broadcastMessage = new WebSocketMessageDto();
|
||||
|
@ -88,14 +88,31 @@ public class WebSocketUtils {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定的WebSocket会话发送Pong消息
|
||||
*
|
||||
* @param session 要发送Pong消息的WebSocket会话
|
||||
*/
|
||||
public static void sendPongMessage(WebSocketSession session) {
|
||||
sendMessage(session, new PongMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定的WebSocket会话发送文本消息
|
||||
*
|
||||
* @param session WebSocket会话
|
||||
* @param message 要发送的文本消息内容
|
||||
*/
|
||||
public static void sendMessage(WebSocketSession session, String message) {
|
||||
sendMessage(session, new TextMessage(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定的WebSocket会话发送WebSocket消息对象
|
||||
*
|
||||
* @param session WebSocket会话
|
||||
* @param message 要发送的WebSocket消息对象
|
||||
*/
|
||||
private static void sendMessage(WebSocketSession session, WebSocketMessage<?> message) {
|
||||
if (session == null || !session.isOpen()) {
|
||||
log.warn("[send] session会话已经关闭");
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
|
||||
FROM bellsoft/liberica-openjdk-debian:17.0.11-cds
|
||||
#FROM bellsoft/liberica-openjdk-debian:21.0.3-cds
|
||||
#FROM findepi/graalvm:java17-native
|
||||
FROM openjdk:17.0.2-oraclelinux8
|
||||
|
||||
MAINTAINER Lion Li
|
||||
LABEL maintainer="Lion Li"
|
||||
|
||||
RUN mkdir -p /ruoyi/monitor/logs
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
|
||||
FROM bellsoft/liberica-openjdk-debian:17.0.11-cds
|
||||
#FROM bellsoft/liberica-openjdk-debian:21.0.3-cds
|
||||
#FROM findepi/graalvm:java17-native
|
||||
FROM openjdk:17.0.2-oraclelinux8
|
||||
|
||||
MAINTAINER Lion Li
|
||||
LABEL maintainer="Lion Li"
|
||||
|
||||
RUN mkdir -p /ruoyi/snailjob/logs
|
||||
|
||||
|
@ -10,7 +12,7 @@ WORKDIR /ruoyi/snailjob
|
|||
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="-Xms512m -Xmx1024m"
|
||||
|
||||
EXPOSE 8800
|
||||
EXPOSE 1788
|
||||
EXPOSE 17888
|
||||
|
||||
ADD ./target/ruoyi-snailjob-server.jar ./app.jar
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ snail-job:
|
|||
# 拉取重试数据的每批次的大小
|
||||
job-pull-page-size: 1000
|
||||
# 服务端netty端口
|
||||
netty-port: 1788
|
||||
netty-port: 17888
|
||||
# 一个客户端每秒最多接收的重试数量指令
|
||||
limiter: 1000
|
||||
# 号段模式下步长配置
|
||||
|
|
|
@ -21,7 +21,7 @@ snail-job:
|
|||
# 拉取重试数据的每批次的大小
|
||||
job-pull-page-size: 1000
|
||||
# 服务端 netty 端口
|
||||
netty-port: 1788
|
||||
netty-port: 17888
|
||||
# 一个客户端每秒最多接收的重试数量指令
|
||||
limiter: 1000
|
||||
# 号段模式下步长配置
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
<module>ruoyi-generator</module>
|
||||
<module>ruoyi-job</module>
|
||||
<module>ruoyi-system</module>
|
||||
<module>ruoyi-file</module>
|
||||
</modules>
|
||||
|
||||
<artifactId>ruoyi-modules</artifactId>
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
|
@ -1,65 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-modules</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-file</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!-- 通用工具-->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-mybatis</artifactId>
|
||||
</dependency>
|
||||
<!-- OSS功能模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-oss</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-log</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-idempotent</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-sensitive</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-encrypt</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -1,104 +0,0 @@
|
|||
package org.dromara.file.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.file.domain.bo.SysCatalogResourceBo;
|
||||
import org.dromara.file.domain.vo.SysCatalogResourceVo;
|
||||
import org.dromara.file.service.ISysCatalogResourceService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 目录-专题资源
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/catalog/resource")
|
||||
public class SysCatalogResourceController extends BaseController {
|
||||
|
||||
private final ISysCatalogResourceService sysCatalogResourceService;
|
||||
|
||||
/**
|
||||
* 查询目录-专题资源列表
|
||||
*/
|
||||
@SaCheckPermission("resource:catalogResource:list")
|
||||
@GetMapping("/list")
|
||||
public R<List<SysCatalogResourceVo>> list(SysCatalogResourceBo bo) {
|
||||
List<SysCatalogResourceVo> list = sysCatalogResourceService.queryList(bo);
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出目录-专题资源列表
|
||||
*/
|
||||
// @SaCheckPermission("resource:catalogResource:export")
|
||||
// @Log(title = "目录-专题资源", businessType = BusinessType.EXPORT)
|
||||
// @PostMapping("/export")
|
||||
// public void export(SysCatalogResourceBo bo, HttpServletResponse response) {
|
||||
// List<SysCatalogResourceVo> list = sysCatalogResourceService.queryList(bo);
|
||||
// ExcelUtil.exportExcel(list, "目录-专题资源", SysCatalogResourceVo.class, response);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取目录-专题资源详细信息
|
||||
*
|
||||
* @param catalogId 主键
|
||||
*/
|
||||
@SaCheckPermission("resource:catalogResource:query")
|
||||
@GetMapping("/{catalogId}")
|
||||
public R<SysCatalogResourceVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long catalogId) {
|
||||
return R.ok(sysCatalogResourceService.queryById(catalogId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增目录-专题资源
|
||||
*/
|
||||
@SaCheckPermission("resource:catalogResource:add")
|
||||
@Log(title = "目录-专题资源", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody SysCatalogResourceBo bo) {
|
||||
return toAjax(sysCatalogResourceService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改目录-专题资源
|
||||
*/
|
||||
@SaCheckPermission("resource:catalogResource:edit")
|
||||
@Log(title = "目录-专题资源", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysCatalogResourceBo bo) {
|
||||
return toAjax(sysCatalogResourceService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除目录-专题资源
|
||||
*
|
||||
* @param catalogIds 主键串
|
||||
*/
|
||||
@SaCheckPermission("resource:catalogResource:remove")
|
||||
@Log(title = "目录-专题资源", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{catalogIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] catalogIds) {
|
||||
return toAjax(sysCatalogResourceService.deleteWithValidByIds(List.of(catalogIds), true));
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
package org.dromara.file.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.file.domain.bo.SysCatalogTextbookBo;
|
||||
import org.dromara.file.domain.vo.SysCatalogTextbookVo;
|
||||
import org.dromara.file.service.ISysCatalogTextbookService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 目录-同步教材
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/catalog/textbook")
|
||||
public class SysCatalogTextbookController extends BaseController {
|
||||
|
||||
private final ISysCatalogTextbookService sysCatalogTextbookService;
|
||||
|
||||
/**
|
||||
* 查询目录-同步教材列表
|
||||
*/
|
||||
@SaCheckPermission("resource:catalogTextbook:list")
|
||||
@GetMapping("/list")
|
||||
public R<List<SysCatalogTextbookVo>> list(SysCatalogTextbookBo bo) {
|
||||
List<SysCatalogTextbookVo> list = sysCatalogTextbookService.queryList(bo);
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出目录-同步教材列表
|
||||
*/
|
||||
// @SaCheckPermission("resource:catalogTextbook:export")
|
||||
// @Log(title = "目录-同步教材", businessType = BusinessType.EXPORT)
|
||||
// @PostMapping("/export")
|
||||
// public void export(SysCatalogTextbookBo bo, HttpServletResponse response) {
|
||||
// List<SysCatalogTextbookVo> list = sysCatalogTextbookService.queryList(bo);
|
||||
// ExcelUtil.exportExcel(list, "目录-同步教材", SysCatalogTextbookVo.class, response);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取目录-同步教材详细信息
|
||||
*
|
||||
* @param catalogId 主键
|
||||
*/
|
||||
@SaCheckPermission("resource:catalogTextbook:query")
|
||||
@GetMapping("/{catalogId}")
|
||||
public R<SysCatalogTextbookVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long catalogId) {
|
||||
return R.ok(sysCatalogTextbookService.queryById(catalogId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增目录-同步教材
|
||||
*/
|
||||
@SaCheckPermission("resource:catalogTextbook:add")
|
||||
@Log(title = "目录-同步教材", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody SysCatalogTextbookBo bo) {
|
||||
return toAjax(sysCatalogTextbookService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改目录-同步教材
|
||||
*/
|
||||
@SaCheckPermission("resource:catalogTextbook:edit")
|
||||
@Log(title = "目录-同步教材", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysCatalogTextbookBo bo) {
|
||||
return toAjax(sysCatalogTextbookService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除目录-同步教材
|
||||
*
|
||||
* @param catalogIds 主键串
|
||||
*/
|
||||
@SaCheckPermission("resource:catalogTextbook:remove")
|
||||
@Log(title = "目录-同步教材", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{catalogIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] catalogIds) {
|
||||
return toAjax(sysCatalogTextbookService.deleteWithValidByIds(List.of(catalogIds), true));
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
package org.dromara.file.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.file.domain.bo.SysOssResourceBo;
|
||||
import org.dromara.file.domain.vo.SysOssResourceVo;
|
||||
import org.dromara.file.service.ISysOssResourceService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 【请填写功能名称】
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/file/resource")
|
||||
public class SysOssResourceController extends BaseController {
|
||||
|
||||
private final ISysOssResourceService sysOssResourceService;
|
||||
|
||||
/**
|
||||
* 查询【请填写功能名称】列表
|
||||
*/
|
||||
@SaCheckPermission("system:ossResource:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<SysOssResourceVo> list(SysOssResourceBo bo, PageQuery pageQuery) {
|
||||
return sysOssResourceService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出【请填写功能名称】列表
|
||||
*/
|
||||
// @SaCheckPermission("system:ossResource:export")
|
||||
// @Log(title = "【请填写功能名称】", businessType = BusinessType.EXPORT)
|
||||
// @PostMapping("/export")
|
||||
// public void export(SysOssResourceBo bo, HttpServletResponse response) {
|
||||
// List<SysOssResourceVo> list = sysOssResourceService.queryList(bo);
|
||||
// ExcelUtil.exportExcel(list, "【请填写功能名称】", SysOssResourceVo.class, response);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取【请填写功能名称】详细信息
|
||||
*
|
||||
* @param ossId 主键
|
||||
*/
|
||||
@SaCheckPermission("system:ossResource:query")
|
||||
@GetMapping("/{ossId}")
|
||||
public R<SysOssResourceVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long ossId) {
|
||||
return R.ok(sysOssResourceService.queryById(ossId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增【请填写功能名称】
|
||||
*/
|
||||
@SaCheckPermission("system:ossResource:add")
|
||||
@Log(title = "【请填写功能名称】", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody SysOssResourceBo bo) {
|
||||
return toAjax(sysOssResourceService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改【请填写功能名称】
|
||||
*/
|
||||
@SaCheckPermission("system:ossResource:edit")
|
||||
@Log(title = "【请填写功能名称】", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysOssResourceBo bo) {
|
||||
return toAjax(sysOssResourceService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除【请填写功能名称】
|
||||
*
|
||||
* @param ossIds 主键串
|
||||
*/
|
||||
@SaCheckPermission("system:ossResource:remove")
|
||||
@Log(title = "【请填写功能名称】", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ossIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ossIds) {
|
||||
return toAjax(sysOssResourceService.deleteWithValidByIds(List.of(ossIds), true));
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
package org.dromara.file.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.file.domain.bo.SysOssTextbookBo;
|
||||
import org.dromara.file.domain.vo.SysOssTextbookVo;
|
||||
import org.dromara.file.service.ISysOssTextbookService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 【请填写功能名称】
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/file/textbook")
|
||||
public class SysOssTextbookController extends BaseController {
|
||||
|
||||
private final ISysOssTextbookService sysOssTextbookService;
|
||||
|
||||
/**
|
||||
* 查询【请填写功能名称】列表
|
||||
*/
|
||||
@SaCheckPermission("system:ossTextbook:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<SysOssTextbookVo> list(SysOssTextbookBo bo, PageQuery pageQuery) {
|
||||
return sysOssTextbookService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 导出【请填写功能名称】列表
|
||||
// */
|
||||
// @SaCheckPermission("system:ossTextbook:export")
|
||||
// @Log(title = "【请填写功能名称】", businessType = BusinessType.EXPORT)
|
||||
// @PostMapping("/export")
|
||||
// public void export(SysOssTextbookBo bo, HttpServletResponse response) {
|
||||
// List<SysOssTextbookVo> list = sysOssTextbookService.queryList(bo);
|
||||
// ExcelUtil.exportExcel(list, "【请填写功能名称】", SysOssTextbookVo.class, response);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取【请填写功能名称】详细信息
|
||||
*
|
||||
* @param ossId 主键
|
||||
*/
|
||||
@SaCheckPermission("system:ossTextbook:query")
|
||||
@GetMapping("/{ossId}")
|
||||
public R<SysOssTextbookVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long ossId) {
|
||||
return R.ok(sysOssTextbookService.queryById(ossId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增【请填写功能名称】
|
||||
*/
|
||||
@SaCheckPermission("system:ossTextbook:add")
|
||||
@Log(title = "【请填写功能名称】", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody SysOssTextbookBo bo) {
|
||||
return toAjax(sysOssTextbookService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改【请填写功能名称】
|
||||
*/
|
||||
@SaCheckPermission("system:ossTextbook:edit")
|
||||
@Log(title = "【请填写功能名称】", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysOssTextbookBo bo) {
|
||||
return toAjax(sysOssTextbookService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除【请填写功能名称】
|
||||
*
|
||||
* @param ossIds 主键串
|
||||
*/
|
||||
@SaCheckPermission("system:ossTextbook:remove")
|
||||
@Log(title = "【请填写功能名称】", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ossIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ossIds) {
|
||||
return toAjax(sysOssTextbookService.deleteWithValidByIds(List.of(ossIds), true));
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package org.dromara.file.domain;
|
||||
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 【请填写功能名称】对象 sys_oss_resource
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("sys_oss_resource")
|
||||
public class SysOssResource extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@TableId(value = "oss_id")
|
||||
private Long ossId;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@TableId(value = "catalog_id")
|
||||
private Long catalogId;
|
||||
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package org.dromara.file.domain;
|
||||
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 【请填写功能名称】对象 sys_oss_textbook
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("sys_oss_textbook")
|
||||
public class SysOssTextbook extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@TableId(value = "oss_id")
|
||||
private Long ossId;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@TableId(value = "catalog_id")
|
||||
private Long catalogId;
|
||||
|
||||
/**
|
||||
* 1课件,2课堂,3作业,4试卷
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package org.dromara.file.domain.bo;
|
||||
|
||||
import org.dromara.file.domain.SysOssTextbook;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
/**
|
||||
* 【请填写功能名称】业务对象 sys_oss_textbook
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = SysOssTextbook.class, reverseConvertGenerate = false)
|
||||
public class SysOssTextbookBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@NotNull(message = "不能为空", groups = { EditGroup.class })
|
||||
private Long ossId;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@NotNull(message = "不能为空", groups = { EditGroup.class })
|
||||
private Long catalogId;
|
||||
|
||||
/**
|
||||
* 1课件,2课堂,3作业,4试卷
|
||||
*/
|
||||
@NotNull(message = "1课件,2课堂,3作业,4试卷不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Integer type;
|
||||
|
||||
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package org.dromara.file.domain.vo;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.dromara.file.domain.SysOssResource;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 【请填写功能名称】视图对象 sys_oss_resource
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
@Data
|
||||
|
||||
@AutoMapper(target = SysOssResource.class)
|
||||
public class SysOssResourceVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private Long ossId;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private Long catalogId;
|
||||
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package org.dromara.file.domain.vo;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.dromara.file.domain.SysOssTextbook;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 【请填写功能名称】视图对象 sys_oss_textbook
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
@Data
|
||||
|
||||
@AutoMapper(target = SysOssTextbook.class)
|
||||
public class SysOssTextbookVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private Long ossId;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private Long catalogId;
|
||||
|
||||
/**
|
||||
* 1课件,2课堂,3作业,4试卷
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package org.dromara.file.mapper;
|
||||
|
||||
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.file.domain.SysCatalogResource;
|
||||
import org.dromara.file.domain.vo.SysCatalogResourceVo;
|
||||
|
||||
/**
|
||||
* 目录-专题资源Mapper接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
public interface SysCatalogResourceMapper extends BaseMapperPlus<SysCatalogResource, SysCatalogResourceVo> {
|
||||
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package org.dromara.file.mapper;
|
||||
|
||||
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.file.domain.SysCatalogTextbook;
|
||||
import org.dromara.file.domain.vo.SysCatalogTextbookVo;
|
||||
|
||||
/**
|
||||
* 目录-同步教材Mapper接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
public interface SysCatalogTextbookMapper extends BaseMapperPlus<SysCatalogTextbook, SysCatalogTextbookVo> {
|
||||
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package org.dromara.file.mapper;
|
||||
|
||||
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.file.domain.SysOssResource;
|
||||
import org.dromara.file.domain.vo.SysOssResourceVo;
|
||||
|
||||
/**
|
||||
* 【请填写功能名称】Mapper接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
public interface SysOssResourceMapper extends BaseMapperPlus<SysOssResource, SysOssResourceVo> {
|
||||
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package org.dromara.file.mapper;
|
||||
|
||||
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.file.domain.SysOssTextbook;
|
||||
import org.dromara.file.domain.vo.SysOssTextbookVo;
|
||||
|
||||
/**
|
||||
* 【请填写功能名称】Mapper接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
public interface SysOssTextbookMapper extends BaseMapperPlus<SysOssTextbook, SysOssTextbookVo> {
|
||||
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package org.dromara.file.service;
|
||||
|
||||
|
||||
import org.dromara.file.domain.bo.SysCatalogResourceBo;
|
||||
import org.dromara.file.domain.vo.SysCatalogResourceVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 目录-专题资源Service接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
public interface ISysCatalogResourceService {
|
||||
|
||||
/**
|
||||
* 查询目录-专题资源
|
||||
*
|
||||
* @param catalogId 主键
|
||||
* @return 目录-专题资源
|
||||
*/
|
||||
SysCatalogResourceVo queryById(Long catalogId);
|
||||
|
||||
|
||||
/**
|
||||
* 查询符合条件的目录-专题资源列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 目录-专题资源列表
|
||||
*/
|
||||
List<SysCatalogResourceVo> queryList(SysCatalogResourceBo bo);
|
||||
|
||||
/**
|
||||
* 新增目录-专题资源
|
||||
*
|
||||
* @param bo 目录-专题资源
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
Boolean insertByBo(SysCatalogResourceBo bo);
|
||||
|
||||
/**
|
||||
* 修改目录-专题资源
|
||||
*
|
||||
* @param bo 目录-专题资源
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean updateByBo(SysCatalogResourceBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除目录-专题资源信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package org.dromara.file.service;
|
||||
|
||||
|
||||
import org.dromara.file.domain.bo.SysCatalogTextbookBo;
|
||||
import org.dromara.file.domain.vo.SysCatalogTextbookVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 目录-同步教材Service接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
public interface ISysCatalogTextbookService {
|
||||
|
||||
/**
|
||||
* 查询目录-同步教材
|
||||
*
|
||||
* @param catalogId 主键
|
||||
* @return 目录-同步教材
|
||||
*/
|
||||
SysCatalogTextbookVo queryById(Long catalogId);
|
||||
|
||||
|
||||
/**
|
||||
* 查询符合条件的目录-同步教材列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 目录-同步教材列表
|
||||
*/
|
||||
List<SysCatalogTextbookVo> queryList(SysCatalogTextbookBo bo);
|
||||
|
||||
/**
|
||||
* 新增目录-同步教材
|
||||
*
|
||||
* @param bo 目录-同步教材
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
Boolean insertByBo(SysCatalogTextbookBo bo);
|
||||
|
||||
/**
|
||||
* 修改目录-同步教材
|
||||
*
|
||||
* @param bo 目录-同步教材
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean updateByBo(SysCatalogTextbookBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除目录-同步教材信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
package org.dromara.file.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.file.domain.SysCatalogResource;
|
||||
import org.dromara.file.domain.bo.SysCatalogResourceBo;
|
||||
import org.dromara.file.domain.vo.SysCatalogResourceVo;
|
||||
import org.dromara.file.mapper.SysCatalogResourceMapper;
|
||||
import org.dromara.file.service.ISysCatalogResourceService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 目录-专题资源Service业务层处理
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class SysCatalogResourceServiceImpl implements ISysCatalogResourceService {
|
||||
|
||||
private final SysCatalogResourceMapper baseMapper;
|
||||
|
||||
/**
|
||||
* 查询目录-专题资源
|
||||
*
|
||||
* @param catalogId 主键
|
||||
* @return 目录-专题资源
|
||||
*/
|
||||
@Override
|
||||
public SysCatalogResourceVo queryById(Long catalogId){
|
||||
return baseMapper.selectVoById(catalogId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询符合条件的目录-专题资源列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 目录-专题资源列表
|
||||
*/
|
||||
@Override
|
||||
public List<SysCatalogResourceVo> queryList(SysCatalogResourceBo bo) {
|
||||
LambdaQueryWrapper<SysCatalogResource> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<SysCatalogResource> buildQueryWrapper(SysCatalogResourceBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<SysCatalogResource> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getParentId() != null, SysCatalogResource::getParentId, bo.getParentId());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getAncestors()), SysCatalogResource::getAncestors, bo.getAncestors());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getCatalogName()), SysCatalogResource::getCatalogName, bo.getCatalogName());
|
||||
lqw.eq(bo.getOrderNum() != null, SysCatalogResource::getOrderNum, bo.getOrderNum());
|
||||
lqw.eq(bo.getCover() != null, SysCatalogResource::getCover, bo.getCover());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增目录-专题资源
|
||||
*
|
||||
* @param bo 目录-专题资源
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean insertByBo(SysCatalogResourceBo bo) {
|
||||
SysCatalogResource add = MapstructUtils.convert(bo, SysCatalogResource.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setCatalogId(add.getCatalogId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改目录-专题资源
|
||||
*
|
||||
* @param bo 目录-专题资源
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(SysCatalogResourceBo bo) {
|
||||
SysCatalogResource update = MapstructUtils.convert(bo, SysCatalogResource.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(SysCatalogResource entity){
|
||||
//TODO 做一些数据校验,如唯一约束
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验并批量删除目录-专题资源信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if(isValid){
|
||||
//TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
package org.dromara.file.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.file.domain.SysCatalogTextbook;
|
||||
import org.dromara.file.domain.bo.SysCatalogTextbookBo;
|
||||
import org.dromara.file.domain.vo.SysCatalogTextbookVo;
|
||||
import org.dromara.file.mapper.SysCatalogTextbookMapper;
|
||||
import org.dromara.file.service.ISysCatalogTextbookService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 目录-同步教材Service业务层处理
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class SysCatalogTextbookServiceImpl implements ISysCatalogTextbookService {
|
||||
|
||||
private final SysCatalogTextbookMapper baseMapper;
|
||||
|
||||
/**
|
||||
* 查询目录-同步教材
|
||||
*
|
||||
* @param catalogId 主键
|
||||
* @return 目录-同步教材
|
||||
*/
|
||||
@Override
|
||||
public SysCatalogTextbookVo queryById(Long catalogId){
|
||||
return baseMapper.selectVoById(catalogId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询符合条件的目录-同步教材列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 目录-同步教材列表
|
||||
*/
|
||||
@Override
|
||||
public List<SysCatalogTextbookVo> queryList(SysCatalogTextbookBo bo) {
|
||||
LambdaQueryWrapper<SysCatalogTextbook> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<SysCatalogTextbook> buildQueryWrapper(SysCatalogTextbookBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<SysCatalogTextbook> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getParentId() != null, SysCatalogTextbook::getParentId, bo.getParentId());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getAncestors()), SysCatalogTextbook::getAncestors, bo.getAncestors());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getCatalogName()), SysCatalogTextbook::getCatalogName, bo.getCatalogName());
|
||||
lqw.eq(bo.getOrderNum() != null, SysCatalogTextbook::getOrderNum, bo.getOrderNum());
|
||||
lqw.eq(bo.getType() != null, SysCatalogTextbook::getType, bo.getType());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增目录-同步教材
|
||||
*
|
||||
* @param bo 目录-同步教材
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean insertByBo(SysCatalogTextbookBo bo) {
|
||||
SysCatalogTextbook add = MapstructUtils.convert(bo, SysCatalogTextbook.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setCatalogId(add.getCatalogId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改目录-同步教材
|
||||
*
|
||||
* @param bo 目录-同步教材
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(SysCatalogTextbookBo bo) {
|
||||
SysCatalogTextbook update = MapstructUtils.convert(bo, SysCatalogTextbook.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(SysCatalogTextbook entity){
|
||||
//TODO 做一些数据校验,如唯一约束
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验并批量删除目录-同步教材信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if(isValid){
|
||||
//TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
package org.dromara.file.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.file.domain.SysOssResource;
|
||||
import org.dromara.file.domain.bo.SysOssResourceBo;
|
||||
import org.dromara.file.domain.vo.SysOssResourceVo;
|
||||
import org.dromara.file.mapper.SysOssResourceMapper;
|
||||
import org.dromara.file.service.ISysOssResourceService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 【请填写功能名称】Service业务层处理
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-05-28
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class SysOssResourceServiceImpl implements ISysOssResourceService {
|
||||
|
||||
private final SysOssResourceMapper baseMapper;
|
||||
|
||||
/**
|
||||
* 查询【请填写功能名称】
|
||||
*
|
||||
* @param ossId 主键
|
||||
* @return 【请填写功能名称】
|
||||
*/
|
||||
@Override
|
||||
public SysOssResourceVo queryById(Long ossId){
|
||||
return baseMapper.selectVoById(ossId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询【请填写功能名称】列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 【请填写功能名称】分页列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<SysOssResourceVo> queryPageList(SysOssResourceBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<SysOssResource> lqw = buildQueryWrapper(bo);
|
||||
Page<SysOssResourceVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询符合条件的【请填写功能名称】列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 【请填写功能名称】列表
|
||||
*/
|
||||
@Override
|
||||
public List<SysOssResourceVo> queryList(SysOssResourceBo bo) {
|
||||
LambdaQueryWrapper<SysOssResource> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<SysOssResource> buildQueryWrapper(SysOssResourceBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<SysOssResource> lqw = Wrappers.lambdaQuery();
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增【请填写功能名称】
|
||||
*
|
||||
* @param bo 【请填写功能名称】
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean insertByBo(SysOssResourceBo bo) {
|
||||
SysOssResource add = MapstructUtils.convert(bo, SysOssResource.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setOssId(add.getOssId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改【请填写功能名称】
|
||||
*
|
||||
* @param bo 【请填写功能名称】
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(SysOssResourceBo bo) {
|
||||
SysOssResource update = MapstructUtils.convert(bo, SysOssResource.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(SysOssResource entity){
|
||||
//TODO 做一些数据校验,如唯一约束
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验并批量删除【请填写功能名称】信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if(isValid){
|
||||
//TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue