mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-09-20 18:15:56 +08:00
Merge branch 'dev' into future/flowable
This commit is contained in:
commit
5e55435fe9
@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
<configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||||
<deployment type="dockerfile">
|
<deployment type="dockerfile">
|
||||||
<settings>
|
<settings>
|
||||||
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.1.2" />
|
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.2.0" />
|
||||||
<option name="buildOnly" value="true" />
|
<option name="buildOnly" value="true" />
|
||||||
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
|
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
|
||||||
</settings>
|
</settings>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="ruoyi-powerjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
<configuration default="false" name="ruoyi-powerjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||||
<deployment type="dockerfile">
|
<deployment type="dockerfile">
|
||||||
<settings>
|
<settings>
|
||||||
<option name="imageTag" value="ruoyi/ruoyi-powerjob-server:5.1.2" />
|
<option name="imageTag" value="ruoyi/ruoyi-powerjob-server:5.2.0" />
|
||||||
<option name="buildOnly" value="true" />
|
<option name="buildOnly" value="true" />
|
||||||
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-powerjob-server/Dockerfile" />
|
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-powerjob-server/Dockerfile" />
|
||||||
</settings>
|
</settings>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||||
<deployment type="dockerfile">
|
<deployment type="dockerfile">
|
||||||
<settings>
|
<settings>
|
||||||
<option name="imageTag" value="ruoyi/ruoyi-server:5.1.2" />
|
<option name="imageTag" value="ruoyi/ruoyi-server:5.2.0" />
|
||||||
<option name="buildOnly" value="true" />
|
<option name="buildOnly" value="true" />
|
||||||
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
|
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
|
||||||
</settings>
|
</settings>
|
||||||
|
|||||||
29
README.md
29
README.md
@ -9,7 +9,7 @@
|
|||||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
|
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
|
||||||
[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
|
[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
|
||||||
<br>
|
<br>
|
||||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
||||||
[]()
|
[]()
|
||||||
[]()
|
[]()
|
||||||
[]()
|
[]()
|
||||||
@ -23,7 +23,13 @@
|
|||||||
|
|
||||||
> 前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)
|
> 前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)
|
||||||
|
|
||||||
> 文档地址: [plus-doc](https://plus-doc.dromara.org) - [plus-doc(国内备用)](https://dromara.gitee.io/plus-doc)
|
> 文档地址: [plus-doc](https://plus-doc.dromara.org)
|
||||||
|
|
||||||
|
## 赞助商
|
||||||
|
|
||||||
|
MaxKey - https://gitee.com/dromara/MaxKey <br>
|
||||||
|
CCFlow - https://gitee.com/opencc/RuoYi-JFlow <br>
|
||||||
|
[如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group)
|
||||||
|
|
||||||
# 本框架与RuoYi的功能差异
|
# 本框架与RuoYi的功能差异
|
||||||
|
|
||||||
@ -116,12 +122,11 @@
|
|||||||
>[部署项目 必看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
|
>[部署项目 必看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
|
||||||
>>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
|
>>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
|
||||||
>
|
>
|
||||||
|
>[如何加群](https://plus-doc.dromara.org/#/common/add_group)
|
||||||
|
>>[https://plus-doc.dromara.org/#/common/add_group](https://plus-doc.dromara.org/#/common/add_group)
|
||||||
|
>
|
||||||
>[参考文档 Wiki](https://plus-doc.dromara.org)
|
>[参考文档 Wiki](https://plus-doc.dromara.org)
|
||||||
>>[https://plus-doc.dromara.org](https://plus-doc.dromara.org)
|
>>[https://plus-doc.dromara.org](https://plus-doc.dromara.org)
|
||||||
>
|
|
||||||
>[参考文档(国内备份)](https://dromara.gitee.io/plus-doc)
|
|
||||||
>>[https://dromara.gitee.io/plus-doc](https://dromara.gitee.io/plus-doc)
|
|
||||||
|
|
||||||
|
|
||||||
## 软件架构图
|
## 软件架构图
|
||||||
|
|
||||||
@ -131,18 +136,6 @@
|
|||||||
|
|
||||||
[参与贡献的方式 https://plus-doc.dromara.org/#/common/contribution](https://plus-doc.dromara.org/#/common/contribution)
|
[参与贡献的方式 https://plus-doc.dromara.org/#/common/contribution](https://plus-doc.dromara.org/#/common/contribution)
|
||||||
|
|
||||||
### 其他
|
|
||||||
|
|
||||||
* 定期同步升级 RuoYi-Vue 有用的更新
|
|
||||||
* GitHub 地址 [RuoYi-Vue-Plus](https://github.com/dromara/RuoYi-Vue-Plus)
|
|
||||||
* 微服务 分支 [RuoYi-Cloud-Plus](https://gitee.com/JavaLionLi/RuoYi-Cloud-Plus)
|
|
||||||
* 前端项目 地址 [plus-ui](https://gitee.com/JavaLionLi/plus-ui)
|
|
||||||
* 用户扩展项目 [扩展项目列表](https://plus-doc.dromara.org/#/ruoyi-vue-plus/extend-project/list)
|
|
||||||
|
|
||||||
## 加群与捐献
|
|
||||||
>[加群与捐献](https://plus-doc.dromara.org/#/ruoyi-vue-plus/other/group_chat)
|
|
||||||
>>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/other/group_chat](https://plus-doc.dromara.org/#/ruoyi-vue-plus/other/group_chat)
|
|
||||||
|
|
||||||
## 捐献作者
|
## 捐献作者
|
||||||
作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭
|
作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭
|
||||||
<img src="https://foruda.gitee.com/images/1678975784848381069/d8661ed9_1766278.png" width="300px" height="450px" />
|
<img src="https://foruda.gitee.com/images/1678975784848381069/d8661ed9_1766278.png" width="300px" height="450px" />
|
||||||
|
|||||||
16
pom.xml
16
pom.xml
@ -13,13 +13,13 @@
|
|||||||
<description>RuoYi-Vue-Plus多租户管理系统</description>
|
<description>RuoYi-Vue-Plus多租户管理系统</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>5.1.2</revision>
|
<revision>5.2.0-SNAPSHOT</revision>
|
||||||
<spring-boot.version>3.1.7</spring-boot.version>
|
<spring-boot.version>3.2.1</spring-boot.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<java.version>17</java.version>
|
<java.version>17</java.version>
|
||||||
<spring-boot.mybatis>3.0.3</spring-boot.mybatis>
|
<spring-boot.mybatis>3.0.3</spring-boot.mybatis>
|
||||||
<springdoc.version>2.2.0</springdoc.version>
|
<springdoc.version>2.3.0</springdoc.version>
|
||||||
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
|
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
|
||||||
<poi.version>5.2.3</poi.version>
|
<poi.version>5.2.3</poi.version>
|
||||||
<easyexcel.version>3.3.3</easyexcel.version>
|
<easyexcel.version>3.3.3</easyexcel.version>
|
||||||
@ -27,12 +27,12 @@
|
|||||||
<satoken.version>1.37.0</satoken.version>
|
<satoken.version>1.37.0</satoken.version>
|
||||||
<mybatis-plus.version>3.5.5</mybatis-plus.version>
|
<mybatis-plus.version>3.5.5</mybatis-plus.version>
|
||||||
<p6spy.version>3.9.1</p6spy.version>
|
<p6spy.version>3.9.1</p6spy.version>
|
||||||
<hutool.version>5.8.22</hutool.version>
|
<hutool.version>5.8.24</hutool.version>
|
||||||
<okhttp.version>4.10.0</okhttp.version>
|
<okhttp.version>4.10.0</okhttp.version>
|
||||||
<spring-boot-admin.version>3.1.8</spring-boot-admin.version>
|
<spring-boot-admin.version>3.2.0</spring-boot-admin.version>
|
||||||
<redisson.version>3.24.3</redisson.version>
|
<redisson.version>3.25.2</redisson.version>
|
||||||
<lock4j.version>2.2.5</lock4j.version>
|
<lock4j.version>2.2.5</lock4j.version>
|
||||||
<dynamic-ds.version>4.2.0</dynamic-ds.version>
|
<dynamic-ds.version>4.3.0</dynamic-ds.version>
|
||||||
<alibaba-ttl.version>2.14.4</alibaba-ttl.version>
|
<alibaba-ttl.version>2.14.4</alibaba-ttl.version>
|
||||||
<powerjob.version>4.3.6</powerjob.version>
|
<powerjob.version>4.3.6</powerjob.version>
|
||||||
<mapstruct-plus.version>1.3.5</mapstruct-plus.version>
|
<mapstruct-plus.version>1.3.5</mapstruct-plus.version>
|
||||||
@ -223,7 +223,7 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||||
<version>${mybatis-plus.version}</version>
|
<version>${mybatis-plus.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.dromara.common.core.constant.CacheConstants;
|
import org.dromara.common.core.constant.CacheConstants;
|
||||||
import org.dromara.common.core.constant.Constants;
|
import org.dromara.common.core.constant.Constants;
|
||||||
import org.dromara.common.core.domain.dto.UserOnlineDTO;
|
import org.dromara.common.core.domain.dto.UserOnlineDTO;
|
||||||
import org.dromara.common.core.domain.model.LoginUser;
|
|
||||||
import org.dromara.common.core.utils.MessageUtils;
|
import org.dromara.common.core.utils.MessageUtils;
|
||||||
import org.dromara.common.core.utils.ServletUtils;
|
import org.dromara.common.core.utils.ServletUtils;
|
||||||
import org.dromara.common.core.utils.SpringUtils;
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
@ -18,6 +17,7 @@ import org.dromara.common.core.utils.ip.AddressUtils;
|
|||||||
import org.dromara.common.log.event.LogininforEvent;
|
import org.dromara.common.log.event.LogininforEvent;
|
||||||
import org.dromara.common.redis.utils.RedisUtils;
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
import org.dromara.common.satoken.utils.LoginHelper;
|
import org.dromara.common.satoken.utils.LoginHelper;
|
||||||
|
import org.dromara.common.tenant.helper.TenantHelper;
|
||||||
import org.dromara.web.service.SysLoginService;
|
import org.dromara.web.service.SysLoginService;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@ -43,7 +43,6 @@ public class UserActionListener implements SaTokenListener {
|
|||||||
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
|
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
|
||||||
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
|
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||||
String ip = ServletUtils.getClientIP();
|
String ip = ServletUtils.getClientIP();
|
||||||
LoginUser user = LoginHelper.getLoginUser();
|
|
||||||
UserOnlineDTO dto = new UserOnlineDTO();
|
UserOnlineDTO dto = new UserOnlineDTO();
|
||||||
dto.setIpaddr(ip);
|
dto.setIpaddr(ip);
|
||||||
dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
|
dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
|
||||||
@ -51,25 +50,29 @@ public class UserActionListener implements SaTokenListener {
|
|||||||
dto.setOs(userAgent.getOs().getName());
|
dto.setOs(userAgent.getOs().getName());
|
||||||
dto.setLoginTime(System.currentTimeMillis());
|
dto.setLoginTime(System.currentTimeMillis());
|
||||||
dto.setTokenId(tokenValue);
|
dto.setTokenId(tokenValue);
|
||||||
dto.setUserName(user.getUsername());
|
String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY);
|
||||||
dto.setClientKey(user.getClientKey());
|
String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY);
|
||||||
dto.setDeviceType(user.getDeviceType());
|
dto.setUserName(username);
|
||||||
dto.setDeptName(user.getDeptName());
|
dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY));
|
||||||
|
dto.setDeviceType(loginModel.getDevice());
|
||||||
|
dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY));
|
||||||
|
TenantHelper.dynamic(tenantId, () -> {
|
||||||
if(tokenConfig.getTimeout() == -1) {
|
if(tokenConfig.getTimeout() == -1) {
|
||||||
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
|
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
|
||||||
} else {
|
} else {
|
||||||
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));
|
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
// 记录登录日志
|
// 记录登录日志
|
||||||
LogininforEvent logininforEvent = new LogininforEvent();
|
LogininforEvent logininforEvent = new LogininforEvent();
|
||||||
logininforEvent.setTenantId(user.getTenantId());
|
logininforEvent.setTenantId(tenantId);
|
||||||
logininforEvent.setUsername(user.getUsername());
|
logininforEvent.setUsername(username);
|
||||||
logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
|
logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
|
||||||
logininforEvent.setMessage(MessageUtils.message("user.login.success"));
|
logininforEvent.setMessage(MessageUtils.message("user.login.success"));
|
||||||
logininforEvent.setRequest(ServletUtils.getRequest());
|
logininforEvent.setRequest(ServletUtils.getRequest());
|
||||||
SpringUtils.context().publishEvent(logininforEvent);
|
SpringUtils.context().publishEvent(logininforEvent);
|
||||||
// 更新登录信息
|
// 更新登录信息
|
||||||
loginService.recordLoginInfo(user.getUserId(), ip);
|
loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip);
|
||||||
log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
|
log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -57,7 +57,7 @@ public class SysLoginService {
|
|||||||
private final ISysPermissionService permissionService;
|
private final ISysPermissionService permissionService;
|
||||||
private final ISysSocialService sysSocialService;
|
private final ISysSocialService sysSocialService;
|
||||||
private final ISysRoleService roleService;
|
private final ISysRoleService roleService;
|
||||||
private final SysDeptMapper deptMapper;
|
private final ISysDeptService deptService;
|
||||||
private final SysUserMapper userMapper;
|
private final SysUserMapper userMapper;
|
||||||
|
|
||||||
|
|
||||||
@ -145,11 +145,9 @@ public class SysLoginService {
|
|||||||
loginUser.setUserType(user.getUserType());
|
loginUser.setUserType(user.getUserType());
|
||||||
loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId()));
|
loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId()));
|
||||||
loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId()));
|
loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId()));
|
||||||
SysDeptVo dept = deptMapper.selectVoById(user.getDeptId());
|
SysDeptVo dept = deptService.selectDeptById(user.getDeptId());
|
||||||
loginUser.setDeptName(ObjectUtil.isNull(dept) ? "" : dept.getDeptName());
|
loginUser.setDeptName(ObjectUtil.isNull(dept) ? "" : dept.getDeptName());
|
||||||
List<SysRoleVo> roles = DataPermissionHelper.ignore(() -> {
|
List<SysRoleVo> roles = roleService.selectRolesByUserId(user.getUserId());
|
||||||
return roleService.selectRolesByUserId(user.getUserId());
|
|
||||||
});
|
|
||||||
loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
|
loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
|
||||||
return loginUser;
|
return loginUser;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import cn.hutool.core.map.MapUtil;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.http.HttpUtil;
|
import cn.hutool.http.HttpUtil;
|
||||||
import cn.hutool.http.Method;
|
import cn.hutool.http.Method;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import me.zhyd.oauth.model.AuthResponse;
|
import me.zhyd.oauth.model.AuthResponse;
|
||||||
@ -23,8 +22,6 @@ import org.dromara.common.satoken.utils.LoginHelper;
|
|||||||
import org.dromara.common.social.config.properties.SocialProperties;
|
import org.dromara.common.social.config.properties.SocialProperties;
|
||||||
import org.dromara.common.social.utils.SocialUtils;
|
import org.dromara.common.social.utils.SocialUtils;
|
||||||
import org.dromara.common.tenant.helper.TenantHelper;
|
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.SysClientVo;
|
||||||
import org.dromara.system.domain.vo.SysSocialVo;
|
import org.dromara.system.domain.vo.SysSocialVo;
|
||||||
import org.dromara.system.domain.vo.SysUserVo;
|
import org.dromara.system.domain.vo.SysUserVo;
|
||||||
@ -84,11 +81,16 @@ public class SocialAuthStrategy implements IAuthStrategy {
|
|||||||
if (CollUtil.isEmpty(list)) {
|
if (CollUtil.isEmpty(list)) {
|
||||||
throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
|
throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
|
||||||
}
|
}
|
||||||
|
SysSocialVo social;
|
||||||
|
if (TenantHelper.isEnable()) {
|
||||||
Optional<SysSocialVo> opt = list.stream().filter(x -> x.getTenantId().equals(loginBody.getTenantId())).findAny();
|
Optional<SysSocialVo> opt = list.stream().filter(x -> x.getTenantId().equals(loginBody.getTenantId())).findAny();
|
||||||
if (opt.isEmpty()) {
|
if (opt.isEmpty()) {
|
||||||
throw new ServiceException("对不起,你没有权限登录当前租户!");
|
throw new ServiceException("对不起,你没有权限登录当前租户!");
|
||||||
}
|
}
|
||||||
SysSocialVo social = opt.get();
|
social = opt.get();
|
||||||
|
} else {
|
||||||
|
social = list.get(0);
|
||||||
|
}
|
||||||
// 查找用户
|
// 查找用户
|
||||||
SysUserVo user = loadUser(social.getTenantId(), social.getUserId());
|
SysUserVo user = loadUser(social.getTenantId(), social.getUserId());
|
||||||
|
|
||||||
|
|||||||
@ -207,6 +207,13 @@ justauth:
|
|||||||
client-id: 876892492581044224
|
client-id: 876892492581044224
|
||||||
client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
|
client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
|
||||||
redirect-uri: ${justauth.address}/social-callback?source=maxkey
|
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:
|
qq:
|
||||||
client-id: 10**********6
|
client-id: 10**********6
|
||||||
client-secret: 1f7d08**********5b7**********29e
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
|||||||
@ -61,6 +61,11 @@ user:
|
|||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: ${ruoyi.name}
|
name: ${ruoyi.name}
|
||||||
|
threads:
|
||||||
|
# 开启虚拟线程 仅jdk21可用
|
||||||
|
# 开启后还需更改 UndertowConfig 虚拟线程配置
|
||||||
|
virtual:
|
||||||
|
enabled: false
|
||||||
# 资源信息
|
# 资源信息
|
||||||
messages:
|
messages:
|
||||||
# 国际化资源文件路径
|
# 国际化资源文件路径
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
</description>
|
</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>5.1.2</revision>
|
<revision>5.2.0-SNAPSHOT</revision>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
|||||||
@ -1,57 +0,0 @@
|
|||||||
package org.dromara.common.core.context;
|
|
||||||
|
|
||||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 线程持有类
|
|
||||||
*
|
|
||||||
* @author Michelle.Chung
|
|
||||||
*/
|
|
||||||
public class ThreadLocalHolder {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化 (支持异步)
|
|
||||||
*/
|
|
||||||
private static final ThreadLocal<Map<String, Object>> THREAD_LOCAL = TransmittableThreadLocal.withInitial(HashMap::new);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置值
|
|
||||||
*
|
|
||||||
* @param key 键
|
|
||||||
* @param value 值
|
|
||||||
*/
|
|
||||||
public static <T> void set(String key, T value) {
|
|
||||||
THREAD_LOCAL.get().put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取值
|
|
||||||
*
|
|
||||||
* @param key 键
|
|
||||||
* @return 值
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static <T> T get(String key) {
|
|
||||||
return (T) THREAD_LOCAL.get().get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 移除值
|
|
||||||
*
|
|
||||||
* @param key 键
|
|
||||||
*/
|
|
||||||
public static void remove(String key) {
|
|
||||||
THREAD_LOCAL.get().remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清空值
|
|
||||||
*/
|
|
||||||
public static void clear() {
|
|
||||||
THREAD_LOCAL.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -4,8 +4,14 @@ import cn.dev33.satoken.SaManager;
|
|||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.crypto.SecureUtil;
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.AfterReturning;
|
||||||
|
import org.aspectj.lang.annotation.AfterThrowing;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Before;
|
||||||
import org.dromara.common.core.constant.GlobalConstants;
|
import org.dromara.common.core.constant.GlobalConstants;
|
||||||
import org.dromara.common.core.context.ThreadLocalHolder;
|
|
||||||
import org.dromara.common.core.domain.R;
|
import org.dromara.common.core.domain.R;
|
||||||
import org.dromara.common.core.exception.ServiceException;
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
import org.dromara.common.core.utils.MessageUtils;
|
import org.dromara.common.core.utils.MessageUtils;
|
||||||
@ -14,13 +20,6 @@ import org.dromara.common.core.utils.StringUtils;
|
|||||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||||
import org.dromara.common.json.utils.JsonUtils;
|
import org.dromara.common.json.utils.JsonUtils;
|
||||||
import org.dromara.common.redis.utils.RedisUtils;
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import org.aspectj.lang.JoinPoint;
|
|
||||||
import org.aspectj.lang.annotation.AfterReturning;
|
|
||||||
import org.aspectj.lang.annotation.AfterThrowing;
|
|
||||||
import org.aspectj.lang.annotation.Aspect;
|
|
||||||
import org.aspectj.lang.annotation.Before;
|
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@ -37,7 +36,7 @@ import java.util.StringJoiner;
|
|||||||
@Aspect
|
@Aspect
|
||||||
public class RepeatSubmitAspect {
|
public class RepeatSubmitAspect {
|
||||||
|
|
||||||
private static final String KEY_CACHE = "keyCache";
|
private static final ThreadLocal<String> KEY_CACHE = new ThreadLocal<>();
|
||||||
|
|
||||||
@Before("@annotation(repeatSubmit)")
|
@Before("@annotation(repeatSubmit)")
|
||||||
public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable {
|
public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable {
|
||||||
@ -60,7 +59,7 @@ public class RepeatSubmitAspect {
|
|||||||
// 唯一标识(指定key + url + 消息头)
|
// 唯一标识(指定key + url + 消息头)
|
||||||
String cacheRepeatKey = GlobalConstants.REPEAT_SUBMIT_KEY + url + submitKey;
|
String cacheRepeatKey = GlobalConstants.REPEAT_SUBMIT_KEY + url + submitKey;
|
||||||
if (RedisUtils.setObjectIfAbsent(cacheRepeatKey, "", Duration.ofMillis(interval))) {
|
if (RedisUtils.setObjectIfAbsent(cacheRepeatKey, "", Duration.ofMillis(interval))) {
|
||||||
ThreadLocalHolder.set(KEY_CACHE, cacheRepeatKey);
|
KEY_CACHE.set(cacheRepeatKey);
|
||||||
} else {
|
} else {
|
||||||
String message = repeatSubmit.message();
|
String message = repeatSubmit.message();
|
||||||
if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) {
|
if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) {
|
||||||
@ -83,10 +82,9 @@ public class RepeatSubmitAspect {
|
|||||||
if (r.getCode() == R.SUCCESS) {
|
if (r.getCode() == R.SUCCESS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String cacheKey = ThreadLocalHolder.get(KEY_CACHE);
|
RedisUtils.deleteObject(KEY_CACHE.get());
|
||||||
RedisUtils.deleteObject(cacheKey);
|
|
||||||
} finally {
|
} finally {
|
||||||
ThreadLocalHolder.remove(KEY_CACHE);
|
KEY_CACHE.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,9 +97,8 @@ public class RepeatSubmitAspect {
|
|||||||
*/
|
*/
|
||||||
@AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e")
|
@AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e")
|
||||||
public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) {
|
public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) {
|
||||||
String cacheKey = ThreadLocalHolder.get(KEY_CACHE);
|
RedisUtils.deleteObject(KEY_CACHE.get());
|
||||||
RedisUtils.deleteObject(cacheKey);
|
KEY_CACHE.remove();
|
||||||
ThreadLocalHolder.remove(KEY_CACHE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,7 +127,7 @@ public class RepeatSubmitAspect {
|
|||||||
public boolean isFilterObject(final Object o) {
|
public boolean isFilterObject(final Object o) {
|
||||||
Class<?> clazz = o.getClass();
|
Class<?> clazz = o.getClass();
|
||||||
if (clazz.isArray()) {
|
if (clazz.isArray()) {
|
||||||
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
|
return MultipartFile.class.isAssignableFrom(clazz.getComponentType());
|
||||||
} else if (Collection.class.isAssignableFrom(clazz)) {
|
} else if (Collection.class.isAssignableFrom(clazz)) {
|
||||||
Collection collection = (Collection) o;
|
Collection collection = (Collection) o;
|
||||||
for (Object value : collection) {
|
for (Object value : collection) {
|
||||||
|
|||||||
@ -4,16 +4,6 @@ import cn.hutool.core.lang.Dict;
|
|||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import org.dromara.common.core.context.ThreadLocalHolder;
|
|
||||||
import org.dromara.common.core.domain.model.LoginUser;
|
|
||||||
import org.dromara.common.core.utils.ServletUtils;
|
|
||||||
import org.dromara.common.core.utils.SpringUtils;
|
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
|
||||||
import org.dromara.common.json.utils.JsonUtils;
|
|
||||||
import org.dromara.common.log.annotation.Log;
|
|
||||||
import org.dromara.common.log.enums.BusinessStatus;
|
|
||||||
import org.dromara.common.log.event.OperLogEvent;
|
|
||||||
import org.dromara.common.satoken.utils.LoginHelper;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -23,6 +13,15 @@ import org.aspectj.lang.annotation.AfterReturning;
|
|||||||
import org.aspectj.lang.annotation.AfterThrowing;
|
import org.aspectj.lang.annotation.AfterThrowing;
|
||||||
import org.aspectj.lang.annotation.Aspect;
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
import org.aspectj.lang.annotation.Before;
|
import org.aspectj.lang.annotation.Before;
|
||||||
|
import org.dromara.common.core.domain.model.LoginUser;
|
||||||
|
import org.dromara.common.core.utils.ServletUtils;
|
||||||
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.dromara.common.json.utils.JsonUtils;
|
||||||
|
import org.dromara.common.log.annotation.Log;
|
||||||
|
import org.dromara.common.log.enums.BusinessStatus;
|
||||||
|
import org.dromara.common.log.event.OperLogEvent;
|
||||||
|
import org.dromara.common.satoken.utils.LoginHelper;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
@ -51,7 +50,7 @@ public class LogAspect {
|
|||||||
/**
|
/**
|
||||||
* 计时 key
|
* 计时 key
|
||||||
*/
|
*/
|
||||||
private static final String LOG_STOP_WATCH_KEY = "logStopwatch";
|
private static final ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理请求前执行
|
* 处理请求前执行
|
||||||
@ -59,7 +58,7 @@ public class LogAspect {
|
|||||||
@Before(value = "@annotation(controllerLog)")
|
@Before(value = "@annotation(controllerLog)")
|
||||||
public void boBefore(JoinPoint joinPoint, Log controllerLog) {
|
public void boBefore(JoinPoint joinPoint, Log controllerLog) {
|
||||||
StopWatch stopWatch = new StopWatch();
|
StopWatch stopWatch = new StopWatch();
|
||||||
ThreadLocalHolder.set(LOG_STOP_WATCH_KEY, stopWatch);
|
KEY_CACHE.set(stopWatch);
|
||||||
stopWatch.start();
|
stopWatch.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +111,7 @@ public class LogAspect {
|
|||||||
// 处理设置注解上的参数
|
// 处理设置注解上的参数
|
||||||
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
|
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
|
||||||
// 设置消耗时间
|
// 设置消耗时间
|
||||||
StopWatch stopWatch = ThreadLocalHolder.get(LOG_STOP_WATCH_KEY);
|
StopWatch stopWatch = KEY_CACHE.get();
|
||||||
stopWatch.stop();
|
stopWatch.stop();
|
||||||
operLog.setCostTime(stopWatch.getTime());
|
operLog.setCostTime(stopWatch.getTime());
|
||||||
// 发布事件保存数据库
|
// 发布事件保存数据库
|
||||||
@ -122,7 +121,7 @@ public class LogAspect {
|
|||||||
log.error("异常信息:{}", exp.getMessage());
|
log.error("异常信息:{}", exp.getMessage());
|
||||||
exp.printStackTrace();
|
exp.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
ThreadLocalHolder.remove(LOG_STOP_WATCH_KEY);
|
KEY_CACHE.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +203,7 @@ public class LogAspect {
|
|||||||
public boolean isFilterObject(final Object o) {
|
public boolean isFilterObject(final Object o) {
|
||||||
Class<?> clazz = o.getClass();
|
Class<?> clazz = o.getClass();
|
||||||
if (clazz.isArray()) {
|
if (clazz.isArray()) {
|
||||||
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
|
return MultipartFile.class.isAssignableFrom(clazz.getComponentType());
|
||||||
} else if (Collection.class.isAssignableFrom(clazz)) {
|
} else if (Collection.class.isAssignableFrom(clazz)) {
|
||||||
Collection collection = (Collection) o;
|
Collection collection = (Collection) o;
|
||||||
for (Object value : collection) {
|
for (Object value : collection) {
|
||||||
|
|||||||
@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>org.mybatis</groupId>
|
<groupId>org.mybatis</groupId>
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
|
|||||||
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
|
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.dromara.common.core.context.ThreadLocalHolder;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|||||||
@ -32,6 +32,11 @@
|
|||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
|
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@ -0,0 +1,45 @@
|
|||||||
|
package org.dromara.common.redis.config;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import org.dromara.common.redis.manager.PlusSpringCacheManager;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存配置
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@AutoConfiguration
|
||||||
|
@EnableCaching
|
||||||
|
public class CacheConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* caffeine 本地缓存处理器
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public Cache<Object, Object> caffeine() {
|
||||||
|
return Caffeine.newBuilder()
|
||||||
|
// 设置最后一次写入或访问后经过固定时间过期
|
||||||
|
.expireAfterWrite(30, TimeUnit.SECONDS)
|
||||||
|
// 初始的缓存空间大小
|
||||||
|
.initialCapacity(100)
|
||||||
|
// 缓存的最大条数
|
||||||
|
.maximumSize(1000)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义缓存管理器 整合spring-cache
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public CacheManager cacheManager() {
|
||||||
|
return new PlusSpringCacheManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -8,7 +8,6 @@ import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.common.redis.config.properties.RedissonProperties;
|
import org.dromara.common.redis.config.properties.RedissonProperties;
|
||||||
import org.dromara.common.redis.handler.KeyPrefixHandler;
|
import org.dromara.common.redis.handler.KeyPrefixHandler;
|
||||||
import org.dromara.common.redis.manager.PlusSpringCacheManager;
|
|
||||||
import org.redisson.client.codec.StringCodec;
|
import org.redisson.client.codec.StringCodec;
|
||||||
import org.redisson.codec.CompositeCodec;
|
import org.redisson.codec.CompositeCodec;
|
||||||
import org.redisson.codec.TypedJsonJacksonCodec;
|
import org.redisson.codec.TypedJsonJacksonCodec;
|
||||||
@ -16,8 +15,6 @@ import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.cache.CacheManager;
|
|
||||||
import org.springframework.cache.annotation.EnableCaching;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,7 +24,6 @@ import org.springframework.context.annotation.Bean;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@EnableCaching
|
|
||||||
@EnableConfigurationProperties(RedissonProperties.class)
|
@EnableConfigurationProperties(RedissonProperties.class)
|
||||||
public class RedisConfig {
|
public class RedisConfig {
|
||||||
|
|
||||||
@ -86,14 +82,6 @@ public class RedisConfig {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义缓存管理器 整合spring-cache
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
public CacheManager cacheManager() {
|
|
||||||
return new PlusSpringCacheManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* redis集群配置 yml
|
* redis集群配置 yml
|
||||||
*
|
*
|
||||||
|
|||||||
@ -0,0 +1,88 @@
|
|||||||
|
package org.dromara.common.redis.manager;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Console;
|
||||||
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache 装饰器模式(用于扩展 Caffeine 一级缓存)
|
||||||
|
*
|
||||||
|
* @author LionLi
|
||||||
|
*/
|
||||||
|
public class CaffeineCacheDecorator implements Cache {
|
||||||
|
|
||||||
|
private static final com.github.benmanes.caffeine.cache.Cache<Object, Object>
|
||||||
|
CAFFEINE = SpringUtils.getBean("caffeine");
|
||||||
|
|
||||||
|
private final Cache cache;
|
||||||
|
|
||||||
|
public CaffeineCacheDecorator(Cache cache) {
|
||||||
|
this.cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return cache.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getNativeCache() {
|
||||||
|
return cache.getNativeCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueWrapper get(Object key) {
|
||||||
|
Object o = CAFFEINE.get(key, k -> cache.get(key));
|
||||||
|
Console.log("redisson caffeine -> key: " + key + ",value:" + o);
|
||||||
|
return (ValueWrapper) o;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T get(Object key, Class<T> type) {
|
||||||
|
Object o = CAFFEINE.get(key, k -> cache.get(key, type));
|
||||||
|
Console.log("redisson caffeine -> key: " + key + ",value:" + o);
|
||||||
|
return (T) o;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void put(Object key, Object value) {
|
||||||
|
cache.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueWrapper putIfAbsent(Object key, Object value) {
|
||||||
|
return cache.putIfAbsent(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evict(Object key) {
|
||||||
|
evictIfPresent(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean evictIfPresent(Object key) {
|
||||||
|
boolean b = cache.evictIfPresent(key);
|
||||||
|
if (b) {
|
||||||
|
CAFFEINE.invalidate(key);
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
cache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean invalidate() {
|
||||||
|
return cache.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T> T get(Object key, Callable<T> valueLoader) {
|
||||||
|
Object o = CAFFEINE.get(key, k -> cache.get(key, valueLoader));
|
||||||
|
Console.log("redisson caffeine -> key: " + key + ",value:" + o);
|
||||||
|
return (T) o;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -156,7 +156,7 @@ public class PlusSpringCacheManager implements CacheManager {
|
|||||||
private Cache createMap(String name, CacheConfig config) {
|
private Cache createMap(String name, CacheConfig config) {
|
||||||
RMap<Object, Object> map = RedisUtils.getClient().getMap(name);
|
RMap<Object, Object> map = RedisUtils.getClient().getMap(name);
|
||||||
|
|
||||||
Cache cache = new RedissonCache(map, allowNullValues);
|
Cache cache = new CaffeineCacheDecorator(new RedissonCache(map, allowNullValues));
|
||||||
if (transactionAware) {
|
if (transactionAware) {
|
||||||
cache = new TransactionAwareCacheDecorator(cache);
|
cache = new TransactionAwareCacheDecorator(cache);
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@ public class PlusSpringCacheManager implements CacheManager {
|
|||||||
private Cache createMapCache(String name, CacheConfig config) {
|
private Cache createMapCache(String name, CacheConfig config) {
|
||||||
RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name);
|
RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name);
|
||||||
|
|
||||||
Cache cache = new RedissonCache(map, config, allowNullValues);
|
Cache cache = new CaffeineCacheDecorator(new RedissonCache(map, config, allowNullValues));
|
||||||
if (transactionAware) {
|
if (transactionAware) {
|
||||||
cache = new TransactionAwareCacheDecorator(cache);
|
cache = new TransactionAwareCacheDecorator(cache);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
org.dromara.common.redis.config.RedisConfig
|
org.dromara.common.redis.config.RedisConfig
|
||||||
|
org.dromara.common.redis.config.CacheConfig
|
||||||
|
|||||||
@ -36,6 +36,11 @@
|
|||||||
<artifactId>sa-token-jwt</artifactId>
|
<artifactId>sa-token-jwt</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@ -2,26 +2,43 @@ package org.dromara.common.satoken.core.dao;
|
|||||||
|
|
||||||
import cn.dev33.satoken.dao.SaTokenDao;
|
import cn.dev33.satoken.dao.SaTokenDao;
|
||||||
import cn.dev33.satoken.util.SaFoxUtil;
|
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;
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一)
|
* Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一)
|
||||||
|
* <p>
|
||||||
|
* 采用 caffeine + redis 多级缓存 优化并发查询效率
|
||||||
*
|
*
|
||||||
* @author Lion Li
|
* @author Lion Li
|
||||||
*/
|
*/
|
||||||
public class PlusSaTokenDao implements SaTokenDao {
|
public class PlusSaTokenDao implements SaTokenDao {
|
||||||
|
|
||||||
|
private static final Cache<String, Object> CAFFEINE = Caffeine.newBuilder()
|
||||||
|
// 设置最后一次写入或访问后经过固定时间过期
|
||||||
|
.expireAfterWrite(5, TimeUnit.SECONDS)
|
||||||
|
// 初始的缓存空间大小
|
||||||
|
.initialCapacity(100)
|
||||||
|
// 缓存的最大条数
|
||||||
|
.maximumSize(1000)
|
||||||
|
.build();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取Value,如无返空
|
* 获取Value,如无返空
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String get(String key) {
|
public String get(String key) {
|
||||||
return RedisUtils.getCacheObject(key);
|
Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key));
|
||||||
|
Console.log("caffeine -> key:" + key + ",value:" + o);
|
||||||
|
return (String) o;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,6 +55,7 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||||||
} else {
|
} else {
|
||||||
RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
|
RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
|
||||||
}
|
}
|
||||||
|
CAFFEINE.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,6 +65,7 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||||||
public void update(String key, String value) {
|
public void update(String key, String value) {
|
||||||
if (RedisUtils.hasKey(key)) {
|
if (RedisUtils.hasKey(key)) {
|
||||||
RedisUtils.setCacheObject(key, value, true);
|
RedisUtils.setCacheObject(key, value, true);
|
||||||
|
CAFFEINE.put(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +100,9 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Object getObject(String key) {
|
public Object getObject(String key) {
|
||||||
return RedisUtils.getCacheObject(key);
|
Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key));
|
||||||
|
Console.log("caffeine -> key:" + key + ",value:" + o);
|
||||||
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -98,6 +119,7 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||||||
} else {
|
} else {
|
||||||
RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));
|
RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));
|
||||||
}
|
}
|
||||||
|
CAFFEINE.put(key, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,6 +129,7 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||||||
public void updateObject(String key, Object object) {
|
public void updateObject(String key, Object object) {
|
||||||
if (RedisUtils.hasKey(key)) {
|
if (RedisUtils.hasKey(key)) {
|
||||||
RedisUtils.setCacheObject(key, object, true);
|
RedisUtils.setCacheObject(key, object, true);
|
||||||
|
CAFFEINE.put(key, object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,10 +162,14 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||||||
/**
|
/**
|
||||||
* 搜索数据
|
* 搜索数据
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
|
public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
|
||||||
Collection<String> keys = RedisUtils.keys(prefix + "*" + keyword + "*");
|
String keyStr = prefix + "*" + keyword + "*";
|
||||||
|
return (List<String>) CAFFEINE.get(keyStr, k -> {
|
||||||
|
Collection<String> keys = RedisUtils.keys(keyStr);
|
||||||
List<String> list = new ArrayList<>(keys);
|
List<String> list = new ArrayList<>(keys);
|
||||||
return SaFoxUtil.searchList(list, start, size, sortType);
|
return SaFoxUtil.searchList(list, start, size, sortType);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,12 +9,10 @@ import lombok.AccessLevel;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.dromara.common.core.constant.TenantConstants;
|
import org.dromara.common.core.constant.TenantConstants;
|
||||||
import org.dromara.common.core.constant.UserConstants;
|
import org.dromara.common.core.constant.UserConstants;
|
||||||
import org.dromara.common.core.context.ThreadLocalHolder;
|
|
||||||
import org.dromara.common.core.domain.model.LoginUser;
|
import org.dromara.common.core.domain.model.LoginUser;
|
||||||
import org.dromara.common.core.enums.UserType;
|
import org.dromara.common.core.enums.UserType;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录鉴权助手
|
* 登录鉴权助手
|
||||||
@ -34,9 +32,10 @@ public class LoginHelper {
|
|||||||
public static final String LOGIN_USER_KEY = "loginUser";
|
public static final String LOGIN_USER_KEY = "loginUser";
|
||||||
public static final String TENANT_KEY = "tenantId";
|
public static final String TENANT_KEY = "tenantId";
|
||||||
public static final String USER_KEY = "userId";
|
public static final String USER_KEY = "userId";
|
||||||
|
public static final String USER_NAME_KEY = "userName";
|
||||||
public static final String DEPT_KEY = "deptId";
|
public static final String DEPT_KEY = "deptId";
|
||||||
|
public static final String DEPT_NAME_KEY = "deptName";
|
||||||
public static final String CLIENT_KEY = "clientid";
|
public static final String CLIENT_KEY = "clientid";
|
||||||
public static final String TENANT_ADMIN_KEY = "isTenantAdmin";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录系统 基于 设备类型
|
* 登录系统 基于 设备类型
|
||||||
@ -46,29 +45,28 @@ public class LoginHelper {
|
|||||||
* @param model 配置参数
|
* @param model 配置参数
|
||||||
*/
|
*/
|
||||||
public static void login(LoginUser loginUser, SaLoginModel model) {
|
public static void login(LoginUser loginUser, SaLoginModel model) {
|
||||||
ThreadLocalHolder.set(LOGIN_USER_KEY, loginUser);
|
|
||||||
ThreadLocalHolder.set(TENANT_KEY, loginUser.getTenantId());
|
|
||||||
ThreadLocalHolder.set(USER_KEY, loginUser.getUserId());
|
|
||||||
ThreadLocalHolder.set(DEPT_KEY, loginUser.getDeptId());
|
|
||||||
model = ObjectUtil.defaultIfNull(model, new SaLoginModel());
|
model = ObjectUtil.defaultIfNull(model, new SaLoginModel());
|
||||||
StpUtil.login(loginUser.getLoginId(),
|
StpUtil.login(loginUser.getLoginId(),
|
||||||
model.setExtra(TENANT_KEY, loginUser.getTenantId())
|
model.setExtra(TENANT_KEY, loginUser.getTenantId())
|
||||||
.setExtra(USER_KEY, loginUser.getUserId())
|
.setExtra(USER_KEY, loginUser.getUserId())
|
||||||
.setExtra(DEPT_KEY, loginUser.getDeptId()));
|
.setExtra(USER_NAME_KEY, loginUser.getUsername())
|
||||||
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
|
.setExtra(DEPT_KEY, loginUser.getDeptId())
|
||||||
|
.setExtra(DEPT_NAME_KEY, loginUser.getDeptName())
|
||||||
|
);
|
||||||
|
SaSession tokenSession = StpUtil.getTokenSession();
|
||||||
|
tokenSession.updateTimeout(model.getTimeout());
|
||||||
|
tokenSession.set(LOGIN_USER_KEY, loginUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户(多级缓存)
|
* 获取用户(多级缓存)
|
||||||
*/
|
*/
|
||||||
public static LoginUser getLoginUser() {
|
public static LoginUser getLoginUser() {
|
||||||
return (LoginUser) getStorageIfAbsentSet(LOGIN_USER_KEY, () -> {
|
|
||||||
SaSession session = StpUtil.getTokenSession();
|
SaSession session = StpUtil.getTokenSession();
|
||||||
if (ObjectUtil.isNull(session)) {
|
if (ObjectUtil.isNull(session)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return session.get(LOGIN_USER_KEY);
|
return (LoginUser) session.get(LOGIN_USER_KEY);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,7 +102,12 @@ public class LoginHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Object getExtra(String key) {
|
private static Object getExtra(String key) {
|
||||||
return getStorageIfAbsentSet(key, () -> StpUtil.getExtra(key));
|
try {
|
||||||
|
return StpUtil.getExtra(key);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -147,26 +150,11 @@ public class LoginHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isTenantAdmin() {
|
public static boolean isTenantAdmin() {
|
||||||
Object value = getStorageIfAbsentSet(TENANT_ADMIN_KEY, () -> {
|
return Convert.toBool(isTenantAdmin(getLoginUser().getRolePermission()));
|
||||||
return isTenantAdmin(getLoginUser().getRolePermission());
|
|
||||||
});
|
|
||||||
return Convert.toBool(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isLogin() {
|
public static boolean isLogin() {
|
||||||
return getLoginUser() != null;
|
return getLoginUser() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object getStorageIfAbsentSet(String key, Supplier<Object> handle) {
|
|
||||||
try {
|
|
||||||
Object obj = ThreadLocalHolder.get(key);
|
|
||||||
if (ObjectUtil.isNull(obj)) {
|
|
||||||
obj = handle.get();
|
|
||||||
ThreadLocalHolder.set(key, obj);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,17 +4,14 @@ import cn.dev33.satoken.exception.NotLoginException;
|
|||||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||||
import cn.dev33.satoken.router.SaRouter;
|
import cn.dev33.satoken.router.SaRouter;
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import lombok.RequiredArgsConstructor;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.common.core.utils.ServletUtils;
|
import org.dromara.common.core.utils.ServletUtils;
|
||||||
import org.dromara.common.core.utils.SpringUtils;
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
import org.dromara.common.satoken.utils.LoginHelper;
|
import org.dromara.common.satoken.utils.LoginHelper;
|
||||||
import org.dromara.common.core.context.ThreadLocalHolder;
|
|
||||||
import org.dromara.common.security.config.properties.SecurityProperties;
|
import org.dromara.common.security.config.properties.SecurityProperties;
|
||||||
import org.dromara.common.security.handler.AllUrlHandler;
|
import org.dromara.common.security.handler.AllUrlHandler;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
@ -62,9 +59,6 @@ public class SecurityConfig implements WebMvcConfigurer {
|
|||||||
StpUtil.getTokenValue());
|
StpUtil.getTokenValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存用户信息
|
|
||||||
ThreadLocalHolder.set(LoginHelper.LOGIN_USER_KEY, LoginHelper.getLoginUser());
|
|
||||||
|
|
||||||
// 有效率影响 用于临时测试
|
// 有效率影响 用于临时测试
|
||||||
// if (log.isDebugEnabled()) {
|
// if (log.isDebugEnabled()) {
|
||||||
// log.info("剩余有效时间: {}", StpUtil.getTokenTimeout());
|
// log.info("剩余有效时间: {}", StpUtil.getTokenTimeout());
|
||||||
@ -72,13 +66,7 @@ public class SecurityConfig implements WebMvcConfigurer {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
});
|
});
|
||||||
})
|
})).addPathPatterns("/**")
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
|
||||||
ThreadLocalHolder.remove(LoginHelper.LOGIN_USER_KEY);
|
|
||||||
}
|
|
||||||
}).addPathPatterns("/**")
|
|
||||||
// 排除不需要拦截的路径
|
// 排除不需要拦截的路径
|
||||||
.excludePathPatterns(securityProperties.getExcludes());
|
.excludePathPatterns(securityProperties.getExcludes());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,8 @@ package org.dromara.common.social.config.properties;
|
|||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 社交登录配置
|
* 社交登录配置
|
||||||
*
|
*
|
||||||
@ -65,4 +67,9 @@ public class SocialLoginConfigProperties {
|
|||||||
*/
|
*/
|
||||||
private String serverUrl;
|
private String serverUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求范围
|
||||||
|
*/
|
||||||
|
private List<String> scopes;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@ import org.dromara.common.core.utils.SpringUtils;
|
|||||||
import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
|
import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
|
||||||
import org.dromara.common.social.config.properties.SocialProperties;
|
import org.dromara.common.social.config.properties.SocialProperties;
|
||||||
import org.dromara.common.social.maxkey.AuthMaxKeyRequest;
|
import org.dromara.common.social.maxkey.AuthMaxKeyRequest;
|
||||||
|
import org.dromara.common.social.topiam.AuthTopIamRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 认证授权工具类
|
* 认证授权工具类
|
||||||
@ -38,7 +39,8 @@ public class SocialUtils {
|
|||||||
AuthConfig.AuthConfigBuilder builder = AuthConfig.builder()
|
AuthConfig.AuthConfigBuilder builder = AuthConfig.builder()
|
||||||
.clientId(obj.getClientId())
|
.clientId(obj.getClientId())
|
||||||
.clientSecret(obj.getClientSecret())
|
.clientSecret(obj.getClientSecret())
|
||||||
.redirectUri(obj.getRedirectUri());
|
.redirectUri(obj.getRedirectUri())
|
||||||
|
.scopes(obj.getScopes());
|
||||||
return switch (source.toLowerCase()) {
|
return switch (source.toLowerCase()) {
|
||||||
case "dingtalk" -> new AuthDingTalkRequest(builder.build(), STATE_CACHE);
|
case "dingtalk" -> new AuthDingTalkRequest(builder.build(), STATE_CACHE);
|
||||||
case "baidu" -> new AuthBaiduRequest(builder.build(), STATE_CACHE);
|
case "baidu" -> new AuthBaiduRequest(builder.build(), STATE_CACHE);
|
||||||
@ -63,6 +65,7 @@ public class SocialUtils {
|
|||||||
case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE);
|
case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE);
|
||||||
case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE);
|
case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE);
|
||||||
case "maxkey" -> new AuthMaxKeyRequest(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配置");
|
default -> throw new AuthException("未获取到有效的Auth配置");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
package org.dromara.common.tenant.helper;
|
package org.dromara.common.tenant.helper;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.context.SaHolder;
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||||
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
|
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
|
||||||
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
|
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.common.core.constant.GlobalConstants;
|
import org.dromara.common.core.constant.GlobalConstants;
|
||||||
import org.dromara.common.core.context.ThreadLocalHolder;
|
|
||||||
import org.dromara.common.core.utils.SpringUtils;
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
import org.dromara.common.redis.utils.RedisUtils;
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
@ -27,7 +28,7 @@ public class TenantHelper {
|
|||||||
|
|
||||||
private static final String DYNAMIC_TENANT_KEY = GlobalConstants.GLOBAL_REDIS_KEY + "dynamicTenant";
|
private static final String DYNAMIC_TENANT_KEY = GlobalConstants.GLOBAL_REDIS_KEY + "dynamicTenant";
|
||||||
|
|
||||||
private static final String TENANT_ID_KEY = "tempDynamicTenant";
|
private static final ThreadLocal<String> TEMP_DYNAMIC_TENANT = new TransmittableThreadLocal<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户功能是否启用
|
* 租户功能是否启用
|
||||||
@ -88,12 +89,12 @@ public class TenantHelper {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isLogin()) {
|
if (!isLogin()) {
|
||||||
ThreadLocalHolder.set(TENANT_ID_KEY, tenantId);
|
TEMP_DYNAMIC_TENANT.set(tenantId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
|
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
|
||||||
RedisUtils.setCacheObject(cacheKey, tenantId);
|
RedisUtils.setCacheObject(cacheKey, tenantId);
|
||||||
ThreadLocalHolder.set(cacheKey, tenantId);
|
SaHolder.getStorage().set(cacheKey, tenantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,15 +107,15 @@ public class TenantHelper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!isLogin()) {
|
if (!isLogin()) {
|
||||||
return ThreadLocalHolder.get(TENANT_ID_KEY);
|
return TEMP_DYNAMIC_TENANT.get();
|
||||||
}
|
}
|
||||||
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
|
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
|
||||||
String tenantId = ThreadLocalHolder.get(cacheKey);
|
String tenantId = (String) SaHolder.getStorage().get(cacheKey);
|
||||||
if (StringUtils.isNotBlank(tenantId)) {
|
if (StringUtils.isNotBlank(tenantId)) {
|
||||||
return tenantId;
|
return tenantId;
|
||||||
}
|
}
|
||||||
tenantId = RedisUtils.getCacheObject(cacheKey);
|
tenantId = RedisUtils.getCacheObject(cacheKey);
|
||||||
ThreadLocalHolder.set(cacheKey, tenantId);
|
SaHolder.getStorage().set(cacheKey, tenantId);
|
||||||
return tenantId;
|
return tenantId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,12 +127,12 @@ public class TenantHelper {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isLogin()) {
|
if (!isLogin()) {
|
||||||
ThreadLocalHolder.remove(TENANT_ID_KEY);
|
TEMP_DYNAMIC_TENANT.remove();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
|
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
|
||||||
RedisUtils.deleteObject(cacheKey);
|
RedisUtils.deleteObject(cacheKey);
|
||||||
ThreadLocalHolder.remove(cacheKey);
|
SaHolder.getStorage().delete(cacheKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -24,6 +24,10 @@ public class UndertowConfig implements WebServerFactoryCustomizer<UndertowServle
|
|||||||
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
|
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
|
||||||
webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 512));
|
webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 512));
|
||||||
deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
|
deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
|
||||||
|
// 使用虚拟线程 解除下方注释 仅限jdk21
|
||||||
|
// if (SpringUtils.getProperty("spring.threads.virtual.enabled", Boolean.class, false)) {
|
||||||
|
// deploymentInfo.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,15 +2,14 @@ package org.dromara.common.web.interceptor;
|
|||||||
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import org.dromara.common.core.context.ThreadLocalHolder;
|
|
||||||
import org.dromara.common.core.utils.SpringUtils;
|
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
|
||||||
import org.dromara.common.json.utils.JsonUtils;
|
|
||||||
import org.dromara.common.web.filter.RepeatedlyRequestWrapper;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.time.StopWatch;
|
import org.apache.commons.lang3.time.StopWatch;
|
||||||
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.dromara.common.json.utils.JsonUtils;
|
||||||
|
import org.dromara.common.web.filter.RepeatedlyRequestWrapper;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
@ -30,7 +29,7 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
|
|||||||
|
|
||||||
private final String prodProfile = "prod";
|
private final String prodProfile = "prod";
|
||||||
|
|
||||||
private final String STOP_WATCH_KEY = "stopwatch";
|
private final static ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||||
@ -56,7 +55,7 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StopWatch stopWatch = new StopWatch();
|
StopWatch stopWatch = new StopWatch();
|
||||||
ThreadLocalHolder.set(STOP_WATCH_KEY, stopWatch);
|
KEY_CACHE.set(stopWatch);
|
||||||
stopWatch.start();
|
stopWatch.start();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -70,10 +69,10 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
|
|||||||
@Override
|
@Override
|
||||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||||
if (!prodProfile.equals(SpringUtils.getActiveProfile())) {
|
if (!prodProfile.equals(SpringUtils.getActiveProfile())) {
|
||||||
StopWatch stopWatch = ThreadLocalHolder.get(STOP_WATCH_KEY);
|
StopWatch stopWatch = KEY_CACHE.get();
|
||||||
stopWatch.stop();
|
stopWatch.stop();
|
||||||
log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime());
|
log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime());
|
||||||
ThreadLocalHolder.clear();
|
KEY_CACHE.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,6 @@ import java.util.Arrays;
|
|||||||
public class SysProfileController extends BaseController {
|
public class SysProfileController extends BaseController {
|
||||||
|
|
||||||
private final ISysUserService userService;
|
private final ISysUserService userService;
|
||||||
private final ISysRoleService roleService;
|
|
||||||
private final ISysOssService ossService;
|
private final ISysOssService ossService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,7 +49,6 @@ public class SysProfileController extends BaseController {
|
|||||||
@GetMapping
|
@GetMapping
|
||||||
public R<ProfileVo> profile() {
|
public R<ProfileVo> profile() {
|
||||||
SysUserVo user = userService.selectUserById(LoginHelper.getUserId());
|
SysUserVo user = userService.selectUserById(LoginHelper.getUserId());
|
||||||
user.setRoles(roleService.selectRolesByUserId(user.getUserId()));
|
|
||||||
ProfileVo profileVo = new ProfileVo();
|
ProfileVo profileVo = new ProfileVo();
|
||||||
profileVo.setUser(user);
|
profileVo.setUser(user);
|
||||||
profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserId()));
|
profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserId()));
|
||||||
|
|||||||
@ -72,9 +72,8 @@ public class SysUserController extends BaseController {
|
|||||||
@SaCheckPermission("system:user:export")
|
@SaCheckPermission("system:user:export")
|
||||||
@PostMapping("/export")
|
@PostMapping("/export")
|
||||||
public void export(SysUserBo user, HttpServletResponse response) {
|
public void export(SysUserBo user, HttpServletResponse response) {
|
||||||
List<SysUserVo> list = userService.selectUserList(user);
|
List<SysUserExportVo> list = userService.selectUserExportList(user);
|
||||||
List<SysUserExportVo> listVo = MapstructUtils.convert(list, SysUserExportVo.class);
|
ExcelUtil.exportExcel(list, "用户数据", SysUserExportVo.class, response);
|
||||||
ExcelUtil.exportExcel(listVo, "用户数据", SysUserExportVo.class, response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -116,7 +115,6 @@ public class SysUserController extends BaseController {
|
|||||||
if (ObjectUtil.isNull(user)) {
|
if (ObjectUtil.isNull(user)) {
|
||||||
return R.fail("没有权限访问用户数据!");
|
return R.fail("没有权限访问用户数据!");
|
||||||
}
|
}
|
||||||
user.setRoles(roleService.selectRolesByUserId(user.getUserId()));
|
|
||||||
userInfoVo.setUser(user);
|
userInfoVo.setUser(user);
|
||||||
userInfoVo.setPermissions(loginUser.getMenuPermission());
|
userInfoVo.setPermissions(loginUser.getMenuPermission());
|
||||||
userInfoVo.setRoles(loginUser.getRolePermission());
|
userInfoVo.setRoles(loginUser.getRolePermission());
|
||||||
|
|||||||
@ -20,7 +20,6 @@ import java.util.Date;
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AutoMapper(target = SysUserVo.class, convertGenerate = false)
|
|
||||||
public class SysUserExportVo implements Serializable {
|
public class SysUserExportVo implements Serializable {
|
||||||
|
|
||||||
@Serial
|
@Serial
|
||||||
@ -85,14 +84,12 @@ public class SysUserExportVo implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* 部门名称
|
* 部门名称
|
||||||
*/
|
*/
|
||||||
@ReverseAutoMapping(target = "deptName", source = "dept.deptName")
|
|
||||||
@ExcelProperty(value = "部门名称")
|
@ExcelProperty(value = "部门名称")
|
||||||
private String deptName;
|
private String deptName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 负责人
|
* 负责人
|
||||||
*/
|
*/
|
||||||
@ReverseAutoMapping(target = "leaderName", source = "dept.leaderName")
|
|
||||||
@ExcelProperty(value = "部门负责人")
|
@ExcelProperty(value = "部门负责人")
|
||||||
private String leaderName;
|
private String leaderName;
|
||||||
|
|
||||||
|
|||||||
@ -114,9 +114,10 @@ public class SysUserVo implements Serializable {
|
|||||||
private Date createTime;
|
private Date createTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 部门对象
|
* 部门名
|
||||||
*/
|
*/
|
||||||
private SysDeptVo dept;
|
@Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "deptId")
|
||||||
|
private String deptName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 角色对象
|
* 角色对象
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import org.dromara.common.mybatis.annotation.DataColumn;
|
|||||||
import org.dromara.common.mybatis.annotation.DataPermission;
|
import org.dromara.common.mybatis.annotation.DataPermission;
|
||||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||||
import org.dromara.system.domain.SysUser;
|
import org.dromara.system.domain.SysUser;
|
||||||
|
import org.dromara.system.domain.vo.SysUserExportVo;
|
||||||
import org.dromara.system.domain.vo.SysUserVo;
|
import org.dromara.system.domain.vo.SysUserVo;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -20,7 +21,7 @@ import java.util.List;
|
|||||||
public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
|
public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
|
||||||
|
|
||||||
@DataPermission({
|
@DataPermission({
|
||||||
@DataColumn(key = "deptName", value = "d.dept_id"),
|
@DataColumn(key = "deptName", value = "u.dept_id"),
|
||||||
@DataColumn(key = "userName", value = "u.user_id")
|
@DataColumn(key = "userName", value = "u.user_id")
|
||||||
})
|
})
|
||||||
Page<SysUserVo> selectPageUserList(@Param("page") Page<SysUser> page, @Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);
|
Page<SysUserVo> selectPageUserList(@Param("page") Page<SysUser> page, @Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);
|
||||||
@ -35,7 +36,7 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
|
|||||||
@DataColumn(key = "deptName", value = "d.dept_id"),
|
@DataColumn(key = "deptName", value = "d.dept_id"),
|
||||||
@DataColumn(key = "userName", value = "u.user_id")
|
@DataColumn(key = "userName", value = "u.user_id")
|
||||||
})
|
})
|
||||||
List<SysUserVo> selectUserList(@Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);
|
List<SysUserExportVo> selectUserExportList(@Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据条件分页查询已配用户角色列表
|
* 根据条件分页查询已配用户角色列表
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package org.dromara.system.service;
|
|||||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||||
import org.dromara.system.domain.bo.SysUserBo;
|
import org.dromara.system.domain.bo.SysUserBo;
|
||||||
|
import org.dromara.system.domain.vo.SysUserExportVo;
|
||||||
import org.dromara.system.domain.vo.SysUserVo;
|
import org.dromara.system.domain.vo.SysUserVo;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -23,7 +24,7 @@ public interface ISysUserService {
|
|||||||
* @param user 用户信息
|
* @param user 用户信息
|
||||||
* @return 用户信息集合信息
|
* @return 用户信息集合信息
|
||||||
*/
|
*/
|
||||||
List<SysUserVo> selectUserList(SysUserBo user);
|
List<SysUserExportVo> selectUserExportList(SysUserBo user);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据条件分页查询已分配用户角色列表
|
* 根据条件分页查询已分配用户角色列表
|
||||||
|
|||||||
@ -6,9 +6,8 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import org.dromara.common.core.constant.CacheConstants;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.dromara.common.core.constant.CacheNames;
|
import org.dromara.common.core.constant.CacheNames;
|
||||||
import org.dromara.common.core.context.ThreadLocalHolder;
|
|
||||||
import org.dromara.common.core.exception.ServiceException;
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
import org.dromara.common.core.service.DictService;
|
import org.dromara.common.core.service.DictService;
|
||||||
import org.dromara.common.core.utils.MapstructUtils;
|
import org.dromara.common.core.utils.MapstructUtils;
|
||||||
@ -26,7 +25,6 @@ import org.dromara.system.domain.vo.SysDictTypeVo;
|
|||||||
import org.dromara.system.mapper.SysDictDataMapper;
|
import org.dromara.system.mapper.SysDictDataMapper;
|
||||||
import org.dromara.system.mapper.SysDictTypeMapper;
|
import org.dromara.system.mapper.SysDictTypeMapper;
|
||||||
import org.dromara.system.service.ISysDictTypeService;
|
import org.dromara.system.service.ISysDictTypeService;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.cache.annotation.CachePut;
|
import org.springframework.cache.annotation.CachePut;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -217,16 +215,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
|
|||||||
* @param separator 分隔符
|
* @param separator 分隔符
|
||||||
* @return 字典标签
|
* @return 字典标签
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked cast")
|
|
||||||
@Override
|
@Override
|
||||||
public String getDictLabel(String dictType, String dictValue, String separator) {
|
public String getDictLabel(String dictType, String dictValue, String separator) {
|
||||||
// 优先从本地缓存获取
|
List<SysDictDataVo> datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
|
||||||
List<SysDictDataVo> datas = ThreadLocalHolder.get(CacheConstants.SYS_DICT_KEY + dictType);
|
|
||||||
if (ObjectUtil.isNull(datas)) {
|
|
||||||
datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
|
|
||||||
ThreadLocalHolder.set(CacheConstants.SYS_DICT_KEY + dictType, datas);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, String> map = StreamUtils.toMap(datas, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel);
|
Map<String, String> map = StreamUtils.toMap(datas, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel);
|
||||||
if (StringUtils.containsAny(dictValue, separator)) {
|
if (StringUtils.containsAny(dictValue, separator)) {
|
||||||
return Arrays.stream(dictValue.split(separator))
|
return Arrays.stream(dictValue.split(separator))
|
||||||
@ -245,16 +236,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
|
|||||||
* @param separator 分隔符
|
* @param separator 分隔符
|
||||||
* @return 字典值
|
* @return 字典值
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked cast")
|
|
||||||
@Override
|
@Override
|
||||||
public String getDictValue(String dictType, String dictLabel, String separator) {
|
public String getDictValue(String dictType, String dictLabel, String separator) {
|
||||||
// 优先从本地缓存获取
|
List<SysDictDataVo> datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
|
||||||
List<SysDictDataVo> datas = ThreadLocalHolder.get(CacheConstants.SYS_DICT_KEY + dictType);
|
|
||||||
if (ObjectUtil.isNull(datas)) {
|
|
||||||
datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
|
|
||||||
ThreadLocalHolder.set(CacheConstants.SYS_DICT_KEY + dictType, datas);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, String> map = StreamUtils.toMap(datas, SysDictDataVo::getDictLabel, SysDictDataVo::getDictValue);
|
Map<String, String> map = StreamUtils.toMap(datas, SysDictDataVo::getDictLabel, SysDictDataVo::getDictValue);
|
||||||
if (StringUtils.containsAny(dictLabel, separator)) {
|
if (StringUtils.containsAny(dictLabel, separator)) {
|
||||||
return Arrays.stream(dictLabel.split(separator))
|
return Arrays.stream(dictLabel.split(separator))
|
||||||
|
|||||||
@ -80,7 +80,6 @@ public class SysRoleServiceImpl implements ISysRoleService {
|
|||||||
.between(params.get("beginTime") != null && params.get("endTime") != null,
|
.between(params.get("beginTime") != null && params.get("endTime") != null,
|
||||||
"r.create_time", params.get("beginTime"), params.get("endTime"))
|
"r.create_time", params.get("beginTime"), params.get("endTime"))
|
||||||
.orderByAsc("r.role_sort").orderByAsc("r.create_time");
|
.orderByAsc("r.role_sort").orderByAsc("r.create_time");
|
||||||
;
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import org.dromara.system.domain.SysUserRole;
|
|||||||
import org.dromara.system.domain.bo.SysUserBo;
|
import org.dromara.system.domain.bo.SysUserBo;
|
||||||
import org.dromara.system.domain.vo.SysPostVo;
|
import org.dromara.system.domain.vo.SysPostVo;
|
||||||
import org.dromara.system.domain.vo.SysRoleVo;
|
import org.dromara.system.domain.vo.SysRoleVo;
|
||||||
|
import org.dromara.system.domain.vo.SysUserExportVo;
|
||||||
import org.dromara.system.domain.vo.SysUserVo;
|
import org.dromara.system.domain.vo.SysUserVo;
|
||||||
import org.dromara.system.mapper.*;
|
import org.dromara.system.mapper.*;
|
||||||
import org.dromara.system.service.ISysUserService;
|
import org.dromara.system.service.ISysUserService;
|
||||||
@ -69,8 +70,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
|
|||||||
* @return 用户信息集合信息
|
* @return 用户信息集合信息
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<SysUserVo> selectUserList(SysUserBo user) {
|
public List<SysUserExportVo> selectUserExportList(SysUserBo user) {
|
||||||
return baseMapper.selectUserList(this.buildQueryWrapper(user));
|
return baseMapper.selectUserExportList(this.buildQueryWrapper(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Wrapper<SysUser> buildQueryWrapper(SysUserBo user) {
|
private Wrapper<SysUser> buildQueryWrapper(SysUserBo user) {
|
||||||
@ -163,7 +164,12 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public SysUserVo selectUserById(Long userId) {
|
public SysUserVo selectUserById(Long userId) {
|
||||||
return baseMapper.selectVoById(userId);
|
SysUserVo user = baseMapper.selectVoById(userId);
|
||||||
|
if (ObjectUtil.isNull(user)) {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
user.setRoles(roleMapper.selectRolesByUserId(user.getUserId()));
|
||||||
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -4,31 +4,21 @@
|
|||||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="org.dromara.system.mapper.SysUserMapper">
|
<mapper namespace="org.dromara.system.mapper.SysUserMapper">
|
||||||
|
|
||||||
<!-- 多结构嵌套自动映射需带上每个实体的主键id 否则映射会失败 -->
|
|
||||||
<resultMap type="org.dromara.system.domain.vo.SysUserVo" id="SysUserResult">
|
<resultMap type="org.dromara.system.domain.vo.SysUserVo" id="SysUserResult">
|
||||||
<id property="userId" column="user_id"/>
|
<id property="userId" column="user_id"/>
|
||||||
<result property="deptId" column="dept_id"/>
|
|
||||||
<association property="dept" column="dept_id" resultMap="deptResult"/>
|
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
<resultMap type="org.dromara.system.domain.vo.SysUserExportVo" id="SysUserExportResult">
|
||||||
<resultMap id="deptResult" type="org.dromara.system.domain.vo.SysDeptVo">
|
<id property="userId" column="user_id"/>
|
||||||
<id property="deptId" column="dept_id"/>
|
|
||||||
<result property="email" column="dept_email"/>
|
|
||||||
<result property="status" column="dept_status"/>
|
|
||||||
<result property="createTime" column="dept_create_time"/>
|
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<select id="selectPageUserList" resultMap="SysUserResult">
|
<select id="selectPageUserList" resultMap="SysUserResult">
|
||||||
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
|
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
|
||||||
u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
|
u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark
|
||||||
d.dept_name, d.leader, u1.user_name as leaderName
|
|
||||||
from sys_user u
|
from sys_user u
|
||||||
left join sys_dept d on u.dept_id = d.dept_id
|
|
||||||
left join sys_user u1 on u1.user_id = d.leader
|
|
||||||
${ew.getCustomSqlSegment}
|
${ew.getCustomSqlSegment}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectUserList" resultMap="SysUserResult">
|
<select id="selectUserExportList" resultMap="SysUserExportResult">
|
||||||
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
|
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
|
||||||
u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
|
u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
|
||||||
d.dept_name, d.leader, u1.user_name as leaderName
|
d.dept_name, d.leader, u1.user_name as leaderName
|
||||||
|
|||||||
@ -100,7 +100,7 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
|
|
||||||
ruoyi-server1:
|
ruoyi-server1:
|
||||||
image: ruoyi/ruoyi-server:5.1.2
|
image: ruoyi/ruoyi-server:5.2.0
|
||||||
container_name: ruoyi-server1
|
container_name: ruoyi-server1
|
||||||
environment:
|
environment:
|
||||||
# 时区上海
|
# 时区上海
|
||||||
@ -115,7 +115,7 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
|
|
||||||
ruoyi-server2:
|
ruoyi-server2:
|
||||||
image: ruoyi/ruoyi-server:5.1.2
|
image: ruoyi/ruoyi-server:5.2.0
|
||||||
container_name: ruoyi-server2
|
container_name: ruoyi-server2
|
||||||
environment:
|
environment:
|
||||||
# 时区上海
|
# 时区上海
|
||||||
@ -130,7 +130,7 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
|
|
||||||
ruoyi-monitor-admin:
|
ruoyi-monitor-admin:
|
||||||
image: ruoyi/ruoyi-monitor-admin:5.1.2
|
image: ruoyi/ruoyi-monitor-admin:5.2.0
|
||||||
container_name: ruoyi-monitor-admin
|
container_name: ruoyi-monitor-admin
|
||||||
environment:
|
environment:
|
||||||
# 时区上海
|
# 时区上海
|
||||||
@ -142,7 +142,7 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
|
|
||||||
ruoyi-powerjob-server:
|
ruoyi-powerjob-server:
|
||||||
image: ruoyi/ruoyi-powerjob-server:5.1.2
|
image: ruoyi/ruoyi-powerjob-server:5.2.0
|
||||||
container_name: ruoyi-powerjob-server
|
container_name: ruoyi-powerjob-server
|
||||||
environment:
|
environment:
|
||||||
# 时区上海
|
# 时区上海
|
||||||
|
|||||||
@ -108,7 +108,7 @@ http {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# 解决 powerjob 代理之后静态文件无法访问的问题 请勿修改乱动
|
# 解决 powerjob 代理之后静态文件无法访问的问题 请勿修改乱动
|
||||||
location ~ ^/(js|css|jpg|png|svg|woff|ttf|ico|img)/ {
|
location ~ ^/(js|img|fonts)/ {
|
||||||
proxy_pass http://powerjob-server;
|
proxy_pass http://powerjob-server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@ create table sys_social
|
|||||||
union_id varchar2(255) default null,
|
union_id varchar2(255) default null,
|
||||||
scope varchar2(255) default null,
|
scope varchar2(255) default null,
|
||||||
token_type varchar2(255) default null,
|
token_type varchar2(255) default null,
|
||||||
id_token varchar2(255) default null,
|
id_token varchar2(2000) default null,
|
||||||
mac_algorithm varchar2(255) default null,
|
mac_algorithm varchar2(255) default null,
|
||||||
mac_key varchar2(255) default null,
|
mac_key varchar2(255) default null,
|
||||||
code varchar2(255) default null,
|
code varchar2(255) default null,
|
||||||
|
|||||||
@ -20,7 +20,7 @@ create table sys_social
|
|||||||
union_id varchar(255) default null::varchar,
|
union_id varchar(255) default null::varchar,
|
||||||
scope varchar(255) default null::varchar,
|
scope varchar(255) default null::varchar,
|
||||||
token_type varchar(255) default null::varchar,
|
token_type varchar(255) default null::varchar,
|
||||||
id_token varchar(255) default null::varchar,
|
id_token varchar(2000) default null::varchar,
|
||||||
mac_algorithm varchar(255) default null::varchar,
|
mac_algorithm varchar(255) default null::varchar,
|
||||||
mac_key varchar(255) default null::varchar,
|
mac_key varchar(255) default null::varchar,
|
||||||
code varchar(255) default null::varchar,
|
code varchar(255) default null::varchar,
|
||||||
|
|||||||
@ -21,7 +21,7 @@ create table sys_social
|
|||||||
union_id varchar(255) default null comment '用户的 unionid',
|
union_id varchar(255) default null comment '用户的 unionid',
|
||||||
scope varchar(255) default null comment '授予的权限,部分平台可能没有',
|
scope varchar(255) default null comment '授予的权限,部分平台可能没有',
|
||||||
token_type varchar(255) default null comment '个别平台的授权信息,部分平台可能没有',
|
token_type varchar(255) default null comment '个别平台的授权信息,部分平台可能没有',
|
||||||
id_token varchar(255) default null comment 'id token,部分平台可能没有',
|
id_token varchar(2000) default null comment 'id token,部分平台可能没有',
|
||||||
mac_algorithm varchar(255) default null comment '小米平台用户的附带属性,部分平台可能没有',
|
mac_algorithm varchar(255) default null comment '小米平台用户的附带属性,部分平台可能没有',
|
||||||
mac_key varchar(255) default null comment '小米平台用户的附带属性,部分平台可能没有',
|
mac_key varchar(255) default null comment '小米平台用户的附带属性,部分平台可能没有',
|
||||||
code varchar(255) default null comment '用户的授权code,部分平台可能没有',
|
code varchar(255) default null comment '用户的授权code,部分平台可能没有',
|
||||||
|
|||||||
@ -17,7 +17,7 @@ create table sys_social
|
|||||||
union_id nvarchar(255) NULL,
|
union_id nvarchar(255) NULL,
|
||||||
scope nvarchar(255) NULL,
|
scope nvarchar(255) NULL,
|
||||||
token_type nvarchar(255) NULL,
|
token_type nvarchar(255) NULL,
|
||||||
id_token nvarchar(255) NULL,
|
id_token nvarchar(2000) NULL,
|
||||||
mac_algorithm nvarchar(255) NULL,
|
mac_algorithm nvarchar(255) NULL,
|
||||||
mac_key nvarchar(255) NULL,
|
mac_key nvarchar(255) NULL,
|
||||||
code nvarchar(255) NULL,
|
code nvarchar(255) NULL,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user