浏览代码

Merge remote-tracking branch 'origin/dev' into dev

xlsea 6 月之前
父节点
当前提交
4d566071db
共有 33 个文件被更改,包括 781 次插入125 次删除
  1. 1 1
      pom.xml
  2. 9 4
      ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java
  3. 23 3
      ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java
  4. 37 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/DeptDTO.java
  5. 46 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/PostDTO.java
  6. 7 1
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginUser.java
  7. 146 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/FormatsType.java
  8. 19 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DeptService.java
  9. 10 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PostService.java
  10. 10 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/RoleService.java
  11. 9 0
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java
  12. 185 67
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java
  13. 0 12
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/Threads.java
  14. 7 1
      ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/properties/MailProperties.java
  15. 5 0
      ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java
  16. 165 0
      ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java
  17. 7 0
      ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java
  18. 2 2
      ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/maxkey/AuthMaxKeyRequest.java
  19. 2 2
      ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java
  20. 2 5
      ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisCacheController.java
  21. 5 12
      ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisLockController.java
  22. 1 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java
  23. 1 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java
  24. 1 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java
  25. 8 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java
  26. 1 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java
  27. 2 2
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java
  28. 28 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java
  29. 1 2
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java
  30. 1 2
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java
  31. 13 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java
  32. 4 3
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java
  33. 23 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java

+ 1 - 1
pom.xml

@@ -36,7 +36,7 @@
         <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
         <lombok.version>1.18.34</lombok.version>
         <bouncycastle.version>1.76</bouncycastle.version>
-        <justauth.version>1.16.6</justauth.version>
+        <justauth.version>1.16.7</justauth.version>
         <!-- 离线IP地址定位库 -->
         <ip2region.version>2.7.0</ip2region.version>
 

+ 9 - 4
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<SysDeptVo> 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<SysRoleVo> roles = roleService.selectRolesByUserId(user.getUserId());
+        List<SysRoleVo> roles = roleService.selectRolesByUserId(userId);
+        List<SysPostVo> posts = postService.selectPostsByUserId(userId);
         loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
+        loginUser.setPosts(BeanUtil.copyToList(posts, PostDTO.class));
         return loginUser;
     }
 

+ 23 - 3
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<AuthUser> resp = authRequest.login(authCallback);
+        String openid, unionId;
+        if (resp.ok()) {
+            AuthToken token = resp.getData().getToken();
+            openid = token.getOpenId();
+            // 微信小程序只有关联到微信开放平台下之后才能获取到 unionId,因此unionId不一定能返回。
+            unionId = token.getUnionId();
+        } else {
+            throw new ServiceException(resp.getMsg());
+        }
         // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
         SysUserVo user = loadUserByOpenid(openid);
-
         // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
         XcxLoginUser loginUser = new XcxLoginUser();
         loginUser.setTenantId(user.getTenantId());

+ 37 - 0
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;
+
+}

+ 46 - 0
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;
+
+}

+ 7 - 1
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<RoleDTO> roles;
 
+    /**
+     * 岗位对象
+     */
+    private List<PostDTO> posts;
+
     /**
      * 数据权限 当前角色ID
      */

+ 146 - 0
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);
+    }
+}

+ 19 - 0
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<DeptDTO> selectDeptsByList();
+
 }

+ 10 - 0
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 {
+
+}

+ 10 - 0
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 {
+
+}

+ 9 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java

@@ -82,4 +82,13 @@ public interface UserService {
      * @return 用户
      */
     List<UserDTO> selectUsersByDeptIds(List<Long> deptIds);
+
+    /**
+     * 通过岗位ID查询用户
+     *
+     * @param postIds 岗位ids
+     * @return 用户
+     */
+    List<UserDTO> selectUsersByPostIds(List<Long> postIds);
+
 }

+ 185 - 67
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);
     }
 
-    public static String dateTimeNow() {
-        return dateTimeNow(YYYYMMDDHHMMSS);
+    /**
+     * 获取当前时间的字符串表示,格式为 "HH:MM:SS"
+     *
+     * @return 当前时间的字符串表示,格式为 "HH:MM:SS"
+     */
+    public static String getTimeWithHourMinuteSecond() {
+        return dateTimeNow(FormatsType.HH_MM_SS);
     }
 
-    public static String dateTimeNow(final String format) {
-        return parseDateToStr(format, new Date());
+    /**
+     * 获取当前日期和时间的字符串表示,格式为YYYYMMDDHHMMSS
+     *
+     * @return 当前日期和时间的字符串表示
+     */
+    public static String dateTimeNow() {
+        return dateTimeNow(FormatsType.YYYYMMDDHHMMSS);
     }
 
-    public static String dateTime(final Date date) {
-        return parseDateToStr(YYYY_MM_DD, date);
+    /**
+     * 获取当前日期和时间的指定格式的字符串表示
+     *
+     * @param format 日期时间格式,例如"YYYY-MM-DD HH:MM:SS"
+     * @return 当前日期和时间的字符串表示
+     */
+    public static String dateTimeNow(final FormatsType format) {
+        return parseDateToStr(format, new Date());
     }
 
-    public static String parseDateToStr(final String format, final Date date) {
-        return new SimpleDateFormat(format).format(date);
+    /**
+     * 将指定日期格式化为 YYYY-MM-DD 格式的字符串
+     *
+     * @param date 要格式化的日期对象
+     * @return 格式化后的日期字符串
+     */
+    public static String formatDate(final Date date) {
+        return parseDateToStr(FormatsType.YYYY_MM_DD, date);
     }
 
-    public static Date dateTime(final String format, final String ts) {
-        try {
-            return new SimpleDateFormat(format).parse(ts);
-        } catch (ParseException e) {
-            throw new RuntimeException(e);
-        }
+    /**
+     * 将指定日期格式化为 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);
     }
 
     /**
-     * 日期路径 即年/月/日 如2018/08/08
+     * 将指定日期按照指定格式进行格式化
+     *
+     * @param format 要使用的日期时间格式,例如"YYYY-MM-DD HH:MM:SS"
+     * @param date   要格式化的日期对象
+     * @return 格式化后的日期时间字符串
      */
-    public static String datePath() {
-        Date now = new Date();
-        return DateFormatUtils.format(now, "yyyy/MM/dd");
+    public static String parseDateToStr(final FormatsType format, final Date date) {
+        return new SimpleDateFormat(format.getTimeFormat()).format(date);
     }
 
     /**
-     * 日期路径 即年/月/日 如20180808
+     * 将指定格式的日期时间字符串转换为 Date 对象
+     *
+     * @param format 要解析的日期时间格式,例如"YYYY-MM-DD HH:MM:SS"
+     * @param ts     要解析的日期时间字符串
+     * @return 解析后的 Date 对象
+     * @throws RuntimeException 如果解析过程中发生异常
      */
-    public static String dateTime() {
-        Date now = new Date();
-        return DateFormatUtils.format(now, "yyyyMMdd");
+    public static Date parseDateTime(final FormatsType format, final String ts) {
+        try {
+            return new SimpleDateFormat(format.getTimeFormat()).parse(ts);
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     /**
-     * 日期型字符串转化为日期 格式
+     * 将对象转换为日期对象
+     *
+     * @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 + "分钟";
-    }
-
-    /**
-     * 增加 LocalDateTime ==> Date
+        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);
+    }
+
+    /**
+     * 计算两个时间点的差值(天、小时、分钟、秒),当值为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());
+        }
+    }
+
 }

+ 0 - 12
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/Threads.java

@@ -14,18 +14,6 @@ import java.util.concurrent.*;
 @Slf4j
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class Threads {
-
-    /**
-     * sleep等待,单位为毫秒
-     */
-    public static void sleep(long milliseconds) {
-        try {
-            Thread.sleep(milliseconds);
-        } catch (InterruptedException e) {
-            return;
-        }
-    }
-
     /**
      * 停止线程池
      * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.

+ 7 - 1
ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/properties/MailProperties.java

@@ -43,7 +43,13 @@ public class MailProperties {
     private String pass;
 
     /**
-     * 发送方,遵循RFC-822标准
+     * 发送方,遵循RFC-822标准<br>
+     * 发件人可以是以下形式:
+     *
+     * <pre>
+     * 1. user@xxx.xx
+     * 2.  name &lt;user@xxx.xx&gt;
+     * </pre>
      */
     private String from;
 

+ 5 - 0
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;
+    }
+
 }

+ 165 - 0
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);
+    }
+
+}

+ 7 - 0
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));
+    }
+
     /**
      * 获取用户账户
      */

+ 2 - 2
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 验证异常

+ 2 - 2
ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java

@@ -44,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);
@@ -58,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);

+ 2 - 5
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisCacheController.java

@@ -1,5 +1,6 @@
 package org.dromara.demo.controller;
 
+import cn.hutool.core.thread.ThreadUtil;
 import org.dromara.common.core.constant.CacheNames;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.redis.utils.RedisUtils;
@@ -83,11 +84,7 @@ public class RedisCacheController {
         RedisUtils.setCacheObject(key, value);
         boolean flag = RedisUtils.expire(key, Duration.ofSeconds(10));
         System.out.println("***********" + flag);
-        try {
-            Thread.sleep(11 * 1000);
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-        }
+        ThreadUtil.sleep(11 * 1000);
         Object obj = RedisUtils.getCacheObject(key);
         return R.ok(value.equals(obj));
     }

+ 5 - 12
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisLockController.java

@@ -1,5 +1,6 @@
 package org.dromara.demo.controller;
 
+import cn.hutool.core.thread.ThreadUtil;
 import com.baomidou.lock.LockInfo;
 import com.baomidou.lock.LockTemplate;
 import com.baomidou.lock.annotation.Lock4j;
@@ -33,13 +34,9 @@ public class RedisLockController {
     @Lock4j(keys = {"#key"})
     @GetMapping("/testLock4j")
     public R<String> testLock4j(String key, String value) {
-        System.out.println("start:" + key + ",time:" + LocalTime.now().toString());
-        try {
-            Thread.sleep(10000);
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-        System.out.println("end :" + key + ",time:" + LocalTime.now().toString());
+        System.out.println("start:" + key + ",time:" + LocalTime.now());
+        ThreadUtil.sleep(10000);
+        System.out.println("end :" + key + ",time:" + LocalTime.now());
         return R.ok("操作成功", value);
     }
 
@@ -54,11 +51,7 @@ public class RedisLockController {
         }
         // 获取锁成功,处理业务
         try {
-            try {
-                Thread.sleep(8000);
-            } catch (InterruptedException e) {
-                //
-            }
+            ThreadUtil.sleep(8000);
             System.out.println("执行简单方法1 , 当前线程:" + Thread.currentThread().getName());
         } finally {
             //释放锁

+ 1 - 1
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<Void> changeStatus(@RequestBody SysClientBo bo) {
-        return toAjax(sysClientService.updateUserStatus(bo.getClientId(), bo.getStatus()));
+        return toAjax(sysClientService.updateClientStatus(bo.getClientId(), bo.getStatus()));
     }
 
     /**

+ 1 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java

@@ -218,7 +218,7 @@ public class SysUserController extends BaseController {
     @GetMapping("/optionselect")
     public R<List<SysUserVo>> optionselect(@RequestParam(required = false) Long[] userIds,
                                            @RequestParam(required = false) Long deptId) {
-        return R.ok(userService.selectUserByIds(userIds == null ? null : List.of(userIds), deptId));
+        return R.ok(userService.selectUserByIds(ArrayUtil.isEmpty(userIds) ? null : List.of(userIds), deptId));
     }
 
     /**

+ 1 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java

@@ -50,7 +50,7 @@ public interface ISysClientService {
     /**
      * 修改状态
      */
-    int updateUserStatus(String clientId, String status);
+    int updateClientStatus(String clientId, String status);
 
     /**
      * 校验并批量删除客户端管理信息

+ 8 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java

@@ -25,6 +25,14 @@ public interface ISysPostService {
      */
     List<SysPostVo> selectPostList(SysPostBo post);
 
+    /**
+     * 查询用户所属岗位组
+     *
+     * @param userId 用户ID
+     * @return 岗位ID
+     */
+    List<SysPostVo> selectPostsByUserId(Long userId);
+
     /**
      * 查询所有岗位
      *

+ 1 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java

@@ -123,7 +123,7 @@ public class SysClientServiceImpl implements ISysClientService {
      */
     @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId")
     @Override
-    public int updateUserStatus(String clientId, String status) {
+    public int updateClientStatus(String clientId, String status) {
         return baseMapper.update(null,
             new LambdaUpdateWrapper<SysClient>()
                 .set(SysClient::getStatus, status)

+ 2 - 2
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)) {

+ 28 - 1
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.*;
@@ -110,7 +112,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService {
                         .setName(dept.getDeptName())
                         .setWeight(dept.getOrderNum())
                         .putExtra("disabled", SystemConstants.DISABLE.equals(dept.getStatus())));
-                Tree<Long> tree = trees.stream().filter(it -> it.getId().longValue() == d.getDeptId()).findFirst().get();
+                Tree<Long> tree = StreamUtils.findFirst(trees, it -> it.getId().longValue() == d.getDeptId());
                 treeList.add(tree);
             }
         }
@@ -174,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<DeptDTO> selectDeptsByList() {
+        List<SysDeptVo> list = baseMapper.selectDeptList(new LambdaQueryWrapper<SysDept>()
+            .select(SysDept::getDeptId, SysDept::getDeptName, SysDept::getParentId)
+            .eq(SysDept::getStatus, SystemConstants.NORMAL));
+        return BeanUtil.copyToList(list, DeptDTO.class);
+    }
+
     /**
      * 根据ID查询所有子部门数(正常状态)
      *

+ 1 - 2
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java

@@ -118,8 +118,7 @@ public class SysLogininforServiceImpl implements ISysLogininforService {
             .between(params.get("beginTime") != null && params.get("endTime") != null,
                 SysLogininfor::getLoginTime, params.get("beginTime"), params.get("endTime"));
         if (StringUtils.isBlank(pageQuery.getOrderByColumn())) {
-            pageQuery.setOrderByColumn("info_id");
-            pageQuery.setIsAsc("desc");
+            lqw.orderByDesc(SysLogininfor::getInfoId);
         }
         Page<SysLogininforVo> page = baseMapper.selectVoPage(pageQuery.build(), lqw);
         return TableDataInfo.build(page);

+ 1 - 2
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java

@@ -53,8 +53,7 @@ public class SysOperLogServiceImpl implements ISysOperLogService {
     public TableDataInfo<SysOperLogVo> selectPageOperLogList(SysOperLogBo operLog, PageQuery pageQuery) {
         LambdaQueryWrapper<SysOperLog> lqw = buildQueryWrapper(operLog);
         if (StringUtils.isBlank(pageQuery.getOrderByColumn())) {
-            pageQuery.setOrderByColumn("oper_id");
-            pageQuery.setIsAsc("desc");
+            lqw.orderByDesc(SysOperLog::getOperId);
         }
         Page<SysOperLogVo> page = baseMapper.selectVoPage(pageQuery.build(), lqw);
         return TableDataInfo.build(page);

+ 13 - 1
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<SysPostVo> selectPostsByUserId(Long userId) {
+        return baseMapper.selectPostsByUserId(userId);
+    }
+
     /**
      * 根据查询条件构建查询包装器
      *

+ 4 - 3
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<SysRoleMenu> list = new ArrayList<SysRoleMenu>();
+        List<SysRoleMenu> 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<SysRoleDept> list = new ArrayList<SysRoleDept>();
+        List<SysRoleDept> list = new ArrayList<>();
         for (Long deptId : role.getDeptIds()) {
             SysRoleDept rd = new SysRoleDept();
             rd.setRoleId(role.getRoleId());

+ 23 - 0
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<UserDTO> selectUsersByPostIds(List<Long> postIds) {
+        if (CollUtil.isEmpty(postIds)) {
+            return List.of();
+        }
+
+        // 通过岗位ID获取用户岗位信息
+        List<SysUserPost> userPosts = userPostMapper.selectList(
+            new LambdaQueryWrapper<SysUserPost>().in(SysUserPost::getPostId, postIds));
+
+        // 获取用户ID列表
+        Set<Long> userIds = StreamUtils.toSet(userPosts, SysUserPost::getUserId);
+
+        return selectListByIds(new ArrayList<>(userIds));
+    }
+
 }