26 Commits

Author SHA1 Message Date
AprilWind
2bc7171abd fix 修复密码校验误删字段 2025-07-04 19:29:30 +08:00
AprilWind
4f99487d24 update 优化密码校验 2025-07-04 19:18:42 +08:00
AprilWind
a7cddc8d40 update 优化校验角色是否有数据权限 2025-07-04 18:52:29 +08:00
AprilWind
f3c4c02d73 update 优化校验角色是否有数据权限 2025-07-04 18:39:54 +08:00
疯狂的狮子Li
eb631360f4 update 优化 删除无用注释 2025-07-04 18:19:50 +08:00
秋辞未寒
a62bf04428 !721 update 发号器工具类便利性优化
* update 发号器工具类便利性优化
2025-07-04 09:46:23 +00:00
疯狂的狮子Li
7147f81b42 fix 修复 错误修改导致页面逻辑错误 2025-07-04 17:42:19 +08:00
疯狂的狮子Li
c9098563ca fix 修复 数据权限字段编辑错误 2025-07-04 16:55:20 +08:00
AprilWind
d4a8c25eab update 优化数据权限 2025-07-04 16:53:18 +08:00
AprilWind
0ddba506bf update 优化新增角色信息 2025-07-04 16:16:12 +08:00
AprilWind
d02bea85cb update 优化新增用户岗位信息判空逻辑 2025-07-04 15:54:39 +08:00
AprilWind
d27c58bfe8 docs 补充重构数据权限注释 2025-07-04 15:35:04 +08:00
疯狂的狮子Li
34bb51f5c0 update 优化 增加岗位修改校验 2025-07-04 15:29:51 +08:00
疯狂的狮子Li
64c37aaec6 update 重构用户 角色 部门 菜单的数据权限设计逻辑更符合实际业务场景与优化查询写法提高效率 2025-07-04 14:50:33 +08:00
疯狂的狮子Li
3de036adde update 优化 屏蔽掉无用接口 2025-07-04 09:34:15 +08:00
疯狂的狮子Li
e0df8c15d8 update 优化 修改类名 避免无用扫描 2025-07-04 09:19:29 +08:00
疯狂的狮子Li
176793e15b update 优化 工作流代码写法 2025-07-03 14:48:53 +08:00
疯狂的狮子Li
589ec1fdbc update 优化 增加oss扩展contentType存储 2025-07-03 10:39:23 +08:00
AprilWind
b421c8d017 !711 update 优化附件扩展字段对象(存储在 SysOss.ext1 的 JSON 字符串中)
* update 优化文件上传附件扩展字段对象
* update 优化保存文件的大小,方便前端进行分片下载
2025-07-03 02:20:53 +00:00
AprilWind
e2200bac71 update 优化流程图按审批人分组去重 2025-07-02 19:08:52 +08:00
AprilWind
f8950d1e20 update 优化获取流程记录 2025-07-02 18:21:53 +08:00
AprilWind
9dfe9f610d !715 update 优化工作流待办任务查询
* update 优化工作流待办任务查询
2025-07-02 09:15:56 +00:00
疯狂的狮子Li
17610e8721 remove 删除 无用注解 2025-07-02 14:55:44 +08:00
疯狂的狮子Li
f29b787767 fix 修复 有某些无聊人士 对一个demo案例提漏洞 CVE-2025-6925 2025-07-02 14:35:25 +08:00
AprilWind
9775283a24 !714 update 优化工作流小改动
* update 优化工作流小改动
2025-07-02 05:17:43 +00:00
AprilWind
debc73d7d4 !713 update 优化StreamUtils使用以及岗位删除优化
* update 优化命名含义
* update 优化StreamUtils使用以及岗位删除优化
2025-07-02 05:17:18 +00:00
63 changed files with 1151 additions and 800 deletions

View File

@@ -189,13 +189,6 @@ springdoc:
name: Lion Li name: Lion Li
email: crazylionli@163.com email: crazylionli@163.com
url: https://gitee.com/dromara/RuoYi-Vue-Plus url: https://gitee.com/dromara/RuoYi-Vue-Plus
components:
# 鉴权方式配置
security-schemes:
apiKey:
type: APIKEY
in: HEADER
name: ${sa-token.token-name}
#这里定义了两个分组,可定义多个,也可以不定义 #这里定义了两个分组,可定义多个,也可以不定义
group-configs: group-configs:
- group: 1.演示模块 - group: 1.演示模块
@@ -213,7 +206,7 @@ springdoc:
xss: xss:
# 过滤开关 # 过滤开关
enabled: true enabled: true
# 排除链接(多个用逗号分隔) # 排除链接
excludeUrls: excludeUrls:
- /system/notice - /system/notice

View File

@@ -17,6 +17,7 @@ user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空 user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符 user.password.not.valid=* 5-50个字符
user.password.format.valid=密码必须包含大写字母、小写字母、数字和特殊字符
user.email.not.valid=邮箱格式错误 user.email.not.valid=邮箱格式错误
user.email.not.blank=邮箱不能为空 user.email.not.blank=邮箱不能为空
user.phonenumber.not.blank=用户手机号不能为空 user.phonenumber.not.blank=用户手机号不能为空

View File

@@ -17,6 +17,7 @@ user.username.length.valid=Account length must be between {min} and {max} charac
user.password.not.blank=Password cannot be empty user.password.not.blank=Password cannot be empty
user.password.length.valid=Password length must be between {min} and {max} characters user.password.length.valid=Password length must be between {min} and {max} characters
user.password.not.valid=* 5-50 characters user.password.not.valid=* 5-50 characters
user.password.format.valid=Password must contain uppercase, lowercase, digit, and special character
user.email.not.valid=Mailbox format error user.email.not.valid=Mailbox format error
user.email.not.blank=Mailbox cannot be blank user.email.not.blank=Mailbox cannot be blank
user.phonenumber.not.blank=Phone number cannot be blank user.phonenumber.not.blank=Phone number cannot be blank

View File

@@ -17,6 +17,7 @@ user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空 user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符 user.password.not.valid=* 5-50个字符
user.password.format.valid=密码必须包含大写字母、小写字母、数字和特殊字符
user.email.not.valid=邮箱格式错误 user.email.not.valid=邮箱格式错误
user.email.not.blank=邮箱不能为空 user.email.not.blank=邮箱不能为空
user.phonenumber.not.blank=用户手机号不能为空 user.phonenumber.not.blank=用户手机号不能为空

View File

@@ -26,6 +26,7 @@ public class PasswordLoginBody extends LoginBody {
*/ */
@NotBlank(message = "{user.password.not.blank}") @NotBlank(message = "{user.password.not.blank}")
@Length(min = 5, max = 30, message = "{user.password.length.valid}") @Length(min = 5, max = 30, message = "{user.password.length.valid}")
// @Pattern(regexp = RegexConstants.PASSWORD, message = "{user.password.format.valid}")
private String password; private String password;
} }

View File

@@ -26,8 +26,12 @@ public class RegisterBody extends LoginBody {
*/ */
@NotBlank(message = "{user.password.not.blank}") @NotBlank(message = "{user.password.not.blank}")
@Length(min = 5, max = 30, message = "{user.password.length.valid}") @Length(min = 5, max = 30, message = "{user.password.length.valid}")
// @Pattern(regexp = RegexConstants.PASSWORD, message = "{user.password.format.valid}")
private String password; private String password;
/**
* 用户类型
*/
private String userType; private String userType;
} }

View File

@@ -3,6 +3,7 @@ package org.dromara.common.core.service;
import org.dromara.common.core.domain.dto.DeptDTO; import org.dromara.common.core.domain.dto.DeptDTO;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 通用 部门服务 * 通用 部门服务
@@ -34,4 +35,12 @@ public interface DeptService {
*/ */
List<DeptDTO> selectDeptsByList(); List<DeptDTO> selectDeptsByList();
/**
* 根据部门 ID 列表查询部门名称映射关系
*
* @param deptIds 部门 ID 列表
* @return Map其中 key 为部门 IDvalue 为对应的部门名称
*/
Map<Long, String> selectDeptNamesByIds(List<Long> deptIds);
} }

View File

@@ -1,5 +1,8 @@
package org.dromara.common.core.service; package org.dromara.common.core.service;
import java.util.List;
import java.util.Map;
/** /**
* 通用 岗位服务 * 通用 岗位服务
* *
@@ -7,4 +10,12 @@ package org.dromara.common.core.service;
*/ */
public interface PostService { public interface PostService {
/**
* 根据岗位 ID 列表查询岗位名称映射关系
*
* @param postIds 岗位 ID 列表
* @return Map其中 key 为岗位 IDvalue 为对应的岗位名称
*/
Map<Long, String> selectPostNamesByIds(List<Long> postIds);
} }

View File

@@ -1,5 +1,8 @@
package org.dromara.common.core.service; package org.dromara.common.core.service;
import java.util.List;
import java.util.Map;
/** /**
* 通用 角色服务 * 通用 角色服务
* *
@@ -7,4 +10,12 @@ package org.dromara.common.core.service;
*/ */
public interface RoleService { public interface RoleService {
/**
* 根据角色 ID 列表查询角色名称映射关系
*
* @param roleIds 角色 ID 列表
* @return Map其中 key 为角色 IDvalue 为对应的角色名称
*/
Map<Long, String> selectRoleNamesByIds(List<Long> roleIds);
} }

View File

@@ -100,28 +100,4 @@ public interface UserService {
*/ */
Map<Long, String> selectUserNamesByIds(List<Long> userIds); Map<Long, String> selectUserNamesByIds(List<Long> userIds);
/**
* 根据角色 ID 列表查询角色名称映射关系
*
* @param roleIds 角色 ID 列表
* @return Map其中 key 为角色 IDvalue 为对应的角色名称
*/
Map<Long, String> selectRoleNamesByIds(List<Long> roleIds);
/**
* 根据部门 ID 列表查询部门名称映射关系
*
* @param deptIds 部门 ID 列表
* @return Map其中 key 为部门 IDvalue 为对应的部门名称
*/
Map<Long, String> selectDeptNamesByIds(List<Long> deptIds);
/**
* 根据岗位 ID 列表查询岗位名称映射关系
*
* @param postIds 岗位 ID 列表
* @return Map其中 key 为岗位 IDvalue 为对应的岗位名称
*/
Map<Long, String> selectPostNamesByIds(List<Long> postIds);
} }

View File

@@ -1,7 +1,6 @@
package org.dromara.common.redis.utils; package org.dromara.common.redis.utils;
import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
@@ -10,6 +9,10 @@ import org.redisson.api.RIdGenerator;
import org.redisson.api.RedissonClient; import org.redisson.api.RedissonClient;
import java.time.Duration; import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
/** /**
* 发号器工具类 * 发号器工具类
@@ -23,12 +26,12 @@ public class SequenceUtils {
/** /**
* 默认初始值 * 默认初始值
*/ */
public static final Long DEFAULT_INIT_VALUE = 1L; public static final long DEFAULT_INIT_VALUE = 1L;
/** /**
* 默认步长 * 默认步长
*/ */
public static final Long DEFAULT_STEP_VALUE = 1L; public static final long DEFAULT_STEP_VALUE = 1L;
/** /**
* 默认过期时间-天 * 默认过期时间-天
@@ -40,6 +43,11 @@ public class SequenceUtils {
*/ */
public static final Duration DEFAULT_EXPIRE_TIME_MINUTE = Duration.ofMinutes(1); public static final Duration DEFAULT_EXPIRE_TIME_MINUTE = Duration.ofMinutes(1);
/**
* 默认最小ID容量位数 - 6位数即至少可以生成的ID为999999个
*/
public static final int DEFAULT_MIN_ID_CAPACITY_BITS = 6;
/** /**
* 获取Redisson客户端实例 * 获取Redisson客户端实例
*/ */
@@ -54,14 +62,11 @@ public class SequenceUtils {
* @param stepValue ID步长 * @param stepValue ID步长
* @return ID生成器 * @return ID生成器
*/ */
private static RIdGenerator getIdGenerator(String key, Duration expireTime, Long initValue, Long stepValue) { public 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); RIdGenerator idGenerator = REDISSON_CLIENT.getIdGenerator(key);
// 初始值和步长不能小于等于0
initValue = initValue <= 0 ? DEFAULT_INIT_VALUE : initValue;
stepValue = stepValue <= 0 ? DEFAULT_STEP_VALUE : stepValue;
// 设置初始值和步长 // 设置初始值和步长
idGenerator.tryInit(initValue, stepValue); idGenerator.tryInit(initValue, stepValue);
// 设置过期时间 // 设置过期时间
@@ -69,6 +74,17 @@ public class SequenceUtils {
return idGenerator; return idGenerator;
} }
/**
* 获取ID生成器
*
* @param key 业务key
* @param expireTime 过期时间
* @return ID生成器
*/
public static RIdGenerator getIdGenerator(String key, Duration expireTime) {
return getIdGenerator(key, expireTime, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE);
}
/** /**
* 获取指定业务key的唯一id * 获取指定业务key的唯一id
* *
@@ -78,10 +94,21 @@ public class SequenceUtils {
* @param stepValue ID步长 * @param stepValue ID步长
* @return 唯一id * @return 唯一id
*/ */
public static long nextId(String key, Duration expireTime, Long initValue, Long stepValue) { public static long getNextId(String key, Duration expireTime, long initValue, long stepValue) {
return getIdGenerator(key, expireTime, initValue, stepValue).nextId(); return getIdGenerator(key, expireTime, initValue, stepValue).nextId();
} }
/**
* 获取指定业务key的唯一id (ID初始值=1,ID步长=1)
*
* @param key 业务key
* @param expireTime 过期时间
* @return 唯一id
*/
public static long getNextId(String key, Duration expireTime) {
return getIdGenerator(key, expireTime).nextId();
}
/** /**
* 获取指定业务key的唯一id字符串 * 获取指定业务key的唯一id字符串
* *
@@ -91,19 +118,8 @@ public class SequenceUtils {
* @param stepValue ID步长 * @param stepValue ID步长
* @return 唯一id * @return 唯一id
*/ */
public static String nextIdStr(String key, Duration expireTime, Long initValue, Long stepValue) { public static String getNextIdString(String key, Duration expireTime, long initValue, long stepValue) {
return String.valueOf(nextId(key, expireTime, initValue, stepValue)); return String.valueOf(getNextId(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();
} }
/** /**
@@ -113,8 +129,8 @@ public class SequenceUtils {
* @param expireTime 过期时间 * @param expireTime 过期时间
* @return 唯一id * @return 唯一id
*/ */
public static String nextIdStr(String key, Duration expireTime) { public static String getNextIdString(String key, Duration expireTime) {
return String.valueOf(nextId(key, expireTime)); return String.valueOf(getNextId(key, expireTime));
} }
/** /**
@@ -125,56 +141,210 @@ public class SequenceUtils {
* @param width 位数不足左补0 * @param width 位数不足左补0
* @return 补零后的唯一id字符串 * @return 补零后的唯一id字符串
*/ */
public static String nextPaddedIdStr(String key, Duration expireTime, Integer width) { public static String getPaddedNextIdString(String key, Duration expireTime, Integer width) {
return StringUtils.leftPad(nextIdStr(key, expireTime), width, '0'); return StringUtils.leftPad(getNextIdString(key, expireTime), width, '0');
} }
/** /**
* 获取 yyyyMMdd 开头的唯一id * 获取 yyyyMMdd 格式的唯一id
* *
* @return 唯一id * @return 唯一id
* @deprecated 请使用 {@link #getDateId(String)} 或 {@link #getDateId(String, boolean)}、{@link #getDateId(String, boolean, int)}确保不同业务的ID连续性
*/ */
public static String nextIdDate() { @Deprecated
return nextIdDate(""); public static String getDateId() {
return getDateId("");
} }
/** /**
* 获取 prefix + yyyyMMdd 开头的唯一id * 获取 prefix + yyyyMMdd 格式的唯一id
* *
* @param prefix 业务前缀 * @param prefix 业务前缀
* @return 唯一id * @return 唯一id
*/ */
public static String nextIdDate(String prefix) { public static String getDateId(String prefix) {
// 前缀+日期 构建 prefixKey return getDateId(prefix, true);
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 * 获取 prefix + yyyyMMdd 格式的唯一id
* *
* @param prefix 业务前缀
* @param isWithPrefix id是否携带业务前缀
* @return 唯一id * @return 唯一id
*/ */
public static String nextIdDateTime() { public static String getDateId(String prefix, boolean isWithPrefix) {
return nextIdDateTime(""); return getDateId(prefix, isWithPrefix, -1);
} }
/** /**
* 获取 prefix + yyyyMMddHHmmss 开头的唯一id * 获取 prefix + yyyyMMdd 格式的唯一id (启用ID补位补位长度 = {@link #DEFAULT_MIN_ID_CAPACITY_BITS})}
*
* @param prefix 业务前缀
* @param isWithPrefix id是否携带业务前缀
* @return 唯一id
*/
public static String getPaddedDateId(String prefix, boolean isWithPrefix) {
return getDateId(prefix, isWithPrefix, DEFAULT_MIN_ID_CAPACITY_BITS);
}
/**
* 获取 prefix + yyyyMMdd 格式的唯一id
*
* @param prefix 业务前缀
* @param isWithPrefix id是否携带业务前缀
* @param minIdCapacityBits 最小ID容量位数小于该位数的ID左补0小于等于0表示不启用补位
* @return 唯一id
*/
public static String getDateId(String prefix, boolean isWithPrefix, int minIdCapacityBits) {
return getDateId(prefix, isWithPrefix, minIdCapacityBits, LocalDate.now());
}
/**
* 获取 prefix + yyyyMMdd 格式的唯一id
*
* @param prefix 业务前缀
* @param isWithPrefix id是否携带业务前缀
* @param minIdCapacityBits 最小ID容量位数小于该位数的ID左补0小于等于0表示不启用补位
* @param time 时间
* @return 唯一id
*/
public static String getDateId(String prefix, boolean isWithPrefix, int minIdCapacityBits, LocalDate time) {
return getDateId(prefix, isWithPrefix, minIdCapacityBits, time, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE);
}
/**
* 获取 prefix + yyyyMMdd 格式的唯一id
*
* @param prefix 业务前缀
* @param isWithPrefix id是否携带业务前缀
* @param minIdCapacityBits 最小ID容量位数小于该位数的ID左补0小于等于0表示不启用补位
* @param time 时间
* @param initValue ID初始值
* @param stepValue ID步长
* @return 唯一id
*/
public static String getDateId(String prefix, boolean isWithPrefix, int minIdCapacityBits, LocalDate time, long initValue, long stepValue) {
return getDatePatternId(prefix, isWithPrefix, minIdCapacityBits, time, DatePattern.PURE_DATE_FORMATTER, DEFAULT_EXPIRE_TIME_DAY, initValue, stepValue);
}
/**
* 获取 yyyyMMddHHmmss 格式的唯一id
*
* @return 唯一id
* @deprecated 请使用 {@link #getDateTimeId(String)} 或 {@link #getDateTimeId(String, boolean)}、{@link #getDateTimeId(String, boolean, int)}确保不同业务的ID连续性
*/
@Deprecated
public static String getDateTimeId() {
return getDateTimeId("", false);
}
/**
* 获取 prefix + yyyyMMddHHmmss 格式的唯一id
* *
* @param prefix 业务前缀 * @param prefix 业务前缀
* @return 唯一id * @return 唯一id
*/ */
public static String nextIdDateTime(String prefix) { public static String getDateTimeId(String prefix) {
// 前缀+日期时间 构建 prefixKey return getDateTimeId(prefix, true);
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);
} }
/**
* 获取 prefix + yyyyMMddHHmmss 格式的唯一id
*
* @param prefix 业务前缀
* @param isWithPrefix id是否携带业务前缀
* @return 唯一id
*/
public static String getDateTimeId(String prefix, boolean isWithPrefix) {
return getDateTimeId(prefix, isWithPrefix, -1);
}
/**
* 获取 prefix + yyyyMMddHHmmss 格式的唯一id (启用ID补位补位长度 = {@link #DEFAULT_MIN_ID_CAPACITY_BITS})}
*
* @param prefix 业务前缀
* @param isWithPrefix id是否携带业务前缀
* @return 唯一id
*/
public static String getPaddedDateTimeId(String prefix, boolean isWithPrefix) {
return getDateTimeId(prefix, isWithPrefix, DEFAULT_MIN_ID_CAPACITY_BITS);
}
/**
* 获取 prefix + yyyyMMddHHmmss 格式的唯一id
*
* @param prefix 业务前缀
* @param isWithPrefix id是否携带业务前缀
* @param minIdCapacityBits 最小ID容量位数小于该位数的ID左补0小于等于0表示不启用补位
* @return 唯一id
*/
public static String getDateTimeId(String prefix, boolean isWithPrefix, int minIdCapacityBits) {
return getDateTimeId(prefix, isWithPrefix, minIdCapacityBits, LocalDateTime.now());
}
/**
* 获取 prefix + yyyyMMddHHmmss 格式的唯一id
*
* @param prefix 业务前缀
* @param isWithPrefix id是否携带业务前缀
* @param minIdCapacityBits 最小ID容量位数小于该位数的ID左补0小于等于0表示不启用补位
* @param time 时间
* @return 唯一id
*/
public static String getDateTimeId(String prefix, boolean isWithPrefix, int minIdCapacityBits, LocalDateTime time) {
return getDateTimeId(prefix, isWithPrefix, minIdCapacityBits, time, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE);
}
/**
* 获取 prefix + yyyyMMddHHmmss 格式的唯一id
*
* @param prefix 业务前缀
* @param isWithPrefix id是否携带业务前缀
* @param minIdCapacityBits 最小ID容量位数小于该位数的ID左补0小于等于0表示不启用补位
* @param initValue ID初始值
* @param stepValue ID步长
* @return 唯一id
*/
public static String getDateTimeId(String prefix, boolean isWithPrefix, int minIdCapacityBits, LocalDateTime time, long initValue, long stepValue) {
return getDatePatternId(prefix, isWithPrefix, minIdCapacityBits, time, DatePattern.PURE_DATETIME_FORMATTER, DEFAULT_EXPIRE_TIME_MINUTE, initValue, stepValue);
}
/**
* 获取指定业务key的指定时间格式的ID
*
* @param prefix 业务前缀
* @param isWithPrefix id是否携带业务前缀
* @param minIdCapacityBits 最小ID容量位数小于该位数的ID左补0小于等于0表示不启用补位
* @param temporalAccessor 时间访问器
* @param timeFormatter 时间格式
* @param expireTime 过期时间
* @param initValue ID初始值
* @param stepValue ID步长
* @return 唯一id
*/
private static String getDatePatternId(String prefix, boolean isWithPrefix, int minIdCapacityBits, TemporalAccessor temporalAccessor, DateTimeFormatter timeFormatter, Duration expireTime, long initValue, long stepValue) {
// 时间前缀
String timePrefix = timeFormatter.format(temporalAccessor);
// 业务前缀 + 时间前缀 构建 prefixKey
String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), timePrefix);
// 获取id例 -> 1
String nextId = getNextIdString(prefixKey, expireTime, initValue, stepValue);
// minIdCapacityBits 大于0且 nextId 的长度小于 minIdCapacityBits则左补0
if (minIdCapacityBits > 0 && nextId.length() < minIdCapacityBits) {
nextId = StringUtils.leftPad(nextId, minIdCapacityBits, '0');
}
// 是否携带业务前缀
if (isWithPrefix) {
// 例 -> P202507031
// 其中 P 为业务前缀202507031 为 yyyyMMdd 格式时间, 1 为nextId
return StringUtils.format("{}{}", prefixKey, nextId);
}
// 例 -> 202507031
// 其中 202507031 为 yyyyMMdd 格式时间, 1 为nextId
return StringUtils.format("{}{}", timePrefix, nextId);
}
} }

View File

@@ -6,7 +6,6 @@ import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.sse.core.SseEmitterManager; import org.dromara.common.sse.core.SseEmitterManager;
import org.dromara.common.sse.dto.SseMessageDto;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@@ -14,8 +13,6 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.List;
/** /**
* SSE 控制器 * SSE 控制器
* *
@@ -51,31 +48,32 @@ public class SseController implements DisposableBean {
return R.ok(); return R.ok();
} }
/** // 以下为demo仅供参考 禁止使用 请在业务逻辑中使用工具发送而不是用接口发送
* 向特定用户发送消息 // /**
* // * 向特定用户发送消息
* @param userId 目标用户的 ID // *
* @param msg 要发送的消息内容 // * @param userId 目标用户的 ID
*/ // * @param msg 要发送的消息内容
@GetMapping(value = "${sse.path}/send") // */
public R<Void> send(Long userId, String msg) { // @GetMapping(value = "${sse.path}/send")
SseMessageDto dto = new SseMessageDto(); // public R<Void> send(Long userId, String msg) {
dto.setUserIds(List.of(userId)); // SseMessageDto dto = new SseMessageDto();
dto.setMessage(msg); // dto.setUserIds(List.of(userId));
sseEmitterManager.publishMessage(dto); // dto.setMessage(msg);
return R.ok(); // sseEmitterManager.publishMessage(dto);
} // return R.ok();
// }
/** //
* 向所有用户发送消息 // /**
* // * 向所有用户发送消息
* @param msg 要发送的消息内容 // *
*/ // * @param msg 要发送的消息内容
@GetMapping(value = "${sse.path}/sendAll") // */
public R<Void> send(String msg) { // @GetMapping(value = "${sse.path}/sendAll")
sseEmitterManager.publishAll(msg); // public R<Void> send(String msg) {
return R.ok(); // sseEmitterManager.publishAll(msg);
} // return R.ok();
// }
/** /**
* 清理资源。此方法目前不执行任何操作,但避免因未实现而导致错误 * 清理资源。此方法目前不执行任何操作,但避免因未实现而导致错误

View File

@@ -1,6 +1,5 @@
package org.dromara.demo.controller; package org.dromara.demo.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.mail.utils.MailUtils; import org.dromara.common.mail.utils.MailUtils;
@@ -18,12 +17,11 @@ import java.util.Arrays;
* *
* @author Michelle.Chung * @author Michelle.Chung
*/ */
@SaIgnore
@Validated @Validated
@RequiredArgsConstructor @RequiredArgsConstructor
@RestController @RestController
@RequestMapping("/demo/mail") @RequestMapping("/demo/mail")
public class MailController { public class MailSendController {
/** /**
* 发送邮件 * 发送邮件
@@ -44,11 +42,11 @@ public class MailController {
* @param to 接收人 * @param to 接收人
* @param subject 标题 * @param subject 标题
* @param text 内容 * @param text 内容
* @param filePath 附件路径
*/ */
@GetMapping("/sendMessageWithAttachment") @GetMapping("/sendMessageWithAttachment")
public R<Void> sendMessageWithAttachment(String to, String subject, String text, String filePath) { public R<Void> sendMessageWithAttachment(String to, String subject, String text) {
MailUtils.sendText(to, subject, text, new File(filePath)); // 附件路径 禁止前端传递 有任意读取系统文件风险
MailUtils.sendText(to, subject, text, new File("/xxx/xxx"));
return R.ok(); return R.ok();
} }
@@ -58,10 +56,11 @@ public class MailController {
* @param to 接收人 * @param to 接收人
* @param subject 标题 * @param subject 标题
* @param text 内容 * @param text 内容
* @param paths 附件路径
*/ */
@GetMapping("/sendMessageWithAttachments") @GetMapping("/sendMessageWithAttachments")
public R<Void> sendMessageWithAttachments(String to, String subject, String text, String[] paths) { public R<Void> sendMessageWithAttachments(String to, String subject, String text) {
// 附件路径 禁止前端传递 有任意读取系统文件风险
String[] paths = new String[]{"/xxx/xxx", "/xxx/xxx"};
File[] array = Arrays.stream(paths).map(File::new).toArray(File[]::new); File[] array = Arrays.stream(paths).map(File::new).toArray(File[]::new);
MailUtils.sendText(to, subject, text, array); MailUtils.sendText(to, subject, text, array);
return R.ok(); return R.ok();

View File

@@ -19,6 +19,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
@@ -107,7 +108,7 @@ public class SysPostController extends BaseController {
@Log(title = "岗位管理", businessType = BusinessType.DELETE) @Log(title = "岗位管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{postIds}") @DeleteMapping("/{postIds}")
public R<Void> remove(@PathVariable Long[] postIds) { public R<Void> remove(@PathVariable Long[] postIds) {
return toAjax(postService.deletePostByIds(postIds)); return toAjax(postService.deletePostByIds(Arrays.asList(postIds)));
} }
/** /**

View File

@@ -144,7 +144,7 @@ public class SysRoleController extends BaseController {
@Log(title = "角色管理", businessType = BusinessType.DELETE) @Log(title = "角色管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{roleIds}") @DeleteMapping("/{roleIds}")
public R<Void> remove(@PathVariable Long[] roleIds) { public R<Void> remove(@PathVariable Long[] roleIds) {
return toAjax(roleService.deleteRoleByIds(roleIds)); return toAjax(roleService.deleteRoleByIds(List.of(roleIds)));
} }
/** /**

View File

@@ -0,0 +1,75 @@
package org.dromara.system.domain;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 附件扩展字段对象(存储在 SysOss.ext1 的 JSON 字符串中)
*
* @author AprilWind
*/
@Data
public class SysOssExt implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 所属业务类型(如 avatar、report、contract
*/
private String bizType;
/**
* 文件大小(单位:字节)
*/
private Long fileSize;
/**
* 文件类型MIME类型如 image/png
*/
private String contentType;
/**
* 来源标识(如 userUpload、systemImport
*/
private String source;
/**
* 上传 IP 地址,便于审计和追踪
*/
private String uploadIp;
/**
* 附件说明或备注
*/
private String remark;
/**
* 附件标签,如 ["图片", "证件"]
*/
private List<String> tags;
/**
* 业务绑定ID如某业务记录ID
*/
private String refId;
/**
* 绑定业务类型
*/
private String refType;
/**
* 是否为临时文件,用于区分正式或待清理
*/
private Boolean isTemp;
/**
* 文件MD5值可用于去重或校验
*/
private String md5;
}

View File

@@ -1,17 +1,17 @@
package org.dromara.system.domain.bo; package org.dromara.system.domain.bo;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.system.domain.SysTenant;
import io.github.linpeilie.annotations.AutoMapper; import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*; import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.system.domain.SysTenant;
import java.util.Date; import java.util.Date;
import org.dromara.common.mybatis.core.domain.BaseEntity;
/** /**
* 租户业务对象 sys_tenant * 租户业务对象 sys_tenant
* *
@@ -62,6 +62,7 @@ public class SysTenantBo extends BaseEntity {
* 密码(创建系统用户) * 密码(创建系统用户)
*/ */
@NotBlank(message = "密码不能为空", groups = { AddGroup.class }) @NotBlank(message = "密码不能为空", groups = { AddGroup.class })
// @Pattern(regexp = RegexConstants.PASSWORD, message = "{user.password.format.valid}", groups = { AddGroup.class })
private String password; private String password;
/** /**

View File

@@ -3,7 +3,7 @@ package org.dromara.system.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param; import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataColumn;
import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.annotation.DataPermission;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
@@ -20,6 +20,38 @@ import java.util.List;
*/ */
public interface SysDeptMapper extends BaseMapperPlus<SysDept, SysDeptVo> { public interface SysDeptMapper extends BaseMapperPlus<SysDept, SysDeptVo> {
/**
* 构建角色对应的部门 SQL 查询语句
*
* <p>该 SQL 用于查询某个角色关联的所有部门 ID常用于数据权限控制</p>
*
* @param roleId 角色ID
* @return 查询部门ID的 SQL 语句字符串
*/
default String buildDeptByRoleSql(Long roleId) {
return """
select dept_id from sys_role_dept where role_id = %d
""".formatted(roleId);
}
/**
* 构建 SQL 查询用于获取当前角色拥有的部门中所有的父部门ID
*
* <p>
* 该 SQL 用于 deptCheckStrictly 场景下,排除非叶子节点(父节点)用。
* </p>
*
* @param roleId 角色ID
* @return SQL 语句字符串查询角色下部门的所有父部门ID
*/
default String buildParentDeptByRoleSql(Long roleId) {
return """
select parent_id from sys_dept where dept_id in (
select dept_id from sys_role_dept where role_id = %d
)
""".formatted(roleId);
}
/** /**
* 查询部门管理数据 * 查询部门管理数据
* *
@@ -72,6 +104,19 @@ public interface SysDeptMapper extends BaseMapperPlus<SysDept, SysDeptVo> {
.apply(DataBaseHelper.findInSet(parentId, "ancestors"))); .apply(DataBaseHelper.findInSet(parentId, "ancestors")));
} }
/**
* 查询某个部门及其所有子部门ID含自身
*
* @param parentId 父部门ID
* @return 部门ID集合
*/
default List<Long> selectDeptAndChildById(Long parentId) {
List<SysDept> deptList = this.selectListByParentId(parentId);
List<Long> deptIds = StreamUtils.toList(deptList, SysDept::getDeptId);
deptIds.add(parentId);
return deptIds;
}
/** /**
* 根据角色ID查询部门树信息 * 根据角色ID查询部门树信息
* *
@@ -79,6 +124,16 @@ public interface SysDeptMapper extends BaseMapperPlus<SysDept, SysDeptVo> {
* @param deptCheckStrictly 部门树选择项是否关联显示 * @param deptCheckStrictly 部门树选择项是否关联显示
* @return 选中部门列表 * @return 选中部门列表
*/ */
List<Long> selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); default List<Long> selectDeptListByRoleId(Long roleId, boolean deptCheckStrictly) {
LambdaQueryWrapper<SysDept> wrapper = new LambdaQueryWrapper<>();
wrapper.select(SysDept::getDeptId)
.inSql(SysDept::getDeptId, this.buildDeptByRoleSql(roleId))
.orderByAsc(SysDept::getParentId)
.orderByAsc(SysDept::getOrderNum);
if (deptCheckStrictly) {
wrapper.notInSql(SysDept::getDeptId, this.buildParentDeptByRoleSql(roleId));
}
return this.selectObjs(wrapper);
}
} }

View File

@@ -1,9 +1,6 @@
package org.dromara.system.mapper; package org.dromara.system.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.system.domain.SysMenu; import org.dromara.system.domain.SysMenu;
@@ -19,12 +16,58 @@ import java.util.List;
public interface SysMenuMapper extends BaseMapperPlus<SysMenu, SysMenuVo> { public interface SysMenuMapper extends BaseMapperPlus<SysMenu, SysMenuVo> {
/** /**
* 根据用户查询系统菜单列表 * 构建用户权限菜单 SQL
* *
* @param queryWrapper 查询条件 * <p>
* @return 菜单列表 * 查询用户所属角色所拥有的菜单权限,用于权限判断、菜单加载等场景
* </p>
*
* @param userId 用户ID
* @return SQL 字符串,用于 inSql 条件
*/ */
List<SysMenu> selectMenuListByUserId(@Param(Constants.WRAPPER) Wrapper<SysMenu> queryWrapper); default String buildMenuByUserSql(Long userId) {
return """
select menu_id from sys_role_menu where role_id in (
select role_id from sys_user_role where user_id = %d
)
""".formatted(userId);
}
/**
* 构建角色对应的菜单ID SQL 子查询
*
* <p>
* 用于根据角色ID查询其所拥有的菜单权限用于权限标识、菜单显示等场景
* 通常配合 inSql 使用
* </p>
*
* @param roleId 角色ID
* @return 查询菜单ID的 SQL 子查询字符串
*/
default String buildMenuByRoleSql(Long roleId) {
return """
select menu_id from sys_role_menu where role_id = %d
""".formatted(roleId);
}
/**
* 构建角色所关联菜单的父菜单ID查询 SQL
*
* <p>
* 用于配合菜单勾选树结构的 {@code menuCheckStrictly} 模式,过滤掉非叶子节点(父菜单),
* 只返回角色实际勾选的末级菜单
* </p>
*
* @param roleId 角色ID
* @return SQL 语句字符串查询菜单的父菜单ID
*/
default String buildParentMenuByRoleSql(Long roleId) {
return """
select parent_id from sys_menu where menu_id in (
select menu_id from sys_role_menu where role_id = %d
)
""".formatted(roleId);
}
/** /**
* 根据用户ID查询权限 * 根据用户ID查询权限
@@ -32,7 +75,13 @@ public interface SysMenuMapper extends BaseMapperPlus<SysMenu, SysMenuVo> {
* @param userId 用户ID * @param userId 用户ID
* @return 权限列表 * @return 权限列表
*/ */
List<String> selectMenuPermsByUserId(Long userId); default List<String> selectMenuPermsByUserId(Long userId) {
return this.selectObjs(
new LambdaQueryWrapper<SysMenu>()
.select(SysMenu::getPerms)
.inSql(SysMenu::getMenuId, this.buildMenuByUserSql(userId))
);
}
/** /**
* 根据角色ID查询权限 * 根据角色ID查询权限
@@ -40,7 +89,13 @@ public interface SysMenuMapper extends BaseMapperPlus<SysMenu, SysMenuVo> {
* @param roleId 角色ID * @param roleId 角色ID
* @return 权限列表 * @return 权限列表
*/ */
List<String> selectMenuPermsByRoleId(Long roleId); default List<String> selectMenuPermsByRoleId(Long roleId) {
return this.selectObjs(
new LambdaQueryWrapper<SysMenu>()
.select(SysMenu::getPerms)
.inSql(SysMenu::getMenuId, this.buildMenuByRoleSql(roleId))
);
}
/** /**
* 根据用户ID查询菜单 * 根据用户ID查询菜单
@@ -56,14 +111,6 @@ public interface SysMenuMapper extends BaseMapperPlus<SysMenu, SysMenuVo> {
return this.selectList(lqw); return this.selectList(lqw);
} }
/**
* 根据用户ID查询菜单
*
* @param userId 用户ID
* @return 菜单列表
*/
List<SysMenu> selectMenuTreeByUserId(Long userId);
/** /**
* 根据角色ID查询菜单树信息 * 根据角色ID查询菜单树信息
* *
@@ -71,6 +118,16 @@ public interface SysMenuMapper extends BaseMapperPlus<SysMenu, SysMenuVo> {
* @param menuCheckStrictly 菜单树选择项是否关联显示 * @param menuCheckStrictly 菜单树选择项是否关联显示
* @return 选中菜单列表 * @return 选中菜单列表
*/ */
List<Long> selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); default List<Long> selectMenuListByRoleId(Long roleId, boolean menuCheckStrictly) {
LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
wrapper.select(SysMenu::getMenuId)
.inSql(SysMenu::getMenuId, buildMenuByRoleSql(roleId))
.orderByAsc(SysMenu::getParentId)
.orderByAsc(SysMenu::getOrderNum);
if (menuCheckStrictly) {
wrapper.notInSql(SysMenu::getMenuId, this.buildParentMenuByRoleSql(roleId));
}
return this.selectObjs(wrapper);
}
} }

View File

@@ -1,6 +1,7 @@
package org.dromara.system.mapper; package org.dromara.system.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataColumn;
import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.annotation.DataPermission;
@@ -33,11 +34,42 @@ public interface SysPostMapper extends BaseMapperPlus<SysPost, SysPostVo> {
} }
/** /**
* 查询用户所属岗位组 * 查询岗位列表
*
* @param queryWrapper 查询条件
* @return 岗位信息列表
*/
@DataPermission({
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "create_by")
})
default List<SysPostVo> selectPostList(Wrapper<SysPost> queryWrapper) {
return this.selectVoList(queryWrapper);
}
/**
* 根据岗位ID集合查询岗位数量
*
* @param postIds 岗位ID列表
* @return 匹配的岗位数量
*/
@DataPermission({
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "create_by")
})
default long selectPostCount(List<Long> postIds) {
return this.selectCount(new LambdaQueryWrapper<SysPost>().in(SysPost::getPostId, postIds));
}
/**
* 根据用户ID查询其关联的岗位列表
* *
* @param userId 用户ID * @param userId 用户ID
* @return 结果 * @return 岗位信息列表
*/ */
List<SysPostVo> selectPostsByUserId(Long userId); default List<SysPostVo> selectPostsByUserId(Long userId) {
return this.selectVoList(new LambdaQueryWrapper<SysPost>()
.inSql(SysPost::getPostId, "select post_id from sys_user_post where user_id = " + userId));
}
} }

View File

@@ -1,6 +1,7 @@
package org.dromara.system.mapper; package org.dromara.system.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@@ -19,6 +20,18 @@ import java.util.List;
*/ */
public interface SysRoleMapper extends BaseMapperPlus<SysRole, SysRoleVo> { public interface SysRoleMapper extends BaseMapperPlus<SysRole, SysRoleVo> {
/**
* 构建根据用户ID查询角色ID的SQL子查询
*
* @param userId 用户ID
* @return 查询用户对应角色ID的SQL语句字符串
*/
default String buildRoleByUserSql(Long userId) {
return """
select role_id from sys_user_role where user_id = %d
""".formatted(userId);
}
/** /**
* 分页查询角色列表 * 分页查询角色列表
* *
@@ -27,22 +40,40 @@ public interface SysRoleMapper extends BaseMapperPlus<SysRole, SysRoleVo> {
* @return 包含角色信息的分页结果 * @return 包含角色信息的分页结果
*/ */
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "deptName", value = "create_dept"),
@DataColumn(key = "userName", value = "r.create_by") @DataColumn(key = "userName", value = "create_by")
}) })
Page<SysRoleVo> selectPageRoleList(@Param("page") Page<SysRole> page, @Param(Constants.WRAPPER) Wrapper<SysRole> queryWrapper); default Page<SysRoleVo> selectPageRoleList(@Param("page") Page<SysRole> page, @Param(Constants.WRAPPER) Wrapper<SysRole> queryWrapper) {
return this.selectVoPage(page, queryWrapper);
}
/** /**
* 根据条件分页查询角色数据 * 根据条件查询角色数据
* *
* @param queryWrapper 查询条件 * @param queryWrapper 查询条件
* @return 角色数据集合信息 * @return 角色数据集合信息
*/ */
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "deptName", value = "create_dept"),
@DataColumn(key = "userName", value = "r.create_by") @DataColumn(key = "userName", value = "create_by")
}) })
List<SysRoleVo> selectRoleList(@Param(Constants.WRAPPER) Wrapper<SysRole> queryWrapper); default List<SysRoleVo> selectRoleList(@Param(Constants.WRAPPER) Wrapper<SysRole> queryWrapper) {
return this.selectVoList(queryWrapper);
}
/**
* 根据角色ID集合查询角色数量
*
* @param roleIds 角色ID列表
* @return 匹配的角色数量
*/
@DataPermission({
@DataColumn(key = "deptName", value = "create_dept"),
@DataColumn(key = "userName", value = "create_by")
})
default long selectRoleCount(List<Long> roleIds) {
return this.selectCount(new LambdaQueryWrapper<SysRole>().in(SysRole::getRoleId, roleIds));
}
/** /**
* 根据角色ID查询角色信息 * 根据角色ID查询角色信息
@@ -51,10 +82,12 @@ public interface SysRoleMapper extends BaseMapperPlus<SysRole, SysRoleVo> {
* @return 对应的角色信息 * @return 对应的角色信息
*/ */
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "deptName", value = "create_dept"),
@DataColumn(key = "userName", value = "r.create_by") @DataColumn(key = "userName", value = "create_by")
}) })
SysRoleVo selectRoleById(Long roleId); default SysRoleVo selectRoleById(Long roleId) {
return this.selectVoById(roleId);
}
/** /**
* 根据用户ID查询角色 * 根据用户ID查询角色
@@ -62,14 +95,11 @@ public interface SysRoleMapper extends BaseMapperPlus<SysRole, SysRoleVo> {
* @param userId 用户ID * @param userId 用户ID
* @return 角色列表 * @return 角色列表
*/ */
List<SysRoleVo> selectRolePermissionByUserId(Long userId); default List<SysRoleVo> selectRolesByUserId(Long userId) {
return this.selectVoList(new LambdaQueryWrapper<SysRole>()
/** .select(SysRole::getRoleId, SysRole::getRoleName, SysRole::getRoleKey,
* 根据用户ID查询角色 SysRole::getRoleSort, SysRole::getDataScope, SysRole::getStatus)
* .inSql(SysRole::getRoleId, this.buildRoleByUserSql(userId)));
* @param userId 用户ID }
* @return 角色列表
*/
List<SysRoleVo> selectRolesByUserId(Long userId);
} }

View File

@@ -30,7 +30,7 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
*/ */
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "dept_id"), @DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id") @DataColumn(key = "userName", value = "create_by")
}) })
default Page<SysUserVo> selectPageUserList(Page<SysUser> page, Wrapper<SysUser> queryWrapper) { default Page<SysUserVo> selectPageUserList(Page<SysUser> page, Wrapper<SysUser> queryWrapper) {
return this.selectVoPage(page, queryWrapper); return this.selectVoPage(page, queryWrapper);
@@ -44,7 +44,7 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
*/ */
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "dept_id"), @DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id") @DataColumn(key = "userName", value = "create_by")
}) })
default List<SysUserVo> selectUserList(Wrapper<SysUser> queryWrapper) { default List<SysUserVo> selectUserList(Wrapper<SysUser> queryWrapper) {
return this.selectVoList(queryWrapper); return this.selectVoList(queryWrapper);
@@ -58,7 +58,7 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
*/ */
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "deptName", value = "d.dept_id"),
@DataColumn(key = "userName", value = "u.user_id") @DataColumn(key = "userName", value = "u.create_by")
}) })
List<SysUserExportVo> selectUserExportList(@Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper); List<SysUserExportVo> selectUserExportList(@Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);
@@ -71,7 +71,7 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
*/ */
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "deptName", value = "d.dept_id"),
@DataColumn(key = "userName", value = "u.user_id") @DataColumn(key = "userName", value = "u.create_by")
}) })
Page<SysUserVo> selectAllocatedList(@Param("page") Page<SysUser> page, @Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper); Page<SysUserVo> selectAllocatedList(@Param("page") Page<SysUser> page, @Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);
@@ -83,7 +83,7 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
*/ */
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "deptName", value = "d.dept_id"),
@DataColumn(key = "userName", value = "u.user_id") @DataColumn(key = "userName", value = "u.create_by")
}) })
Page<SysUserVo> selectUnallocatedList(@Param("page") Page<SysUser> page, @Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper); Page<SysUserVo> selectUnallocatedList(@Param("page") Page<SysUser> page, @Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);
@@ -95,7 +95,7 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
*/ */
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "dept_id"), @DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id") @DataColumn(key = "userName", value = "create_by")
}) })
default long countUserById(Long userId) { default long countUserById(Long userId) {
return this.selectCount(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserId, userId)); return this.selectCount(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserId, userId));
@@ -111,7 +111,7 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
@Override @Override
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "dept_id"), @DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id") @DataColumn(key = "userName", value = "create_by")
}) })
int update(@Param(Constants.ENTITY) SysUser user, @Param(Constants.WRAPPER) Wrapper<SysUser> updateWrapper); int update(@Param(Constants.ENTITY) SysUser user, @Param(Constants.WRAPPER) Wrapper<SysUser> updateWrapper);
@@ -124,7 +124,7 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
@Override @Override
@DataPermission({ @DataPermission({
@DataColumn(key = "deptName", value = "dept_id"), @DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id") @DataColumn(key = "userName", value = "create_by")
}) })
int updateById(@Param(Constants.ENTITY) SysUser user); int updateById(@Param(Constants.ENTITY) SysUser user);

View File

@@ -1,5 +1,6 @@
package org.dromara.system.mapper; package org.dromara.system.mapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.system.domain.SysUserRole; import org.dromara.system.domain.SysUserRole;
@@ -18,6 +19,10 @@ public interface SysUserRoleMapper extends BaseMapperPlus<SysUserRole, SysUserRo
* @param roleId 角色ID * @param roleId 角色ID
* @return 关联到指定角色的用户ID列表 * @return 关联到指定角色的用户ID列表
*/ */
List<Long> selectUserIdsByRoleId(Long roleId); default List<Long> selectUserIdsByRoleId(Long roleId) {
return this.selectObjs(new LambdaQueryWrapper<SysUserRole>()
.select(SysUserRole::getUserId).eq(SysUserRole::getRoleId, roleId)
);
}
} }

View File

@@ -14,7 +14,13 @@ import java.util.List;
*/ */
public interface ISysConfigService { public interface ISysConfigService {
/**
* 分页查询参数配置列表
*
* @param config 查询条件
* @param pageQuery 分页参数
* @return 参数配置分页列表
*/
TableDataInfo<SysConfigVo> selectPageConfigList(SysConfigBo config, PageQuery pageQuery); TableDataInfo<SysConfigVo> selectPageConfigList(SysConfigBo config, PageQuery pageQuery);
/** /**

View File

@@ -14,7 +14,13 @@ import java.util.List;
*/ */
public interface ISysDictDataService { public interface ISysDictDataService {
/**
* 分页查询字典数据列表
*
* @param dictData 查询条件
* @param pageQuery 分页参数
* @return 字典数据分页列表
*/
TableDataInfo<SysDictDataVo> selectPageDictDataList(SysDictDataBo dictData, PageQuery pageQuery); TableDataInfo<SysDictDataVo> selectPageDictDataList(SysDictDataBo dictData, PageQuery pageQuery);
/** /**

View File

@@ -15,7 +15,13 @@ import java.util.List;
*/ */
public interface ISysDictTypeService { public interface ISysDictTypeService {
/**
* 分页查询字典类型列表
*
* @param dictType 查询条件
* @param pageQuery 分页参数
* @return 字典类型分页列表
*/
TableDataInfo<SysDictTypeVo> selectPageDictTypeList(SysDictTypeBo dictType, PageQuery pageQuery); TableDataInfo<SysDictTypeVo> selectPageDictTypeList(SysDictTypeBo dictType, PageQuery pageQuery);
/** /**

View File

@@ -14,7 +14,13 @@ import java.util.List;
*/ */
public interface ISysLogininforService { public interface ISysLogininforService {
/**
* 分页查询登录日志列表
*
* @param logininfor 查询条件
* @param pageQuery 分页参数
* @return 登录日志分页列表
*/
TableDataInfo<SysLogininforVo> selectPageLogininforList(SysLogininforBo logininfor, PageQuery pageQuery); TableDataInfo<SysLogininforVo> selectPageLogininforList(SysLogininforBo logininfor, PageQuery pageQuery);
/** /**

View File

@@ -14,7 +14,13 @@ import java.util.List;
*/ */
public interface ISysNoticeService { public interface ISysNoticeService {
/**
* 分页查询通知公告列表
*
* @param notice 查询条件
* @param pageQuery 分页参数
* @return 通知公告分页列表
*/
TableDataInfo<SysNoticeVo> selectPageNoticeList(SysNoticeBo notice, PageQuery pageQuery); TableDataInfo<SysNoticeVo> selectPageNoticeList(SysNoticeBo notice, PageQuery pageQuery);
/** /**

View File

@@ -14,6 +14,13 @@ import java.util.List;
*/ */
public interface ISysOperLogService { public interface ISysOperLogService {
/**
* 分页查询操作日志列表
*
* @param operLog 查询条件
* @param pageQuery 分页参数
* @return 操作日志分页列表
*/
TableDataInfo<SysOperLogVo> selectPageOperLogList(SysOperLogBo operLog, PageQuery pageQuery); TableDataInfo<SysOperLogVo> selectPageOperLogList(SysOperLogBo operLog, PageQuery pageQuery);
/** /**

View File

@@ -14,7 +14,13 @@ import java.util.List;
*/ */
public interface ISysPostService { public interface ISysPostService {
/**
* 分页查询岗位列表
*
* @param post 查询条件
* @param pageQuery 分页参数
* @return 岗位分页列表
*/
TableDataInfo<SysPostVo> selectPagePostList(SysPostBo post, PageQuery pageQuery); TableDataInfo<SysPostVo> selectPagePostList(SysPostBo post, PageQuery pageQuery);
/** /**
@@ -110,7 +116,7 @@ public interface ISysPostService {
* @param postIds 需要删除的岗位ID * @param postIds 需要删除的岗位ID
* @return 结果 * @return 结果
*/ */
int deletePostByIds(Long[] postIds); int deletePostByIds(List<Long> postIds);
/** /**
* 新增保存岗位信息 * 新增保存岗位信息

View File

@@ -16,11 +16,17 @@ import java.util.Set;
*/ */
public interface ISysRoleService { public interface ISysRoleService {
/**
* 分页查询角色列表
*
* @param role 查询条件
* @param pageQuery 分页参数
* @return 角色分页列表
*/
TableDataInfo<SysRoleVo> selectPageRoleList(SysRoleBo role, PageQuery pageQuery); TableDataInfo<SysRoleVo> selectPageRoleList(SysRoleBo role, PageQuery pageQuery);
/** /**
* 根据条件分页查询角色数据 * 根据条件查询角色数据
* *
* @param role 角色信息 * @param role 角色信息
* @return 角色数据集合信息 * @return 角色数据集合信息
@@ -112,6 +118,13 @@ public interface ISysRoleService {
*/ */
void checkRoleDataScope(Long roleId); void checkRoleDataScope(Long roleId);
/**
* 校验角色是否有数据权限
*
* @param roleIds 角色ID列表支持传单个ID
*/
void checkRoleDataScope(List<Long> roleIds);
/** /**
* 通过角色ID查询角色使用数量 * 通过角色ID查询角色使用数量
* *
@@ -167,7 +180,7 @@ public interface ISysRoleService {
* @param roleIds 需要删除的角色ID * @param roleIds 需要删除的角色ID
* @return 结果 * @return 结果
*/ */
int deleteRoleByIds(Long[] roleIds); int deleteRoleByIds(List<Long> roleIds);
/** /**
* 取消授权用户角色 * 取消授权用户角色
@@ -195,8 +208,29 @@ public interface ISysRoleService {
*/ */
int insertAuthUsers(Long roleId, Long[] userIds); int insertAuthUsers(Long roleId, Long[] userIds);
/**
* 根据角色ID清除该角色关联的所有在线用户的登录状态踢出在线用户
*
* <p>
* 先判断角色是否绑定用户,若无绑定则直接返回
* 然后遍历当前所有在线Token查找拥有该角色的用户并强制登出
* 注意:在线用户量过大时,操作可能导致 Redis 阻塞,需谨慎调用
* </p>
*
* @param roleId 角色ID
*/
void cleanOnlineUserByRole(Long roleId); void cleanOnlineUserByRole(Long roleId);
/**
* 根据用户ID列表清除对应在线用户的登录状态踢出指定用户
*
* <p>
* 遍历当前所有在线Token匹配用户ID列表中的用户强制登出
* 注意:在线用户量过大时,操作可能导致 Redis 阻塞,需谨慎调用
* </p>
*
* @param userIds 需要清除的用户ID列表
*/
void cleanOnlineUser(List<Long> userIds); void cleanOnlineUser(List<Long> userIds);
} }

View File

@@ -41,6 +41,13 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService {
private final SysConfigMapper baseMapper; private final SysConfigMapper baseMapper;
/**
* 分页查询参数配置列表
*
* @param config 查询条件
* @param pageQuery 分页参数
* @return 参数配置分页列表
*/
@Override @Override
public TableDataInfo<SysConfigVo> selectPageConfigList(SysConfigBo config, PageQuery pageQuery) { public TableDataInfo<SysConfigVo> selectPageConfigList(SysConfigBo config, PageQuery pageQuery) {
LambdaQueryWrapper<SysConfig> lqw = buildQueryWrapper(config); LambdaQueryWrapper<SysConfig> lqw = buildQueryWrapper(config);

View File

@@ -7,7 +7,6 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StreamUtils;
import org.dromara.system.domain.SysDept;
import org.dromara.system.domain.SysRoleDept; import org.dromara.system.domain.SysRoleDept;
import org.dromara.system.mapper.SysDeptMapper; import org.dromara.system.mapper.SysDeptMapper;
import org.dromara.system.mapper.SysRoleDeptMapper; import org.dromara.system.mapper.SysRoleDeptMapper;
@@ -66,13 +65,8 @@ public class SysDataScopeServiceImpl implements ISysDataScopeService {
if (ObjectUtil.isNull(deptId)) { if (ObjectUtil.isNull(deptId)) {
return "-1"; return "-1";
} }
List<SysDept> deptList = deptMapper.selectListByParentId(deptId); List<Long> deptIds = deptMapper.selectDeptAndChildById(deptId);
List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId); return CollUtil.isNotEmpty(deptIds) ? StreamUtils.join(deptIds, Convert::toStr) : "-1";
ids.add(deptId);
if (CollUtil.isNotEmpty(ids)) {
return StreamUtils.join(ids, Convert::toStr);
}
return "-1";
} }
} }

View File

@@ -36,10 +36,7 @@ import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/** /**
* 部门管理 服务实现 * 部门管理 服务实现
@@ -110,10 +107,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService {
if (ObjectUtil.isNotNull(bo.getBelongDeptId())) { if (ObjectUtil.isNotNull(bo.getBelongDeptId())) {
//部门树搜索 //部门树搜索
lqw.and(x -> { lqw.and(x -> {
Long parentId = bo.getBelongDeptId(); List<Long> deptIds = baseMapper.selectDeptAndChildById(bo.getBelongDeptId());
List<SysDept> deptList = baseMapper.selectListByParentId(parentId);
List<Long> deptIds = StreamUtils.toList(deptList, SysDept::getDeptId);
deptIds.add(parentId);
x.in(SysDept::getDeptId, deptIds); x.in(SysDept::getDeptId, deptIds);
}); });
} }
@@ -409,4 +403,24 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService {
return baseMapper.deleteById(deptId); return baseMapper.deleteById(deptId);
} }
/**
* 根据部门 ID 列表查询部门名称映射关系
*
* @param deptIds 部门 ID 列表
* @return Map其中 key 为部门 IDvalue 为对应的部门名称
*/
@Override
public Map<Long, String> selectDeptNamesByIds(List<Long> deptIds) {
if (CollUtil.isEmpty(deptIds)) {
return Collections.emptyMap();
}
List<SysDept> list = baseMapper.selectList(
new LambdaQueryWrapper<SysDept>()
.select(SysDept::getDeptId, SysDept::getDeptName)
.in(SysDept::getDeptId, deptIds)
);
return StreamUtils.toMap(list, SysDept::getDeptId, SysDept::getDeptName);
}
} }

View File

@@ -33,6 +33,13 @@ public class SysDictDataServiceImpl implements ISysDictDataService {
private final SysDictDataMapper baseMapper; private final SysDictDataMapper baseMapper;
/**
* 分页查询字典数据列表
*
* @param dictData 查询条件
* @param pageQuery 分页参数
* @return 字典数据分页列表
*/
@Override @Override
public TableDataInfo<SysDictDataVo> selectPageDictDataList(SysDictDataBo dictData, PageQuery pageQuery) { public TableDataInfo<SysDictDataVo> selectPageDictDataList(SysDictDataBo dictData, PageQuery pageQuery) {
LambdaQueryWrapper<SysDictData> lqw = buildQueryWrapper(dictData); LambdaQueryWrapper<SysDictData> lqw = buildQueryWrapper(dictData);

View File

@@ -48,6 +48,13 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
private final SysDictTypeMapper baseMapper; private final SysDictTypeMapper baseMapper;
private final SysDictDataMapper dictDataMapper; private final SysDictDataMapper dictDataMapper;
/**
* 分页查询字典类型列表
*
* @param dictType 查询条件
* @param pageQuery 分页参数
* @return 字典类型分页列表
*/
@Override @Override
public TableDataInfo<SysDictTypeVo> selectPageDictTypeList(SysDictTypeBo dictType, PageQuery pageQuery) { public TableDataInfo<SysDictTypeVo> selectPageDictTypeList(SysDictTypeBo dictType, PageQuery pageQuery) {
LambdaQueryWrapper<SysDictType> lqw = buildQueryWrapper(dictType); LambdaQueryWrapper<SysDictType> lqw = buildQueryWrapper(dictType);

View File

@@ -108,6 +108,13 @@ public class SysLogininforServiceImpl implements ISysLogininforService {
return "[" + msg.toString() + "]"; return "[" + msg.toString() + "]";
} }
/**
* 分页查询登录日志列表
*
* @param logininfor 查询条件
* @param pageQuery 分页参数
* @return 登录日志分页列表
*/
@Override @Override
public TableDataInfo<SysLogininforVo> selectPageLogininforList(SysLogininforBo logininfor, PageQuery pageQuery) { public TableDataInfo<SysLogininforVo> selectPageLogininforList(SysLogininforBo logininfor, PageQuery pageQuery) {
Map<String, Object> params = logininfor.getParams(); Map<String, Object> params = logininfor.getParams();

View File

@@ -5,8 +5,6 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.SystemConstants;
@@ -67,29 +65,20 @@ public class SysMenuServiceImpl implements ISysMenuService {
@Override @Override
public List<SysMenuVo> selectMenuList(SysMenuBo menu, Long userId) { public List<SysMenuVo> selectMenuList(SysMenuBo menu, Long userId) {
List<SysMenuVo> menuList; List<SysMenuVo> menuList;
// 管理员显示所有菜单信息 LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
if (LoginHelper.isSuperAdmin(userId)) { // 管理员显示所有菜单信息 不是管理员 按用户id过滤菜单
menuList = baseMapper.selectVoList(new LambdaQueryWrapper<SysMenu>() if (!LoginHelper.isSuperAdmin(userId)) {
.like(StringUtils.isNotBlank(menu.getMenuName()), SysMenu::getMenuName, menu.getMenuName()) // 通过用户id获取角色id 通过角色id获取菜单id 然后in菜单
wrapper.inSql(SysMenu::getMenuId, baseMapper.buildMenuByUserSql(userId));
}
menuList = baseMapper.selectVoList(
wrapper.like(StringUtils.isNotBlank(menu.getMenuName()), SysMenu::getMenuName, menu.getMenuName())
.eq(StringUtils.isNotBlank(menu.getVisible()), SysMenu::getVisible, menu.getVisible()) .eq(StringUtils.isNotBlank(menu.getVisible()), SysMenu::getVisible, menu.getVisible())
.eq(StringUtils.isNotBlank(menu.getStatus()), SysMenu::getStatus, menu.getStatus()) .eq(StringUtils.isNotBlank(menu.getStatus()), SysMenu::getStatus, menu.getStatus())
.eq(StringUtils.isNotBlank(menu.getMenuType()), SysMenu::getMenuType, menu.getMenuType()) .eq(StringUtils.isNotBlank(menu.getMenuType()), SysMenu::getMenuType, menu.getMenuType())
.eq(ObjectUtil.isNotNull(menu.getParentId()), SysMenu::getParentId, menu.getParentId()) .eq(ObjectUtil.isNotNull(menu.getParentId()), SysMenu::getParentId, menu.getParentId())
.orderByAsc(SysMenu::getParentId) .orderByAsc(SysMenu::getParentId)
.orderByAsc(SysMenu::getOrderNum)); .orderByAsc(SysMenu::getOrderNum));
} else {
QueryWrapper<SysMenu> wrapper = Wrappers.query();
wrapper.inSql("r.role_id", "select role_id from sys_user_role where user_id = " + userId)
.like(StringUtils.isNotBlank(menu.getMenuName()), "m.menu_name", menu.getMenuName())
.eq(StringUtils.isNotBlank(menu.getVisible()), "m.visible", menu.getVisible())
.eq(StringUtils.isNotBlank(menu.getStatus()), "m.status", menu.getStatus())
.eq(StringUtils.isNotBlank(menu.getMenuType()), "m.menu_type", menu.getMenuType())
.eq(ObjectUtil.isNotNull(menu.getParentId()), "m.parent_id", menu.getParentId())
.orderByAsc("m.parent_id")
.orderByAsc("m.order_num");
List<SysMenu> list = baseMapper.selectMenuListByUserId(wrapper);
menuList = MapstructUtils.convert(list, SysMenuVo.class);
}
return menuList; return menuList;
} }
@@ -141,7 +130,13 @@ public class SysMenuServiceImpl implements ISysMenuService {
if (LoginHelper.isSuperAdmin(userId)) { if (LoginHelper.isSuperAdmin(userId)) {
menus = baseMapper.selectMenuTreeAll(); menus = baseMapper.selectMenuTreeAll();
} else { } else {
menus = baseMapper.selectMenuTreeByUserId(userId); LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
menus = baseMapper.selectList(
wrapper.in(SysMenu::getMenuType, SystemConstants.TYPE_DIR, SystemConstants.TYPE_MENU)
.eq(SysMenu::getStatus, SystemConstants.NORMAL)
.inSql(SysMenu::getMenuId, baseMapper.buildMenuByUserSql(userId))
.orderByAsc(SysMenu::getParentId)
.orderByAsc(SysMenu::getOrderNum));
} }
return getChildPerms(menus, Constants.TOP_PARENT_ID); return getChildPerms(menus, Constants.TOP_PARENT_ID);
} }

View File

@@ -3,6 +3,7 @@ package org.dromara.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
@@ -16,7 +17,6 @@ import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysNoticeMapper; import org.dromara.system.mapper.SysNoticeMapper;
import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.ISysNoticeService; import org.dromara.system.service.ISysNoticeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Arrays; import java.util.Arrays;
@@ -34,6 +34,13 @@ public class SysNoticeServiceImpl implements ISysNoticeService {
private final SysNoticeMapper baseMapper; private final SysNoticeMapper baseMapper;
private final SysUserMapper userMapper; private final SysUserMapper userMapper;
/**
* 分页查询通知公告列表
*
* @param notice 查询条件
* @param pageQuery 分页参数
* @return 通知公告分页列表
*/
@Override @Override
public TableDataInfo<SysNoticeVo> selectPageNoticeList(SysNoticeBo notice, PageQuery pageQuery) { public TableDataInfo<SysNoticeVo> selectPageNoticeList(SysNoticeBo notice, PageQuery pageQuery) {
LambdaQueryWrapper<SysNotice> lqw = buildQueryWrapper(notice); LambdaQueryWrapper<SysNotice> lqw = buildQueryWrapper(notice);

View File

@@ -49,6 +49,13 @@ public class SysOperLogServiceImpl implements ISysOperLogService {
insertOperlog(operLog); insertOperlog(operLog);
} }
/**
* 分页查询操作日志列表
*
* @param operLog 查询条件
* @param pageQuery 分页参数
* @return 操作日志分页列表
*/
@Override @Override
public TableDataInfo<SysOperLogVo> selectPageOperLogList(SysOperLogBo operLog, PageQuery pageQuery) { public TableDataInfo<SysOperLogVo> selectPageOperLogList(SysOperLogBo operLog, PageQuery pageQuery) {
LambdaQueryWrapper<SysOperLog> lqw = buildQueryWrapper(operLog); LambdaQueryWrapper<SysOperLog> lqw = buildQueryWrapper(operLog);

View File

@@ -17,6 +17,7 @@ import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.file.FileUtils; import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.oss.core.OssClient; import org.dromara.common.oss.core.OssClient;
@@ -24,6 +25,7 @@ import org.dromara.common.oss.entity.UploadResult;
import org.dromara.common.oss.enums.AccessPolicyType; import org.dromara.common.oss.enums.AccessPolicyType;
import org.dromara.common.oss.factory.OssFactory; import org.dromara.common.oss.factory.OssFactory;
import org.dromara.system.domain.SysOss; import org.dromara.system.domain.SysOss;
import org.dromara.system.domain.SysOssExt;
import org.dromara.system.domain.bo.SysOssBo; import org.dromara.system.domain.bo.SysOssBo;
import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.mapper.SysOssMapper; import org.dromara.system.mapper.SysOssMapper;
@@ -199,8 +201,11 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
} catch (IOException e) { } catch (IOException e) {
throw new ServiceException(e.getMessage()); throw new ServiceException(e.getMessage());
} }
SysOssExt ext1 = new SysOssExt();
ext1.setFileSize(file.getSize());
ext1.setContentType(file.getContentType());
// 保存文件信息 // 保存文件信息
return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult); return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult, ext1);
} }
/** /**
@@ -215,18 +220,21 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length()); String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
OssClient storage = OssFactory.instance(); OssClient storage = OssFactory.instance();
UploadResult uploadResult = storage.uploadSuffix(file, suffix); UploadResult uploadResult = storage.uploadSuffix(file, suffix);
SysOssExt ext1 = new SysOssExt();
ext1.setFileSize(file.length());
// 保存文件信息 // 保存文件信息
return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult); return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult, ext1);
} }
@NotNull @NotNull
private SysOssVo buildResultEntity(String originalfileName, String suffix, String configKey, UploadResult uploadResult) { private SysOssVo buildResultEntity(String originalfileName, String suffix, String configKey, UploadResult uploadResult, SysOssExt ext1) {
SysOss oss = new SysOss(); SysOss oss = new SysOss();
oss.setUrl(uploadResult.getUrl()); oss.setUrl(uploadResult.getUrl());
oss.setFileSuffix(suffix); oss.setFileSuffix(suffix);
oss.setFileName(uploadResult.getFilename()); oss.setFileName(uploadResult.getFilename());
oss.setOriginalName(originalfileName); oss.setOriginalName(originalfileName);
oss.setService(configKey); oss.setService(configKey);
oss.setExt1(JsonUtils.toJsonString(ext1));
baseMapper.insert(oss); baseMapper.insert(oss);
SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class); SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class);
return this.matchingUrl(sysOssVo); return this.matchingUrl(sysOssVo);

View File

@@ -14,7 +14,6 @@ import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.domain.SysDept;
import org.dromara.system.domain.SysPost; import org.dromara.system.domain.SysPost;
import org.dromara.system.domain.SysUserPost; import org.dromara.system.domain.SysUserPost;
import org.dromara.system.domain.bo.SysPostBo; import org.dromara.system.domain.bo.SysPostBo;
@@ -25,7 +24,7 @@ import org.dromara.system.mapper.SysUserPostMapper;
import org.dromara.system.service.ISysPostService; import org.dromara.system.service.ISysPostService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Arrays; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -42,6 +41,13 @@ public class SysPostServiceImpl implements ISysPostService, PostService {
private final SysDeptMapper deptMapper; private final SysDeptMapper deptMapper;
private final SysUserPostMapper userPostMapper; private final SysUserPostMapper userPostMapper;
/**
* 分页查询岗位列表
*
* @param post 查询条件
* @param pageQuery 分页参数
* @return 岗位分页列表
*/
@Override @Override
public TableDataInfo<SysPostVo> selectPagePostList(SysPostBo post, PageQuery pageQuery) { public TableDataInfo<SysPostVo> selectPagePostList(SysPostBo post, PageQuery pageQuery) {
Page<SysPostVo> page = baseMapper.selectPagePostList(pageQuery.build(), buildQueryWrapper(post)); Page<SysPostVo> page = baseMapper.selectPagePostList(pageQuery.build(), buildQueryWrapper(post));
@@ -92,9 +98,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService {
} else if (ObjectUtil.isNotNull(bo.getBelongDeptId())) { } else if (ObjectUtil.isNotNull(bo.getBelongDeptId())) {
//部门树搜索 //部门树搜索
wrapper.and(x -> { wrapper.and(x -> {
List<SysDept> deptList = deptMapper.selectListByParentId(bo.getBelongDeptId()); List<Long> deptIds = deptMapper.selectDeptAndChildById(bo.getBelongDeptId());
List<Long> deptIds = StreamUtils.toList(deptList, SysDept::getDeptId);
deptIds.add(bo.getBelongDeptId());
x.in(SysPost::getDeptId, deptIds); x.in(SysPost::getDeptId, deptIds);
}); });
} }
@@ -217,14 +221,14 @@ public class SysPostServiceImpl implements ISysPostService, PostService {
* @return 结果 * @return 结果
*/ */
@Override @Override
public int deletePostByIds(Long[] postIds) { public int deletePostByIds(List<Long> postIds) {
for (Long postId : postIds) { List<SysPost> list = baseMapper.selectByIds(postIds);
SysPost post = baseMapper.selectById(postId); for (SysPost post : list) {
if (countUserPostById(postId) > 0) { if (this.countUserPostById(post.getPostId()) > 0) {
throw new ServiceException(String.format("%1$s已分配不能删除!", post.getPostName())); throw new ServiceException(String.format("%1$s已分配不能删除!", post.getPostName()));
} }
} }
return baseMapper.deleteByIds(Arrays.asList(postIds)); return baseMapper.deleteByIds(postIds);
} }
/** /**
@@ -251,4 +255,23 @@ public class SysPostServiceImpl implements ISysPostService, PostService {
return baseMapper.updateById(post); return baseMapper.updateById(post);
} }
/**
* 根据岗位 ID 列表查询岗位名称映射关系
*
* @param postIds 岗位 ID 列表
* @return Map其中 key 为岗位 IDvalue 为对应的岗位名称
*/
@Override
public Map<Long, String> selectPostNamesByIds(List<Long> postIds) {
if (CollUtil.isEmpty(postIds)) {
return Collections.emptyMap();
}
List<SysPost> list = baseMapper.selectList(
new LambdaQueryWrapper<SysPost>()
.select(SysPost::getPostId, SysPost::getPostName)
.in(SysPost::getPostId, postIds)
);
return StreamUtils.toMap(list, SysPost::getPostId, SysPost::getPostName);
}
} }

View File

@@ -7,7 +7,6 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -55,6 +54,13 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
private final SysUserRoleMapper userRoleMapper; private final SysUserRoleMapper userRoleMapper;
private final SysRoleDeptMapper roleDeptMapper; private final SysRoleDeptMapper roleDeptMapper;
/**
* 分页查询角色列表
*
* @param role 查询条件
* @param pageQuery 分页参数
* @return 角色分页列表
*/
@Override @Override
public TableDataInfo<SysRoleVo> selectPageRoleList(SysRoleBo role, PageQuery pageQuery) { public TableDataInfo<SysRoleVo> selectPageRoleList(SysRoleBo role, PageQuery pageQuery) {
Page<SysRoleVo> page = baseMapper.selectPageRoleList(pageQuery.build(), this.buildQueryWrapper(role)); Page<SysRoleVo> page = baseMapper.selectPageRoleList(pageQuery.build(), this.buildQueryWrapper(role));
@@ -62,7 +68,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
} }
/** /**
* 根据条件分页查询角色数据 * 根据条件查询角色数据
* *
* @param role 角色信息 * @param role 角色信息
* @return 角色数据集合信息 * @return 角色数据集合信息
@@ -74,15 +80,14 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
private Wrapper<SysRole> buildQueryWrapper(SysRoleBo bo) { private Wrapper<SysRole> buildQueryWrapper(SysRoleBo bo) {
Map<String, Object> params = bo.getParams(); Map<String, Object> params = bo.getParams();
QueryWrapper<SysRole> wrapper = Wrappers.query(); LambdaQueryWrapper<SysRole> wrapper = Wrappers.lambdaQuery();
wrapper.eq("r.del_flag", SystemConstants.NORMAL) wrapper.eq(ObjectUtil.isNotNull(bo.getRoleId()), SysRole::getRoleId, bo.getRoleId())
.eq(ObjectUtil.isNotNull(bo.getRoleId()), "r.role_id", bo.getRoleId()) .like(StringUtils.isNotBlank(bo.getRoleName()), SysRole::getRoleName, bo.getRoleName())
.like(StringUtils.isNotBlank(bo.getRoleName()), "r.role_name", bo.getRoleName()) .eq(StringUtils.isNotBlank(bo.getStatus()), SysRole::getStatus, bo.getStatus())
.eq(StringUtils.isNotBlank(bo.getStatus()), "r.status", bo.getStatus()) .like(StringUtils.isNotBlank(bo.getRoleKey()), SysRole::getRoleKey, bo.getRoleKey())
.like(StringUtils.isNotBlank(bo.getRoleKey()), "r.role_key", bo.getRoleKey())
.between(params.get("beginTime") != null && params.get("endTime") != null, .between(params.get("beginTime") != null && params.get("endTime") != null,
"r.create_time", params.get("beginTime"), params.get("endTime")) SysRole::getCreateTime, params.get("beginTime"), params.get("endTime"))
.orderByAsc("r.role_sort").orderByAsc("r.create_time"); .orderByAsc(SysRole::getRoleSort).orderByAsc(SysRole::getCreateTime);
return wrapper; return wrapper;
} }
@@ -176,9 +181,9 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
*/ */
@Override @Override
public List<SysRoleVo> selectRoleByIds(List<Long> roleIds) { public List<SysRoleVo> selectRoleByIds(List<Long> roleIds) {
return baseMapper.selectRoleList(new QueryWrapper<SysRole>() return baseMapper.selectRoleList(new LambdaQueryWrapper<SysRole>()
.eq("r.status", SystemConstants.NORMAL) .eq(SysRole::getStatus, SystemConstants.NORMAL)
.in(CollUtil.isNotEmpty(roleIds), "r.role_id", roleIds)); .in(CollUtil.isNotEmpty(roleIds), SysRole::getRoleId, roleIds));
} }
/** /**
@@ -249,14 +254,23 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
if (ObjectUtil.isNull(roleId)) { if (ObjectUtil.isNull(roleId)) {
return; return;
} }
if (LoginHelper.isSuperAdmin()) { this.checkRoleDataScope(Collections.singletonList(roleId));
}
/**
* 校验角色是否有数据权限
*
* @param roleIds 角色ID列表支持传单个ID
*/
@Override
public void checkRoleDataScope(List<Long> roleIds) {
if (CollUtil.isEmpty(roleIds) || LoginHelper.isSuperAdmin()) {
return; return;
} }
List<SysRoleVo> roles = this.selectRoleList(new SysRoleBo(roleId)); long count = baseMapper.selectRoleCount(roleIds);
if (CollUtil.isEmpty(roles)) { if (count != roleIds.size()) {
throw new ServiceException("没有权限访问角色数据!"); throw new ServiceException("没有权限访问部分角色数据!");
} }
} }
/** /**
@@ -359,7 +373,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
rm.setMenuId(menuId); rm.setMenuId(menuId);
list.add(rm); list.add(rm);
} }
if (list.size() > 0) { if (CollUtil.isNotEmpty(list)) {
rows = roleMenuMapper.insertBatch(list) ? list.size() : 0; rows = roleMenuMapper.insertBatch(list) ? list.size() : 0;
} }
return rows; return rows;
@@ -380,7 +394,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
rd.setDeptId(deptId); rd.setDeptId(deptId);
list.add(rd); list.add(rd);
} }
if (list.size() > 0) { if (CollUtil.isNotEmpty(list)) {
rows = roleDeptMapper.insertBatch(list) ? list.size() : 0; rows = roleDeptMapper.insertBatch(list) ? list.size() : 0;
} }
return rows; return rows;
@@ -412,21 +426,20 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
@CacheEvict(cacheNames = CacheNames.SYS_ROLE_CUSTOM, allEntries = true) @CacheEvict(cacheNames = CacheNames.SYS_ROLE_CUSTOM, allEntries = true)
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public int deleteRoleByIds(Long[] roleIds) { public int deleteRoleByIds(List<Long> roleIds) {
for (Long roleId : roleIds) { this.checkRoleDataScope(roleIds);
SysRole role = baseMapper.selectById(roleId); List<SysRole> roles = baseMapper.selectByIds(roleIds);
for (SysRole role : roles) {
checkRoleAllowed(BeanUtil.toBean(role, SysRoleBo.class)); checkRoleAllowed(BeanUtil.toBean(role, SysRoleBo.class));
checkRoleDataScope(roleId); if (countUserRoleByRoleId(role.getRoleId()) > 0) {
if (countUserRoleByRoleId(roleId) > 0) {
throw new ServiceException(String.format("%1$s已分配不能删除!", role.getRoleName())); throw new ServiceException(String.format("%1$s已分配不能删除!", role.getRoleName()));
} }
} }
List<Long> ids = Arrays.asList(roleIds);
// 删除角色与菜单关联 // 删除角色与菜单关联
roleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenu>().in(SysRoleMenu::getRoleId, ids)); roleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenu>().in(SysRoleMenu::getRoleId, roleIds));
// 删除角色与部门关联 // 删除角色与部门关联
roleDeptMapper.delete(new LambdaQueryWrapper<SysRoleDept>().in(SysRoleDept::getRoleId, ids)); roleDeptMapper.delete(new LambdaQueryWrapper<SysRoleDept>().in(SysRoleDept::getRoleId, roleIds));
return baseMapper.deleteByIds(ids); return baseMapper.deleteByIds(roleIds);
} }
/** /**
@@ -437,6 +450,9 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
*/ */
@Override @Override
public int deleteAuthUser(SysUserRole userRole) { public int deleteAuthUser(SysUserRole userRole) {
if (LoginHelper.getUserId().equals(userRole.getUserId())) {
throw new ServiceException("不允许修改当前用户角色!");
}
int rows = userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>() int rows = userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()
.eq(SysUserRole::getRoleId, userRole.getRoleId()) .eq(SysUserRole::getRoleId, userRole.getRoleId())
.eq(SysUserRole::getUserId, userRole.getUserId())); .eq(SysUserRole::getUserId, userRole.getUserId()));
@@ -456,6 +472,9 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
@Override @Override
public int deleteAuthUsers(Long roleId, Long[] userIds) { public int deleteAuthUsers(Long roleId, Long[] userIds) {
List<Long> ids = List.of(userIds); List<Long> ids = List.of(userIds);
if (ids.contains(LoginHelper.getUserId())) {
throw new ServiceException("不允许修改当前用户角色!");
}
int rows = userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>() int rows = userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()
.eq(SysUserRole::getRoleId, roleId) .eq(SysUserRole::getRoleId, roleId)
.in(SysUserRole::getUserId, ids)); .in(SysUserRole::getUserId, ids));
@@ -477,6 +496,9 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
// 新增用户与角色管理 // 新增用户与角色管理
int rows = 1; int rows = 1;
List<Long> ids = List.of(userIds); List<Long> ids = List.of(userIds);
if (ids.contains(LoginHelper.getUserId())) {
throw new ServiceException("不允许修改当前用户角色!");
}
List<SysUserRole> list = StreamUtils.toList(ids, userId -> { List<SysUserRole> list = StreamUtils.toList(ids, userId -> {
SysUserRole ur = new SysUserRole(); SysUserRole ur = new SysUserRole();
ur.setUserId(userId); ur.setUserId(userId);
@@ -492,6 +514,17 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
return rows; return rows;
} }
/**
* 根据角色ID清除该角色关联的所有在线用户的登录状态踢出在线用户
*
* <p>
* 先判断角色是否绑定用户,若无绑定则直接返回
* 然后遍历当前所有在线Token查找拥有该角色的用户并强制登出
* 注意:在线用户量过大时,操作可能导致 Redis 阻塞,需谨慎调用
* </p>
*
* @param roleId 角色ID
*/
@Override @Override
public void cleanOnlineUserByRole(Long roleId) { public void cleanOnlineUserByRole(Long roleId) {
// 如果角色未绑定用户 直接返回 // 如果角色未绑定用户 直接返回
@@ -523,6 +556,16 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
}); });
} }
/**
* 根据用户ID列表清除对应在线用户的登录状态踢出指定用户
*
* <p>
* 遍历当前所有在线Token匹配用户ID列表中的用户强制登出
* 注意:在线用户量过大时,操作可能导致 Redis 阻塞,需谨慎调用
* </p>
*
* @param userIds 需要清除的用户ID列表
*/
@Override @Override
public void cleanOnlineUser(List<Long> userIds) { public void cleanOnlineUser(List<Long> userIds) {
List<String> keys = StpUtil.searchTokenValue("", 0, -1, false); List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
@@ -549,4 +592,23 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
}); });
} }
/**
* 根据角色 ID 列表查询角色名称映射关系
*
* @param roleIds 角色 ID 列表
* @return Map其中 key 为角色 IDvalue 为对应的角色名称
*/
@Override
public Map<Long, String> selectRoleNamesByIds(List<Long> roleIds) {
if (CollUtil.isEmpty(roleIds)) {
return Collections.emptyMap();
}
List<SysRole> list = baseMapper.selectList(
new LambdaQueryWrapper<SysRole>()
.select(SysRole::getRoleId, SysRole::getRoleName)
.in(SysRole::getRoleId, roleIds)
);
return StreamUtils.toMap(list, SysRole::getRoleId, SysRole::getRoleName);
}
} }

View File

@@ -22,7 +22,9 @@ import org.dromara.common.core.utils.*;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.*; import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.SysUserPost;
import org.dromara.system.domain.SysUserRole;
import org.dromara.system.domain.bo.SysUserBo; import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.domain.vo.SysPostVo; import org.dromara.system.domain.vo.SysPostVo;
import org.dromara.system.domain.vo.SysRoleVo; import org.dromara.system.domain.vo.SysRoleVo;
@@ -36,7 +38,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
/** /**
* 用户 业务层处理 * 用户 业务层处理
@@ -79,10 +80,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
.between(params.get("beginTime") != null && params.get("endTime") != null, .between(params.get("beginTime") != null && params.get("endTime") != null,
"u.create_time", params.get("beginTime"), params.get("endTime")) "u.create_time", params.get("beginTime"), params.get("endTime"))
.and(ObjectUtil.isNotNull(user.getDeptId()), w -> { .and(ObjectUtil.isNotNull(user.getDeptId()), w -> {
List<SysDept> deptList = deptMapper.selectListByParentId(user.getDeptId()); List<Long> deptIds = deptMapper.selectDeptAndChildById(user.getDeptId());
List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId); w.in("u.dept_id", deptIds);
ids.add(user.getDeptId());
w.in("u.dept_id", ids);
}).orderByAsc("u.user_id"); }).orderByAsc("u.user_id");
return baseMapper.selectUserExportList(wrapper); return baseMapper.selectUserExportList(wrapper);
} }
@@ -100,9 +99,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
.between(params.get("beginTime") != null && params.get("endTime") != null, .between(params.get("beginTime") != null && params.get("endTime") != null,
SysUser::getCreateTime, params.get("beginTime"), params.get("endTime")) SysUser::getCreateTime, params.get("beginTime"), params.get("endTime"))
.and(ObjectUtil.isNotNull(user.getDeptId()), w -> { .and(ObjectUtil.isNotNull(user.getDeptId()), w -> {
List<SysDept> deptList = deptMapper.selectListByParentId(user.getDeptId()); List<Long> ids = deptMapper.selectDeptAndChildById(user.getDeptId());
List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);
ids.add(user.getDeptId());
w.in(SysUser::getDeptId, ids); w.in(SysUser::getDeptId, ids);
}).orderByAsc(SysUser::getUserId); }).orderByAsc(SysUser::getUserId);
if (StringUtils.isNotBlank(user.getExcludeUserIds())) { if (StringUtils.isNotBlank(user.getExcludeUserIds())) {
@@ -454,21 +451,31 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
* @param clear 清除已存在的关联数据 * @param clear 清除已存在的关联数据
*/ */
private void insertUserPost(SysUserBo user, boolean clear) { private void insertUserPost(SysUserBo user, boolean clear) {
Long[] posts = user.getPostIds(); Long[] postIdArr = user.getPostIds();
if (ArrayUtil.isNotEmpty(posts)) { if (ArrayUtil.isEmpty(postIdArr)) {
if (clear) { return;
// 删除用户与岗位关联 }
userPostMapper.delete(new LambdaQueryWrapper<SysUserPost>().eq(SysUserPost::getUserId, user.getUserId())); List<Long> postIds = Arrays.asList(postIdArr);
}
// 新增用户与岗位管理 // 校验是否有权限操作这些岗位(含数据权限控制)
List<SysUserPost> list = StreamUtils.toList(List.of(posts), postId -> { if (postMapper.selectPostCount(postIds) != postIds.size()) {
throw new ServiceException("没有权限访问岗位的数据");
}
// 是否清除旧的用户岗位绑定
if (clear) {
userPostMapper.delete(new LambdaQueryWrapper<SysUserPost>().eq(SysUserPost::getUserId, user.getUserId()));
}
// 构建用户岗位关联列表并批量插入
List<SysUserPost> list = StreamUtils.toList(postIds,
postId -> {
SysUserPost up = new SysUserPost(); SysUserPost up = new SysUserPost();
up.setUserId(user.getUserId()); up.setUserId(user.getUserId());
up.setPostId(postId); up.setPostId(postId);
return up; return up;
}); });
userPostMapper.insertBatch(list); userPostMapper.insertBatch(list);
}
} }
/** /**
@@ -479,30 +486,36 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
* @param clear 清除已存在的关联数据 * @param clear 清除已存在的关联数据
*/ */
private void insertUserRole(Long userId, Long[] roleIds, boolean clear) { private void insertUserRole(Long userId, Long[] roleIds, boolean clear) {
if (ArrayUtil.isNotEmpty(roleIds)) { if (ArrayUtil.isEmpty(roleIds)) {
List<Long> roleList = new ArrayList<>(List.of(roleIds)); return;
if (!LoginHelper.isSuperAdmin(userId)) { }
roleList.remove(SystemConstants.SUPER_ADMIN_ID);
} List<Long> roleList = new ArrayList<>(Arrays.asList(roleIds));
// 判断是否具有此角色的操作权限
List<SysRoleVo> roles = roleMapper.selectRoleList( // 非超级管理员,禁止包含超级管理员角色
new QueryWrapper<SysRole>().in("r.role_id", roleList)); if (!LoginHelper.isSuperAdmin(userId)) {
if (CollUtil.isEmpty(roles)) { roleList.remove(SystemConstants.SUPER_ADMIN_ID);
throw new ServiceException("没有权限访问角色的数据"); }
}
if (clear) { // 校验是否有权限访问这些角色(含数据权限控制)
// 删除用户与角色关联 if (roleMapper.selectRoleCount(roleList) != roleList.size()) {
userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, userId)); throw new ServiceException("没有权限访问角色的数据");
} }
// 新增用户与角色管理
List<SysUserRole> list = StreamUtils.toList(roleList, roleId -> { // 是否清除原有绑定
if (clear) {
userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, userId));
}
// 批量插入用户-角色关联
List<SysUserRole> list = StreamUtils.toList(roleList,
roleId -> {
SysUserRole ur = new SysUserRole(); SysUserRole ur = new SysUserRole();
ur.setUserId(userId); ur.setUserId(userId);
ur.setRoleId(roleId); ur.setRoleId(roleId);
return ur; return ur;
}); });
userRoleMapper.insertBatch(list); userRoleMapper.insertBatch(list);
}
} }
/** /**
@@ -748,69 +761,12 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
if (CollUtil.isEmpty(userIds)) { if (CollUtil.isEmpty(userIds)) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
return baseMapper.selectList( List<SysUser> list = baseMapper.selectList(
new LambdaQueryWrapper<SysUser>() new LambdaQueryWrapper<SysUser>()
.select(SysUser::getUserId, SysUser::getNickName) .select(SysUser::getUserId, SysUser::getNickName)
.in(SysUser::getUserId, userIds) .in(SysUser::getUserId, userIds)
).stream() );
.collect(Collectors.toMap(SysUser::getUserId, SysUser::getNickName)); return StreamUtils.toMap(list, SysUser::getUserId, SysUser::getNickName);
}
/**
* 根据角色 ID 列表查询角色名称映射关系
*
* @param roleIds 角色 ID 列表
* @return Map其中 key 为角色 IDvalue 为对应的角色名称
*/
@Override
public Map<Long, String> selectRoleNamesByIds(List<Long> roleIds) {
if (CollUtil.isEmpty(roleIds)) {
return Collections.emptyMap();
}
return roleMapper.selectList(
new LambdaQueryWrapper<SysRole>()
.select(SysRole::getRoleId, SysRole::getRoleName)
.in(SysRole::getRoleId, roleIds)
).stream()
.collect(Collectors.toMap(SysRole::getRoleId, SysRole::getRoleName));
}
/**
* 根据部门 ID 列表查询部门名称映射关系
*
* @param deptIds 部门 ID 列表
* @return Map其中 key 为部门 IDvalue 为对应的部门名称
*/
@Override
public Map<Long, String> selectDeptNamesByIds(List<Long> deptIds) {
if (CollUtil.isEmpty(deptIds)) {
return Collections.emptyMap();
}
return deptMapper.selectList(
new LambdaQueryWrapper<SysDept>()
.select(SysDept::getDeptId, SysDept::getDeptName)
.in(SysDept::getDeptId, deptIds)
).stream()
.collect(Collectors.toMap(SysDept::getDeptId, SysDept::getDeptName));
}
/**
* 根据岗位 ID 列表查询岗位名称映射关系
*
* @param postIds 岗位 ID 列表
* @return Map其中 key 为岗位 IDvalue 为对应的岗位名称
*/
@Override
public Map<Long, String> selectPostNamesByIds(List<Long> postIds) {
if (CollUtil.isEmpty(postIds)) {
return Collections.emptyMap();
}
return postMapper.selectList(
new LambdaQueryWrapper<SysPost>()
.select(SysPost::getPostId, SysPost::getPostName)
.in(SysPost::getPostId, postIds)
).stream()
.collect(Collectors.toMap(SysPost::getPostId, SysPost::getPostName));
} }
} }

View File

@@ -4,18 +4,4 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.system.mapper.SysDeptMapper"> <mapper namespace="org.dromara.system.mapper.SysDeptMapper">
<resultMap type="org.dromara.system.domain.vo.SysDeptVo" id="SysDeptResult">
</resultMap>
<select id="selectDeptListByRoleId" resultType="Long">
select d.dept_id
from sys_dept d
left join sys_role_dept rd on d.dept_id = rd.dept_id
where rd.role_id = #{roleId}
<if test="deptCheckStrictly">
and d.dept_id not in (select d.parent_id from sys_dept d inner join sys_role_dept rd on d.dept_id = rd.dept_id and rd.role_id = #{roleId})
</if>
order by d.parent_id, d.order_num
</select>
</mapper> </mapper>

View File

@@ -4,67 +4,4 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.system.mapper.SysMenuMapper"> <mapper namespace="org.dromara.system.mapper.SysMenuMapper">
<resultMap type="org.dromara.system.domain.SysMenu" id="SysMenuResult">
</resultMap>
<select id="selectMenuListByUserId" resultMap="SysMenuResult">
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query_param, m.visible, m.status,
m.perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
left join sys_role r on rm.role_id = r.role_id
${ew.getCustomSqlSegment}
</select>
<select id="selectMenuTreeByUserId" parameterType="Long" resultMap="SysMenuResult">
select distinct m.menu_id,
m.parent_id,
m.menu_name,
m.path,
m.component,
m.query_param,
m.visible,
m.status,
m.perms,
m.is_frame,
m.is_cache,
m.menu_type,
m.icon,
m.order_num,
m.create_time
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id and m.status = '0'
left join sys_role r on rm.role_id = r.role_id and r.status = '0'
where m.menu_type in ('M', 'C')
and r.role_id in (select role_id from sys_user_role where user_id = #{userId})
order by m.parent_id, m.order_num
</select>
<select id="selectMenuListByRoleId" resultType="Long">
select m.menu_id
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
where rm.role_id = #{roleId}
<if test="menuCheckStrictly">
and m.menu_id not in (select m.parent_id from sys_menu m inner join sys_role_menu rm on m.menu_id =
rm.menu_id and rm.role_id = #{roleId})
</if>
order by m.parent_id, m.order_num
</select>
<select id="selectMenuPermsByUserId" parameterType="Long" resultType="String">
select distinct m.perms
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id and m.status = '0'
left join sys_role r on r.role_id = rm.role_id and r.status = '0'
where r.role_id in (select role_id from sys_user_role where user_id = #{userId})
</select>
<select id="selectMenuPermsByRoleId" parameterType="Long" resultType="String">
select distinct m.perms
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
where m.status = '0' and rm.role_id = #{roleId}
</select>
</mapper> </mapper>

View File

@@ -4,15 +4,4 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.system.mapper.SysPostMapper"> <mapper namespace="org.dromara.system.mapper.SysPostMapper">
<resultMap type="org.dromara.system.domain.vo.SysPostVo" id="SysPostResult">
</resultMap>
<select id="selectPostsByUserId" parameterType="Long" resultMap="SysPostResult">
select p.post_id, p.dept_id, p.post_name, p.post_code, p.post_category
from sys_post p
left join sys_user_post up on up.post_id = p.post_id
left join sys_user u on u.user_id = up.user_id
where u.user_id = #{userId}
</select>
</mapper> </mapper>

View File

@@ -4,56 +4,4 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.system.mapper.SysRoleMapper"> <mapper namespace="org.dromara.system.mapper.SysRoleMapper">
<resultMap type="org.dromara.system.domain.vo.SysRoleVo" id="SysRoleResult">
</resultMap>
<sql id="selectRoleVo">
select distinct r.role_id,
r.role_name,
r.role_key,
r.role_sort,
r.data_scope,
r.menu_check_strictly,
r.dept_check_strictly,
r.status,
r.del_flag,
r.create_time,
r.remark
from sys_role r
left join sys_user_role sur on sur.role_id = r.role_id
left join sys_user u on u.user_id = sur.user_id
left join sys_dept d on u.dept_id = d.dept_id
</sql>
<select id="selectPageRoleList" resultMap="SysRoleResult">
<include refid="selectRoleVo"/>
${ew.getCustomSqlSegment}
</select>
<select id="selectRoleList" resultMap="SysRoleResult">
<include refid="selectRoleVo"/>
${ew.getCustomSqlSegment}
</select>
<select id="selectRolePermissionByUserId" parameterType="Long" resultMap="SysRoleResult">
<include refid="selectRoleVo"/>
WHERE r.del_flag = '0' and sur.user_id = #{userId}
</select>
<select id="selectRolesByUserId" parameterType="Long" resultMap="SysRoleResult">
select r.role_id,
r.role_name,
r.role_key,
r.role_sort,
r.data_scope,
r.status
from sys_role r
WHERE r.del_flag = '0' and r.role_id in (select role_id from sys_user_role where user_id = #{userId})
</select>
<select id="selectRoleById" resultMap="SysRoleResult">
<include refid="selectRoleVo"/>
WHERE r.del_flag = '0' and r.role_id = #{roleId}
</select>
</mapper> </mapper>

View File

@@ -4,10 +4,4 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.system.mapper.SysUserRoleMapper"> <mapper namespace="org.dromara.system.mapper.SysUserRoleMapper">
<select id="selectUserIdsByRoleId" resultType="Long">
select u.user_id from sys_user u
inner join sys_user_role sur
on u.user_id = sur.user_id and sur.role_id = #{roleId}
</select>
</mapper> </mapper>

View File

@@ -206,7 +206,7 @@ public class FlwTaskController extends BaseController {
*/ */
@GetMapping("/currentTaskAllUser/{taskId}") @GetMapping("/currentTaskAllUser/{taskId}")
public R<List<UserDTO>> currentTaskAllUser(@PathVariable Long taskId) { public R<List<UserDTO>> currentTaskAllUser(@PathVariable Long taskId) {
return R.ok(flwTaskService.currentTaskAllUser(taskId)); return R.ok(flwTaskService.currentTaskAllUser(List.of(taskId)));
} }
} }

View File

@@ -145,6 +145,7 @@ public class FlowTaskVo implements Serializable {
/** /**
* 办理人名称 * 办理人名称
*/ */
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "assigneeIds")
private String assigneeNames; private String assigneeNames;
/** /**

View File

@@ -3,11 +3,14 @@ package org.dromara.workflow.handler;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.dto.UserDTO;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.dto.FlowParams;
import org.dromara.warm.flow.core.handler.PermissionHandler; import org.dromara.warm.flow.core.handler.PermissionHandler;
import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.service.IFlwCommonService; import org.dromara.workflow.service.IFlwTaskAssigneeService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.Collections; import java.util.Collections;
@@ -24,7 +27,7 @@ import java.util.List;
@Slf4j @Slf4j
public class WorkflowPermissionHandler implements PermissionHandler { public class WorkflowPermissionHandler implements PermissionHandler {
private final IFlwCommonService flwCommonService; private final IFlwTaskAssigneeService flwTaskAssigneeService;
/** /**
* 办理人权限标识,比如用户,角色,部门等,用于校验是否有权限办理任务 * 办理人权限标识,比如用户,角色,部门等,用于校验是否有权限办理任务
@@ -51,9 +54,11 @@ public class WorkflowPermissionHandler implements PermissionHandler {
*/ */
@Override @Override
public List<String> convertPermissions(List<String> permissions) { public List<String> convertPermissions(List<String> permissions) {
if (CollUtil.isNotEmpty(permissions)) { if (CollUtil.isEmpty(permissions)) {
permissions = flwCommonService.buildUser(permissions); return permissions;
} }
return permissions; String storageIds = CollUtil.join(permissions, StringUtils.SEPARATOR);
List<UserDTO> users = flwTaskAssigneeService.fetchUsersByStorageIds(storageIds);
return StreamUtils.toList(users, userDTO -> String.valueOf(userDTO.getUserId()));
} }
} }

View File

@@ -1,6 +1,7 @@
package org.dromara.workflow.listener; package org.dromara.workflow.listener;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -136,33 +137,36 @@ public class WorkflowGlobalListener implements GlobalListener {
return; return;
} }
// 只有办理或者退回的时候才执行消息通知和抄送 // 只有办理或者退回的时候才执行消息通知和抄送
if (TaskStatusEnum.PASS.getStatus().equals(flowParams.getHisStatus()) if (!StringUtils.equalsAny(flowParams.getHisStatus(),
|| TaskStatusEnum.BACK.getStatus().equals(flowParams.getHisStatus())) { TaskStatusEnum.PASS.getStatus(), TaskStatusEnum.BACK.getStatus())) {
if (variable != null) { return;
if (variable.containsKey(FlowConstant.FLOW_COPY_LIST)) { }
List<FlowCopyBo> flowCopyList = (List<FlowCopyBo>) variable.get(FlowConstant.FLOW_COPY_LIST); if (ObjectUtil.isNull(variable)) {
// 添加抄送人 return;
flwTaskService.setCopy(task, flowCopyList); }
}
if (variable.containsKey(FlowConstant.MESSAGE_TYPE)) { if (variable.containsKey(FlowConstant.FLOW_COPY_LIST)) {
List<String> messageType = (List<String>) variable.get(FlowConstant.MESSAGE_TYPE); List<FlowCopyBo> flowCopyList = MapUtil.get(variable, FlowConstant.FLOW_COPY_LIST, new TypeReference<>() {});
String notice = (String) variable.get(FlowConstant.MESSAGE_NOTICE); // 添加抄送人
// 消息通知 flwTaskService.setCopy(task, flowCopyList);
if (CollUtil.isNotEmpty(messageType)) { }
flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice); if (variable.containsKey(FlowConstant.MESSAGE_TYPE)) {
} List<String> messageType = MapUtil.get(variable, FlowConstant.FLOW_COPY_LIST, new TypeReference<>() {});
} String notice = MapUtil.getStr(variable, FlowConstant.MESSAGE_NOTICE);
FlowInstance ins = new FlowInstance(); // 消息通知
Map<String, Object> variableMap = instance.getVariableMap(); if (CollUtil.isNotEmpty(messageType)) {
variableMap.remove(FlowConstant.FLOW_COPY_LIST); flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice);
variableMap.remove(FlowConstant.MESSAGE_TYPE);
variableMap.remove(FlowConstant.MESSAGE_NOTICE);
variableMap.remove(FlowConstant.SUBMIT);
ins.setId(instance.getId());
ins.setVariable(FlowEngine.jsonConvert.objToStr(variableMap));
insService.updateById(ins);
} }
} }
FlowInstance ins = new FlowInstance();
Map<String, Object> variableMap = instance.getVariableMap();
variableMap.remove(FlowConstant.FLOW_COPY_LIST);
variableMap.remove(FlowConstant.MESSAGE_TYPE);
variableMap.remove(FlowConstant.MESSAGE_NOTICE);
variableMap.remove(FlowConstant.SUBMIT);
ins.setId(instance.getId());
ins.setVariable(FlowEngine.jsonConvert.objToStr(variableMap));
insService.updateById(ins);
} }
/** /**

View File

@@ -9,8 +9,6 @@ import org.dromara.workflow.domain.bo.FlowTaskBo;
import org.dromara.workflow.domain.vo.FlowHisTaskVo; import org.dromara.workflow.domain.vo.FlowHisTaskVo;
import org.dromara.workflow.domain.vo.FlowTaskVo; import org.dromara.workflow.domain.vo.FlowTaskVo;
import java.util.List;
/** /**
* 任务信息Mapper接口 * 任务信息Mapper接口
@@ -29,14 +27,6 @@ public interface FlwTaskMapper {
*/ */
Page<FlowTaskVo> getListRunTask(@Param("page") Page<FlowTaskVo> page, @Param(Constants.WRAPPER) Wrapper<FlowTaskBo> queryWrapper); Page<FlowTaskVo> getListRunTask(@Param("page") Page<FlowTaskVo> page, @Param(Constants.WRAPPER) Wrapper<FlowTaskBo> queryWrapper);
/**
* 获取待办信息
*
* @param queryWrapper 条件
* @return 结果
*/
List<FlowTaskVo> getListRunTask(@Param(Constants.WRAPPER) Wrapper<FlowTaskBo> queryWrapper);
/** /**
* 获取已办 * 获取已办
* *

View File

@@ -9,14 +9,6 @@ import java.util.List;
*/ */
public interface IFlwCommonService { public interface IFlwCommonService {
/**
* 构建工作流用户
*
* @param permissionList 办理用户
* @return 用户
*/
List<String> buildUser(List<String> permissionList);
/** /**
* 发送消息 * 发送消息
* *

View File

@@ -141,14 +141,6 @@ public interface IFlwInstanceService {
*/ */
FlowInstance selectByTaskId(Long taskId); FlowInstance selectByTaskId(Long taskId);
/**
* 按任务id查询实例
*
* @param taskIdList 任务id
* @return 结果
*/
List<FlowInstance> selectByTaskIdList(List<Long> taskIdList);
/** /**
* 作废流程 * 作废流程
* *

View File

@@ -14,7 +14,6 @@ import org.dromara.workflow.domain.vo.FlowHisTaskVo;
import org.dromara.workflow.domain.vo.FlowTaskVo; import org.dromara.workflow.domain.vo.FlowTaskVo;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 任务 服务层 * 任务 服务层
@@ -150,14 +149,6 @@ public interface IFlwTaskService {
*/ */
List<FlowNode> getNextNodeList(FlowNextNodeBo bo); List<FlowNode> getNextNodeList(FlowNextNodeBo bo);
/**
* 按照任务id查询任务
*
* @param taskIdList 任务id
* @return 结果
*/
List<FlowHisTask> selectHisTaskByIdList(List<Long> taskIdList);
/** /**
* 按照任务id查询任务 * 按照任务id查询任务
* *
@@ -166,14 +157,6 @@ public interface IFlwTaskService {
*/ */
FlowHisTask selectHisTaskById(Long taskId); FlowHisTask selectHisTaskById(Long taskId);
/**
* 按照实例id查询任务
*
* @param instanceIdList 流程实例id
* @return 结果
*/
List<FlowTask> selectByInstIdList(List<Long> instanceIdList);
/** /**
* 按照实例id查询任务 * 按照实例id查询任务
* *
@@ -191,21 +174,13 @@ public interface IFlwTaskService {
*/ */
boolean taskOperation(TaskOperationBo bo, String taskOperation); boolean taskOperation(TaskOperationBo bo, String taskOperation);
/**
* 获取任务所有办理人
*
* @param taskIdList 任务id
* @return 结果
*/
Map<Long, List<UserDTO>> currentTaskAllUser(List<Long> taskIdList);
/** /**
* 获取当前任务的所有办理人 * 获取当前任务的所有办理人
* *
* @param taskId 任务id * @param taskIds 任务id
* @return 结果 * @return 结果
*/ */
List<UserDTO> currentTaskAllUser(Long taskId); List<UserDTO> currentTaskAllUser(List<Long> taskIds);
/** /**
* 按照节点编码查询节点 * 按照节点编码查询节点

View File

@@ -27,8 +27,12 @@ import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.constant.FlowConstant;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/** /**
* 流程图提示信息 * 流程图提示信息
@@ -74,15 +78,26 @@ public class FlwChartExtServiceImpl implements ChartExtService {
Map<String, String> dictType = dictService.getAllDictByDictType(FlowConstant.WF_TASK_STATUS); Map<String, String> dictType = dictService.getAllDictByDictType(FlowConstant.WF_TASK_STATUS);
// 遍历流程定义中的每个节点,调用处理方法,将对应节点的任务列表及用户信息传入,生成扩展提示内容
for (NodeJson nodeJson : defJson.getNodeList()) { for (NodeJson nodeJson : defJson.getNodeList()) {
// 获取当前节点对应的历史任务列表,如果没有则返回空列表避免空指针
List<FlowHisTask> taskList = groupedByNode.get(nodeJson.getNodeCode()); List<FlowHisTask> taskList = groupedByNode.get(nodeJson.getNodeCode());
if (CollUtil.isEmpty(taskList)) { if (CollUtil.isEmpty(taskList)) {
continue; continue;
} }
// 处理当前节点的扩展信息,包括构建审批人提示内容等
this.processNodeExtInfo(nodeJson, taskList, userMap, dictType); // 按审批人分组去重,保留最新处理记录,最终转换成 List
List<FlowHisTask> latestPerApprover = taskList.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
FlowHisTask::getApprover,
Function.identity(),
(oldTask, newTask) -> newTask.getUpdateTime().after(oldTask.getUpdateTime()) ? newTask : oldTask,
LinkedHashMap::new
),
map -> new ArrayList<>(map.values())
));
// 处理当前节点的扩展信息
this.processNodeExtInfo(nodeJson, latestPerApprover, userMap, dictType);
} }
} }
@@ -240,7 +255,7 @@ public class FlwChartExtServiceImpl implements ChartExtService {
LambdaQueryWrapper<FlowHisTask> wrapper = Wrappers.lambdaQuery(); LambdaQueryWrapper<FlowHisTask> wrapper = Wrappers.lambdaQuery();
wrapper.eq(FlowHisTask::getInstanceId, instanceId) wrapper.eq(FlowHisTask::getInstanceId, instanceId)
.eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey()) .eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey())
.orderByDesc(FlowHisTask::getCreateTime, FlowHisTask::getUpdateTime); .orderByDesc(FlowHisTask::getUpdateTime);
return flowHisTaskMapper.selectList(wrapper); return flowHisTaskMapper.selectList(wrapper);
} }

View File

@@ -19,7 +19,6 @@ import org.dromara.warm.flow.orm.entity.FlowTask;
import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.common.enums.MessageTypeEnum; import org.dromara.workflow.common.enums.MessageTypeEnum;
import org.dromara.workflow.service.IFlwCommonService; import org.dromara.workflow.service.IFlwCommonService;
import org.dromara.workflow.service.IFlwTaskAssigneeService;
import org.dromara.workflow.service.IFlwTaskService; import org.dromara.workflow.service.IFlwTaskService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -40,26 +39,6 @@ import java.util.stream.Collectors;
public class FlwCommonServiceImpl implements IFlwCommonService { public class FlwCommonServiceImpl implements IFlwCommonService {
private final NodeService nodeService; private final NodeService nodeService;
/**
* 构建工作流用户
*
* @param permissionList 办理用户
* @return 用户
*/
@Override
public List<String> buildUser(List<String> permissionList) {
if (CollUtil.isEmpty(permissionList)) {
return List.of();
}
IFlwTaskAssigneeService taskAssigneeService = SpringUtils.getBean(IFlwTaskAssigneeService.class);
String processedBys = CollUtil.join(permissionList, StringUtils.SEPARATOR);
// 根据 processedBy 前缀判断处理人类型,分别获取用户列表
List<UserDTO> users = taskAssigneeService.fetchUsersByStorageIds(processedBys);
return StreamUtils.toList(users, userDTO -> String.valueOf(userDTO.getUserId()));
}
/** /**
* 发送消息 * 发送消息
* *
@@ -70,40 +49,36 @@ public class FlwCommonServiceImpl implements IFlwCommonService {
@Override @Override
public void sendMessage(String flowName, Long instId, List<String> messageType, String message) { public void sendMessage(String flowName, Long instId, List<String> messageType, String message) {
IFlwTaskService flwTaskService = SpringUtils.getBean(IFlwTaskService.class); IFlwTaskService flwTaskService = SpringUtils.getBean(IFlwTaskService.class);
List<UserDTO> userList = new ArrayList<>();
List<FlowTask> list = flwTaskService.selectByInstId(instId); List<FlowTask> list = flwTaskService.selectByInstId(instId);
if (StringUtils.isBlank(message)) { if (StringUtils.isBlank(message)) {
message = "有新的【" + flowName + "】单据已经提交至您,请您及时处理。"; message = "有新的【" + flowName + "】单据已经提交至您,请您及时处理。";
} }
for (Task task : list) { List<UserDTO> userList = flwTaskService.currentTaskAllUser(StreamUtils.toList(list, FlowTask::getId));
List<UserDTO> users = flwTaskService.currentTaskAllUser(task.getId()); if (CollUtil.isEmpty(userList)) {
if (CollUtil.isNotEmpty(users)) { return;
userList.addAll(users);
}
} }
if (CollUtil.isNotEmpty(userList)) { for (String code : messageType) {
for (String code : messageType) { MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code);
MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code); if (ObjectUtil.isEmpty(messageTypeEnum)) {
if (ObjectUtil.isNotEmpty(messageTypeEnum)) { continue;
switch (messageTypeEnum) { }
case SYSTEM_MESSAGE: switch (messageTypeEnum) {
SseMessageDto dto = new SseMessageDto(); case SYSTEM_MESSAGE -> {
dto.setUserIds(StreamUtils.toList(userList, UserDTO::getUserId).stream().distinct().collect(Collectors.toList())); SseMessageDto dto = new SseMessageDto();
dto.setMessage(message); dto.setUserIds(StreamUtils.toList(userList, UserDTO::getUserId).stream().distinct().collect(Collectors.toList()));
SseMessageUtils.publishMessage(dto); dto.setMessage(message);
break; SseMessageUtils.publishMessage(dto);
case EMAIL_MESSAGE:
MailUtils.sendText(StreamUtils.join(userList, UserDTO::getEmail), "单据审批提醒", message);
break;
case SMS_MESSAGE:
//todo 短信发送
break;
default:
throw new IllegalStateException("Unexpected value: " + messageTypeEnum);
}
} }
case EMAIL_MESSAGE -> {
MailUtils.sendText(StreamUtils.join(userList, UserDTO::getEmail), "单据审批提醒", message);
}
case SMS_MESSAGE -> {
//todo 短信发送
}
default -> throw new IllegalStateException("Unexpected value: " + messageTypeEnum);
} }
} }
} }

View File

@@ -121,8 +121,8 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService {
List<FlowNode> flowNodes = flowNodeMapper.selectList(new LambdaQueryWrapper<FlowNode>().eq(FlowNode::getDefinitionId, id)); List<FlowNode> flowNodes = flowNodeMapper.selectList(new LambdaQueryWrapper<FlowNode>().eq(FlowNode::getDefinitionId, id));
List<String> errorMsg = new ArrayList<>(); List<String> errorMsg = new ArrayList<>();
if (CollUtil.isNotEmpty(flowNodes)) { if (CollUtil.isNotEmpty(flowNodes)) {
String applyNodeCode = flwCommonService.applyNodeCode(id);
for (FlowNode flowNode : flowNodes) { for (FlowNode flowNode : flowNodes) {
String applyNodeCode = flwCommonService.applyNodeCode(id);
if (StringUtils.isBlank(flowNode.getPermissionFlag()) && !applyNodeCode.equals(flowNode.getNodeCode()) && NodeType.BETWEEN.getKey().equals(flowNode.getNodeType())) { if (StringUtils.isBlank(flowNode.getPermissionFlag()) && !applyNodeCode.equals(flowNode.getNodeCode()) && NodeType.BETWEEN.getKey().equals(flowNode.getNodeType())) {
errorMsg.add(flowNode.getNodeName()); errorMsg.add(flowNode.getNodeName());
} }
@@ -213,7 +213,8 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService {
return; return;
} }
FlowCategory flowCategory = flwCategoryMapper.selectOne(new LambdaQueryWrapper<FlowCategory>() FlowCategory flowCategory = flwCategoryMapper.selectOne(new LambdaQueryWrapper<FlowCategory>()
.eq(FlowCategory::getTenantId, DEFAULT_TENANT_ID).eq(FlowCategory::getCategoryId, FlowConstant.FLOW_CATEGORY_ID)); .eq(FlowCategory::getTenantId, DEFAULT_TENANT_ID)
.eq(FlowCategory::getCategoryId, FlowConstant.FLOW_CATEGORY_ID));
flowCategory.setCategoryId(null); flowCategory.setCategoryId(null);
flowCategory.setTenantId(tenantId); flowCategory.setTenantId(tenantId);
flowCategory.setCreateDept(null); flowCategory.setCreateDept(null);

View File

@@ -11,7 +11,6 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.dto.UserDTO;
import org.dromara.common.core.enums.BusinessStatusEnum; import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StreamUtils;
@@ -19,11 +18,13 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.warm.flow.core.FlowEngine;
import org.dromara.warm.flow.core.constant.ExceptionCons; import org.dromara.warm.flow.core.constant.ExceptionCons;
import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.dto.FlowParams;
import org.dromara.warm.flow.core.entity.Definition; import org.dromara.warm.flow.core.entity.Definition;
import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.Instance;
import org.dromara.warm.flow.core.entity.Task; import org.dromara.warm.flow.core.entity.Task;
import org.dromara.warm.flow.core.entity.User;
import org.dromara.warm.flow.core.enums.NodeType; import org.dromara.warm.flow.core.enums.NodeType;
import org.dromara.warm.flow.core.service.DefService; import org.dromara.warm.flow.core.service.DefService;
import org.dromara.warm.flow.core.service.InsService; import org.dromara.warm.flow.core.service.InsService;
@@ -49,7 +50,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.function.Function;
/** /**
* 流程实例 服务层实现 * 流程实例 服务层实现
@@ -203,9 +204,11 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
return false; return false;
} }
// 获取定义信息 // 获取定义信息
Map<Long, Definition> definitionMap = defService.getByIds( Map<Long, Definition> definitionMap = StreamUtils.toMap(
StreamUtils.toList(instances, Instance::getDefinitionId) defService.getByIds(StreamUtils.toList(instances, Instance::getDefinitionId)),
).stream().collect(Collectors.toMap(Definition::getId, definition -> definition)); Definition::getId,
Function.identity()
);
// 逐一触发删除事件 // 逐一触发删除事件
instances.forEach(instance -> { instances.forEach(instance -> {
@@ -281,37 +284,50 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE); throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE);
} }
Long instanceId = flowInstance.getId(); Long instanceId = flowInstance.getId();
//运行中的任务
List<FlowHisTaskVo> list = new ArrayList<>(); // 先组装待审批任务(运行中的任务)
List<FlowTask> flowTaskList = flwTaskService.selectByInstId(instanceId); List<FlowHisTaskVo> runningTaskVos = new ArrayList<>();
if (CollUtil.isNotEmpty(flowTaskList)) { List<FlowTask> runningTasks = flwTaskService.selectByInstId(instanceId);
List<FlowHisTaskVo> flowHisTaskVos = BeanUtil.copyToList(flowTaskList, FlowHisTaskVo.class); if (CollUtil.isNotEmpty(runningTasks)) {
for (FlowHisTaskVo flowHisTaskVo : flowHisTaskVos) { runningTaskVos = BeanUtil.copyToList(runningTasks, FlowHisTaskVo.class);
flowHisTaskVo.setFlowStatus(TaskStatusEnum.WAITING.getStatus());
flowHisTaskVo.setUpdateTime(null); List<User> associatedUsers = FlowEngine.userService()
flowHisTaskVo.setRunDuration(null); .getByAssociateds(StreamUtils.toList(runningTasks, FlowTask::getId));
List<UserDTO> allUser = flwTaskService.currentTaskAllUser(flowHisTaskVo.getId()); Map<Long, List<User>> taskUserMap = StreamUtils.groupByKey(associatedUsers, User::getAssociated);
if (CollUtil.isNotEmpty(allUser)) {
String join = StreamUtils.join(allUser, e -> String.valueOf(e.getUserId())); for (FlowHisTaskVo vo : runningTaskVos) {
flowHisTaskVo.setApprover(join); vo.setFlowStatus(TaskStatusEnum.WAITING.getStatus());
vo.setUpdateTime(null);
vo.setRunDuration(null);
List<User> users = taskUserMap.get(vo.getId());
if (CollUtil.isNotEmpty(users)) {
vo.setApprover(StreamUtils.join(users, User::getProcessedBy));
} }
if (BusinessStatusEnum.isDraftOrCancelOrBack(flowInstance.getFlowStatus())) { if (BusinessStatusEnum.isDraftOrCancelOrBack(flowInstance.getFlowStatus())) {
flowHisTaskVo.setApprover(LoginHelper.getUserIdStr()); vo.setApprover(LoginHelper.getUserIdStr());
flowHisTaskVo.setApproveName(LoginHelper.getLoginUser().getNickname());
} }
} }
list.addAll(flowHisTaskVos);
} }
//历史任务
LambdaQueryWrapper<FlowHisTask> wrapper = Wrappers.lambdaQuery(); // 再组装历史任务(已处理任务)
wrapper.eq(FlowHisTask::getInstanceId, instanceId) List<FlowHisTaskVo> hisTaskVos = new ArrayList<>();
.eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey()) List<FlowHisTask> hisTasks = flowHisTaskMapper.selectList(
.orderByDesc(FlowHisTask::getCreateTime, FlowHisTask::getUpdateTime); new LambdaQueryWrapper<FlowHisTask>()
List<FlowHisTask> flowHisTasks = flowHisTaskMapper.selectList(wrapper); .eq(FlowHisTask::getInstanceId, instanceId)
if (CollUtil.isNotEmpty(flowHisTasks)) { .eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey())
list.addAll(BeanUtil.copyToList(flowHisTasks, FlowHisTaskVo.class)); .orderByDesc(FlowHisTask::getUpdateTime)
);
if (CollUtil.isNotEmpty(hisTasks)) {
hisTaskVos = BeanUtil.copyToList(hisTasks, FlowHisTaskVo.class);
} }
return Map.of("list", list, "instanceId", instanceId);
// 结果列表,待审批任务在前,历史任务在后
List<FlowHisTaskVo> combinedList = new ArrayList<>();
combinedList.addAll(runningTaskVos);
combinedList.addAll(hisTaskVos);
return Map.of("list", combinedList, "instanceId", instanceId);
} }
/** /**
@@ -377,31 +393,6 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
return null; return null;
} }
/**
* 按任务id查询实例
*
* @param taskIdList 任务id
*/
@Override
public List<FlowInstance> selectByTaskIdList(List<Long> taskIdList) {
if (CollUtil.isEmpty(taskIdList)) {
return Collections.emptyList();
}
Set<Long> instanceIds = new HashSet<>();
List<FlowTask> flowTaskList = flwTaskService.selectByIdList(taskIdList);
for (FlowTask flowTask : flowTaskList) {
instanceIds.add(flowTask.getInstanceId());
}
List<FlowHisTask> flowHisTaskList = flwTaskService.selectHisTaskByIdList(taskIdList);
for (FlowHisTask flowHisTask : flowHisTaskList) {
instanceIds.add(flowHisTask.getInstanceId());
}
if (!instanceIds.isEmpty()) {
return this.selectInstListByIdList(new ArrayList<>(instanceIds));
}
return Collections.emptyList();
}
/** /**
* 作废流程 * 作废流程
* *

View File

@@ -12,9 +12,7 @@ import org.dromara.common.core.domain.dto.TaskAssigneeDTO;
import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.core.domain.dto.UserDTO;
import org.dromara.common.core.domain.model.TaskAssigneeBody; import org.dromara.common.core.domain.model.TaskAssigneeBody;
import org.dromara.common.core.enums.FormatsType; import org.dromara.common.core.enums.FormatsType;
import org.dromara.common.core.service.DeptService; import org.dromara.common.core.service.*;
import org.dromara.common.core.service.TaskAssigneeService;
import org.dromara.common.core.service.UserService;
import org.dromara.common.core.utils.DateUtils; import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.warm.flow.ui.dto.HandlerFunDto; import org.dromara.warm.flow.ui.dto.HandlerFunDto;
@@ -45,6 +43,8 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand
private final TaskAssigneeService taskAssigneeService; private final TaskAssigneeService taskAssigneeService;
private final UserService userService; private final UserService userService;
private final DeptService deptService; private final DeptService deptService;
private final RoleService roleService;
private final PostService postService;
/** /**
* 获取办理人权限设置列表tabs页签 * 获取办理人权限设置列表tabs页签
@@ -216,9 +216,9 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand
private Map<Long, String> getNamesByType(TaskAssigneeEnum type, List<Long> ids) { private Map<Long, String> getNamesByType(TaskAssigneeEnum type, List<Long> ids) {
return switch (type) { return switch (type) {
case USER -> userService.selectUserNamesByIds(ids); case USER -> userService.selectUserNamesByIds(ids);
case ROLE -> userService.selectRoleNamesByIds(ids); case ROLE -> roleService.selectRoleNamesByIds(ids);
case DEPT -> userService.selectDeptNamesByIds(ids); case DEPT -> deptService.selectDeptNamesByIds(ids);
case POST -> userService.selectPostNamesByIds(ids); case POST -> postService.selectPostNamesByIds(ids);
}; };
} }

View File

@@ -55,7 +55,6 @@ import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import static org.dromara.workflow.common.constant.FlowConstant.*; import static org.dromara.workflow.common.constant.FlowConstant.*;
@@ -252,14 +251,12 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
hisTask.setCreateTime(updateTime); hisTask.setCreateTime(updateTime);
hisTask.setUpdateTime(updateTime); hisTask.setUpdateTime(updateTime);
hisTaskService.save(hisTask); hisTaskService.save(hisTask);
List<User> userList = flowCopyList.stream() List<User> userList = StreamUtils.toList(flowCopyList, x ->
.map(flowCopy -> { new FlowUser()
FlowUser flowUser = new FlowUser(); .setType(TaskAssigneeType.COPY.getCode())
flowUser.setType(TaskAssigneeType.COPY.getCode()); .setProcessedBy(String.valueOf(x.getUserId()))
flowUser.setProcessedBy(String.valueOf(flowCopy.getUserId())); .setAssociated(taskId)
flowUser.setAssociated(taskId); );
return flowUser;
}).collect(Collectors.toList());
// 批量保存抄送人员 // 批量保存抄送人员
FlowEngine.userService().saveBatch(userList); FlowEngine.userService().saveBatch(userList);
} }
@@ -276,7 +273,8 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey()); queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey());
queryWrapper.in("t.processed_by", LoginHelper.getUserIdStr()); queryWrapper.in("t.processed_by", LoginHelper.getUserIdStr());
queryWrapper.in("t.flow_status", BusinessStatusEnum.WAITING.getStatus()); queryWrapper.in("t.flow_status", BusinessStatusEnum.WAITING.getStatus());
Page<FlowTaskVo> page = this.getFlowTaskVoPage(pageQuery, queryWrapper); Page<FlowTaskVo> page = flwTaskMapper.getListRunTask(pageQuery.build(), queryWrapper);
this.wrapAssigneeInfo(page.getRecords());
return TableDataInfo.build(page); return TableDataInfo.build(page);
} }
@@ -306,25 +304,28 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
public TableDataInfo<FlowTaskVo> pageByAllTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) { public TableDataInfo<FlowTaskVo> pageByAllTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
QueryWrapper<FlowTaskBo> queryWrapper = buildQueryWrapper(flowTaskBo); QueryWrapper<FlowTaskBo> queryWrapper = buildQueryWrapper(flowTaskBo);
queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey()); queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey());
Page<FlowTaskVo> page = getFlowTaskVoPage(pageQuery, queryWrapper); Page<FlowTaskVo> page = flwTaskMapper.getListRunTask(pageQuery.build(), queryWrapper);
this.wrapAssigneeInfo(page.getRecords());
return TableDataInfo.build(page); return TableDataInfo.build(page);
} }
private Page<FlowTaskVo> getFlowTaskVoPage(PageQuery pageQuery, QueryWrapper<FlowTaskBo> queryWrapper) { /**
Page<FlowTaskVo> page = flwTaskMapper.getListRunTask(pageQuery.build(), queryWrapper); * 为流程任务列表封装处理人 IDassigneeIds
List<FlowTaskVo> records = page.getRecords(); *
if (CollUtil.isNotEmpty(records)) { * @param taskList 流程任务列表
List<Long> taskIds = StreamUtils.toList(records, FlowTaskVo::getId); */
Map<Long, List<UserDTO>> listMap = currentTaskAllUser(taskIds); private void wrapAssigneeInfo(List<FlowTaskVo> taskList) {
records.forEach(t -> { if (CollUtil.isEmpty(taskList)) {
List<UserDTO> userList = listMap.getOrDefault(t.getId(), Collections.emptyList()); return;
if (CollUtil.isNotEmpty(userList)) { }
t.setAssigneeIds(StreamUtils.join(userList, e -> String.valueOf(e.getUserId()))); List<User> associatedUsers = FlowEngine.userService()
t.setAssigneeNames(StreamUtils.join(userList, UserDTO::getNickName)); .getByAssociateds(StreamUtils.toList(taskList, FlowTaskVo::getId));
} Map<Long, List<User>> taskUserMap = StreamUtils.groupByKey(associatedUsers, User::getAssociated);
}); // 组装用户数据回任务列表
for (FlowTaskVo task : taskList) {
List<User> users = taskUserMap.get(task.getId());
task.setAssigneeIds(StreamUtils.join(users, User::getProcessedBy));
} }
return page;
} }
/** /**
@@ -527,15 +528,15 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
// 只获取中间节点 // 只获取中间节点
nextFlowNodes = StreamUtils.filter(nextFlowNodes, node -> NodeType.BETWEEN.getKey().equals(node.getNodeType())); nextFlowNodes = StreamUtils.filter(nextFlowNodes, node -> NodeType.BETWEEN.getKey().equals(node.getNodeType()));
if (CollUtil.isNotEmpty(nextNodeList)) { if (CollUtil.isNotEmpty(nextNodeList)) {
// 构建以下节点数据 //构建以下节点数据
List<Task> buildNextTaskList = StreamUtils.toList(nextNodeList, node -> taskService.addTask(node, instance, definition, FlowParams.build())); List<Task> buildNextTaskList = StreamUtils.toList(nextNodeList, node -> taskService.addTask(node, instance, definition, FlowParams.build()));
// 办理人变量替换 //办理人变量替换
ExpressionUtil.evalVariable(buildNextTaskList, ExpressionUtil.evalVariable(buildNextTaskList, FlowParams.build().variable(mergeVariable));
FlowParams.build()
.variable(mergeVariable)
);
for (FlowNode flowNode : nextFlowNodes) { for (FlowNode flowNode : nextFlowNodes) {
buildNextTaskList.stream().filter(t -> t.getNodeCode().equals(flowNode.getNodeCode())).findFirst().ifPresent(t -> { Optional<Task> first = buildNextTaskList.stream()
.filter(t -> t.getNodeCode().equals(flowNode.getNodeCode()))
.findFirst();
first.ifPresent(t -> {
if (CollUtil.isNotEmpty(t.getPermissionList())) { if (CollUtil.isNotEmpty(t.getPermissionList())) {
List<UserDTO> users = flwTaskAssigneeService.fetchUsersByStorageIds(String.join(StringUtils.SEPARATOR, t.getPermissionList())); List<UserDTO> users = flwTaskAssigneeService.fetchUsersByStorageIds(String.join(StringUtils.SEPARATOR, t.getPermissionList()));
if (CollUtil.isNotEmpty(users)) { if (CollUtil.isNotEmpty(users)) {
@@ -548,18 +549,6 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
return nextFlowNodes; return nextFlowNodes;
} }
/**
* 按照任务id查询任务
*
* @param taskIdList 任务id
* @return 结果
*/
@Override
public List<FlowHisTask> selectHisTaskByIdList(List<Long> taskIdList) {
return flowHisTaskMapper.selectList(new LambdaQueryWrapper<>(FlowHisTask.class)
.in(FlowHisTask::getId, taskIdList));
}
/** /**
* 按照任务id查询任务 * 按照任务id查询任务
* *
@@ -572,17 +561,6 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
.eq(FlowHisTask::getId, taskId)); .eq(FlowHisTask::getId, taskId));
} }
/**
* 按照实例id查询任务
*
* @param instanceIdList 流程实例id
*/
@Override
public List<FlowTask> selectByInstIdList(List<Long> instanceIdList) {
return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class)
.in(FlowTask::getInstanceId, instanceIdList));
}
/** /**
* 按照实例id查询任务 * 按照实例id查询任务
* *
@@ -683,15 +661,12 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
// 批量删除现有任务的办理人记录 // 批量删除现有任务的办理人记录
if (CollUtil.isNotEmpty(flowTasks)) { if (CollUtil.isNotEmpty(flowTasks)) {
FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTasks, FlowTask::getId)); FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTasks, FlowTask::getId));
List<User> userList = flowTasks.stream() List<User> userList = StreamUtils.toList(flowTasks, flowTask ->
.map(flowTask -> { new FlowUser()
FlowUser flowUser = new FlowUser(); .setType(TaskAssigneeType.APPROVER.getCode())
flowUser.setType(TaskAssigneeType.APPROVER.getCode()); .setProcessedBy(userId)
flowUser.setProcessedBy(userId); .setAssociated(flowTask.getId())
flowUser.setAssociated(flowTask.getId()); );
return flowUser;
})
.collect(Collectors.toList());
if (CollUtil.isNotEmpty(userList)) { if (CollUtil.isNotEmpty(userList)) {
FlowEngine.userService().saveBatch(userList); FlowEngine.userService().saveBatch(userList);
} }
@@ -703,36 +678,15 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
return true; return true;
} }
/**
* 获取任务所有办理人
*
* @param taskIdList 任务id
*/
@Override
public Map<Long, List<UserDTO>> currentTaskAllUser(List<Long> taskIdList) {
Map<Long, List<UserDTO>> map = new HashMap<>();
// 获取与当前任务关联的用户列表
List<User> associatedUsers = FlowEngine.userService().getByAssociateds(taskIdList);
Map<Long, List<User>> listMap = StreamUtils.groupByKey(associatedUsers, User::getAssociated);
for (Map.Entry<Long, List<User>> entry : listMap.entrySet()) {
List<User> value = entry.getValue();
if (CollUtil.isNotEmpty(value)) {
List<UserDTO> userDtoList = userService.selectListByIds(StreamUtils.toList(value, e -> Convert.toLong(e.getProcessedBy())));
map.put(entry.getKey(), userDtoList);
}
}
return map;
}
/** /**
* 获取当前任务的所有办理人 * 获取当前任务的所有办理人
* *
* @param taskId 任务id * @param taskIds 任务id
*/ */
@Override @Override
public List<UserDTO> currentTaskAllUser(Long taskId) { public List<UserDTO> currentTaskAllUser(List<Long> taskIds) {
// 获取与当前任务关联的用户列表 // 获取与当前任务关联的用户列表
List<User> userList = FlowEngine.userService().getByAssociateds(Collections.singletonList(taskId)); List<User> userList = FlowEngine.userService().getByAssociateds(taskIds);
if (CollUtil.isEmpty(userList)) { if (CollUtil.isEmpty(userList)) {
return Collections.emptyList(); return Collections.emptyList();
} }