diff --git a/.run/ruoyi-easyretry-server.run.xml b/.run/ruoyi-easyretry-server.run.xml new file mode 100644 index 000000000..2d5d3f58f --- /dev/null +++ b/.run/ruoyi-easyretry-server.run.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/.run/ruoyi-monitor-admin.run.xml b/.run/ruoyi-monitor-admin.run.xml index 4837d6a42..095b3d7cb 100644 --- a/.run/ruoyi-monitor-admin.run.xml +++ b/.run/ruoyi-monitor-admin.run.xml @@ -2,7 +2,7 @@ - + diff --git a/.run/ruoyi-powerjob-server.run.xml b/.run/ruoyi-powerjob-server.run.xml index 77a4af095..e36e2acf3 100644 --- a/.run/ruoyi-powerjob-server.run.xml +++ b/.run/ruoyi-powerjob-server.run.xml @@ -2,7 +2,7 @@ - + diff --git a/.run/ruoyi-server.run.xml b/.run/ruoyi-server.run.xml index a29ff2a7f..0463c3439 100644 --- a/.run/ruoyi-server.run.xml +++ b/.run/ruoyi-server.run.xml @@ -2,7 +2,7 @@ - + diff --git a/README.md b/README.md index a542b4752..e617bc81c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE) [](https://www.jetbrains.com/?from=RuoYi-Vue-Plus) -[](https://gitee.com/dromara/RuoYi-Vue-Plus) +[](https://gitee.com/dromara/RuoYi-Vue-Plus) []() []() []() diff --git a/pom.xml b/pom.xml index f82e582f9..d16604238 100644 --- a/pom.xml +++ b/pom.xml @@ -13,29 +13,30 @@ RuoYi-Vue-Plus多租户管理系统 - 5.1.2 - 3.1.7 + 5.2.0-SNAPSHOT + 3.2.4 UTF-8 UTF-8 17 3.0.3 - 2.2.0 + 2.4.0 0.15.0 5.2.3 3.3.3 2.3 1.37.0 - 3.5.4 + 3.5.5 3.9.1 - 5.8.22 + 5.8.26 4.10.0 - 3.1.8 - 3.24.3 - 2.2.5 - 4.2.0 + 3.2.3 + 3.27.2 + 2.2.7 + 4.3.0 2.14.4 4.3.6 - 1.3.5 + 3.2.0 + 1.3.6 0.2.0 1.18.30 1.76 @@ -44,9 +45,10 @@ 2.7.0 - 1.12.600 + 2.25.15 + 0.29.13 - 2.2.0 + 3.2.0 1.2.83 3.0.4.2 @@ -57,6 +59,9 @@ 3.11.0 3.1.2 1.3.0 + + + 7.0.0 @@ -111,6 +116,14 @@ import + + org.flowable + flowable-bom + ${flowable.version} + pom + import + + me.zhyd.oauth @@ -213,7 +226,7 @@ com.baomidou - mybatis-plus-boot-starter + mybatis-plus-spring-boot3-starter ${mybatis-plus.version} @@ -235,10 +248,23 @@ ${okhttp.version} + - com.amazonaws - aws-java-sdk-s3 - ${aws-java-sdk-s3.version} + software.amazon.awssdk + s3 + ${aws.sdk.version} + + + + software.amazon.awssdk.crt + aws-crt + ${aws.crt.version} + + + + software.amazon.awssdk + s3-transfer-manager + ${aws.sdk.version} @@ -283,6 +309,23 @@ ${powerjob.version} + + + com.aizuda + easy-retry-client-starter + ${easyretry.version} + + + com.aizuda + easy-retry-client-core + ${easyretry.version} + + + com.aizuda + easy-retry-client-job-core + ${easyretry.version} + + com.alibaba transmittable-thread-local diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 46c320fcf..fa4750fc2 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -118,6 +118,12 @@ ruoyi-demo + + + org.dromara + ruoyi-workflow + + de.codecentric spring-boot-admin-starter-client @@ -134,6 +140,19 @@ JustAuth + + com.aizuda + easy-retry-client-starter + + + com.aizuda + easy-retry-client-core + + + com.aizuda + easy-retry-client-job-core + + diff --git a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java index d0d79316e..43e689be3 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java @@ -23,9 +23,10 @@ import org.dromara.common.social.config.properties.SocialLoginConfigProperties; import org.dromara.common.social.config.properties.SocialProperties; import org.dromara.common.social.utils.SocialUtils; import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.common.websocket.dto.WebSocketMessageDto; import org.dromara.common.websocket.utils.WebSocketUtils; -import org.dromara.system.domain.SysClient; import org.dromara.system.domain.bo.SysTenantBo; +import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysTenantVo; import org.dromara.system.service.ISysClientService; import org.dromara.system.service.ISysConfigService; @@ -81,7 +82,7 @@ public class AuthController { // 授权类型和客户端id String clientId = loginBody.getClientId(); String grantType = loginBody.getGrantType(); - SysClient client = clientService.queryByClientId(clientId); + SysClientVo client = clientService.queryByClientId(clientId); // 查询不到 client 或 client 内不包含 grantType if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) { log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType); @@ -96,7 +97,10 @@ public class AuthController { Long userId = LoginHelper.getUserId(); scheduledExecutorService.schedule(() -> { - WebSocketUtils.sendMessage(userId, "欢迎登录RuoYi-Vue-Plus后台管理系统"); + WebSocketMessageDto dto = new WebSocketMessageDto(); + dto.setMessage("欢迎登录RuoYi-Vue-Plus后台管理系统"); + dto.setSessionKeys(List.of(userId)); + WebSocketUtils.publishMessage(dto); }, 3, TimeUnit.SECONDS); return R.ok(loginVo); } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java b/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java index ef33f5b6f..1a476a94a 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java @@ -5,6 +5,9 @@ import cn.hutool.captcha.AbstractCaptcha; import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.RandomUtil; +import jakarta.validation.constraints.NotBlank; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.domain.R; @@ -21,11 +24,7 @@ import org.dromara.common.web.enums.CaptchaType; import org.dromara.sms4j.api.SmsBlend; import org.dromara.sms4j.api.entity.SmsResponse; import org.dromara.sms4j.core.factory.SmsFactory; -import org.dromara.sms4j.provider.enumerate.SupplierType; import org.dromara.web.domain.vo.CaptchaVo; -import jakarta.validation.constraints.NotBlank; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -66,11 +65,11 @@ public class CaptchaController { String templateId = ""; LinkedHashMap map = new LinkedHashMap<>(1); map.put("code", code); - SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA); + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map); - if (!"OK".equals(smsResponse.getCode())) { + if (!smsResponse.isSuccess()) { log.error("验证码短信发送异常 => {}", smsResponse); - return R.fail(smsResponse.getMessage()); + return R.fail(smsResponse.getData().toString()); } return R.ok(); } @@ -121,6 +120,7 @@ public class CaptchaController { AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz()); captcha.setGenerator(codeGenerator); captcha.createCode(); + // 如果是数学验证码,使用SpEL表达式处理验证码结果 String code = captcha.getCode(); if (isMath) { ExpressionParser parser = new SpelExpressionParser(); diff --git a/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/TenantListVo.java b/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/TenantListVo.java index 4d4bc89eb..db9c2712c 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/TenantListVo.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/TenantListVo.java @@ -13,10 +13,19 @@ import lombok.Data; @AutoMapper(target = SysTenantVo.class) public class TenantListVo { + /** + * 租户编号 + */ private String tenantId; + /** + * 企业名称 + */ private String companyName; + /** + * 域名 + */ private String domain; } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java index 6d67fb112..a4724043b 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java @@ -10,7 +10,6 @@ import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.CacheConstants; import org.dromara.common.core.constant.Constants; 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.ServletUtils; 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.redis.utils.RedisUtils; import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.web.service.SysLoginService; 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) { UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); String ip = ServletUtils.getClientIP(); - LoginUser user = LoginHelper.getLoginUser(); UserOnlineDTO dto = new UserOnlineDTO(); dto.setIpaddr(ip); dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); @@ -51,25 +50,29 @@ public class UserActionListener implements SaTokenListener { dto.setOs(userAgent.getOs().getName()); dto.setLoginTime(System.currentTimeMillis()); dto.setTokenId(tokenValue); - dto.setUserName(user.getUsername()); - dto.setClientKey(user.getClientKey()); - dto.setDeviceType(user.getDeviceType()); - dto.setDeptName(user.getDeptName()); - if(tokenConfig.getTimeout() == -1) { - RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); - } else { - RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout())); - } + String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY); + String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY); + dto.setUserName(username); + 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) { + RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); + } else { + RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout())); + } + }); // 记录登录日志 LogininforEvent logininforEvent = new LogininforEvent(); - logininforEvent.setTenantId(user.getTenantId()); - logininforEvent.setUsername(user.getUsername()); + logininforEvent.setTenantId(tenantId); + logininforEvent.setUsername(username); logininforEvent.setStatus(Constants.LOGIN_SUCCESS); logininforEvent.setMessage(MessageUtils.message("user.login.success")); logininforEvent.setRequest(ServletUtils.getRequest()); 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); } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java index d4f9c73f7..a75b9131e 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java @@ -4,6 +4,7 @@ package org.dromara.web.service; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.SpringUtils; import org.dromara.system.domain.SysClient; +import org.dromara.system.domain.vo.SysClientVo; import org.dromara.web.domain.vo.LoginVo; /** @@ -17,8 +18,13 @@ public interface IAuthStrategy { /** * 登录 + * + * @param body 登录对象 + * @param client 授权管理视图对象 + * @param grantType 授权类型 + * @return 登录验证信息 */ - static LoginVo login(String body, SysClient client, String grantType) { + static LoginVo login(String body, SysClientVo client, String grantType) { // 授权类型和客户端id String beanName = grantType + BASE_NAME; if (!SpringUtils.containsBean(beanName)) { @@ -30,7 +36,11 @@ public interface IAuthStrategy { /** * 登录 + * + * @param body 登录对象 + * @param client 授权管理视图对象 + * @return 登录验证信息 */ - LoginVo login(String body, SysClient client); + LoginVo login(String body, SysClientVo client); } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java b/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java index 37802b7f0..b2c9d3a6b 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java @@ -25,13 +25,9 @@ import org.dromara.common.tenant.exception.TenantException; import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.system.domain.SysUser; import org.dromara.system.domain.bo.SysSocialBo; -import org.dromara.system.domain.vo.SysSocialVo; -import org.dromara.system.domain.vo.SysTenantVo; -import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.domain.vo.*; import org.dromara.system.mapper.SysUserMapper; -import org.dromara.system.service.ISysPermissionService; -import org.dromara.system.service.ISysSocialService; -import org.dromara.system.service.ISysTenantService; +import org.dromara.system.service.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -59,6 +55,8 @@ public class SysLoginService { private final ISysTenantService tenantService; private final ISysPermissionService permissionService; private final ISysSocialService sysSocialService; + private final ISysRoleService roleService; + private final ISysDeptService deptService; private final SysUserMapper userMapper; @@ -146,9 +144,15 @@ public class SysLoginService { loginUser.setUserType(user.getUserType()); loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId())); loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId())); - loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName()); - List roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class); - loginUser.setRoles(roles); + TenantHelper.dynamic(user.getTenantId(), () -> { + SysDeptVo dept = null; + if (ObjectUtil.isNotNull(user.getDeptId())) { + dept = deptService.selectDeptById(user.getDeptId()); + } + loginUser.setDeptName(ObjectUtil.isNull(dept) ? "" : dept.getDeptName()); + List roles = roleService.selectRolesByUserId(user.getUserId()); + loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class)); + }); return loginUser; } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java b/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java index f38d074e3..14fa0cc27 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java @@ -82,7 +82,7 @@ public class SysRegisterService { * @param uuid 唯一标识 */ public void validateCaptcha(String tenantId, String username, String code, String uuid) { - String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, ""); + String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, ""); String captcha = RedisUtils.getCacheObject(verifyKey); RedisUtils.deleteObject(verifyKey); if (captcha == null) { diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java index a2636bc22..38fdc448b 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java @@ -23,6 +23,7 @@ import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.system.domain.SysClient; import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.mapper.SysUserMapper; import org.dromara.web.domain.vo.LoginVo; @@ -44,7 +45,7 @@ public class EmailAuthStrategy implements IAuthStrategy { private final SysUserMapper userMapper; @Override - public LoginVo login(String body, SysClient client) { + public LoginVo login(String body, SysClientVo client) { EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class); ValidatorUtils.validate(loginBody); String tenantId = loginBody.getTenantId(); @@ -90,9 +91,7 @@ public class EmailAuthStrategy implements IAuthStrategy { private SysUserVo loadUserByEmail(String tenantId, String email) { return TenantHelper.dynamic(tenantId, () -> { - SysUser user = userMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getEmail, SysUser::getStatus) - .eq(SysUser::getEmail, email)); + SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getEmail, email)); if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", email); throw new UserException("user.not.exists", email); @@ -100,7 +99,7 @@ public class EmailAuthStrategy implements IAuthStrategy { log.info("登录用户:{} 已被停用.", email); throw new UserException("user.blocked", email); } - return userMapper.selectUserByEmail(email); + return user; }); } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java index bcb5916d3..cd33ea408 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java @@ -26,6 +26,7 @@ import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.web.config.properties.CaptchaProperties; import org.dromara.system.domain.SysClient; import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.mapper.SysUserMapper; import org.dromara.web.domain.vo.LoginVo; @@ -48,7 +49,7 @@ public class PasswordAuthStrategy implements IAuthStrategy { private final SysUserMapper userMapper; @Override - public LoginVo login(String body, SysClient client) { + public LoginVo login(String body, SysClientVo client) { PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class); ValidatorUtils.validate(loginBody); String tenantId = loginBody.getTenantId(); @@ -109,9 +110,7 @@ public class PasswordAuthStrategy implements IAuthStrategy { private SysUserVo loadUserByUsername(String tenantId, String username) { return TenantHelper.dynamic(tenantId, () -> { - SysUser user = userMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getUserName, SysUser::getStatus) - .eq(SysUser::getUserName, username)); + SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, username)); if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", username); throw new UserException("user.not.exists", username); @@ -119,7 +118,7 @@ public class PasswordAuthStrategy implements IAuthStrategy { log.info("登录用户:{} 已被停用.", username); throw new UserException("user.blocked", username); } - return userMapper.selectUserByUserName(username); + return user; }); } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java index a4fa11c8e..f883632f9 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java @@ -23,6 +23,7 @@ import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.system.domain.SysClient; import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.mapper.SysUserMapper; import org.dromara.web.domain.vo.LoginVo; @@ -44,7 +45,7 @@ public class SmsAuthStrategy implements IAuthStrategy { private final SysUserMapper userMapper; @Override - public LoginVo login(String body, SysClient client) { + public LoginVo login(String body, SysClientVo client) { SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class); ValidatorUtils.validate(loginBody); String tenantId = loginBody.getTenantId(); @@ -90,9 +91,7 @@ public class SmsAuthStrategy implements IAuthStrategy { private SysUserVo loadUserByPhonenumber(String tenantId, String phonenumber) { return TenantHelper.dynamic(tenantId, () -> { - SysUser user = userMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getPhonenumber, SysUser::getStatus) - .eq(SysUser::getPhonenumber, phonenumber)); + SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getPhonenumber, phonenumber)); if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", phonenumber); throw new UserException("user.not.exists", phonenumber); @@ -100,7 +99,7 @@ public class SmsAuthStrategy implements IAuthStrategy { log.info("登录用户:{} 已被停用.", phonenumber); throw new UserException("user.blocked", phonenumber); } - return userMapper.selectUserByPhonenumber(phonenumber); + return user; }); } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java index a12386e94..66941651c 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java @@ -7,7 +7,6 @@ import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.http.HttpUtil; import cn.hutool.http.Method; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhyd.oauth.model.AuthResponse; @@ -23,8 +22,7 @@ import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.social.config.properties.SocialProperties; import org.dromara.common.social.utils.SocialUtils; import org.dromara.common.tenant.helper.TenantHelper; -import org.dromara.system.domain.SysClient; -import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysSocialVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.mapper.SysUserMapper; @@ -59,7 +57,7 @@ public class SocialAuthStrategy implements IAuthStrategy { * @param client 客户端信息 */ @Override - public LoginVo login(String body, SysClient client) { + public LoginVo login(String body, SysClientVo client) { SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class); ValidatorUtils.validate(loginBody); AuthResponse response = SocialUtils.loginAuth( @@ -83,11 +81,16 @@ public class SocialAuthStrategy implements IAuthStrategy { if (CollUtil.isEmpty(list)) { throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!"); } - Optional opt = list.stream().filter(x -> x.getTenantId().equals(loginBody.getTenantId())).findAny(); - if (opt.isEmpty()) { - throw new ServiceException("对不起,你没有权限登录当前租户!"); + SysSocialVo social; + if (TenantHelper.isEnable()) { + Optional opt = list.stream().filter(x -> x.getTenantId().equals(loginBody.getTenantId())).findAny(); + if (opt.isEmpty()) { + throw new ServiceException("对不起,你没有权限登录当前租户!"); + } + social = opt.get(); + } else { + social = list.get(0); } - SysSocialVo social = opt.get(); // 查找用户 SysUserVo user = loadUser(social.getTenantId(), social.getUserId()); @@ -114,9 +117,7 @@ public class SocialAuthStrategy implements IAuthStrategy { private SysUserVo loadUser(String tenantId, Long userId) { return TenantHelper.dynamic(tenantId, () -> { - SysUser user = userMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getUserName, SysUser::getStatus) - .eq(SysUser::getUserId, userId)); + SysUserVo user = userMapper.selectVoById(userId); if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", ""); throw new UserException("user.not.exists", ""); @@ -124,7 +125,7 @@ public class SocialAuthStrategy implements IAuthStrategy { log.info("登录用户:{} 已被停用.", ""); throw new UserException("user.blocked", ""); } - return userMapper.selectUserByUserName(user.getUserName()); + return user; }); } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java index e5aef1fad..ccab778cf 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java @@ -12,6 +12,7 @@ import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.system.domain.SysClient; +import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.web.domain.vo.LoginVo; import org.dromara.web.service.IAuthStrategy; @@ -31,7 +32,7 @@ public class XcxAuthStrategy implements IAuthStrategy { private final SysLoginService loginService; @Override - public LoginVo login(String body, SysClient client) { + public LoginVo login(String body, SysClientVo client) { XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class); ValidatorUtils.validate(loginBody); // xcxCode 为 小程序调用 wx.login 授权后获取 diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 7065d893c..d41cdba49 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -24,6 +24,20 @@ powerjob: server-address: 127.0.0.1:7700 store-strategy: disk +--- # easy-retry 配置 +easy-retry: + enabled: true + # 需要在EasyRetry后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 + group-name: "ruoyi_group" + # EasyRetry接入验证令牌 详见 script/sql/easy_retry.sql `er_group_config` 表 + token: "ER_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" + server: + host: 127.0.0.1 + port: 1788 + # 详见 script/sql/easy_retry.sql `er_namespace` 表 + namespace: ${spring.profiles.active} + + --- # 数据源配置 spring: datasource: @@ -149,36 +163,40 @@ mail: connectionTimeout: 0 --- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商 -# https://wind.kim/doc/start 文档地址 各个厂商可同时使用 +# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用 sms: - # 阿里云 dysmsapi.aliyuncs.com - alibaba: - #请求地址 默认为 dysmsapi.aliyuncs.com 如无特殊改变可以不用设置 - requestUrl: dysmsapi.aliyuncs.com - #阿里云的accessKey - accessKeyId: xxxxxxx - #阿里云的accessKeySecret - accessKeySecret: xxxxxxx - #短信签名 - signature: 测试 - tencent: - #请求地址默认为 sms.tencentcloudapi.com 如无特殊改变可不用设置 - requestUrl: sms.tencentcloudapi.com - #腾讯云的accessKey - accessKeyId: xxxxxxx - #腾讯云的accessKeySecret - accessKeySecret: xxxxxxx - #短信签名 - signature: 测试 - #短信sdkAppId - sdkAppId: appid - #地域信息默认为 ap-guangzhou 如无特殊改变可不用设置 - territory: ap-guangzhou + # 配置源类型用于标定配置来源(interface,yaml) + config-type: yaml + # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制 + restricted: true + # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效 + minute-max: 1 + # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效 + account-max: 30 + # 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中 + blends: + # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可 + # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户 + config1: + # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: alibaba + # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。 + access-key-id: 您的accessKey + # 称为accessSecret有些称之为apiSecret + access-key-secret: 您的accessKeySecret + signature: 您的短信签名 + sdk-app-id: 您的sdkAppId + config2: + # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: tencent + access-key-id: 您的accessKey + access-key-secret: 您的accessKeySecret + signature: 您的短信签名 + sdk-app-id: 您的sdkAppId --- # 三方授权 justauth: - enabled: true # 前端外网访问地址 address: http://localhost:80 type: @@ -189,6 +207,13 @@ justauth: client-id: 876892492581044224 client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8 redirect-uri: ${justauth.address}/social-callback?source=maxkey + topiam: + # topiam 服务器地址 + server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol + client-id: 449c4*********937************759 + client-secret: ac7***********1e0************28d + redirect-uri: ${justauth.address}/social-callback?source=topiam + scopes: [openid, email, phone, profile] qq: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index 782a680a1..3545a4c4e 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -27,6 +27,19 @@ powerjob: server-address: 127.0.0.1:7700 store-strategy: disk +--- # easy-retry 配置 +easy-retry: + enabled: false + # 需要在EasyRetry后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 + group-name: "ruoyi_group" + # EasyRetry接入验证令牌 详见 script/sql/easy_retry.sql `er_group_config` 表 + token: "ER_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" + server: + host: 127.0.0.1 + port: 1788 + # 详见 script/sql/easy_retry.sql `er_namespace` 表 + namespace: ${spring.profiles.active} + --- # 数据源配置 spring: datasource: @@ -46,7 +59,7 @@ spring: driverClassName: com.mysql.cj.jdbc.Driver # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) - url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true username: root password: root # 从库数据源 @@ -54,7 +67,7 @@ spring: lazy: true type: ${spring.datasource.type} driverClassName: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true username: password: # oracle: @@ -152,35 +165,39 @@ mail: connectionTimeout: 0 --- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商 -# https://wind.kim/doc/start 文档地址 各个厂商可同时使用 +# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用 sms: - # 阿里云 dysmsapi.aliyuncs.com - alibaba: - #请求地址 默认为 dysmsapi.aliyuncs.com 如无特殊改变可以不用设置 - requestUrl: dysmsapi.aliyuncs.com - #阿里云的accessKey - accessKeyId: xxxxxxx - #阿里云的accessKeySecret - accessKeySecret: xxxxxxx - #短信签名 - signature: 测试 - tencent: - #请求地址默认为 sms.tencentcloudapi.com 如无特殊改变可不用设置 - requestUrl: sms.tencentcloudapi.com - #腾讯云的accessKey - accessKeyId: xxxxxxx - #腾讯云的accessKeySecret - accessKeySecret: xxxxxxx - #短信签名 - signature: 测试 - #短信sdkAppId - sdkAppId: appid - #地域信息默认为 ap-guangzhou 如无特殊改变可不用设置 - territory: ap-guangzhou + # 配置源类型用于标定配置来源(interface,yaml) + config-type: yaml + # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制 + restricted: true + # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效 + minute-max: 1 + # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效 + account-max: 30 + # 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中 + blends: + # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可 + # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户 + config1: + # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: alibaba + # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。 + access-key-id: 您的accessKey + # 称为accessSecret有些称之为apiSecret + access-key-secret: 您的accessKeySecret + signature: 您的短信签名 + sdk-app-id: 您的sdkAppId + config2: + # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: tencent + access-key-id: 您的accessKey + access-key-secret: 您的accessKeySecret + signature: 您的短信签名 + sdk-app-id: 您的sdkAppId --- # 三方授权 justauth: - enabled: true # 前端外网访问地址 address: http://localhost:80 type: @@ -191,6 +208,13 @@ justauth: client-id: 876892492581044224 client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8 redirect-uri: ${justauth.address}/social-callback?source=maxkey + topiam: + # topiam 服务器地址 + server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol + client-id: 449c4*********937************759 + client-secret: ac7***********1e0************28d + redirect-uri: ${justauth.address}/social-callback?source=topiam + scopes: [ openid, email, phone, profile ] qq: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 599886c13..448a2ef32 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -5,7 +5,7 @@ ruoyi: # 版本 version: ${revision} # 版权年份 - copyrightYear: 2023 + copyrightYear: 2024 captcha: enable: true @@ -47,6 +47,7 @@ logging: org.dromara: @logging.level@ org.springframework: warn tech.powerjob.worker.background: warn + org.mybatis.spring.mapper: error config: classpath:logback-plus.xml # 用户配置 @@ -61,6 +62,10 @@ user: spring: application: name: ${ruoyi.name} + threads: + # 开启虚拟线程 仅jdk21可用 + virtual: + enabled: false # 资源信息 messages: # 国际化资源文件路径 @@ -118,6 +123,9 @@ security: # actuator 监控配置 - /actuator - /actuator/** + # EasyRetry Job 分派 @see com.aizuda.easy.retry.client.job.core.client.JobEndPoint + - /job/dispatch/v1 + - /job/stop/v1 # 多租户配置 tenant: @@ -138,8 +146,7 @@ tenant: # MyBatisPlus配置 # https://baomidou.com/config/ mybatis-plus: - # 不支持多包, 如有需要可在注解配置 或 提升扫包等级 - # 例如 com.**.**.mapper + # 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper mapperPackage: org.dromara.**.mapper # 对应的 XML 文件位置 mapperLocations: classpath*:mapper/**/*Mapper.xml @@ -226,6 +233,7 @@ xss: urlPatterns: /system/*,/monitor/*,/tool/* # 全局线程池相关配置 +# 如使用JDK21请直接使用虚拟线程 不要开启此配置 thread-pool: # 是否开启线程池 enabled: false @@ -261,3 +269,21 @@ websocket: path: /resource/websocket # 设置访问源地址 allowedOrigins: '*' + +--- #flowable配置 +flowable: + async-executor-activate: false #关闭定时任务JOB + # 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。 + database-schema-update: true + activity-font-name: 宋体 + label-font-name: 宋体 + annotation-font-name: 宋体 + # 关闭各个模块生成表,目前只使用工作流基础表 + idm: + enabled: false + cmmn: + enabled: false + dmn: + enabled: false + app: + enabled: false diff --git a/ruoyi-admin/src/main/resources/ip2region.xdb b/ruoyi-admin/src/main/resources/ip2region.xdb index 31f96a1fb..7052c0571 100644 Binary files a/ruoyi-admin/src/main/resources/ip2region.xdb and b/ruoyi-admin/src/main/resources/ip2region.xdb differ diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml index 004b035de..4a2dc1ceb 100644 --- a/ruoyi-common/ruoyi-common-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-bom/pom.xml @@ -14,7 +14,7 @@ - 5.1.2 + 5.2.0-SNAPSHOT diff --git a/ruoyi-common/ruoyi-common-core/pom.xml b/ruoyi-common/ruoyi-common-core/pom.xml index ad37e90db..5925c9b3c 100644 --- a/ruoyi-common/ruoyi-common-core/pom.xml +++ b/ruoyi-common/ruoyi-common-core/pom.xml @@ -94,6 +94,11 @@ ip2region + + com.alibaba + transmittable-thread-local + + diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java index 07500ba1b..d12008706 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java @@ -2,6 +2,7 @@ package org.dromara.common.core.config; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.scheduling.annotation.EnableAsync; /** * 程序注解配置 @@ -11,6 +12,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy; @AutoConfiguration // 表示通过aop框架暴露该代理对象,AopContext能够访问 @EnableAspectJAutoProxy(exposeProxy = true) +@EnableAsync(proxyTargetClass = true) public class ApplicationConfig { } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java index 9a32afe55..cd01e33d5 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java @@ -5,18 +5,19 @@ import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.SpringUtils; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.core.task.VirtualThreadTaskExecutor; import org.springframework.scheduling.annotation.AsyncConfigurer; -import org.springframework.scheduling.annotation.EnableAsync; import java.util.Arrays; import java.util.concurrent.Executor; /** * 异步配置 + * + * 如果未使用虚拟线程则生效 * * @author Lion Li */ -@EnableAsync(proxyTargetClass = true) @AutoConfiguration public class AsyncConfig implements AsyncConfigurer { @@ -25,6 +26,9 @@ public class AsyncConfig implements AsyncConfigurer { */ @Override public Executor getAsyncExecutor() { + if(SpringUtils.isVirtual()) { + return new VirtualThreadTaskExecutor("async-"); + } return SpringUtils.getBean("scheduledExecutorService"); } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java index e59277aae..28ba17739 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java @@ -35,6 +35,11 @@ public interface CacheNames { */ String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d"; + /** + * 客户端 + */ + String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d"; + /** * 用户账户 */ diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java new file mode 100644 index 000000000..77eed8c09 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java @@ -0,0 +1,54 @@ +package org.dromara.common.core.constant; + +import cn.hutool.core.lang.RegexPool; + +/** + * 常用正则表达式字符串 + * + * 常用正则表达式集合,更多正则见: https://any86.github.io/any-rule/ + * + * @author Feng + */ +public interface RegexConstants extends RegexPool { + + /** + * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) + */ + String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$"; + + /** + * 权限标识必须符合 tool:build:list 格式,或者空字符串 + */ + String PERMISSION_STRING = "^(|^[a-zA-Z0-9_]+:[a-zA-Z0-9_]+:[a-zA-Z0-9_]+)$"; + + /** + * 身份证号码(后6位) + */ + String ID_CARD_LAST_6 = "^(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$"; + + /** + * QQ号码 + */ + String QQ_NUMBER = "^[1-9][0-9]\\d{4,9}$"; + + /** + * 邮政编码 + */ + String POSTAL_CODE = "^[1-9]\\d{5}$"; + + /** + * 注册账号 + */ + String ACCOUNT = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$"; + + /** + * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 + */ + String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$"; + + /** + * 通用状态(0表示正常,1表示停用) + */ + String STATUS = "^[01]$"; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java index 4fb097a88..e9dc6ec9b 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java @@ -1,9 +1,6 @@ package org.dromara.common.core.exception; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; +import lombok.*; import java.io.Serial; @@ -45,19 +42,11 @@ public final class ServiceException extends RuntimeException { this.code = code; } - public String getDetailMessage() { - return detailMessage; - } - @Override public String getMessage() { return message; } - public Integer getCode() { - return code; - } - public ServiceException setMessage(String message) { this.message = message; return this; diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java new file mode 100644 index 000000000..fd907d2c2 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java @@ -0,0 +1,52 @@ +package org.dromara.common.core.factory; + +import cn.hutool.core.lang.PatternPool; +import org.dromara.common.core.constant.RegexConstants; + +import java.util.regex.Pattern; + +/** + * 正则表达式模式池工厂 + * 初始化的时候将正则表达式加入缓存池当中 + * 提高正则表达式的性能,避免重复编译相同的正则表达式 + * + * @author 21001 + */ +public class RegexPatternPoolFactory extends PatternPool { + + /** + * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) + */ + public static final Pattern DICTIONARY_TYPE = get(RegexConstants.DICTIONARY_TYPE); + + /** + * 身份证号码(后6位) + */ + public static final Pattern ID_CARD_LAST_6 = get(RegexConstants.ID_CARD_LAST_6); + + /** + * QQ号码 + */ + public static final Pattern QQ_NUMBER = get(RegexConstants.QQ_NUMBER); + + /** + * 邮政编码 + */ + public static final Pattern POSTAL_CODE = get(RegexConstants.POSTAL_CODE); + + /** + * 注册账号 + */ + public static final Pattern ACCOUNT = get(RegexConstants.ACCOUNT); + + /** + * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 + */ + public static final Pattern PASSWORD = get(RegexConstants.PASSWORD); + + /** + * 通用状态(0表示正常,1表示停用) + */ + public static final Pattern STATUS = get(RegexConstants.STATUS); + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java index d6b312a64..f8f155a7b 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java @@ -23,4 +23,20 @@ public interface UserService { */ String selectNicknameById(Long userId); + /** + * 通过用户ID查询用户手机号 + * + * @param userId 用户id + * @return 用户手机号 + */ + String selectPhonenumberById(Long userId); + + /** + * 通过用户ID查询用户邮箱 + * + * @param userId 用户id + * @return 用户邮箱 + */ + String selectEmailById(Long userId); + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java index ab5053931..e58c394ac 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java @@ -3,7 +3,9 @@ package org.dromara.common.core.utils; import cn.hutool.extra.spring.SpringUtil; import org.springframework.aop.framework.AopContext; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.thread.Threading; import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; /** @@ -59,4 +61,8 @@ public final class SpringUtils extends SpringUtil { return getApplicationContext(); } + public static boolean isVirtual() { + return Threading.VIRTUAL.isActive(getBean(Environment.class)); + } + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java index 5e4db50e3..dd6ebb119 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java @@ -22,6 +22,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils { public static final String SEPARATOR = ","; + public static final String SLASH = "/"; + /** * 获取参数不为空值 * diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java new file mode 100644 index 000000000..b8b12d43c --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java @@ -0,0 +1,30 @@ +package org.dromara.common.core.utils.regex; + + +import cn.hutool.core.util.ReUtil; +import org.dromara.common.core.constant.RegexConstants; + +/** + * 正则相关工具类 + * + * @author Feng + */ +public final class RegexUtils extends ReUtil { + + /** + * 从输入字符串中提取匹配的部分,如果没有匹配则返回默认值 + * + * @param input 要提取的输入字符串 + * @param regex 用于匹配的正则表达式,可以使用 {@link RegexConstants} 中定义的常量 + * @param defaultInput 如果没有匹配时返回的默认值 + * @return 如果找到匹配的部分,则返回匹配的部分,否则返回默认值 + */ + public static String extractFromString(String input, String regex, String defaultInput) { + try { + return ReUtil.get(regex, input, 1); + } catch (Exception e) { + return defaultInput; + } + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java new file mode 100644 index 000000000..c0dda2020 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java @@ -0,0 +1,105 @@ +package org.dromara.common.core.utils.regex; + +import cn.hutool.core.exceptions.ValidateException; +import cn.hutool.core.lang.Validator; +import org.dromara.common.core.factory.RegexPatternPoolFactory; + +import java.util.regex.Pattern; + +/** + * 正则字段校验器 + * 主要验证字段非空、是否为满足指定格式等 + * + * @author Feng + */ +public class RegexValidator extends Validator { + + /** + * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) + */ + public static final Pattern DICTIONARY_TYPE = RegexPatternPoolFactory.DICTIONARY_TYPE; + + /** + * 身份证号码(后6位) + */ + public static final Pattern ID_CARD_LAST_6 = RegexPatternPoolFactory.ID_CARD_LAST_6; + + /** + * QQ号码 + */ + public static final Pattern QQ_NUMBER = RegexPatternPoolFactory.QQ_NUMBER; + + /** + * 邮政编码 + */ + public static final Pattern POSTAL_CODE = RegexPatternPoolFactory.POSTAL_CODE; + + /** + * 注册账号 + */ + public static final Pattern ACCOUNT = RegexPatternPoolFactory.ACCOUNT; + + /** + * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 + */ + public static final Pattern PASSWORD = RegexPatternPoolFactory.PASSWORD; + + /** + * 通用状态(0表示正常,1表示停用) + */ + public static final Pattern STATUS = RegexPatternPoolFactory.STATUS; + + + /** + * 检查输入的账号是否匹配预定义的规则 + * + * @param value 要验证的账号 + * @return 如果账号符合规则,返回 true;否则,返回 false。 + */ + public static boolean isAccount(CharSequence value) { + return isMatchRegex(ACCOUNT, value); + } + + /** + * 验证输入的账号是否符合规则,如果不符合,则抛出 ValidateException 异常 + * + * @param value 要验证的账号 + * @param errorMsg 验证失败时抛出的异常消息 + * @param CharSequence 的子类型 + * @return 如果验证通过,返回输入的账号 + * @throws ValidateException 如果验证失败 + */ + public static T validateAccount(T value, String errorMsg) throws ValidateException { + if (!isAccount(value)) { + throw new ValidateException(errorMsg); + } + return value; + } + + /** + * 检查输入的状态是否匹配预定义的规则 + * + * @param value 要验证的状态 + * @return 如果状态符合规则,返回 true;否则,返回 false。 + */ + public static boolean isStatus(CharSequence value) { + return isMatchRegex(STATUS, value); + } + + /** + * 验证输入的状态是否符合规则,如果不符合,则抛出 ValidateException 异常 + * + * @param value 要验证的状态 + * @param errorMsg 验证失败时抛出的异常消息 + * @param CharSequence 的子类型 + * @return 如果验证通过,返回输入的状态 + * @throws ValidateException 如果验证失败 + */ + public static T validateStatus(T value, String errorMsg) throws ValidateException { + if (!isStatus(value)) { + throw new ValidateException(errorMsg); + } + return value; + } + +} diff --git a/ruoyi-common/ruoyi-common-encrypt/pom.xml b/ruoyi-common/ruoyi-common-encrypt/pom.xml index df3222bee..ed4910ef0 100644 --- a/ruoyi-common/ruoyi-common-encrypt/pom.xml +++ b/ruoyi-common/ruoyi-common-encrypt/pom.xml @@ -22,11 +22,6 @@ ruoyi-common-core - - org.mybatis.spring.boot - mybatis-spring-boot-starter - - org.bouncycastle bcprov-jdk15to18 @@ -42,6 +37,18 @@ spring-webmvc + + com.baomidou + mybatis-plus-spring-boot3-starter + true + + + org.mybatis + mybatis-spring + + + + diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java index e988a3a2c..fbc4e52de 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java @@ -1,5 +1,8 @@ package org.dromara.common.encrypt.config; +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties; +import lombok.extern.slf4j.Slf4j; import org.dromara.common.encrypt.core.EncryptorManager; import org.dromara.common.encrypt.interceptor.MybatisDecryptInterceptor; import org.dromara.common.encrypt.interceptor.MybatisEncryptInterceptor; @@ -16,17 +19,18 @@ import org.springframework.context.annotation.Bean; * @author 老马 * @version 4.6.0 */ -@AutoConfiguration +@AutoConfiguration(after = MybatisPlusAutoConfiguration.class) @EnableConfigurationProperties(EncryptorProperties.class) @ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true") +@Slf4j public class EncryptorAutoConfiguration { @Autowired private EncryptorProperties properties; @Bean - public EncryptorManager encryptorManager() { - return new EncryptorManager(); + public EncryptorManager encryptorManager(MybatisPlusProperties mybatisPlusProperties) { + return new EncryptorManager(mybatisPlusProperties.getTypeAliasesPackage()); } @Bean @@ -38,4 +42,8 @@ public class EncryptorAutoConfiguration { public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) { return new MybatisDecryptInterceptor(encryptorManager, properties); } + } + + + diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java index 498b4b85b..a6d3cf9ce 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java @@ -1,14 +1,23 @@ package org.dromara.common.encrypt.core; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; +import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.io.Resources; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.encrypt.annotation.EncryptField; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.ClassMetadata; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.util.ClassUtils; import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -19,6 +28,7 @@ import java.util.stream.Collectors; * @version 4.6.0 */ @Slf4j +@NoArgsConstructor public class EncryptorManager { /** @@ -31,25 +41,24 @@ public class EncryptorManager { */ Map, Set> fieldCache = new ConcurrentHashMap<>(); + /** + * 构造方法传入类加密字段缓存 + * + * @param typeAliasesPackage 实体类包 + */ + public EncryptorManager(String typeAliasesPackage) { + scanEncryptClasses(typeAliasesPackage); + } + + /** * 获取类加密字段缓存 */ public Set getFieldCache(Class> sourceClazz) { - return fieldCache.computeIfAbsent(sourceClazz, clazz -> { - Set fieldSet = new HashSet<>(); - while (clazz != null) { - Field[] fields = clazz.getDeclaredFields(); - fieldSet.addAll(Arrays.asList(fields)); - clazz = clazz.getSuperclass(); - } - fieldSet = fieldSet.stream().filter(field -> - field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class) - .collect(Collectors.toSet()); - for (Field field : fieldSet) { - field.setAccessible(true); - } - return fieldSet; - }); + if (ObjectUtil.isNotNull(fieldCache)) { + return fieldCache.get(sourceClazz); + } + return null; } /** @@ -97,4 +106,53 @@ public class EncryptorManager { return encryptor.decrypt(value); } + /** + * 通过 typeAliasesPackage 设置的扫描包 扫描缓存实体 + */ + private void scanEncryptClasses(String typeAliasesPackage) { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); + String[] packagePatternArray = StringUtils.splitPreserveAllTokens(typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); + String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; + try { + for (String packagePattern : packagePatternArray) { + String path = ClassUtils.convertClassNameToResourcePath(packagePattern); + Resource[] resources = resolver.getResources(classpath + path + "/*.class"); + for (Resource resource : resources) { + ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata(); + Class> clazz = Resources.classForName(classMetadata.getClassName()); + Set encryptFieldSet = getEncryptFieldSetFromClazz(clazz); + if (CollUtil.isNotEmpty(encryptFieldSet)) { + fieldCache.put(clazz, encryptFieldSet); + } + } + } + } catch (Exception e) { + log.error("初始化数据安全缓存时出错:{}", e.getMessage()); + } + } + + /** + * 获得一个类的加密字段集合 + */ + private Set getEncryptFieldSetFromClazz(Class> clazz) { + Set fieldSet = new HashSet<>(); + // 判断clazz如果是接口,内部类,匿名类就直接返回 + if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) { + return fieldSet; + } + while (clazz != null) { + Field[] fields = clazz.getDeclaredFields(); + fieldSet.addAll(Arrays.asList(fields)); + clazz = clazz.getSuperclass(); + } + fieldSet = fieldSet.stream().filter(field -> + field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class) + .collect(Collectors.toSet()); + for (Field field : fieldSet) { + field.setAccessible(true); + } + return fieldSet; + } + } diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java index b43881a22..98351321d 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java @@ -11,14 +11,12 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.encrypt.annotation.ApiEncrypt; import org.dromara.common.encrypt.properties.ApiDecryptProperties; import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.io.IOException; -import java.io.PrintWriter; /** @@ -37,42 +35,38 @@ public class CryptoFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest servletRequest = (HttpServletRequest) request; HttpServletResponse servletResponse = (HttpServletResponse) response; - - boolean responseFlag = false; + // 获取加密注解 + ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest); + boolean responseFlag = apiEncrypt != null && apiEncrypt.response(); ServletRequest requestWrapper = null; ServletResponse responseWrapper = null; EncryptResponseBodyWrapper responseBodyWrapper = null; - // 是否为 json 请求 - if (StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) { - // 是否为 put 或者 post 请求 - if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) { - // 是否存在加密标头 - String headerValue = servletRequest.getHeader(properties.getHeaderFlag()); - // 获取加密注解 - ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest); - responseFlag = apiEncrypt != null && apiEncrypt.response(); - if (StringUtils.isNotBlank(headerValue)) { - // 请求解密 - requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag()); - } else { - // 是否有注解,有就报错,没有放行 - if (ObjectUtil.isNotNull(apiEncrypt)) { - HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class); - exceptionResolver.resolveException( - servletRequest, servletResponse, null, - new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN)); - return; - } - } - // 判断是否响应加密 - if (responseFlag) { - responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse); - responseWrapper = responseBodyWrapper; + // 是否为 put 或者 post 请求 + if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) { + // 是否存在加密标头 + String headerValue = servletRequest.getHeader(properties.getHeaderFlag()); + if (StringUtils.isNotBlank(headerValue)) { + // 请求解密 + requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag()); + } else { + // 是否有注解,有就报错,没有放行 + if (ObjectUtil.isNotNull(apiEncrypt)) { + HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class); + exceptionResolver.resolveException( + servletRequest, servletResponse, null, + new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN)); + return; } } } + // 判断是否响应加密 + if (responseFlag) { + responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse); + responseWrapper = responseBodyWrapper; + } + chain.doFilter( ObjectUtil.defaultIfNull(requestWrapper, request), ObjectUtil.defaultIfNull(responseWrapper, response)); diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java index 7c2508f86..460aa360e 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java @@ -73,7 +73,11 @@ public class MybatisDecryptInterceptor implements Interceptor { list.forEach(this::decryptHandler); return; } + // 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错) Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); + if(ObjectUtil.isNull(fields)){ + return; + } try { for (Field field : fields) { field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field)); diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java index 152f7db40..bcc2f4c9f 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java @@ -82,7 +82,11 @@ public class MybatisEncryptInterceptor implements Interceptor { list.forEach(this::encryptHandler); return; } + // 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错) Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); + if(ObjectUtil.isNull(fields)){ + return; + } try { for (Field field : fields) { field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field)); diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java index bbdaaa14d..6b9211b53 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java @@ -21,4 +21,9 @@ public @interface CellMerge { */ int index() default -1; + /** + * 合并需要依赖的其他字段名称 + */ + String[] mergeBy() default {}; + } diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java index bcb5be767..7c0a48b9a 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java @@ -1,8 +1,12 @@ package org.dromara.common.excel.core; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.handler.WorkbookWriteHandler; +import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext; import com.alibaba.excel.write.merge.AbstractMergeStrategy; import lombok.AllArgsConstructor; import lombok.Data; @@ -15,10 +19,7 @@ import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.excel.annotation.CellMerge; import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * 列值重复合并策略 @@ -26,7 +27,7 @@ import java.util.Map; * @author Lion Li */ @Slf4j -public class CellMergeStrategy extends AbstractMergeStrategy { +public class CellMergeStrategy extends AbstractMergeStrategy implements WorkbookWriteHandler { private final List cellList; private final boolean hasTitle; @@ -41,17 +42,28 @@ public class CellMergeStrategy extends AbstractMergeStrategy { @Override protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { - // judge the list is not null - if (CollUtil.isNotEmpty(cellList)) { - // the judge is necessary - if (cell.getRowIndex() == rowIndex && cell.getColumnIndex() == 0) { - for (CellRangeAddress item : cellList) { - sheet.addMergedRegion(item); + //单元格写入了,遍历合并区域,如果该Cell在区域内,但非首行,则清空 + final int rowIndex = cell.getRowIndex(); + if (CollUtil.isNotEmpty(cellList)){ + for (CellRangeAddress cellAddresses : cellList) { + final int firstRow = cellAddresses.getFirstRow(); + if (cellAddresses.isInRange(cell) && rowIndex != firstRow){ + cell.setBlank(); } } } } + @Override + public void afterWorkbookDispose(final WorkbookWriteHandlerContext context) { + //当前表格写完后,统一写入 + if (CollUtil.isNotEmpty(cellList)){ + for (CellRangeAddress item : cellList) { + context.getWriteContext().writeSheetHolder().getSheet().addMergedRegion(item); + } + } + } + @SneakyThrows private List handle(List> list, boolean hasTitle) { List cellList = new ArrayList<>(); @@ -93,35 +105,15 @@ public class CellMergeStrategy extends AbstractMergeStrategy { // 空值跳过不合并 continue; } + if (!cellValue.equals(val)) { - if (i - repeatCell.getCurrent() > 1) { + if ((i - repeatCell.getCurrent() > 1) && isMerge(list, i, field)) { cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); } map.put(field, new RepeatCell(val, i)); - } else if (j == 0) { - if (i == list.size() - 1) { - if (i > repeatCell.getCurrent()) { - cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); - } - } - } else { - // 判断前面的是否合并了 - RepeatCell firstCell = map.get(mergeFields.get(0)); - if (repeatCell.getCurrent() != firstCell.getCurrent()) { - if (i == list.size() - 1) { - if (i > repeatCell.getCurrent()) { - cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); - } - } else if (repeatCell.getCurrent() < firstCell.getCurrent()) { - if (i - repeatCell.getCurrent() > 1) { - cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); - } - map.put(field, new RepeatCell(val, i)); - } - } else if (i == list.size() - 1) { - if (i > repeatCell.getCurrent()) { - cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); - } + } else if (i == list.size() - 1) { + if (i > repeatCell.getCurrent() && isMerge(list, i, field)) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); } } } @@ -130,6 +122,24 @@ public class CellMergeStrategy extends AbstractMergeStrategy { return cellList; } + private boolean isMerge(List> list, int i, Field field) { + boolean isMerge = true; + CellMerge cm = field.getAnnotation(CellMerge.class); + final String[] mergeBy = cm.mergeBy(); + if (StrUtil.isAllNotBlank(mergeBy)) { + //比对当前list(i)和list(i - 1)的各个属性值一一比对 如果全为真 则为真 + for (String fieldName : mergeBy) { + final Object valCurrent = ReflectUtil.getFieldValue(list.get(i), fieldName); + final Object valPre = ReflectUtil.getFieldValue(list.get(i - 1), fieldName); + if (!Objects.equals(valPre, valCurrent)) { + //依赖字段如有任一不等值,则标记为不可合并 + isMerge = false; + } + } + } + return isMerge; + } + @Data @AllArgsConstructor static class RepeatCell { diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java index 3b791ea54..b3f68ed1e 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java @@ -20,6 +20,7 @@ import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.service.DictService; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.annotation.ExcelEnumFormat; @@ -99,15 +100,16 @@ public class ExcelDownHandler implements SheetWriteHandler { ExcelDictFormat format = field.getDeclaredAnnotation(ExcelDictFormat.class); String dictType = format.dictType(); String converterExp = format.readConverterExp(); - if (StrUtil.isNotBlank(dictType)) { + if (StringUtils.isNotBlank(dictType)) { // 如果传递了字典名,则依据字典建立下拉 Collection values = Optional.ofNullable(dictService.getAllDictByDictType(dictType)) .orElseThrow(() -> new ServiceException(String.format("字典 %s 不存在", dictType))) .values(); options = new ArrayList<>(values); - } else if (StrUtil.isNotBlank(converterExp)) { + } else if (StringUtils.isNotBlank(converterExp)) { // 如果指定了确切的值,则直接解析确切的值 - options = StrUtil.split(converterExp, format.separator(), true, true); + List strList = StringUtils.splitList(converterExp, format.separator()); + options = StreamUtils.toList(strList, s -> StringUtils.split(s, "=")[1]); } } else if (field.isAnnotationPresent(ExcelEnumFormat.class)) { // 否则如果指定了@ExcelEnumFormat,则使用枚举的逻辑 diff --git a/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java b/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java index 016fe0fb1..5a27e9189 100644 --- a/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java +++ b/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java @@ -4,6 +4,13 @@ import cn.dev33.satoken.SaManager; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; 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.domain.R; import org.dromara.common.core.exception.ServiceException; @@ -13,13 +20,6 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.idempotent.annotation.RepeatSubmit; import org.dromara.common.json.utils.JsonUtils; 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.web.multipart.MultipartFile; @@ -127,7 +127,7 @@ public class RepeatSubmitAspect { public boolean isFilterObject(final Object o) { Class> clazz = o.getClass(); if (clazz.isArray()) { - return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + return MultipartFile.class.isAssignableFrom(clazz.getComponentType()); } else if (Collection.class.isAssignableFrom(clazz)) { Collection collection = (Collection) o; for (Object value : collection) { diff --git a/ruoyi-common/ruoyi-common-job/pom.xml b/ruoyi-common/ruoyi-common-job/pom.xml index 1a6e729b4..47bfc432e 100644 --- a/ruoyi-common/ruoyi-common-job/pom.xml +++ b/ruoyi-common/ruoyi-common-job/pom.xml @@ -38,6 +38,20 @@ powerjob-official-processors + + + com.aizuda + easy-retry-client-starter + + + com.aizuda + easy-retry-client-core + + + com.aizuda + easy-retry-client-job-core + + org.projectlombok lombok diff --git a/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/EasyRetryConfig.java b/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/EasyRetryConfig.java new file mode 100644 index 000000000..13f36dc32 --- /dev/null +++ b/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/EasyRetryConfig.java @@ -0,0 +1,37 @@ +package org.dromara.common.job.config; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import com.aizuda.easy.retry.client.common.appender.EasyRetryLogbackAppender; +import com.aizuda.easy.retry.client.common.event.EasyRetryStartingEvent; +import com.aizuda.easy.retry.client.starter.EnableEasyRetry; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * 启动定时任务 + * + * @author dhb52 + * @since 2024/3/12 + */ +@AutoConfiguration +@ConditionalOnProperty(prefix = "easy-retry", name = "enabled", havingValue = "true") +@EnableScheduling +@EnableEasyRetry(group = "${easy-retry.group-name}") +public class EasyRetryConfig { + + @EventListener(EasyRetryStartingEvent.class) + public void onStarting(EasyRetryStartingEvent event) { + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + EasyRetryLogbackAppender ca = new EasyRetryLogbackAppender<>(); + ca.setName("easy_log_appender"); + ca.start(); + Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME); + rootLogger.addAppender(ca); + } + +} diff --git a/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/PowerJobConfig.java b/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/PowerJobConfig.java index 67208c0e5..9835c7f6e 100644 --- a/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/PowerJobConfig.java +++ b/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/PowerJobConfig.java @@ -1,8 +1,8 @@ package org.dromara.common.job.config; +import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import tech.powerjob.worker.PowerJobWorker; @@ -11,7 +11,7 @@ import tech.powerjob.worker.PowerJobWorker; * @author yhan219 * @since 2023/6/2 */ -@Configuration +@AutoConfiguration @ConditionalOnBean(PowerJobWorker.class) @ConditionalOnProperty(prefix = "powerjob.worker", name = "enabled", havingValue = "true") @EnableScheduling diff --git a/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..2e3dc3209 --- /dev/null +++ b/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +org.dromara.common.job.config.PowerJobConfig +org.dromara.common.job.config.EasyRetryConfig diff --git a/ruoyi-common/ruoyi-common-log/pom.xml b/ruoyi-common/ruoyi-common-log/pom.xml index 28bd5304a..1e2b33b44 100644 --- a/ruoyi-common/ruoyi-common-log/pom.xml +++ b/ruoyi-common/ruoyi-common-log/pom.xml @@ -27,11 +27,6 @@ ruoyi-common-json - - com.alibaba - transmittable-thread-local - - diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java index e98f4f7fa..87240729c 100644 --- a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java +++ b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java @@ -4,16 +4,6 @@ import cn.hutool.core.lang.Dict; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; -import com.alibaba.ttl.TransmittableThreadLocal; -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.HttpServletResponse; 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.Aspect; 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.http.HttpMethod; import org.springframework.validation.BindingResult; @@ -49,9 +48,9 @@ public class LogAspect { /** - * 计算操作消耗时间 + * 计时 key */ - private static final ThreadLocal TIME_THREADLOCAL = new TransmittableThreadLocal<>(); + private static final ThreadLocal KEY_CACHE = new ThreadLocal<>(); /** * 处理请求前执行 @@ -59,7 +58,7 @@ public class LogAspect { @Before(value = "@annotation(controllerLog)") public void boBefore(JoinPoint joinPoint, Log controllerLog) { StopWatch stopWatch = new StopWatch(); - TIME_THREADLOCAL.set(stopWatch); + KEY_CACHE.set(stopWatch); stopWatch.start(); } @@ -112,7 +111,7 @@ public class LogAspect { // 处理设置注解上的参数 getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); // 设置消耗时间 - StopWatch stopWatch = TIME_THREADLOCAL.get(); + StopWatch stopWatch = KEY_CACHE.get(); stopWatch.stop(); operLog.setCostTime(stopWatch.getTime()); // 发布事件保存数据库 @@ -122,7 +121,7 @@ public class LogAspect { log.error("异常信息:{}", exp.getMessage()); exp.printStackTrace(); } finally { - TIME_THREADLOCAL.remove(); + KEY_CACHE.remove(); } } @@ -204,7 +203,7 @@ public class LogAspect { public boolean isFilterObject(final Object o) { Class> clazz = o.getClass(); if (clazz.isArray()) { - return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + return MultipartFile.class.isAssignableFrom(clazz.getComponentType()); } else if (Collection.class.isAssignableFrom(clazz)) { Collection collection = (Collection) o; for (Object value : collection) { diff --git a/ruoyi-common/ruoyi-common-mybatis/pom.xml b/ruoyi-common/ruoyi-common-mybatis/pom.xml index 07b486c92..44b11ef5e 100644 --- a/ruoyi-common/ruoyi-common-mybatis/pom.xml +++ b/ruoyi-common/ruoyi-common-mybatis/pom.xml @@ -39,7 +39,7 @@ com.baomidou - mybatis-plus-boot-starter + mybatis-plus-spring-boot3-starter org.mybatis diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java index 1689e3cdf..495daf10a 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java @@ -1,34 +1,29 @@ package org.dromara.common.mybatis.config; import cn.hutool.core.net.NetUtil; -import com.baomidou.mybatisplus.autoconfigure.DdlApplicationRunner; -import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; -import com.baomidou.mybatisplus.extension.ddl.IDdl; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import org.dromara.common.core.factory.YmlPropertySourceFactory; +import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler; import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor; import org.mybatis.spring.annotation.MapperScan; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.beans.BeansException; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; import org.springframework.transaction.annotation.EnableTransactionManagement; -import java.util.List; - /** * mybatis-plus配置类(下方注释有插件介绍) * * @author Lion Li */ @EnableTransactionManagement(proxyTargetClass = true) -@AutoConfiguration(before = MybatisPlusAutoConfiguration.class) @MapperScan("${mybatis-plus.mapperPackage}") @PropertySource(value = "classpath:common-mybatis.yml", factory = YmlPropertySourceFactory.class) public class MybatisPlusConfig { @@ -36,6 +31,12 @@ public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 多租户插件 必须放到第一位 + try { + TenantLineInnerInterceptor tenant = SpringUtils.getBean(TenantLineInnerInterceptor.class); + interceptor.addInnerInterceptor(tenant); + } catch (BeansException ignore) { + } // 数据权限处理 interceptor.addInnerInterceptor(dataPermissionInterceptor()); // 分页插件 @@ -49,7 +50,7 @@ public class MybatisPlusConfig { * 数据权限拦截器 */ public PlusDataPermissionInterceptor dataPermissionInterceptor() { - return new PlusDataPermissionInterceptor(); + return new PlusDataPermissionInterceptor(SpringUtils.getProperty("mybatis-plus.mapperPackage")); } /** @@ -108,9 +109,4 @@ public class MybatisPlusConfig { * https://baomidou.com/pages/2a45ff/ */ - @Bean - public DdlApplicationRunner ddlApplicationRunner(@Autowired(required = false) List ddlList) { - return new DdlApplicationRunner(ddlList); - } - } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java index 9018a791c..08723f608 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java @@ -35,7 +35,6 @@ public interface BaseMapperPlus extends BaseMapper { Log log = LogFactory.getLog(BaseMapperPlus.class); default Class currentVoClass() { - GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class); return (Class) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[1]; } @@ -145,11 +144,22 @@ public interface BaseMapperPlus extends BaseMapper { return selectVoOne(wrapper, this.currentVoClass()); } + default V selectVoOne(Wrapper wrapper, boolean throwEx) { + return selectVoOne(wrapper, this.currentVoClass(), throwEx); + } + /** * 根据 entity 条件,查询一条记录 */ default C selectVoOne(Wrapper wrapper, Class voClass) { - T obj = this.selectOne(wrapper); + return selectVoOne(wrapper, voClass, true); + } + + /** + * 根据 entity 条件,查询一条记录 + */ + default C selectVoOne(Wrapper wrapper, Class voClass, boolean throwEx) { + T obj = this.selectOne(wrapper, throwEx); if (ObjectUtil.isNull(obj)) { return null; } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java index 8ef4a578d..40b7530e5 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java @@ -111,4 +111,8 @@ public class PageQuery implements Serializable { return list; } + public Integer getFirstNum() { + return (pageNum - 1) * pageSize; + } + } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java index 63653de3c..a66908f09 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java @@ -29,16 +29,17 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler { ? baseEntity.getCreateTime() : new Date(); baseEntity.setCreateTime(current); baseEntity.setUpdateTime(current); - LoginUser loginUser = getLoginUser(); - if (ObjectUtil.isNotNull(loginUser)) { - Long userId = ObjectUtil.isNotNull(baseEntity.getCreateBy()) - ? baseEntity.getCreateBy() : loginUser.getUserId(); - // 当前已登录 且 创建人为空 则填充 - baseEntity.setCreateBy(userId); - // 当前已登录 且 更新人为空 则填充 - baseEntity.setUpdateBy(userId); - baseEntity.setCreateDept(ObjectUtil.isNotNull(baseEntity.getCreateDept()) - ? baseEntity.getCreateDept() : loginUser.getDeptId()); + if (ObjectUtil.isNull(baseEntity.getCreateBy())) { + LoginUser loginUser = getLoginUser(); + if (ObjectUtil.isNotNull(loginUser)) { + Long userId = loginUser.getUserId(); + // 当前已登录 且 创建人为空 则填充 + baseEntity.setCreateBy(userId); + // 当前已登录 且 更新人为空 则填充 + baseEntity.setUpdateBy(userId); + baseEntity.setCreateDept(ObjectUtil.isNotNull(baseEntity.getCreateDept()) + ? baseEntity.getCreateDept() : loginUser.getDeptId()); + } } } } catch (Exception e) { @@ -53,11 +54,12 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler { Date current = new Date(); // 更新时间填充(不管为不为空) baseEntity.setUpdateTime(current); - LoginUser loginUser = getLoginUser(); // 当前已登录 更新人填充(不管为不为空) - if (ObjectUtil.isNotNull(loginUser)) { - baseEntity.setUpdateBy(loginUser.getUserId()); + Long userId = LoginHelper.getUserId(); + if (ObjectUtil.isNotNull(userId)) { + baseEntity.setUpdateBy(userId); } + } } catch (Exception e) { throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java index c517fa728..ec3ee0df7 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java @@ -2,6 +2,7 @@ package org.dromara.common.mybatis.handler; import org.dromara.common.core.domain.R; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.StringUtils; import org.mybatis.spring.MyBatisSystemException; import org.springframework.dao.DuplicateKeyException; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -35,7 +36,7 @@ public class MybatisExceptionHandler { public R handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); String message = e.getMessage(); - if ("CannotFindDataSourceException".contains(message)) { + if (StringUtils.contains("CannotFindDataSourceException", message)) { log.error("请求地址'{}', 未找到数据源", requestURI); return R.fail("未找到数据源,请联系管理员确认"); } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java index 6ddaa243c..7d7fd84a9 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java @@ -2,7 +2,6 @@ package org.dromara.common.mybatis.handler; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ObjectUtil; import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.JSQLParserException; @@ -10,6 +9,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.Parenthesis; import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import org.apache.ibatis.io.Resources; import org.dromara.common.core.domain.dto.RoleDTO; import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.exception.ServiceException; @@ -21,16 +21,26 @@ import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.enums.DataScopeType; import org.dromara.common.mybatis.helper.DataPermissionHelper; import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.ClassMetadata; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.expression.BeanResolver; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParserContext; import org.springframework.expression.common.TemplateParserContext; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.util.ClassUtils; import java.lang.reflect.Method; -import java.util.*; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; @@ -58,9 +68,13 @@ public class PlusDataPermissionHandler { */ private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory()); + public PlusDataPermissionHandler(String mapperPackage) { + scanMapperClasses(mapperPackage); + } + public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) { - DataColumn[] dataColumns = findAnnotation(mappedStatementId); + DataPermission dataPermission = getDataPermission(mappedStatementId); LoginUser currentUser = DataPermissionHelper.getVariable("user"); if (ObjectUtil.isNull(currentUser)) { currentUser = LoginHelper.getLoginUser(); @@ -70,7 +84,7 @@ public class PlusDataPermissionHandler { if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) { return where; } - String dataFilterSql = buildDataFilter(dataColumns, isSelect); + String dataFilterSql = buildDataFilter(dataPermission.value(), isSelect); if (StringUtils.isBlank(dataFilterSql)) { return where; } @@ -144,43 +158,64 @@ public class PlusDataPermissionHandler { return ""; } - public DataColumn[] findAnnotation(String mappedStatementId) { - StringBuilder sb = new StringBuilder(mappedStatementId); - int index = sb.lastIndexOf("."); - String clazzName = sb.substring(0, index); - String methodName = sb.substring(index + 1, sb.length()); - Class> clazz; + /** + * 通过 mapperPackage 设置的扫描包 扫描缓存有注解的方法与类 + */ + private void scanMapperClasses(String mapperPackage) { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); + String[] packagePatternArray = StringUtils.splitPreserveAllTokens(mapperPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); + String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; try { - clazz = ClassUtil.loadClass(clazzName); + for (String packagePattern : packagePatternArray) { + String path = ClassUtils.convertClassNameToResourcePath(packagePattern); + Resource[] resources = resolver.getResources(classpath + path + "/*.class"); + for (Resource resource : resources) { + ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata(); + Class> clazz = Resources.classForName(classMetadata.getClassName()); + findAnnotation(clazz); + } + } } catch (Exception e) { - return null; + log.error("初始化数据安全缓存时出错:{}", e.getMessage()); } - List methods = Arrays.stream(ClassUtil.getDeclaredMethods(clazz)) - .filter(method -> method.getName().equals(methodName)).toList(); + } + + private void findAnnotation(Class> clazz) { DataPermission dataPermission; // 获取方法注解 - for (Method method : methods) { - dataPermission = dataPermissionCacheMap.get(mappedStatementId); - if (ObjectUtil.isNotNull(dataPermission)) { - return dataPermission.value(); + for (Method method : clazz.getMethods()) { + if (method.isDefault() || method.isVarArgs()) { + continue; } + String mappedStatementId = clazz.getName() + "." + method.getName(); if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) { dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class); dataPermissionCacheMap.put(mappedStatementId, dataPermission); - return dataPermission.value(); } } - dataPermission = dataPermissionCacheMap.get(clazz.getName()); - if (ObjectUtil.isNotNull(dataPermission)) { - return dataPermission.value(); - } // 获取类注解 if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) { dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class); dataPermissionCacheMap.put(clazz.getName(), dataPermission); - return dataPermission.value(); + } + } + + public DataPermission getDataPermission(String mapperId) { + if (dataPermissionCacheMap.containsKey(mapperId)) { + return dataPermissionCacheMap.get(mapperId); + } + String clazzName = mapperId.substring(0, mapperId.lastIndexOf(".")); + if (dataPermissionCacheMap.containsKey(clazzName)) { + return dataPermissionCacheMap.get(clazzName); } return null; } + /** + * 是否无效 + */ + public boolean invalid(String mapperId) { + return getDataPermission(mapperId) == null; + } } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java index 0ab0c1139..f287846ce 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java @@ -1,13 +1,10 @@ package org.dromara.common.mybatis.interceptor; -import cn.hutool.core.collection.ConcurrentHashSet; -import cn.hutool.core.util.ArrayUtil; import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import com.baomidou.mybatisplus.core.toolkit.PluginUtils; import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport; import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; -import org.dromara.common.mybatis.annotation.DataColumn; -import org.dromara.common.mybatis.handler.PlusDataPermissionHandler; +import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.statement.delete.Delete; import net.sf.jsqlparser.statement.select.PlainSelect; @@ -22,11 +19,11 @@ import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; +import org.dromara.common.mybatis.handler.PlusDataPermissionHandler; import java.sql.Connection; import java.sql.SQLException; import java.util.List; -import java.util.Set; /** * 数据权限拦截器 @@ -34,13 +31,14 @@ import java.util.Set; * @author Lion Li * @version 3.5.0 */ +@Slf4j public class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor { - private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler(); - /** - * 无效注解方法缓存用于快速返回 - */ - private final Set invalidCacheSet = new ConcurrentHashSet<>(); + private final PlusDataPermissionHandler dataPermissionHandler; + + public PlusDataPermissionInterceptor(String mapperPackage) { + this.dataPermissionHandler = new PlusDataPermissionHandler(mapperPackage); + } @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { @@ -49,12 +47,7 @@ public class PlusDataPermissionInterceptor extends JsqlParserSupport implements return; } // 检查是否无效 无数据权限注解 - if (invalidCacheSet.contains(ms.getId())) { - return; - } - DataColumn[] dataColumns = dataPermissionHandler.findAnnotation(ms.getId()); - if (ArrayUtil.isEmpty(dataColumns)) { - invalidCacheSet.add(ms.getId()); + if (dataPermissionHandler.invalid(ms.getId())) { return; } // 解析 sql 分配对应方法 @@ -72,12 +65,7 @@ public class PlusDataPermissionInterceptor extends JsqlParserSupport implements return; } // 检查是否无效 无数据权限注解 - if (invalidCacheSet.contains(ms.getId())) { - return; - } - DataColumn[] dataColumns = dataPermissionHandler.findAnnotation(ms.getId()); - if (ArrayUtil.isEmpty(dataColumns)) { - invalidCacheSet.add(ms.getId()); + if (dataPermissionHandler.invalid(ms.getId())) { return; } PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); diff --git a/ruoyi-admin/src/main/resources/spy.properties b/ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties similarity index 80% rename from ruoyi-admin/src/main/resources/spy.properties rename to ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties index abbd8931d..e9d10f3f1 100644 --- a/ruoyi-admin/src/main/resources/spy.properties +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties @@ -6,8 +6,6 @@ logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger # 使用日志系统记录 sql #appender=com.p6spy.engine.spy.appender.Slf4JLogger -# 设置 p6spy driver 代理 -#deregisterdrivers=true # 取消JDBC URL前缀 useprefix=true # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset. @@ -16,12 +14,6 @@ excludecategories=info,debug,result,commit,resultset dateformat=yyyy-MM-dd HH:mm:ss # SQL语句打印时间格式 databaseDialectTimestampFormat=yyyy-MM-dd HH:mm:ss -# 实际驱动可多个 -#driverlist=org.h2.Driver -# 是否开启慢SQL记录 -outagedetection=true -# 慢SQL记录标准 2 秒 -outagedetectioninterval=2 # 是否过滤 Log filter=true # 过滤 Log 时所排除的 sql 关键字,以逗号分隔 diff --git a/ruoyi-common/ruoyi-common-oss/pom.xml b/ruoyi-common/ruoyi-common-oss/pom.xml index 8e7affff4..18d004f57 100644 --- a/ruoyi-common/ruoyi-common-oss/pom.xml +++ b/ruoyi-common/ruoyi-common-oss/pom.xml @@ -26,10 +26,46 @@ ruoyi-common-redis + - com.amazonaws - aws-java-sdk-s3 + software.amazon.awssdk + s3 + + + + software.amazon.awssdk + netty-nio-client + + + + software.amazon.awssdk + aws-crt-client + + + + software.amazon.awssdk + apache-client + + + + software.amazon.awssdk + url-connection-client + + + + + + software.amazon.awssdk.crt + aws-crt + + + + + software.amazon.awssdk + s3-transfer-manager + + diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java index 53e05c927..eb1656c06 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java @@ -2,73 +2,114 @@ package org.dromara.common.oss.core; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.IdUtil; -import com.amazonaws.ClientConfiguration; -import com.amazonaws.HttpMethod; -import com.amazonaws.Protocol; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.client.builder.AwsClientBuilder; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; -import com.amazonaws.services.s3.model.*; +import org.dromara.common.core.constant.Constants; import org.dromara.common.core.utils.DateUtils; import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.file.FileUtils; import org.dromara.common.oss.constant.OssConstant; import org.dromara.common.oss.entity.UploadResult; import org.dromara.common.oss.enumd.AccessPolicyType; import org.dromara.common.oss.enumd.PolicyType; import org.dromara.common.oss.exception.OssException; import org.dromara.common.oss.properties.OssProperties; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.core.async.BlockingInputStreamAsyncRequestBody; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.S3Configuration; +import software.amazon.awssdk.services.s3.model.NoSuchBucketException; +import software.amazon.awssdk.services.s3.model.S3Exception; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.transfer.s3.S3TransferManager; +import software.amazon.awssdk.transfer.s3.model.*; +import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener; import java.io.ByteArrayInputStream; import java.io.File; +import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.net.URL; -import java.util.Date; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; /** * S3 存储协议 所有兼容S3协议的云厂商均支持 * 阿里云 腾讯云 七牛云 minio * - * @author Lion Li + * @author AprilWind */ public class OssClient { + /** + * 服务商 + */ private final String configKey; + /** + * 配置属性 + */ private final OssProperties properties; - private final AmazonS3 client; + /** + * Amazon S3 异步客户端 + */ + private final S3AsyncClient client; + /** + * 用于管理 S3 数据传输的高级工具 + */ + private final S3TransferManager transferManager; + + /** + * AWS S3 预签名 URL 的生成器 + */ + private final S3Presigner presigner; + + /** + * 构造方法 + * + * @param configKey 配置键 + * @param ossProperties Oss配置属性 + */ public OssClient(String configKey, OssProperties ossProperties) { this.configKey = configKey; this.properties = ossProperties; try { - AwsClientBuilder.EndpointConfiguration endpointConfig = - new AwsClientBuilder.EndpointConfiguration(properties.getEndpoint(), properties.getRegion()); + // 创建 AWS 认证信息 + StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create( + AwsBasicCredentials.create(properties.getAccessKey(), properties.getSecretKey())); - AWSCredentials credentials = new BasicAWSCredentials(properties.getAccessKey(), properties.getSecretKey()); - AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials); - ClientConfiguration clientConfig = new ClientConfiguration(); - if (OssConstant.IS_HTTPS.equals(properties.getIsHttps())) { - clientConfig.setProtocol(Protocol.HTTPS); - } else { - clientConfig.setProtocol(Protocol.HTTP); - } - AmazonS3ClientBuilder build = AmazonS3Client.builder() - .withEndpointConfiguration(endpointConfig) - .withClientConfiguration(clientConfig) - .withCredentials(credentialsProvider) - .disableChunkedEncoding(); - if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)) { + //创建AWS基于 CRT 的 S3 客户端 + this.client = S3AsyncClient.crtBuilder() + .credentialsProvider(credentialsProvider) + .endpointOverride(URI.create(getEndpoint())) + .region(of()) + .targetThroughputInGbps(20.0) + .minimumPartSizeInBytes(10 * 1025 * 1024L) + .checksumValidationEnabled(false) + .build(); + + //AWS基于 CRT 的 S3 AsyncClient 实例用作 S3 传输管理器的底层客户端 + this.transferManager = S3TransferManager.builder().s3Client(this.client).build(); + + // 检查是否连接到 MinIO,MinIO 使用 HTTPS 限制使用域名访问,需要启用路径样式访问 + S3Configuration config = S3Configuration.builder().chunkedEncodingEnabled(false) // minio 使用https限制使用域名访问 需要此配置 站点填域名 - build.enablePathStyleAccess(); - } - this.client = build.build(); + .pathStyleAccessEnabled(!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)).build(); + // 创建 预签名 URL 的生成器 实例,用于生成 S3 预签名 URL + this.presigner = S3Presigner.builder() + .region(of()) + .credentialsProvider(credentialsProvider) + .endpointOverride(URI.create(getDomain())) + .serviceConfiguration(config) + .build(); + + // 创建存储桶 createBucket(); } catch (Exception e) { if (e instanceof OssException) { @@ -78,126 +119,158 @@ public class OssClient { } } + /** + * 同步创建存储桶 + * 如果存储桶不存在,会进行创建;如果存储桶存在,不执行任何操作 + * + * @throws OssException 当创建存储桶时发生异常时抛出 + */ public void createBucket() { + String bucketName = properties.getBucketName(); try { - String bucketName = properties.getBucketName(); - if (client.doesBucketExistV2(bucketName)) { - return; + // 尝试获取存储桶的信息 + client.headBucket( + x -> x.bucket(bucketName) + .build()) + .join(); + } catch (Exception ex) { + if (ex.getCause() instanceof NoSuchBucketException) { + try { + // 存储桶不存在,尝试创建存储桶 + client.createBucket( + x -> x.bucket(bucketName)) + .join(); + + // 设置存储桶的访问策略(Bucket Policy) + client.putBucketPolicy( + x -> x.bucket(bucketName) + .policy(getPolicy(bucketName, getAccessPolicy().getPolicyType()))) + .join(); + } catch (S3Exception e) { + // 存储桶创建或策略设置失败 + throw new OssException("创建Bucket失败, 请核对配置信息:[" + e.getMessage() + "]"); + } + } else { + throw new OssException("判断Bucket是否存在失败,请核对配置信息:[" + ex.getMessage() + "]"); } - CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName); - AccessPolicyType accessPolicy = getAccessPolicy(); - createBucketRequest.setCannedAcl(accessPolicy.getAcl()); - client.createBucket(createBucketRequest); - client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType())); - } catch (Exception e) { - throw new OssException("创建Bucket失败, 请核对配置信息:[" + e.getMessage() + "]"); } } - public UploadResult upload(byte[] data, String path, String contentType) { - return upload(new ByteArrayInputStream(data), path, contentType); + /** + * 上传文件到 Amazon S3,并返回上传结果 + * + * @param filePath 本地文件路径 + * @param key 在 Amazon S3 中的对象键 + * @param md5Digest 本地文件的 MD5 哈希值(可选) + * @return UploadResult 包含上传后的文件信息 + * @throws OssException 如果上传失败,抛出自定义异常 + */ + public UploadResult upload(Path filePath, String key, String md5Digest) { + try { + // 构建上传请求对象 + FileUpload fileUpload = transferManager.uploadFile( + x -> x.putObjectRequest( + y -> y.bucket(properties.getBucketName()) + .key(key) + .contentMD5(StringUtils.isNotEmpty(md5Digest) ? md5Digest : null) + .build()) + .addTransferListener(LoggingTransferListener.create()) + .source(filePath).build()); + + // 等待上传完成并获取上传结果 + CompletedFileUpload uploadResult = fileUpload.completionFuture().join(); + String eTag = uploadResult.response().eTag(); + + // 提取上传结果中的 ETag,并构建一个自定义的 UploadResult 对象 + return UploadResult.builder().url(getUrl() + StringUtils.SLASH + key).filename(key).eTag(eTag).build(); + } catch (Exception e) { + // 捕获异常并抛出自定义异常 + throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]"); + } finally { + // 无论上传是否成功,最终都会删除临时文件 + FileUtils.del(filePath); + } } - public UploadResult upload(InputStream inputStream, String path, String contentType) { + /** + * 上传 InputStream 到 Amazon S3 + * + * @param inputStream 要上传的输入流 + * @param key 在 Amazon S3 中的对象键 + * @param length 输入流的长度 + * @return UploadResult 包含上传后的文件信息 + * @throws OssException 如果上传失败,抛出自定义异常 + */ + public UploadResult upload(InputStream inputStream, String key, Long length) { + // 如果输入流不是 ByteArrayInputStream,则将其读取为字节数组再创建 ByteArrayInputStream if (!(inputStream instanceof ByteArrayInputStream)) { inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream)); } try { - ObjectMetadata metadata = new ObjectMetadata(); - metadata.setContentType(contentType); - metadata.setContentLength(inputStream.available()); - PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata); - // 设置上传对象的 Acl 为公共读 - putObjectRequest.setCannedAcl(getAccessPolicy().getAcl()); - client.putObject(putObjectRequest); + // 创建异步请求体(length如果为空会报错) + BlockingInputStreamAsyncRequestBody body = AsyncRequestBody.forBlockingInputStream(length); + + // 使用 transferManager 进行上传 + Upload upload = transferManager.upload( + x -> x.requestBody(body) + .putObjectRequest( + y -> y.bucket(properties.getBucketName()) + .key(key) + .build()) + .build()); + + // 将输入流写入请求体 + body.writeInputStream(inputStream); + + // 等待文件上传操作完成 + CompletedUpload uploadResult = upload.completionFuture().join(); + String eTag = uploadResult.response().eTag(); + + // 提取上传结果中的 ETag,并构建一个自定义的 UploadResult 对象 + return UploadResult.builder().url(getUrl() + StringUtils.SLASH + key).filename(key).eTag(eTag).build(); } catch (Exception e) { throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]"); } - return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build(); - } - - public UploadResult upload(File file, String path) { - try { - PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, file); - // 设置上传对象的 Acl 为公共读 - putObjectRequest.setCannedAcl(getAccessPolicy().getAcl()); - client.putObject(putObjectRequest); - } catch (Exception e) { - throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]"); - } - return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build(); - } - - public void delete(String path) { - path = path.replace(getUrl() + "/", ""); - try { - client.deleteObject(properties.getBucketName(), path); - } catch (Exception e) { - throw new OssException("删除文件失败,请检查配置信息:[" + e.getMessage() + "]"); - } - } - - public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) { - return upload(data, getPath(properties.getPrefix(), suffix), contentType); - } - - public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) { - return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType); - } - - public UploadResult uploadSuffix(File file, String suffix) { - return upload(file, getPath(properties.getPrefix(), suffix)); } /** - * 获取文件元数据 + * 下载文件从 Amazon S3 到临时目录 * - * @param path 完整文件路径 + * @param path 文件在 Amazon S3 中的对象键 + * @return 下载后的文件在本地的临时路径 + * @throws OssException 如果下载失败,抛出自定义异常 */ - public ObjectMetadata getObjectMetadata(String path) { - path = path.replace(getUrl() + "/", ""); - S3Object object = client.getObject(properties.getBucketName(), path); - return object.getObjectMetadata(); + public Path fileDownload(String path) { + // 构建临时文件 + Path tempFilePath = FileUtils.createTempFile().toPath(); + // 使用 S3TransferManager 下载文件 + FileDownload downloadFile = transferManager.downloadFile( + x -> x.getObjectRequest( + y -> y.bucket(properties.getBucketName()) + .key(removeBaseUrl(path)) + .build()) + .addTransferListener(LoggingTransferListener.create()) + .destination(tempFilePath) + .build()); + // 等待文件下载操作完成 + downloadFile.completionFuture().join(); + return tempFilePath; } - public InputStream getObjectContent(String path) { - path = path.replace(getUrl() + "/", ""); - S3Object object = client.getObject(properties.getBucketName(), path); - return object.getObjectContent(); - } - - public String getUrl() { - String domain = properties.getDomain(); - String endpoint = properties.getEndpoint(); - String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://"; - // 云服务商直接返回 - if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) { - if (StringUtils.isNotBlank(domain)) { - return header + domain; - } - return header + properties.getBucketName() + "." + endpoint; + /** + * 删除云存储服务中指定路径下文件 + * + * @param path 指定路径 + */ + public void delete(String path) { + try { + client.deleteObject( + x -> x.bucket(properties.getBucketName()) + .key(removeBaseUrl(path)) + .build()); + } catch (Exception e) { + throw new OssException("删除文件失败,请检查配置信息:[" + e.getMessage() + "]"); } - // minio 单独处理 - if (StringUtils.isNotBlank(domain)) { - return header + domain + "/" + properties.getBucketName(); - } - return header + endpoint + "/" + properties.getBucketName(); - } - - public String getPath(String prefix, String suffix) { - // 生成uuid - String uuid = IdUtil.fastSimpleUUID(); - // 文件路径 - String path = DateUtils.datePath() + "/" + uuid; - if (StringUtils.isNotBlank(prefix)) { - path = prefix + "/" + path; - } - return path + suffix; - } - - - public String getConfigKey() { - return configKey; } /** @@ -207,14 +280,189 @@ public class OssClient { * @param second 授权时间 */ public String getPrivateUrl(String objectKey, Integer second) { - GeneratePresignedUrlRequest generatePresignedUrlRequest = - new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey) - .withMethod(HttpMethod.GET) - .withExpiration(new Date(System.currentTimeMillis() + 1000L * second)); - URL url = client.generatePresignedUrl(generatePresignedUrlRequest); + // 使用 AWS S3 预签名 URL 的生成器 获取对象的预签名 URL + URL url = presigner.presignGetObject( + x -> x.signatureDuration(Duration.ofSeconds(second)) + .getObjectRequest( + y -> y.bucket(properties.getBucketName()) + .key(objectKey) + .build()) + .build()) + .url(); return url.toString(); } + /** + * 上传 byte[] 数据到 Amazon S3,使用指定的后缀构造对象键。 + * + * @param data 要上传的 byte[] 数据 + * @param suffix 对象键的后缀 + * @return UploadResult 包含上传后的文件信息 + * @throws OssException 如果上传失败,抛出自定义异常 + */ + public UploadResult uploadSuffix(byte[] data, String suffix) { + return upload(new ByteArrayInputStream(data), getPath(properties.getPrefix(), suffix), Long.valueOf(data.length)); + } + + /** + * 上传 InputStream 到 Amazon S3,使用指定的后缀构造对象键。 + * + * @param inputStream 要上传的输入流 + * @param suffix 对象键的后缀 + * @param length 输入流的长度 + * @return UploadResult 包含上传后的文件信息 + * @throws OssException 如果上传失败,抛出自定义异常 + */ + public UploadResult uploadSuffix(InputStream inputStream, String suffix, Long length) { + return upload(inputStream, getPath(properties.getPrefix(), suffix), length); + } + + /** + * 上传文件到 Amazon S3,使用指定的后缀构造对象键 + * + * @param file 要上传的文件 + * @param suffix 对象键的后缀 + * @return UploadResult 包含上传后的文件信息 + * @throws OssException 如果上传失败,抛出自定义异常 + */ + public UploadResult uploadSuffix(File file, String suffix) { + return upload(file.toPath(), getPath(properties.getPrefix(), suffix), null); + } + + /** + * 获取文件输入流 + * + * @param path 完整文件路径 + * @return 输入流 + */ + public InputStream getObjectContent(String path) throws IOException { + // 下载文件到临时目录 + Path tempFilePath = fileDownload(path); + // 创建输入流 + InputStream inputStream = Files.newInputStream(tempFilePath); + // 删除临时文件 + FileUtils.del(tempFilePath); + // 返回对象内容的输入流 + return inputStream; + } + + /** + * 获取 S3 客户端的终端点 URL + * + * @return 终端点 URL + */ + public String getEndpoint() { + // 根据配置文件中的是否使用 HTTPS,设置协议头部 + String header = getIsHttps(); + // 拼接协议头部和终端点,得到完整的终端点 URL + return header + properties.getEndpoint(); + } + + /** + * 获取 S3 客户端的终端点 URL(自定义域名) + * + * @return 终端点 URL + */ + public String getDomain() { + // 从配置中获取域名、终端点、是否使用 HTTPS 等信息 + String domain = properties.getDomain(); + String endpoint = properties.getEndpoint(); + String header = getIsHttps(); + + // 如果是云服务商,直接返回域名或终端点 + if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) { + return StringUtils.isNotEmpty(domain) ? header + domain : header + endpoint; + } + + // 如果是 MinIO,处理域名并返回 + if (StringUtils.isNotEmpty(domain)) { + return domain.startsWith(Constants.HTTPS) || domain.startsWith(Constants.HTTP) ? domain : header + domain; + } + + // 返回终端点 + return header + endpoint; + } + + /** + * 根据传入的 region 参数返回相应的 AWS 区域 + * 如果 region 参数非空,使用 Region.of 方法创建并返回对应的 AWS 区域对象 + * 如果 region 参数为空,返回一个默认的 AWS 区域(例如,us-east-1),作为广泛支持的区域 + * + * @return 对应的 AWS 区域对象,或者默认的广泛支持的区域(us-east-1) + */ + public Region of() { + //AWS 区域字符串 + String region = properties.getRegion(); + // 如果 region 参数非空,使用 Region.of 方法创建对应的 AWS 区域对象,否则返回默认区域 + return StringUtils.isNotEmpty(region) ? Region.of(region) : Region.US_EAST_1; + } + + /** + * 获取云存储服务的URL + * + * @return 文件路径 + */ + public String getUrl() { + String domain = properties.getDomain(); + String endpoint = properties.getEndpoint(); + String header = getIsHttps(); + // 云服务商直接返回 + if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) { + return header + (StringUtils.isNotEmpty(domain) ? domain : properties.getBucketName() + "." + endpoint); + } + // MinIO 单独处理 + if (StringUtils.isNotEmpty(domain)) { + // 如果 domain 以 "https://" 或 "http://" 开头 + return (domain.startsWith(Constants.HTTPS) || domain.startsWith(Constants.HTTP)) ? + domain + StringUtils.SLASH + properties.getBucketName() : header + domain + StringUtils.SLASH + properties.getBucketName(); + } + return header + endpoint + StringUtils.SLASH + properties.getBucketName(); + } + + /** + * 生成一个符合特定规则的、唯一的文件路径。通过使用日期、UUID、前缀和后缀等元素的组合,确保了文件路径的独一无二性 + * + * @param prefix 前缀 + * @param suffix 后缀 + * @return 文件路径 + */ + public String getPath(String prefix, String suffix) { + // 生成uuid + String uuid = IdUtil.fastSimpleUUID(); + // 生成日期路径 + String datePath = DateUtils.datePath(); + // 拼接路径 + String path = StringUtils.isNotEmpty(prefix) ? + prefix + StringUtils.SLASH + datePath + StringUtils.SLASH + uuid : datePath + StringUtils.SLASH + uuid; + return path + suffix; + } + + /** + * 移除路径中的基础URL部分,得到相对路径 + * + * @param path 完整的路径,包括基础URL和相对路径 + * @return 去除基础URL后的相对路径 + */ + public String removeBaseUrl(String path) { + return path.replace(getUrl() + StringUtils.SLASH, ""); + } + + /** + * 服务商 + */ + public String getConfigKey() { + return configKey; + } + + /** + * 获取是否使用 HTTPS 的配置,并返回相应的协议头部。 + * + * @return 协议头部,根据是否使用 HTTPS 返回 "https://" 或 "http://" + */ + public String getIsHttps() { + return OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? Constants.HTTPS : Constants.HTTP; + } + /** * 检查配置是否相同 */ @@ -231,32 +479,77 @@ public class OssClient { return AccessPolicyType.getByType(properties.getAccessPolicy()); } + /** + * 生成 AWS S3 存储桶访问策略 + * + * @param bucketName 存储桶 + * @param policyType 桶策略类型 + * @return 符合 AWS S3 存储桶访问策略格式的字符串 + */ private static String getPolicy(String bucketName, PolicyType policyType) { - StringBuilder builder = new StringBuilder(); - builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n"); - builder.append(switch (policyType) { - case WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucketMultipartUploads\"\n"; - case READ_WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucket\",\n\"s3:ListBucketMultipartUploads\"\n"; - default -> "\"s3:GetBucketLocation\"\n"; - }); - builder.append("],\n\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); - builder.append(bucketName); - builder.append("\"\n},\n"); - if (policyType == PolicyType.READ) { - builder.append("{\n\"Action\": [\n\"s3:ListBucket\"\n],\n\"Effect\": \"Deny\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); - builder.append(bucketName); - builder.append("\"\n},\n"); - } - builder.append("{\n\"Action\": "); - builder.append(switch (policyType) { - case WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n"; - case READ_WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:GetObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n"; - default -> "\"s3:GetObject\",\n"; - }); - builder.append("\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); - builder.append(bucketName); - builder.append("/*\"\n}\n],\n\"Version\": \"2012-10-17\"\n}\n"); - return builder.toString(); + String policy = switch (policyType) { + case WRITE -> """ + { + "Version": "2012-10-17", + "Statement": [] + } + """; + case READ_WRITE -> """ + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": "*", + "Action": [ + "s3:GetBucketLocation", + "s3:ListBucket", + "s3:ListBucketMultipartUploads" + ], + "Resource": "arn:aws:s3:::bucketName" + }, + { + "Effect": "Allow", + "Principal": "*", + "Action": [ + "s3:AbortMultipartUpload", + "s3:DeleteObject", + "s3:GetObject", + "s3:ListMultipartUploadParts", + "s3:PutObject" + ], + "Resource": "arn:aws:s3:::bucketName/*" + } + ] + } + """; + case READ -> """ + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": "*", + "Action": ["s3:GetBucketLocation"], + "Resource": "arn:aws:s3:::bucketName" + }, + { + "Effect": "Deny", + "Principal": "*", + "Action": ["s3:ListBucket"], + "Resource": "arn:aws:s3:::bucketName" + }, + { + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::bucketName/*" + } + ] + } + """; + }; + return policy.replaceAll("bucketName", bucketName); } } diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/UploadResult.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/UploadResult.java index a6f57e5ad..81a18e62a 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/UploadResult.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/UploadResult.java @@ -21,4 +21,10 @@ public class UploadResult { * 文件名 */ private String filename; + + /** + * 已上传对象的实体标记(用来校验文件) + */ + private String eTag; + } diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/enumd/AccessPolicyType.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/enumd/AccessPolicyType.java index 9074d72de..6d39133d5 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/enumd/AccessPolicyType.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/enumd/AccessPolicyType.java @@ -1,8 +1,9 @@ package org.dromara.common.oss.enumd; -import com.amazonaws.services.s3.model.CannedAccessControlList; import lombok.AllArgsConstructor; import lombok.Getter; +import software.amazon.awssdk.services.s3.model.BucketCannedACL; +import software.amazon.awssdk.services.s3.model.ObjectCannedACL; /** * 桶访问策略配置 @@ -16,27 +17,32 @@ public enum AccessPolicyType { /** * private */ - PRIVATE("0", CannedAccessControlList.Private, PolicyType.WRITE), + PRIVATE("0", BucketCannedACL.PRIVATE, ObjectCannedACL.PRIVATE, PolicyType.WRITE), /** * public */ - PUBLIC("1", CannedAccessControlList.PublicRead, PolicyType.READ), + PUBLIC("1", BucketCannedACL.PUBLIC_READ_WRITE, ObjectCannedACL.PUBLIC_READ_WRITE, PolicyType.READ_WRITE), /** * custom */ - CUSTOM("2",CannedAccessControlList.PublicRead, PolicyType.READ); + CUSTOM("2", BucketCannedACL.PUBLIC_READ, ObjectCannedACL.PUBLIC_READ, PolicyType.READ); /** - * 桶 权限类型 + * 桶 权限类型(数据库值) */ private final String type; + /** + * 桶 权限类型 + */ + private final BucketCannedACL bucketCannedACL; + /** * 文件对象 权限类型 */ - private final CannedAccessControlList acl; + private final ObjectCannedACL objectCannedACL; /** * 桶策略类型 diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java index 763b090e3..d70270a15 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java @@ -1,5 +1,6 @@ package org.dromara.common.oss.factory; +import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.json.utils.JsonUtils; @@ -9,10 +10,10 @@ import org.dromara.common.oss.exception.OssException; import org.dromara.common.oss.properties.OssProperties; import org.dromara.common.redis.utils.CacheUtils; import org.dromara.common.redis.utils.RedisUtils; -import lombok.extern.slf4j.Slf4j; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; /** * 文件上传Factory @@ -23,6 +24,7 @@ import java.util.concurrent.ConcurrentHashMap; public class OssFactory { private static final Map CLIENT_CACHE = new ConcurrentHashMap<>(); + private static final ReentrantLock LOCK = new ReentrantLock(); /** * 获取默认实例 @@ -39,7 +41,7 @@ public class OssFactory { /** * 根据类型获取实例 */ - public static synchronized OssClient instance(String configKey) { + public static OssClient instance(String configKey) { String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey); if (json == null) { throw new OssException("系统异常, '" + configKey + "'配置信息不存在!"); @@ -48,16 +50,19 @@ public class OssFactory { // 使用租户标识避免多个租户相同key实例覆盖 String key = properties.getTenantId() + ":" + configKey; OssClient client = CLIENT_CACHE.get(key); - if (client == null) { - CLIENT_CACHE.put(key, new OssClient(configKey, properties)); - log.info("创建OSS实例 key => {}", configKey); - return CLIENT_CACHE.get(key); - } - // 配置不相同则重新构建 - if (!client.checkPropertiesSame(properties)) { - CLIENT_CACHE.put(key, new OssClient(configKey, properties)); - log.info("重载OSS实例 key => {}", configKey); - return CLIENT_CACHE.get(key); + // 客户端不存在或配置不相同则重新构建 + if (client == null || !client.checkPropertiesSame(properties)) { + LOCK.lock(); + try { + client = CLIENT_CACHE.get(key); + if (client == null || !client.checkPropertiesSame(properties)) { + CLIENT_CACHE.put(key, new OssClient(configKey, properties)); + log.info("创建OSS实例 key => {}", configKey); + return CLIENT_CACHE.get(key); + } + } finally { + LOCK.unlock(); + } } return client; } diff --git a/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java index 8f3a5cafb..02735b073 100644 --- a/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java +++ b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java @@ -1,29 +1,29 @@ package org.dromara.common.ratelimiter.aspectj; -import cn.hutool.core.util.ArrayUtil; -import org.dromara.common.core.constant.GlobalConstants; -import org.dromara.common.core.exception.ServiceException; -import org.dromara.common.core.utils.MessageUtils; -import org.dromara.common.core.utils.ServletUtils; -import org.dromara.common.core.utils.StringUtils; -import org.dromara.common.ratelimiter.annotation.RateLimiter; -import org.dromara.common.ratelimiter.enums.LimitType; -import org.dromara.common.redis.utils.RedisUtils; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; +import org.dromara.common.core.constant.GlobalConstants; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MessageUtils; +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.ratelimiter.annotation.RateLimiter; +import org.dromara.common.ratelimiter.enums.LimitType; +import org.dromara.common.redis.utils.RedisUtils; import org.redisson.api.RateType; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.context.expression.MethodBasedEvaluationContext; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; -import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParserContext; import org.springframework.expression.common.TemplateParserContext; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; import java.lang.reflect.Method; @@ -44,21 +44,18 @@ public class RateLimiterAspect { * 定义spel解析模版 */ private final ParserContext parserContext = new TemplateParserContext(); - /** - * 定义spel上下文对象进行解析 - */ - private final EvaluationContext context = new StandardEvaluationContext(); /** * 方法参数解析器 */ private final ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer(); + @Before("@annotation(rateLimiter)") - public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable { + public void doBefore(JoinPoint point, RateLimiter rateLimiter) { int time = rateLimiter.time(); int count = rateLimiter.count(); - String combineKey = getCombineKey(rateLimiter, point); try { + String combineKey = getCombineKey(rateLimiter, point); RateType rateType = RateType.OVERALL; if (rateLimiter.limitType() == LimitType.CLUSTER) { rateType = RateType.PER_CLIENT; @@ -76,42 +73,29 @@ public class RateLimiterAspect { if (e instanceof ServiceException) { throw e; } else { - throw new RuntimeException("服务器限流异常,请稍候再试"); + throw new RuntimeException("服务器限流异常,请稍候再试", e); } } } - public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) { + private String getCombineKey(RateLimiter rateLimiter, JoinPoint point) { String key = rateLimiter.key(); - // 获取方法(通过方法签名来获取) - MethodSignature signature = (MethodSignature) point.getSignature(); - Method method = signature.getMethod(); - Class> targetClass = method.getDeclaringClass(); - // 判断是否是spel格式 - if (StringUtils.containsAny(key, "#")) { - // 获取参数值 + if (StringUtils.isNotBlank(key)) { + MethodSignature signature = (MethodSignature) point.getSignature(); + Method targetMethod = signature.getMethod(); Object[] args = point.getArgs(); - // 获取方法上参数的名称 - String[] parameterNames = pnd.getParameterNames(method); - if (ArrayUtil.isEmpty(parameterNames)) { - throw new ServiceException("限流key解析异常!请联系管理员!"); - } - for (int i = 0; i < parameterNames.length; i++) { - context.setVariable(parameterNames[i], args[i]); - } - // 解析返回给key - try { - Expression expression; - if (StringUtils.startsWith(key, parserContext.getExpressionPrefix()) - && StringUtils.endsWith(key, parserContext.getExpressionSuffix())) { - expression = parser.parseExpression(key, parserContext); - } else { - expression = parser.parseExpression(key); - } - key = expression.getValue(context, String.class) + ":"; - } catch (Exception e) { - throw new ServiceException("限流key解析异常!请联系管理员!"); + //noinspection DataFlowIssue + MethodBasedEvaluationContext context = + new MethodBasedEvaluationContext(null, targetMethod, args, pnd); + context.setBeanResolver(new BeanFactoryResolver(SpringUtils.getBeanFactory())); + Expression expression; + if (StringUtils.startsWith(key, parserContext.getExpressionPrefix()) + && StringUtils.endsWith(key, parserContext.getExpressionSuffix())) { + expression = parser.parseExpression(key, parserContext); + } else { + expression = parser.parseExpression(key); } + key = expression.getValue(context, String.class); } StringBuilder stringBuffer = new StringBuilder(GlobalConstants.RATE_LIMIT_KEY); stringBuffer.append(ServletUtils.getRequest().getRequestURI()).append(":"); diff --git a/ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/spel-extension.json b/ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/spel-extension.json new file mode 100644 index 000000000..b16432b29 --- /dev/null +++ b/ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/spel-extension.json @@ -0,0 +1,7 @@ +{ + "org.dromara.common.ratelimiter.annotation.RateLimiter@key": { + "method": { + "parameters": true + } + } +} diff --git a/ruoyi-common/ruoyi-common-redis/pom.xml b/ruoyi-common/ruoyi-common-redis/pom.xml index 5e5c277f3..140866575 100644 --- a/ruoyi-common/ruoyi-common-redis/pom.xml +++ b/ruoyi-common/ruoyi-common-redis/pom.xml @@ -32,6 +32,11 @@ com.baomidou lock4j-redisson-spring-boot-starter + + + com.github.ben-manes.caffeine + caffeine + diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/CacheConfig.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/CacheConfig.java new file mode 100644 index 000000000..d57ba4e65 --- /dev/null +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/CacheConfig.java @@ -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 caffeine() { + return Caffeine.newBuilder() + // 设置最后一次写入或访问后经过固定时间过期 + .expireAfterWrite(30, TimeUnit.SECONDS) + // 初始的缓存空间大小 + .initialCapacity(100) + // 缓存的最大条数 + .maximumSize(1000) + .build(); + } + + /** + * 自定义缓存管理器 整合spring-cache + */ + @Bean + public CacheManager cacheManager() { + return new PlusSpringCacheManager(); + } + +} diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java index 7999f2b34..f4328f4d4 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java @@ -6,9 +6,9 @@ import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.redis.config.properties.RedissonProperties; import org.dromara.common.redis.handler.KeyPrefixHandler; -import org.dromara.common.redis.manager.PlusSpringCacheManager; import org.redisson.client.codec.StringCodec; import org.redisson.codec.CompositeCodec; import org.redisson.codec.TypedJsonJacksonCodec; @@ -16,9 +16,8 @@ import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfiguration; 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.core.task.VirtualThreadTaskExecutor; /** * redis配置 @@ -27,7 +26,6 @@ import org.springframework.context.annotation.Bean; */ @Slf4j @AutoConfiguration -@EnableCaching @EnableConfigurationProperties(RedissonProperties.class) public class RedisConfig { @@ -52,6 +50,9 @@ public class RedisConfig { // 缓存 Lua 脚本 减少网络传输(redisson 大部分的功能都是基于 Lua 脚本实现) .setUseScriptCache(true) .setCodec(codec); + if (SpringUtils.isVirtual()) { + config.setNettyExecutor(new VirtualThreadTaskExecutor("redisson-")); + } RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig(); if (ObjectUtil.isNotNull(singleServerConfig)) { // 使用单机模式 @@ -86,14 +87,6 @@ public class RedisConfig { }; } - /** - * 自定义缓存管理器 整合spring-cache - */ - @Bean - public CacheManager cacheManager() { - return new PlusSpringCacheManager(); - } - /** * redis集群配置 yml * diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java new file mode 100644 index 000000000..7d3ab4266 --- /dev/null +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java @@ -0,0 +1,89 @@ +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 + 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(); + } + + public String getUniqueKey(Object key) { + return cache.getName() + ":" + key; + } + + @Override + public ValueWrapper get(Object key) { + Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key)); + return (ValueWrapper) o; + } + + @SuppressWarnings("unchecked") + public T get(Object key, Class type) { + Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, type)); + 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(getUniqueKey(key)); + } + return b; + } + + @Override + public void clear() { + cache.clear(); + } + + public boolean invalidate() { + return cache.invalidate(); + } + + @SuppressWarnings("unchecked") + @Override + public T get(Object key, Callable valueLoader) { + Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, valueLoader)); + return (T) o; + } + +} diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java index 08ebc7826..a48cb1422 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java @@ -156,7 +156,7 @@ public class PlusSpringCacheManager implements CacheManager { private Cache createMap(String name, CacheConfig config) { RMap map = RedisUtils.getClient().getMap(name); - Cache cache = new RedissonCache(map, allowNullValues); + Cache cache = new CaffeineCacheDecorator(new RedissonCache(map, allowNullValues)); if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } @@ -170,7 +170,7 @@ public class PlusSpringCacheManager implements CacheManager { private Cache createMapCache(String name, CacheConfig config) { RMapCache map = RedisUtils.getClient().getMapCache(name); - Cache cache = new RedissonCache(map, config, allowNullValues); + Cache cache = new CaffeineCacheDecorator(new RedissonCache(map, config, allowNullValues)); if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } diff --git a/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 59192c5ce..0475b19f4 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1 +1,2 @@ org.dromara.common.redis.config.RedisConfig +org.dromara.common.redis.config.CacheConfig diff --git a/ruoyi-common/ruoyi-common-satoken/pom.xml b/ruoyi-common/ruoyi-common-satoken/pom.xml index 01c282b63..4df7d4ded 100644 --- a/ruoyi-common/ruoyi-common-satoken/pom.xml +++ b/ruoyi-common/ruoyi-common-satoken/pom.xml @@ -36,6 +36,11 @@ sa-token-jwt + + com.github.ben-manes.caffeine + caffeine + + diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java index d88596228..0664755a4 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java @@ -2,26 +2,42 @@ package org.dromara.common.satoken.core.dao; import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.util.SaFoxUtil; +import cn.hutool.core.lang.Console; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import org.dromara.common.redis.utils.RedisUtils; import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.concurrent.TimeUnit; /** * Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一) + * + * 采用 caffeine + redis 多级缓存 优化并发查询效率 * * @author Lion Li */ public class PlusSaTokenDao implements SaTokenDao { + private static final Cache CAFFEINE = Caffeine.newBuilder() + // 设置最后一次写入或访问后经过固定时间过期 + .expireAfterWrite(5, TimeUnit.SECONDS) + // 初始的缓存空间大小 + .initialCapacity(100) + // 缓存的最大条数 + .maximumSize(1000) + .build(); + /** * 获取Value,如无返空 */ @Override public String get(String key) { - return RedisUtils.getCacheObject(key); + Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key)); + return (String) o; } /** @@ -38,6 +54,7 @@ public class PlusSaTokenDao implements SaTokenDao { } else { RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout)); } + CAFFEINE.put(key, value); } /** @@ -47,6 +64,7 @@ public class PlusSaTokenDao implements SaTokenDao { public void update(String key, String value) { if (RedisUtils.hasKey(key)) { RedisUtils.setCacheObject(key, value, true); + CAFFEINE.put(key, value); } } @@ -81,7 +99,8 @@ public class PlusSaTokenDao implements SaTokenDao { */ @Override public Object getObject(String key) { - return RedisUtils.getCacheObject(key); + Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key)); + return o; } /** @@ -98,6 +117,7 @@ public class PlusSaTokenDao implements SaTokenDao { } else { RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout)); } + CAFFEINE.put(key, object); } /** @@ -107,6 +127,7 @@ public class PlusSaTokenDao implements SaTokenDao { public void updateObject(String key, Object object) { if (RedisUtils.hasKey(key)) { RedisUtils.setCacheObject(key, object, true); + CAFFEINE.put(key, object); } } @@ -139,10 +160,14 @@ public class PlusSaTokenDao implements SaTokenDao { /** * 搜索数据 */ + @SuppressWarnings("unchecked") @Override public List searchData(String prefix, String keyword, int start, int size, boolean sortType) { - Collection keys = RedisUtils.keys(prefix + "*" + keyword + "*"); - List list = new ArrayList<>(keys); - return SaFoxUtil.searchList(list, start, size, sortType); + String keyStr = prefix + "*" + keyword + "*"; + return (List) CAFFEINE.get(keyStr, k -> { + Collection keys = RedisUtils.keys(keyStr); + List list = new ArrayList<>(keys); + return SaFoxUtil.searchList(list, start, size, sortType); + }); } } diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java index 8488ac199..ce3d34451 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java @@ -1,7 +1,5 @@ package org.dromara.common.satoken.utils; -import cn.dev33.satoken.context.SaHolder; -import cn.dev33.satoken.context.model.SaStorage; import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; @@ -15,7 +13,6 @@ import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.enums.UserType; import java.util.Set; -import java.util.function.Supplier; /** * 登录鉴权助手 @@ -35,9 +32,10 @@ public class LoginHelper { public static final String LOGIN_USER_KEY = "loginUser"; public static final String TENANT_KEY = "tenantId"; 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_NAME_KEY = "deptName"; public static final String CLIENT_KEY = "clientid"; - public static final String TENANT_ADMIN_KEY = "isTenantAdmin"; /** * 登录系统 基于 设备类型 @@ -47,11 +45,6 @@ public class LoginHelper { * @param model 配置参数 */ public static void login(LoginUser loginUser, SaLoginModel model) { - SaStorage storage = SaHolder.getStorage(); - storage.set(LOGIN_USER_KEY, loginUser); - storage.set(TENANT_KEY, loginUser.getTenantId()); - storage.set(USER_KEY, loginUser.getUserId()); - storage.set(DEPT_KEY, loginUser.getDeptId()); model = ObjectUtil.defaultIfNull(model, new SaLoginModel()); StpUtil.login(loginUser.getLoginId(), model.setExtra(TENANT_KEY, loginUser.getTenantId()) @@ -66,13 +59,11 @@ public class LoginHelper { * 获取用户(多级缓存) */ public static LoginUser getLoginUser() { - return (LoginUser) getStorageIfAbsentSet(LOGIN_USER_KEY, () -> { - SaSession session = StpUtil.getTokenSession(); - if (ObjectUtil.isNull(session)) { - return null; - } - return session.get(LOGIN_USER_KEY); - }); + SaSession session = StpUtil.getTokenSession(); + if (ObjectUtil.isNull(session)) { + return null; + } + return (LoginUser) session.get(LOGIN_USER_KEY); } /** @@ -90,7 +81,7 @@ public class LoginHelper { * 获取用户id */ public static Long getUserId() { - return Convert.toLong(getExtra(USER_KEY)); + return Convert.toLong(getExtra(USER_KEY)); } /** @@ -107,8 +98,18 @@ public class LoginHelper { return Convert.toLong(getExtra(DEPT_KEY)); } + /** + * 获取当前 Token 的扩展信息 + * + * @param key 键值 + * @return 对应的扩展数据 + */ private static Object getExtra(String key) { - return getStorageIfAbsentSet(key, () -> StpUtil.getExtra(key)); + try { + return StpUtil.getExtra(key); + } catch (Exception e) { + return null; + } } /** @@ -136,12 +137,17 @@ public class LoginHelper { return UserConstants.SUPER_ADMIN_ID.equals(userId); } + /** + * 是否为超级管理员 + * + * @return 结果 + */ public static boolean isSuperAdmin() { return isSuperAdmin(getUserId()); } /** - * 是否为超级管理员 + * 是否为租户管理员 * * @param rolePermission 角色权限标识组 * @return 结果 @@ -150,27 +156,22 @@ public class LoginHelper { return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY); } + /** + * 是否为租户管理员 + * + * @return 结果 + */ public static boolean isTenantAdmin() { - Object value = getStorageIfAbsentSet(TENANT_ADMIN_KEY, () -> { - return isTenantAdmin(getLoginUser().getRolePermission()); - }); - return Convert.toBool(value); + return Convert.toBool(isTenantAdmin(getLoginUser().getRolePermission())); } + /** + * 检查当前用户是否已登录 + * + * @return 结果 + */ public static boolean isLogin() { return getLoginUser() != null; } - public static Object getStorageIfAbsentSet(String key, Supplier handle) { - try { - Object obj = SaHolder.getStorage().get(key); - if (ObjectUtil.isNull(obj)) { - obj = handle.get(); - SaHolder.getStorage().set(key, obj); - } - return obj; - } catch (Exception e) { - return null; - } - } } diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java index 63d7eb15b..b9283e038 100644 --- a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java +++ b/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java @@ -4,14 +4,14 @@ import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.interceptor.SaInterceptor; import cn.dev33.satoken.router.SaRouter; import cn.dev33.satoken.stp.StpUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; 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.satoken.utils.LoginHelper; import org.dromara.common.security.config.properties.SecurityProperties; 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.context.properties.EnableConfigurationProperties; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; @@ -38,35 +38,35 @@ public class SecurityConfig implements WebMvcConfigurer { public void addInterceptors(InterceptorRegistry registry) { // 注册路由拦截器,自定义验证规则 registry.addInterceptor(new SaInterceptor(handler -> { - AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class); - // 登录验证 -- 排除多个路径 - SaRouter - // 获取所有的 - .match(allUrlHandler.getUrls()) - // 对未排除的路径进行检查 - .check(() -> { - // 检查是否登录 是否有token - StpUtil.checkLogin(); + AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class); + // 登录验证 -- 排除多个路径 + SaRouter + // 获取所有的 + .match(allUrlHandler.getUrls()) + // 对未排除的路径进行检查 + .check(() -> { + // 检查是否登录 是否有token + StpUtil.checkLogin(); - // 检查 header 与 param 里的 clientid 与 token 里的是否一致 - String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY); - String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY); - String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString(); - if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) { - // token 无效 - throw NotLoginException.newInstance(StpUtil.getLoginType(), - "-100", "客户端ID与Token不匹配", - StpUtil.getTokenValue()); - } + // 检查 header 与 param 里的 clientid 与 token 里的是否一致 + String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY); + String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY); + String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString(); + if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) { + // token 无效 + throw NotLoginException.newInstance(StpUtil.getLoginType(), + "-100", "客户端ID与Token不匹配", + StpUtil.getTokenValue()); + } - // 有效率影响 用于临时测试 - // if (log.isDebugEnabled()) { - // log.info("剩余有效时间: {}", StpUtil.getTokenTimeout()); - // log.info("临时有效时间: {}", StpUtil.getTokenActivityTimeout()); - // } + // 有效率影响 用于临时测试 + // if (log.isDebugEnabled()) { + // log.info("剩余有效时间: {}", StpUtil.getTokenTimeout()); + // log.info("临时有效时间: {}", StpUtil.getTokenActivityTimeout()); + // } - }); - })).addPathPatterns("/**") + }); + })).addPathPatterns("/**") // 排除不需要拦截的路径 .excludePathPatterns(securityProperties.getExcludes()); } diff --git a/ruoyi-common/ruoyi-common-sms/pom.xml b/ruoyi-common/ruoyi-common-sms/pom.xml index c50f222ac..932cb9d52 100644 --- a/ruoyi-common/ruoyi-common-sms/pom.xml +++ b/ruoyi-common/ruoyi-common-sms/pom.xml @@ -20,13 +20,12 @@ org.dromara.sms4j sms4j-spring-boot-starter - - - - com.alibaba - fastjson - - + + + + + org.dromara + ruoyi-common-redis diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java index 29f60fc50..1e88407e8 100644 --- a/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java +++ b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java @@ -1,14 +1,24 @@ package org.dromara.common.sms.config; +import org.dromara.common.sms.core.dao.PlusSmsDao; +import org.dromara.sms4j.api.dao.SmsDao; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; /** - * 短信配置类(暂时没用 预留扩展) + * 短信配置类 * - * @author Lion Li - * @version 4.2.0 + * @author Feng */ -@AutoConfiguration +@AutoConfiguration(after = {RedisAutoConfiguration.class}) public class SmsAutoConfiguration { + @Primary + @Bean + public SmsDao smsDao() { + return new PlusSmsDao(); + } + } diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/dao/PlusSmsDao.java b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/dao/PlusSmsDao.java new file mode 100644 index 000000000..91d8d243a --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/dao/PlusSmsDao.java @@ -0,0 +1,72 @@ +package org.dromara.common.sms.core.dao; + +import org.dromara.common.core.constant.GlobalConstants; +import org.dromara.common.redis.utils.RedisUtils; +import org.dromara.sms4j.api.dao.SmsDao; + +import java.time.Duration; + +/** + * SmsDao缓存配置 (使用框架自带RedisUtils实现 协议统一) + * 主要用于短信重试和拦截的缓存 + * + * @author Feng + */ +public class PlusSmsDao implements SmsDao { + + /** + * 存储 + * + * @param key 键 + * @param value 值 + * @param cacheTime 缓存时间(单位:秒) + */ + @Override + public void set(String key, Object value, long cacheTime) { + RedisUtils.setCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key, value, Duration.ofSeconds(cacheTime)); + } + + /** + * 存储 + * + * @param key 键 + * @param value 值 + */ + @Override + public void set(String key, Object value) { + RedisUtils.setCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key, value, true); + } + + /** + * 读取 + * + * @param key 键 + * @return 值 + */ + @Override + public Object get(String key) { + return RedisUtils.getCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + /** + * remove + * 根据key移除缓存 + * + * @param key 缓存键 + * @return 被删除的value + * @author :Wind + */ + @Override + public Object remove(String key) { + return RedisUtils.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + /** + * 清空 + */ + @Override + public void clean() { + RedisUtils.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + "sms:"); + } + +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java index 76be234c6..5f49d9c27 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java @@ -2,6 +2,8 @@ package org.dromara.common.social.config.properties; import lombok.Data; +import java.util.List; + /** * 社交登录配置 * @@ -65,4 +67,9 @@ public class SocialLoginConfigProperties { */ private String serverUrl; + /** + * 请求范围 + */ + private List scopes; + } diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java index 1beb7d042..1487a6a92 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java @@ -16,11 +16,6 @@ import java.util.Map; @ConfigurationProperties(prefix = "justauth") public class SocialProperties { - /** - * 是否启用 - */ - private Boolean enabled; - /** * 授权类型 */ diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java new file mode 100644 index 000000000..13649f9fd --- /dev/null +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java @@ -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")); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java new file mode 100644 index 000000000..e47d6c640 --- /dev/null +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java @@ -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; + } + + } +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java index 51b7e13d7..04f621489 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java @@ -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.SocialProperties; 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() .clientId(obj.getClientId()) .clientSecret(obj.getClientSecret()) - .redirectUri(obj.getRedirectUri()); + .redirectUri(obj.getRedirectUri()) + .scopes(obj.getScopes()); return switch (source.toLowerCase()) { case "dingtalk" -> new AuthDingTalkRequest(builder.build(), STATE_CACHE); case "baidu" -> new AuthBaiduRequest(builder.build(), STATE_CACHE); @@ -63,6 +65,7 @@ public class SocialUtils { case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE); case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE); case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE); + case "topiam" -> new AuthTopIamRequest(builder.build(), STATE_CACHE); default -> throw new AuthException("未获取到有效的Auth配置"); }; } diff --git a/ruoyi-common/ruoyi-common-tenant/pom.xml b/ruoyi-common/ruoyi-common-tenant/pom.xml index 2b87bf961..8e1a6ab1e 100644 --- a/ruoyi-common/ruoyi-common-tenant/pom.xml +++ b/ruoyi-common/ruoyi-common-tenant/pom.xml @@ -19,6 +19,7 @@ org.dromara ruoyi-common-mybatis + true @@ -26,11 +27,6 @@ ruoyi-common-redis - - com.alibaba - transmittable-thread-local - - diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java index 8610641dd..0ff39fdc0 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java @@ -2,8 +2,6 @@ package org.dromara.common.tenant.config; import cn.dev33.satoken.dao.SaTokenDao; import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; -import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.mybatis.config.MybatisPlusConfig; @@ -24,9 +22,6 @@ import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; -import java.util.ArrayList; -import java.util.List; - /** * 租户配置类 * @@ -37,23 +32,10 @@ import java.util.List; @ConditionalOnProperty(value = "tenant.enable", havingValue = "true") public class TenantConfig { - /** - * 初始化租户配置 - */ - @Bean - public boolean tenantInit(MybatisPlusInterceptor mybatisPlusInterceptor, - TenantProperties tenantProperties) { - List interceptors = new ArrayList<>(); - // 多租户插件 必须放到第一位 - interceptors.add(tenantLineInnerInterceptor(tenantProperties)); - interceptors.addAll(mybatisPlusInterceptor.getInterceptors()); - mybatisPlusInterceptor.setInterceptors(interceptors); - return true; - } - /** * 多租户插件 */ + @Bean public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties) { return new TenantLineInnerInterceptor(new PlusTenantLineHandler(tenantProperties)); } diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java index 14dad1a71..947539863 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java @@ -1,5 +1,6 @@ package org.dromara.common.tenant.handle; +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.utils.StringUtils; @@ -26,6 +27,9 @@ public class TenantKeyPrefixHandler extends KeyPrefixHandler { if (StringUtils.isBlank(name)) { return null; } + if (InterceptorIgnoreHelper.willIgnoreTenantLine("")) { + return super.map(name); + } if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) { return super.map(name); } @@ -49,6 +53,9 @@ public class TenantKeyPrefixHandler extends KeyPrefixHandler { if (StringUtils.isBlank(unmap)) { return null; } + if (InterceptorIgnoreHelper.willIgnoreTenantLine("")) { + return super.unmap(name); + } if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) { return super.unmap(name); } diff --git a/ruoyi-common/ruoyi-common-web/pom.xml b/ruoyi-common/ruoyi-common-web/pom.xml index 293c52229..b250fa9d0 100644 --- a/ruoyi-common/ruoyi-common-web/pom.xml +++ b/ruoyi-common/ruoyi-common-web/pom.xml @@ -57,11 +57,6 @@ cn.hutool hutool-crypto - - - com.alibaba - transmittable-thread-local - diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java index 9225a51bf..650517ed1 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java @@ -23,7 +23,7 @@ public class CaptchaConfig { private static final int WIDTH = 160; private static final int HEIGHT = 60; - private static final Color BACKGROUND = Color.PINK; + private static final Color BACKGROUND = Color.LIGHT_GRAY; private static final Font FONT = new Font("Arial", Font.BOLD, 48); /** diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java index 421ce6d14..0f7892861 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java @@ -2,9 +2,11 @@ package org.dromara.common.web.config; import io.undertow.server.DefaultByteBufferPool; import io.undertow.websockets.jsr.WebSocketDeploymentInfo; +import org.dromara.common.core.utils.SpringUtils; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.core.task.VirtualThreadTaskExecutor; /** * Undertow 自定义配置 @@ -24,6 +26,12 @@ public class UndertowConfig implements WebServerFactoryCustomizer invokeTimeTL = new TransmittableThreadLocal<>(); + private final static ThreadLocal KEY_CACHE = new ThreadLocal<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { @@ -56,7 +55,7 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { } StopWatch stopWatch = new StopWatch(); - invokeTimeTL.set(stopWatch); + KEY_CACHE.set(stopWatch); stopWatch.start(); } return true; @@ -70,10 +69,10 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { if (!prodProfile.equals(SpringUtils.getActiveProfile())) { - StopWatch stopWatch = invokeTimeTL.get(); + StopWatch stopWatch = KEY_CACHE.get(); stopWatch.stop(); log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime()); - invokeTimeTL.remove(); + KEY_CACHE.remove(); } } diff --git a/ruoyi-extend/pom.xml b/ruoyi-extend/pom.xml index f5348cc13..0e9533f18 100644 --- a/ruoyi-extend/pom.xml +++ b/ruoyi-extend/pom.xml @@ -14,6 +14,7 @@ ruoyi-monitor-admin ruoyi-powerjob-server + ruoyi-easyretry-server diff --git a/ruoyi-extend/ruoyi-easyretry-server/Dockerfile b/ruoyi-extend/ruoyi-easyretry-server/Dockerfile new file mode 100644 index 000000000..7ad0a0776 --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/Dockerfile @@ -0,0 +1,18 @@ +#FROM findepi/graalvm:java17-native +FROM openjdk:17.0.2-oraclelinux8 + +MAINTAINER Lion Li + +RUN mkdir -p /ruoyi/easyretry/logs + +WORKDIR /ruoyi/easyretry + +ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="-Xms512m -Xmx1024m" + +EXPOSE 8800 +EXPOSE 1788 + +ADD ./target/ruoyi-easyretry-server.jar ./app.jar + +ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -jar app.jar \ + -XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*,:time,tags,level -XX:+UseZGC ${JAVA_OPTS} diff --git a/ruoyi-extend/ruoyi-easyretry-server/pom.xml b/ruoyi-extend/ruoyi-easyretry-server/pom.xml new file mode 100644 index 000000000..4ea4d743b --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/pom.xml @@ -0,0 +1,46 @@ + + + + org.dromara + ruoyi-extend + ${revision} + + 4.0.0 + jar + ruoyi-easyretry-server + + + + com.aizuda + easy-retry-server-starter + ${easyretry.version} + + + + de.codecentric + spring-boot-admin-starter-client + ${spring-boot-admin.version} + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + + diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/java/org/dromara/easyretry/EasyRetryServerApplication.java b/ruoyi-extend/ruoyi-easyretry-server/src/main/java/org/dromara/easyretry/EasyRetryServerApplication.java new file mode 100644 index 000000000..9d01bc05b --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/java/org/dromara/easyretry/EasyRetryServerApplication.java @@ -0,0 +1,18 @@ +package org.dromara.easyretry; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * EasyRetry Server 启动程序 + * + * @author dhb52 + */ +@SpringBootApplication +public class EasyRetryServerApplication { + + public static void main(String[] args) { + SpringApplication.run(com.aizuda.easy.retry.server.EasyRetryServerApplication.class, args); + } + +} diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-dev.yml b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-dev.yml new file mode 100644 index 000000000..c151a6195 --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-dev.yml @@ -0,0 +1,50 @@ +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: root + hikari: + connection-timeout: 30000 + validation-timeout: 5000 + minimum-idle: 10 + maximum-pool-size: 20 + idle-timeout: 600000 + max-lifetime: 900000 + keepaliveTime: 30000 + +--- # easy-retry 服务端配置 +easy-retry: + # 拉取重试数据的每批次的大小 + retry-pull-page-size: 1000 + # 拉取重试数据的每批次的大小 + job-pull-page-size: 1000 + # 服务端 netty 端口 + netty-port: 1788 + # 重试和死信表的分区总数 + total-partition: 2 + # 一个客户端每秒最多接收的重试数量指令 + limiter: 1000 + # 号段模式下步长配置 + step: 100 + # 日志保存时间(单位: day) + log-storage: 90 + # 回调配置 + callback: + #回调最大执行次数 + max-count: 288 + #间隔时间 + trigger-interval: 900 + mode: all + retry-max-pull-count: 10 + +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: true + url: http://localhost:9090/admin + instance: + service-host-type: IP + username: ruoyi + password: 123456 diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-prod.yml b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-prod.yml new file mode 100644 index 000000000..c151a6195 --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-prod.yml @@ -0,0 +1,50 @@ +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: root + hikari: + connection-timeout: 30000 + validation-timeout: 5000 + minimum-idle: 10 + maximum-pool-size: 20 + idle-timeout: 600000 + max-lifetime: 900000 + keepaliveTime: 30000 + +--- # easy-retry 服务端配置 +easy-retry: + # 拉取重试数据的每批次的大小 + retry-pull-page-size: 1000 + # 拉取重试数据的每批次的大小 + job-pull-page-size: 1000 + # 服务端 netty 端口 + netty-port: 1788 + # 重试和死信表的分区总数 + total-partition: 2 + # 一个客户端每秒最多接收的重试数量指令 + limiter: 1000 + # 号段模式下步长配置 + step: 100 + # 日志保存时间(单位: day) + log-storage: 90 + # 回调配置 + callback: + #回调最大执行次数 + max-count: 288 + #间隔时间 + trigger-interval: 900 + mode: all + retry-max-pull-count: 10 + +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: true + url: http://localhost:9090/admin + instance: + service-host-type: IP + username: ruoyi + password: 123456 diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application.yml b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application.yml new file mode 100644 index 000000000..b6d8998e8 --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application.yml @@ -0,0 +1,40 @@ +server: + port: 8800 + servlet: + context-path: /easy-retry + +spring: + application: + name: ruoyi-easyretry-server + profiles: + active: @profiles.active@ + web: + resources: + static-locations: classpath:admin/ + +mybatis-plus: + typeAliasesPackage: com.aizuda.easy.retry.template.datasource.persistence.po + global-config: + db-config: + table-prefix: er_ + where-strategy: NOT_EMPTY + capital-mode: false + logic-delete-value: 1 + logic-not-delete-value: 0 + configuration: + map-underscore-to-camel-case: true + cache-enabled: true + +logging: + config: classpath:logback-plus.xml + +management: + endpoints: + web: + exposure: + include: '*' + endpoint: + health: + show-details: ALWAYS + logfile: + external-file: ./logs/ruoyi-easyretry-server/console.log diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/banner.txt b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/banner.txt new file mode 100644 index 000000000..869a4209c --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/banner.txt @@ -0,0 +1,10 @@ +Application Version: ${revision} +Spring Boot Version: ${spring-boot.version} + _ + | | + ___ __ _ ___ _ _ ______ _ __ ___| |_ _ __ _ _ ______ ___ ___ _ ____ _____ _ __ + / _ \/ _` / __| | | |______| '__/ _ \ __| '__| | | |______/ __|/ _ \ '__\ \ / / _ \ '__| +| __/ (_| \__ \ |_| | | | | __/ |_| | | |_| | \__ \ __/ | \ V / __/ | + \___|\__,_|___/\__, | |_| \___|\__|_| \__, | |___/\___|_| \_/ \___|_| + __/ | __/ | + |___/ |___/ diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/logback-plus.xml b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/logback-plus.xml new file mode 100644 index 000000000..bcd3abc4c --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/logback-plus.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + ${console.log.pattern} + utf-8 + + + + + + ${log.path}/console.log + + + ${log.path}/console.%d{yyyy-MM-dd}.log + + 1 + + + ${log.pattern} + utf-8 + + + + INFO + + + + + ${log.path}/info.log + + ${log.path}/info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + INFO + ACCEPT + DENY + + + + + ${log.path}/error.log + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + ERROR + ACCEPT + DENY + + + + + 100 + 1024 + + + + + 100 + 1024 + + + + + + + + + + + + + + + diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java index 1f70c75f7..53d248e39 100644 --- a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java +++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java @@ -3,7 +3,7 @@ package org.dromara.monitor.admin.config; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; -import org.springframework.boot.task.TaskExecutorBuilder; +import org.springframework.boot.task.ThreadPoolTaskExecutorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; @@ -23,7 +23,7 @@ public class AdminServerConfig { @Lazy @Bean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME) @ConditionalOnMissingBean(Executor.class) - public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) { + public ThreadPoolTaskExecutor applicationTaskExecutor(ThreadPoolTaskExecutorBuilder builder) { return builder.build(); } diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java index fb1973148..b993f60b5 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java @@ -5,7 +5,6 @@ import org.dromara.common.core.domain.R; import org.dromara.sms4j.api.SmsBlend; import org.dromara.sms4j.api.entity.SmsResponse; import org.dromara.sms4j.core.factory.SmsFactory; -import org.dromara.sms4j.provider.enumerate.SupplierType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -25,7 +24,6 @@ import java.util.LinkedHashMap; @RestController @RequestMapping("/demo/sms") public class SmsController { - /** * 发送短信Aliyun * @@ -36,7 +34,7 @@ public class SmsController { public R sendAliyun(String phones, String templateId) { LinkedHashMap map = new LinkedHashMap<>(1); map.put("code", "1234"); - SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA); + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map); return R.ok(smsResponse); } @@ -52,9 +50,33 @@ public class SmsController { LinkedHashMap map = new LinkedHashMap<>(1); // map.put("2", "测试测试"); map.put("1", "1234"); - SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.TENCENT); + SmsBlend smsBlend = SmsFactory.getSmsBlend("config2"); SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map); return R.ok(smsResponse); } + /** + * 添加黑名单 + * + * @param phone 手机号 + */ + @GetMapping("/addBlacklist") + public R addBlacklist(String phone){ + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + smsBlend.joinInBlacklist(phone); + return R.ok(); + } + + /** + * 移除黑名单 + * + * @param phone 手机号 + */ + @GetMapping("/removeBlacklist") + public R removeBlacklist(String phone){ + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + smsBlend.removeFromBlacklist(phone); + return R.ok(); + } + } diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java index 5ce7ad043..ebdb9930c 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java @@ -42,7 +42,7 @@ public class GenTableColumn extends BaseEntity { /** * 列描述 */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String columnComment; /** @@ -64,43 +64,43 @@ public class GenTableColumn extends BaseEntity { /** * 是否主键(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isPk; /** * 是否自增(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isIncrement; /** * 是否必填(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isRequired; /** * 是否为插入字段(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isInsert; /** * 是否编辑字段(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isEdit; /** * 是否列表字段(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isList; /** * 是否查询字段(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isQuery; /** diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm index 14177b5df..64389717d 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm @@ -20,7 +20,7 @@ import org.dromara.common.excel.utils.ExcelUtil; import ${packageName}.domain.vo.${ClassName}Vo; import ${packageName}.domain.bo.${ClassName}Bo; import ${packageName}.service.I${ClassName}Service; -#if($table.crud || $table.sub) +#if($table.crud) import org.dromara.common.mybatis.core.page.TableDataInfo; #elseif($table.tree) #end @@ -44,7 +44,7 @@ public class ${ClassName}Controller extends BaseController { */ @SaCheckPermission("${permissionPrefix}:list") @GetMapping("/list") -#if($table.crud || $table.sub) +#if($table.crud) public TableDataInfo<${ClassName}Vo> list(${ClassName}Bo bo, PageQuery pageQuery) { return ${className}Service.queryPageList(bo, pageQuery); } diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm index d596a0e91..f1fa3d991 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm @@ -3,7 +3,7 @@ package ${packageName}.service; import ${packageName}.domain.${ClassName}; import ${packageName}.domain.vo.${ClassName}Vo; import ${packageName}.domain.bo.${ClassName}Bo; -#if($table.crud || $table.sub) +#if($table.crud) import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; #end @@ -24,7 +24,7 @@ public interface I${ClassName}Service { */ ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}); -#if($table.crud || $table.sub) +#if($table.crud) /** * 查询${functionName}列表 */ diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm index 75a9b83a2..d603de120 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -2,7 +2,7 @@ package ${packageName}.service.impl; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; -#if($table.crud || $table.sub) +#if($table.crud) import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -41,7 +41,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { return baseMapper.selectVoById(${pkColumn.javaField}); } -#if($table.crud || $table.sub) +#if($table.crud) /** * 查询${functionName}列表 */ diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm index 8b132f4ab..aaceca611 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -439,7 +439,7 @@ const submitForm = () => { } else { await add${BusinessName}(form.value).finally(() => buttonLoading.value = false); } - proxy?.#[[$modal]]#.msgSuccess("修改成功"); + proxy?.#[[$modal]]#.msgSuccess("操作成功"); dialog.visible = false; await getList(); } diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestAnnoJobExecutor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestAnnoJobExecutor.java new file mode 100644 index 000000000..05c4f2fb0 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestAnnoJobExecutor.java @@ -0,0 +1,23 @@ +package org.dromara.job.easyretry; + +import com.aizuda.easy.retry.client.job.core.annotation.JobExecutor; +import com.aizuda.easy.retry.client.job.core.dto.JobArgs; +import com.aizuda.easy.retry.client.model.ExecuteResult; +import com.aizuda.easy.retry.common.core.util.JsonUtil; +import com.aizuda.easy.retry.common.log.EasyRetryLog; +import org.springframework.stereotype.Component; + +/** + * @author www.byteblogs.com + * @date 2023-09-28 22:54:07 + */ +@Component +@JobExecutor(name = "testJobExecutor") +public class TestAnnoJobExecutor { + + public ExecuteResult jobExecute(JobArgs jobArgs) { + EasyRetryLog.LOCAL.info("testJobExecutor. jobArgs:{}", JsonUtil.toJsonString(jobArgs)); + EasyRetryLog.REMOTE.info("testJobExecutor. jobArgs:{}", JsonUtil.toJsonString(jobArgs)); + return ExecuteResult.success("测试成功"); + } +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestClassJobExecutor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestClassJobExecutor.java new file mode 100644 index 000000000..d675cec73 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestClassJobExecutor.java @@ -0,0 +1,19 @@ +package org.dromara.job.easyretry; + +import com.aizuda.easy.retry.client.job.core.dto.JobArgs; +import com.aizuda.easy.retry.client.job.core.executor.AbstractJobExecutor; +import com.aizuda.easy.retry.client.model.ExecuteResult; +import org.springframework.stereotype.Component; + +/** + * @author www.byteblogs.com + * @date 2023-09-28 22:54:07 + */ +@Component +public class TestClassJobExecutor extends AbstractJobExecutor { + + @Override + protected ExecuteResult doJobExecute(JobArgs jobArgs) { + return ExecuteResult.success("TestJobExecutor测试成功"); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java index def448d4d..9b08c2d2c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java @@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** * 在线用户监控 @@ -87,4 +88,43 @@ public class SysUserOnlineController extends BaseController { } return R.ok(); } + + /** + * 获取当前用户登录在线设备 + */ + @GetMapping() + public TableDataInfo getInfo() { + // 获取指定账号 id 的 token 集合 + List tokenIds = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString()); + List userOnlineDTOList = tokenIds.stream() + .filter(token -> StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) >= -1) + .map(token -> (UserOnlineDTO) RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token)) + .collect(Collectors.toList()); + //复制和处理 SysUserOnline 对象列表 + Collections.reverse(userOnlineDTOList); + userOnlineDTOList.removeAll(Collections.singleton(null)); + List userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class); + return TableDataInfo.build(userOnlineList); + } + + /** + * 强退当前在线设备 + * + * @param tokenId token值 + */ + @Log(title = "在线设备", businessType = BusinessType.FORCE) + @PostMapping("/{tokenId}") + public R remove(@PathVariable("tokenId") String tokenId) { + try { + // 获取指定账号 id 的 token 集合 + List keys = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString()); + keys.stream() + .filter(key -> key.equals(tokenId)) + .findFirst() + .ifPresent(key -> StpUtil.kickoutByTokenValue(tokenId)); + } catch (NotLoginException ignored) { + } + return R.ok(); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java index 61d30c76a..13be4a4a9 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java @@ -97,7 +97,7 @@ public class SysClientController extends BaseController { @Log(title = "客户端管理", businessType = BusinessType.UPDATE) @PutMapping("/changeStatus") public R changeStatus(@RequestBody SysClientBo bo) { - return toAjax(sysClientService.updateUserStatus(bo.getId(), bo.getStatus())); + return toAjax(sysClientService.updateUserStatus(bo.getClientId(), bo.getStatus())); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java index 130c0d1f3..4f5f23f32 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java @@ -2,6 +2,7 @@ package org.dromara.system.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.convert.Convert; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.domain.R; import org.dromara.common.core.utils.StringUtils; @@ -11,7 +12,6 @@ import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.bo.SysDeptBo; import org.dromara.system.domain.vo.SysDeptVo; import org.dromara.system.service.ISysDeptService; -import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -120,4 +120,16 @@ public class SysDeptController extends BaseController { deptService.checkDeptDataScope(deptId); return toAjax(deptService.deleteDeptById(deptId)); } + + /** + * 获取部门选择框列表 + * + * @param deptIds 部门ID串 + */ + @SaCheckPermission("system:dept:query") + @GetMapping("/optionselect") + public R> optionselect(@RequestParam(required = false) Long[] deptIds) { + return R.ok(deptService.selectDeptByIds(deptIds == null ? null : List.of(deptIds))); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java index fe62fdbbc..330b3fb52 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java @@ -1,6 +1,8 @@ package org.dromara.system.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.domain.R; import org.dromara.common.excel.utils.ExcelUtil; @@ -12,8 +14,6 @@ import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.bo.SysPostBo; import org.dromara.system.domain.vo.SysPostVo; import org.dromara.system.service.ISysPostService; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -110,12 +110,13 @@ public class SysPostController extends BaseController { /** * 获取岗位选择框列表 + * + * @param postIds 岗位ID串 */ + @SaCheckPermission("system:post:query") @GetMapping("/optionselect") - public R> optionselect() { - SysPostBo postBo = new SysPostBo(); - postBo.setStatus(UserConstants.POST_NORMAL); - List posts = postService.selectPostList(postBo); - return R.ok(posts); + public R> optionselect(@RequestParam(required = false) Long[] postIds) { + return R.ok(postService.selectPostByIds(postIds == null ? null : List.of(postIds))); } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java index f9c4b3d31..0cdb67548 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java @@ -19,6 +19,7 @@ import org.dromara.system.domain.vo.ProfileVo; import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.service.ISysOssService; +import org.dromara.system.service.ISysRoleService; import org.dromara.system.service.ISysUserService; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; @@ -50,8 +51,8 @@ public class SysProfileController extends BaseController { SysUserVo user = userService.selectUserById(LoginHelper.getUserId()); ProfileVo profileVo = new ProfileVo(); profileVo.setUser(user); - profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserName())); - profileVo.setPostGroup(userService.selectUserPostGroup(user.getUserName())); + profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserId())); + profileVo.setPostGroup(userService.selectUserPostGroup(user.getUserId())); return R.ok(profileVo); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java index 3a9418b23..d4a9dc8a4 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java @@ -149,11 +149,13 @@ public class SysRoleController extends BaseController { /** * 获取角色选择框列表 + * + * @param roleIds 角色ID串 */ @SaCheckPermission("system:role:query") @GetMapping("/optionselect") - public R> optionselect() { - return R.ok(roleService.selectRoleAll()); + public R> optionselect(@RequestParam(required = false) Long[] roleIds) { + return R.ok(roleService.selectRoleByIds(roleIds == null ? null : List.of(roleIds))); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java index beefe4a81..b1502ac92 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java @@ -11,7 +11,6 @@ import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.model.LoginUser; -import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.encrypt.annotation.ApiEncrypt; @@ -72,9 +71,8 @@ public class SysUserController extends BaseController { @SaCheckPermission("system:user:export") @PostMapping("/export") public void export(SysUserBo user, HttpServletResponse response) { - List list = userService.selectUserList(user); - List listVo = MapstructUtils.convert(list, SysUserExportVo.class); - ExcelUtil.exportExcel(listVo, "用户数据", SysUserExportVo.class, response); + List list = userService.selectUserExportList(user); + ExcelUtil.exportExcel(list, "用户数据", SysUserExportVo.class, response); } /** @@ -142,7 +140,7 @@ public class SysUserController extends BaseController { if (ObjectUtil.isNotNull(userId)) { SysUserVo sysUser = userService.selectUserById(userId); userInfoVo.setUser(sysUser); - userInfoVo.setRoleIds(StreamUtils.toList(sysUser.getRoles(), SysRoleVo::getRoleId)); + userInfoVo.setRoleIds(roleService.selectRoleListByUserId(userId)); userInfoVo.setPostIds(postService.selectPostListByUserId(userId)); } return R.ok(userInfoVo); @@ -207,6 +205,19 @@ public class SysUserController extends BaseController { return toAjax(userService.deleteUserByIds(userIds)); } + /** + * 根据用户ID串批量获取用户基础信息 + * + * @param userIds 用户ID串 + * @param deptId 部门ID + */ + @SaCheckPermission("system:user:query") + @GetMapping("/optionselect") + public R> optionselect(@RequestParam(required = false) Long[] userIds, + @RequestParam(required = false) Long deptId) { + return R.ok(userService.selectUserByIds(userIds == null ? null : List.of(userIds), deptId)); + } + /** * 重置密码 */ @@ -241,8 +252,9 @@ public class SysUserController extends BaseController { @SaCheckPermission("system:user:query") @GetMapping("/authRole/{userId}") public R authRole(@PathVariable Long userId) { + userService.checkUserDataScope(userId); SysUserVo user = userService.selectUserById(userId); - List roles = roleService.selectRolesByUserId(userId); + List roles = roleService.selectRolesAuthByUserId(userId); SysUserInfoVo userInfoVo = new SysUserInfoVo(); userInfoVo.setUser(user); userInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isSuperAdmin())); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java index 6def906e1..ba30eb609 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java @@ -7,7 +7,6 @@ import lombok.Data; * * @author Lion Li */ - @Data public class SysUserOnline { diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java index 39257f70b..fcc1ac11d 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java @@ -6,6 +6,7 @@ import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import lombok.Data; import lombok.EqualsAndHashCode; +import org.dromara.common.core.constant.RegexConstants; import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.system.domain.SysDictType; @@ -37,7 +38,7 @@ public class SysDictTypeBo extends BaseEntity { */ @NotBlank(message = "字典类型不能为空") @Size(min = 0, max = 100, message = "字典类型类型长度不能超过{max}个字符") - @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + @Pattern(regexp = RegexConstants.DICTIONARY_TYPE, message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") private String dictType; /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java index 498280a90..fbaafaa63 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java @@ -4,9 +4,11 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.github.linpeilie.annotations.AutoMapper; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import lombok.Data; import lombok.EqualsAndHashCode; +import org.dromara.common.core.constant.RegexConstants; import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.system.domain.SysMenu; @@ -92,6 +94,7 @@ public class SysMenuBo extends BaseEntity { */ @JsonInclude(JsonInclude.Include.NON_NULL) @Size(min = 0, max = 100, message = "权限标识长度不能超过{max}个字符") + @Pattern(regexp = RegexConstants.PERMISSION_STRING, message = "权限标识必须符合 tool:build:list 格式") private String perms; /** @@ -104,5 +107,4 @@ public class SysMenuBo extends BaseEntity { */ private String remark; - } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java index 3cc5186c8..37ec6b709 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java @@ -20,7 +20,6 @@ import java.util.Date; @Data @NoArgsConstructor -@AutoMapper(target = SysUserVo.class, convertGenerate = false) public class SysUserExportVo implements Serializable { @Serial @@ -85,14 +84,12 @@ public class SysUserExportVo implements Serializable { /** * 部门名称 */ - @ReverseAutoMapping(target = "deptName", source = "dept.deptName") @ExcelProperty(value = "部门名称") private String deptName; /** * 负责人 */ - @ReverseAutoMapping(target = "leaderName", source = "dept.leaderName") @ExcelProperty(value = "部门负责人") private String leaderName; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java index 7db91d180..d1f405919 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java @@ -114,9 +114,10 @@ public class SysUserVo implements Serializable { private Date createTime; /** - * 部门对象 + * 部门名 */ - private SysDeptVo dept; + @Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "deptId") + private String deptName; /** * 角色对象 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java index 45ad77e9c..08dda662c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java @@ -32,7 +32,7 @@ public interface SysDeptMapper extends BaseMapperPlus { @DataPermission({ @DataColumn(key = "deptName", value = "dept_id") }) - SysDeptVo selectDeptById(Long deptId); + long countDeptById(Long deptId); /** * 根据角色ID查询部门树信息 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java index 48e6a121f..d6d27596f 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java @@ -13,20 +13,12 @@ import java.util.List; */ public interface SysPostMapper extends BaseMapperPlus { - /** - * 根据用户ID获取岗位选择框列表 - * - * @param userId 用户ID - * @return 选中岗位ID列表 - */ - List selectPostListByUserId(Long userId); - /** * 查询用户所属岗位组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ - List selectPostsByUserName(String userName); + List selectPostsByUserId(Long userId); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java index 55ca76976..ac5a47e54 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java @@ -3,12 +3,12 @@ package org.dromara.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.system.domain.SysRole; import org.dromara.system.domain.vo.SysRoleVo; -import org.apache.ibatis.annotations.Param; import java.util.List; @@ -51,21 +51,12 @@ public interface SysRoleMapper extends BaseMapperPlus { */ List selectRolePermissionByUserId(Long userId); - - /** - * 根据用户ID获取角色选择框列表 - * - * @param userId 用户ID - * @return 选中角色ID列表 - */ - List selectRoleListByUserId(Long userId); - /** * 根据用户ID查询角色 * - * @param userName 用户名 + * @param userId 用户ID * @return 角色列表 */ - List selectRolesByUserName(String userName); + List selectRolesByUserId(Long userId); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java index 43222253c..fc7fc6e16 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java @@ -8,6 +8,7 @@ import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.vo.SysUserExportVo; import org.dromara.system.domain.vo.SysUserVo; import java.util.List; @@ -20,11 +21,17 @@ import java.util.List; public interface SysUserMapper extends BaseMapperPlus { @DataPermission({ - @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "deptName", value = "u.dept_id"), @DataColumn(key = "userName", value = "u.user_id") }) Page selectPageUserList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + List selectUserList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + /** * 根据条件分页查询用户列表 * @@ -35,7 +42,7 @@ public interface SysUserMapper extends BaseMapperPlus { @DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "userName", value = "u.user_id") }) - List selectUserList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + List selectUserExportList(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 根据条件分页查询已配用户角色列表 @@ -61,41 +68,11 @@ public interface SysUserMapper extends BaseMapperPlus { }) Page selectUnallocatedList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); - /** - * 通过用户名查询用户 - * - * @param userName 用户名 - * @return 用户对象信息 - */ - SysUserVo selectUserByUserName(String userName); - - /** - * 通过手机号查询用户 - * - * @param phonenumber 手机号 - * @return 用户对象信息 - */ - SysUserVo selectUserByPhonenumber(String phonenumber); - - /** - * 通过邮箱查询用户 - * - * @param email 邮箱 - * @return 用户对象信息 - */ - SysUserVo selectUserByEmail(String email); - - /** - * 通过用户ID查询用户 - * - * @param userId 用户ID - * @return 用户对象信息 - */ @DataPermission({ - @DataColumn(key = "deptName", value = "d.dept_id"), - @DataColumn(key = "userName", value = "u.user_id") + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") }) - SysUserVo selectUserById(Long userId); + long countUserById(Long userId); @Override @DataPermission({ diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java index db48d4b79..d0f8a3cbe 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java @@ -25,7 +25,7 @@ public interface ISysClientService { /** * 查询客户端信息基于客户端id */ - SysClient queryByClientId(String clientId); + SysClientVo queryByClientId(String clientId); /** * 查询客户端管理列表 @@ -50,7 +50,7 @@ public interface ISysClientService { /** * 修改状态 */ - int updateUserStatus(Long id, String status); + int updateUserStatus(String clientId, String status); /** * 校验并批量删除客户端管理信息 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java index b20938187..bf16642fe 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java @@ -52,6 +52,14 @@ public interface ISysDeptService { */ SysDeptVo selectDeptById(Long deptId); + /** + * 通过部门ID串查询部门 + * + * @param deptIds 部门id串 + * @return 部门列表信息 + */ + List selectDeptByIds(List deptIds); + /** * 根据ID查询所有子部门数(正常状态) * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java index a8bc57b9c..2f6dfc9a1 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java @@ -31,12 +31,11 @@ public interface ISysOssConfigService { */ TableDataInfo queryPageList(SysOssConfigBo bo, PageQuery pageQuery); - /** * 根据新增业务对象插入对象存储配置 * * @param bo 对象存储配置新增业务对象 - * @return + * @return 结果 */ Boolean insertByBo(SysOssConfigBo bo); @@ -44,7 +43,7 @@ public interface ISysOssConfigService { * 根据编辑业务对象修改对象存储配置 * * @param bo 对象存储配置编辑业务对象 - * @return + * @return 结果 */ Boolean updateByBo(SysOssConfigBo bo); @@ -53,7 +52,7 @@ public interface ISysOssConfigService { * * @param ids 主键集合 * @param isValid 是否校验,true-删除前校验,false-不校验 - * @return + * @return 结果 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java index 2dfe01fe9..057c068ce 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java @@ -19,18 +19,62 @@ import java.util.List; */ public interface ISysOssService { + /** + * 查询OSS对象存储列表 + * + * @param sysOss OSS对象存储分页查询对象 + * @param pageQuery 分页查询实体类 + * @return 结果 + */ TableDataInfo queryPageList(SysOssBo sysOss, PageQuery pageQuery); + /** + * 根据一组 ossIds 获取对应的 SysOssVo 列表 + * + * @param ossIds 一组文件在数据库中的唯一标识集合 + * @return 包含 SysOssVo 对象的列表 + */ List listByIds(Collection ossIds); + /** + * 根据 ossId 从缓存或数据库中获取 SysOssVo 对象 + * + * @param ossId 文件在数据库中的唯一标识 + * @return SysOssVo 对象,包含文件信息 + */ SysOssVo getById(Long ossId); + /** + * 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的 MultipartFile 对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + */ SysOssVo upload(MultipartFile file); + /** + * 上传文件到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的文件对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + */ SysOssVo upload(File file); + /** + * 文件下载方法,支持一次性下载完整文件 + * + * @param ossId OSS对象ID + * @param response HttpServletResponse对象,用于设置响应头和向客户端发送文件内容 + */ void download(Long ossId, HttpServletResponse response) throws IOException; + /** + * 删除OSS对象存储 + * + * @param ids OSS对象ID串 + * @param isValid 判断是否需要校验 + * @return 结果 + */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java index c6409c862..c43f0395c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java @@ -48,6 +48,14 @@ public interface ISysPostService { */ List selectPostListByUserId(Long userId); + /** + * 通过岗位ID串查询岗位 + * + * @param postIds 岗位id串 + * @return 岗位列表信息 + */ + List selectPostByIds(List postIds); + /** * 校验岗位名称 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java index d2ee61f8a..64740aeff 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java @@ -35,6 +35,14 @@ public interface ISysRoleService { */ List selectRolesByUserId(Long userId); + /** + * 根据用户ID查询角色列表(包含被授权状态) + * + * @param userId 用户ID + * @return 角色列表 + */ + List selectRolesAuthByUserId(Long userId); + /** * 根据用户ID查询角色权限 * @@ -66,6 +74,14 @@ public interface ISysRoleService { */ SysRoleVo selectRoleById(Long roleId); + /** + * 通过角色ID串查询角色 + * + * @param roleIds 角色ID串 + * @return 角色列表信息 + */ + List selectRoleByIds(List roleIds); + /** * 校验角色名称是否唯一 * @@ -180,4 +196,5 @@ public interface ISysRoleService { int insertAuthUsers(Long roleId, Long[] userIds); void cleanOnlineUserByRole(Long roleId); + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java index bbe78be2b..0325a2558 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java @@ -3,6 +3,7 @@ package org.dromara.system.service; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.system.domain.bo.SysUserBo; +import org.dromara.system.domain.vo.SysUserExportVo; import org.dromara.system.domain.vo.SysUserVo; import java.util.List; @@ -23,7 +24,7 @@ public interface ISysUserService { * @param user 用户信息 * @return 用户信息集合信息 */ - List selectUserList(SysUserBo user); + List selectUserExportList(SysUserBo user); /** * 根据条件分页查询已分配用户角色列表 @@ -65,21 +66,30 @@ public interface ISysUserService { */ SysUserVo selectUserById(Long userId); + /** + * 通过用户ID串查询用户 + * + * @param userIds 用户ID串 + * @param deptId 部门id + * @return 用户列表信息 + */ + List selectUserByIds(List userIds, Long deptId); + /** * 根据用户ID查询用户所属角色组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ - String selectUserRoleGroup(String userName); + String selectUserRoleGroup(Long userId); /** * 根据用户ID查询用户所属岗位组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ - String selectUserPostGroup(String userName); + String selectUserPostGroup(Long userId); /** * 校验用户名称是否唯一 @@ -205,8 +215,8 @@ public interface ISysUserService { /** * 通过部门id查询当前部门所有用户 * - * @param deptId - * @return + * @param deptId 部门id + * @return 结果 */ List selectUserListByDept(Long deptId); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java index aecaf668c..26bc49157 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java @@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; @@ -16,6 +17,8 @@ import org.dromara.system.domain.bo.SysClientBo; import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.mapper.SysClientMapper; import org.dromara.system.service.ISysClientService; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.Collection; @@ -48,9 +51,10 @@ public class SysClientServiceImpl implements ISysClientService { /** * 查询客户端管理 */ + @Cacheable(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId") @Override - public SysClient queryByClientId(String clientId) { - return baseMapper.selectOne(new LambdaQueryWrapper().eq(SysClient::getClientId, clientId)); + public SysClientVo queryByClientId(String clientId) { + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysClient::getClientId, clientId)); } /** @@ -105,6 +109,7 @@ public class SysClientServiceImpl implements ISysClientService { /** * 修改客户端管理 */ + @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, key = "#bo.clientId") @Override public Boolean updateByBo(SysClientBo bo) { SysClient update = MapstructUtils.convert(bo, SysClient.class); @@ -116,12 +121,13 @@ public class SysClientServiceImpl implements ISysClientService { /** * 修改状态 */ + @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId") @Override - public int updateUserStatus(Long id, String status) { + public int updateUserStatus(String clientId, String status) { return baseMapper.update(null, new LambdaUpdateWrapper() .set(SysClient::getStatus, status) - .eq(SysClient::getId, id)); + .eq(SysClient::getClientId, clientId)); } /** @@ -134,6 +140,7 @@ public class SysClientServiceImpl implements ISysClientService { /** * 批量删除客户端管理 */ + @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, allEntries = true) @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java index fe5cc0e54..86edf59d4 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java @@ -7,6 +7,7 @@ import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.exception.ServiceException; @@ -27,7 +28,6 @@ import org.dromara.system.mapper.SysDeptMapper; import org.dromara.system.mapper.SysRoleMapper; import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.service.ISysDeptService; -import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -83,6 +83,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { lqw.eq(ObjectUtil.isNotNull(bo.getParentId()), SysDept::getParentId, bo.getParentId()); lqw.like(StringUtils.isNotBlank(bo.getDeptName()), SysDept::getDeptName, bo.getDeptName()); lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDept::getStatus, bo.getStatus()); + lqw.orderByAsc(SysDept::getAncestors); lqw.orderByAsc(SysDept::getParentId); lqw.orderByAsc(SysDept::getOrderNum); lqw.orderByAsc(SysDept::getDeptId); @@ -138,6 +139,14 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { return dept; } + @Override + public List selectDeptByIds(List deptIds) { + return baseMapper.selectDeptList(new LambdaQueryWrapper() + .select(SysDept::getDeptId, SysDept::getDeptName, SysDept::getLeader) + .eq(SysDept::getStatus, UserConstants.DEPT_NORMAL) + .in(CollUtil.isNotEmpty(deptIds), SysDept::getDeptId, deptIds)); + } + /** * 通过部门ID查询部门名称 * @@ -221,8 +230,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { if (LoginHelper.isSuperAdmin()) { return; } - SysDeptVo dept = baseMapper.selectDeptById(deptId); - if (ObjectUtil.isNull(dept)) { + if (baseMapper.countDeptById(deptId) == 0) { throw new ServiceException("没有权限访问部门数据!"); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java index 346d6bc83..bc6663f0c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java @@ -1,13 +1,12 @@ package org.dromara.system.service.impl; -import cn.dev33.satoken.context.SaHolder; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; 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.exception.ServiceException; import org.dromara.common.core.service.DictService; @@ -26,7 +25,6 @@ import org.dromara.system.domain.vo.SysDictTypeVo; import org.dromara.system.mapper.SysDictDataMapper; import org.dromara.system.mapper.SysDictTypeMapper; import org.dromara.system.service.ISysDictTypeService; -import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -217,16 +215,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService * @param separator 分隔符 * @return 字典标签 */ - @SuppressWarnings("unchecked cast") @Override public String getDictLabel(String dictType, String dictValue, String separator) { - // 优先从本地缓存获取 - List datas = (List) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType); - if (ObjectUtil.isNull(datas)) { - datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); - SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas); - } - + List datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); Map map = StreamUtils.toMap(datas, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel); if (StringUtils.containsAny(dictValue, separator)) { return Arrays.stream(dictValue.split(separator)) @@ -245,16 +236,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService * @param separator 分隔符 * @return 字典值 */ - @SuppressWarnings("unchecked cast") @Override public String getDictValue(String dictType, String dictLabel, String separator) { - // 优先从本地缓存获取 - List datas = (List) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType); - if (ObjectUtil.isNull(datas)) { - datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); - SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas); - } - + List datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); Map map = StreamUtils.toMap(datas, SysDictDataVo::getDictLabel, SysDictDataVo::getDictValue); if (StringUtils.containsAny(dictLabel, separator)) { return Arrays.stream(dictLabel.split(separator)) diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java index d215d5951..9c930a0c5 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java @@ -5,30 +5,29 @@ import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgentUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.Constants; import org.dromara.common.core.utils.MapstructUtils; -import org.dromara.common.mybatis.core.page.PageQuery; -import org.dromara.common.log.event.LogininforEvent; -import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.ip.AddressUtils; +import org.dromara.common.log.event.LogininforEvent; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.satoken.utils.LoginHelper; -import org.dromara.system.domain.SysClient; import org.dromara.system.domain.SysLogininfor; import org.dromara.system.domain.bo.SysLogininforBo; +import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysLogininforVo; -import org.dromara.system.mapper.SysClientMapper; import org.dromara.system.mapper.SysLogininforMapper; import org.dromara.system.service.ISysClientService; import org.dromara.system.service.ISysLogininforService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; -import jakarta.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -60,10 +59,10 @@ public class SysLogininforServiceImpl implements ISysLogininforService { final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); final String ip = ServletUtils.getClientIP(request); // 客户端信息 - String clientid = request.getHeader(LoginHelper.CLIENT_KEY); - SysClient client = null; - if (StringUtils.isNotBlank(clientid)) { - client = clientService.queryByClientId(clientid); + String clientId = request.getHeader(LoginHelper.CLIENT_KEY); + SysClientVo client = null; + if (StringUtils.isNotBlank(clientId)) { + client = clientService.queryByClientId(clientId); } String address = AddressUtils.getRealAddressByIP(ip); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java index 8e2b5ac4a..18b7a08da 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java @@ -9,6 +9,7 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.system.domain.SysNotice; +import org.dromara.system.domain.SysUser; import org.dromara.system.domain.bo.SysNoticeBo; import org.dromara.system.domain.vo.SysNoticeVo; import org.dromara.system.domain.vo.SysUserVo; @@ -68,7 +69,7 @@ public class SysNoticeServiceImpl implements ISysNoticeService { lqw.like(StringUtils.isNotBlank(bo.getNoticeTitle()), SysNotice::getNoticeTitle, bo.getNoticeTitle()); lqw.eq(StringUtils.isNotBlank(bo.getNoticeType()), SysNotice::getNoticeType, bo.getNoticeType()); if (StringUtils.isNotBlank(bo.getCreateByName())) { - SysUserVo sysUser = userMapper.selectUserByUserName(bo.getCreateByName()); + SysUserVo sysUser = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, bo.getCreateByName())); lqw.eq(SysNotice::getCreateBy, ObjectUtil.isNotNull(sysUser) ? sysUser.getUserId() : null); } lqw.orderByAsc(SysNotice::getNoticeId); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java index c7d47191d..1df221c32 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java @@ -6,6 +6,8 @@ import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.service.OssService; @@ -25,8 +27,6 @@ import org.dromara.system.domain.bo.SysOssBo; import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.mapper.SysOssMapper; import org.dromara.system.service.ISysOssService; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; import org.springframework.cache.annotation.Cacheable; import org.springframework.http.MediaType; @@ -36,7 +36,10 @@ import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; /** * 文件上传 服务层实现 @@ -49,6 +52,13 @@ public class SysOssServiceImpl implements ISysOssService, OssService { private final SysOssMapper baseMapper; + /** + * 查询OSS对象存储列表 + * + * @param bo OSS对象存储分页查询对象 + * @param pageQuery 分页查询实体类 + * @return 结果 + */ @Override public TableDataInfo queryPageList(SysOssBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); @@ -58,6 +68,12 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return TableDataInfo.build(result); } + /** + * 根据一组 ossIds 获取对应的 SysOssVo 列表 + * + * @param ossIds 一组文件在数据库中的唯一标识集合 + * @return 包含 SysOssVo 对象的列表 + */ @Override public List listByIds(Collection ossIds) { List list = new ArrayList<>(); @@ -75,6 +91,12 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return list; } + /** + * 根据一组 ossIds 获取对应文件的 URL 列表 + * + * @param ossIds 以逗号分隔的 ossId 字符串 + * @return 以逗号分隔的文件 URL 字符串 + */ @Override public String selectUrlByIds(String ossIds) { List list = new ArrayList<>(); @@ -107,12 +129,25 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return lqw; } + /** + * 根据 ossId 从缓存或数据库中获取 SysOssVo 对象 + * + * @param ossId 文件在数据库中的唯一标识 + * @return SysOssVo 对象,包含文件信息 + */ @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId") @Override public SysOssVo getById(Long ossId) { return baseMapper.selectVoById(ossId); } + + /** + * 文件下载方法,支持一次性下载完整文件 + * + * @param ossId OSS对象ID + * @param response HttpServletResponse对象,用于设置响应头和向客户端发送文件内容 + */ @Override public void download(Long ossId, HttpServletResponse response) throws IOException { SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId); @@ -122,7 +157,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName()); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); OssClient storage = OssFactory.instance(sysOss.getService()); - try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) { + try (InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) { int available = inputStream.available(); IoUtil.copy(inputStream, response.getOutputStream(), available); response.setContentLength(available); @@ -131,6 +166,13 @@ public class SysOssServiceImpl implements ISysOssService, OssService { } } + /** + * 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的 MultipartFile 对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + * @throws ServiceException 如果上传过程中发生异常,则抛出 ServiceException 异常 + */ @Override public SysOssVo upload(MultipartFile file) { String originalfileName = file.getOriginalFilename(); @@ -138,7 +180,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { OssClient storage = OssFactory.instance(); UploadResult uploadResult; try { - uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType()); + uploadResult = storage.uploadSuffix(file.getBytes(), suffix); } catch (IOException e) { throw new ServiceException(e.getMessage()); } @@ -146,6 +188,12 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult); } + /** + * 上传文件到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的文件对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + */ @Override public SysOssVo upload(File file) { String originalfileName = file.getName(); @@ -169,6 +217,13 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return this.matchingUrl(sysOssVo); } + /** + * 删除OSS对象存储 + * + * @param ids OSS对象ID串 + * @param isValid 判断是否需要校验 + * @return 结果 + */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { @@ -183,7 +238,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { } /** - * 匹配Url + * 桶类型为 private 的URL 修改为临时URL时长为120s * * @param oss OSS对象 * @return oss 匹配Url的OSS对象 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java index b69a06c5f..33b98af59 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java @@ -1,12 +1,16 @@ package org.dromara.system.service.impl; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; @@ -17,7 +21,6 @@ import org.dromara.system.domain.vo.SysPostVo; import org.dromara.system.mapper.SysPostMapper; import org.dromara.system.mapper.SysUserPostMapper; import org.dromara.system.service.ISysPostService; -import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.Arrays; @@ -92,7 +95,22 @@ public class SysPostServiceImpl implements ISysPostService { */ @Override public List selectPostListByUserId(Long userId) { - return baseMapper.selectPostListByUserId(userId); + List list = baseMapper.selectPostsByUserId(userId); + return StreamUtils.toList(list, SysPostVo::getPostId); + } + + /** + * 通过岗位ID串查询岗位 + * + * @param postIds 岗位id串 + * @return 岗位列表信息 + */ + @Override + public List selectPostByIds(List postIds) { + return baseMapper.selectVoList(new LambdaQueryWrapper() + .select(SysPost::getPostId, SysPost::getPostName, SysPost::getPostCode) + .eq(SysPost::getStatus, UserConstants.POST_NORMAL) + .in(CollUtil.isNotEmpty(postIds), SysPost::getPostId, postIds)); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java index 8a2b9390f..abd294f3e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java @@ -80,7 +80,6 @@ public class SysRoleServiceImpl implements ISysRoleService { .between(params.get("beginTime") != null && params.get("endTime") != null, "r.create_time", params.get("beginTime"), params.get("endTime")) .orderByAsc("r.role_sort").orderByAsc("r.create_time"); - ; return wrapper; } @@ -92,6 +91,17 @@ public class SysRoleServiceImpl implements ISysRoleService { */ @Override public List selectRolesByUserId(Long userId) { + return baseMapper.selectRolesByUserId(userId); + } + + /** + * 根据用户ID查询角色列表(包含被授权状态) + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List selectRolesAuthByUserId(Long userId) { List userRoles = baseMapper.selectRolePermissionByUserId(userId); List roles = selectRoleAll(); for (SysRoleVo role : roles) { @@ -141,7 +151,8 @@ public class SysRoleServiceImpl implements ISysRoleService { */ @Override public List selectRoleListByUserId(Long userId) { - return baseMapper.selectRoleListByUserId(userId); + List list = baseMapper.selectRolesByUserId(userId); + return StreamUtils.toList(list, SysRoleVo::getRoleId); } /** @@ -155,6 +166,19 @@ public class SysRoleServiceImpl implements ISysRoleService { return baseMapper.selectRoleById(roleId); } + /** + * 通过角色ID串查询角色 + * + * @param roleIds 角色ID串 + * @return 角色列表信息 + */ + @Override + public List selectRoleByIds(List roleIds) { + return baseMapper.selectRoleList(new QueryWrapper() + .eq("r.status", UserConstants.ROLE_NORMAL) + .in(CollUtil.isNotEmpty(roleIds), "r.role_id", roleIds)); + } + /** * 校验角色名称是否唯一 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java index 7ee19e851..a4d553b49 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java @@ -29,6 +29,7 @@ import org.dromara.system.domain.SysUserRole; import org.dromara.system.domain.bo.SysUserBo; import org.dromara.system.domain.vo.SysPostVo; 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.mapper.*; import org.dromara.system.service.ISysUserService; @@ -69,28 +70,28 @@ public class SysUserServiceImpl implements ISysUserService, UserService { * @return 用户信息集合信息 */ @Override - public List selectUserList(SysUserBo user) { - return baseMapper.selectUserList(this.buildQueryWrapper(user)); + public List selectUserExportList(SysUserBo user) { + return baseMapper.selectUserExportList(this.buildQueryWrapper(user)); } private Wrapper buildQueryWrapper(SysUserBo user) { Map params = user.getParams(); QueryWrapper wrapper = Wrappers.query(); wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) - .eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId()) - .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) - .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) - .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) - .between(params.get("beginTime") != null && params.get("endTime") != null, - "u.create_time", params.get("beginTime"), params.get("endTime")) - .and(ObjectUtil.isNotNull(user.getDeptId()), w -> { - List deptList = deptMapper.selectList(new LambdaQueryWrapper() - .select(SysDept::getDeptId) - .apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors"))); - List ids = StreamUtils.toList(deptList, SysDept::getDeptId); - ids.add(user.getDeptId()); - w.in("u.dept_id", ids); - }).orderByAsc("u.user_id"); + .eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId()) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + "u.create_time", params.get("beginTime"), params.get("endTime")) + .and(ObjectUtil.isNotNull(user.getDeptId()), w -> { + List deptList = deptMapper.selectList(new LambdaQueryWrapper() + .select(SysDept::getDeptId) + .apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors"))); + List ids = StreamUtils.toList(deptList, SysDept::getDeptId); + ids.add(user.getDeptId()); + w.in("u.dept_id", ids); + }).orderByAsc("u.user_id"); return wrapper; } @@ -104,11 +105,11 @@ public class SysUserServiceImpl implements ISysUserService, UserService { public TableDataInfo selectAllocatedList(SysUserBo user, PageQuery pageQuery) { QueryWrapper wrapper = Wrappers.query(); wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) - .eq(ObjectUtil.isNotNull(user.getRoleId()), "r.role_id", user.getRoleId()) - .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) - .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) - .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) - .orderByAsc("u.user_id"); + .eq(ObjectUtil.isNotNull(user.getRoleId()), "r.role_id", user.getRoleId()) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .orderByAsc("u.user_id"); Page page = baseMapper.selectAllocatedList(pageQuery.build(), wrapper); return TableDataInfo.build(page); } @@ -124,11 +125,11 @@ public class SysUserServiceImpl implements ISysUserService, UserService { List userIds = userRoleMapper.selectUserIdsByRoleId(user.getRoleId()); QueryWrapper wrapper = Wrappers.query(); wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) - .and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id")) - .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds) - .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) - .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) - .orderByAsc("u.user_id"); + .and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id")) + .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .orderByAsc("u.user_id"); Page page = baseMapper.selectUnallocatedList(pageQuery.build(), wrapper); return TableDataInfo.build(page); } @@ -141,7 +142,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserByUserName(String userName) { - return baseMapper.selectUserByUserName(userName); + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, userName)); } /** @@ -152,7 +153,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserByPhonenumber(String phonenumber) { - return baseMapper.selectUserByPhonenumber(phonenumber); + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getPhonenumber, phonenumber)); } /** @@ -163,18 +164,39 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserById(Long userId) { - return baseMapper.selectUserById(userId); + SysUserVo user = baseMapper.selectVoById(userId); + if (ObjectUtil.isNull(user)) { + return user; + } + user.setRoles(roleMapper.selectRolesByUserId(user.getUserId())); + return user; + } + + /** + * 通过用户ID串查询用户 + * + * @param userIds 用户ID串 + * @param deptId 部门id + * @return 用户列表信息 + */ + @Override + public List selectUserByIds(List userIds, Long deptId) { + return baseMapper.selectUserList(new LambdaQueryWrapper() + .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName) + .eq(SysUser::getStatus, UserConstants.USER_NORMAL) + .eq(ObjectUtil.isNotNull(deptId), SysUser::getDeptId, deptId) + .in(CollUtil.isNotEmpty(userIds), SysUser::getUserId, userIds)); } /** * 查询用户所属角色组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ @Override - public String selectUserRoleGroup(String userName) { - List list = roleMapper.selectRolesByUserName(userName); + public String selectUserRoleGroup(Long userId) { + List list = roleMapper.selectRolesByUserId(userId); if (CollUtil.isEmpty(list)) { return StringUtils.EMPTY; } @@ -184,12 +206,12 @@ public class SysUserServiceImpl implements ISysUserService, UserService { /** * 查询用户所属岗位组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ @Override - public String selectUserPostGroup(String userName) { - List list = postMapper.selectPostsByUserName(userName); + public String selectUserPostGroup(Long userId) { + List list = postMapper.selectPostsByUserId(userId); if (CollUtil.isEmpty(list)) { return StringUtils.EMPTY; } @@ -205,8 +227,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public boolean checkUserNameUnique(SysUserBo user) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() - .eq(SysUser::getUserName, user.getUserName()) - .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + .eq(SysUser::getUserName, user.getUserName()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); return !exist; } @@ -218,8 +240,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public boolean checkPhoneUnique(SysUserBo user) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() - .eq(SysUser::getPhonenumber, user.getPhonenumber()) - .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + .eq(SysUser::getPhonenumber, user.getPhonenumber()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); return !exist; } @@ -231,8 +253,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public boolean checkEmailUnique(SysUserBo user) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() - .eq(SysUser::getEmail, user.getEmail()) - .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + .eq(SysUser::getEmail, user.getEmail()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); return !exist; } @@ -261,7 +283,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { if (LoginHelper.isSuperAdmin()) { return; } - if (ObjectUtil.isNull(baseMapper.selectUserById(userId))) { + if (baseMapper.countUserById(userId) == 0) { throw new ServiceException("没有权限访问用户数据!"); } } @@ -345,9 +367,9 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public int updateUserStatus(Long userId, String status) { return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysUser::getStatus, status) - .eq(SysUser::getUserId, userId)); + new LambdaUpdateWrapper() + .set(SysUser::getStatus, status) + .eq(SysUser::getUserId, userId)); } /** @@ -359,12 +381,12 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public int updateUserProfile(SysUserBo user) { return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(ObjectUtil.isNotNull(user.getNickName()), SysUser::getNickName, user.getNickName()) - .set(SysUser::getPhonenumber, user.getPhonenumber()) - .set(SysUser::getEmail, user.getEmail()) - .set(SysUser::getSex, user.getSex()) - .eq(SysUser::getUserId, user.getUserId())); + new LambdaUpdateWrapper() + .set(ObjectUtil.isNotNull(user.getNickName()), SysUser::getNickName, user.getNickName()) + .set(SysUser::getPhonenumber, user.getPhonenumber()) + .set(SysUser::getEmail, user.getEmail()) + .set(SysUser::getSex, user.getSex()) + .eq(SysUser::getUserId, user.getUserId())); } /** @@ -377,9 +399,9 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public boolean updateUserAvatar(Long userId, Long avatar) { return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysUser::getAvatar, avatar) - .eq(SysUser::getUserId, userId)) > 0; + new LambdaUpdateWrapper() + .set(SysUser::getAvatar, avatar) + .eq(SysUser::getUserId, userId)) > 0; } /** @@ -392,9 +414,9 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public int resetUserPwd(Long userId, String password) { return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysUser::getPassword, password) - .eq(SysUser::getUserId, userId)); + new LambdaUpdateWrapper() + .set(SysUser::getPassword, password) + .eq(SysUser::getUserId, userId)); } /** @@ -518,8 +540,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService { /** * 通过部门id查询当前部门所有用户 * - * @param deptId - * @return + * @param deptId 部门ID + * @return 用户信息集合信息 */ @Override public List selectUserListByDept(Long deptId) { @@ -529,14 +551,26 @@ public class SysUserServiceImpl implements ISysUserService, UserService { return baseMapper.selectVoList(lqw); } + /** + * 通过用户ID查询用户账户 + * + * @param userId 用户ID + * @return 用户账户 + */ @Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = "#userId") @Override public String selectUserNameById(Long userId) { SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getUserName).eq(SysUser::getUserId, userId)); + .select(SysUser::getUserName).eq(SysUser::getUserId, userId)); return ObjectUtil.isNull(sysUser) ? null : sysUser.getUserName(); } + /** + * 通过用户ID查询用户账户 + * + * @param userId 用户ID + * @return 用户账户 + */ @Override @Cacheable(cacheNames = CacheNames.SYS_NICKNAME, key = "#userId") public String selectNicknameById(Long userId) { @@ -544,4 +578,31 @@ public class SysUserServiceImpl implements ISysUserService, UserService { .select(SysUser::getNickName).eq(SysUser::getUserId, userId)); return ObjectUtil.isNull(sysUser) ? null : sysUser.getNickName(); } + + /** + * 通过用户ID查询用户手机号 + * + * @param userId 用户id + * @return 用户手机号 + */ + @Override + public String selectPhonenumberById(Long userId) { + SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getPhonenumber).eq(SysUser::getUserId, userId)); + return ObjectUtil.isNull(sysUser) ? null : sysUser.getPhonenumber(); + } + + /** + * 通过用户ID查询用户邮箱 + * + * @param userId 用户id + * @return 用户邮箱 + */ + @Override + public String selectEmailById(Long userId) { + SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getEmail).eq(SysUser::getUserId, userId)); + return ObjectUtil.isNull(sysUser) ? null : sysUser.getEmail(); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml index bba949d7b..6ad866f29 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -8,11 +8,18 @@ - select * from sys_dept ${ew.getCustomSqlSegment} + select + + ${ew.getSqlSelect} + + + * + + from sys_dept ${ew.getCustomSqlSegment} - - select * from sys_dept where del_flag = '0' and dept_id = #{deptId} + + select count(*) from sys_dept where del_flag = '0' and dept_id = #{deptId} diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml index d2108ebbc..199dffe4d 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -7,20 +7,12 @@ - - select p.post_id + + select p.post_id, p.post_name, p.post_code from sys_post p left join sys_user_post up on up.post_id = p.post_id left join sys_user u on u.user_id = up.user_id where u.user_id = #{userId} - - select p.post_id, p.post_name, p.post_code - from sys_post p - left join sys_user_post up on up.post_id = p.post_id - left join sys_user u on u.user_id = up.user_id - where u.user_name = #{userName} - - diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml index b1916985e..7b8bba761 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -40,23 +40,17 @@ WHERE r.del_flag = '0' and sur.user_id = #{userId} - - select r.role_id - from sys_role r - left join sys_user_role sur on sur.role_id = r.role_id - left join sys_user u on u.user_id = sur.user_id - where u.user_id = #{userId} - - - + select r.role_id, r.role_name, r.role_key, - r.role_sort + r.role_sort, + r.data_scope, + r.status from sys_role r left join sys_user_role sur on sur.role_id = r.role_id left join sys_user u on u.user_id = sur.user_id - WHERE r.del_flag = '0' and u.user_name = #{userName} + WHERE r.del_flag = '0' and sur.user_id = #{userId} diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml index 7af7dd0cc..ded6fa858 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -4,78 +4,40 @@ "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - - - - - - - - - - + + - - - - - - - - select u.user_id, - u.tenant_id, - u.dept_id, - u.user_name, - u.nick_name, - u.user_type, - u.email, - u.avatar, - u.phonenumber, - u.password, - u.sex, - u.status, - u.del_flag, - u.login_ip, - u.login_date, - u.create_by, - u.create_time, - u.remark, - d.dept_id, - d.parent_id, - d.ancestors, - d.dept_name, - d.order_num, - d.leader, - d.status as dept_status, - d.email as dept_email, - d.create_time as dept_cteate_time, - r.role_id, - r.role_name, - r.role_key, - r.role_sort, - r.data_scope, - r.status as role_status - from sys_user u - left join sys_dept d on u.dept_id = d.dept_id - left join sys_user_role sur on u.user_id = sur.user_id - left join sys_role r on r.role_id = sur.role_id - - - 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, - d.dept_name, d.leader, u1.user_name as leaderName + select + + ${ew.getSqlSelect} + + + 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 + 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} + select + + ${ew.getSqlSelect} + + + 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 + + from sys_user u + ${ew.getCustomSqlSegment} + + + 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, d.dept_name, d.leader, u1.user_name as leaderName @@ -103,24 +65,8 @@ ${ew.getCustomSqlSegment} - - - where u.del_flag = '0' and u.user_name = #{userName} - - - - - where u.del_flag = '0' and u.phonenumber = #{phonenumber} - - - - - where u.del_flag = '0' and u.email = #{email} - - - - - where u.del_flag = '0' and u.user_id = #{userId} + + select count(*) from sys_user where del_flag = '0' and user_id = #{userId} diff --git a/ruoyi-modules/ruoyi-workflow/pom.xml b/ruoyi-modules/ruoyi-workflow/pom.xml new file mode 100644 index 000000000..f0391e358 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/pom.xml @@ -0,0 +1,93 @@ + + + + org.dromara + ruoyi-modules + ${revision} + ../pom.xml + + 4.0.0 + jar + ruoyi-workflow + + + 工作流模块 + + + + + + + org.flowable + flowable-spring-boot-autoconfigure + + + org.flowable + flowable-spring-security + + + + + + org.flowable + flowable-spring-configurator + + + + org.flowable + flowable-spring-boot-starter-actuator + + + + + org.flowable + flowable-image-generator + + + + + org.flowable + flowable-json-converter + 6.8.0 + + + + + org.apache.xmlgraphics + batik-all + 1.10 + + + xalan + xalan + + + + + + + org.dromara + ruoyi-system + + + + org.dromara + ruoyi-common-websocket + + + + org.dromara + ruoyi-common-mail + + + + org.dromara + ruoyi-common-sms + + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java new file mode 100644 index 000000000..5ea262d17 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java @@ -0,0 +1,27 @@ +package org.dromara.workflow.annotation; + + +import java.lang.annotation.*; + +/** + * 流程任务监听注解 + * + * @author may + * @date 2023-12-27 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface FlowListenerAnnotation { + + /** + * 流程定义key + */ + String processDefinitionKey(); + + /** + * 节点id + */ + String taskDefId() default ""; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java new file mode 100644 index 000000000..df27d3cf9 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java @@ -0,0 +1,137 @@ +package org.dromara.workflow.common.constant; + + +/** + * 工作流常量 + * + * @author may + */ +public interface FlowConstant { + + String MESSAGE_CURRENT_TASK_IS_NULL = "当前任务不存在或你不是任务办理人!"; + + String MESSAGE_SUSPENDED = "当前任务已挂起不可审批!"; + + /** + * 连线 + */ + String SEQUENCE_FLOW = "sequenceFlow"; + + /** + * 并行网关 + */ + String PARALLEL_GATEWAY = "parallelGateway"; + + /** + * 排它网关 + */ + String EXCLUSIVE_GATEWAY = "exclusiveGateway"; + + /** + * 包含网关 + */ + String INCLUSIVE_GATEWAY = "inclusiveGateway"; + + /** + * 结束节点 + */ + String END_EVENT = "endEvent"; + + + /** + * 流程委派标识 + */ + String PENDING = "PENDING"; + + /** + * 候选人标识 + */ + String CANDIDATE = "candidate"; + + /** + * 会签任务总数 + */ + String NUMBER_OF_INSTANCES = "nrOfInstances"; + + /** + * 正在执行的会签总数 + */ + String NUMBER_OF_ACTIVE_INSTANCES = "nrOfActiveInstances"; + + /** + * 已完成的会签任务总数 + */ + String NUMBER_OF_COMPLETED_INSTANCES = "nrOfCompletedInstances"; + + /** + * 循环的索引值,可以使用elementIndexVariable属性修改loopCounter的变量名 + */ + String LOOP_COUNTER = "loopCounter"; + + String ZIP = "ZIP"; + + /** + * 流程实例对象 + */ + String PROCESS_INSTANCE_VO = "processInstanceVo"; + + /** + * 流程定义配置 + */ + String WF_DEFINITION_CONFIG_VO = "wfDefinitionConfigVo"; + + /** + * 节点配置 + */ + String WF_NODE_CONFIG_VO = "wfNodeConfigVo"; + + /** + * 流程发起人 + */ + String INITIATOR = "initiator"; + + /** + * 流程实例id + */ + String PROCESS_INSTANCE_ID = "processInstanceId"; + + /** + * 业务id + */ + String BUSINESS_KEY = "businessKey"; + + /** + * 流程定义id + */ + String PROCESS_DEFINITION_ID = "processDefinitionId"; + + /** + * 开启跳过表达式变量 + */ + String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; + + /** + * 模型标识key命名规范正则表达式 + */ + String MODEL_KEY_PATTERN = "^[a-zA-Z][a-zA-Z0-9_]{0,254}$"; + + /** + * 用户任务 + */ + String USER_TASK = "userTask"; + + /** + * 会签 + */ + String MULTI_INSTANCE = "multiInstance"; + + /** + * 是 + */ + String TRUE = "0"; + + /** + * 否 + */ + String FALSE = "1"; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java new file mode 100644 index 000000000..6eb6ffe6b --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java @@ -0,0 +1,152 @@ +package org.dromara.workflow.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; + +import java.util.Arrays; + +/** + * 业务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum BusinessStatusEnum { + /** + * 已撤销 + */ + CANCEL("cancel", "已撤销"), + /** + * 草稿 + */ + DRAFT("draft", "草稿"), + /** + * 待审核 + */ + WAITING("waiting", "待审核"), + /** + * 已完成 + */ + FINISH("finish", "已完成"), + /** + * 已作废 + */ + INVALID("invalid", "已作废"), + /** + * 已退回 + */ + BACK("back", "已退回"), + /** + * 已终止 + */ + TERMINATION("termination", "已终止"); + + /** + * 状态 + */ + private final String status; + + /** + * 描述 + */ + private final String desc; + + /** + * 获取业务状态 + * + * @param status 状态 + */ + public static String findByStatus(String status) { + if (StringUtils.isBlank(status)) { + return StrUtil.EMPTY; + } + return Arrays.stream(BusinessStatusEnum.values()) + .filter(statusEnum -> statusEnum.getStatus().equals(status)) + .findFirst() + .map(BusinessStatusEnum::getDesc) + .orElse(StrUtil.EMPTY); + } + + /** + * 启动流程校验 + * + * @param status 状态 + */ + public static void checkStartStatus(String status) { + if (WAITING.getStatus().equals(status)) { + throw new ServiceException("该单据已提交过申请,正在审批中!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 撤销流程校验 + * + * @param status 状态 + */ + public static void checkCancelStatus(String status) { + if (CANCEL.getStatus().equals(status)) { + throw new ServiceException("该单据已撤销!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (BACK.getStatus().equals(status)) { + throw new ServiceException("该单据已退回!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 驳回流程校验 + * + * @param status 状态 + */ + public static void checkBackStatus(String status) { + if (BACK.getStatus().equals(status)) { + throw new ServiceException("该单据已退回!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (CANCEL.getStatus().equals(status)) { + throw new ServiceException("该单据已撤销!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 作废,终止流程校验 + * + * @param status 状态 + */ + public static void checkInvalidStatus(String status) { + if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java new file mode 100644 index 000000000..083ab7b85 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java @@ -0,0 +1,54 @@ +package org.dromara.workflow.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 任务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum FormTypeEnum { + /** + * 自定义表单 + */ + STATIC("static", "自定义表单"), + /** + * 动态表单 + */ + DYNAMIC("dynamic", "动态表单"); + + /** + * 类型 + */ + private final String type; + + /** + * 描述 + */ + private final String desc; + + /** + * 表单类型 + * + * @param formType 表单类型 + */ + public static String findByType(String formType) { + if (StringUtils.isBlank(formType)) { + return StrUtil.EMPTY; + } + + return Arrays.stream(FormTypeEnum.values()) + .filter(statusEnum -> statusEnum.getType().equals(formType)) + .findFirst() + .map(FormTypeEnum::getDesc) + .orElse(StrUtil.EMPTY); + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java new file mode 100644 index 000000000..a28295822 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java @@ -0,0 +1,51 @@ +package org.dromara.workflow.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 消息类型枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum MessageTypeEnum { + /** + * 站内信 + */ + SYSTEM_MESSAGE("1", "站内信"), + /** + * 邮箱 + */ + EMAIL_MESSAGE("2", "邮箱"), + /** + * 短信 + */ + SMS_MESSAGE("3", "短信"); + + private final String code; + + private final String desc; + + private final static Map MESSAGE_TYPE_ENUM_MAP = new ConcurrentHashMap<>(MessageTypeEnum.values().length); + + static { + for (MessageTypeEnum messageType : MessageTypeEnum.values()) { + MESSAGE_TYPE_ENUM_MAP.put(messageType.code, messageType); + } + } + + /** + * 根据消息类型 code 获取 MessageTypeEnum + * @param code 消息类型code + * @return MessageTypeEnum + */ + public static MessageTypeEnum getByCode(String code) { + return MESSAGE_TYPE_ENUM_MAP.get(code); + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java new file mode 100644 index 000000000..7b2f55c20 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java @@ -0,0 +1,94 @@ +package org.dromara.workflow.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 任务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum TaskStatusEnum { + /** + * 撤销 + */ + CANCEL("cancel", "撤销"), + /** + * 通过 + */ + PASS("pass", "通过"), + /** + * 待审核 + */ + WAITING("waiting", "待审核"), + /** + * 作废 + */ + INVALID("invalid", "作废"), + /** + * 退回 + */ + BACK("back", "退回"), + /** + * 终止 + */ + TERMINATION("termination", "终止"), + /** + * 转办 + */ + TRANSFER("transfer", "转办"), + /** + * 委托 + */ + PENDING("pending", "委托"), + /** + * 抄送 + */ + COPY("copy", "抄送"), + /** + * 加签 + */ + SIGN("sign", "加签"), + /** + * 减签 + */ + SIGN_OFF("sign_off", "减签"), + /** + * 超时 + */ + TIMEOUT("timeout", "超时"); + + /** + * 状态 + */ + private final String status; + + /** + * 描述 + */ + private final String desc; + + /** + * 任务业务状态 + * + * @param status 状态 + */ + public static String findByStatus(String status) { + if (StringUtils.isBlank(status)) { + return StrUtil.EMPTY; + } + + return Arrays.stream(TaskStatusEnum.values()) + .filter(statusEnum -> statusEnum.getStatus().equals(status)) + .findFirst() + .map(TaskStatusEnum::getDesc) + .orElse(StrUtil.EMPTY); + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java new file mode 100644 index 000000000..3332f86a5 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java @@ -0,0 +1,147 @@ +package org.dromara.workflow.controller; + +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.bo.ModelBo; +import org.dromara.workflow.domain.vo.ModelVo; +import org.dromara.workflow.service.IActModelService; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Model; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +/** + * 模型管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/model") +public class ActModelController extends BaseController { + + private final RepositoryService repositoryService; + + private final IActModelService actModelService; + + + /** + * 分页查询模型 + * + * @param modelBo 模型参数 + */ + @GetMapping("/list") + public TableDataInfo page(ModelBo modelBo, PageQuery pageQuery) { + return actModelService.page(modelBo, pageQuery); + } + + /** + * 新增模型 + * + * @param modelBo 模型请求对象 + */ + @Log(title = "模型管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/save") + public R saveNewModel(@Validated(AddGroup.class) @RequestBody ModelBo modelBo) { + return toAjax(actModelService.saveNewModel(modelBo)); + } + + /** + * 查询模型 + * + * @param id 模型id + */ + @GetMapping("/getInfo/{id}") + public R getInfo(@NotBlank(message = "模型id不能为空") @PathVariable String id) { + return R.ok(actModelService.getInfo(id)); + } + + /** + * 修改模型信息 + * + * @param modelBo 模型数据 + */ + @Log(title = "模型管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping(value = "/update") + public R update(@RequestBody ModelBo modelBo) { + return toAjax(actModelService.update(modelBo)); + } + + /** + * 编辑XMl模型 + * + * @param modelBo 模型数据 + */ + @Log(title = "模型管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping(value = "/editModelXml") + public R editModel(@Validated(EditGroup.class) @RequestBody ModelBo modelBo) { + return toAjax(actModelService.editModelXml(modelBo)); + } + + /** + * 删除流程模型 + * + * @param ids 模型id + */ + @Log(title = "模型管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @DeleteMapping("/{ids}") + @Transactional(rollbackFor = Exception.class) + public R delete(@NotEmpty(message = "主键不能为空") @PathVariable String[] ids) { + Arrays.stream(ids).parallel().forEachOrdered(repositoryService::deleteModel); + return R.ok(); + } + + /** + * 模型部署 + * + * @param id 模型id + */ + @Log(title = "模型管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/modelDeploy/{id}") + public R deploy(@NotBlank(message = "模型id不能为空") @PathVariable("id") String id) { + return toAjax(actModelService.modelDeploy(id)); + } + + /** + * 导出模型zip压缩包 + * + * @param modelIds 模型id + * @param response 相应 + */ + @GetMapping("/export/zip/{modelIds}") + public void exportZip(@NotEmpty(message = "模型id不能为空") @PathVariable List modelIds, + HttpServletResponse response) { + actModelService.exportZip(modelIds, response); + } + + /** + * 复制模型 + * + * @param modelBo 模型数据 + */ + @PostMapping("/copyModel") + public R copyModel(@RequestBody ModelBo modelBo) { + return toAjax(actModelService.copyModel(modelBo)); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java new file mode 100644 index 000000000..5198bd164 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java @@ -0,0 +1,147 @@ +package org.dromara.workflow.controller; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.bo.ProcessDefinitionBo; +import org.dromara.workflow.domain.vo.ProcessDefinitionVo; +import org.dromara.workflow.service.IActProcessDefinitionService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * 流程定义管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/processDefinition") +public class ActProcessDefinitionController extends BaseController { + + private final IActProcessDefinitionService actProcessDefinitionService; + + /** + * 分页查询 + * + * @param bo 参数 + */ + @GetMapping("/list") + public TableDataInfo page(ProcessDefinitionBo bo, PageQuery pageQuery) { + return actProcessDefinitionService.page(bo, pageQuery); + } + + /** + * 查询历史流程定义列表 + * + * @param key 流程定义key + */ + @GetMapping("/getListByKey/{key}") + public R> getListByKey(@NotEmpty(message = "流程定义key不能为空") @PathVariable String key) { + return R.ok("操作成功", actProcessDefinitionService.getListByKey(key)); + } + + /** + * 查看流程定义图片 + * + * @param processDefinitionId 流程定义id + */ + @GetMapping("/definitionImage/{processDefinitionId}") + public R definitionImage(@PathVariable String processDefinitionId) { + return R.ok("操作成功", actProcessDefinitionService.definitionImage(processDefinitionId)); + } + + /** + * 查看流程定义xml文件 + * + * @param processDefinitionId 流程定义id + */ + @GetMapping("/definitionXml/{processDefinitionId}") + public R> definitionXml(@NotBlank(message = "流程定义id不能为空") @PathVariable String processDefinitionId) { + Map map = new HashMap<>(); + String xmlStr = actProcessDefinitionService.definitionXml(processDefinitionId); + map.put("xml", Arrays.asList(xmlStr.split("\n"))); + map.put("xmlStr", xmlStr); + return R.ok(map); + } + + /** + * 删除流程定义 + * + * @param deploymentIds 部署id + * @param processDefinitionIds 流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deploymentIds}/{processDefinitionIds}") + public R deleteDeployment(@NotNull(message = "流程部署id不能为空") @PathVariable List deploymentIds, + @NotNull(message = "流程定义id不能为空") @PathVariable List processDefinitionIds) { + return toAjax(actProcessDefinitionService.deleteDeployment(deploymentIds, processDefinitionIds)); + } + + /** + * 激活或者挂起流程定义 + * + * @param processDefinitionId 流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/updateDefinitionState/{processDefinitionId}") + public R updateDefinitionState(@NotBlank(message = "流程定义id不能为空") @PathVariable String processDefinitionId) { + return toAjax(actProcessDefinitionService.updateDefinitionState(processDefinitionId)); + } + + /** + * 迁移流程定义 + * + * @param currentProcessDefinitionId 当前流程定义id + * @param fromProcessDefinitionId 需要迁移到的流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/migrationDefinition/{currentProcessDefinitionId}/{fromProcessDefinitionId}") + public R migrationDefinition(@NotBlank(message = "当前流程定义id") @PathVariable String currentProcessDefinitionId, + @NotBlank(message = "需要迁移到的流程定义id") @PathVariable String fromProcessDefinitionId) { + return toAjax(actProcessDefinitionService.migrationDefinition(currentProcessDefinitionId, fromProcessDefinitionId)); + } + + /** + * 流程定义转换为模型 + * + * @param processDefinitionId 流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/convertToModel/{processDefinitionId}") + public R convertToModel(@NotEmpty(message = "流程定义id不能为空") @PathVariable String processDefinitionId) { + return toAjax(actProcessDefinitionService.convertToModel(processDefinitionId)); + } + + /** + * 通过zip或xml部署流程定义 + * + * @param file 文件 + * @param categoryCode 分类 + */ + @Log(title = "流程定义管理", businessType = BusinessType.INSERT) + @PostMapping("/deployByFile") + public void deployByFile(@RequestParam("file") MultipartFile file, @RequestParam("categoryCode") String categoryCode) { + actProcessDefinitionService.deployByFile(file, categoryCode); + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java new file mode 100644 index 000000000..4d433df43 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java @@ -0,0 +1,158 @@ +package org.dromara.workflow.controller; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.bo.ProcessInstanceBo; +import org.dromara.workflow.domain.bo.ProcessInvalidBo; +import org.dromara.workflow.domain.bo.TaskUrgingBo; +import org.dromara.workflow.domain.vo.ProcessInstanceVo; +import org.dromara.workflow.service.IActProcessInstanceService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.Map; + +/** + * 流程实例管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/processInstance") +public class ActProcessInstanceController extends BaseController { + + private final IActProcessInstanceService actProcessInstanceService; + + /** + * 分页查询正在运行的流程实例 + * + * @param bo 参数 + */ + @GetMapping("/getPageByRunning") + public TableDataInfo getPageByRunning(ProcessInstanceBo bo, PageQuery pageQuery) { + return actProcessInstanceService.getPageByRunning(bo, pageQuery); + } + + /** + * 分页查询已结束的流程实例 + * + * @param bo 参数 + */ + @GetMapping("/getPageByFinish") + public TableDataInfo getPageByFinish(ProcessInstanceBo bo, PageQuery pageQuery) { + return actProcessInstanceService.getPageByFinish(bo, pageQuery); + } + + /** + * 通过流程实例id获取历史流程图 + * + * @param processInstanceId 流程实例id + */ + @GetMapping("/getHistoryImage/{processInstanceId}") + public R getHistoryImage(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) { + return R.ok("操作成功", actProcessInstanceService.getHistoryImage(processInstanceId)); + } + + /** + * 通过流程实例id获取历史流程图运行中,历史等节点 + * + * @param processInstanceId 流程实例id + */ + @GetMapping("/getHistoryList/{processInstanceId}") + public R> getHistoryList(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) { + return R.ok("操作成功", actProcessInstanceService.getHistoryList(processInstanceId)); + } + + /** + * 获取审批记录 + * + * @param processInstanceId 流程实例id + */ + @GetMapping("/getHistoryRecord/{processInstanceId}") + public R> getHistoryRecord(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) { + return R.ok(actProcessInstanceService.getHistoryRecord(processInstanceId)); + } + + /** + * 作废流程实例,不会删除历史记录(删除运行中的实例) + * + * @param processInvalidBo 参数 + */ + @Log(title = "流程实例管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @PostMapping("/deleteRunInstance") + public R deleteRunInstance(@Validated(AddGroup.class) @RequestBody ProcessInvalidBo processInvalidBo) { + return toAjax(actProcessInstanceService.deleteRunInstance(processInvalidBo)); + } + + /** + * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param processInstanceIds 流程实例id + */ + @Log(title = "流程实例管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @DeleteMapping("/deleteRunAndHisInstance/{processInstanceIds}") + public R deleteRunAndHisInstance(@NotNull(message = "流程实例id不能为空") @PathVariable String[] processInstanceIds) { + return toAjax(actProcessInstanceService.deleteRunAndHisInstance(Arrays.asList(processInstanceIds))); + } + + /** + * 已完成的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param processInstanceIds 流程实例id + */ + @Log(title = "流程实例管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @DeleteMapping("/deleteFinishAndHisInstance/{processInstanceIds}") + public R deleteFinishAndHisInstance(@NotNull(message = "流程实例id不能为空") @PathVariable String[] processInstanceIds) { + return toAjax(actProcessInstanceService.deleteFinishAndHisInstance(Arrays.asList(processInstanceIds))); + } + + /** + * 撤销流程申请 + * + * @param processInstanceId 流程实例id + */ + @Log(title = "流程实例管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/cancelProcessApply/{processInstanceId}") + public R cancelProcessApply(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) { + return toAjax(actProcessInstanceService.cancelProcessApply(processInstanceId)); + } + + /** + * 分页查询当前登录人单据 + * + * @param bo 参数 + */ + @GetMapping("/getPageByCurrent") + public TableDataInfo getPageByCurrent(ProcessInstanceBo bo, PageQuery pageQuery) { + return actProcessInstanceService.getPageByCurrent(bo, pageQuery); + } + + /** + * 任务催办(给当前任务办理人发送站内信,邮件,短信等) + * + * @param taskUrgingBo 任务催办 + */ + @Log(title = "流程实例管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/taskUrging") + public R taskUrging(@RequestBody TaskUrgingBo taskUrgingBo) { + return toAjax(actProcessInstanceService.taskUrging(taskUrgingBo)); + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java new file mode 100644 index 000000000..aa290127a --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java @@ -0,0 +1,274 @@ +package org.dromara.workflow.controller; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import jakarta.validation.constraints.NotBlank; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.WfTaskBackNode; +import org.dromara.workflow.domain.bo.*; +import org.dromara.workflow.domain.vo.TaskVo; +import org.dromara.workflow.domain.vo.VariableVo; +import org.dromara.workflow.service.IActTaskService; +import org.dromara.workflow.service.IWfTaskBackNodeService; +import org.dromara.workflow.utils.QueryUtils; +import org.flowable.engine.TaskService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 任务管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/task") +public class ActTaskController extends BaseController { + + private final IActTaskService actTaskService; + + private final TaskService taskService; + + private final IWfTaskBackNodeService iWfTaskBackNodeService; + + + /** + * 启动任务 + * + * @param startProcessBo 启动流程参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/startWorkFlow") + public R> startWorkFlow(@Validated(AddGroup.class) @RequestBody StartProcessBo startProcessBo) { + Map map = actTaskService.startWorkFlow(startProcessBo); + return R.ok("提交成功", map); + } + + /** + * 办理任务 + * + * @param completeTaskBo 办理任务参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/completeTask") + public R completeTask(@Validated(AddGroup.class) @RequestBody CompleteTaskBo completeTaskBo) { + return toAjax(actTaskService.completeTask(completeTaskBo)); + } + + /** + * 查询当前用户的待办任务 + * + * @param taskBo 参数 + */ + @GetMapping("/getPageByTaskWait") + public TableDataInfo
+ * 如果未使用虚拟线程则生效 * * @author Lion Li */ -@EnableAsync(proxyTargetClass = true) @AutoConfiguration public class AsyncConfig implements AsyncConfigurer { @@ -25,6 +26,9 @@ public class AsyncConfig implements AsyncConfigurer { */ @Override public Executor getAsyncExecutor() { + if(SpringUtils.isVirtual()) { + return new VirtualThreadTaskExecutor("async-"); + } return SpringUtils.getBean("scheduledExecutorService"); } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java index e59277aae..28ba17739 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java @@ -35,6 +35,11 @@ public interface CacheNames { */ String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d"; + /** + * 客户端 + */ + String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d"; + /** * 用户账户 */ diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java new file mode 100644 index 000000000..77eed8c09 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java @@ -0,0 +1,54 @@ +package org.dromara.common.core.constant; + +import cn.hutool.core.lang.RegexPool; + +/** + * 常用正则表达式字符串 + *
+ * 常用正则表达式集合,更多正则见: https://any86.github.io/any-rule/ + * + * @author Feng + */ +public interface RegexConstants extends RegexPool { + + /** + * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) + */ + String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$"; + + /** + * 权限标识必须符合 tool:build:list 格式,或者空字符串 + */ + String PERMISSION_STRING = "^(|^[a-zA-Z0-9_]+:[a-zA-Z0-9_]+:[a-zA-Z0-9_]+)$"; + + /** + * 身份证号码(后6位) + */ + String ID_CARD_LAST_6 = "^(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$"; + + /** + * QQ号码 + */ + String QQ_NUMBER = "^[1-9][0-9]\\d{4,9}$"; + + /** + * 邮政编码 + */ + String POSTAL_CODE = "^[1-9]\\d{5}$"; + + /** + * 注册账号 + */ + String ACCOUNT = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$"; + + /** + * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 + */ + String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$"; + + /** + * 通用状态(0表示正常,1表示停用) + */ + String STATUS = "^[01]$"; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java index 4fb097a88..e9dc6ec9b 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java @@ -1,9 +1,6 @@ package org.dromara.common.core.exception; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; +import lombok.*; import java.io.Serial; @@ -45,19 +42,11 @@ public final class ServiceException extends RuntimeException { this.code = code; } - public String getDetailMessage() { - return detailMessage; - } - @Override public String getMessage() { return message; } - public Integer getCode() { - return code; - } - public ServiceException setMessage(String message) { this.message = message; return this; diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java new file mode 100644 index 000000000..fd907d2c2 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java @@ -0,0 +1,52 @@ +package org.dromara.common.core.factory; + +import cn.hutool.core.lang.PatternPool; +import org.dromara.common.core.constant.RegexConstants; + +import java.util.regex.Pattern; + +/** + * 正则表达式模式池工厂 + *
初始化的时候将正则表达式加入缓存池当中
提高正则表达式的性能,避免重复编译相同的正则表达式
+ * 采用 caffeine + redis 多级缓存 优化并发查询效率 * * @author Lion Li */ public class PlusSaTokenDao implements SaTokenDao { + private static final Cache CAFFEINE = Caffeine.newBuilder() + // 设置最后一次写入或访问后经过固定时间过期 + .expireAfterWrite(5, TimeUnit.SECONDS) + // 初始的缓存空间大小 + .initialCapacity(100) + // 缓存的最大条数 + .maximumSize(1000) + .build(); + /** * 获取Value,如无返空 */ @Override public String get(String key) { - return RedisUtils.getCacheObject(key); + Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key)); + return (String) o; } /** @@ -38,6 +54,7 @@ public class PlusSaTokenDao implements SaTokenDao { } else { RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout)); } + CAFFEINE.put(key, value); } /** @@ -47,6 +64,7 @@ public class PlusSaTokenDao implements SaTokenDao { public void update(String key, String value) { if (RedisUtils.hasKey(key)) { RedisUtils.setCacheObject(key, value, true); + CAFFEINE.put(key, value); } } @@ -81,7 +99,8 @@ public class PlusSaTokenDao implements SaTokenDao { */ @Override public Object getObject(String key) { - return RedisUtils.getCacheObject(key); + Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key)); + return o; } /** @@ -98,6 +117,7 @@ public class PlusSaTokenDao implements SaTokenDao { } else { RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout)); } + CAFFEINE.put(key, object); } /** @@ -107,6 +127,7 @@ public class PlusSaTokenDao implements SaTokenDao { public void updateObject(String key, Object object) { if (RedisUtils.hasKey(key)) { RedisUtils.setCacheObject(key, object, true); + CAFFEINE.put(key, object); } } @@ -139,10 +160,14 @@ public class PlusSaTokenDao implements SaTokenDao { /** * 搜索数据 */ + @SuppressWarnings("unchecked") @Override public List searchData(String prefix, String keyword, int start, int size, boolean sortType) { - Collection keys = RedisUtils.keys(prefix + "*" + keyword + "*"); - List list = new ArrayList<>(keys); - return SaFoxUtil.searchList(list, start, size, sortType); + String keyStr = prefix + "*" + keyword + "*"; + return (List) CAFFEINE.get(keyStr, k -> { + Collection keys = RedisUtils.keys(keyStr); + List list = new ArrayList<>(keys); + return SaFoxUtil.searchList(list, start, size, sortType); + }); } } diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java index 8488ac199..ce3d34451 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java @@ -1,7 +1,5 @@ package org.dromara.common.satoken.utils; -import cn.dev33.satoken.context.SaHolder; -import cn.dev33.satoken.context.model.SaStorage; import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; @@ -15,7 +13,6 @@ import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.enums.UserType; import java.util.Set; -import java.util.function.Supplier; /** * 登录鉴权助手 @@ -35,9 +32,10 @@ public class LoginHelper { public static final String LOGIN_USER_KEY = "loginUser"; public static final String TENANT_KEY = "tenantId"; 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_NAME_KEY = "deptName"; public static final String CLIENT_KEY = "clientid"; - public static final String TENANT_ADMIN_KEY = "isTenantAdmin"; /** * 登录系统 基于 设备类型 @@ -47,11 +45,6 @@ public class LoginHelper { * @param model 配置参数 */ public static void login(LoginUser loginUser, SaLoginModel model) { - SaStorage storage = SaHolder.getStorage(); - storage.set(LOGIN_USER_KEY, loginUser); - storage.set(TENANT_KEY, loginUser.getTenantId()); - storage.set(USER_KEY, loginUser.getUserId()); - storage.set(DEPT_KEY, loginUser.getDeptId()); model = ObjectUtil.defaultIfNull(model, new SaLoginModel()); StpUtil.login(loginUser.getLoginId(), model.setExtra(TENANT_KEY, loginUser.getTenantId()) @@ -66,13 +59,11 @@ public class LoginHelper { * 获取用户(多级缓存) */ public static LoginUser getLoginUser() { - return (LoginUser) getStorageIfAbsentSet(LOGIN_USER_KEY, () -> { - SaSession session = StpUtil.getTokenSession(); - if (ObjectUtil.isNull(session)) { - return null; - } - return session.get(LOGIN_USER_KEY); - }); + SaSession session = StpUtil.getTokenSession(); + if (ObjectUtil.isNull(session)) { + return null; + } + return (LoginUser) session.get(LOGIN_USER_KEY); } /** @@ -90,7 +81,7 @@ public class LoginHelper { * 获取用户id */ public static Long getUserId() { - return Convert.toLong(getExtra(USER_KEY)); + return Convert.toLong(getExtra(USER_KEY)); } /** @@ -107,8 +98,18 @@ public class LoginHelper { return Convert.toLong(getExtra(DEPT_KEY)); } + /** + * 获取当前 Token 的扩展信息 + * + * @param key 键值 + * @return 对应的扩展数据 + */ private static Object getExtra(String key) { - return getStorageIfAbsentSet(key, () -> StpUtil.getExtra(key)); + try { + return StpUtil.getExtra(key); + } catch (Exception e) { + return null; + } } /** @@ -136,12 +137,17 @@ public class LoginHelper { return UserConstants.SUPER_ADMIN_ID.equals(userId); } + /** + * 是否为超级管理员 + * + * @return 结果 + */ public static boolean isSuperAdmin() { return isSuperAdmin(getUserId()); } /** - * 是否为超级管理员 + * 是否为租户管理员 * * @param rolePermission 角色权限标识组 * @return 结果 @@ -150,27 +156,22 @@ public class LoginHelper { return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY); } + /** + * 是否为租户管理员 + * + * @return 结果 + */ public static boolean isTenantAdmin() { - Object value = getStorageIfAbsentSet(TENANT_ADMIN_KEY, () -> { - return isTenantAdmin(getLoginUser().getRolePermission()); - }); - return Convert.toBool(value); + return Convert.toBool(isTenantAdmin(getLoginUser().getRolePermission())); } + /** + * 检查当前用户是否已登录 + * + * @return 结果 + */ public static boolean isLogin() { return getLoginUser() != null; } - public static Object getStorageIfAbsentSet(String key, Supplier handle) { - try { - Object obj = SaHolder.getStorage().get(key); - if (ObjectUtil.isNull(obj)) { - obj = handle.get(); - SaHolder.getStorage().set(key, obj); - } - return obj; - } catch (Exception e) { - return null; - } - } } diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java index 63d7eb15b..b9283e038 100644 --- a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java +++ b/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java @@ -4,14 +4,14 @@ import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.interceptor.SaInterceptor; import cn.dev33.satoken.router.SaRouter; import cn.dev33.satoken.stp.StpUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; 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.satoken.utils.LoginHelper; import org.dromara.common.security.config.properties.SecurityProperties; 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.context.properties.EnableConfigurationProperties; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; @@ -38,35 +38,35 @@ public class SecurityConfig implements WebMvcConfigurer { public void addInterceptors(InterceptorRegistry registry) { // 注册路由拦截器,自定义验证规则 registry.addInterceptor(new SaInterceptor(handler -> { - AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class); - // 登录验证 -- 排除多个路径 - SaRouter - // 获取所有的 - .match(allUrlHandler.getUrls()) - // 对未排除的路径进行检查 - .check(() -> { - // 检查是否登录 是否有token - StpUtil.checkLogin(); + AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class); + // 登录验证 -- 排除多个路径 + SaRouter + // 获取所有的 + .match(allUrlHandler.getUrls()) + // 对未排除的路径进行检查 + .check(() -> { + // 检查是否登录 是否有token + StpUtil.checkLogin(); - // 检查 header 与 param 里的 clientid 与 token 里的是否一致 - String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY); - String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY); - String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString(); - if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) { - // token 无效 - throw NotLoginException.newInstance(StpUtil.getLoginType(), - "-100", "客户端ID与Token不匹配", - StpUtil.getTokenValue()); - } + // 检查 header 与 param 里的 clientid 与 token 里的是否一致 + String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY); + String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY); + String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString(); + if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) { + // token 无效 + throw NotLoginException.newInstance(StpUtil.getLoginType(), + "-100", "客户端ID与Token不匹配", + StpUtil.getTokenValue()); + } - // 有效率影响 用于临时测试 - // if (log.isDebugEnabled()) { - // log.info("剩余有效时间: {}", StpUtil.getTokenTimeout()); - // log.info("临时有效时间: {}", StpUtil.getTokenActivityTimeout()); - // } + // 有效率影响 用于临时测试 + // if (log.isDebugEnabled()) { + // log.info("剩余有效时间: {}", StpUtil.getTokenTimeout()); + // log.info("临时有效时间: {}", StpUtil.getTokenActivityTimeout()); + // } - }); - })).addPathPatterns("/**") + }); + })).addPathPatterns("/**") // 排除不需要拦截的路径 .excludePathPatterns(securityProperties.getExcludes()); } diff --git a/ruoyi-common/ruoyi-common-sms/pom.xml b/ruoyi-common/ruoyi-common-sms/pom.xml index c50f222ac..932cb9d52 100644 --- a/ruoyi-common/ruoyi-common-sms/pom.xml +++ b/ruoyi-common/ruoyi-common-sms/pom.xml @@ -20,13 +20,12 @@ org.dromara.sms4j sms4j-spring-boot-starter - - - - com.alibaba - fastjson - - + + + + + org.dromara + ruoyi-common-redis diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java index 29f60fc50..1e88407e8 100644 --- a/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java +++ b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java @@ -1,14 +1,24 @@ package org.dromara.common.sms.config; +import org.dromara.common.sms.core.dao.PlusSmsDao; +import org.dromara.sms4j.api.dao.SmsDao; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; /** - * 短信配置类(暂时没用 预留扩展) + * 短信配置类 * - * @author Lion Li - * @version 4.2.0 + * @author Feng */ -@AutoConfiguration +@AutoConfiguration(after = {RedisAutoConfiguration.class}) public class SmsAutoConfiguration { + @Primary + @Bean + public SmsDao smsDao() { + return new PlusSmsDao(); + } + } diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/dao/PlusSmsDao.java b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/dao/PlusSmsDao.java new file mode 100644 index 000000000..91d8d243a --- /dev/null +++ b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/dao/PlusSmsDao.java @@ -0,0 +1,72 @@ +package org.dromara.common.sms.core.dao; + +import org.dromara.common.core.constant.GlobalConstants; +import org.dromara.common.redis.utils.RedisUtils; +import org.dromara.sms4j.api.dao.SmsDao; + +import java.time.Duration; + +/** + * SmsDao缓存配置 (使用框架自带RedisUtils实现 协议统一) + * 主要用于短信重试和拦截的缓存 + * + * @author Feng + */ +public class PlusSmsDao implements SmsDao { + + /** + * 存储 + * + * @param key 键 + * @param value 值 + * @param cacheTime 缓存时间(单位:秒) + */ + @Override + public void set(String key, Object value, long cacheTime) { + RedisUtils.setCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key, value, Duration.ofSeconds(cacheTime)); + } + + /** + * 存储 + * + * @param key 键 + * @param value 值 + */ + @Override + public void set(String key, Object value) { + RedisUtils.setCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key, value, true); + } + + /** + * 读取 + * + * @param key 键 + * @return 值 + */ + @Override + public Object get(String key) { + return RedisUtils.getCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + /** + * remove + * 根据key移除缓存 + * + * @param key 缓存键 + * @return 被删除的value + * @author :Wind + */ + @Override + public Object remove(String key) { + return RedisUtils.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + /** + * 清空 + */ + @Override + public void clean() { + RedisUtils.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + "sms:"); + } + +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java index 76be234c6..5f49d9c27 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java @@ -2,6 +2,8 @@ package org.dromara.common.social.config.properties; import lombok.Data; +import java.util.List; + /** * 社交登录配置 * @@ -65,4 +67,9 @@ public class SocialLoginConfigProperties { */ private String serverUrl; + /** + * 请求范围 + */ + private List scopes; + } diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java index 1beb7d042..1487a6a92 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java @@ -16,11 +16,6 @@ import java.util.Map; @ConfigurationProperties(prefix = "justauth") public class SocialProperties { - /** - * 是否启用 - */ - private Boolean enabled; - /** * 授权类型 */ diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java new file mode 100644 index 000000000..13649f9fd --- /dev/null +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java @@ -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")); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java new file mode 100644 index 000000000..e47d6c640 --- /dev/null +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java @@ -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; + } + + } +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java index 51b7e13d7..04f621489 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java @@ -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.SocialProperties; 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() .clientId(obj.getClientId()) .clientSecret(obj.getClientSecret()) - .redirectUri(obj.getRedirectUri()); + .redirectUri(obj.getRedirectUri()) + .scopes(obj.getScopes()); return switch (source.toLowerCase()) { case "dingtalk" -> new AuthDingTalkRequest(builder.build(), STATE_CACHE); case "baidu" -> new AuthBaiduRequest(builder.build(), STATE_CACHE); @@ -63,6 +65,7 @@ public class SocialUtils { case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE); case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE); case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE); + case "topiam" -> new AuthTopIamRequest(builder.build(), STATE_CACHE); default -> throw new AuthException("未获取到有效的Auth配置"); }; } diff --git a/ruoyi-common/ruoyi-common-tenant/pom.xml b/ruoyi-common/ruoyi-common-tenant/pom.xml index 2b87bf961..8e1a6ab1e 100644 --- a/ruoyi-common/ruoyi-common-tenant/pom.xml +++ b/ruoyi-common/ruoyi-common-tenant/pom.xml @@ -19,6 +19,7 @@ org.dromara ruoyi-common-mybatis + true @@ -26,11 +27,6 @@ ruoyi-common-redis - - com.alibaba - transmittable-thread-local - - diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java index 8610641dd..0ff39fdc0 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java @@ -2,8 +2,6 @@ package org.dromara.common.tenant.config; import cn.dev33.satoken.dao.SaTokenDao; import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; -import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.mybatis.config.MybatisPlusConfig; @@ -24,9 +22,6 @@ import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; -import java.util.ArrayList; -import java.util.List; - /** * 租户配置类 * @@ -37,23 +32,10 @@ import java.util.List; @ConditionalOnProperty(value = "tenant.enable", havingValue = "true") public class TenantConfig { - /** - * 初始化租户配置 - */ - @Bean - public boolean tenantInit(MybatisPlusInterceptor mybatisPlusInterceptor, - TenantProperties tenantProperties) { - List interceptors = new ArrayList<>(); - // 多租户插件 必须放到第一位 - interceptors.add(tenantLineInnerInterceptor(tenantProperties)); - interceptors.addAll(mybatisPlusInterceptor.getInterceptors()); - mybatisPlusInterceptor.setInterceptors(interceptors); - return true; - } - /** * 多租户插件 */ + @Bean public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties) { return new TenantLineInnerInterceptor(new PlusTenantLineHandler(tenantProperties)); } diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java index 14dad1a71..947539863 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java @@ -1,5 +1,6 @@ package org.dromara.common.tenant.handle; +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.utils.StringUtils; @@ -26,6 +27,9 @@ public class TenantKeyPrefixHandler extends KeyPrefixHandler { if (StringUtils.isBlank(name)) { return null; } + if (InterceptorIgnoreHelper.willIgnoreTenantLine("")) { + return super.map(name); + } if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) { return super.map(name); } @@ -49,6 +53,9 @@ public class TenantKeyPrefixHandler extends KeyPrefixHandler { if (StringUtils.isBlank(unmap)) { return null; } + if (InterceptorIgnoreHelper.willIgnoreTenantLine("")) { + return super.unmap(name); + } if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) { return super.unmap(name); } diff --git a/ruoyi-common/ruoyi-common-web/pom.xml b/ruoyi-common/ruoyi-common-web/pom.xml index 293c52229..b250fa9d0 100644 --- a/ruoyi-common/ruoyi-common-web/pom.xml +++ b/ruoyi-common/ruoyi-common-web/pom.xml @@ -57,11 +57,6 @@ cn.hutool hutool-crypto - - - com.alibaba - transmittable-thread-local - diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java index 9225a51bf..650517ed1 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java @@ -23,7 +23,7 @@ public class CaptchaConfig { private static final int WIDTH = 160; private static final int HEIGHT = 60; - private static final Color BACKGROUND = Color.PINK; + private static final Color BACKGROUND = Color.LIGHT_GRAY; private static final Font FONT = new Font("Arial", Font.BOLD, 48); /** diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java index 421ce6d14..0f7892861 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java @@ -2,9 +2,11 @@ package org.dromara.common.web.config; import io.undertow.server.DefaultByteBufferPool; import io.undertow.websockets.jsr.WebSocketDeploymentInfo; +import org.dromara.common.core.utils.SpringUtils; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.core.task.VirtualThreadTaskExecutor; /** * Undertow 自定义配置 @@ -24,6 +26,12 @@ public class UndertowConfig implements WebServerFactoryCustomizer invokeTimeTL = new TransmittableThreadLocal<>(); + private final static ThreadLocal KEY_CACHE = new ThreadLocal<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { @@ -56,7 +55,7 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { } StopWatch stopWatch = new StopWatch(); - invokeTimeTL.set(stopWatch); + KEY_CACHE.set(stopWatch); stopWatch.start(); } return true; @@ -70,10 +69,10 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { if (!prodProfile.equals(SpringUtils.getActiveProfile())) { - StopWatch stopWatch = invokeTimeTL.get(); + StopWatch stopWatch = KEY_CACHE.get(); stopWatch.stop(); log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime()); - invokeTimeTL.remove(); + KEY_CACHE.remove(); } } diff --git a/ruoyi-extend/pom.xml b/ruoyi-extend/pom.xml index f5348cc13..0e9533f18 100644 --- a/ruoyi-extend/pom.xml +++ b/ruoyi-extend/pom.xml @@ -14,6 +14,7 @@ ruoyi-monitor-admin ruoyi-powerjob-server + ruoyi-easyretry-server diff --git a/ruoyi-extend/ruoyi-easyretry-server/Dockerfile b/ruoyi-extend/ruoyi-easyretry-server/Dockerfile new file mode 100644 index 000000000..7ad0a0776 --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/Dockerfile @@ -0,0 +1,18 @@ +#FROM findepi/graalvm:java17-native +FROM openjdk:17.0.2-oraclelinux8 + +MAINTAINER Lion Li + +RUN mkdir -p /ruoyi/easyretry/logs + +WORKDIR /ruoyi/easyretry + +ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="-Xms512m -Xmx1024m" + +EXPOSE 8800 +EXPOSE 1788 + +ADD ./target/ruoyi-easyretry-server.jar ./app.jar + +ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -jar app.jar \ + -XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*,:time,tags,level -XX:+UseZGC ${JAVA_OPTS} diff --git a/ruoyi-extend/ruoyi-easyretry-server/pom.xml b/ruoyi-extend/ruoyi-easyretry-server/pom.xml new file mode 100644 index 000000000..4ea4d743b --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/pom.xml @@ -0,0 +1,46 @@ + + + + org.dromara + ruoyi-extend + ${revision} + + 4.0.0 + jar + ruoyi-easyretry-server + + + + com.aizuda + easy-retry-server-starter + ${easyretry.version} + + + + de.codecentric + spring-boot-admin-starter-client + ${spring-boot-admin.version} + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + + diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/java/org/dromara/easyretry/EasyRetryServerApplication.java b/ruoyi-extend/ruoyi-easyretry-server/src/main/java/org/dromara/easyretry/EasyRetryServerApplication.java new file mode 100644 index 000000000..9d01bc05b --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/java/org/dromara/easyretry/EasyRetryServerApplication.java @@ -0,0 +1,18 @@ +package org.dromara.easyretry; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * EasyRetry Server 启动程序 + * + * @author dhb52 + */ +@SpringBootApplication +public class EasyRetryServerApplication { + + public static void main(String[] args) { + SpringApplication.run(com.aizuda.easy.retry.server.EasyRetryServerApplication.class, args); + } + +} diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-dev.yml b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-dev.yml new file mode 100644 index 000000000..c151a6195 --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-dev.yml @@ -0,0 +1,50 @@ +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: root + hikari: + connection-timeout: 30000 + validation-timeout: 5000 + minimum-idle: 10 + maximum-pool-size: 20 + idle-timeout: 600000 + max-lifetime: 900000 + keepaliveTime: 30000 + +--- # easy-retry 服务端配置 +easy-retry: + # 拉取重试数据的每批次的大小 + retry-pull-page-size: 1000 + # 拉取重试数据的每批次的大小 + job-pull-page-size: 1000 + # 服务端 netty 端口 + netty-port: 1788 + # 重试和死信表的分区总数 + total-partition: 2 + # 一个客户端每秒最多接收的重试数量指令 + limiter: 1000 + # 号段模式下步长配置 + step: 100 + # 日志保存时间(单位: day) + log-storage: 90 + # 回调配置 + callback: + #回调最大执行次数 + max-count: 288 + #间隔时间 + trigger-interval: 900 + mode: all + retry-max-pull-count: 10 + +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: true + url: http://localhost:9090/admin + instance: + service-host-type: IP + username: ruoyi + password: 123456 diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-prod.yml b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-prod.yml new file mode 100644 index 000000000..c151a6195 --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-prod.yml @@ -0,0 +1,50 @@ +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: root + hikari: + connection-timeout: 30000 + validation-timeout: 5000 + minimum-idle: 10 + maximum-pool-size: 20 + idle-timeout: 600000 + max-lifetime: 900000 + keepaliveTime: 30000 + +--- # easy-retry 服务端配置 +easy-retry: + # 拉取重试数据的每批次的大小 + retry-pull-page-size: 1000 + # 拉取重试数据的每批次的大小 + job-pull-page-size: 1000 + # 服务端 netty 端口 + netty-port: 1788 + # 重试和死信表的分区总数 + total-partition: 2 + # 一个客户端每秒最多接收的重试数量指令 + limiter: 1000 + # 号段模式下步长配置 + step: 100 + # 日志保存时间(单位: day) + log-storage: 90 + # 回调配置 + callback: + #回调最大执行次数 + max-count: 288 + #间隔时间 + trigger-interval: 900 + mode: all + retry-max-pull-count: 10 + +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: true + url: http://localhost:9090/admin + instance: + service-host-type: IP + username: ruoyi + password: 123456 diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application.yml b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application.yml new file mode 100644 index 000000000..b6d8998e8 --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application.yml @@ -0,0 +1,40 @@ +server: + port: 8800 + servlet: + context-path: /easy-retry + +spring: + application: + name: ruoyi-easyretry-server + profiles: + active: @profiles.active@ + web: + resources: + static-locations: classpath:admin/ + +mybatis-plus: + typeAliasesPackage: com.aizuda.easy.retry.template.datasource.persistence.po + global-config: + db-config: + table-prefix: er_ + where-strategy: NOT_EMPTY + capital-mode: false + logic-delete-value: 1 + logic-not-delete-value: 0 + configuration: + map-underscore-to-camel-case: true + cache-enabled: true + +logging: + config: classpath:logback-plus.xml + +management: + endpoints: + web: + exposure: + include: '*' + endpoint: + health: + show-details: ALWAYS + logfile: + external-file: ./logs/ruoyi-easyretry-server/console.log diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/banner.txt b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/banner.txt new file mode 100644 index 000000000..869a4209c --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/banner.txt @@ -0,0 +1,10 @@ +Application Version: ${revision} +Spring Boot Version: ${spring-boot.version} + _ + | | + ___ __ _ ___ _ _ ______ _ __ ___| |_ _ __ _ _ ______ ___ ___ _ ____ _____ _ __ + / _ \/ _` / __| | | |______| '__/ _ \ __| '__| | | |______/ __|/ _ \ '__\ \ / / _ \ '__| +| __/ (_| \__ \ |_| | | | | __/ |_| | | |_| | \__ \ __/ | \ V / __/ | + \___|\__,_|___/\__, | |_| \___|\__|_| \__, | |___/\___|_| \_/ \___|_| + __/ | __/ | + |___/ |___/ diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/logback-plus.xml b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/logback-plus.xml new file mode 100644 index 000000000..bcd3abc4c --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/logback-plus.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + ${console.log.pattern} + utf-8 + + + + + + ${log.path}/console.log + + + ${log.path}/console.%d{yyyy-MM-dd}.log + + 1 + + + ${log.pattern} + utf-8 + + + + INFO + + + + + ${log.path}/info.log + + ${log.path}/info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + INFO + ACCEPT + DENY + + + + + ${log.path}/error.log + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + ERROR + ACCEPT + DENY + + + + + 100 + 1024 + + + + + 100 + 1024 + + + + + + + + + + + + + + + diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java index 1f70c75f7..53d248e39 100644 --- a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java +++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java @@ -3,7 +3,7 @@ package org.dromara.monitor.admin.config; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; -import org.springframework.boot.task.TaskExecutorBuilder; +import org.springframework.boot.task.ThreadPoolTaskExecutorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; @@ -23,7 +23,7 @@ public class AdminServerConfig { @Lazy @Bean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME) @ConditionalOnMissingBean(Executor.class) - public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) { + public ThreadPoolTaskExecutor applicationTaskExecutor(ThreadPoolTaskExecutorBuilder builder) { return builder.build(); } diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java index fb1973148..b993f60b5 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java @@ -5,7 +5,6 @@ import org.dromara.common.core.domain.R; import org.dromara.sms4j.api.SmsBlend; import org.dromara.sms4j.api.entity.SmsResponse; import org.dromara.sms4j.core.factory.SmsFactory; -import org.dromara.sms4j.provider.enumerate.SupplierType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -25,7 +24,6 @@ import java.util.LinkedHashMap; @RestController @RequestMapping("/demo/sms") public class SmsController { - /** * 发送短信Aliyun * @@ -36,7 +34,7 @@ public class SmsController { public R sendAliyun(String phones, String templateId) { LinkedHashMap map = new LinkedHashMap<>(1); map.put("code", "1234"); - SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA); + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map); return R.ok(smsResponse); } @@ -52,9 +50,33 @@ public class SmsController { LinkedHashMap map = new LinkedHashMap<>(1); // map.put("2", "测试测试"); map.put("1", "1234"); - SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.TENCENT); + SmsBlend smsBlend = SmsFactory.getSmsBlend("config2"); SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map); return R.ok(smsResponse); } + /** + * 添加黑名单 + * + * @param phone 手机号 + */ + @GetMapping("/addBlacklist") + public R addBlacklist(String phone){ + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + smsBlend.joinInBlacklist(phone); + return R.ok(); + } + + /** + * 移除黑名单 + * + * @param phone 手机号 + */ + @GetMapping("/removeBlacklist") + public R removeBlacklist(String phone){ + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + smsBlend.removeFromBlacklist(phone); + return R.ok(); + } + } diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java index 5ce7ad043..ebdb9930c 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java @@ -42,7 +42,7 @@ public class GenTableColumn extends BaseEntity { /** * 列描述 */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String columnComment; /** @@ -64,43 +64,43 @@ public class GenTableColumn extends BaseEntity { /** * 是否主键(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isPk; /** * 是否自增(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isIncrement; /** * 是否必填(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isRequired; /** * 是否为插入字段(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isInsert; /** * 是否编辑字段(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isEdit; /** * 是否列表字段(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isList; /** * 是否查询字段(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isQuery; /** diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm index 14177b5df..64389717d 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm @@ -20,7 +20,7 @@ import org.dromara.common.excel.utils.ExcelUtil; import ${packageName}.domain.vo.${ClassName}Vo; import ${packageName}.domain.bo.${ClassName}Bo; import ${packageName}.service.I${ClassName}Service; -#if($table.crud || $table.sub) +#if($table.crud) import org.dromara.common.mybatis.core.page.TableDataInfo; #elseif($table.tree) #end @@ -44,7 +44,7 @@ public class ${ClassName}Controller extends BaseController { */ @SaCheckPermission("${permissionPrefix}:list") @GetMapping("/list") -#if($table.crud || $table.sub) +#if($table.crud) public TableDataInfo<${ClassName}Vo> list(${ClassName}Bo bo, PageQuery pageQuery) { return ${className}Service.queryPageList(bo, pageQuery); } diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm index d596a0e91..f1fa3d991 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm @@ -3,7 +3,7 @@ package ${packageName}.service; import ${packageName}.domain.${ClassName}; import ${packageName}.domain.vo.${ClassName}Vo; import ${packageName}.domain.bo.${ClassName}Bo; -#if($table.crud || $table.sub) +#if($table.crud) import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; #end @@ -24,7 +24,7 @@ public interface I${ClassName}Service { */ ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}); -#if($table.crud || $table.sub) +#if($table.crud) /** * 查询${functionName}列表 */ diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm index 75a9b83a2..d603de120 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -2,7 +2,7 @@ package ${packageName}.service.impl; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; -#if($table.crud || $table.sub) +#if($table.crud) import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -41,7 +41,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { return baseMapper.selectVoById(${pkColumn.javaField}); } -#if($table.crud || $table.sub) +#if($table.crud) /** * 查询${functionName}列表 */ diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm index 8b132f4ab..aaceca611 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -439,7 +439,7 @@ const submitForm = () => { } else { await add${BusinessName}(form.value).finally(() => buttonLoading.value = false); } - proxy?.#[[$modal]]#.msgSuccess("修改成功"); + proxy?.#[[$modal]]#.msgSuccess("操作成功"); dialog.visible = false; await getList(); } diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestAnnoJobExecutor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestAnnoJobExecutor.java new file mode 100644 index 000000000..05c4f2fb0 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestAnnoJobExecutor.java @@ -0,0 +1,23 @@ +package org.dromara.job.easyretry; + +import com.aizuda.easy.retry.client.job.core.annotation.JobExecutor; +import com.aizuda.easy.retry.client.job.core.dto.JobArgs; +import com.aizuda.easy.retry.client.model.ExecuteResult; +import com.aizuda.easy.retry.common.core.util.JsonUtil; +import com.aizuda.easy.retry.common.log.EasyRetryLog; +import org.springframework.stereotype.Component; + +/** + * @author www.byteblogs.com + * @date 2023-09-28 22:54:07 + */ +@Component +@JobExecutor(name = "testJobExecutor") +public class TestAnnoJobExecutor { + + public ExecuteResult jobExecute(JobArgs jobArgs) { + EasyRetryLog.LOCAL.info("testJobExecutor. jobArgs:{}", JsonUtil.toJsonString(jobArgs)); + EasyRetryLog.REMOTE.info("testJobExecutor. jobArgs:{}", JsonUtil.toJsonString(jobArgs)); + return ExecuteResult.success("测试成功"); + } +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestClassJobExecutor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestClassJobExecutor.java new file mode 100644 index 000000000..d675cec73 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestClassJobExecutor.java @@ -0,0 +1,19 @@ +package org.dromara.job.easyretry; + +import com.aizuda.easy.retry.client.job.core.dto.JobArgs; +import com.aizuda.easy.retry.client.job.core.executor.AbstractJobExecutor; +import com.aizuda.easy.retry.client.model.ExecuteResult; +import org.springframework.stereotype.Component; + +/** + * @author www.byteblogs.com + * @date 2023-09-28 22:54:07 + */ +@Component +public class TestClassJobExecutor extends AbstractJobExecutor { + + @Override + protected ExecuteResult doJobExecute(JobArgs jobArgs) { + return ExecuteResult.success("TestJobExecutor测试成功"); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java index def448d4d..9b08c2d2c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java @@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** * 在线用户监控 @@ -87,4 +88,43 @@ public class SysUserOnlineController extends BaseController { } return R.ok(); } + + /** + * 获取当前用户登录在线设备 + */ + @GetMapping() + public TableDataInfo getInfo() { + // 获取指定账号 id 的 token 集合 + List tokenIds = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString()); + List userOnlineDTOList = tokenIds.stream() + .filter(token -> StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) >= -1) + .map(token -> (UserOnlineDTO) RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token)) + .collect(Collectors.toList()); + //复制和处理 SysUserOnline 对象列表 + Collections.reverse(userOnlineDTOList); + userOnlineDTOList.removeAll(Collections.singleton(null)); + List userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class); + return TableDataInfo.build(userOnlineList); + } + + /** + * 强退当前在线设备 + * + * @param tokenId token值 + */ + @Log(title = "在线设备", businessType = BusinessType.FORCE) + @PostMapping("/{tokenId}") + public R remove(@PathVariable("tokenId") String tokenId) { + try { + // 获取指定账号 id 的 token 集合 + List keys = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString()); + keys.stream() + .filter(key -> key.equals(tokenId)) + .findFirst() + .ifPresent(key -> StpUtil.kickoutByTokenValue(tokenId)); + } catch (NotLoginException ignored) { + } + return R.ok(); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java index 61d30c76a..13be4a4a9 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java @@ -97,7 +97,7 @@ public class SysClientController extends BaseController { @Log(title = "客户端管理", businessType = BusinessType.UPDATE) @PutMapping("/changeStatus") public R changeStatus(@RequestBody SysClientBo bo) { - return toAjax(sysClientService.updateUserStatus(bo.getId(), bo.getStatus())); + return toAjax(sysClientService.updateUserStatus(bo.getClientId(), bo.getStatus())); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java index 130c0d1f3..4f5f23f32 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java @@ -2,6 +2,7 @@ package org.dromara.system.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.convert.Convert; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.domain.R; import org.dromara.common.core.utils.StringUtils; @@ -11,7 +12,6 @@ import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.bo.SysDeptBo; import org.dromara.system.domain.vo.SysDeptVo; import org.dromara.system.service.ISysDeptService; -import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -120,4 +120,16 @@ public class SysDeptController extends BaseController { deptService.checkDeptDataScope(deptId); return toAjax(deptService.deleteDeptById(deptId)); } + + /** + * 获取部门选择框列表 + * + * @param deptIds 部门ID串 + */ + @SaCheckPermission("system:dept:query") + @GetMapping("/optionselect") + public R> optionselect(@RequestParam(required = false) Long[] deptIds) { + return R.ok(deptService.selectDeptByIds(deptIds == null ? null : List.of(deptIds))); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java index fe62fdbbc..330b3fb52 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java @@ -1,6 +1,8 @@ package org.dromara.system.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.domain.R; import org.dromara.common.excel.utils.ExcelUtil; @@ -12,8 +14,6 @@ import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.bo.SysPostBo; import org.dromara.system.domain.vo.SysPostVo; import org.dromara.system.service.ISysPostService; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -110,12 +110,13 @@ public class SysPostController extends BaseController { /** * 获取岗位选择框列表 + * + * @param postIds 岗位ID串 */ + @SaCheckPermission("system:post:query") @GetMapping("/optionselect") - public R> optionselect() { - SysPostBo postBo = new SysPostBo(); - postBo.setStatus(UserConstants.POST_NORMAL); - List posts = postService.selectPostList(postBo); - return R.ok(posts); + public R> optionselect(@RequestParam(required = false) Long[] postIds) { + return R.ok(postService.selectPostByIds(postIds == null ? null : List.of(postIds))); } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java index f9c4b3d31..0cdb67548 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java @@ -19,6 +19,7 @@ import org.dromara.system.domain.vo.ProfileVo; import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.service.ISysOssService; +import org.dromara.system.service.ISysRoleService; import org.dromara.system.service.ISysUserService; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; @@ -50,8 +51,8 @@ public class SysProfileController extends BaseController { SysUserVo user = userService.selectUserById(LoginHelper.getUserId()); ProfileVo profileVo = new ProfileVo(); profileVo.setUser(user); - profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserName())); - profileVo.setPostGroup(userService.selectUserPostGroup(user.getUserName())); + profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserId())); + profileVo.setPostGroup(userService.selectUserPostGroup(user.getUserId())); return R.ok(profileVo); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java index 3a9418b23..d4a9dc8a4 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java @@ -149,11 +149,13 @@ public class SysRoleController extends BaseController { /** * 获取角色选择框列表 + * + * @param roleIds 角色ID串 */ @SaCheckPermission("system:role:query") @GetMapping("/optionselect") - public R> optionselect() { - return R.ok(roleService.selectRoleAll()); + public R> optionselect(@RequestParam(required = false) Long[] roleIds) { + return R.ok(roleService.selectRoleByIds(roleIds == null ? null : List.of(roleIds))); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java index beefe4a81..b1502ac92 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java @@ -11,7 +11,6 @@ import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.model.LoginUser; -import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.encrypt.annotation.ApiEncrypt; @@ -72,9 +71,8 @@ public class SysUserController extends BaseController { @SaCheckPermission("system:user:export") @PostMapping("/export") public void export(SysUserBo user, HttpServletResponse response) { - List list = userService.selectUserList(user); - List listVo = MapstructUtils.convert(list, SysUserExportVo.class); - ExcelUtil.exportExcel(listVo, "用户数据", SysUserExportVo.class, response); + List list = userService.selectUserExportList(user); + ExcelUtil.exportExcel(list, "用户数据", SysUserExportVo.class, response); } /** @@ -142,7 +140,7 @@ public class SysUserController extends BaseController { if (ObjectUtil.isNotNull(userId)) { SysUserVo sysUser = userService.selectUserById(userId); userInfoVo.setUser(sysUser); - userInfoVo.setRoleIds(StreamUtils.toList(sysUser.getRoles(), SysRoleVo::getRoleId)); + userInfoVo.setRoleIds(roleService.selectRoleListByUserId(userId)); userInfoVo.setPostIds(postService.selectPostListByUserId(userId)); } return R.ok(userInfoVo); @@ -207,6 +205,19 @@ public class SysUserController extends BaseController { return toAjax(userService.deleteUserByIds(userIds)); } + /** + * 根据用户ID串批量获取用户基础信息 + * + * @param userIds 用户ID串 + * @param deptId 部门ID + */ + @SaCheckPermission("system:user:query") + @GetMapping("/optionselect") + public R> optionselect(@RequestParam(required = false) Long[] userIds, + @RequestParam(required = false) Long deptId) { + return R.ok(userService.selectUserByIds(userIds == null ? null : List.of(userIds), deptId)); + } + /** * 重置密码 */ @@ -241,8 +252,9 @@ public class SysUserController extends BaseController { @SaCheckPermission("system:user:query") @GetMapping("/authRole/{userId}") public R authRole(@PathVariable Long userId) { + userService.checkUserDataScope(userId); SysUserVo user = userService.selectUserById(userId); - List roles = roleService.selectRolesByUserId(userId); + List roles = roleService.selectRolesAuthByUserId(userId); SysUserInfoVo userInfoVo = new SysUserInfoVo(); userInfoVo.setUser(user); userInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isSuperAdmin())); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java index 6def906e1..ba30eb609 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java @@ -7,7 +7,6 @@ import lombok.Data; * * @author Lion Li */ - @Data public class SysUserOnline { diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java index 39257f70b..fcc1ac11d 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java @@ -6,6 +6,7 @@ import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import lombok.Data; import lombok.EqualsAndHashCode; +import org.dromara.common.core.constant.RegexConstants; import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.system.domain.SysDictType; @@ -37,7 +38,7 @@ public class SysDictTypeBo extends BaseEntity { */ @NotBlank(message = "字典类型不能为空") @Size(min = 0, max = 100, message = "字典类型类型长度不能超过{max}个字符") - @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + @Pattern(regexp = RegexConstants.DICTIONARY_TYPE, message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") private String dictType; /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java index 498280a90..fbaafaa63 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java @@ -4,9 +4,11 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.github.linpeilie.annotations.AutoMapper; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import lombok.Data; import lombok.EqualsAndHashCode; +import org.dromara.common.core.constant.RegexConstants; import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.system.domain.SysMenu; @@ -92,6 +94,7 @@ public class SysMenuBo extends BaseEntity { */ @JsonInclude(JsonInclude.Include.NON_NULL) @Size(min = 0, max = 100, message = "权限标识长度不能超过{max}个字符") + @Pattern(regexp = RegexConstants.PERMISSION_STRING, message = "权限标识必须符合 tool:build:list 格式") private String perms; /** @@ -104,5 +107,4 @@ public class SysMenuBo extends BaseEntity { */ private String remark; - } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java index 3cc5186c8..37ec6b709 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java @@ -20,7 +20,6 @@ import java.util.Date; @Data @NoArgsConstructor -@AutoMapper(target = SysUserVo.class, convertGenerate = false) public class SysUserExportVo implements Serializable { @Serial @@ -85,14 +84,12 @@ public class SysUserExportVo implements Serializable { /** * 部门名称 */ - @ReverseAutoMapping(target = "deptName", source = "dept.deptName") @ExcelProperty(value = "部门名称") private String deptName; /** * 负责人 */ - @ReverseAutoMapping(target = "leaderName", source = "dept.leaderName") @ExcelProperty(value = "部门负责人") private String leaderName; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java index 7db91d180..d1f405919 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java @@ -114,9 +114,10 @@ public class SysUserVo implements Serializable { private Date createTime; /** - * 部门对象 + * 部门名 */ - private SysDeptVo dept; + @Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "deptId") + private String deptName; /** * 角色对象 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java index 45ad77e9c..08dda662c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java @@ -32,7 +32,7 @@ public interface SysDeptMapper extends BaseMapperPlus { @DataPermission({ @DataColumn(key = "deptName", value = "dept_id") }) - SysDeptVo selectDeptById(Long deptId); + long countDeptById(Long deptId); /** * 根据角色ID查询部门树信息 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java index 48e6a121f..d6d27596f 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java @@ -13,20 +13,12 @@ import java.util.List; */ public interface SysPostMapper extends BaseMapperPlus { - /** - * 根据用户ID获取岗位选择框列表 - * - * @param userId 用户ID - * @return 选中岗位ID列表 - */ - List selectPostListByUserId(Long userId); - /** * 查询用户所属岗位组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ - List selectPostsByUserName(String userName); + List selectPostsByUserId(Long userId); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java index 55ca76976..ac5a47e54 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java @@ -3,12 +3,12 @@ package org.dromara.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.system.domain.SysRole; import org.dromara.system.domain.vo.SysRoleVo; -import org.apache.ibatis.annotations.Param; import java.util.List; @@ -51,21 +51,12 @@ public interface SysRoleMapper extends BaseMapperPlus { */ List selectRolePermissionByUserId(Long userId); - - /** - * 根据用户ID获取角色选择框列表 - * - * @param userId 用户ID - * @return 选中角色ID列表 - */ - List selectRoleListByUserId(Long userId); - /** * 根据用户ID查询角色 * - * @param userName 用户名 + * @param userId 用户ID * @return 角色列表 */ - List selectRolesByUserName(String userName); + List selectRolesByUserId(Long userId); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java index 43222253c..fc7fc6e16 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java @@ -8,6 +8,7 @@ import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.vo.SysUserExportVo; import org.dromara.system.domain.vo.SysUserVo; import java.util.List; @@ -20,11 +21,17 @@ import java.util.List; public interface SysUserMapper extends BaseMapperPlus { @DataPermission({ - @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "deptName", value = "u.dept_id"), @DataColumn(key = "userName", value = "u.user_id") }) Page selectPageUserList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + List selectUserList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + /** * 根据条件分页查询用户列表 * @@ -35,7 +42,7 @@ public interface SysUserMapper extends BaseMapperPlus { @DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "userName", value = "u.user_id") }) - List selectUserList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + List selectUserExportList(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 根据条件分页查询已配用户角色列表 @@ -61,41 +68,11 @@ public interface SysUserMapper extends BaseMapperPlus { }) Page selectUnallocatedList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); - /** - * 通过用户名查询用户 - * - * @param userName 用户名 - * @return 用户对象信息 - */ - SysUserVo selectUserByUserName(String userName); - - /** - * 通过手机号查询用户 - * - * @param phonenumber 手机号 - * @return 用户对象信息 - */ - SysUserVo selectUserByPhonenumber(String phonenumber); - - /** - * 通过邮箱查询用户 - * - * @param email 邮箱 - * @return 用户对象信息 - */ - SysUserVo selectUserByEmail(String email); - - /** - * 通过用户ID查询用户 - * - * @param userId 用户ID - * @return 用户对象信息 - */ @DataPermission({ - @DataColumn(key = "deptName", value = "d.dept_id"), - @DataColumn(key = "userName", value = "u.user_id") + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") }) - SysUserVo selectUserById(Long userId); + long countUserById(Long userId); @Override @DataPermission({ diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java index db48d4b79..d0f8a3cbe 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java @@ -25,7 +25,7 @@ public interface ISysClientService { /** * 查询客户端信息基于客户端id */ - SysClient queryByClientId(String clientId); + SysClientVo queryByClientId(String clientId); /** * 查询客户端管理列表 @@ -50,7 +50,7 @@ public interface ISysClientService { /** * 修改状态 */ - int updateUserStatus(Long id, String status); + int updateUserStatus(String clientId, String status); /** * 校验并批量删除客户端管理信息 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java index b20938187..bf16642fe 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java @@ -52,6 +52,14 @@ public interface ISysDeptService { */ SysDeptVo selectDeptById(Long deptId); + /** + * 通过部门ID串查询部门 + * + * @param deptIds 部门id串 + * @return 部门列表信息 + */ + List selectDeptByIds(List deptIds); + /** * 根据ID查询所有子部门数(正常状态) * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java index a8bc57b9c..2f6dfc9a1 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java @@ -31,12 +31,11 @@ public interface ISysOssConfigService { */ TableDataInfo queryPageList(SysOssConfigBo bo, PageQuery pageQuery); - /** * 根据新增业务对象插入对象存储配置 * * @param bo 对象存储配置新增业务对象 - * @return + * @return 结果 */ Boolean insertByBo(SysOssConfigBo bo); @@ -44,7 +43,7 @@ public interface ISysOssConfigService { * 根据编辑业务对象修改对象存储配置 * * @param bo 对象存储配置编辑业务对象 - * @return + * @return 结果 */ Boolean updateByBo(SysOssConfigBo bo); @@ -53,7 +52,7 @@ public interface ISysOssConfigService { * * @param ids 主键集合 * @param isValid 是否校验,true-删除前校验,false-不校验 - * @return + * @return 结果 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java index 2dfe01fe9..057c068ce 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java @@ -19,18 +19,62 @@ import java.util.List; */ public interface ISysOssService { + /** + * 查询OSS对象存储列表 + * + * @param sysOss OSS对象存储分页查询对象 + * @param pageQuery 分页查询实体类 + * @return 结果 + */ TableDataInfo queryPageList(SysOssBo sysOss, PageQuery pageQuery); + /** + * 根据一组 ossIds 获取对应的 SysOssVo 列表 + * + * @param ossIds 一组文件在数据库中的唯一标识集合 + * @return 包含 SysOssVo 对象的列表 + */ List listByIds(Collection ossIds); + /** + * 根据 ossId 从缓存或数据库中获取 SysOssVo 对象 + * + * @param ossId 文件在数据库中的唯一标识 + * @return SysOssVo 对象,包含文件信息 + */ SysOssVo getById(Long ossId); + /** + * 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的 MultipartFile 对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + */ SysOssVo upload(MultipartFile file); + /** + * 上传文件到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的文件对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + */ SysOssVo upload(File file); + /** + * 文件下载方法,支持一次性下载完整文件 + * + * @param ossId OSS对象ID + * @param response HttpServletResponse对象,用于设置响应头和向客户端发送文件内容 + */ void download(Long ossId, HttpServletResponse response) throws IOException; + /** + * 删除OSS对象存储 + * + * @param ids OSS对象ID串 + * @param isValid 判断是否需要校验 + * @return 结果 + */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java index c6409c862..c43f0395c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java @@ -48,6 +48,14 @@ public interface ISysPostService { */ List selectPostListByUserId(Long userId); + /** + * 通过岗位ID串查询岗位 + * + * @param postIds 岗位id串 + * @return 岗位列表信息 + */ + List selectPostByIds(List postIds); + /** * 校验岗位名称 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java index d2ee61f8a..64740aeff 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java @@ -35,6 +35,14 @@ public interface ISysRoleService { */ List selectRolesByUserId(Long userId); + /** + * 根据用户ID查询角色列表(包含被授权状态) + * + * @param userId 用户ID + * @return 角色列表 + */ + List selectRolesAuthByUserId(Long userId); + /** * 根据用户ID查询角色权限 * @@ -66,6 +74,14 @@ public interface ISysRoleService { */ SysRoleVo selectRoleById(Long roleId); + /** + * 通过角色ID串查询角色 + * + * @param roleIds 角色ID串 + * @return 角色列表信息 + */ + List selectRoleByIds(List roleIds); + /** * 校验角色名称是否唯一 * @@ -180,4 +196,5 @@ public interface ISysRoleService { int insertAuthUsers(Long roleId, Long[] userIds); void cleanOnlineUserByRole(Long roleId); + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java index bbe78be2b..0325a2558 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java @@ -3,6 +3,7 @@ package org.dromara.system.service; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.system.domain.bo.SysUserBo; +import org.dromara.system.domain.vo.SysUserExportVo; import org.dromara.system.domain.vo.SysUserVo; import java.util.List; @@ -23,7 +24,7 @@ public interface ISysUserService { * @param user 用户信息 * @return 用户信息集合信息 */ - List selectUserList(SysUserBo user); + List selectUserExportList(SysUserBo user); /** * 根据条件分页查询已分配用户角色列表 @@ -65,21 +66,30 @@ public interface ISysUserService { */ SysUserVo selectUserById(Long userId); + /** + * 通过用户ID串查询用户 + * + * @param userIds 用户ID串 + * @param deptId 部门id + * @return 用户列表信息 + */ + List selectUserByIds(List userIds, Long deptId); + /** * 根据用户ID查询用户所属角色组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ - String selectUserRoleGroup(String userName); + String selectUserRoleGroup(Long userId); /** * 根据用户ID查询用户所属岗位组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ - String selectUserPostGroup(String userName); + String selectUserPostGroup(Long userId); /** * 校验用户名称是否唯一 @@ -205,8 +215,8 @@ public interface ISysUserService { /** * 通过部门id查询当前部门所有用户 * - * @param deptId - * @return + * @param deptId 部门id + * @return 结果 */ List selectUserListByDept(Long deptId); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java index aecaf668c..26bc49157 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java @@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; @@ -16,6 +17,8 @@ import org.dromara.system.domain.bo.SysClientBo; import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.mapper.SysClientMapper; import org.dromara.system.service.ISysClientService; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.Collection; @@ -48,9 +51,10 @@ public class SysClientServiceImpl implements ISysClientService { /** * 查询客户端管理 */ + @Cacheable(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId") @Override - public SysClient queryByClientId(String clientId) { - return baseMapper.selectOne(new LambdaQueryWrapper().eq(SysClient::getClientId, clientId)); + public SysClientVo queryByClientId(String clientId) { + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysClient::getClientId, clientId)); } /** @@ -105,6 +109,7 @@ public class SysClientServiceImpl implements ISysClientService { /** * 修改客户端管理 */ + @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, key = "#bo.clientId") @Override public Boolean updateByBo(SysClientBo bo) { SysClient update = MapstructUtils.convert(bo, SysClient.class); @@ -116,12 +121,13 @@ public class SysClientServiceImpl implements ISysClientService { /** * 修改状态 */ + @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId") @Override - public int updateUserStatus(Long id, String status) { + public int updateUserStatus(String clientId, String status) { return baseMapper.update(null, new LambdaUpdateWrapper() .set(SysClient::getStatus, status) - .eq(SysClient::getId, id)); + .eq(SysClient::getClientId, clientId)); } /** @@ -134,6 +140,7 @@ public class SysClientServiceImpl implements ISysClientService { /** * 批量删除客户端管理 */ + @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, allEntries = true) @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java index fe5cc0e54..86edf59d4 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java @@ -7,6 +7,7 @@ import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.exception.ServiceException; @@ -27,7 +28,6 @@ import org.dromara.system.mapper.SysDeptMapper; import org.dromara.system.mapper.SysRoleMapper; import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.service.ISysDeptService; -import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -83,6 +83,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { lqw.eq(ObjectUtil.isNotNull(bo.getParentId()), SysDept::getParentId, bo.getParentId()); lqw.like(StringUtils.isNotBlank(bo.getDeptName()), SysDept::getDeptName, bo.getDeptName()); lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDept::getStatus, bo.getStatus()); + lqw.orderByAsc(SysDept::getAncestors); lqw.orderByAsc(SysDept::getParentId); lqw.orderByAsc(SysDept::getOrderNum); lqw.orderByAsc(SysDept::getDeptId); @@ -138,6 +139,14 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { return dept; } + @Override + public List selectDeptByIds(List deptIds) { + return baseMapper.selectDeptList(new LambdaQueryWrapper() + .select(SysDept::getDeptId, SysDept::getDeptName, SysDept::getLeader) + .eq(SysDept::getStatus, UserConstants.DEPT_NORMAL) + .in(CollUtil.isNotEmpty(deptIds), SysDept::getDeptId, deptIds)); + } + /** * 通过部门ID查询部门名称 * @@ -221,8 +230,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { if (LoginHelper.isSuperAdmin()) { return; } - SysDeptVo dept = baseMapper.selectDeptById(deptId); - if (ObjectUtil.isNull(dept)) { + if (baseMapper.countDeptById(deptId) == 0) { throw new ServiceException("没有权限访问部门数据!"); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java index 346d6bc83..bc6663f0c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java @@ -1,13 +1,12 @@ package org.dromara.system.service.impl; -import cn.dev33.satoken.context.SaHolder; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; 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.exception.ServiceException; import org.dromara.common.core.service.DictService; @@ -26,7 +25,6 @@ import org.dromara.system.domain.vo.SysDictTypeVo; import org.dromara.system.mapper.SysDictDataMapper; import org.dromara.system.mapper.SysDictTypeMapper; import org.dromara.system.service.ISysDictTypeService; -import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -217,16 +215,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService * @param separator 分隔符 * @return 字典标签 */ - @SuppressWarnings("unchecked cast") @Override public String getDictLabel(String dictType, String dictValue, String separator) { - // 优先从本地缓存获取 - List datas = (List) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType); - if (ObjectUtil.isNull(datas)) { - datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); - SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas); - } - + List datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); Map map = StreamUtils.toMap(datas, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel); if (StringUtils.containsAny(dictValue, separator)) { return Arrays.stream(dictValue.split(separator)) @@ -245,16 +236,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService * @param separator 分隔符 * @return 字典值 */ - @SuppressWarnings("unchecked cast") @Override public String getDictValue(String dictType, String dictLabel, String separator) { - // 优先从本地缓存获取 - List datas = (List) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType); - if (ObjectUtil.isNull(datas)) { - datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); - SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas); - } - + List datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); Map map = StreamUtils.toMap(datas, SysDictDataVo::getDictLabel, SysDictDataVo::getDictValue); if (StringUtils.containsAny(dictLabel, separator)) { return Arrays.stream(dictLabel.split(separator)) diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java index d215d5951..9c930a0c5 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java @@ -5,30 +5,29 @@ import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgentUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.Constants; import org.dromara.common.core.utils.MapstructUtils; -import org.dromara.common.mybatis.core.page.PageQuery; -import org.dromara.common.log.event.LogininforEvent; -import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.ip.AddressUtils; +import org.dromara.common.log.event.LogininforEvent; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.satoken.utils.LoginHelper; -import org.dromara.system.domain.SysClient; import org.dromara.system.domain.SysLogininfor; import org.dromara.system.domain.bo.SysLogininforBo; +import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysLogininforVo; -import org.dromara.system.mapper.SysClientMapper; import org.dromara.system.mapper.SysLogininforMapper; import org.dromara.system.service.ISysClientService; import org.dromara.system.service.ISysLogininforService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; -import jakarta.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -60,10 +59,10 @@ public class SysLogininforServiceImpl implements ISysLogininforService { final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); final String ip = ServletUtils.getClientIP(request); // 客户端信息 - String clientid = request.getHeader(LoginHelper.CLIENT_KEY); - SysClient client = null; - if (StringUtils.isNotBlank(clientid)) { - client = clientService.queryByClientId(clientid); + String clientId = request.getHeader(LoginHelper.CLIENT_KEY); + SysClientVo client = null; + if (StringUtils.isNotBlank(clientId)) { + client = clientService.queryByClientId(clientId); } String address = AddressUtils.getRealAddressByIP(ip); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java index 8e2b5ac4a..18b7a08da 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java @@ -9,6 +9,7 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.system.domain.SysNotice; +import org.dromara.system.domain.SysUser; import org.dromara.system.domain.bo.SysNoticeBo; import org.dromara.system.domain.vo.SysNoticeVo; import org.dromara.system.domain.vo.SysUserVo; @@ -68,7 +69,7 @@ public class SysNoticeServiceImpl implements ISysNoticeService { lqw.like(StringUtils.isNotBlank(bo.getNoticeTitle()), SysNotice::getNoticeTitle, bo.getNoticeTitle()); lqw.eq(StringUtils.isNotBlank(bo.getNoticeType()), SysNotice::getNoticeType, bo.getNoticeType()); if (StringUtils.isNotBlank(bo.getCreateByName())) { - SysUserVo sysUser = userMapper.selectUserByUserName(bo.getCreateByName()); + SysUserVo sysUser = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, bo.getCreateByName())); lqw.eq(SysNotice::getCreateBy, ObjectUtil.isNotNull(sysUser) ? sysUser.getUserId() : null); } lqw.orderByAsc(SysNotice::getNoticeId); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java index c7d47191d..1df221c32 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java @@ -6,6 +6,8 @@ import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.service.OssService; @@ -25,8 +27,6 @@ import org.dromara.system.domain.bo.SysOssBo; import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.mapper.SysOssMapper; import org.dromara.system.service.ISysOssService; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; import org.springframework.cache.annotation.Cacheable; import org.springframework.http.MediaType; @@ -36,7 +36,10 @@ import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; /** * 文件上传 服务层实现 @@ -49,6 +52,13 @@ public class SysOssServiceImpl implements ISysOssService, OssService { private final SysOssMapper baseMapper; + /** + * 查询OSS对象存储列表 + * + * @param bo OSS对象存储分页查询对象 + * @param pageQuery 分页查询实体类 + * @return 结果 + */ @Override public TableDataInfo queryPageList(SysOssBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); @@ -58,6 +68,12 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return TableDataInfo.build(result); } + /** + * 根据一组 ossIds 获取对应的 SysOssVo 列表 + * + * @param ossIds 一组文件在数据库中的唯一标识集合 + * @return 包含 SysOssVo 对象的列表 + */ @Override public List listByIds(Collection ossIds) { List list = new ArrayList<>(); @@ -75,6 +91,12 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return list; } + /** + * 根据一组 ossIds 获取对应文件的 URL 列表 + * + * @param ossIds 以逗号分隔的 ossId 字符串 + * @return 以逗号分隔的文件 URL 字符串 + */ @Override public String selectUrlByIds(String ossIds) { List list = new ArrayList<>(); @@ -107,12 +129,25 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return lqw; } + /** + * 根据 ossId 从缓存或数据库中获取 SysOssVo 对象 + * + * @param ossId 文件在数据库中的唯一标识 + * @return SysOssVo 对象,包含文件信息 + */ @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId") @Override public SysOssVo getById(Long ossId) { return baseMapper.selectVoById(ossId); } + + /** + * 文件下载方法,支持一次性下载完整文件 + * + * @param ossId OSS对象ID + * @param response HttpServletResponse对象,用于设置响应头和向客户端发送文件内容 + */ @Override public void download(Long ossId, HttpServletResponse response) throws IOException { SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId); @@ -122,7 +157,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName()); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); OssClient storage = OssFactory.instance(sysOss.getService()); - try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) { + try (InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) { int available = inputStream.available(); IoUtil.copy(inputStream, response.getOutputStream(), available); response.setContentLength(available); @@ -131,6 +166,13 @@ public class SysOssServiceImpl implements ISysOssService, OssService { } } + /** + * 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的 MultipartFile 对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + * @throws ServiceException 如果上传过程中发生异常,则抛出 ServiceException 异常 + */ @Override public SysOssVo upload(MultipartFile file) { String originalfileName = file.getOriginalFilename(); @@ -138,7 +180,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { OssClient storage = OssFactory.instance(); UploadResult uploadResult; try { - uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType()); + uploadResult = storage.uploadSuffix(file.getBytes(), suffix); } catch (IOException e) { throw new ServiceException(e.getMessage()); } @@ -146,6 +188,12 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult); } + /** + * 上传文件到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的文件对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + */ @Override public SysOssVo upload(File file) { String originalfileName = file.getName(); @@ -169,6 +217,13 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return this.matchingUrl(sysOssVo); } + /** + * 删除OSS对象存储 + * + * @param ids OSS对象ID串 + * @param isValid 判断是否需要校验 + * @return 结果 + */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { @@ -183,7 +238,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { } /** - * 匹配Url + * 桶类型为 private 的URL 修改为临时URL时长为120s * * @param oss OSS对象 * @return oss 匹配Url的OSS对象 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java index b69a06c5f..33b98af59 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java @@ -1,12 +1,16 @@ package org.dromara.system.service.impl; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; @@ -17,7 +21,6 @@ import org.dromara.system.domain.vo.SysPostVo; import org.dromara.system.mapper.SysPostMapper; import org.dromara.system.mapper.SysUserPostMapper; import org.dromara.system.service.ISysPostService; -import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.Arrays; @@ -92,7 +95,22 @@ public class SysPostServiceImpl implements ISysPostService { */ @Override public List selectPostListByUserId(Long userId) { - return baseMapper.selectPostListByUserId(userId); + List list = baseMapper.selectPostsByUserId(userId); + return StreamUtils.toList(list, SysPostVo::getPostId); + } + + /** + * 通过岗位ID串查询岗位 + * + * @param postIds 岗位id串 + * @return 岗位列表信息 + */ + @Override + public List selectPostByIds(List postIds) { + return baseMapper.selectVoList(new LambdaQueryWrapper() + .select(SysPost::getPostId, SysPost::getPostName, SysPost::getPostCode) + .eq(SysPost::getStatus, UserConstants.POST_NORMAL) + .in(CollUtil.isNotEmpty(postIds), SysPost::getPostId, postIds)); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java index 8a2b9390f..abd294f3e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java @@ -80,7 +80,6 @@ public class SysRoleServiceImpl implements ISysRoleService { .between(params.get("beginTime") != null && params.get("endTime") != null, "r.create_time", params.get("beginTime"), params.get("endTime")) .orderByAsc("r.role_sort").orderByAsc("r.create_time"); - ; return wrapper; } @@ -92,6 +91,17 @@ public class SysRoleServiceImpl implements ISysRoleService { */ @Override public List selectRolesByUserId(Long userId) { + return baseMapper.selectRolesByUserId(userId); + } + + /** + * 根据用户ID查询角色列表(包含被授权状态) + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List selectRolesAuthByUserId(Long userId) { List userRoles = baseMapper.selectRolePermissionByUserId(userId); List roles = selectRoleAll(); for (SysRoleVo role : roles) { @@ -141,7 +151,8 @@ public class SysRoleServiceImpl implements ISysRoleService { */ @Override public List selectRoleListByUserId(Long userId) { - return baseMapper.selectRoleListByUserId(userId); + List list = baseMapper.selectRolesByUserId(userId); + return StreamUtils.toList(list, SysRoleVo::getRoleId); } /** @@ -155,6 +166,19 @@ public class SysRoleServiceImpl implements ISysRoleService { return baseMapper.selectRoleById(roleId); } + /** + * 通过角色ID串查询角色 + * + * @param roleIds 角色ID串 + * @return 角色列表信息 + */ + @Override + public List selectRoleByIds(List roleIds) { + return baseMapper.selectRoleList(new QueryWrapper() + .eq("r.status", UserConstants.ROLE_NORMAL) + .in(CollUtil.isNotEmpty(roleIds), "r.role_id", roleIds)); + } + /** * 校验角色名称是否唯一 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java index 7ee19e851..a4d553b49 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java @@ -29,6 +29,7 @@ import org.dromara.system.domain.SysUserRole; import org.dromara.system.domain.bo.SysUserBo; import org.dromara.system.domain.vo.SysPostVo; 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.mapper.*; import org.dromara.system.service.ISysUserService; @@ -69,28 +70,28 @@ public class SysUserServiceImpl implements ISysUserService, UserService { * @return 用户信息集合信息 */ @Override - public List selectUserList(SysUserBo user) { - return baseMapper.selectUserList(this.buildQueryWrapper(user)); + public List selectUserExportList(SysUserBo user) { + return baseMapper.selectUserExportList(this.buildQueryWrapper(user)); } private Wrapper buildQueryWrapper(SysUserBo user) { Map params = user.getParams(); QueryWrapper wrapper = Wrappers.query(); wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) - .eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId()) - .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) - .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) - .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) - .between(params.get("beginTime") != null && params.get("endTime") != null, - "u.create_time", params.get("beginTime"), params.get("endTime")) - .and(ObjectUtil.isNotNull(user.getDeptId()), w -> { - List deptList = deptMapper.selectList(new LambdaQueryWrapper() - .select(SysDept::getDeptId) - .apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors"))); - List ids = StreamUtils.toList(deptList, SysDept::getDeptId); - ids.add(user.getDeptId()); - w.in("u.dept_id", ids); - }).orderByAsc("u.user_id"); + .eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId()) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + "u.create_time", params.get("beginTime"), params.get("endTime")) + .and(ObjectUtil.isNotNull(user.getDeptId()), w -> { + List deptList = deptMapper.selectList(new LambdaQueryWrapper() + .select(SysDept::getDeptId) + .apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors"))); + List ids = StreamUtils.toList(deptList, SysDept::getDeptId); + ids.add(user.getDeptId()); + w.in("u.dept_id", ids); + }).orderByAsc("u.user_id"); return wrapper; } @@ -104,11 +105,11 @@ public class SysUserServiceImpl implements ISysUserService, UserService { public TableDataInfo selectAllocatedList(SysUserBo user, PageQuery pageQuery) { QueryWrapper wrapper = Wrappers.query(); wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) - .eq(ObjectUtil.isNotNull(user.getRoleId()), "r.role_id", user.getRoleId()) - .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) - .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) - .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) - .orderByAsc("u.user_id"); + .eq(ObjectUtil.isNotNull(user.getRoleId()), "r.role_id", user.getRoleId()) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .orderByAsc("u.user_id"); Page page = baseMapper.selectAllocatedList(pageQuery.build(), wrapper); return TableDataInfo.build(page); } @@ -124,11 +125,11 @@ public class SysUserServiceImpl implements ISysUserService, UserService { List userIds = userRoleMapper.selectUserIdsByRoleId(user.getRoleId()); QueryWrapper wrapper = Wrappers.query(); wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) - .and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id")) - .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds) - .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) - .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) - .orderByAsc("u.user_id"); + .and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id")) + .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .orderByAsc("u.user_id"); Page page = baseMapper.selectUnallocatedList(pageQuery.build(), wrapper); return TableDataInfo.build(page); } @@ -141,7 +142,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserByUserName(String userName) { - return baseMapper.selectUserByUserName(userName); + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, userName)); } /** @@ -152,7 +153,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserByPhonenumber(String phonenumber) { - return baseMapper.selectUserByPhonenumber(phonenumber); + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getPhonenumber, phonenumber)); } /** @@ -163,18 +164,39 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserById(Long userId) { - return baseMapper.selectUserById(userId); + SysUserVo user = baseMapper.selectVoById(userId); + if (ObjectUtil.isNull(user)) { + return user; + } + user.setRoles(roleMapper.selectRolesByUserId(user.getUserId())); + return user; + } + + /** + * 通过用户ID串查询用户 + * + * @param userIds 用户ID串 + * @param deptId 部门id + * @return 用户列表信息 + */ + @Override + public List selectUserByIds(List userIds, Long deptId) { + return baseMapper.selectUserList(new LambdaQueryWrapper() + .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName) + .eq(SysUser::getStatus, UserConstants.USER_NORMAL) + .eq(ObjectUtil.isNotNull(deptId), SysUser::getDeptId, deptId) + .in(CollUtil.isNotEmpty(userIds), SysUser::getUserId, userIds)); } /** * 查询用户所属角色组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ @Override - public String selectUserRoleGroup(String userName) { - List list = roleMapper.selectRolesByUserName(userName); + public String selectUserRoleGroup(Long userId) { + List list = roleMapper.selectRolesByUserId(userId); if (CollUtil.isEmpty(list)) { return StringUtils.EMPTY; } @@ -184,12 +206,12 @@ public class SysUserServiceImpl implements ISysUserService, UserService { /** * 查询用户所属岗位组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ @Override - public String selectUserPostGroup(String userName) { - List list = postMapper.selectPostsByUserName(userName); + public String selectUserPostGroup(Long userId) { + List list = postMapper.selectPostsByUserId(userId); if (CollUtil.isEmpty(list)) { return StringUtils.EMPTY; } @@ -205,8 +227,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public boolean checkUserNameUnique(SysUserBo user) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() - .eq(SysUser::getUserName, user.getUserName()) - .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + .eq(SysUser::getUserName, user.getUserName()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); return !exist; } @@ -218,8 +240,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public boolean checkPhoneUnique(SysUserBo user) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() - .eq(SysUser::getPhonenumber, user.getPhonenumber()) - .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + .eq(SysUser::getPhonenumber, user.getPhonenumber()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); return !exist; } @@ -231,8 +253,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public boolean checkEmailUnique(SysUserBo user) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() - .eq(SysUser::getEmail, user.getEmail()) - .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + .eq(SysUser::getEmail, user.getEmail()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); return !exist; } @@ -261,7 +283,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { if (LoginHelper.isSuperAdmin()) { return; } - if (ObjectUtil.isNull(baseMapper.selectUserById(userId))) { + if (baseMapper.countUserById(userId) == 0) { throw new ServiceException("没有权限访问用户数据!"); } } @@ -345,9 +367,9 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public int updateUserStatus(Long userId, String status) { return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysUser::getStatus, status) - .eq(SysUser::getUserId, userId)); + new LambdaUpdateWrapper() + .set(SysUser::getStatus, status) + .eq(SysUser::getUserId, userId)); } /** @@ -359,12 +381,12 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public int updateUserProfile(SysUserBo user) { return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(ObjectUtil.isNotNull(user.getNickName()), SysUser::getNickName, user.getNickName()) - .set(SysUser::getPhonenumber, user.getPhonenumber()) - .set(SysUser::getEmail, user.getEmail()) - .set(SysUser::getSex, user.getSex()) - .eq(SysUser::getUserId, user.getUserId())); + new LambdaUpdateWrapper() + .set(ObjectUtil.isNotNull(user.getNickName()), SysUser::getNickName, user.getNickName()) + .set(SysUser::getPhonenumber, user.getPhonenumber()) + .set(SysUser::getEmail, user.getEmail()) + .set(SysUser::getSex, user.getSex()) + .eq(SysUser::getUserId, user.getUserId())); } /** @@ -377,9 +399,9 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public boolean updateUserAvatar(Long userId, Long avatar) { return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysUser::getAvatar, avatar) - .eq(SysUser::getUserId, userId)) > 0; + new LambdaUpdateWrapper() + .set(SysUser::getAvatar, avatar) + .eq(SysUser::getUserId, userId)) > 0; } /** @@ -392,9 +414,9 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public int resetUserPwd(Long userId, String password) { return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysUser::getPassword, password) - .eq(SysUser::getUserId, userId)); + new LambdaUpdateWrapper() + .set(SysUser::getPassword, password) + .eq(SysUser::getUserId, userId)); } /** @@ -518,8 +540,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService { /** * 通过部门id查询当前部门所有用户 * - * @param deptId - * @return + * @param deptId 部门ID + * @return 用户信息集合信息 */ @Override public List selectUserListByDept(Long deptId) { @@ -529,14 +551,26 @@ public class SysUserServiceImpl implements ISysUserService, UserService { return baseMapper.selectVoList(lqw); } + /** + * 通过用户ID查询用户账户 + * + * @param userId 用户ID + * @return 用户账户 + */ @Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = "#userId") @Override public String selectUserNameById(Long userId) { SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getUserName).eq(SysUser::getUserId, userId)); + .select(SysUser::getUserName).eq(SysUser::getUserId, userId)); return ObjectUtil.isNull(sysUser) ? null : sysUser.getUserName(); } + /** + * 通过用户ID查询用户账户 + * + * @param userId 用户ID + * @return 用户账户 + */ @Override @Cacheable(cacheNames = CacheNames.SYS_NICKNAME, key = "#userId") public String selectNicknameById(Long userId) { @@ -544,4 +578,31 @@ public class SysUserServiceImpl implements ISysUserService, UserService { .select(SysUser::getNickName).eq(SysUser::getUserId, userId)); return ObjectUtil.isNull(sysUser) ? null : sysUser.getNickName(); } + + /** + * 通过用户ID查询用户手机号 + * + * @param userId 用户id + * @return 用户手机号 + */ + @Override + public String selectPhonenumberById(Long userId) { + SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getPhonenumber).eq(SysUser::getUserId, userId)); + return ObjectUtil.isNull(sysUser) ? null : sysUser.getPhonenumber(); + } + + /** + * 通过用户ID查询用户邮箱 + * + * @param userId 用户id + * @return 用户邮箱 + */ + @Override + public String selectEmailById(Long userId) { + SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getEmail).eq(SysUser::getUserId, userId)); + return ObjectUtil.isNull(sysUser) ? null : sysUser.getEmail(); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml index bba949d7b..6ad866f29 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -8,11 +8,18 @@ - select * from sys_dept ${ew.getCustomSqlSegment} + select + + ${ew.getSqlSelect} + + + * + + from sys_dept ${ew.getCustomSqlSegment} - - select * from sys_dept where del_flag = '0' and dept_id = #{deptId} + + select count(*) from sys_dept where del_flag = '0' and dept_id = #{deptId} diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml index d2108ebbc..199dffe4d 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -7,20 +7,12 @@ - - select p.post_id + + select p.post_id, p.post_name, p.post_code from sys_post p left join sys_user_post up on up.post_id = p.post_id left join sys_user u on u.user_id = up.user_id where u.user_id = #{userId} - - select p.post_id, p.post_name, p.post_code - from sys_post p - left join sys_user_post up on up.post_id = p.post_id - left join sys_user u on u.user_id = up.user_id - where u.user_name = #{userName} - - diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml index b1916985e..7b8bba761 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -40,23 +40,17 @@ WHERE r.del_flag = '0' and sur.user_id = #{userId} - - select r.role_id - from sys_role r - left join sys_user_role sur on sur.role_id = r.role_id - left join sys_user u on u.user_id = sur.user_id - where u.user_id = #{userId} - - - + select r.role_id, r.role_name, r.role_key, - r.role_sort + r.role_sort, + r.data_scope, + r.status from sys_role r left join sys_user_role sur on sur.role_id = r.role_id left join sys_user u on u.user_id = sur.user_id - WHERE r.del_flag = '0' and u.user_name = #{userName} + WHERE r.del_flag = '0' and sur.user_id = #{userId} diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml index 7af7dd0cc..ded6fa858 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -4,78 +4,40 @@ "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - - - - - - - - - - + + - - - - - - - - select u.user_id, - u.tenant_id, - u.dept_id, - u.user_name, - u.nick_name, - u.user_type, - u.email, - u.avatar, - u.phonenumber, - u.password, - u.sex, - u.status, - u.del_flag, - u.login_ip, - u.login_date, - u.create_by, - u.create_time, - u.remark, - d.dept_id, - d.parent_id, - d.ancestors, - d.dept_name, - d.order_num, - d.leader, - d.status as dept_status, - d.email as dept_email, - d.create_time as dept_cteate_time, - r.role_id, - r.role_name, - r.role_key, - r.role_sort, - r.data_scope, - r.status as role_status - from sys_user u - left join sys_dept d on u.dept_id = d.dept_id - left join sys_user_role sur on u.user_id = sur.user_id - left join sys_role r on r.role_id = sur.role_id - - - 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, - d.dept_name, d.leader, u1.user_name as leaderName + select + + ${ew.getSqlSelect} + + + 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 + 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} + select + + ${ew.getSqlSelect} + + + 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 + + from sys_user u + ${ew.getCustomSqlSegment} + + + 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, d.dept_name, d.leader, u1.user_name as leaderName @@ -103,24 +65,8 @@ ${ew.getCustomSqlSegment} - - - where u.del_flag = '0' and u.user_name = #{userName} - - - - - where u.del_flag = '0' and u.phonenumber = #{phonenumber} - - - - - where u.del_flag = '0' and u.email = #{email} - - - - - where u.del_flag = '0' and u.user_id = #{userId} + + select count(*) from sys_user where del_flag = '0' and user_id = #{userId} diff --git a/ruoyi-modules/ruoyi-workflow/pom.xml b/ruoyi-modules/ruoyi-workflow/pom.xml new file mode 100644 index 000000000..f0391e358 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/pom.xml @@ -0,0 +1,93 @@ + + + + org.dromara + ruoyi-modules + ${revision} + ../pom.xml + + 4.0.0 + jar + ruoyi-workflow + + + 工作流模块 + + + + + + + org.flowable + flowable-spring-boot-autoconfigure + + + org.flowable + flowable-spring-security + + + + + + org.flowable + flowable-spring-configurator + + + + org.flowable + flowable-spring-boot-starter-actuator + + + + + org.flowable + flowable-image-generator + + + + + org.flowable + flowable-json-converter + 6.8.0 + + + + + org.apache.xmlgraphics + batik-all + 1.10 + + + xalan + xalan + + + + + + + org.dromara + ruoyi-system + + + + org.dromara + ruoyi-common-websocket + + + + org.dromara + ruoyi-common-mail + + + + org.dromara + ruoyi-common-sms + + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java new file mode 100644 index 000000000..5ea262d17 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java @@ -0,0 +1,27 @@ +package org.dromara.workflow.annotation; + + +import java.lang.annotation.*; + +/** + * 流程任务监听注解 + * + * @author may + * @date 2023-12-27 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface FlowListenerAnnotation { + + /** + * 流程定义key + */ + String processDefinitionKey(); + + /** + * 节点id + */ + String taskDefId() default ""; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java new file mode 100644 index 000000000..df27d3cf9 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java @@ -0,0 +1,137 @@ +package org.dromara.workflow.common.constant; + + +/** + * 工作流常量 + * + * @author may + */ +public interface FlowConstant { + + String MESSAGE_CURRENT_TASK_IS_NULL = "当前任务不存在或你不是任务办理人!"; + + String MESSAGE_SUSPENDED = "当前任务已挂起不可审批!"; + + /** + * 连线 + */ + String SEQUENCE_FLOW = "sequenceFlow"; + + /** + * 并行网关 + */ + String PARALLEL_GATEWAY = "parallelGateway"; + + /** + * 排它网关 + */ + String EXCLUSIVE_GATEWAY = "exclusiveGateway"; + + /** + * 包含网关 + */ + String INCLUSIVE_GATEWAY = "inclusiveGateway"; + + /** + * 结束节点 + */ + String END_EVENT = "endEvent"; + + + /** + * 流程委派标识 + */ + String PENDING = "PENDING"; + + /** + * 候选人标识 + */ + String CANDIDATE = "candidate"; + + /** + * 会签任务总数 + */ + String NUMBER_OF_INSTANCES = "nrOfInstances"; + + /** + * 正在执行的会签总数 + */ + String NUMBER_OF_ACTIVE_INSTANCES = "nrOfActiveInstances"; + + /** + * 已完成的会签任务总数 + */ + String NUMBER_OF_COMPLETED_INSTANCES = "nrOfCompletedInstances"; + + /** + * 循环的索引值,可以使用elementIndexVariable属性修改loopCounter的变量名 + */ + String LOOP_COUNTER = "loopCounter"; + + String ZIP = "ZIP"; + + /** + * 流程实例对象 + */ + String PROCESS_INSTANCE_VO = "processInstanceVo"; + + /** + * 流程定义配置 + */ + String WF_DEFINITION_CONFIG_VO = "wfDefinitionConfigVo"; + + /** + * 节点配置 + */ + String WF_NODE_CONFIG_VO = "wfNodeConfigVo"; + + /** + * 流程发起人 + */ + String INITIATOR = "initiator"; + + /** + * 流程实例id + */ + String PROCESS_INSTANCE_ID = "processInstanceId"; + + /** + * 业务id + */ + String BUSINESS_KEY = "businessKey"; + + /** + * 流程定义id + */ + String PROCESS_DEFINITION_ID = "processDefinitionId"; + + /** + * 开启跳过表达式变量 + */ + String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; + + /** + * 模型标识key命名规范正则表达式 + */ + String MODEL_KEY_PATTERN = "^[a-zA-Z][a-zA-Z0-9_]{0,254}$"; + + /** + * 用户任务 + */ + String USER_TASK = "userTask"; + + /** + * 会签 + */ + String MULTI_INSTANCE = "multiInstance"; + + /** + * 是 + */ + String TRUE = "0"; + + /** + * 否 + */ + String FALSE = "1"; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java new file mode 100644 index 000000000..6eb6ffe6b --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java @@ -0,0 +1,152 @@ +package org.dromara.workflow.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; + +import java.util.Arrays; + +/** + * 业务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum BusinessStatusEnum { + /** + * 已撤销 + */ + CANCEL("cancel", "已撤销"), + /** + * 草稿 + */ + DRAFT("draft", "草稿"), + /** + * 待审核 + */ + WAITING("waiting", "待审核"), + /** + * 已完成 + */ + FINISH("finish", "已完成"), + /** + * 已作废 + */ + INVALID("invalid", "已作废"), + /** + * 已退回 + */ + BACK("back", "已退回"), + /** + * 已终止 + */ + TERMINATION("termination", "已终止"); + + /** + * 状态 + */ + private final String status; + + /** + * 描述 + */ + private final String desc; + + /** + * 获取业务状态 + * + * @param status 状态 + */ + public static String findByStatus(String status) { + if (StringUtils.isBlank(status)) { + return StrUtil.EMPTY; + } + return Arrays.stream(BusinessStatusEnum.values()) + .filter(statusEnum -> statusEnum.getStatus().equals(status)) + .findFirst() + .map(BusinessStatusEnum::getDesc) + .orElse(StrUtil.EMPTY); + } + + /** + * 启动流程校验 + * + * @param status 状态 + */ + public static void checkStartStatus(String status) { + if (WAITING.getStatus().equals(status)) { + throw new ServiceException("该单据已提交过申请,正在审批中!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 撤销流程校验 + * + * @param status 状态 + */ + public static void checkCancelStatus(String status) { + if (CANCEL.getStatus().equals(status)) { + throw new ServiceException("该单据已撤销!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (BACK.getStatus().equals(status)) { + throw new ServiceException("该单据已退回!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 驳回流程校验 + * + * @param status 状态 + */ + public static void checkBackStatus(String status) { + if (BACK.getStatus().equals(status)) { + throw new ServiceException("该单据已退回!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (CANCEL.getStatus().equals(status)) { + throw new ServiceException("该单据已撤销!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 作废,终止流程校验 + * + * @param status 状态 + */ + public static void checkInvalidStatus(String status) { + if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java new file mode 100644 index 000000000..083ab7b85 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java @@ -0,0 +1,54 @@ +package org.dromara.workflow.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 任务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum FormTypeEnum { + /** + * 自定义表单 + */ + STATIC("static", "自定义表单"), + /** + * 动态表单 + */ + DYNAMIC("dynamic", "动态表单"); + + /** + * 类型 + */ + private final String type; + + /** + * 描述 + */ + private final String desc; + + /** + * 表单类型 + * + * @param formType 表单类型 + */ + public static String findByType(String formType) { + if (StringUtils.isBlank(formType)) { + return StrUtil.EMPTY; + } + + return Arrays.stream(FormTypeEnum.values()) + .filter(statusEnum -> statusEnum.getType().equals(formType)) + .findFirst() + .map(FormTypeEnum::getDesc) + .orElse(StrUtil.EMPTY); + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java new file mode 100644 index 000000000..a28295822 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java @@ -0,0 +1,51 @@ +package org.dromara.workflow.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 消息类型枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum MessageTypeEnum { + /** + * 站内信 + */ + SYSTEM_MESSAGE("1", "站内信"), + /** + * 邮箱 + */ + EMAIL_MESSAGE("2", "邮箱"), + /** + * 短信 + */ + SMS_MESSAGE("3", "短信"); + + private final String code; + + private final String desc; + + private final static Map MESSAGE_TYPE_ENUM_MAP = new ConcurrentHashMap<>(MessageTypeEnum.values().length); + + static { + for (MessageTypeEnum messageType : MessageTypeEnum.values()) { + MESSAGE_TYPE_ENUM_MAP.put(messageType.code, messageType); + } + } + + /** + * 根据消息类型 code 获取 MessageTypeEnum + * @param code 消息类型code + * @return MessageTypeEnum + */ + public static MessageTypeEnum getByCode(String code) { + return MESSAGE_TYPE_ENUM_MAP.get(code); + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java new file mode 100644 index 000000000..7b2f55c20 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java @@ -0,0 +1,94 @@ +package org.dromara.workflow.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 任务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum TaskStatusEnum { + /** + * 撤销 + */ + CANCEL("cancel", "撤销"), + /** + * 通过 + */ + PASS("pass", "通过"), + /** + * 待审核 + */ + WAITING("waiting", "待审核"), + /** + * 作废 + */ + INVALID("invalid", "作废"), + /** + * 退回 + */ + BACK("back", "退回"), + /** + * 终止 + */ + TERMINATION("termination", "终止"), + /** + * 转办 + */ + TRANSFER("transfer", "转办"), + /** + * 委托 + */ + PENDING("pending", "委托"), + /** + * 抄送 + */ + COPY("copy", "抄送"), + /** + * 加签 + */ + SIGN("sign", "加签"), + /** + * 减签 + */ + SIGN_OFF("sign_off", "减签"), + /** + * 超时 + */ + TIMEOUT("timeout", "超时"); + + /** + * 状态 + */ + private final String status; + + /** + * 描述 + */ + private final String desc; + + /** + * 任务业务状态 + * + * @param status 状态 + */ + public static String findByStatus(String status) { + if (StringUtils.isBlank(status)) { + return StrUtil.EMPTY; + } + + return Arrays.stream(TaskStatusEnum.values()) + .filter(statusEnum -> statusEnum.getStatus().equals(status)) + .findFirst() + .map(TaskStatusEnum::getDesc) + .orElse(StrUtil.EMPTY); + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java new file mode 100644 index 000000000..3332f86a5 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java @@ -0,0 +1,147 @@ +package org.dromara.workflow.controller; + +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.bo.ModelBo; +import org.dromara.workflow.domain.vo.ModelVo; +import org.dromara.workflow.service.IActModelService; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Model; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +/** + * 模型管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/model") +public class ActModelController extends BaseController { + + private final RepositoryService repositoryService; + + private final IActModelService actModelService; + + + /** + * 分页查询模型 + * + * @param modelBo 模型参数 + */ + @GetMapping("/list") + public TableDataInfo page(ModelBo modelBo, PageQuery pageQuery) { + return actModelService.page(modelBo, pageQuery); + } + + /** + * 新增模型 + * + * @param modelBo 模型请求对象 + */ + @Log(title = "模型管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/save") + public R saveNewModel(@Validated(AddGroup.class) @RequestBody ModelBo modelBo) { + return toAjax(actModelService.saveNewModel(modelBo)); + } + + /** + * 查询模型 + * + * @param id 模型id + */ + @GetMapping("/getInfo/{id}") + public R getInfo(@NotBlank(message = "模型id不能为空") @PathVariable String id) { + return R.ok(actModelService.getInfo(id)); + } + + /** + * 修改模型信息 + * + * @param modelBo 模型数据 + */ + @Log(title = "模型管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping(value = "/update") + public R update(@RequestBody ModelBo modelBo) { + return toAjax(actModelService.update(modelBo)); + } + + /** + * 编辑XMl模型 + * + * @param modelBo 模型数据 + */ + @Log(title = "模型管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping(value = "/editModelXml") + public R editModel(@Validated(EditGroup.class) @RequestBody ModelBo modelBo) { + return toAjax(actModelService.editModelXml(modelBo)); + } + + /** + * 删除流程模型 + * + * @param ids 模型id + */ + @Log(title = "模型管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @DeleteMapping("/{ids}") + @Transactional(rollbackFor = Exception.class) + public R delete(@NotEmpty(message = "主键不能为空") @PathVariable String[] ids) { + Arrays.stream(ids).parallel().forEachOrdered(repositoryService::deleteModel); + return R.ok(); + } + + /** + * 模型部署 + * + * @param id 模型id + */ + @Log(title = "模型管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/modelDeploy/{id}") + public R deploy(@NotBlank(message = "模型id不能为空") @PathVariable("id") String id) { + return toAjax(actModelService.modelDeploy(id)); + } + + /** + * 导出模型zip压缩包 + * + * @param modelIds 模型id + * @param response 相应 + */ + @GetMapping("/export/zip/{modelIds}") + public void exportZip(@NotEmpty(message = "模型id不能为空") @PathVariable List modelIds, + HttpServletResponse response) { + actModelService.exportZip(modelIds, response); + } + + /** + * 复制模型 + * + * @param modelBo 模型数据 + */ + @PostMapping("/copyModel") + public R copyModel(@RequestBody ModelBo modelBo) { + return toAjax(actModelService.copyModel(modelBo)); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java new file mode 100644 index 000000000..5198bd164 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java @@ -0,0 +1,147 @@ +package org.dromara.workflow.controller; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.bo.ProcessDefinitionBo; +import org.dromara.workflow.domain.vo.ProcessDefinitionVo; +import org.dromara.workflow.service.IActProcessDefinitionService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * 流程定义管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/processDefinition") +public class ActProcessDefinitionController extends BaseController { + + private final IActProcessDefinitionService actProcessDefinitionService; + + /** + * 分页查询 + * + * @param bo 参数 + */ + @GetMapping("/list") + public TableDataInfo page(ProcessDefinitionBo bo, PageQuery pageQuery) { + return actProcessDefinitionService.page(bo, pageQuery); + } + + /** + * 查询历史流程定义列表 + * + * @param key 流程定义key + */ + @GetMapping("/getListByKey/{key}") + public R> getListByKey(@NotEmpty(message = "流程定义key不能为空") @PathVariable String key) { + return R.ok("操作成功", actProcessDefinitionService.getListByKey(key)); + } + + /** + * 查看流程定义图片 + * + * @param processDefinitionId 流程定义id + */ + @GetMapping("/definitionImage/{processDefinitionId}") + public R definitionImage(@PathVariable String processDefinitionId) { + return R.ok("操作成功", actProcessDefinitionService.definitionImage(processDefinitionId)); + } + + /** + * 查看流程定义xml文件 + * + * @param processDefinitionId 流程定义id + */ + @GetMapping("/definitionXml/{processDefinitionId}") + public R> definitionXml(@NotBlank(message = "流程定义id不能为空") @PathVariable String processDefinitionId) { + Map map = new HashMap<>(); + String xmlStr = actProcessDefinitionService.definitionXml(processDefinitionId); + map.put("xml", Arrays.asList(xmlStr.split("\n"))); + map.put("xmlStr", xmlStr); + return R.ok(map); + } + + /** + * 删除流程定义 + * + * @param deploymentIds 部署id + * @param processDefinitionIds 流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deploymentIds}/{processDefinitionIds}") + public R deleteDeployment(@NotNull(message = "流程部署id不能为空") @PathVariable List deploymentIds, + @NotNull(message = "流程定义id不能为空") @PathVariable List processDefinitionIds) { + return toAjax(actProcessDefinitionService.deleteDeployment(deploymentIds, processDefinitionIds)); + } + + /** + * 激活或者挂起流程定义 + * + * @param processDefinitionId 流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/updateDefinitionState/{processDefinitionId}") + public R updateDefinitionState(@NotBlank(message = "流程定义id不能为空") @PathVariable String processDefinitionId) { + return toAjax(actProcessDefinitionService.updateDefinitionState(processDefinitionId)); + } + + /** + * 迁移流程定义 + * + * @param currentProcessDefinitionId 当前流程定义id + * @param fromProcessDefinitionId 需要迁移到的流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/migrationDefinition/{currentProcessDefinitionId}/{fromProcessDefinitionId}") + public R migrationDefinition(@NotBlank(message = "当前流程定义id") @PathVariable String currentProcessDefinitionId, + @NotBlank(message = "需要迁移到的流程定义id") @PathVariable String fromProcessDefinitionId) { + return toAjax(actProcessDefinitionService.migrationDefinition(currentProcessDefinitionId, fromProcessDefinitionId)); + } + + /** + * 流程定义转换为模型 + * + * @param processDefinitionId 流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/convertToModel/{processDefinitionId}") + public R convertToModel(@NotEmpty(message = "流程定义id不能为空") @PathVariable String processDefinitionId) { + return toAjax(actProcessDefinitionService.convertToModel(processDefinitionId)); + } + + /** + * 通过zip或xml部署流程定义 + * + * @param file 文件 + * @param categoryCode 分类 + */ + @Log(title = "流程定义管理", businessType = BusinessType.INSERT) + @PostMapping("/deployByFile") + public void deployByFile(@RequestParam("file") MultipartFile file, @RequestParam("categoryCode") String categoryCode) { + actProcessDefinitionService.deployByFile(file, categoryCode); + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java new file mode 100644 index 000000000..4d433df43 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java @@ -0,0 +1,158 @@ +package org.dromara.workflow.controller; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.bo.ProcessInstanceBo; +import org.dromara.workflow.domain.bo.ProcessInvalidBo; +import org.dromara.workflow.domain.bo.TaskUrgingBo; +import org.dromara.workflow.domain.vo.ProcessInstanceVo; +import org.dromara.workflow.service.IActProcessInstanceService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.Map; + +/** + * 流程实例管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/processInstance") +public class ActProcessInstanceController extends BaseController { + + private final IActProcessInstanceService actProcessInstanceService; + + /** + * 分页查询正在运行的流程实例 + * + * @param bo 参数 + */ + @GetMapping("/getPageByRunning") + public TableDataInfo getPageByRunning(ProcessInstanceBo bo, PageQuery pageQuery) { + return actProcessInstanceService.getPageByRunning(bo, pageQuery); + } + + /** + * 分页查询已结束的流程实例 + * + * @param bo 参数 + */ + @GetMapping("/getPageByFinish") + public TableDataInfo getPageByFinish(ProcessInstanceBo bo, PageQuery pageQuery) { + return actProcessInstanceService.getPageByFinish(bo, pageQuery); + } + + /** + * 通过流程实例id获取历史流程图 + * + * @param processInstanceId 流程实例id + */ + @GetMapping("/getHistoryImage/{processInstanceId}") + public R getHistoryImage(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) { + return R.ok("操作成功", actProcessInstanceService.getHistoryImage(processInstanceId)); + } + + /** + * 通过流程实例id获取历史流程图运行中,历史等节点 + * + * @param processInstanceId 流程实例id + */ + @GetMapping("/getHistoryList/{processInstanceId}") + public R> getHistoryList(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) { + return R.ok("操作成功", actProcessInstanceService.getHistoryList(processInstanceId)); + } + + /** + * 获取审批记录 + * + * @param processInstanceId 流程实例id + */ + @GetMapping("/getHistoryRecord/{processInstanceId}") + public R> getHistoryRecord(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) { + return R.ok(actProcessInstanceService.getHistoryRecord(processInstanceId)); + } + + /** + * 作废流程实例,不会删除历史记录(删除运行中的实例) + * + * @param processInvalidBo 参数 + */ + @Log(title = "流程实例管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @PostMapping("/deleteRunInstance") + public R deleteRunInstance(@Validated(AddGroup.class) @RequestBody ProcessInvalidBo processInvalidBo) { + return toAjax(actProcessInstanceService.deleteRunInstance(processInvalidBo)); + } + + /** + * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param processInstanceIds 流程实例id + */ + @Log(title = "流程实例管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @DeleteMapping("/deleteRunAndHisInstance/{processInstanceIds}") + public R deleteRunAndHisInstance(@NotNull(message = "流程实例id不能为空") @PathVariable String[] processInstanceIds) { + return toAjax(actProcessInstanceService.deleteRunAndHisInstance(Arrays.asList(processInstanceIds))); + } + + /** + * 已完成的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param processInstanceIds 流程实例id + */ + @Log(title = "流程实例管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @DeleteMapping("/deleteFinishAndHisInstance/{processInstanceIds}") + public R deleteFinishAndHisInstance(@NotNull(message = "流程实例id不能为空") @PathVariable String[] processInstanceIds) { + return toAjax(actProcessInstanceService.deleteFinishAndHisInstance(Arrays.asList(processInstanceIds))); + } + + /** + * 撤销流程申请 + * + * @param processInstanceId 流程实例id + */ + @Log(title = "流程实例管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/cancelProcessApply/{processInstanceId}") + public R cancelProcessApply(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) { + return toAjax(actProcessInstanceService.cancelProcessApply(processInstanceId)); + } + + /** + * 分页查询当前登录人单据 + * + * @param bo 参数 + */ + @GetMapping("/getPageByCurrent") + public TableDataInfo getPageByCurrent(ProcessInstanceBo bo, PageQuery pageQuery) { + return actProcessInstanceService.getPageByCurrent(bo, pageQuery); + } + + /** + * 任务催办(给当前任务办理人发送站内信,邮件,短信等) + * + * @param taskUrgingBo 任务催办 + */ + @Log(title = "流程实例管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/taskUrging") + public R taskUrging(@RequestBody TaskUrgingBo taskUrgingBo) { + return toAjax(actProcessInstanceService.taskUrging(taskUrgingBo)); + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java new file mode 100644 index 000000000..aa290127a --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java @@ -0,0 +1,274 @@ +package org.dromara.workflow.controller; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import jakarta.validation.constraints.NotBlank; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.WfTaskBackNode; +import org.dromara.workflow.domain.bo.*; +import org.dromara.workflow.domain.vo.TaskVo; +import org.dromara.workflow.domain.vo.VariableVo; +import org.dromara.workflow.service.IActTaskService; +import org.dromara.workflow.service.IWfTaskBackNodeService; +import org.dromara.workflow.utils.QueryUtils; +import org.flowable.engine.TaskService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 任务管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/task") +public class ActTaskController extends BaseController { + + private final IActTaskService actTaskService; + + private final TaskService taskService; + + private final IWfTaskBackNodeService iWfTaskBackNodeService; + + + /** + * 启动任务 + * + * @param startProcessBo 启动流程参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/startWorkFlow") + public R> startWorkFlow(@Validated(AddGroup.class) @RequestBody StartProcessBo startProcessBo) { + Map map = actTaskService.startWorkFlow(startProcessBo); + return R.ok("提交成功", map); + } + + /** + * 办理任务 + * + * @param completeTaskBo 办理任务参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/completeTask") + public R completeTask(@Validated(AddGroup.class) @RequestBody CompleteTaskBo completeTaskBo) { + return toAjax(actTaskService.completeTask(completeTaskBo)); + } + + /** + * 查询当前用户的待办任务 + * + * @param taskBo 参数 + */ + @GetMapping("/getPageByTaskWait") + public TableDataInfo
主要用于短信重试和拦截的缓存 + * + * @author Feng + */ +public class PlusSmsDao implements SmsDao { + + /** + * 存储 + * + * @param key 键 + * @param value 值 + * @param cacheTime 缓存时间(单位:秒) + */ + @Override + public void set(String key, Object value, long cacheTime) { + RedisUtils.setCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key, value, Duration.ofSeconds(cacheTime)); + } + + /** + * 存储 + * + * @param key 键 + * @param value 值 + */ + @Override + public void set(String key, Object value) { + RedisUtils.setCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key, value, true); + } + + /** + * 读取 + * + * @param key 键 + * @return 值 + */ + @Override + public Object get(String key) { + return RedisUtils.getCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + /** + * remove + *
根据key移除缓存 + * + * @param key 缓存键 + * @return 被删除的value + * @author :Wind + */ + @Override + public Object remove(String key) { + return RedisUtils.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + key); + } + + /** + * 清空 + */ + @Override + public void clean() { + RedisUtils.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + "sms:"); + } + +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java index 76be234c6..5f49d9c27 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java @@ -2,6 +2,8 @@ package org.dromara.common.social.config.properties; import lombok.Data; +import java.util.List; + /** * 社交登录配置 * @@ -65,4 +67,9 @@ public class SocialLoginConfigProperties { */ private String serverUrl; + /** + * 请求范围 + */ + private List scopes; + } diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java index 1beb7d042..1487a6a92 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java @@ -16,11 +16,6 @@ import java.util.Map; @ConfigurationProperties(prefix = "justauth") public class SocialProperties { - /** - * 是否启用 - */ - private Boolean enabled; - /** * 授权类型 */ diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java new file mode 100644 index 000000000..13649f9fd --- /dev/null +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java @@ -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")); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java new file mode 100644 index 000000000..e47d6c640 --- /dev/null +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java @@ -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; + } + + } +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java index 51b7e13d7..04f621489 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java @@ -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.SocialProperties; 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() .clientId(obj.getClientId()) .clientSecret(obj.getClientSecret()) - .redirectUri(obj.getRedirectUri()); + .redirectUri(obj.getRedirectUri()) + .scopes(obj.getScopes()); return switch (source.toLowerCase()) { case "dingtalk" -> new AuthDingTalkRequest(builder.build(), STATE_CACHE); case "baidu" -> new AuthBaiduRequest(builder.build(), STATE_CACHE); @@ -63,6 +65,7 @@ public class SocialUtils { case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE); case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE); case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE); + case "topiam" -> new AuthTopIamRequest(builder.build(), STATE_CACHE); default -> throw new AuthException("未获取到有效的Auth配置"); }; } diff --git a/ruoyi-common/ruoyi-common-tenant/pom.xml b/ruoyi-common/ruoyi-common-tenant/pom.xml index 2b87bf961..8e1a6ab1e 100644 --- a/ruoyi-common/ruoyi-common-tenant/pom.xml +++ b/ruoyi-common/ruoyi-common-tenant/pom.xml @@ -19,6 +19,7 @@ org.dromara ruoyi-common-mybatis + true @@ -26,11 +27,6 @@ ruoyi-common-redis - - com.alibaba - transmittable-thread-local - - diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java index 8610641dd..0ff39fdc0 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java @@ -2,8 +2,6 @@ package org.dromara.common.tenant.config; import cn.dev33.satoken.dao.SaTokenDao; import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; -import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.mybatis.config.MybatisPlusConfig; @@ -24,9 +22,6 @@ import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; -import java.util.ArrayList; -import java.util.List; - /** * 租户配置类 * @@ -37,23 +32,10 @@ import java.util.List; @ConditionalOnProperty(value = "tenant.enable", havingValue = "true") public class TenantConfig { - /** - * 初始化租户配置 - */ - @Bean - public boolean tenantInit(MybatisPlusInterceptor mybatisPlusInterceptor, - TenantProperties tenantProperties) { - List interceptors = new ArrayList<>(); - // 多租户插件 必须放到第一位 - interceptors.add(tenantLineInnerInterceptor(tenantProperties)); - interceptors.addAll(mybatisPlusInterceptor.getInterceptors()); - mybatisPlusInterceptor.setInterceptors(interceptors); - return true; - } - /** * 多租户插件 */ + @Bean public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties) { return new TenantLineInnerInterceptor(new PlusTenantLineHandler(tenantProperties)); } diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java index 14dad1a71..947539863 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java @@ -1,5 +1,6 @@ package org.dromara.common.tenant.handle; +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.utils.StringUtils; @@ -26,6 +27,9 @@ public class TenantKeyPrefixHandler extends KeyPrefixHandler { if (StringUtils.isBlank(name)) { return null; } + if (InterceptorIgnoreHelper.willIgnoreTenantLine("")) { + return super.map(name); + } if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) { return super.map(name); } @@ -49,6 +53,9 @@ public class TenantKeyPrefixHandler extends KeyPrefixHandler { if (StringUtils.isBlank(unmap)) { return null; } + if (InterceptorIgnoreHelper.willIgnoreTenantLine("")) { + return super.unmap(name); + } if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) { return super.unmap(name); } diff --git a/ruoyi-common/ruoyi-common-web/pom.xml b/ruoyi-common/ruoyi-common-web/pom.xml index 293c52229..b250fa9d0 100644 --- a/ruoyi-common/ruoyi-common-web/pom.xml +++ b/ruoyi-common/ruoyi-common-web/pom.xml @@ -57,11 +57,6 @@ cn.hutool hutool-crypto - - - com.alibaba - transmittable-thread-local - diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java index 9225a51bf..650517ed1 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java @@ -23,7 +23,7 @@ public class CaptchaConfig { private static final int WIDTH = 160; private static final int HEIGHT = 60; - private static final Color BACKGROUND = Color.PINK; + private static final Color BACKGROUND = Color.LIGHT_GRAY; private static final Font FONT = new Font("Arial", Font.BOLD, 48); /** diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java index 421ce6d14..0f7892861 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java @@ -2,9 +2,11 @@ package org.dromara.common.web.config; import io.undertow.server.DefaultByteBufferPool; import io.undertow.websockets.jsr.WebSocketDeploymentInfo; +import org.dromara.common.core.utils.SpringUtils; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.core.task.VirtualThreadTaskExecutor; /** * Undertow 自定义配置 @@ -24,6 +26,12 @@ public class UndertowConfig implements WebServerFactoryCustomizer invokeTimeTL = new TransmittableThreadLocal<>(); + private final static ThreadLocal KEY_CACHE = new ThreadLocal<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { @@ -56,7 +55,7 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { } StopWatch stopWatch = new StopWatch(); - invokeTimeTL.set(stopWatch); + KEY_CACHE.set(stopWatch); stopWatch.start(); } return true; @@ -70,10 +69,10 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { if (!prodProfile.equals(SpringUtils.getActiveProfile())) { - StopWatch stopWatch = invokeTimeTL.get(); + StopWatch stopWatch = KEY_CACHE.get(); stopWatch.stop(); log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime()); - invokeTimeTL.remove(); + KEY_CACHE.remove(); } } diff --git a/ruoyi-extend/pom.xml b/ruoyi-extend/pom.xml index f5348cc13..0e9533f18 100644 --- a/ruoyi-extend/pom.xml +++ b/ruoyi-extend/pom.xml @@ -14,6 +14,7 @@ ruoyi-monitor-admin ruoyi-powerjob-server + ruoyi-easyretry-server diff --git a/ruoyi-extend/ruoyi-easyretry-server/Dockerfile b/ruoyi-extend/ruoyi-easyretry-server/Dockerfile new file mode 100644 index 000000000..7ad0a0776 --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/Dockerfile @@ -0,0 +1,18 @@ +#FROM findepi/graalvm:java17-native +FROM openjdk:17.0.2-oraclelinux8 + +MAINTAINER Lion Li + +RUN mkdir -p /ruoyi/easyretry/logs + +WORKDIR /ruoyi/easyretry + +ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="-Xms512m -Xmx1024m" + +EXPOSE 8800 +EXPOSE 1788 + +ADD ./target/ruoyi-easyretry-server.jar ./app.jar + +ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -jar app.jar \ + -XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*,:time,tags,level -XX:+UseZGC ${JAVA_OPTS} diff --git a/ruoyi-extend/ruoyi-easyretry-server/pom.xml b/ruoyi-extend/ruoyi-easyretry-server/pom.xml new file mode 100644 index 000000000..4ea4d743b --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/pom.xml @@ -0,0 +1,46 @@ + + + + org.dromara + ruoyi-extend + ${revision} + + 4.0.0 + jar + ruoyi-easyretry-server + + + + com.aizuda + easy-retry-server-starter + ${easyretry.version} + + + + de.codecentric + spring-boot-admin-starter-client + ${spring-boot-admin.version} + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + + diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/java/org/dromara/easyretry/EasyRetryServerApplication.java b/ruoyi-extend/ruoyi-easyretry-server/src/main/java/org/dromara/easyretry/EasyRetryServerApplication.java new file mode 100644 index 000000000..9d01bc05b --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/java/org/dromara/easyretry/EasyRetryServerApplication.java @@ -0,0 +1,18 @@ +package org.dromara.easyretry; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * EasyRetry Server 启动程序 + * + * @author dhb52 + */ +@SpringBootApplication +public class EasyRetryServerApplication { + + public static void main(String[] args) { + SpringApplication.run(com.aizuda.easy.retry.server.EasyRetryServerApplication.class, args); + } + +} diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-dev.yml b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-dev.yml new file mode 100644 index 000000000..c151a6195 --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-dev.yml @@ -0,0 +1,50 @@ +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: root + hikari: + connection-timeout: 30000 + validation-timeout: 5000 + minimum-idle: 10 + maximum-pool-size: 20 + idle-timeout: 600000 + max-lifetime: 900000 + keepaliveTime: 30000 + +--- # easy-retry 服务端配置 +easy-retry: + # 拉取重试数据的每批次的大小 + retry-pull-page-size: 1000 + # 拉取重试数据的每批次的大小 + job-pull-page-size: 1000 + # 服务端 netty 端口 + netty-port: 1788 + # 重试和死信表的分区总数 + total-partition: 2 + # 一个客户端每秒最多接收的重试数量指令 + limiter: 1000 + # 号段模式下步长配置 + step: 100 + # 日志保存时间(单位: day) + log-storage: 90 + # 回调配置 + callback: + #回调最大执行次数 + max-count: 288 + #间隔时间 + trigger-interval: 900 + mode: all + retry-max-pull-count: 10 + +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: true + url: http://localhost:9090/admin + instance: + service-host-type: IP + username: ruoyi + password: 123456 diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-prod.yml b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-prod.yml new file mode 100644 index 000000000..c151a6195 --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application-prod.yml @@ -0,0 +1,50 @@ +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: root + hikari: + connection-timeout: 30000 + validation-timeout: 5000 + minimum-idle: 10 + maximum-pool-size: 20 + idle-timeout: 600000 + max-lifetime: 900000 + keepaliveTime: 30000 + +--- # easy-retry 服务端配置 +easy-retry: + # 拉取重试数据的每批次的大小 + retry-pull-page-size: 1000 + # 拉取重试数据的每批次的大小 + job-pull-page-size: 1000 + # 服务端 netty 端口 + netty-port: 1788 + # 重试和死信表的分区总数 + total-partition: 2 + # 一个客户端每秒最多接收的重试数量指令 + limiter: 1000 + # 号段模式下步长配置 + step: 100 + # 日志保存时间(单位: day) + log-storage: 90 + # 回调配置 + callback: + #回调最大执行次数 + max-count: 288 + #间隔时间 + trigger-interval: 900 + mode: all + retry-max-pull-count: 10 + +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: true + url: http://localhost:9090/admin + instance: + service-host-type: IP + username: ruoyi + password: 123456 diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application.yml b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application.yml new file mode 100644 index 000000000..b6d8998e8 --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/application.yml @@ -0,0 +1,40 @@ +server: + port: 8800 + servlet: + context-path: /easy-retry + +spring: + application: + name: ruoyi-easyretry-server + profiles: + active: @profiles.active@ + web: + resources: + static-locations: classpath:admin/ + +mybatis-plus: + typeAliasesPackage: com.aizuda.easy.retry.template.datasource.persistence.po + global-config: + db-config: + table-prefix: er_ + where-strategy: NOT_EMPTY + capital-mode: false + logic-delete-value: 1 + logic-not-delete-value: 0 + configuration: + map-underscore-to-camel-case: true + cache-enabled: true + +logging: + config: classpath:logback-plus.xml + +management: + endpoints: + web: + exposure: + include: '*' + endpoint: + health: + show-details: ALWAYS + logfile: + external-file: ./logs/ruoyi-easyretry-server/console.log diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/banner.txt b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/banner.txt new file mode 100644 index 000000000..869a4209c --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/banner.txt @@ -0,0 +1,10 @@ +Application Version: ${revision} +Spring Boot Version: ${spring-boot.version} + _ + | | + ___ __ _ ___ _ _ ______ _ __ ___| |_ _ __ _ _ ______ ___ ___ _ ____ _____ _ __ + / _ \/ _` / __| | | |______| '__/ _ \ __| '__| | | |______/ __|/ _ \ '__\ \ / / _ \ '__| +| __/ (_| \__ \ |_| | | | | __/ |_| | | |_| | \__ \ __/ | \ V / __/ | + \___|\__,_|___/\__, | |_| \___|\__|_| \__, | |___/\___|_| \_/ \___|_| + __/ | __/ | + |___/ |___/ diff --git a/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/logback-plus.xml b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/logback-plus.xml new file mode 100644 index 000000000..bcd3abc4c --- /dev/null +++ b/ruoyi-extend/ruoyi-easyretry-server/src/main/resources/logback-plus.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + ${console.log.pattern} + utf-8 + + + + + + ${log.path}/console.log + + + ${log.path}/console.%d{yyyy-MM-dd}.log + + 1 + + + ${log.pattern} + utf-8 + + + + INFO + + + + + ${log.path}/info.log + + ${log.path}/info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + INFO + ACCEPT + DENY + + + + + ${log.path}/error.log + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + ERROR + ACCEPT + DENY + + + + + 100 + 1024 + + + + + 100 + 1024 + + + + + + + + + + + + + + + diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java index 1f70c75f7..53d248e39 100644 --- a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java +++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java @@ -3,7 +3,7 @@ package org.dromara.monitor.admin.config; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; -import org.springframework.boot.task.TaskExecutorBuilder; +import org.springframework.boot.task.ThreadPoolTaskExecutorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; @@ -23,7 +23,7 @@ public class AdminServerConfig { @Lazy @Bean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME) @ConditionalOnMissingBean(Executor.class) - public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) { + public ThreadPoolTaskExecutor applicationTaskExecutor(ThreadPoolTaskExecutorBuilder builder) { return builder.build(); } diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java index fb1973148..b993f60b5 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java @@ -5,7 +5,6 @@ import org.dromara.common.core.domain.R; import org.dromara.sms4j.api.SmsBlend; import org.dromara.sms4j.api.entity.SmsResponse; import org.dromara.sms4j.core.factory.SmsFactory; -import org.dromara.sms4j.provider.enumerate.SupplierType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -25,7 +24,6 @@ import java.util.LinkedHashMap; @RestController @RequestMapping("/demo/sms") public class SmsController { - /** * 发送短信Aliyun * @@ -36,7 +34,7 @@ public class SmsController { public R sendAliyun(String phones, String templateId) { LinkedHashMap map = new LinkedHashMap<>(1); map.put("code", "1234"); - SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA); + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map); return R.ok(smsResponse); } @@ -52,9 +50,33 @@ public class SmsController { LinkedHashMap map = new LinkedHashMap<>(1); // map.put("2", "测试测试"); map.put("1", "1234"); - SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.TENCENT); + SmsBlend smsBlend = SmsFactory.getSmsBlend("config2"); SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map); return R.ok(smsResponse); } + /** + * 添加黑名单 + * + * @param phone 手机号 + */ + @GetMapping("/addBlacklist") + public R addBlacklist(String phone){ + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + smsBlend.joinInBlacklist(phone); + return R.ok(); + } + + /** + * 移除黑名单 + * + * @param phone 手机号 + */ + @GetMapping("/removeBlacklist") + public R removeBlacklist(String phone){ + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + smsBlend.removeFromBlacklist(phone); + return R.ok(); + } + } diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java index 5ce7ad043..ebdb9930c 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java @@ -42,7 +42,7 @@ public class GenTableColumn extends BaseEntity { /** * 列描述 */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String columnComment; /** @@ -64,43 +64,43 @@ public class GenTableColumn extends BaseEntity { /** * 是否主键(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isPk; /** * 是否自增(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isIncrement; /** * 是否必填(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isRequired; /** * 是否为插入字段(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isInsert; /** * 是否编辑字段(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isEdit; /** * 是否列表字段(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isList; /** * 是否查询字段(1是) */ - @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) private String isQuery; /** diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm index 14177b5df..64389717d 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm @@ -20,7 +20,7 @@ import org.dromara.common.excel.utils.ExcelUtil; import ${packageName}.domain.vo.${ClassName}Vo; import ${packageName}.domain.bo.${ClassName}Bo; import ${packageName}.service.I${ClassName}Service; -#if($table.crud || $table.sub) +#if($table.crud) import org.dromara.common.mybatis.core.page.TableDataInfo; #elseif($table.tree) #end @@ -44,7 +44,7 @@ public class ${ClassName}Controller extends BaseController { */ @SaCheckPermission("${permissionPrefix}:list") @GetMapping("/list") -#if($table.crud || $table.sub) +#if($table.crud) public TableDataInfo<${ClassName}Vo> list(${ClassName}Bo bo, PageQuery pageQuery) { return ${className}Service.queryPageList(bo, pageQuery); } diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm index d596a0e91..f1fa3d991 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm @@ -3,7 +3,7 @@ package ${packageName}.service; import ${packageName}.domain.${ClassName}; import ${packageName}.domain.vo.${ClassName}Vo; import ${packageName}.domain.bo.${ClassName}Bo; -#if($table.crud || $table.sub) +#if($table.crud) import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; #end @@ -24,7 +24,7 @@ public interface I${ClassName}Service { */ ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}); -#if($table.crud || $table.sub) +#if($table.crud) /** * 查询${functionName}列表 */ diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm index 75a9b83a2..d603de120 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -2,7 +2,7 @@ package ${packageName}.service.impl; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; -#if($table.crud || $table.sub) +#if($table.crud) import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -41,7 +41,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { return baseMapper.selectVoById(${pkColumn.javaField}); } -#if($table.crud || $table.sub) +#if($table.crud) /** * 查询${functionName}列表 */ diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm index 8b132f4ab..aaceca611 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -439,7 +439,7 @@ const submitForm = () => { } else { await add${BusinessName}(form.value).finally(() => buttonLoading.value = false); } - proxy?.#[[$modal]]#.msgSuccess("修改成功"); + proxy?.#[[$modal]]#.msgSuccess("操作成功"); dialog.visible = false; await getList(); } diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestAnnoJobExecutor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestAnnoJobExecutor.java new file mode 100644 index 000000000..05c4f2fb0 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestAnnoJobExecutor.java @@ -0,0 +1,23 @@ +package org.dromara.job.easyretry; + +import com.aizuda.easy.retry.client.job.core.annotation.JobExecutor; +import com.aizuda.easy.retry.client.job.core.dto.JobArgs; +import com.aizuda.easy.retry.client.model.ExecuteResult; +import com.aizuda.easy.retry.common.core.util.JsonUtil; +import com.aizuda.easy.retry.common.log.EasyRetryLog; +import org.springframework.stereotype.Component; + +/** + * @author www.byteblogs.com + * @date 2023-09-28 22:54:07 + */ +@Component +@JobExecutor(name = "testJobExecutor") +public class TestAnnoJobExecutor { + + public ExecuteResult jobExecute(JobArgs jobArgs) { + EasyRetryLog.LOCAL.info("testJobExecutor. jobArgs:{}", JsonUtil.toJsonString(jobArgs)); + EasyRetryLog.REMOTE.info("testJobExecutor. jobArgs:{}", JsonUtil.toJsonString(jobArgs)); + return ExecuteResult.success("测试成功"); + } +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestClassJobExecutor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestClassJobExecutor.java new file mode 100644 index 000000000..d675cec73 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/easyretry/TestClassJobExecutor.java @@ -0,0 +1,19 @@ +package org.dromara.job.easyretry; + +import com.aizuda.easy.retry.client.job.core.dto.JobArgs; +import com.aizuda.easy.retry.client.job.core.executor.AbstractJobExecutor; +import com.aizuda.easy.retry.client.model.ExecuteResult; +import org.springframework.stereotype.Component; + +/** + * @author www.byteblogs.com + * @date 2023-09-28 22:54:07 + */ +@Component +public class TestClassJobExecutor extends AbstractJobExecutor { + + @Override + protected ExecuteResult doJobExecute(JobArgs jobArgs) { + return ExecuteResult.success("TestJobExecutor测试成功"); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java index def448d4d..9b08c2d2c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java @@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** * 在线用户监控 @@ -87,4 +88,43 @@ public class SysUserOnlineController extends BaseController { } return R.ok(); } + + /** + * 获取当前用户登录在线设备 + */ + @GetMapping() + public TableDataInfo getInfo() { + // 获取指定账号 id 的 token 集合 + List tokenIds = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString()); + List userOnlineDTOList = tokenIds.stream() + .filter(token -> StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) >= -1) + .map(token -> (UserOnlineDTO) RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token)) + .collect(Collectors.toList()); + //复制和处理 SysUserOnline 对象列表 + Collections.reverse(userOnlineDTOList); + userOnlineDTOList.removeAll(Collections.singleton(null)); + List userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class); + return TableDataInfo.build(userOnlineList); + } + + /** + * 强退当前在线设备 + * + * @param tokenId token值 + */ + @Log(title = "在线设备", businessType = BusinessType.FORCE) + @PostMapping("/{tokenId}") + public R remove(@PathVariable("tokenId") String tokenId) { + try { + // 获取指定账号 id 的 token 集合 + List keys = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString()); + keys.stream() + .filter(key -> key.equals(tokenId)) + .findFirst() + .ifPresent(key -> StpUtil.kickoutByTokenValue(tokenId)); + } catch (NotLoginException ignored) { + } + return R.ok(); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java index 61d30c76a..13be4a4a9 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java @@ -97,7 +97,7 @@ public class SysClientController extends BaseController { @Log(title = "客户端管理", businessType = BusinessType.UPDATE) @PutMapping("/changeStatus") public R changeStatus(@RequestBody SysClientBo bo) { - return toAjax(sysClientService.updateUserStatus(bo.getId(), bo.getStatus())); + return toAjax(sysClientService.updateUserStatus(bo.getClientId(), bo.getStatus())); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java index 130c0d1f3..4f5f23f32 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java @@ -2,6 +2,7 @@ package org.dromara.system.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.convert.Convert; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.domain.R; import org.dromara.common.core.utils.StringUtils; @@ -11,7 +12,6 @@ import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.bo.SysDeptBo; import org.dromara.system.domain.vo.SysDeptVo; import org.dromara.system.service.ISysDeptService; -import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -120,4 +120,16 @@ public class SysDeptController extends BaseController { deptService.checkDeptDataScope(deptId); return toAjax(deptService.deleteDeptById(deptId)); } + + /** + * 获取部门选择框列表 + * + * @param deptIds 部门ID串 + */ + @SaCheckPermission("system:dept:query") + @GetMapping("/optionselect") + public R> optionselect(@RequestParam(required = false) Long[] deptIds) { + return R.ok(deptService.selectDeptByIds(deptIds == null ? null : List.of(deptIds))); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java index fe62fdbbc..330b3fb52 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java @@ -1,6 +1,8 @@ package org.dromara.system.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.domain.R; import org.dromara.common.excel.utils.ExcelUtil; @@ -12,8 +14,6 @@ import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.bo.SysPostBo; import org.dromara.system.domain.vo.SysPostVo; import org.dromara.system.service.ISysPostService; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -110,12 +110,13 @@ public class SysPostController extends BaseController { /** * 获取岗位选择框列表 + * + * @param postIds 岗位ID串 */ + @SaCheckPermission("system:post:query") @GetMapping("/optionselect") - public R> optionselect() { - SysPostBo postBo = new SysPostBo(); - postBo.setStatus(UserConstants.POST_NORMAL); - List posts = postService.selectPostList(postBo); - return R.ok(posts); + public R> optionselect(@RequestParam(required = false) Long[] postIds) { + return R.ok(postService.selectPostByIds(postIds == null ? null : List.of(postIds))); } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java index f9c4b3d31..0cdb67548 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java @@ -19,6 +19,7 @@ import org.dromara.system.domain.vo.ProfileVo; import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.service.ISysOssService; +import org.dromara.system.service.ISysRoleService; import org.dromara.system.service.ISysUserService; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; @@ -50,8 +51,8 @@ public class SysProfileController extends BaseController { SysUserVo user = userService.selectUserById(LoginHelper.getUserId()); ProfileVo profileVo = new ProfileVo(); profileVo.setUser(user); - profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserName())); - profileVo.setPostGroup(userService.selectUserPostGroup(user.getUserName())); + profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserId())); + profileVo.setPostGroup(userService.selectUserPostGroup(user.getUserId())); return R.ok(profileVo); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java index 3a9418b23..d4a9dc8a4 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java @@ -149,11 +149,13 @@ public class SysRoleController extends BaseController { /** * 获取角色选择框列表 + * + * @param roleIds 角色ID串 */ @SaCheckPermission("system:role:query") @GetMapping("/optionselect") - public R> optionselect() { - return R.ok(roleService.selectRoleAll()); + public R> optionselect(@RequestParam(required = false) Long[] roleIds) { + return R.ok(roleService.selectRoleByIds(roleIds == null ? null : List.of(roleIds))); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java index beefe4a81..b1502ac92 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java @@ -11,7 +11,6 @@ import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.model.LoginUser; -import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.encrypt.annotation.ApiEncrypt; @@ -72,9 +71,8 @@ public class SysUserController extends BaseController { @SaCheckPermission("system:user:export") @PostMapping("/export") public void export(SysUserBo user, HttpServletResponse response) { - List list = userService.selectUserList(user); - List listVo = MapstructUtils.convert(list, SysUserExportVo.class); - ExcelUtil.exportExcel(listVo, "用户数据", SysUserExportVo.class, response); + List list = userService.selectUserExportList(user); + ExcelUtil.exportExcel(list, "用户数据", SysUserExportVo.class, response); } /** @@ -142,7 +140,7 @@ public class SysUserController extends BaseController { if (ObjectUtil.isNotNull(userId)) { SysUserVo sysUser = userService.selectUserById(userId); userInfoVo.setUser(sysUser); - userInfoVo.setRoleIds(StreamUtils.toList(sysUser.getRoles(), SysRoleVo::getRoleId)); + userInfoVo.setRoleIds(roleService.selectRoleListByUserId(userId)); userInfoVo.setPostIds(postService.selectPostListByUserId(userId)); } return R.ok(userInfoVo); @@ -207,6 +205,19 @@ public class SysUserController extends BaseController { return toAjax(userService.deleteUserByIds(userIds)); } + /** + * 根据用户ID串批量获取用户基础信息 + * + * @param userIds 用户ID串 + * @param deptId 部门ID + */ + @SaCheckPermission("system:user:query") + @GetMapping("/optionselect") + public R> optionselect(@RequestParam(required = false) Long[] userIds, + @RequestParam(required = false) Long deptId) { + return R.ok(userService.selectUserByIds(userIds == null ? null : List.of(userIds), deptId)); + } + /** * 重置密码 */ @@ -241,8 +252,9 @@ public class SysUserController extends BaseController { @SaCheckPermission("system:user:query") @GetMapping("/authRole/{userId}") public R authRole(@PathVariable Long userId) { + userService.checkUserDataScope(userId); SysUserVo user = userService.selectUserById(userId); - List roles = roleService.selectRolesByUserId(userId); + List roles = roleService.selectRolesAuthByUserId(userId); SysUserInfoVo userInfoVo = new SysUserInfoVo(); userInfoVo.setUser(user); userInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isSuperAdmin())); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java index 6def906e1..ba30eb609 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java @@ -7,7 +7,6 @@ import lombok.Data; * * @author Lion Li */ - @Data public class SysUserOnline { diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java index 39257f70b..fcc1ac11d 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java @@ -6,6 +6,7 @@ import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import lombok.Data; import lombok.EqualsAndHashCode; +import org.dromara.common.core.constant.RegexConstants; import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.system.domain.SysDictType; @@ -37,7 +38,7 @@ public class SysDictTypeBo extends BaseEntity { */ @NotBlank(message = "字典类型不能为空") @Size(min = 0, max = 100, message = "字典类型类型长度不能超过{max}个字符") - @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + @Pattern(regexp = RegexConstants.DICTIONARY_TYPE, message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") private String dictType; /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java index 498280a90..fbaafaa63 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java @@ -4,9 +4,11 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.github.linpeilie.annotations.AutoMapper; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import lombok.Data; import lombok.EqualsAndHashCode; +import org.dromara.common.core.constant.RegexConstants; import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.system.domain.SysMenu; @@ -92,6 +94,7 @@ public class SysMenuBo extends BaseEntity { */ @JsonInclude(JsonInclude.Include.NON_NULL) @Size(min = 0, max = 100, message = "权限标识长度不能超过{max}个字符") + @Pattern(regexp = RegexConstants.PERMISSION_STRING, message = "权限标识必须符合 tool:build:list 格式") private String perms; /** @@ -104,5 +107,4 @@ public class SysMenuBo extends BaseEntity { */ private String remark; - } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java index 3cc5186c8..37ec6b709 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java @@ -20,7 +20,6 @@ import java.util.Date; @Data @NoArgsConstructor -@AutoMapper(target = SysUserVo.class, convertGenerate = false) public class SysUserExportVo implements Serializable { @Serial @@ -85,14 +84,12 @@ public class SysUserExportVo implements Serializable { /** * 部门名称 */ - @ReverseAutoMapping(target = "deptName", source = "dept.deptName") @ExcelProperty(value = "部门名称") private String deptName; /** * 负责人 */ - @ReverseAutoMapping(target = "leaderName", source = "dept.leaderName") @ExcelProperty(value = "部门负责人") private String leaderName; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java index 7db91d180..d1f405919 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java @@ -114,9 +114,10 @@ public class SysUserVo implements Serializable { private Date createTime; /** - * 部门对象 + * 部门名 */ - private SysDeptVo dept; + @Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "deptId") + private String deptName; /** * 角色对象 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java index 45ad77e9c..08dda662c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java @@ -32,7 +32,7 @@ public interface SysDeptMapper extends BaseMapperPlus { @DataPermission({ @DataColumn(key = "deptName", value = "dept_id") }) - SysDeptVo selectDeptById(Long deptId); + long countDeptById(Long deptId); /** * 根据角色ID查询部门树信息 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java index 48e6a121f..d6d27596f 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java @@ -13,20 +13,12 @@ import java.util.List; */ public interface SysPostMapper extends BaseMapperPlus { - /** - * 根据用户ID获取岗位选择框列表 - * - * @param userId 用户ID - * @return 选中岗位ID列表 - */ - List selectPostListByUserId(Long userId); - /** * 查询用户所属岗位组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ - List selectPostsByUserName(String userName); + List selectPostsByUserId(Long userId); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java index 55ca76976..ac5a47e54 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java @@ -3,12 +3,12 @@ package org.dromara.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.system.domain.SysRole; import org.dromara.system.domain.vo.SysRoleVo; -import org.apache.ibatis.annotations.Param; import java.util.List; @@ -51,21 +51,12 @@ public interface SysRoleMapper extends BaseMapperPlus { */ List selectRolePermissionByUserId(Long userId); - - /** - * 根据用户ID获取角色选择框列表 - * - * @param userId 用户ID - * @return 选中角色ID列表 - */ - List selectRoleListByUserId(Long userId); - /** * 根据用户ID查询角色 * - * @param userName 用户名 + * @param userId 用户ID * @return 角色列表 */ - List selectRolesByUserName(String userName); + List selectRolesByUserId(Long userId); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java index 43222253c..fc7fc6e16 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java @@ -8,6 +8,7 @@ import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.vo.SysUserExportVo; import org.dromara.system.domain.vo.SysUserVo; import java.util.List; @@ -20,11 +21,17 @@ import java.util.List; public interface SysUserMapper extends BaseMapperPlus { @DataPermission({ - @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "deptName", value = "u.dept_id"), @DataColumn(key = "userName", value = "u.user_id") }) Page selectPageUserList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + List selectUserList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + /** * 根据条件分页查询用户列表 * @@ -35,7 +42,7 @@ public interface SysUserMapper extends BaseMapperPlus { @DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "userName", value = "u.user_id") }) - List selectUserList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + List selectUserExportList(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 根据条件分页查询已配用户角色列表 @@ -61,41 +68,11 @@ public interface SysUserMapper extends BaseMapperPlus { }) Page selectUnallocatedList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); - /** - * 通过用户名查询用户 - * - * @param userName 用户名 - * @return 用户对象信息 - */ - SysUserVo selectUserByUserName(String userName); - - /** - * 通过手机号查询用户 - * - * @param phonenumber 手机号 - * @return 用户对象信息 - */ - SysUserVo selectUserByPhonenumber(String phonenumber); - - /** - * 通过邮箱查询用户 - * - * @param email 邮箱 - * @return 用户对象信息 - */ - SysUserVo selectUserByEmail(String email); - - /** - * 通过用户ID查询用户 - * - * @param userId 用户ID - * @return 用户对象信息 - */ @DataPermission({ - @DataColumn(key = "deptName", value = "d.dept_id"), - @DataColumn(key = "userName", value = "u.user_id") + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") }) - SysUserVo selectUserById(Long userId); + long countUserById(Long userId); @Override @DataPermission({ diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java index db48d4b79..d0f8a3cbe 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java @@ -25,7 +25,7 @@ public interface ISysClientService { /** * 查询客户端信息基于客户端id */ - SysClient queryByClientId(String clientId); + SysClientVo queryByClientId(String clientId); /** * 查询客户端管理列表 @@ -50,7 +50,7 @@ public interface ISysClientService { /** * 修改状态 */ - int updateUserStatus(Long id, String status); + int updateUserStatus(String clientId, String status); /** * 校验并批量删除客户端管理信息 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java index b20938187..bf16642fe 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java @@ -52,6 +52,14 @@ public interface ISysDeptService { */ SysDeptVo selectDeptById(Long deptId); + /** + * 通过部门ID串查询部门 + * + * @param deptIds 部门id串 + * @return 部门列表信息 + */ + List selectDeptByIds(List deptIds); + /** * 根据ID查询所有子部门数(正常状态) * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java index a8bc57b9c..2f6dfc9a1 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java @@ -31,12 +31,11 @@ public interface ISysOssConfigService { */ TableDataInfo queryPageList(SysOssConfigBo bo, PageQuery pageQuery); - /** * 根据新增业务对象插入对象存储配置 * * @param bo 对象存储配置新增业务对象 - * @return + * @return 结果 */ Boolean insertByBo(SysOssConfigBo bo); @@ -44,7 +43,7 @@ public interface ISysOssConfigService { * 根据编辑业务对象修改对象存储配置 * * @param bo 对象存储配置编辑业务对象 - * @return + * @return 结果 */ Boolean updateByBo(SysOssConfigBo bo); @@ -53,7 +52,7 @@ public interface ISysOssConfigService { * * @param ids 主键集合 * @param isValid 是否校验,true-删除前校验,false-不校验 - * @return + * @return 结果 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java index 2dfe01fe9..057c068ce 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java @@ -19,18 +19,62 @@ import java.util.List; */ public interface ISysOssService { + /** + * 查询OSS对象存储列表 + * + * @param sysOss OSS对象存储分页查询对象 + * @param pageQuery 分页查询实体类 + * @return 结果 + */ TableDataInfo queryPageList(SysOssBo sysOss, PageQuery pageQuery); + /** + * 根据一组 ossIds 获取对应的 SysOssVo 列表 + * + * @param ossIds 一组文件在数据库中的唯一标识集合 + * @return 包含 SysOssVo 对象的列表 + */ List listByIds(Collection ossIds); + /** + * 根据 ossId 从缓存或数据库中获取 SysOssVo 对象 + * + * @param ossId 文件在数据库中的唯一标识 + * @return SysOssVo 对象,包含文件信息 + */ SysOssVo getById(Long ossId); + /** + * 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的 MultipartFile 对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + */ SysOssVo upload(MultipartFile file); + /** + * 上传文件到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的文件对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + */ SysOssVo upload(File file); + /** + * 文件下载方法,支持一次性下载完整文件 + * + * @param ossId OSS对象ID + * @param response HttpServletResponse对象,用于设置响应头和向客户端发送文件内容 + */ void download(Long ossId, HttpServletResponse response) throws IOException; + /** + * 删除OSS对象存储 + * + * @param ids OSS对象ID串 + * @param isValid 判断是否需要校验 + * @return 结果 + */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java index c6409c862..c43f0395c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java @@ -48,6 +48,14 @@ public interface ISysPostService { */ List selectPostListByUserId(Long userId); + /** + * 通过岗位ID串查询岗位 + * + * @param postIds 岗位id串 + * @return 岗位列表信息 + */ + List selectPostByIds(List postIds); + /** * 校验岗位名称 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java index d2ee61f8a..64740aeff 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java @@ -35,6 +35,14 @@ public interface ISysRoleService { */ List selectRolesByUserId(Long userId); + /** + * 根据用户ID查询角色列表(包含被授权状态) + * + * @param userId 用户ID + * @return 角色列表 + */ + List selectRolesAuthByUserId(Long userId); + /** * 根据用户ID查询角色权限 * @@ -66,6 +74,14 @@ public interface ISysRoleService { */ SysRoleVo selectRoleById(Long roleId); + /** + * 通过角色ID串查询角色 + * + * @param roleIds 角色ID串 + * @return 角色列表信息 + */ + List selectRoleByIds(List roleIds); + /** * 校验角色名称是否唯一 * @@ -180,4 +196,5 @@ public interface ISysRoleService { int insertAuthUsers(Long roleId, Long[] userIds); void cleanOnlineUserByRole(Long roleId); + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java index bbe78be2b..0325a2558 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java @@ -3,6 +3,7 @@ package org.dromara.system.service; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.system.domain.bo.SysUserBo; +import org.dromara.system.domain.vo.SysUserExportVo; import org.dromara.system.domain.vo.SysUserVo; import java.util.List; @@ -23,7 +24,7 @@ public interface ISysUserService { * @param user 用户信息 * @return 用户信息集合信息 */ - List selectUserList(SysUserBo user); + List selectUserExportList(SysUserBo user); /** * 根据条件分页查询已分配用户角色列表 @@ -65,21 +66,30 @@ public interface ISysUserService { */ SysUserVo selectUserById(Long userId); + /** + * 通过用户ID串查询用户 + * + * @param userIds 用户ID串 + * @param deptId 部门id + * @return 用户列表信息 + */ + List selectUserByIds(List userIds, Long deptId); + /** * 根据用户ID查询用户所属角色组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ - String selectUserRoleGroup(String userName); + String selectUserRoleGroup(Long userId); /** * 根据用户ID查询用户所属岗位组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ - String selectUserPostGroup(String userName); + String selectUserPostGroup(Long userId); /** * 校验用户名称是否唯一 @@ -205,8 +215,8 @@ public interface ISysUserService { /** * 通过部门id查询当前部门所有用户 * - * @param deptId - * @return + * @param deptId 部门id + * @return 结果 */ List selectUserListByDept(Long deptId); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java index aecaf668c..26bc49157 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java @@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; @@ -16,6 +17,8 @@ import org.dromara.system.domain.bo.SysClientBo; import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.mapper.SysClientMapper; import org.dromara.system.service.ISysClientService; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.Collection; @@ -48,9 +51,10 @@ public class SysClientServiceImpl implements ISysClientService { /** * 查询客户端管理 */ + @Cacheable(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId") @Override - public SysClient queryByClientId(String clientId) { - return baseMapper.selectOne(new LambdaQueryWrapper().eq(SysClient::getClientId, clientId)); + public SysClientVo queryByClientId(String clientId) { + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysClient::getClientId, clientId)); } /** @@ -105,6 +109,7 @@ public class SysClientServiceImpl implements ISysClientService { /** * 修改客户端管理 */ + @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, key = "#bo.clientId") @Override public Boolean updateByBo(SysClientBo bo) { SysClient update = MapstructUtils.convert(bo, SysClient.class); @@ -116,12 +121,13 @@ public class SysClientServiceImpl implements ISysClientService { /** * 修改状态 */ + @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId") @Override - public int updateUserStatus(Long id, String status) { + public int updateUserStatus(String clientId, String status) { return baseMapper.update(null, new LambdaUpdateWrapper() .set(SysClient::getStatus, status) - .eq(SysClient::getId, id)); + .eq(SysClient::getClientId, clientId)); } /** @@ -134,6 +140,7 @@ public class SysClientServiceImpl implements ISysClientService { /** * 批量删除客户端管理 */ + @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, allEntries = true) @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java index fe5cc0e54..86edf59d4 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java @@ -7,6 +7,7 @@ import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.exception.ServiceException; @@ -27,7 +28,6 @@ import org.dromara.system.mapper.SysDeptMapper; import org.dromara.system.mapper.SysRoleMapper; import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.service.ISysDeptService; -import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -83,6 +83,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { lqw.eq(ObjectUtil.isNotNull(bo.getParentId()), SysDept::getParentId, bo.getParentId()); lqw.like(StringUtils.isNotBlank(bo.getDeptName()), SysDept::getDeptName, bo.getDeptName()); lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDept::getStatus, bo.getStatus()); + lqw.orderByAsc(SysDept::getAncestors); lqw.orderByAsc(SysDept::getParentId); lqw.orderByAsc(SysDept::getOrderNum); lqw.orderByAsc(SysDept::getDeptId); @@ -138,6 +139,14 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { return dept; } + @Override + public List selectDeptByIds(List deptIds) { + return baseMapper.selectDeptList(new LambdaQueryWrapper() + .select(SysDept::getDeptId, SysDept::getDeptName, SysDept::getLeader) + .eq(SysDept::getStatus, UserConstants.DEPT_NORMAL) + .in(CollUtil.isNotEmpty(deptIds), SysDept::getDeptId, deptIds)); + } + /** * 通过部门ID查询部门名称 * @@ -221,8 +230,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { if (LoginHelper.isSuperAdmin()) { return; } - SysDeptVo dept = baseMapper.selectDeptById(deptId); - if (ObjectUtil.isNull(dept)) { + if (baseMapper.countDeptById(deptId) == 0) { throw new ServiceException("没有权限访问部门数据!"); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java index 346d6bc83..bc6663f0c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java @@ -1,13 +1,12 @@ package org.dromara.system.service.impl; -import cn.dev33.satoken.context.SaHolder; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; 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.exception.ServiceException; import org.dromara.common.core.service.DictService; @@ -26,7 +25,6 @@ import org.dromara.system.domain.vo.SysDictTypeVo; import org.dromara.system.mapper.SysDictDataMapper; import org.dromara.system.mapper.SysDictTypeMapper; import org.dromara.system.service.ISysDictTypeService; -import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -217,16 +215,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService * @param separator 分隔符 * @return 字典标签 */ - @SuppressWarnings("unchecked cast") @Override public String getDictLabel(String dictType, String dictValue, String separator) { - // 优先从本地缓存获取 - List datas = (List) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType); - if (ObjectUtil.isNull(datas)) { - datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); - SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas); - } - + List datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); Map map = StreamUtils.toMap(datas, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel); if (StringUtils.containsAny(dictValue, separator)) { return Arrays.stream(dictValue.split(separator)) @@ -245,16 +236,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService * @param separator 分隔符 * @return 字典值 */ - @SuppressWarnings("unchecked cast") @Override public String getDictValue(String dictType, String dictLabel, String separator) { - // 优先从本地缓存获取 - List datas = (List) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType); - if (ObjectUtil.isNull(datas)) { - datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); - SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas); - } - + List datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); Map map = StreamUtils.toMap(datas, SysDictDataVo::getDictLabel, SysDictDataVo::getDictValue); if (StringUtils.containsAny(dictLabel, separator)) { return Arrays.stream(dictLabel.split(separator)) diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java index d215d5951..9c930a0c5 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java @@ -5,30 +5,29 @@ import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgentUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.Constants; import org.dromara.common.core.utils.MapstructUtils; -import org.dromara.common.mybatis.core.page.PageQuery; -import org.dromara.common.log.event.LogininforEvent; -import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.ip.AddressUtils; +import org.dromara.common.log.event.LogininforEvent; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.satoken.utils.LoginHelper; -import org.dromara.system.domain.SysClient; import org.dromara.system.domain.SysLogininfor; import org.dromara.system.domain.bo.SysLogininforBo; +import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysLogininforVo; -import org.dromara.system.mapper.SysClientMapper; import org.dromara.system.mapper.SysLogininforMapper; import org.dromara.system.service.ISysClientService; import org.dromara.system.service.ISysLogininforService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; -import jakarta.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -60,10 +59,10 @@ public class SysLogininforServiceImpl implements ISysLogininforService { final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); final String ip = ServletUtils.getClientIP(request); // 客户端信息 - String clientid = request.getHeader(LoginHelper.CLIENT_KEY); - SysClient client = null; - if (StringUtils.isNotBlank(clientid)) { - client = clientService.queryByClientId(clientid); + String clientId = request.getHeader(LoginHelper.CLIENT_KEY); + SysClientVo client = null; + if (StringUtils.isNotBlank(clientId)) { + client = clientService.queryByClientId(clientId); } String address = AddressUtils.getRealAddressByIP(ip); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java index 8e2b5ac4a..18b7a08da 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java @@ -9,6 +9,7 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.system.domain.SysNotice; +import org.dromara.system.domain.SysUser; import org.dromara.system.domain.bo.SysNoticeBo; import org.dromara.system.domain.vo.SysNoticeVo; import org.dromara.system.domain.vo.SysUserVo; @@ -68,7 +69,7 @@ public class SysNoticeServiceImpl implements ISysNoticeService { lqw.like(StringUtils.isNotBlank(bo.getNoticeTitle()), SysNotice::getNoticeTitle, bo.getNoticeTitle()); lqw.eq(StringUtils.isNotBlank(bo.getNoticeType()), SysNotice::getNoticeType, bo.getNoticeType()); if (StringUtils.isNotBlank(bo.getCreateByName())) { - SysUserVo sysUser = userMapper.selectUserByUserName(bo.getCreateByName()); + SysUserVo sysUser = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, bo.getCreateByName())); lqw.eq(SysNotice::getCreateBy, ObjectUtil.isNotNull(sysUser) ? sysUser.getUserId() : null); } lqw.orderByAsc(SysNotice::getNoticeId); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java index c7d47191d..1df221c32 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java @@ -6,6 +6,8 @@ import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.service.OssService; @@ -25,8 +27,6 @@ import org.dromara.system.domain.bo.SysOssBo; import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.mapper.SysOssMapper; import org.dromara.system.service.ISysOssService; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; import org.springframework.cache.annotation.Cacheable; import org.springframework.http.MediaType; @@ -36,7 +36,10 @@ import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; /** * 文件上传 服务层实现 @@ -49,6 +52,13 @@ public class SysOssServiceImpl implements ISysOssService, OssService { private final SysOssMapper baseMapper; + /** + * 查询OSS对象存储列表 + * + * @param bo OSS对象存储分页查询对象 + * @param pageQuery 分页查询实体类 + * @return 结果 + */ @Override public TableDataInfo queryPageList(SysOssBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); @@ -58,6 +68,12 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return TableDataInfo.build(result); } + /** + * 根据一组 ossIds 获取对应的 SysOssVo 列表 + * + * @param ossIds 一组文件在数据库中的唯一标识集合 + * @return 包含 SysOssVo 对象的列表 + */ @Override public List listByIds(Collection ossIds) { List list = new ArrayList<>(); @@ -75,6 +91,12 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return list; } + /** + * 根据一组 ossIds 获取对应文件的 URL 列表 + * + * @param ossIds 以逗号分隔的 ossId 字符串 + * @return 以逗号分隔的文件 URL 字符串 + */ @Override public String selectUrlByIds(String ossIds) { List list = new ArrayList<>(); @@ -107,12 +129,25 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return lqw; } + /** + * 根据 ossId 从缓存或数据库中获取 SysOssVo 对象 + * + * @param ossId 文件在数据库中的唯一标识 + * @return SysOssVo 对象,包含文件信息 + */ @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId") @Override public SysOssVo getById(Long ossId) { return baseMapper.selectVoById(ossId); } + + /** + * 文件下载方法,支持一次性下载完整文件 + * + * @param ossId OSS对象ID + * @param response HttpServletResponse对象,用于设置响应头和向客户端发送文件内容 + */ @Override public void download(Long ossId, HttpServletResponse response) throws IOException { SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId); @@ -122,7 +157,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName()); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); OssClient storage = OssFactory.instance(sysOss.getService()); - try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) { + try (InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) { int available = inputStream.available(); IoUtil.copy(inputStream, response.getOutputStream(), available); response.setContentLength(available); @@ -131,6 +166,13 @@ public class SysOssServiceImpl implements ISysOssService, OssService { } } + /** + * 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的 MultipartFile 对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + * @throws ServiceException 如果上传过程中发生异常,则抛出 ServiceException 异常 + */ @Override public SysOssVo upload(MultipartFile file) { String originalfileName = file.getOriginalFilename(); @@ -138,7 +180,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { OssClient storage = OssFactory.instance(); UploadResult uploadResult; try { - uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType()); + uploadResult = storage.uploadSuffix(file.getBytes(), suffix); } catch (IOException e) { throw new ServiceException(e.getMessage()); } @@ -146,6 +188,12 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult); } + /** + * 上传文件到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的文件对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + */ @Override public SysOssVo upload(File file) { String originalfileName = file.getName(); @@ -169,6 +217,13 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return this.matchingUrl(sysOssVo); } + /** + * 删除OSS对象存储 + * + * @param ids OSS对象ID串 + * @param isValid 判断是否需要校验 + * @return 结果 + */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { @@ -183,7 +238,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { } /** - * 匹配Url + * 桶类型为 private 的URL 修改为临时URL时长为120s * * @param oss OSS对象 * @return oss 匹配Url的OSS对象 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java index b69a06c5f..33b98af59 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java @@ -1,12 +1,16 @@ package org.dromara.system.service.impl; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; @@ -17,7 +21,6 @@ import org.dromara.system.domain.vo.SysPostVo; import org.dromara.system.mapper.SysPostMapper; import org.dromara.system.mapper.SysUserPostMapper; import org.dromara.system.service.ISysPostService; -import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.Arrays; @@ -92,7 +95,22 @@ public class SysPostServiceImpl implements ISysPostService { */ @Override public List selectPostListByUserId(Long userId) { - return baseMapper.selectPostListByUserId(userId); + List list = baseMapper.selectPostsByUserId(userId); + return StreamUtils.toList(list, SysPostVo::getPostId); + } + + /** + * 通过岗位ID串查询岗位 + * + * @param postIds 岗位id串 + * @return 岗位列表信息 + */ + @Override + public List selectPostByIds(List postIds) { + return baseMapper.selectVoList(new LambdaQueryWrapper() + .select(SysPost::getPostId, SysPost::getPostName, SysPost::getPostCode) + .eq(SysPost::getStatus, UserConstants.POST_NORMAL) + .in(CollUtil.isNotEmpty(postIds), SysPost::getPostId, postIds)); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java index 8a2b9390f..abd294f3e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java @@ -80,7 +80,6 @@ public class SysRoleServiceImpl implements ISysRoleService { .between(params.get("beginTime") != null && params.get("endTime") != null, "r.create_time", params.get("beginTime"), params.get("endTime")) .orderByAsc("r.role_sort").orderByAsc("r.create_time"); - ; return wrapper; } @@ -92,6 +91,17 @@ public class SysRoleServiceImpl implements ISysRoleService { */ @Override public List selectRolesByUserId(Long userId) { + return baseMapper.selectRolesByUserId(userId); + } + + /** + * 根据用户ID查询角色列表(包含被授权状态) + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List selectRolesAuthByUserId(Long userId) { List userRoles = baseMapper.selectRolePermissionByUserId(userId); List roles = selectRoleAll(); for (SysRoleVo role : roles) { @@ -141,7 +151,8 @@ public class SysRoleServiceImpl implements ISysRoleService { */ @Override public List selectRoleListByUserId(Long userId) { - return baseMapper.selectRoleListByUserId(userId); + List list = baseMapper.selectRolesByUserId(userId); + return StreamUtils.toList(list, SysRoleVo::getRoleId); } /** @@ -155,6 +166,19 @@ public class SysRoleServiceImpl implements ISysRoleService { return baseMapper.selectRoleById(roleId); } + /** + * 通过角色ID串查询角色 + * + * @param roleIds 角色ID串 + * @return 角色列表信息 + */ + @Override + public List selectRoleByIds(List roleIds) { + return baseMapper.selectRoleList(new QueryWrapper() + .eq("r.status", UserConstants.ROLE_NORMAL) + .in(CollUtil.isNotEmpty(roleIds), "r.role_id", roleIds)); + } + /** * 校验角色名称是否唯一 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java index 7ee19e851..a4d553b49 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java @@ -29,6 +29,7 @@ import org.dromara.system.domain.SysUserRole; import org.dromara.system.domain.bo.SysUserBo; import org.dromara.system.domain.vo.SysPostVo; 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.mapper.*; import org.dromara.system.service.ISysUserService; @@ -69,28 +70,28 @@ public class SysUserServiceImpl implements ISysUserService, UserService { * @return 用户信息集合信息 */ @Override - public List selectUserList(SysUserBo user) { - return baseMapper.selectUserList(this.buildQueryWrapper(user)); + public List selectUserExportList(SysUserBo user) { + return baseMapper.selectUserExportList(this.buildQueryWrapper(user)); } private Wrapper buildQueryWrapper(SysUserBo user) { Map params = user.getParams(); QueryWrapper wrapper = Wrappers.query(); wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) - .eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId()) - .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) - .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) - .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) - .between(params.get("beginTime") != null && params.get("endTime") != null, - "u.create_time", params.get("beginTime"), params.get("endTime")) - .and(ObjectUtil.isNotNull(user.getDeptId()), w -> { - List deptList = deptMapper.selectList(new LambdaQueryWrapper() - .select(SysDept::getDeptId) - .apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors"))); - List ids = StreamUtils.toList(deptList, SysDept::getDeptId); - ids.add(user.getDeptId()); - w.in("u.dept_id", ids); - }).orderByAsc("u.user_id"); + .eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId()) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + "u.create_time", params.get("beginTime"), params.get("endTime")) + .and(ObjectUtil.isNotNull(user.getDeptId()), w -> { + List deptList = deptMapper.selectList(new LambdaQueryWrapper() + .select(SysDept::getDeptId) + .apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors"))); + List ids = StreamUtils.toList(deptList, SysDept::getDeptId); + ids.add(user.getDeptId()); + w.in("u.dept_id", ids); + }).orderByAsc("u.user_id"); return wrapper; } @@ -104,11 +105,11 @@ public class SysUserServiceImpl implements ISysUserService, UserService { public TableDataInfo selectAllocatedList(SysUserBo user, PageQuery pageQuery) { QueryWrapper wrapper = Wrappers.query(); wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) - .eq(ObjectUtil.isNotNull(user.getRoleId()), "r.role_id", user.getRoleId()) - .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) - .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) - .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) - .orderByAsc("u.user_id"); + .eq(ObjectUtil.isNotNull(user.getRoleId()), "r.role_id", user.getRoleId()) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .orderByAsc("u.user_id"); Page page = baseMapper.selectAllocatedList(pageQuery.build(), wrapper); return TableDataInfo.build(page); } @@ -124,11 +125,11 @@ public class SysUserServiceImpl implements ISysUserService, UserService { List userIds = userRoleMapper.selectUserIdsByRoleId(user.getRoleId()); QueryWrapper wrapper = Wrappers.query(); wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) - .and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id")) - .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds) - .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) - .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) - .orderByAsc("u.user_id"); + .and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id")) + .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .orderByAsc("u.user_id"); Page page = baseMapper.selectUnallocatedList(pageQuery.build(), wrapper); return TableDataInfo.build(page); } @@ -141,7 +142,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserByUserName(String userName) { - return baseMapper.selectUserByUserName(userName); + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, userName)); } /** @@ -152,7 +153,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserByPhonenumber(String phonenumber) { - return baseMapper.selectUserByPhonenumber(phonenumber); + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getPhonenumber, phonenumber)); } /** @@ -163,18 +164,39 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserById(Long userId) { - return baseMapper.selectUserById(userId); + SysUserVo user = baseMapper.selectVoById(userId); + if (ObjectUtil.isNull(user)) { + return user; + } + user.setRoles(roleMapper.selectRolesByUserId(user.getUserId())); + return user; + } + + /** + * 通过用户ID串查询用户 + * + * @param userIds 用户ID串 + * @param deptId 部门id + * @return 用户列表信息 + */ + @Override + public List selectUserByIds(List userIds, Long deptId) { + return baseMapper.selectUserList(new LambdaQueryWrapper() + .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName) + .eq(SysUser::getStatus, UserConstants.USER_NORMAL) + .eq(ObjectUtil.isNotNull(deptId), SysUser::getDeptId, deptId) + .in(CollUtil.isNotEmpty(userIds), SysUser::getUserId, userIds)); } /** * 查询用户所属角色组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ @Override - public String selectUserRoleGroup(String userName) { - List list = roleMapper.selectRolesByUserName(userName); + public String selectUserRoleGroup(Long userId) { + List list = roleMapper.selectRolesByUserId(userId); if (CollUtil.isEmpty(list)) { return StringUtils.EMPTY; } @@ -184,12 +206,12 @@ public class SysUserServiceImpl implements ISysUserService, UserService { /** * 查询用户所属岗位组 * - * @param userName 用户名 + * @param userId 用户ID * @return 结果 */ @Override - public String selectUserPostGroup(String userName) { - List list = postMapper.selectPostsByUserName(userName); + public String selectUserPostGroup(Long userId) { + List list = postMapper.selectPostsByUserId(userId); if (CollUtil.isEmpty(list)) { return StringUtils.EMPTY; } @@ -205,8 +227,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public boolean checkUserNameUnique(SysUserBo user) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() - .eq(SysUser::getUserName, user.getUserName()) - .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + .eq(SysUser::getUserName, user.getUserName()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); return !exist; } @@ -218,8 +240,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public boolean checkPhoneUnique(SysUserBo user) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() - .eq(SysUser::getPhonenumber, user.getPhonenumber()) - .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + .eq(SysUser::getPhonenumber, user.getPhonenumber()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); return !exist; } @@ -231,8 +253,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public boolean checkEmailUnique(SysUserBo user) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() - .eq(SysUser::getEmail, user.getEmail()) - .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + .eq(SysUser::getEmail, user.getEmail()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); return !exist; } @@ -261,7 +283,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { if (LoginHelper.isSuperAdmin()) { return; } - if (ObjectUtil.isNull(baseMapper.selectUserById(userId))) { + if (baseMapper.countUserById(userId) == 0) { throw new ServiceException("没有权限访问用户数据!"); } } @@ -345,9 +367,9 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public int updateUserStatus(Long userId, String status) { return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysUser::getStatus, status) - .eq(SysUser::getUserId, userId)); + new LambdaUpdateWrapper() + .set(SysUser::getStatus, status) + .eq(SysUser::getUserId, userId)); } /** @@ -359,12 +381,12 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public int updateUserProfile(SysUserBo user) { return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(ObjectUtil.isNotNull(user.getNickName()), SysUser::getNickName, user.getNickName()) - .set(SysUser::getPhonenumber, user.getPhonenumber()) - .set(SysUser::getEmail, user.getEmail()) - .set(SysUser::getSex, user.getSex()) - .eq(SysUser::getUserId, user.getUserId())); + new LambdaUpdateWrapper() + .set(ObjectUtil.isNotNull(user.getNickName()), SysUser::getNickName, user.getNickName()) + .set(SysUser::getPhonenumber, user.getPhonenumber()) + .set(SysUser::getEmail, user.getEmail()) + .set(SysUser::getSex, user.getSex()) + .eq(SysUser::getUserId, user.getUserId())); } /** @@ -377,9 +399,9 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public boolean updateUserAvatar(Long userId, Long avatar) { return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysUser::getAvatar, avatar) - .eq(SysUser::getUserId, userId)) > 0; + new LambdaUpdateWrapper() + .set(SysUser::getAvatar, avatar) + .eq(SysUser::getUserId, userId)) > 0; } /** @@ -392,9 +414,9 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public int resetUserPwd(Long userId, String password) { return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysUser::getPassword, password) - .eq(SysUser::getUserId, userId)); + new LambdaUpdateWrapper() + .set(SysUser::getPassword, password) + .eq(SysUser::getUserId, userId)); } /** @@ -518,8 +540,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService { /** * 通过部门id查询当前部门所有用户 * - * @param deptId - * @return + * @param deptId 部门ID + * @return 用户信息集合信息 */ @Override public List selectUserListByDept(Long deptId) { @@ -529,14 +551,26 @@ public class SysUserServiceImpl implements ISysUserService, UserService { return baseMapper.selectVoList(lqw); } + /** + * 通过用户ID查询用户账户 + * + * @param userId 用户ID + * @return 用户账户 + */ @Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = "#userId") @Override public String selectUserNameById(Long userId) { SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getUserName).eq(SysUser::getUserId, userId)); + .select(SysUser::getUserName).eq(SysUser::getUserId, userId)); return ObjectUtil.isNull(sysUser) ? null : sysUser.getUserName(); } + /** + * 通过用户ID查询用户账户 + * + * @param userId 用户ID + * @return 用户账户 + */ @Override @Cacheable(cacheNames = CacheNames.SYS_NICKNAME, key = "#userId") public String selectNicknameById(Long userId) { @@ -544,4 +578,31 @@ public class SysUserServiceImpl implements ISysUserService, UserService { .select(SysUser::getNickName).eq(SysUser::getUserId, userId)); return ObjectUtil.isNull(sysUser) ? null : sysUser.getNickName(); } + + /** + * 通过用户ID查询用户手机号 + * + * @param userId 用户id + * @return 用户手机号 + */ + @Override + public String selectPhonenumberById(Long userId) { + SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getPhonenumber).eq(SysUser::getUserId, userId)); + return ObjectUtil.isNull(sysUser) ? null : sysUser.getPhonenumber(); + } + + /** + * 通过用户ID查询用户邮箱 + * + * @param userId 用户id + * @return 用户邮箱 + */ + @Override + public String selectEmailById(Long userId) { + SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getEmail).eq(SysUser::getUserId, userId)); + return ObjectUtil.isNull(sysUser) ? null : sysUser.getEmail(); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml index bba949d7b..6ad866f29 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -8,11 +8,18 @@ - select * from sys_dept ${ew.getCustomSqlSegment} + select + + ${ew.getSqlSelect} + + + * + + from sys_dept ${ew.getCustomSqlSegment} - - select * from sys_dept where del_flag = '0' and dept_id = #{deptId} + + select count(*) from sys_dept where del_flag = '0' and dept_id = #{deptId} diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml index d2108ebbc..199dffe4d 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -7,20 +7,12 @@ - - select p.post_id + + select p.post_id, p.post_name, p.post_code from sys_post p left join sys_user_post up on up.post_id = p.post_id left join sys_user u on u.user_id = up.user_id where u.user_id = #{userId} - - select p.post_id, p.post_name, p.post_code - from sys_post p - left join sys_user_post up on up.post_id = p.post_id - left join sys_user u on u.user_id = up.user_id - where u.user_name = #{userName} - - diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml index b1916985e..7b8bba761 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -40,23 +40,17 @@ WHERE r.del_flag = '0' and sur.user_id = #{userId} - - select r.role_id - from sys_role r - left join sys_user_role sur on sur.role_id = r.role_id - left join sys_user u on u.user_id = sur.user_id - where u.user_id = #{userId} - - - + select r.role_id, r.role_name, r.role_key, - r.role_sort + r.role_sort, + r.data_scope, + r.status from sys_role r left join sys_user_role sur on sur.role_id = r.role_id left join sys_user u on u.user_id = sur.user_id - WHERE r.del_flag = '0' and u.user_name = #{userName} + WHERE r.del_flag = '0' and sur.user_id = #{userId} diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml index 7af7dd0cc..ded6fa858 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -4,78 +4,40 @@ "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - - - - - - - - - - + + - - - - - - - - select u.user_id, - u.tenant_id, - u.dept_id, - u.user_name, - u.nick_name, - u.user_type, - u.email, - u.avatar, - u.phonenumber, - u.password, - u.sex, - u.status, - u.del_flag, - u.login_ip, - u.login_date, - u.create_by, - u.create_time, - u.remark, - d.dept_id, - d.parent_id, - d.ancestors, - d.dept_name, - d.order_num, - d.leader, - d.status as dept_status, - d.email as dept_email, - d.create_time as dept_cteate_time, - r.role_id, - r.role_name, - r.role_key, - r.role_sort, - r.data_scope, - r.status as role_status - from sys_user u - left join sys_dept d on u.dept_id = d.dept_id - left join sys_user_role sur on u.user_id = sur.user_id - left join sys_role r on r.role_id = sur.role_id - - - 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, - d.dept_name, d.leader, u1.user_name as leaderName + select + + ${ew.getSqlSelect} + + + 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 + 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} + select + + ${ew.getSqlSelect} + + + 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 + + from sys_user u + ${ew.getCustomSqlSegment} + + + 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, d.dept_name, d.leader, u1.user_name as leaderName @@ -103,24 +65,8 @@ ${ew.getCustomSqlSegment} - - - where u.del_flag = '0' and u.user_name = #{userName} - - - - - where u.del_flag = '0' and u.phonenumber = #{phonenumber} - - - - - where u.del_flag = '0' and u.email = #{email} - - - - - where u.del_flag = '0' and u.user_id = #{userId} + + select count(*) from sys_user where del_flag = '0' and user_id = #{userId} diff --git a/ruoyi-modules/ruoyi-workflow/pom.xml b/ruoyi-modules/ruoyi-workflow/pom.xml new file mode 100644 index 000000000..f0391e358 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/pom.xml @@ -0,0 +1,93 @@ + + + + org.dromara + ruoyi-modules + ${revision} + ../pom.xml + + 4.0.0 + jar + ruoyi-workflow + + + 工作流模块 + + + + + + + org.flowable + flowable-spring-boot-autoconfigure + + + org.flowable + flowable-spring-security + + + + + + org.flowable + flowable-spring-configurator + + + + org.flowable + flowable-spring-boot-starter-actuator + + + + + org.flowable + flowable-image-generator + + + + + org.flowable + flowable-json-converter + 6.8.0 + + + + + org.apache.xmlgraphics + batik-all + 1.10 + + + xalan + xalan + + + + + + + org.dromara + ruoyi-system + + + + org.dromara + ruoyi-common-websocket + + + + org.dromara + ruoyi-common-mail + + + + org.dromara + ruoyi-common-sms + + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java new file mode 100644 index 000000000..5ea262d17 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java @@ -0,0 +1,27 @@ +package org.dromara.workflow.annotation; + + +import java.lang.annotation.*; + +/** + * 流程任务监听注解 + * + * @author may + * @date 2023-12-27 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface FlowListenerAnnotation { + + /** + * 流程定义key + */ + String processDefinitionKey(); + + /** + * 节点id + */ + String taskDefId() default ""; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java new file mode 100644 index 000000000..df27d3cf9 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java @@ -0,0 +1,137 @@ +package org.dromara.workflow.common.constant; + + +/** + * 工作流常量 + * + * @author may + */ +public interface FlowConstant { + + String MESSAGE_CURRENT_TASK_IS_NULL = "当前任务不存在或你不是任务办理人!"; + + String MESSAGE_SUSPENDED = "当前任务已挂起不可审批!"; + + /** + * 连线 + */ + String SEQUENCE_FLOW = "sequenceFlow"; + + /** + * 并行网关 + */ + String PARALLEL_GATEWAY = "parallelGateway"; + + /** + * 排它网关 + */ + String EXCLUSIVE_GATEWAY = "exclusiveGateway"; + + /** + * 包含网关 + */ + String INCLUSIVE_GATEWAY = "inclusiveGateway"; + + /** + * 结束节点 + */ + String END_EVENT = "endEvent"; + + + /** + * 流程委派标识 + */ + String PENDING = "PENDING"; + + /** + * 候选人标识 + */ + String CANDIDATE = "candidate"; + + /** + * 会签任务总数 + */ + String NUMBER_OF_INSTANCES = "nrOfInstances"; + + /** + * 正在执行的会签总数 + */ + String NUMBER_OF_ACTIVE_INSTANCES = "nrOfActiveInstances"; + + /** + * 已完成的会签任务总数 + */ + String NUMBER_OF_COMPLETED_INSTANCES = "nrOfCompletedInstances"; + + /** + * 循环的索引值,可以使用elementIndexVariable属性修改loopCounter的变量名 + */ + String LOOP_COUNTER = "loopCounter"; + + String ZIP = "ZIP"; + + /** + * 流程实例对象 + */ + String PROCESS_INSTANCE_VO = "processInstanceVo"; + + /** + * 流程定义配置 + */ + String WF_DEFINITION_CONFIG_VO = "wfDefinitionConfigVo"; + + /** + * 节点配置 + */ + String WF_NODE_CONFIG_VO = "wfNodeConfigVo"; + + /** + * 流程发起人 + */ + String INITIATOR = "initiator"; + + /** + * 流程实例id + */ + String PROCESS_INSTANCE_ID = "processInstanceId"; + + /** + * 业务id + */ + String BUSINESS_KEY = "businessKey"; + + /** + * 流程定义id + */ + String PROCESS_DEFINITION_ID = "processDefinitionId"; + + /** + * 开启跳过表达式变量 + */ + String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; + + /** + * 模型标识key命名规范正则表达式 + */ + String MODEL_KEY_PATTERN = "^[a-zA-Z][a-zA-Z0-9_]{0,254}$"; + + /** + * 用户任务 + */ + String USER_TASK = "userTask"; + + /** + * 会签 + */ + String MULTI_INSTANCE = "multiInstance"; + + /** + * 是 + */ + String TRUE = "0"; + + /** + * 否 + */ + String FALSE = "1"; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java new file mode 100644 index 000000000..6eb6ffe6b --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java @@ -0,0 +1,152 @@ +package org.dromara.workflow.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; + +import java.util.Arrays; + +/** + * 业务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum BusinessStatusEnum { + /** + * 已撤销 + */ + CANCEL("cancel", "已撤销"), + /** + * 草稿 + */ + DRAFT("draft", "草稿"), + /** + * 待审核 + */ + WAITING("waiting", "待审核"), + /** + * 已完成 + */ + FINISH("finish", "已完成"), + /** + * 已作废 + */ + INVALID("invalid", "已作废"), + /** + * 已退回 + */ + BACK("back", "已退回"), + /** + * 已终止 + */ + TERMINATION("termination", "已终止"); + + /** + * 状态 + */ + private final String status; + + /** + * 描述 + */ + private final String desc; + + /** + * 获取业务状态 + * + * @param status 状态 + */ + public static String findByStatus(String status) { + if (StringUtils.isBlank(status)) { + return StrUtil.EMPTY; + } + return Arrays.stream(BusinessStatusEnum.values()) + .filter(statusEnum -> statusEnum.getStatus().equals(status)) + .findFirst() + .map(BusinessStatusEnum::getDesc) + .orElse(StrUtil.EMPTY); + } + + /** + * 启动流程校验 + * + * @param status 状态 + */ + public static void checkStartStatus(String status) { + if (WAITING.getStatus().equals(status)) { + throw new ServiceException("该单据已提交过申请,正在审批中!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 撤销流程校验 + * + * @param status 状态 + */ + public static void checkCancelStatus(String status) { + if (CANCEL.getStatus().equals(status)) { + throw new ServiceException("该单据已撤销!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (BACK.getStatus().equals(status)) { + throw new ServiceException("该单据已退回!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 驳回流程校验 + * + * @param status 状态 + */ + public static void checkBackStatus(String status) { + if (BACK.getStatus().equals(status)) { + throw new ServiceException("该单据已退回!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (CANCEL.getStatus().equals(status)) { + throw new ServiceException("该单据已撤销!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 作废,终止流程校验 + * + * @param status 状态 + */ + public static void checkInvalidStatus(String status) { + if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java new file mode 100644 index 000000000..083ab7b85 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java @@ -0,0 +1,54 @@ +package org.dromara.workflow.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 任务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum FormTypeEnum { + /** + * 自定义表单 + */ + STATIC("static", "自定义表单"), + /** + * 动态表单 + */ + DYNAMIC("dynamic", "动态表单"); + + /** + * 类型 + */ + private final String type; + + /** + * 描述 + */ + private final String desc; + + /** + * 表单类型 + * + * @param formType 表单类型 + */ + public static String findByType(String formType) { + if (StringUtils.isBlank(formType)) { + return StrUtil.EMPTY; + } + + return Arrays.stream(FormTypeEnum.values()) + .filter(statusEnum -> statusEnum.getType().equals(formType)) + .findFirst() + .map(FormTypeEnum::getDesc) + .orElse(StrUtil.EMPTY); + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java new file mode 100644 index 000000000..a28295822 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java @@ -0,0 +1,51 @@ +package org.dromara.workflow.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 消息类型枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum MessageTypeEnum { + /** + * 站内信 + */ + SYSTEM_MESSAGE("1", "站内信"), + /** + * 邮箱 + */ + EMAIL_MESSAGE("2", "邮箱"), + /** + * 短信 + */ + SMS_MESSAGE("3", "短信"); + + private final String code; + + private final String desc; + + private final static Map MESSAGE_TYPE_ENUM_MAP = new ConcurrentHashMap<>(MessageTypeEnum.values().length); + + static { + for (MessageTypeEnum messageType : MessageTypeEnum.values()) { + MESSAGE_TYPE_ENUM_MAP.put(messageType.code, messageType); + } + } + + /** + * 根据消息类型 code 获取 MessageTypeEnum + * @param code 消息类型code + * @return MessageTypeEnum + */ + public static MessageTypeEnum getByCode(String code) { + return MESSAGE_TYPE_ENUM_MAP.get(code); + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java new file mode 100644 index 000000000..7b2f55c20 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java @@ -0,0 +1,94 @@ +package org.dromara.workflow.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 任务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum TaskStatusEnum { + /** + * 撤销 + */ + CANCEL("cancel", "撤销"), + /** + * 通过 + */ + PASS("pass", "通过"), + /** + * 待审核 + */ + WAITING("waiting", "待审核"), + /** + * 作废 + */ + INVALID("invalid", "作废"), + /** + * 退回 + */ + BACK("back", "退回"), + /** + * 终止 + */ + TERMINATION("termination", "终止"), + /** + * 转办 + */ + TRANSFER("transfer", "转办"), + /** + * 委托 + */ + PENDING("pending", "委托"), + /** + * 抄送 + */ + COPY("copy", "抄送"), + /** + * 加签 + */ + SIGN("sign", "加签"), + /** + * 减签 + */ + SIGN_OFF("sign_off", "减签"), + /** + * 超时 + */ + TIMEOUT("timeout", "超时"); + + /** + * 状态 + */ + private final String status; + + /** + * 描述 + */ + private final String desc; + + /** + * 任务业务状态 + * + * @param status 状态 + */ + public static String findByStatus(String status) { + if (StringUtils.isBlank(status)) { + return StrUtil.EMPTY; + } + + return Arrays.stream(TaskStatusEnum.values()) + .filter(statusEnum -> statusEnum.getStatus().equals(status)) + .findFirst() + .map(TaskStatusEnum::getDesc) + .orElse(StrUtil.EMPTY); + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java new file mode 100644 index 000000000..3332f86a5 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java @@ -0,0 +1,147 @@ +package org.dromara.workflow.controller; + +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.bo.ModelBo; +import org.dromara.workflow.domain.vo.ModelVo; +import org.dromara.workflow.service.IActModelService; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Model; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +/** + * 模型管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/model") +public class ActModelController extends BaseController { + + private final RepositoryService repositoryService; + + private final IActModelService actModelService; + + + /** + * 分页查询模型 + * + * @param modelBo 模型参数 + */ + @GetMapping("/list") + public TableDataInfo page(ModelBo modelBo, PageQuery pageQuery) { + return actModelService.page(modelBo, pageQuery); + } + + /** + * 新增模型 + * + * @param modelBo 模型请求对象 + */ + @Log(title = "模型管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/save") + public R saveNewModel(@Validated(AddGroup.class) @RequestBody ModelBo modelBo) { + return toAjax(actModelService.saveNewModel(modelBo)); + } + + /** + * 查询模型 + * + * @param id 模型id + */ + @GetMapping("/getInfo/{id}") + public R getInfo(@NotBlank(message = "模型id不能为空") @PathVariable String id) { + return R.ok(actModelService.getInfo(id)); + } + + /** + * 修改模型信息 + * + * @param modelBo 模型数据 + */ + @Log(title = "模型管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping(value = "/update") + public R update(@RequestBody ModelBo modelBo) { + return toAjax(actModelService.update(modelBo)); + } + + /** + * 编辑XMl模型 + * + * @param modelBo 模型数据 + */ + @Log(title = "模型管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping(value = "/editModelXml") + public R editModel(@Validated(EditGroup.class) @RequestBody ModelBo modelBo) { + return toAjax(actModelService.editModelXml(modelBo)); + } + + /** + * 删除流程模型 + * + * @param ids 模型id + */ + @Log(title = "模型管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @DeleteMapping("/{ids}") + @Transactional(rollbackFor = Exception.class) + public R delete(@NotEmpty(message = "主键不能为空") @PathVariable String[] ids) { + Arrays.stream(ids).parallel().forEachOrdered(repositoryService::deleteModel); + return R.ok(); + } + + /** + * 模型部署 + * + * @param id 模型id + */ + @Log(title = "模型管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/modelDeploy/{id}") + public R deploy(@NotBlank(message = "模型id不能为空") @PathVariable("id") String id) { + return toAjax(actModelService.modelDeploy(id)); + } + + /** + * 导出模型zip压缩包 + * + * @param modelIds 模型id + * @param response 相应 + */ + @GetMapping("/export/zip/{modelIds}") + public void exportZip(@NotEmpty(message = "模型id不能为空") @PathVariable List modelIds, + HttpServletResponse response) { + actModelService.exportZip(modelIds, response); + } + + /** + * 复制模型 + * + * @param modelBo 模型数据 + */ + @PostMapping("/copyModel") + public R copyModel(@RequestBody ModelBo modelBo) { + return toAjax(actModelService.copyModel(modelBo)); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java new file mode 100644 index 000000000..5198bd164 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java @@ -0,0 +1,147 @@ +package org.dromara.workflow.controller; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.bo.ProcessDefinitionBo; +import org.dromara.workflow.domain.vo.ProcessDefinitionVo; +import org.dromara.workflow.service.IActProcessDefinitionService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * 流程定义管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/processDefinition") +public class ActProcessDefinitionController extends BaseController { + + private final IActProcessDefinitionService actProcessDefinitionService; + + /** + * 分页查询 + * + * @param bo 参数 + */ + @GetMapping("/list") + public TableDataInfo page(ProcessDefinitionBo bo, PageQuery pageQuery) { + return actProcessDefinitionService.page(bo, pageQuery); + } + + /** + * 查询历史流程定义列表 + * + * @param key 流程定义key + */ + @GetMapping("/getListByKey/{key}") + public R> getListByKey(@NotEmpty(message = "流程定义key不能为空") @PathVariable String key) { + return R.ok("操作成功", actProcessDefinitionService.getListByKey(key)); + } + + /** + * 查看流程定义图片 + * + * @param processDefinitionId 流程定义id + */ + @GetMapping("/definitionImage/{processDefinitionId}") + public R definitionImage(@PathVariable String processDefinitionId) { + return R.ok("操作成功", actProcessDefinitionService.definitionImage(processDefinitionId)); + } + + /** + * 查看流程定义xml文件 + * + * @param processDefinitionId 流程定义id + */ + @GetMapping("/definitionXml/{processDefinitionId}") + public R> definitionXml(@NotBlank(message = "流程定义id不能为空") @PathVariable String processDefinitionId) { + Map map = new HashMap<>(); + String xmlStr = actProcessDefinitionService.definitionXml(processDefinitionId); + map.put("xml", Arrays.asList(xmlStr.split("\n"))); + map.put("xmlStr", xmlStr); + return R.ok(map); + } + + /** + * 删除流程定义 + * + * @param deploymentIds 部署id + * @param processDefinitionIds 流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deploymentIds}/{processDefinitionIds}") + public R deleteDeployment(@NotNull(message = "流程部署id不能为空") @PathVariable List deploymentIds, + @NotNull(message = "流程定义id不能为空") @PathVariable List processDefinitionIds) { + return toAjax(actProcessDefinitionService.deleteDeployment(deploymentIds, processDefinitionIds)); + } + + /** + * 激活或者挂起流程定义 + * + * @param processDefinitionId 流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/updateDefinitionState/{processDefinitionId}") + public R updateDefinitionState(@NotBlank(message = "流程定义id不能为空") @PathVariable String processDefinitionId) { + return toAjax(actProcessDefinitionService.updateDefinitionState(processDefinitionId)); + } + + /** + * 迁移流程定义 + * + * @param currentProcessDefinitionId 当前流程定义id + * @param fromProcessDefinitionId 需要迁移到的流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/migrationDefinition/{currentProcessDefinitionId}/{fromProcessDefinitionId}") + public R migrationDefinition(@NotBlank(message = "当前流程定义id") @PathVariable String currentProcessDefinitionId, + @NotBlank(message = "需要迁移到的流程定义id") @PathVariable String fromProcessDefinitionId) { + return toAjax(actProcessDefinitionService.migrationDefinition(currentProcessDefinitionId, fromProcessDefinitionId)); + } + + /** + * 流程定义转换为模型 + * + * @param processDefinitionId 流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/convertToModel/{processDefinitionId}") + public R convertToModel(@NotEmpty(message = "流程定义id不能为空") @PathVariable String processDefinitionId) { + return toAjax(actProcessDefinitionService.convertToModel(processDefinitionId)); + } + + /** + * 通过zip或xml部署流程定义 + * + * @param file 文件 + * @param categoryCode 分类 + */ + @Log(title = "流程定义管理", businessType = BusinessType.INSERT) + @PostMapping("/deployByFile") + public void deployByFile(@RequestParam("file") MultipartFile file, @RequestParam("categoryCode") String categoryCode) { + actProcessDefinitionService.deployByFile(file, categoryCode); + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java new file mode 100644 index 000000000..4d433df43 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java @@ -0,0 +1,158 @@ +package org.dromara.workflow.controller; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.bo.ProcessInstanceBo; +import org.dromara.workflow.domain.bo.ProcessInvalidBo; +import org.dromara.workflow.domain.bo.TaskUrgingBo; +import org.dromara.workflow.domain.vo.ProcessInstanceVo; +import org.dromara.workflow.service.IActProcessInstanceService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.Map; + +/** + * 流程实例管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/processInstance") +public class ActProcessInstanceController extends BaseController { + + private final IActProcessInstanceService actProcessInstanceService; + + /** + * 分页查询正在运行的流程实例 + * + * @param bo 参数 + */ + @GetMapping("/getPageByRunning") + public TableDataInfo getPageByRunning(ProcessInstanceBo bo, PageQuery pageQuery) { + return actProcessInstanceService.getPageByRunning(bo, pageQuery); + } + + /** + * 分页查询已结束的流程实例 + * + * @param bo 参数 + */ + @GetMapping("/getPageByFinish") + public TableDataInfo getPageByFinish(ProcessInstanceBo bo, PageQuery pageQuery) { + return actProcessInstanceService.getPageByFinish(bo, pageQuery); + } + + /** + * 通过流程实例id获取历史流程图 + * + * @param processInstanceId 流程实例id + */ + @GetMapping("/getHistoryImage/{processInstanceId}") + public R getHistoryImage(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) { + return R.ok("操作成功", actProcessInstanceService.getHistoryImage(processInstanceId)); + } + + /** + * 通过流程实例id获取历史流程图运行中,历史等节点 + * + * @param processInstanceId 流程实例id + */ + @GetMapping("/getHistoryList/{processInstanceId}") + public R> getHistoryList(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) { + return R.ok("操作成功", actProcessInstanceService.getHistoryList(processInstanceId)); + } + + /** + * 获取审批记录 + * + * @param processInstanceId 流程实例id + */ + @GetMapping("/getHistoryRecord/{processInstanceId}") + public R> getHistoryRecord(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) { + return R.ok(actProcessInstanceService.getHistoryRecord(processInstanceId)); + } + + /** + * 作废流程实例,不会删除历史记录(删除运行中的实例) + * + * @param processInvalidBo 参数 + */ + @Log(title = "流程实例管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @PostMapping("/deleteRunInstance") + public R deleteRunInstance(@Validated(AddGroup.class) @RequestBody ProcessInvalidBo processInvalidBo) { + return toAjax(actProcessInstanceService.deleteRunInstance(processInvalidBo)); + } + + /** + * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param processInstanceIds 流程实例id + */ + @Log(title = "流程实例管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @DeleteMapping("/deleteRunAndHisInstance/{processInstanceIds}") + public R deleteRunAndHisInstance(@NotNull(message = "流程实例id不能为空") @PathVariable String[] processInstanceIds) { + return toAjax(actProcessInstanceService.deleteRunAndHisInstance(Arrays.asList(processInstanceIds))); + } + + /** + * 已完成的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param processInstanceIds 流程实例id + */ + @Log(title = "流程实例管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @DeleteMapping("/deleteFinishAndHisInstance/{processInstanceIds}") + public R deleteFinishAndHisInstance(@NotNull(message = "流程实例id不能为空") @PathVariable String[] processInstanceIds) { + return toAjax(actProcessInstanceService.deleteFinishAndHisInstance(Arrays.asList(processInstanceIds))); + } + + /** + * 撤销流程申请 + * + * @param processInstanceId 流程实例id + */ + @Log(title = "流程实例管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/cancelProcessApply/{processInstanceId}") + public R cancelProcessApply(@NotBlank(message = "流程实例id不能为空") @PathVariable String processInstanceId) { + return toAjax(actProcessInstanceService.cancelProcessApply(processInstanceId)); + } + + /** + * 分页查询当前登录人单据 + * + * @param bo 参数 + */ + @GetMapping("/getPageByCurrent") + public TableDataInfo getPageByCurrent(ProcessInstanceBo bo, PageQuery pageQuery) { + return actProcessInstanceService.getPageByCurrent(bo, pageQuery); + } + + /** + * 任务催办(给当前任务办理人发送站内信,邮件,短信等) + * + * @param taskUrgingBo 任务催办 + */ + @Log(title = "流程实例管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/taskUrging") + public R taskUrging(@RequestBody TaskUrgingBo taskUrgingBo) { + return toAjax(actProcessInstanceService.taskUrging(taskUrgingBo)); + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java new file mode 100644 index 000000000..aa290127a --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java @@ -0,0 +1,274 @@ +package org.dromara.workflow.controller; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import jakarta.validation.constraints.NotBlank; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.WfTaskBackNode; +import org.dromara.workflow.domain.bo.*; +import org.dromara.workflow.domain.vo.TaskVo; +import org.dromara.workflow.domain.vo.VariableVo; +import org.dromara.workflow.service.IActTaskService; +import org.dromara.workflow.service.IWfTaskBackNodeService; +import org.dromara.workflow.utils.QueryUtils; +import org.flowable.engine.TaskService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 任务管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/task") +public class ActTaskController extends BaseController { + + private final IActTaskService actTaskService; + + private final TaskService taskService; + + private final IWfTaskBackNodeService iWfTaskBackNodeService; + + + /** + * 启动任务 + * + * @param startProcessBo 启动流程参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/startWorkFlow") + public R> startWorkFlow(@Validated(AddGroup.class) @RequestBody StartProcessBo startProcessBo) { + Map map = actTaskService.startWorkFlow(startProcessBo); + return R.ok("提交成功", map); + } + + /** + * 办理任务 + * + * @param completeTaskBo 办理任务参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/completeTask") + public R completeTask(@Validated(AddGroup.class) @RequestBody CompleteTaskBo completeTaskBo) { + return toAjax(actTaskService.completeTask(completeTaskBo)); + } + + /** + * 查询当前用户的待办任务 + * + * @param taskBo 参数 + */ + @GetMapping("/getPageByTaskWait") + public TableDataInfo