This commit is contained in:
2026-03-20 16:30:16 +08:00
parent 78705c2ada
commit 7d1ad3e58c
15 changed files with 57 additions and 260 deletions

View File

@@ -16,10 +16,7 @@ public class DeptRespDTO {
@Schema(description = "父部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long parentId;
@Schema(description = "负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long leaderUserId;
@Schema(description = "部门状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status; // 参见 CommonStatusEnum 枚举
private Integer status;
}

View File

@@ -1,12 +1,12 @@
package com.njcn.rdms.module.system.api.user;
import cn.hutool.core.convert.Convert;
import com.fhs.core.trans.anno.AutoTrans;
import com.fhs.trans.service.AutoTransable;
import com.njcn.rdms.framework.common.pojo.CommonResult;
import com.njcn.rdms.framework.common.util.collection.CollectionUtils;
import com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO;
import com.njcn.rdms.module.system.enums.ApiConstants;
import com.fhs.core.trans.anno.AutoTrans;
import com.fhs.trans.service.AutoTransable;
import feign.FeignIgnore;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -22,7 +22,7 @@ import java.util.Map;
import static com.njcn.rdms.module.system.api.user.AdminUserApi.PREFIX;
@FeignClient(name = ApiConstants.NAME)
@FeignClient(name = ApiConstants.NAME)
@Tag(name = "RPC 服务 - 管理员用户")
@AutoTrans(namespace = PREFIX, fields = {"nickname"})
public interface AdminUserApi extends AutoTransable<AdminUserRespDTO> {
@@ -34,11 +34,6 @@ public interface AdminUserApi extends AutoTransable<AdminUserRespDTO> {
@Parameter(name = "id", description = "用户编号", example = "1", required = true)
CommonResult<AdminUserRespDTO> getUser(@RequestParam("id") Long id);
@GetMapping(PREFIX + "/list-by-subordinate")
@Operation(summary = "通过用户 ID 查询用户下属")
@Parameter(name = "id", description = "用户编号", example = "1", required = true)
CommonResult<List<AdminUserRespDTO>> getUserListBySubordinate(@RequestParam("id") Long id);
@GetMapping(PREFIX + "/list")
@Operation(summary = "通过用户 ID 查询用户们")
@Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true)
@@ -66,9 +61,7 @@ public interface AdminUserApi extends AutoTransable<AdminUserRespDTO> {
}
/**
* 校验用户是否有效。如下情况,视为无效:
* 1. 用户编号不存在
* 2. 用户被禁用
* 校验用户是否有效
*
* @param id 用户编号
*/

View File

@@ -10,7 +10,6 @@ public interface DictTypeConstants {
// ========== system ==========
String USER_SEX = "system_user_sex";
String DATA_SCOPE = "system_data_scope";
String LOGIN_TYPE = "system_login_type";
String LOGIN_RESULT = "system_login_result";

View File

@@ -1,40 +0,0 @@
package com.njcn.rdms.module.system.enums.permission;
import com.njcn.rdms.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 数据范围枚举类
*
* 用于实现数据级别的权限
*
* @author hongawen
*/
@Getter
@AllArgsConstructor
public enum DataScopeEnum implements ArrayValuable<Integer> {
ALL(1), // 全部数据权限
DEPT_CUSTOM(2), // 指定部门数据权限
DEPT_ONLY(3), // 部门数据权限
DEPT_AND_CHILD(4), // 部门及以下数据权限
SELF(5); // 仅本人数据权限
/**
* 范围
*/
private final Integer scope;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DataScopeEnum::getScope).toArray(Integer[]::new);
@Override
public Integer[] array() {
return ARRAYS;
}
}

View File

@@ -1,25 +1,18 @@
package com.njcn.rdms.module.system.api.user;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.njcn.rdms.framework.common.pojo.CommonResult;
import com.njcn.rdms.framework.common.util.object.BeanUtils;
import com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO;
import com.njcn.rdms.module.system.dal.dataobject.dept.DeptDO;
import com.njcn.rdms.module.system.dal.dataobject.user.AdminUserDO;
import com.njcn.rdms.module.system.service.dept.DeptService;
import com.njcn.rdms.module.system.service.user.AdminUserService;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
import static com.njcn.rdms.framework.common.util.collection.CollectionUtils.convertSet;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
@@ -27,8 +20,6 @@ public class AdminUserApiImpl implements AdminUserApi {
@Resource
private AdminUserService userService;
@Resource
private DeptService deptService;
@Override
public CommonResult<AdminUserRespDTO> getUser(Long id) {
@@ -36,34 +27,6 @@ public class AdminUserApiImpl implements AdminUserApi {
return success(BeanUtils.toBean(user, AdminUserRespDTO.class));
}
@Override
public CommonResult<List<AdminUserRespDTO>> getUserListBySubordinate(Long id) {
// 1.1 获取用户负责的部门
AdminUserDO user = userService.getUser(id);
if (user == null) {
return success(Collections.emptyList());
}
ArrayList<Long> deptIds = new ArrayList<>();
DeptDO dept = deptService.getDept(user.getDeptId());
if (dept == null) {
return success(Collections.emptyList());
}
if (ObjUtil.notEqual(dept.getLeaderUserId(), id)) { // 校验为负责人
return success(Collections.emptyList());
}
deptIds.add(dept.getId());
// 1.2 获取所有子部门
List<DeptDO> childDeptList = deptService.getChildDeptList(dept.getId());
if (CollUtil.isNotEmpty(childDeptList)) {
deptIds.addAll(convertSet(childDeptList, DeptDO::getId));
}
// 2. 获取部门对应的用户信息
List<AdminUserDO> users = userService.getUserListByDeptIds(deptIds);
users.removeIf(item -> ObjUtil.equal(item.getId(), id)); // 排除自己
return success(BeanUtils.toBean(users, AdminUserRespDTO.class));
}
@Override
public CommonResult<List<AdminUserRespDTO>> getUserList(Collection<Long> ids) {
List<AdminUserDO> users = userService.getUserList(ids);

View File

@@ -12,7 +12,7 @@ public class DeptRespVO {
@Schema(description = "部门编号", example = "1024")
private Long id;
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发中心")
private String name;
@Schema(description = "父部门 ID", example = "1024")
@@ -33,19 +33,10 @@ public class DeptRespVO {
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer sort;
@Schema(description = "负责人的用户编号", example = "2048")
private Long leaderUserId;
@Schema(description = "联系电话", example = "15601691000")
private String phone;
@Schema(description = "邮箱", example = "rdms@iocoder.cn")
private String email;
@Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式")
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -3,7 +3,6 @@ package com.njcn.rdms.module.system.controller.admin.dept.vo.dept;
import com.njcn.rdms.framework.common.enums.CommonStatusEnum;
import com.njcn.rdms.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@@ -16,7 +15,7 @@ public class DeptSaveReqVO {
@Schema(description = "部门编号", example = "1024")
private Long id;
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发中心")
@NotBlank(message = "部门名称不能为空")
@Size(max = 30, message = "部门名称长度不能超过 30 个字符")
private String name;
@@ -36,19 +35,7 @@ public class DeptSaveReqVO {
@NotNull(message = "显示顺序不能为空")
private Integer sort;
@Schema(description = "负责人的用户编号", example = "2048")
private Long leaderUserId;
@Schema(description = "联系电话", example = "15601691000")
@Size(max = 11, message = "联系电话长度不能超过11个字符")
private String phone;
@Schema(description = "邮箱", example = "rdms@iocoder.cn")
@Email(message = "邮箱格式不正确")
@Size(max = 50, message = "邮箱长度不能超过 50 个字符")
private String email;
@Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;

View File

@@ -1,10 +1,9 @@
package com.njcn.rdms.module.system.dal.dataobject.dept;
import com.njcn.rdms.framework.common.enums.CommonStatusEnum;
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
import com.njcn.rdms.module.system.dal.dataobject.user.AdminUserDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.njcn.rdms.framework.common.enums.CommonStatusEnum;
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -12,7 +11,6 @@ import lombok.EqualsAndHashCode;
* 部门表
*
* @author hongawen
* @author hongawen
*/
@TableName("system_dept")
@Data
@@ -22,7 +20,7 @@ public class DeptDO extends BaseDO {
public static final Long PARENT_ID_ROOT = 0L;
/**
* 部门ID
* 部门 ID
*/
@TableId
private Long id;
@@ -31,9 +29,7 @@ public class DeptDO extends BaseDO {
*/
private String name;
/**
* 父部门ID
*
* 关联 {@link #id}
* 父部门 ID
*/
private Long parentId;
/**
@@ -56,20 +52,6 @@ public class DeptDO extends BaseDO {
* 显示顺序
*/
private Integer sort;
/**
* 负责人
*
* 关联 {@link AdminUserDO#getId()}
*/
private Long leaderUserId;
/**
* 联系电话
*/
private String phone;
/**
* 邮箱
*/
private String email;
/**
* 部门状态
*

View File

@@ -1,38 +0,0 @@
package com.njcn.rdms.module.system.dal.dataobject.dept;
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
import com.njcn.rdms.module.system.dal.dataobject.user.AdminUserDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 用户和岗位关联
*
* @author hongawen
*/
@TableName("system_user_post")
@Data
@EqualsAndHashCode(callSuper = true)
public class UserPostDO extends BaseDO {
/**
* 自增主键
*/
@TableId
private Long id;
/**
* 用户 ID
*
* 关联 {@link AdminUserDO#getId()}
*/
private Long userId;
/**
* 角色 ID
*
* 关联 {@link PostDO#getId()}
*/
private Long postId;
}

View File

@@ -2,17 +2,12 @@ package com.njcn.rdms.module.system.dal.dataobject.permission;
import com.njcn.rdms.framework.common.enums.CommonStatusEnum;
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
import com.njcn.rdms.module.system.enums.permission.DataScopeEnum;
import com.njcn.rdms.module.system.enums.permission.RoleTypeEnum;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Set;
/**
* 角色 DO
*
@@ -59,18 +54,4 @@ public class RoleDO extends BaseDO {
*/
private String remark;
/**
* 数据范围
*
* 枚举 {@link DataScopeEnum}
*/
private Integer dataScope;
/**
* 数据范围(指定部门数组)
*
* 适用于 {@link #dataScope} 的值为 {@link DataScopeEnum#DEPT_CUSTOM} 时
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Set<Long> dataScopeDeptIds;
}

View File

@@ -35,10 +35,6 @@ public interface DeptMapper extends BaseMapperX<DeptDO> {
return selectList(DeptDO::getParentId, parentIds);
}
default List<DeptDO> selectListByLeaderUserId(Long id) {
return selectList(DeptDO::getLeaderUserId, id);
}
default List<DeptDO> selectListByPathPrefix(String pathPrefix) {
return selectList(new LambdaQueryWrapperX<DeptDO>().likeRight(DeptDO::getPath, pathPrefix));
}

View File

@@ -1,32 +0,0 @@
package com.njcn.rdms.module.system.dal.mysql.dept;
import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.njcn.rdms.module.system.dal.dataobject.dept.UserPostDO;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
@Mapper
public interface UserPostMapper extends BaseMapperX<UserPostDO> {
default List<UserPostDO> selectListByUserId(Long userId) {
return selectList(UserPostDO::getUserId, userId);
}
default void deleteByUserIdAndPostId(Long userId, Collection<Long> postIds) {
delete(new LambdaQueryWrapperX<UserPostDO>()
.eq(UserPostDO::getUserId, userId)
.in(UserPostDO::getPostId, postIds));
}
default List<UserPostDO> selectListByPostIds(Collection<Long> postIds) {
return selectList(UserPostDO::getPostId, postIds);
}
default void deleteByUserId(Long userId) {
delete(Wrappers.lambdaUpdate(UserPostDO.class).eq(UserPostDO::getUserId, userId));
}
}

View File

@@ -5,7 +5,11 @@ import com.njcn.rdms.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import com.njcn.rdms.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
import com.njcn.rdms.module.system.dal.dataobject.dept.DeptDO;
import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 部门 Service 接口
@@ -96,28 +100,18 @@ public interface DeptService {
*/
List<DeptDO> getChildDeptList(Collection<Long> ids);
/**
* 获得指定领导者的部门列表
*
* @param id 领导者编号
* @return 部门列表
*/
List<DeptDO> getDeptListByLeaderUserId(Long id);
/**
* 获得所有子部门,从缓存中
*
* @param id 父部门编号
* @return 子部门列表
* @return 子部门编号集合
*/
Set<Long> getChildDeptIdListFromCache(Long id);
/**
* 校验部门们是否有效。如下情况,视为无效:
* 1. 部门编号不存在
* 2. 部门被禁用
* 校验部门们是否有效
*
* @param ids 角色编号数组
* @param ids 部门编号数组
*/
void validateDeptList(Collection<Long> ids);

View File

@@ -244,11 +244,6 @@ public class DeptServiceImpl implements DeptService {
return children;
}
@Override
public List<DeptDO> getDeptListByLeaderUserId(Long id) {
return deptMapper.selectListByLeaderUserId(id);
}
@Override
@Cacheable(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, key = "#id")
public Set<Long> getChildDeptIdListFromCache(Long id) {

View File

@@ -128,6 +128,23 @@ public class PermissionServiceImpl implements PermissionService {
return enabledMenus;
}
/**
* 为已选菜单补齐父链,避免只授权子菜单或按钮时,权限树缺少上级节点。
*/
private Set<Long> expandMenuIdsWithAncestors(Collection<Long> menuIds) {
Set<Long> results = new LinkedHashSet<>(menuIds);
menuIds.forEach(menuId -> {
MenuDO menu = menuService.getMenu(menuId);
while (menu != null && !MenuDO.ID_ROOT.equals(menu.getParentId())) {
if (!results.add(menu.getParentId())) {
break;
}
menu = menuService.getMenu(menu.getParentId());
}
});
return results;
}
@Override
public boolean hasAnyRoles(Long userId, String... roles) {
// 如果为空,说明已经有权限
@@ -162,7 +179,7 @@ public class PermissionServiceImpl implements PermissionService {
// 获得角色拥有菜单编号
Set<Long> dbMenuIds = convertSet(roleMenuMapper.selectListByRoleId(roleId), RoleMenuDO::getMenuId);
// 计算新增和删除的菜单编号
Set<Long> menuIdList = CollUtil.emptyIfNull(menuIds);
Set<Long> menuIdList = expandMenuIdsWithAncestors(CollUtil.emptyIfNull(menuIds));
Collection<Long> createMenuIds = CollUtil.subtract(menuIdList, dbMenuIds);
Collection<Long> deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIdList);
// 执行新增和删除。对于已经授权的菜单,不用做任何处理
@@ -208,10 +225,12 @@ public class PermissionServiceImpl implements PermissionService {
// 如果是管理员的情况下,获取全部菜单编号
if (roleService.hasAnySuperAdmin(roleIds)) {
return convertSet(menuService.getMenuList(), MenuDO::getId);
return convertSet(menuService.filterDisableMenus(menuService.getMenuList()), MenuDO::getId);
}
// 如果是非管理员的情况下,获得拥有的菜单编号
return convertSet(roleMenuMapper.selectListByRoleId(roleIds), RoleMenuDO::getMenuId);
// 如果是非管理员的情况下,仅返回当前仍然有效的菜单,并补齐其父链
Set<Long> menuIds = convertSet(roleMenuMapper.selectListByRoleId(roleIds), RoleMenuDO::getMenuId);
List<MenuDO> menus = menuService.filterDisableMenus(menuService.getMenuList(menuIds));
return expandMenuIdsWithAncestors(convertSet(menus, MenuDO::getId));
}
@Override
@@ -257,13 +276,16 @@ public class PermissionServiceImpl implements PermissionService {
@Override
public Set<Long> getUserRoleIdListByUserId(Long userId) {
return convertSet(userRoleMapper.selectListByUserId(userId), UserRoleDO::getRoleId);
Set<Long> roleIds = getRawUserRoleIdListByUserId(userId);
List<RoleDO> roles = roleService.getRoleList(roleIds);
roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus()));
return convertSet(roles, RoleDO::getId);
}
@Override
@Cacheable(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId")
public Set<Long> getUserRoleIdListByUserIdFromCache(Long userId) {
return getUserRoleIdListByUserId(userId);
return getRawUserRoleIdListByUserId(userId);
}
@Override
@@ -296,4 +318,11 @@ public class PermissionServiceImpl implements PermissionService {
return SpringUtil.getBean(getClass());
}
/**
* 获得用户已分配的原始角色编号集合,不做启停状态过滤,供缓存与底层鉴权链路复用。
*/
private Set<Long> getRawUserRoleIdListByUserId(Long userId) {
return convertSet(userRoleMapper.selectListByUserId(userId), UserRoleDO::getRoleId);
}
}