diff --git a/README.md b/README.md index 5ed7878f1..7a8864116 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey
CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow
数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/
引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc
+**启山商城系统 多租户商城源码可免费商用可二次开发 - https://www.73app.cn/**
[如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group) # 本框架与RuoYi的功能差异 diff --git a/pom.xml b/pom.xml index 58672cae4..ebf2814ed 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ 0.2.0 1.18.34 1.76 - 1.16.6 + 1.16.7 2.7.0 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 2eae90ab7..41a802b44 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 @@ -14,6 +14,7 @@ import org.dromara.common.core.constant.CacheConstants; import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.TenantConstants; +import org.dromara.common.core.domain.dto.PostDTO; import org.dromara.common.core.domain.dto.RoleDTO; import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.enums.LoginType; @@ -60,6 +61,7 @@ public class SysLoginService { private final ISysSocialService sysSocialService; private final ISysRoleService roleService; private final ISysDeptService deptService; + private final ISysPostService postService; private final SysUserMapper userMapper; @@ -148,21 +150,24 @@ public class SysLoginService { */ public LoginUser buildLoginUser(SysUserVo user) { LoginUser loginUser = new LoginUser(); + Long userId = user.getUserId(); loginUser.setTenantId(user.getTenantId()); - loginUser.setUserId(user.getUserId()); + loginUser.setUserId(userId); loginUser.setDeptId(user.getDeptId()); loginUser.setUsername(user.getUserName()); loginUser.setNickname(user.getNickName()); loginUser.setUserType(user.getUserType()); - loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId())); - loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId())); + loginUser.setMenuPermission(permissionService.getMenuPermission(userId)); + loginUser.setRolePermission(permissionService.getRolePermission(userId)); if (ObjectUtil.isNotNull(user.getDeptId())) { Opt deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById); loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY)); loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY)); } - List roles = roleService.selectRolesByUserId(user.getUserId()); + List roles = roleService.selectRolesByUserId(userId); + List posts = postService.selectPostsByUserId(userId); loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class)); + loginUser.setPosts(BeanUtil.copyToList(posts, PostDTO.class)); return loginUser; } 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 948ed9acb..fa9b61819 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 @@ -5,9 +5,17 @@ import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.util.ObjectUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthToken; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthRequest; +import me.zhyd.oauth.request.AuthWechatMiniProgramRequest; import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.domain.model.XcxLoginBody; import org.dromara.common.core.domain.model.XcxLoginUser; +import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.satoken.utils.LoginHelper; @@ -39,12 +47,24 @@ public class XcxAuthStrategy implements IAuthStrategy { // 多个小程序识别使用 String appid = loginBody.getAppid(); - // todo 以下自行实现 // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid - String openid = ""; + AuthRequest authRequest = new AuthWechatMiniProgramRequest(AuthConfig.builder() + .clientId(appid).clientSecret("自行填写密钥 可根据不同appid填入不同密钥") + .ignoreCheckRedirectUri(true).ignoreCheckState(true).build()); + AuthCallback authCallback = new AuthCallback(); + authCallback.setCode(xcxCode); + AuthResponse resp = authRequest.login(authCallback); + String openid, unionId; + if (resp.ok()) { + AuthToken token = resp.getData().getToken(); + openid = token.getOpenId(); + // 微信小程序只有关联到微信开放平台下之后才能获取到 unionId,因此unionId不一定能返回。 + unionId = token.getUnionId(); + } else { + throw new ServiceException(resp.getMsg()); + } // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可 SysUserVo user = loadUserByOpenid(openid); - // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 XcxLoginUser loginUser = new XcxLoginUser(); loginUser.setTenantId(user.getTenantId()); diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 283cdad44..e4f6086cc 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -200,7 +200,7 @@ justauth: redirect-uri: ${justauth.address}/social-callback?source=maxkey topiam: # topiam 服务器地址 - server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol + server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol client-id: 449c4*********937************759 client-secret: ac7***********1e0************28d redirect-uri: ${justauth.address}/social-callback?source=topiam diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index dead74198..c42bb7f26 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -139,6 +139,8 @@ tenant: # MyBatisPlus配置 # https://baomidou.com/config/ mybatis-plus: + # 自定义配置 是否全局开启逻辑删除 关闭后 所有逻辑删除功能将失效 + enableLogicDelete: true # 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper mapperPackage: org.dromara.**.mapper # 对应的 XML 文件位置 diff --git a/ruoyi-admin/src/main/resources/logback-plus.xml b/ruoyi-admin/src/main/resources/logback-plus.xml index 40fa33b7c..b74289ecd 100644 --- a/ruoyi-admin/src/main/resources/logback-plus.xml +++ b/ruoyi-admin/src/main/resources/logback-plus.xml @@ -2,7 +2,7 @@ + value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/> diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/DeptDTO.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/DeptDTO.java new file mode 100644 index 000000000..65c012faf --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/DeptDTO.java @@ -0,0 +1,37 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 部门 + * + * @author AprilWind + */ + +@Data +@NoArgsConstructor +public class DeptDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 父部门ID + */ + private Long parentId; + + /** + * 部门名称 + */ + private String deptName; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/PostDTO.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/PostDTO.java new file mode 100644 index 000000000..7536ee33d --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/PostDTO.java @@ -0,0 +1,46 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 岗位 + * + * @author AprilWind + */ +@Data +@NoArgsConstructor +public class PostDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 岗位ID + */ + private Long postId; + + /** + * 部门id + */ + private Long deptId; + + /** + * 岗位编码 + */ + private String postCode; + + /** + * 岗位名称 + */ + private String postName; + + /** + * 岗位类别编码 + */ + private String postCategory; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginUser.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginUser.java index c723e7668..338d4d70a 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginUser.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginUser.java @@ -1,8 +1,9 @@ package org.dromara.common.core.domain.model; -import org.dromara.common.core.domain.dto.RoleDTO; import lombok.Data; import lombok.NoArgsConstructor; +import org.dromara.common.core.domain.dto.PostDTO; +import org.dromara.common.core.domain.dto.RoleDTO; import java.io.Serial; import java.io.Serializable; @@ -111,6 +112,11 @@ public class LoginUser implements Serializable { */ private List roles; + /** + * 岗位对象 + */ + private List posts; + /** * 数据权限 当前角色ID */ diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/FormatsType.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/FormatsType.java new file mode 100644 index 000000000..8d4b6d911 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/FormatsType.java @@ -0,0 +1,146 @@ +package org.dromara.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dromara.common.core.utils.StringUtils; + +/* + * 日期格式 + * "yyyy":4位数的年份,例如:2023年表示为"2023"。 + * "yy":2位数的年份,例如:2023年表示为"23"。 + * "MM":2位数的月份,取值范围为01到12,例如:7月表示为"07"。 + * "M":不带前导零的月份,取值范围为1到12,例如:7月表示为"7"。 + * "dd":2位数的日期,取值范围为01到31,例如:22日表示为"22"。 + * "d":不带前导零的日期,取值范围为1到31,例如:22日表示为"22"。 + * "EEEE":星期的全名,例如:星期三表示为"Wednesday"。 + * "E":星期的缩写,例如:星期三表示为"Wed"。 + * "DDD" 或 "D":一年中的第几天,取值范围为001到366,例如:第200天表示为"200"。 + * 时间格式 + * "HH":24小时制的小时数,取值范围为00到23,例如:下午5点表示为"17"。 + * "hh":12小时制的小时数,取值范围为01到12,例如:下午5点表示为"05"。 + * "mm":分钟数,取值范围为00到59,例如:30分钟表示为"30"。 + * "ss":秒数,取值范围为00到59,例如:45秒表示为"45"。 + * "SSS":毫秒数,取值范围为000到999,例如:123毫秒表示为"123"。 + */ + +/** + * 日期格式与时间格式枚举 + */ +@Getter +@AllArgsConstructor +public enum FormatsType { + + /** + * 例如:2023年表示为"23" + */ + YY("yy"), + + /** + * 例如:2023年表示为"2023" + */ + YYYY("yyyy"), + + /** + * 例例如,2023年7月可以表示为 "2023-07" + */ + YYYY_MM("yyyy-MM"), + + /** + * 例如,日期 "2023年7月22日" 可以表示为 "2023-07-22" + */ + YYYY_MM_DD("yyyy-MM-dd"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023-07-22 15:30" + */ + YYYY_MM_DD_HH_MM("yyyy-MM-dd HH:mm"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023-07-22 15:30:45" + */ + YYYY_MM_DD_HH_MM_SS("yyyy-MM-dd HH:mm:ss"), + + /** + * 例如:下午3点30分45秒,表示为 "15:30:45" + */ + HH_MM_SS("HH:mm:ss"), + + /** + * 例例如,2023年7月可以表示为 "2023/07" + */ + YYYY_MM_SLASH("yyyy/MM"), + + /** + * 例如,日期 "2023年7月22日" 可以表示为 "2023/07/22" + */ + YYYY_MM_DD_SLASH("yyyy/MM/dd"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45" + */ + YYYY_MM_DD_HH_MM_SLASH("yyyy/MM/dd HH:mm"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45" + */ + YYYY_MM_DD_HH_MM_SS_SLASH("yyyy/MM/dd HH:mm:ss"), + + /** + * 例例如,2023年7月可以表示为 "2023.07" + */ + YYYY_MM_DOT("yyyy.MM"), + + /** + * 例如,日期 "2023年7月22日" 可以表示为 "2023.07.22" + */ + YYYY_MM_DD_DOT("yyyy.MM.dd"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023.07.22 15:30" + */ + YYYY_MM_DD_HH_MM_DOT("yyyy.MM.dd HH:mm"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023.07.22 15:30:45" + */ + YYYY_MM_DD_HH_MM_SS_DOT("yyyy.MM.dd HH:mm:ss"), + + /** + * 例如,2023年7月可以表示为 "202307" + */ + YYYYMM("yyyyMM"), + + /** + * 例如,2023年7月22日可以表示为 "20230722" + */ + YYYYMMDD("yyyyMMdd"), + + /** + * 例如,2023年7月22日下午3点可以表示为 "2023072215" + */ + YYYYMMDDHH("yyyyMMddHH"), + + /** + * 例如,2023年7月22日下午3点30分可以表示为 "202307221530" + */ + YYYYMMDDHHMM("yyyyMMddHHmm"), + + /** + * 例如,2023年7月22日下午3点30分45秒可以表示为 "20230722153045" + */ + YYYYMMDDHHMMSS("yyyyMMddHHmmss"); + + /** + * 时间格式 + */ + private final String timeFormat; + + public static FormatsType getFormatsType(String str) { + for (FormatsType value : values()) { + if (StringUtils.contains(str, value.getTimeFormat())) { + return value; + } + } + throw new RuntimeException("'FormatsType' not found By " + str); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DeptService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DeptService.java index db9463e79..f93d1778a 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DeptService.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DeptService.java @@ -1,5 +1,9 @@ package org.dromara.common.core.service; +import org.dromara.common.core.domain.dto.DeptDTO; + +import java.util.List; + /** * 通用 部门服务 * @@ -15,4 +19,19 @@ public interface DeptService { */ String selectDeptNameByIds(String deptIds); + /** + * 根据部门ID查询部门负责人 + * + * @param deptId 部门ID,用于指定需要查询的部门 + * @return 返回该部门的负责人ID + */ + Long selectDeptLeaderById(Long deptId); + + /** + * 查询部门 + * + * @return 部门列表 + */ + List selectDeptsByList(); + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PostService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PostService.java new file mode 100644 index 000000000..41d4e8308 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PostService.java @@ -0,0 +1,10 @@ +package org.dromara.common.core.service; + +/** + * 通用 岗位服务 + * + * @author AprilWind + */ +public interface PostService { + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/RoleService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/RoleService.java new file mode 100644 index 000000000..ba62c82ae --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/RoleService.java @@ -0,0 +1,10 @@ +package org.dromara.common.core.service; + +/** + * 通用 角色服务 + * + * @author AprilWind + */ +public interface RoleService { + +} 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 43aef28cb..67cd54fba 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 @@ -82,4 +82,13 @@ public interface UserService { * @return 用户 */ List selectUsersByDeptIds(List deptIds); + + /** + * 通过岗位ID查询用户 + * + * @param postIds 岗位ids + * @return 用户 + */ + List selectUsersByPostIds(List postIds); + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java index 72178a7b6..88cdbbc72 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java @@ -3,16 +3,15 @@ package org.dromara.common.core.utils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.apache.commons.lang3.time.DateFormatUtils; +import org.dromara.common.core.enums.FormatsType; +import org.dromara.common.core.exception.ServiceException; import java.lang.management.ManagementFactory; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; +import java.time.*; import java.util.Date; +import java.util.concurrent.TimeUnit; /** * 时间工具类 @@ -21,86 +20,137 @@ import java.util.Date; */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class DateUtils extends org.apache.commons.lang3.time.DateUtils { - - public static final String YYYY = "yyyy"; - - public static final String YYYY_MM = "yyyy-MM"; - - public static final String YYYY_MM_DD = "yyyy-MM-dd"; - - public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; - - public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; - private static final String[] PARSE_PATTERNS = { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; /** - * 获取当前Date型日期 + * 获取当前日期和时间 * - * @return Date() 当前日期 + * @return 当前日期和时间的 Date 对象表示 */ public static Date getNowDate() { return new Date(); } /** - * 获取当前日期, 默认格式为yyyy-MM-dd + * 获取当前日期的字符串表示,格式为YYYY-MM-DD * - * @return String + * @return 当前日期的字符串表示 */ public static String getDate() { - return dateTimeNow(YYYY_MM_DD); + return dateTimeNow(FormatsType.YYYY_MM_DD); } + /** + * 获取当前日期的字符串表示,格式为yyyyMMdd + * + * @return 当前日期的字符串表示 + */ + public static String getCurrentDate() { + return DateFormatUtils.format(new Date(), FormatsType.YYYYMMDD.getTimeFormat()); + } + + /** + * 获取当前日期的路径格式字符串,格式为"yyyy/MM/dd" + * + * @return 当前日期的路径格式字符串 + */ + public static String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, FormatsType.YYYY_MM_DD_SLASH.getTimeFormat()); + } + + /** + * 获取当前时间的字符串表示,格式为YYYY-MM-DD HH:MM:SS + * + * @return 当前时间的字符串表示 + */ public static String getTime() { - return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + return dateTimeNow(FormatsType.YYYY_MM_DD_HH_MM_SS); } + /** + * 获取当前时间的字符串表示,格式为 "HH:MM:SS" + * + * @return 当前时间的字符串表示,格式为 "HH:MM:SS" + */ + public static String getTimeWithHourMinuteSecond() { + return dateTimeNow(FormatsType.HH_MM_SS); + } + + /** + * 获取当前日期和时间的字符串表示,格式为YYYYMMDDHHMMSS + * + * @return 当前日期和时间的字符串表示 + */ public static String dateTimeNow() { - return dateTimeNow(YYYYMMDDHHMMSS); + return dateTimeNow(FormatsType.YYYYMMDDHHMMSS); } - public static String dateTimeNow(final String format) { + /** + * 获取当前日期和时间的指定格式的字符串表示 + * + * @param format 日期时间格式,例如"YYYY-MM-DD HH:MM:SS" + * @return 当前日期和时间的字符串表示 + */ + public static String dateTimeNow(final FormatsType format) { return parseDateToStr(format, new Date()); } - public static String dateTime(final Date date) { - return parseDateToStr(YYYY_MM_DD, date); + /** + * 将指定日期格式化为 YYYY-MM-DD 格式的字符串 + * + * @param date 要格式化的日期对象 + * @return 格式化后的日期字符串 + */ + public static String formatDate(final Date date) { + return parseDateToStr(FormatsType.YYYY_MM_DD, date); } - public static String parseDateToStr(final String format, final Date date) { - return new SimpleDateFormat(format).format(date); + /** + * 将指定日期格式化为 YYYY-MM-DD HH:MM:SS 格式的字符串 + * + * @param date 要格式化的日期对象 + * @return 格式化后的日期时间字符串 + */ + public static String formatDateTime(final Date date) { + return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, date); } - public static Date dateTime(final String format, final String ts) { + /** + * 将指定日期按照指定格式进行格式化 + * + * @param format 要使用的日期时间格式,例如"YYYY-MM-DD HH:MM:SS" + * @param date 要格式化的日期对象 + * @return 格式化后的日期时间字符串 + */ + public static String parseDateToStr(final FormatsType format, final Date date) { + return new SimpleDateFormat(format.getTimeFormat()).format(date); + } + + /** + * 将指定格式的日期时间字符串转换为 Date 对象 + * + * @param format 要解析的日期时间格式,例如"YYYY-MM-DD HH:MM:SS" + * @param ts 要解析的日期时间字符串 + * @return 解析后的 Date 对象 + * @throws RuntimeException 如果解析过程中发生异常 + */ + public static Date parseDateTime(final FormatsType format, final String ts) { try { - return new SimpleDateFormat(format).parse(ts); + return new SimpleDateFormat(format.getTimeFormat()).parse(ts); } catch (ParseException e) { throw new RuntimeException(e); } } /** - * 日期路径 即年/月/日 如2018/08/08 - */ - public static String datePath() { - Date now = new Date(); - return DateFormatUtils.format(now, "yyyy/MM/dd"); - } - - /** - * 日期路径 即年/月/日 如20180808 - */ - public static String dateTime() { - Date now = new Date(); - return DateFormatUtils.format(now, "yyyyMMdd"); - } - - /** - * 日期型字符串转化为日期 格式 + * 将对象转换为日期对象 + * + * @param str 要转换的对象,通常是字符串 + * @return 转换后的日期对象,如果转换失败或输入为null,则返回null */ public static Date parseDate(Object str) { if (str == null) { @@ -115,6 +165,8 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils { /** * 获取服务器启动时间 + * + * @return 服务器启动时间的 Date 对象表示 */ public static Date getServerStartDate() { long time = ManagementFactory.getRuntimeMXBean().getStartTime(); @@ -122,35 +174,66 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils { } /** - * 计算相差天数 + * 计算两个日期之间的天数差(以毫秒为单位) + * + * @param date1 第一个日期 + * @param date2 第二个日期 + * @return 两个日期之间的天数差的绝对值 */ public static int differentDaysByMillisecond(Date date1, Date date2) { return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); } /** - * 计算两个时间差 + * 计算两个日期之间的时间差,并以天、小时和分钟的格式返回 + * + * @param endDate 结束日期 + * @param nowDate 当前日期 + * @return 表示时间差的字符串,格式为"天 小时 分钟" */ public static String getDatePoor(Date endDate, Date nowDate) { - long nd = 1000 * 24 * 60 * 60; - long nh = 1000 * 60 * 60; - long nm = 1000 * 60; - // long ns = 1000; - // 获得两个时间的毫秒时间差异 - long diff = endDate.getTime() - nowDate.getTime(); - // 计算差多少天 - long day = diff / nd; - // 计算差多少小时 - long hour = diff % nd / nh; - // 计算差多少分钟 - long min = diff % nd % nh / nm; - // 计算差多少秒//输出结果 - // long sec = diff % nd % nh % nm / ns; - return day + "天" + hour + "小时" + min + "分钟"; + long diffInMillis = endDate.getTime() - nowDate.getTime(); + long day = TimeUnit.MILLISECONDS.toDays(diffInMillis); + long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24; + long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60; + return String.format("%d天 %d小时 %d分钟", day, hour, min); } /** - * 增加 LocalDateTime ==> Date + * 计算两个时间点的差值(天、小时、分钟、秒),当值为0时不显示该单位 + * + * @param endDate 结束时间 + * @param nowDate 当前时间 + * @return 时间差字符串,格式为 "x天 x小时 x分钟 x秒",若为 0 则不显示 + */ + public static String getTimeDifference(Date endDate, Date nowDate) { + long diffInMillis = endDate.getTime() - nowDate.getTime(); + long day = TimeUnit.MILLISECONDS.toDays(diffInMillis); + long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24; + long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60; + long sec = TimeUnit.MILLISECONDS.toSeconds(diffInMillis) % 60; + // 构建时间差字符串,条件是值不为0才显示 + StringBuilder result = new StringBuilder(); + if (day > 0) { + result.append(String.format("%d天 ", day)); + } + if (hour > 0) { + result.append(String.format("%d小时 ", hour)); + } + if (min > 0) { + result.append(String.format("%d分钟 ", min)); + } + if (sec > 0) { + result.append(String.format("%d秒", sec)); + } + return result.length() > 0 ? result.toString().trim() : "0秒"; + } + + /** + * 将 LocalDateTime 对象转换为 Date 对象 + * + * @param temporalAccessor 要转换的 LocalDateTime 对象 + * @return 转换后的 Date 对象 */ public static Date toDate(LocalDateTime temporalAccessor) { ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); @@ -158,11 +241,46 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils { } /** - * 增加 LocalDate ==> Date + * 将 LocalDate 对象转换为 Date 对象 + * + * @param temporalAccessor 要转换的 LocalDate 对象 + * @return 转换后的 Date 对象 */ public static Date toDate(LocalDate temporalAccessor) { LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); return Date.from(zdt.toInstant()); } + + /** + * 校验日期范围 + * + * @param startDate 开始日期 + * @param endDate 结束日期 + * @param maxValue 最大时间跨度的限制值 + * @param unit 时间跨度的单位,可选择 "DAYS"、"HOURS" 或 "MINUTES" + */ + public static void validateDateRange(Date startDate, Date endDate, int maxValue, TimeUnit unit) { + // 校验结束日期不能早于开始日期 + if (endDate.before(startDate)) { + throw new ServiceException("结束日期不能早于开始日期"); + } + + // 计算时间跨度 + long diffInMillis = endDate.getTime() - startDate.getTime(); + + // 根据单位转换时间跨度 + long diff = switch (unit) { + case DAYS -> TimeUnit.MILLISECONDS.toDays(diffInMillis); + case HOURS -> TimeUnit.MILLISECONDS.toHours(diffInMillis); + case MINUTES -> TimeUnit.MILLISECONDS.toMinutes(diffInMillis); + default -> throw new IllegalArgumentException("不支持的时间单位"); + }; + + // 校验时间跨度不超过最大限制 + if (diff > maxValue) { + throw new ServiceException("最大时间跨度为 " + maxValue + " " + unit.toString().toLowerCase()); + } + } + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java index fcf9fb4b6..1020c81eb 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java @@ -15,7 +15,7 @@ public class SqlUtil { /** * 定义常用的 sql关键字 */ - public static String SQL_REGEX = "and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()"; + public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()"; /** * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelNotation.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelNotation.java new file mode 100644 index 000000000..f358afcd6 --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelNotation.java @@ -0,0 +1,24 @@ +package org.dromara.common.excel.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 批注 + * @author guzhouyanyu + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExcelNotation { + + /** + * col index + */ + int index() default -1; + /** + * 批注内容 + */ + String value() default ""; +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelRequired.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelRequired.java new file mode 100644 index 000000000..15784e140 --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelRequired.java @@ -0,0 +1,26 @@ +package org.dromara.common.excel.annotation; + +import org.apache.poi.ss.usermodel.IndexedColors; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 是否必填 + * @author guzhouyanyu + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExcelRequired { + + /** + * col index + */ + int index() default -1; + /** + * 字体颜色 + */ + IndexedColors fontColor() default IndexedColors.RED; +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java new file mode 100644 index 000000000..a2aa4951b --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java @@ -0,0 +1,135 @@ +package org.dromara.common.excel.handler; + +import cn.hutool.core.collection.CollUtil; +import com.alibaba.excel.metadata.data.DataFormatData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.util.StyleUtil; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFRichTextString; +import org.dromara.common.core.utils.reflect.ReflectUtils; +import org.dromara.common.excel.annotation.ExcelNotation; +import org.dromara.common.excel.annotation.ExcelRequired; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +/** + * 批注、必填 + * + * @author guzhouyanyu + */ +public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler { + + /** + * 批注 + */ + private final Map notationMap; + + /** + * 头列字体颜色 + */ + private final Map headColumnMap; + + + public DataWriteHandler(Class clazz) { + notationMap = getNotationMap(clazz); + headColumnMap = getRequiredMap(clazz); + } + + @Override + public void afterCellDispose(CellWriteHandlerContext context) { + if (CollUtil.isEmpty(notationMap) && CollUtil.isEmpty(headColumnMap)) { + return; + } + WriteCellData cellData = context.getFirstCellData(); + WriteCellStyle writeCellStyle = cellData.getOrCreateStyle(); + + DataFormatData dataFormatData = new DataFormatData(); + // 单元格设置为文本格式 + dataFormatData.setIndex((short) 49); + writeCellStyle.setDataFormatData(dataFormatData); + + if (context.getHead()) { + Cell cell = context.getCell(); + WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder(); + Sheet sheet = writeSheetHolder.getSheet(); + Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); + Drawing drawing = sheet.createDrawingPatriarch(); + // 设置标题字体样式 + WriteFont headWriteFont = new WriteFont(); + // 加粗 + headWriteFont.setBold(true); + if (CollUtil.isNotEmpty(headColumnMap) && headColumnMap.containsKey(cell.getColumnIndex())) { + // 设置字体颜色 + headWriteFont.setColor(headColumnMap.get(cell.getColumnIndex())); + } + writeCellStyle.setWriteFont(headWriteFont); + CellStyle cellStyle = StyleUtil.buildCellStyle(workbook, null, writeCellStyle); + cell.setCellStyle(cellStyle); + + if (CollUtil.isNotEmpty(notationMap) && notationMap.containsKey(cell.getColumnIndex())) { + // 批注内容 + String notationContext = notationMap.get(cell.getColumnIndex()); + // 创建绘图对象 + Comment comment = drawing.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), 0, (short) 5, 5)); + comment.setString(new XSSFRichTextString(notationContext)); + cell.setCellComment(comment); + } + } + } + + /** + * 获取必填列 + */ + private static Map getRequiredMap(Class clazz) { + Map requiredMap = new HashMap<>(); + Field[] fields = clazz.getDeclaredFields(); + // 检查 fields 数组是否为空 + if (fields.length == 0) { + return requiredMap; + } + Field[] filteredFields = ReflectUtils.getFields(clazz, field -> !"serialVersionUID".equals(field.getName())); + + for (int i = 0; i < filteredFields.length; i++) { + Field field = filteredFields[i]; + if (!field.isAnnotationPresent(ExcelRequired.class)) { + continue; + } + ExcelRequired excelRequired = field.getAnnotation(ExcelRequired.class); + int columnIndex = excelRequired.index() == -1 ? i : excelRequired.index(); + requiredMap.put(columnIndex, excelRequired.fontColor().getIndex()); + } + return requiredMap; + } + + /** + * 获取批注 + */ + private static Map getNotationMap(Class clazz) { + Map notationMap = new HashMap<>(); + Field[] fields = clazz.getDeclaredFields(); + // 检查 fields 数组是否为空 + if (fields.length == 0) { + return notationMap; + } + Field[] filteredFields = ReflectUtils.getFields(clazz, field -> !"serialVersionUID".equals(field.getName())); + for (int i = 0; i < filteredFields.length; i++) { + Field field = filteredFields[i]; + if (!field.isAnnotationPresent(ExcelNotation.class)) { + continue; + } + ExcelNotation excelNotation = field.getAnnotation(ExcelNotation.class); + int columnIndex = excelNotation.index() == -1 ? i : excelNotation.index(); + notationMap.put(columnIndex, excelNotation.value()); + } + return notationMap; + } +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java index a6c14ad51..b22e6f987 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java @@ -18,6 +18,7 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.file.FileUtils; import org.dromara.common.excel.convert.ExcelBigNumberConvert; import org.dromara.common.excel.core.*; +import org.dromara.common.excel.handler.DataWriteHandler; import java.io.IOException; import java.io.InputStream; @@ -191,6 +192,7 @@ public class ExcelUtil { .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) + .registerWriteHandler(new DataWriteHandler(clazz)) .sheet(sheetName); if (merge) { // 合并处理器 @@ -211,7 +213,7 @@ public class ExcelUtil { * @param data 模板需要的数据 * @param response 响应体 */ - public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { + public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { try { resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); @@ -230,20 +232,21 @@ public class ExcelUtil { * @param data 模板需要的数据 * @param os 输出流 */ - public static void exportTemplate(List data, String templatePath, OutputStream os) { + public static void exportTemplate(List data, String templatePath, OutputStream os) { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) .autoCloseStream(false) // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) + .registerWriteHandler(new DataWriteHandler(data.get(0).getClass())) .build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } // 单表多数据导出 模板格式为 {.属性} - for (Object d : data) { + for (T d : data) { excelWriter.fill(d, writeSheet); } excelWriter.finish(); @@ -299,6 +302,9 @@ public class ExcelUtil { * @param os 输出流 */ public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) @@ -307,9 +313,6 @@ public class ExcelUtil { .registerConverter(new ExcelBigNumberConvert()) .build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } for (Map.Entry map : data.entrySet()) { // 设置列表后续还有数据 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); @@ -333,6 +336,9 @@ public class ExcelUtil { * @param os 输出流 */ public static void exportTemplateMultiSheet(List> data, String templatePath, OutputStream os) { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) @@ -340,9 +346,6 @@ public class ExcelUtil { // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) .build(); - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } for (int i = 0; i < data.size(); i++) { WriteSheet writeSheet = EasyExcel.writerSheet(i).build(); for (Map.Entry map : data.get(i).entrySet()) { diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/BigNumberSerializer.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/BigNumberSerializer.java index f2a7c2d8c..8752353c5 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/BigNumberSerializer.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/BigNumberSerializer.java @@ -32,7 +32,7 @@ public class BigNumberSerializer extends NumberSerializer { @Override public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException { - // 超出范围 序列化位字符串 + // 超出范围 序列化为字符串 if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { super.serialize(value, gen, provider); } else { 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 1e8d619fa..00c26912e 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 @@ -2,6 +2,7 @@ package org.dromara.common.mybatis.config; import cn.hutool.core.net.NetUtil; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler; import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; @@ -13,6 +14,7 @@ import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.mybatis.aspect.DataPermissionAspect; import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler; import org.dromara.common.mybatis.handler.MybatisExceptionHandler; +import org.dromara.common.mybatis.handler.PlusPostInitTableInfoHandler; import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.BeansException; @@ -105,6 +107,14 @@ public class MybatisPlusConfig { return new MybatisExceptionHandler(); } + /** + * 初始化表对象处理器 + */ + @Bean + public PostInitTableInfoHandler postInitTableInfoHandler() { + return new PlusPostInitTableInfoHandler(); + } + /** * PaginationInnerInterceptor 分页插件,自动识别数据库类型 * https://baomidou.com/pages/97710a/ 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 3f7797d6b..1d5c3c9da 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 @@ -119,4 +119,9 @@ public class PageQuery implements Serializable { return (pageNum - 1) * pageSize; } + public PageQuery(Integer pageSize, Integer pageNum) { + this.pageSize = pageSize; + this.pageNum = pageNum; + } + } 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 c529c5332..783d3526e 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 @@ -3,6 +3,7 @@ package org.dromara.common.mybatis.handler; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Expression; @@ -28,9 +29,7 @@ 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.*; import org.springframework.expression.common.TemplateParserContext; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -130,7 +129,9 @@ public class PlusDataPermissionHandler { joinStr = " " + dataPermission.joinStr() + " "; } LoginUser user = DataPermissionHelper.getVariable("user"); - StandardEvaluationContext context = new StandardEvaluationContext(); + Object defaultValue = "-1"; + NullSafeStandardEvaluationContext context = new NullSafeStandardEvaluationContext(defaultValue); + context.addPropertyAccessor(new NullSafePropertyAccessor(context.getPropertyAccessors().get(0), defaultValue)); context.setBeanResolver(beanResolver); DataPermissionHelper.getContext().forEach(context::setVariable); Set conditions = new HashSet<>(); @@ -257,4 +258,64 @@ public class PlusDataPermissionHandler { return getDataPermission(mapperId) == null; } + /** + * 对所有null变量找不到的变量返回默认值 + */ + @AllArgsConstructor + private static class NullSafeStandardEvaluationContext extends StandardEvaluationContext { + + private final Object defaultValue; + + @Override + public Object lookupVariable(String name) { + Object obj = super.lookupVariable(name); + // 如果读取到的值是 null,则返回默认值 + if (obj == null) { + return defaultValue; + } + return obj; + } + + } + + /** + * 对所有null变量找不到的变量返回默认值 委托模式 将不需要处理的方法委托给原处理器 + */ + @AllArgsConstructor + private static class NullSafePropertyAccessor implements PropertyAccessor { + + private final PropertyAccessor delegate; + private final Object defaultValue; + + @Override + public Class[] getSpecificTargetClasses() { + return delegate.getSpecificTargetClasses(); + } + + @Override + public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { + return delegate.canRead(context, target, name); + } + + @Override + public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { + TypedValue value = delegate.read(context, target, name); + // 如果读取到的值是 null,则返回默认值 + if (value.getValue() == null) { + return new TypedValue(defaultValue); + } + return value; + } + + @Override + public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { + return delegate.canWrite(context, target, name); + } + + @Override + public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException { + delegate.write(context, target, name, newValue); + } + } + } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusPostInitTableInfoHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusPostInitTableInfoHandler.java new file mode 100644 index 000000000..60ca20b4b --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusPostInitTableInfoHandler.java @@ -0,0 +1,27 @@ +package org.dromara.common.mybatis.handler; + +import cn.hutool.core.convert.Convert; +import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import org.apache.ibatis.session.Configuration; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.reflect.ReflectUtils; + +/** + * 修改表信息初始化方式 + * 目前用于全局修改是否使用逻辑删除 + * + * @author Lion Li + */ +public class PlusPostInitTableInfoHandler implements PostInitTableInfoHandler { + + @Override + public void postTableInfo(TableInfo tableInfo, Configuration configuration) { + String flag = SpringUtils.getProperty("mybatis-plus.enableLogicDelete", "true"); + // 只有关闭时 统一设置false 为true时mp自动判断不处理 + if (!Convert.toBool(flag)) { + ReflectUtils.setFieldValue(tableInfo, "withLogicDelete", false); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java new file mode 100644 index 000000000..3c2f70329 --- /dev/null +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java @@ -0,0 +1,165 @@ +package org.dromara.common.redis.utils; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.redisson.api.RIdGenerator; +import org.redisson.api.RedissonClient; + +import java.time.Duration; + +/** + * 发号器工具类 + * + * @author 秋辞未寒 + * @date 2024-12-10 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SequenceUtils { + + /** + * 默认初始值 + */ + public static final Long DEFAULT_INIT_VALUE = 1L; + /** + * 默认步长 + */ + public static final Long DEFAULT_STEP_VALUE = 1L; + /** + * 默认过期时间-天 + */ + public static final Duration DEFAULT_EXPIRE_TIME_DAY = Duration.ofDays(1); + /** + * 默认过期时间-分钟 + */ + public static final Duration DEFAULT_EXPIRE_TIME_MINUTE = Duration.ofMinutes(1); + + /** + * 获取Redisson客户端实例 + */ + private static final RedissonClient REDISSON_CLIENT = SpringUtils.getBean(RedissonClient.class); + + /** + * 获取ID生成器 + * + * @param key 业务key + * @param expireTime 过期时间 + * @param initValue ID初始值 + * @param stepValue ID步长 + * @return ID生成器 + */ + private static RIdGenerator getIdGenerator(String key, Duration expireTime, Long initValue, Long stepValue) { + if (initValue == null || initValue <= 0) { + initValue = DEFAULT_INIT_VALUE; + } + if (stepValue == null || stepValue <= 0) { + stepValue = DEFAULT_STEP_VALUE; + } + RIdGenerator idGenerator = REDISSON_CLIENT.getIdGenerator(key); + // 设置过期时间 + idGenerator.expire(expireTime); + // 设置初始值和步长 + idGenerator.tryInit(initValue, stepValue); + return idGenerator; + } + + /** + * 获取指定业务key的唯一id + * + * @param key 业务key + * @param expireTime 过期时间 + * @param initValue ID初始值 + * @param stepValue ID步长 + * @return 唯一id + */ + public static long nextId(String key, Duration expireTime, Long initValue, Long stepValue) { + return getIdGenerator(key, expireTime, initValue, stepValue).nextId(); + } + + /** + * 获取指定业务key的唯一id字符串 + * + * @param key 业务key + * @param expireTime 过期时间 + * @param initValue ID初始值 + * @param stepValue ID步长 + * @return 唯一id + */ + public static String nextIdStr(String key, Duration expireTime, Long initValue, Long stepValue) { + return String.valueOf(nextId(key, expireTime, initValue, stepValue)); + } + + /** + * 获取指定业务key的唯一id (ID初始值=1,ID步长=1) + * + * @param key 业务key + * @param expireTime 过期时间 + * @return 唯一id + */ + public static long nextId(String key, Duration expireTime) { + return getIdGenerator(key, expireTime, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); + } + + /** + * 获取指定业务key的唯一id字符串 (ID初始值=1,ID步长=1) + * + * @param key 业务key + * @param expireTime 过期时间 + * @return 唯一id + */ + public static String nextIdStr(String key, Duration expireTime) { + return String.valueOf(nextId(key, expireTime)); + } + + /** + * 获取 yyyyMMdd 开头的唯一id + * + * @return 唯一id + */ + public static String nextIdDate() { + return nextIdDate(""); + } + + /** + * 获取 prefix + yyyyMMdd 开头的唯一id + * + * @param prefix 业务前缀 + * @return 唯一id + */ + public static String nextIdDate(String prefix) { + // 前缀+日期 构建 prefixKey + String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMATTER)); + // 获取下一个id + long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_DAY, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); + // 返回完整id + return StringUtils.format("{}{}", prefixKey, nextId); + } + + /** + * 获取 yyyyMMddHHmmss 开头的唯一id + * + * @return 唯一id + */ + public static String nextIdDateTime() { + return nextIdDateTime(""); + } + + /** + * 获取 prefix + yyyyMMddHHmmss 开头的唯一id + * + * @param prefix 业务前缀 + * @return 唯一id + */ + public static String nextIdDateTime(String prefix) { + // 前缀+日期时间 构建 prefixKey + String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_FORMATTER)); + // 获取下一个id + long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_MINUTE, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); + // 返回完整id + return StringUtils.format("{}{}", prefixKey, nextId); + } + +} 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 cdc5684fa..19557e131 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 @@ -88,6 +88,13 @@ public class LoginHelper { return Convert.toLong(getExtra(USER_KEY)); } + /** + * 获取用户id + */ + public static String getUserIdStr() { + return Convert.toStr(getExtra(USER_KEY)); + } + /** * 获取用户账户 */ diff --git a/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveStrategy.java b/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveStrategy.java index 995dcbd96..7af5cee9e 100644 --- a/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveStrategy.java +++ b/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveStrategy.java @@ -80,12 +80,12 @@ public enum SensitiveStrategy { FIRST_MASK(DesensitizedUtil::firstMask), /** - * 清空为null + * 清空为"" */ CLEAR(s -> DesensitizedUtil.clear()), /** - * 清空为"" + * 清空为null */ CLEAR_TO_NULL(s -> DesensitizedUtil.clearToNull()); diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/maxkey/AuthMaxKeyRequest.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/maxkey/AuthMaxKeyRequest.java index b95c19ee8..97774ac5d 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/maxkey/AuthMaxKeyRequest.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/maxkey/AuthMaxKeyRequest.java @@ -30,7 +30,7 @@ public class AuthMaxKeyRequest extends AuthDefaultRequest { } @Override - protected AuthToken getAccessToken(AuthCallback authCallback) { + public AuthToken getAccessToken(AuthCallback authCallback) { String body = doPostAuthorizationCode(authCallback.getCode()); Dict object = JsonUtils.parseMap(body); // oauth/token 验证异常 @@ -51,7 +51,7 @@ public class AuthMaxKeyRequest extends AuthDefaultRequest { } @Override - protected AuthUser getUserInfo(AuthToken authToken) { + public AuthUser getUserInfo(AuthToken authToken) { String body = doGetUserInfo(authToken); Dict object = JsonUtils.parseMap(body); // oauth/token 验证异常 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 index 13649f9fd..31bb955d4 100644 --- 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 @@ -1,7 +1,10 @@ package org.dromara.common.social.topiam; +import cn.hutool.core.codec.Base64; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; import com.xkcoding.http.support.HttpHeader; import lombok.extern.slf4j.Slf4j; import me.zhyd.oauth.cache.AuthStateCache; @@ -41,7 +44,7 @@ public class AuthTopIamRequest extends AuthDefaultRequest { } @Override - protected AuthToken getAccessToken(AuthCallback authCallback) { + public AuthToken getAccessToken(AuthCallback authCallback) { String body = doPostAuthorizationCode(authCallback.getCode()); Dict object = JsonUtils.parseMap(body); checkResponse(object); @@ -55,7 +58,7 @@ public class AuthTopIamRequest extends AuthDefaultRequest { } @Override - protected AuthUser getUserInfo(AuthToken authToken) { + public AuthUser getUserInfo(AuthToken authToken) { String body = doGetUserInfo(authToken); Dict object = JsonUtils.parseMap(body); checkResponse(object); @@ -70,6 +73,16 @@ public class AuthTopIamRequest extends AuthDefaultRequest { .build(); } + @Override + protected String doPostAuthorizationCode(String code) { + HttpRequest request = HttpRequest.post(source.accessToken()) + .header("Authorization", "Basic " + Base64.encode("%s:%s".formatted(config.getClientId(), config.getClientSecret()))) + .form("grant_type", "authorization_code") + .form("code", code) + .form("redirect_uri", config.getRedirectUri()); + HttpResponse response = request.execute(); + return response.body(); + } @Override protected String doGetUserInfo(AuthToken authToken) { @@ -86,7 +99,7 @@ public class AuthTopIamRequest extends AuthDefaultRequest { .build(); } - public static void checkResponse(Dict object) { + private static void checkResponse(Dict object) { // oauth/token 验证异常 if (object.containsKey("error")) { throw new AuthException(object.getStr("error_description")); diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java index fd22db694..a9a367627 100644 --- a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java @@ -1,5 +1,6 @@ package org.dromara.common.sse.core; +import cn.hutool.core.map.MapUtil; import lombok.extern.slf4j.Slf4j; import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.sse.dto.SseMessageDto; @@ -65,7 +66,7 @@ public class SseEmitterManager { */ public void disconnect(Long userId, String token) { Map emitters = USER_TOKEN_EMITTERS.get(userId); - if (emitters != null) { + if (MapUtil.isNotEmpty(emitters)) { try { SseEmitter sseEmitter = emitters.get(token); sseEmitter.send(SseEmitter.event().comment("disconnected")); @@ -73,6 +74,8 @@ public class SseEmitterManager { } catch (Exception ignore) { } emitters.remove(token); + } else { + USER_TOKEN_EMITTERS.remove(userId); } } @@ -93,7 +96,7 @@ public class SseEmitterManager { */ public void sendMessage(Long userId, String message) { Map emitters = USER_TOKEN_EMITTERS.get(userId); - if (emitters != null) { + if (MapUtil.isNotEmpty(emitters)) { for (Map.Entry entry : emitters.entrySet()) { try { entry.getValue().send(SseEmitter.event() @@ -103,6 +106,8 @@ public class SseEmitterManager { emitters.remove(entry.getKey()); } } + } else { + USER_TOKEN_EMITTERS.remove(userId); } } diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/utils/SseMessageUtils.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/utils/SseMessageUtils.java index c6abdc8fd..586103417 100644 --- a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/utils/SseMessageUtils.java +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/utils/SseMessageUtils.java @@ -16,7 +16,14 @@ import org.dromara.common.sse.dto.SseMessageDto; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class SseMessageUtils { - private final static SseEmitterManager MANAGER = SpringUtils.getBean(SseEmitterManager.class); + private final static Boolean SSE_ENABLE = SpringUtils.getProperty("sse.enabled", Boolean.class, true); + private static SseEmitterManager MANAGER; + + static { + if (isEnable() && MANAGER == null) { + MANAGER = SpringUtils.getBean(SseEmitterManager.class); + } + } /** * 向指定的WebSocket会话发送消息 @@ -25,6 +32,9 @@ public class SseMessageUtils { * @param message 要发送的消息内容 */ public static void sendMessage(Long userId, String message) { + if (!isEnable()) { + return; + } MANAGER.sendMessage(userId, message); } @@ -34,6 +44,9 @@ public class SseMessageUtils { * @param message 要发送的消息内容 */ public static void sendMessage(String message) { + if (!isEnable()) { + return; + } MANAGER.sendMessage(message); } @@ -43,6 +56,9 @@ public class SseMessageUtils { * @param sseMessageDto 要发布的SSE消息对象 */ public static void publishMessage(SseMessageDto sseMessageDto) { + if (!isEnable()) { + return; + } MANAGER.publishMessage(sseMessageDto); } @@ -52,7 +68,17 @@ public class SseMessageUtils { * @param message 要发布的消息内容 */ public static void publishAll(String message) { + if (!isEnable()) { + return; + } MANAGER.publishAll(message); } + /** + * 是否开启 + */ + public static Boolean isEnable() { + return SSE_ENABLE; + } + } diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback-plus.xml b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback-plus.xml index 16bb9376b..45cbbba40 100644 --- a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback-plus.xml +++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback-plus.xml @@ -4,7 +4,7 @@ logback + value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/> @@ -31,4 +31,4 @@ - \ No newline at end of file + diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/logback-plus.xml b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/logback-plus.xml index a40262c9f..be588110c 100644 --- a/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/logback-plus.xml +++ b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/logback-plus.xml @@ -2,7 +2,7 @@ + value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/> diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java index 016c2f7dc..e7ea8075c 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java @@ -2,6 +2,8 @@ package org.dromara.demo.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelNotation; +import org.dromara.common.excel.annotation.ExcelRequired; import org.dromara.common.translation.annotation.Translation; import org.dromara.common.translation.constant.TransConstant; import org.dromara.demo.domain.TestDemo; @@ -36,30 +38,35 @@ public class TestDemoVo implements Serializable { /** * 部门id */ + @ExcelRequired @ExcelProperty(value = "部门id") private Long deptId; /** * 用户id */ + @ExcelRequired @ExcelProperty(value = "用户id") private Long userId; /** * 排序号 */ + @ExcelRequired @ExcelProperty(value = "排序号") private Integer orderNum; /** * key键 */ + @ExcelNotation(value = "测试key") @ExcelProperty(value = "key键") private String testKey; /** * 值 */ + @ExcelNotation(value = "测试value") @ExcelProperty(value = "值") private String value; 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 893b381e3..5f187cb91 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 @@ -98,8 +98,8 @@ public class SysProfileController extends BaseController { if (BCrypt.checkpw(bo.getNewPassword(), password)) { return R.fail("新密码不能与旧密码相同"); } - - if (userService.resetUserPwd(user.getUserId(), BCrypt.hashpw(bo.getNewPassword())) > 0) { + int rows = DataPermissionHelper.ignore(() -> userService.resetUserPwd(user.getUserId(), BCrypt.hashpw(bo.getNewPassword()))); + if (rows > 0) { return R.ok(); } return R.fail("修改密码异常,请联系管理员"); @@ -121,7 +121,8 @@ public class SysProfileController extends BaseController { } SysOssVo oss = ossService.upload(avatarfile); String avatar = oss.getUrl(); - if (userService.updateUserAvatar(LoginHelper.getUserId(), oss.getOssId())) { + boolean updateSuccess = DataPermissionHelper.ignore(() -> userService.updateUserAvatar(LoginHelper.getUserId(), oss.getOssId())); + if (updateSuccess) { AvatarVo avatarVo = new AvatarVo(); avatarVo.setImgUrl(avatar); return R.ok(avatarVo); 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 3751b23be..a760d497e 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 @@ -25,6 +25,14 @@ public interface ISysPostService { */ List selectPostList(SysPostBo post); + /** + * 查询用户所属岗位组 + * + * @param userId 用户ID + * @return 岗位ID + */ + List selectPostsByUserId(Long userId); + /** * 查询所有岗位 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java index 470646f35..12a507203 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java @@ -38,7 +38,7 @@ public class SysDataScopeServiceImpl implements ISysDataScopeService { * @param roleId 角色Id * @return 部门Id组 */ - @Cacheable(cacheNames = CacheNames.SYS_ROLE_CUSTOM, key = "#roleId") + @Cacheable(cacheNames = CacheNames.SYS_ROLE_CUSTOM, key = "#roleId", condition = "#roleId != null") @Override public String getRoleCustom(Long roleId) { if (ObjectUtil.isNull(roleId)) { @@ -60,7 +60,7 @@ public class SysDataScopeServiceImpl implements ISysDataScopeService { * @param deptId 部门Id * @return 部门Id组 */ - @Cacheable(cacheNames = CacheNames.SYS_DEPT_AND_CHILD, key = "#deptId") + @Cacheable(cacheNames = CacheNames.SYS_DEPT_AND_CHILD, key = "#deptId", condition = "#deptId != null") @Override public String getDeptAndChild(Long deptId) { if (ObjectUtil.isNull(deptId)) { 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 1d6da45da..0199ec719 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 @@ -1,5 +1,6 @@ package org.dromara.system.service.impl; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.tree.Tree; @@ -10,6 +11,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.constant.SystemConstants; +import org.dromara.common.core.domain.dto.DeptDTO; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.service.DeptService; import org.dromara.common.core.utils.*; @@ -67,8 +69,6 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ @Override public List> selectDeptTreeList(SysDeptBo bo) { - // 只查询未禁用部门 - bo.setStatus(SystemConstants.NORMAL); LambdaQueryWrapper lqw = buildQueryWrapper(bo); List depts = baseMapper.selectDeptList(lqw); return buildDeptTreeSelect(depts); @@ -176,6 +176,31 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { return String.join(StringUtils.SEPARATOR, list); } + /** + * 根据部门ID查询部门负责人 + * + * @param deptId 部门ID,用于指定需要查询的部门 + * @return 返回该部门的负责人ID + */ + @Override + public Long selectDeptLeaderById(Long deptId) { + SysDeptVo vo = SpringUtils.getAopProxy(this).selectDeptById(deptId); + return vo.getLeader(); + } + + /** + * 查询部门 + * + * @return 部门列表 + */ + @Override + public List selectDeptsByList() { + List list = baseMapper.selectDeptList(new LambdaQueryWrapper() + .select(SysDept::getDeptId, SysDept::getDeptName, SysDept::getParentId) + .eq(SysDept::getStatus, SystemConstants.NORMAL)); + return BeanUtil.copyToList(list, DeptDTO.class); + } + /** * 根据ID查询所有子部门数(正常状态) * 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 8643f0d92..72f1a5e31 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 @@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.service.PostService; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; @@ -34,7 +35,7 @@ import java.util.List; */ @RequiredArgsConstructor @Service -public class SysPostServiceImpl implements ISysPostService { +public class SysPostServiceImpl implements ISysPostService, PostService { private final SysPostMapper baseMapper; private final SysDeptMapper deptMapper; @@ -57,6 +58,17 @@ public class SysPostServiceImpl implements ISysPostService { return baseMapper.selectVoList(buildQueryWrapper(post)); } + /** + * 查询用户所属岗位组 + * + * @param userId 用户ID + * @return 岗位ID + */ + @Override + public List selectPostsByUserId(Long userId) { + return baseMapper.selectPostsByUserId(userId); + } + /** * 根据查询条件构建查询包装器 * 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 cbca57e5d..4c007358e 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 @@ -17,6 +17,7 @@ import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.TenantConstants; import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.service.RoleService; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; @@ -47,7 +48,7 @@ import java.util.*; */ @RequiredArgsConstructor @Service -public class SysRoleServiceImpl implements ISysRoleService { +public class SysRoleServiceImpl implements ISysRoleService, RoleService { private final SysRoleMapper baseMapper; private final SysRoleMenuMapper roleMenuMapper; @@ -351,7 +352,7 @@ public class SysRoleServiceImpl implements ISysRoleService { private int insertRoleMenu(SysRoleBo role) { int rows = 1; // 新增用户与角色管理 - List list = new ArrayList(); + List list = new ArrayList<>(); for (Long menuId : role.getMenuIds()) { SysRoleMenu rm = new SysRoleMenu(); rm.setRoleId(role.getRoleId()); @@ -372,7 +373,7 @@ public class SysRoleServiceImpl implements ISysRoleService { private int insertRoleDept(SysRoleBo role) { int rows = 1; // 新增角色与部门(数据权限)管理 - List list = new ArrayList(); + List list = new ArrayList<>(); for (Long deptId : role.getDeptIds()) { SysRoleDept rd = new SysRoleDept(); rd.setRoleId(role.getRoleId()); 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 4b5758763..40811707b 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 @@ -696,4 +696,27 @@ public class SysUserServiceImpl implements ISysUserService, UserService { .in(SysUser::getDeptId, deptIds)); return BeanUtil.copyToList(list, UserDTO.class); } + + /** + * 通过岗位ID查询用户 + * + * @param postIds 岗位ids + * @return 用户 + */ + @Override + public List selectUsersByPostIds(List postIds) { + if (CollUtil.isEmpty(postIds)) { + return List.of(); + } + + // 通过岗位ID获取用户岗位信息 + List userPosts = userPostMapper.selectList( + new LambdaQueryWrapper().in(SysUserPost::getPostId, postIds)); + + // 获取用户ID列表 + Set userIds = StreamUtils.toSet(userPosts, SysUserPost::getUserId); + + return selectListByIds(new ArrayList<>(userIds)); + } + }