Browse Source

增加 get-info 接口的相关方法

YunaiV 4 years ago
parent
commit
162bebf5fb
32 changed files with 1153 additions and 39 deletions
  1. 65 0
      src/main/java/cn/iocoder/dashboard/framework/redis/core/RedisKeyDefine.java
  2. 5 2
      src/main/java/cn/iocoder/dashboard/framework/security/core/LoginUser.java
  3. 16 0
      src/main/java/cn/iocoder/dashboard/framework/security/core/util/SecurityUtils.java
  4. 18 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.http
  5. 42 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java
  6. 19 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthGetInfoRespVO.java
  7. 42 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthLoginReqVO.java
  8. 20 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthLoginRespVO.java
  9. 13 0
      src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java
  10. 0 1
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/package-info.java
  11. 9 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysMenuMapper.java
  12. 9 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleDeptMapper.java
  13. 9 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMapper.java
  14. 9 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMenuMapper.java
  15. 17 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysUserRoleMapper.java
  16. 100 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/dept/SysDept.java
  17. 0 1
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/package-info.java
  18. 112 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysMenuDO.java
  19. 97 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysRoleDO.java
  20. 33 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysRoleDeptDO.java
  21. 33 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysRoleMenuDO.java
  22. 33 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysUserRoleDO.java
  23. 8 19
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/user/SysUserDO.java
  24. 21 0
      src/main/java/cn/iocoder/dashboard/modules/system/enums/permission/RoleKeyEnum.java
  25. 15 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java
  26. 38 16
      src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java
  27. 33 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysMenuService.java
  28. 46 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysPermissionService.java
  29. 36 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysRoleService.java
  30. 81 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java
  31. 104 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java
  32. 70 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java

+ 65 - 0
src/main/java/cn/iocoder/dashboard/framework/redis/core/RedisKeyDefine.java

@@ -0,0 +1,65 @@
+package cn.iocoder.dashboard.framework.redis.core;
+
+import lombok.Data;
+
+import java.time.Duration;
+
+/**
+ * Redis Key 定义类
+ *
+ * @author 芋道源码
+ */
+@Data
+public class RedisKeyDefine {
+
+    public enum KeyTypeEnum {
+
+        STRING,
+        LIST,
+        HASH,
+        SET,
+        ZSET,
+        STREAM,
+        PUBSUB;
+
+    }
+
+    /**
+     * 过期时间 - 永不过期
+     */
+    public static final Duration TIMEOUT_FOREVER = null;
+
+    /**
+     * 过期时间 - 动态,通过参数传入
+     */
+    public static final Duration TIMEOUT_DYNAMIC = null;
+
+    /**
+     * Key 模板
+     */
+    private final String keyTemplate;
+    /**
+     * Key 类型的枚举
+     */
+    private final KeyTypeEnum keyType;
+    /**
+     * Value 类型
+     *
+     * 如果是使用分布式锁,设置为 {@link java.util.concurrent.locks.Lock} 类型
+     */
+    private final Class<?> valueType;
+    /**
+     * 过期时间
+     *
+     * 为空时,表示永不过期 {@link #TIMEOUT_FOREVER}
+     */
+    private final Duration timeout;
+
+    public RedisKeyDefine(String keyTemplate, KeyTypeEnum keyType, Class<?> valueType, Duration timeout) {
+        this.keyTemplate = keyTemplate;
+        this.keyType = keyType;
+        this.valueType = valueType;
+        this.timeout = timeout;
+    }
+
+}

+ 5 - 2
src/main/java/cn/iocoder/dashboard/framework/security/core/LoginUser.java

@@ -3,7 +3,6 @@ package cn.iocoder.dashboard.framework.security.core;
 import cn.iocoder.dashboard.modules.system.enums.user.UserStatus;
 import com.alibaba.fastjson.annotation.JSONField;
 import lombok.Data;
-import org.springframework.data.annotation.Transient;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.userdetails.UserDetails;
 
@@ -23,10 +22,14 @@ public class LoginUser implements UserDetails {
      * 用户编号
      */
     private Long userId;
+    /**
+     * 科室编号
+     */
+    private Long deptId;
     /**
      * 角色编号数组
      */
-    private Set<Integer> roleIds;
+    private Set<Long> roleIds;
     /**
      * 最后更新时间
      */

+ 16 - 0
src/main/java/cn/iocoder/dashboard/framework/security/core/util/SecurityUtils.java

@@ -7,6 +7,7 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS
 import org.springframework.util.StringUtils;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.Set;
 
 /**
  * 安全服务工具类
@@ -15,6 +16,8 @@ import javax.servlet.http.HttpServletRequest;
  */
 public class SecurityUtils {
 
+    private SecurityUtils() {}
+
     /**
      * 从请求中,获得认证 Token
      *
@@ -41,6 +44,19 @@ public class SecurityUtils {
         return (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
     }
 
+    /**
+     * 获得当前用户的编号
+     *
+     * @return 用户编号
+     */
+    public static Long getLoginUserId() {
+        return getLoginUser().getUserId();
+    }
+
+    public static Set<Long> getLoginUserRoleIds() {
+        return getLoginUser().getRoleIds();
+    }
+
     /**
      * 设置当前用户
      *

+ 18 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.http

@@ -0,0 +1,18 @@
+### 请求 /login 接口 => 成功
+POST {{baseUrl}}/login
+Content-Type: application/json
+
+{
+  "username": "admin",
+  "password": "admin123",
+  "uuid": "9b2ffbc1-7425-4155-9894-9d5c08541d62",
+  "code": "1024"
+}
+
+### 请求 /get-info 接口 => 成功
+GET {{baseUrl}}/get-info
+Authorization: Bearer {{token}}
+
+### 请求 /druid/xxx 接口 => 失败 TODO 临时测试
+GET http://127.0.0.1:8080/druid/123
+Authorization: Bearer {{token}}

+ 42 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java

@@ -0,0 +1,42 @@
+package cn.iocoder.dashboard.modules.system.controller.auth;
+
+import cn.iocoder.dashboard.common.pojo.CommonResult;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetInfoRespVO;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthLoginReqVO;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthLoginRespVO;
+import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
+import static cn.iocoder.dashboard.framework.security.core.util.SecurityUtils.getLoginUserId;
+import static cn.iocoder.dashboard.framework.security.core.util.SecurityUtils.getLoginUserRoleIds;
+
+@Api(tags = "认证 API")
+@RestController
+@RequestMapping("/")
+public class SysAuthController {
+
+    @Resource
+    private SysAuthService authService;
+
+    @ApiOperation("使用账号密码登录")
+    @PostMapping("/login")
+    public CommonResult<SysAuthLoginRespVO> login(@RequestBody @Valid SysAuthLoginReqVO reqVO) {
+        String token = authService.login(reqVO.getUsername(), reqVO.getPassword(), reqVO.getUuid(), reqVO.getCode());
+        // 返回结果
+        return success(SysAuthLoginRespVO.builder().token(token).build());
+    }
+
+    @ApiOperation("获取登陆用户的信息")
+    @GetMapping("/get-info")
+    public CommonResult<SysAuthGetInfoRespVO> getInfo() {
+        SysAuthGetInfoRespVO respVO = authService.getInfo(getLoginUserId(), getLoginUserRoleIds());
+        return success(respVO);
+    }
+
+}

+ 19 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthGetInfoRespVO.java

@@ -0,0 +1,19 @@
+package cn.iocoder.dashboard.modules.system.controller.auth.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Set;
+
+@ApiModel("获得用户信息 Resp VO")
+@Data
+public class SysAuthGetInfoRespVO {
+
+    @ApiModelProperty(value = "角色权限数组", required = true)
+    private Set<String> roles;
+
+    @ApiModelProperty(value = "菜单权限数组", required = true)
+    private Set<String> permissions;
+
+}

+ 42 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthLoginReqVO.java

@@ -0,0 +1,42 @@
+package cn.iocoder.dashboard.modules.system.controller.auth.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.hibernate.validator.constraints.Length;
+import org.springframework.validation.annotation.Validated;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.Pattern;
+
+@ApiModel("账号密码登陆 Request VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class SysAuthLoginReqVO {
+
+    @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma")
+    @NotEmpty(message = "登陆账号不能为空")
+    @Length(min = 5, max = 16, message = "账号长度为 5-16 位")
+    @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
+    private String username;
+
+    @ApiModelProperty(value = "密码", required = true, example = "buzhidao")
+    @NotEmpty(message = "密码不能为空")
+    @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+    private String password;
+
+    @ApiModelProperty(value = "验证码", required = true, example = "1024")
+    @NotEmpty(message = "验证码不能为空")
+    private String code;
+
+    @ApiModelProperty(value = "验证码的唯一标识", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
+    @NotEmpty(message = "唯一标识不能为空")
+    private String uuid;
+
+}

+ 20 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthLoginRespVO.java

@@ -0,0 +1,20 @@
+package cn.iocoder.dashboard.modules.system.controller.auth.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@ApiModel("账号密码登陆 Response VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class SysAuthLoginRespVO {
+
+    @ApiModelProperty(value = "token", required = true, example = "yudaoyuanma")
+    private String token;
+
+}

+ 13 - 0
src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java

@@ -1,11 +1,17 @@
 package cn.iocoder.dashboard.modules.system.convert.auth;
 
 import cn.iocoder.dashboard.framework.security.core.LoginUser;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetInfoRespVO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
+import cn.iocoder.dashboard.util.collection.CollectionUtils;
 import org.mapstruct.Mapper;
 import org.mapstruct.Mapping;
 import org.mapstruct.factory.Mappers;
 
+import java.util.List;
+
 @Mapper
 public interface SysAuthConvert {
 
@@ -14,4 +20,11 @@ public interface SysAuthConvert {
     @Mapping(source = "updateTime", target = "updateTime", ignore = true) // 字段相同,但是含义不同,忽略
     LoginUser convert(SysUserDO bean);
 
+    default SysAuthGetInfoRespVO convert(SysUserDO user, List<SysRoleDO> roleList, List<SysMenuDO> menuList) {
+        SysAuthGetInfoRespVO respVO = new SysAuthGetInfoRespVO();
+        respVO.setRoles(CollectionUtils.convertSet(roleList, SysRoleDO::getRoleKey));
+        respVO.setPermissions(CollectionUtils.convertSet(menuList, SysMenuDO::getPerms));
+        return respVO;
+    }
+
 }

+ 0 - 1
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/package-info.java

@@ -1 +0,0 @@
-package cn.iocoder.dashboard.modules.system.dal.mysql.dao;

+ 9 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysMenuMapper.java

@@ -0,0 +1,9 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission;
+
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SysMenuMapper extends BaseMapper<SysMenuDO> {
+}

+ 9 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleDeptMapper.java

@@ -0,0 +1,9 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission;
+
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDeptDO;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SysRoleDeptMapper extends BaseMapper<SysRoleDeptDO> {
+}

+ 9 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMapper.java

@@ -0,0 +1,9 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission;
+
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SysRoleMapper extends BaseMapper<SysRoleDO> {
+}

+ 9 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMenuMapper.java

@@ -0,0 +1,9 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission;
+
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleMenuDO;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenuDO> {
+}

+ 17 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysUserRoleMapper.java

@@ -0,0 +1,17 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission;
+
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysUserRoleDO;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface SysUserRoleMapper extends BaseMapper<SysUserRoleDO> {
+
+    default List<SysUserRoleDO> selectListByUserId(Long userId) {
+        return selectList(new QueryWrapper<SysUserRoleDO>().eq("user_id", userId));
+    }
+
+}

+ 100 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/dept/SysDept.java

@@ -0,0 +1,100 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept;
+
+import cn.iocoder.dashboard.framework.mybatis.core.BaseDO;
+
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 部门表 sys_dept
+ *
+ * @author ruoyi
+ */
+public class SysDept extends BaseDO {
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+    /**
+     * 父部门ID
+     */
+    private Long parentId;
+
+    /**
+     * 祖级列表
+     */
+    private String ancestors;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    /**
+     * 显示顺序
+     */
+    private String orderNum;
+
+    /**
+     * 负责人
+     */
+    private String leader;
+
+    /**
+     * 联系电话
+     */
+    private String phone;
+
+    /**
+     * 邮箱
+     */
+    private String email;
+
+    /**
+     * 部门状态:0正常,1停用
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    private String delFlag;
+
+    /**
+     * 父部门名称
+     */
+    private String parentName;
+
+    /**
+     * 子部门
+     */
+    private List<SysDept> children = new ArrayList<SysDept>();
+
+    @NotBlank(message = "部门名称不能为空")
+    @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符")
+    public String getDeptName() {
+        return deptName;
+    }
+
+    @NotBlank(message = "显示顺序不能为空")
+    public String getOrderNum() {
+        return orderNum;
+    }
+
+    @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符")
+    public String getPhone() {
+        return phone;
+    }
+
+    @Email(message = "邮箱格式不正确")
+    @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
+    public String getEmail() {
+        return email;
+    }
+
+}

+ 0 - 1
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/package-info.java

@@ -1 +0,0 @@
-package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject;

+ 112 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysMenuDO.java

@@ -0,0 +1,112 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission;
+
+import cn.iocoder.dashboard.framework.mybatis.core.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 菜单权限表
+ *
+ * @author ruoyi
+ */
+@TableName("sys_menu")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SysMenuDO extends BaseDO {
+
+    /**
+     * 菜单ID
+     */
+    @TableId
+    private Long menuId;
+
+    /**
+     * 菜单名称
+     */
+    @NotBlank(message = "菜单名称不能为空")
+    @Size(max = 50, message = "菜单名称长度不能超过50个字符")
+    private String menuName;
+
+    /**
+     * 父菜单ID
+     */
+    private Long parentId;
+
+    /**
+     * 显示顺序
+     */
+    @NotBlank(message = "显示顺序不能为空")
+    private String orderNum;
+
+    /**
+     * 路由地址
+     */
+    @Size(max = 200, message = "路由地址不能超过200个字符")
+    private String path;
+
+    /**
+     * 组件路径
+     */
+    @Size(max = 200, message = "组件路径不能超过255个字符")
+    private String component;
+
+    /**
+     * 是否为外链(0是 1否)
+     */
+    private String isFrame;
+
+    /**
+     * 是否缓存(0缓存 1不缓存)
+     */
+    private String isCache;
+
+    /**
+     * 类型(M目录 C菜单 F按钮)
+     */
+    @NotBlank(message = "菜单类型不能为空")
+    private String menuType;
+
+    /**
+     * 显示状态(0显示 1隐藏)
+     */
+    private String visible;
+
+    /**
+     * 菜单状态(0显示 1隐藏)
+     */
+    private String status;
+
+    /**
+     * 权限字符串
+     */
+    @Size(max = 100, message = "权限标识长度不能超过100个字符")
+    private String perms;
+
+    /**
+     * 菜单图标
+     */
+    private String icon;
+
+    // TODO 芋艿:非存储字段,需要提出
+
+    /**
+     * 父菜单名称
+     */
+    @TableField(exist = false)
+    private String parentName;
+
+    /**
+     * 子菜单
+     */
+    @TableField(exist = false)
+    private List<SysMenuDO> children = new ArrayList<SysMenuDO>();
+
+}

+ 97 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysRoleDO.java

@@ -0,0 +1,97 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission;
+
+import cn.iocoder.dashboard.framework.excel.Excel;
+import cn.iocoder.dashboard.framework.mybatis.core.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+
+/**
+ * 角色 DO
+ *
+ * @author ruoyi
+ */
+@TableName("sys_role")
+@Data
+@EqualsAndHashCode(callSuper = true)
+// TODO 芋艿:数据库的字段默认值
+public class SysRoleDO extends BaseDO {
+
+    /**
+     * 角色ID
+     */
+    @TableId
+    @Excel(name = "角色序号", cellType = Excel.ColumnType.NUMERIC)
+    private Long roleId;
+
+    /**
+     * 角色名称
+     */
+    @Excel(name = "角色名称")
+    @NotBlank(message = "角色名称不能为空")
+    @Size(max = 30, message = "角色名称长度不能超过30个字符")
+    private String roleName;
+
+    /**
+     * 角色权限
+     */
+    @Excel(name = "角色权限")
+    @NotBlank(message = "权限字符不能为空")
+    @Size(max = 100, message = "权限字符长度不能超过100个字符")
+    private String roleKey;
+
+    /**
+     * 角色排序
+     */
+    @Excel(name = "角色排序")
+    @NotBlank(message = "显示顺序不能为空")
+    private Integer roleSort;
+
+    /**
+     * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限)
+     */
+    @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限")
+    private String dataScope;
+
+    /**
+     * 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示)
+     */
+    private boolean menuCheckStrictly;
+
+    /**
+     * 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 )
+     */
+    private boolean deptCheckStrictly;
+
+    /**
+     * 角色状态(0正常 1停用)
+     */
+    @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    // TODO FROM 芋艿:下面的字段,需要忽略
+
+    /**
+     * 用户是否存在此角色标识 默认不存在
+     */
+    @TableField(exist = false)
+    private boolean flag = false;
+
+    /**
+     * 菜单组
+     */
+    @TableField(exist = false)
+    private Long[] menuIds;
+
+    /**
+     * 部门组(数据权限)
+     */
+    @TableField(exist = false)
+    private Long[] deptIds;
+
+}

+ 33 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysRoleDeptDO.java

@@ -0,0 +1,33 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission;
+
+import cn.iocoder.dashboard.framework.mybatis.core.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 部门和角色关联 DO
+ *
+ * @author ruoyi
+ */
+@TableName("sys_role_dept")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SysRoleDeptDO extends BaseDO {
+
+    /**
+     * 自增主键
+     */
+    @TableId
+    private Long id;
+    /**
+     * 部门 ID
+     */
+    private Long deptId;
+    /**
+     * 角色 ID
+     */
+    private Long roleId;
+
+}

+ 33 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysRoleMenuDO.java

@@ -0,0 +1,33 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission;
+
+import cn.iocoder.dashboard.framework.mybatis.core.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 角色和菜单关联
+ *
+ * @author ruoyi
+ */
+@TableName("sys_role_menu")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SysRoleMenuDO extends BaseDO {
+
+    /**
+     * 自增主键
+     */
+    @TableId
+    private Long id;
+    /**
+     * 角色ID
+     */
+    private Long roleId;
+    /**
+     * 菜单ID
+     */
+    private Long menuId;
+
+}

+ 33 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysUserRoleDO.java

@@ -0,0 +1,33 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission;
+
+import cn.iocoder.dashboard.framework.mybatis.core.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 用户和角色关联
+ *
+ * @author ruoyi
+ */
+@TableName("sys_user_role")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SysUserRoleDO extends BaseDO {
+
+    /**
+     * 自增主键
+     */
+    @TableId
+    private Long id;
+    /**
+     * 用户 ID
+     */
+    private Long userId;
+    /**
+     * 角色 ID
+     */
+    private Long roleId;
+
+}

+ 8 - 19
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/user/SysUserDO.java

@@ -1,25 +1,23 @@
 package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user;
 
-import java.util.Date;
-import java.util.List;
-import javax.validation.constraints.Email;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.Size;
-
 import cn.iocoder.dashboard.framework.excel.Excel;
 import cn.iocoder.dashboard.framework.excel.Excels;
 import cn.iocoder.dashboard.framework.mybatis.core.BaseDO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysDept;
-import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRole;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import java.util.Date;
+import java.util.List;
+
 /**
  * 用户 DO
  *
@@ -128,7 +126,7 @@ public class SysUserDO extends BaseDO {
      * 角色对象
      */
     @TableField(exist = false)
-    private List<SysRole> roles;
+    private List<SysRoleDO> roles;
 
     /**
      * 角色组
@@ -142,13 +140,4 @@ public class SysUserDO extends BaseDO {
     @TableField(exist = false)
     private Long[] postIds;
 
-    // TODO 芋艿:后续清理掉
-    public boolean isAdmin() {
-        return isAdmin(this.userId);
-    }
-
-    public static boolean isAdmin(Long userId) {
-        return userId != null && 1L == userId;
-    }
-
 }

+ 21 - 0
src/main/java/cn/iocoder/dashboard/modules/system/enums/permission/RoleKeyEnum.java

@@ -0,0 +1,21 @@
+package cn.iocoder.dashboard.modules.system.enums.permission;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 角色标识枚举
+ */
+@Getter
+@AllArgsConstructor
+public enum RoleKeyEnum {
+
+    ADMIN("admin"), // 超级管理员
+    ;
+
+    /**
+     * 角色编码
+     */
+    private final String key;
+
+}

+ 15 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java

@@ -1,6 +1,9 @@
 package cn.iocoder.dashboard.modules.system.service.auth;
 
 import cn.iocoder.dashboard.framework.security.core.service.SecurityFrameworkService;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetInfoRespVO;
+
+import java.util.Set;
 
 /**
  * 认证 Service 接口
@@ -13,4 +16,16 @@ public interface SysAuthService extends SecurityFrameworkService {
 
     String login(String username, String password, String captchaUUID, String captchaCode);
 
+    /**
+     * 获得用户的基本信息
+     *
+     * 这里传输 roleIds 参数的原因,是该参数是从 LoginUser 缓存中获取到的,而我们校验权限时也是从 LoginUser 缓存中获取 roleIds
+     * 通过这样的方式,保持一致
+     *
+     * @param userId 用户编号
+     * @param roleIds 用户拥有的角色编号数组
+     * @return 用户的信息,包括角色权限和菜单权限
+     */
+    SysAuthGetInfoRespVO getInfo(Long userId, Set<Long> roleIds);
+
 }

+ 38 - 16
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java

@@ -4,13 +4,19 @@ import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
 import cn.iocoder.dashboard.framework.security.core.LoginUser;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetInfoRespVO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
 import cn.iocoder.dashboard.modules.system.enums.user.UserStatus;
 import cn.iocoder.dashboard.modules.system.convert.auth.SysAuthConvert;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
 import cn.iocoder.dashboard.modules.system.dal.redis.dao.auth.SysLoginUserRedisDAO;
 import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService;
 import cn.iocoder.dashboard.modules.system.service.auth.SysTokenService;
+import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
+import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService;
 import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
+import cn.iocoder.dashboard.util.collection.CollectionUtils;
 import cn.iocoder.dashboard.util.date.DateUtils;
 import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.JwtException;
@@ -27,9 +33,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.util.Assert;
 
 import javax.annotation.Resource;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Set;
+import java.util.*;
 
 import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
@@ -52,6 +56,11 @@ public class SysAuthServiceImpl implements SysAuthService {
     private AuthenticationManager authenticationManager;
     @Resource
     private SysUserService userService;
+    @Resource
+    private SysRoleService roleService;
+    @Resource
+    private SysPermissionService permissionService;
+
     @Resource
     private SysLoginUserRedisDAO loginUserRedisDAO;
 
@@ -62,7 +71,6 @@ public class SysAuthServiceImpl implements SysAuthService {
         if (user == null) {
             throw new UsernameNotFoundException(username);
         }
-
         // 创建 LoginUser 对象
         return SysAuthConvert.INSTANCE.convert(user);
     }
@@ -74,11 +82,10 @@ public class SysAuthServiceImpl implements SysAuthService {
         if (user == null) {
             throw new UsernameNotFoundException(String.valueOf(userId));
         }
-
         // 创建 LoginUser 对象
         LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user);
         loginUser.setUpdateTime(new Date());
-        loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId()));
+        loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId(), loginUser.getDeptId()));
         return loginUser;
     }
 
@@ -89,11 +96,10 @@ public class SysAuthServiceImpl implements SysAuthService {
 
         // 使用账号密码,进行登陆。
         LoginUser loginUser = this.login0(username, password);
-
         // 缓存登陆用户到 Redis 中
         String sessionId = IdUtil.fastSimpleUUID();
         loginUser.setUpdateTime(new Date());
-        loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId()));
+        loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId(), loginUser.getDeptId()));
         loginUserRedisDAO.set(sessionId, loginUser);
 
         // 创建 Token
@@ -143,11 +149,15 @@ public class SysAuthServiceImpl implements SysAuthService {
      * 获得 User 拥有的角色编号数组
      *
      * @param userId 用户编号
+     * @param deptId 科室编号
      * @return 角色编号数组
      */
-    private Set<Integer> getUserRoleIds(Long userId) {
-        // TODO 芋艿:读取角色编号
-        return Collections.emptySet();
+    private Set<Long> getUserRoleIds(Long userId, Long deptId) {
+        // 用户拥有的角色
+        Set<Long> roleIds = new HashSet<>(permissionService.listUserRoleIds(userId));
+        // 部门拥有的角色
+        CollectionUtils.addIfNotNull(roleIds, permissionService.getDeptRoleId(deptId));
+        return roleIds;
     }
 
     @Override
@@ -162,7 +172,6 @@ public class SysAuthServiceImpl implements SysAuthService {
         // 获得 LoginUser
         LoginUser loginUser = loginUserRedisDAO.get(sessionId);
         if (loginUser == null) {
-//            throw exception(AUTH_SESSION_TIMEOUT);
             return null;
         }
         // 刷新 LoginUser 缓存
@@ -176,18 +185,15 @@ public class SysAuthServiceImpl implements SysAuthService {
             claims = tokenService.parseToken(token);
         } catch (JwtException jwtException) {
             log.warn("[verifyToken][token({}) 解析发生异常]", token);
-//            throw exception(TOKEN_PARSE_FAIL);
             return null;
         }
         // token 已经过期
         if (DateUtils.isExpired(claims.getExpiration())) {
-//            throw exception(TOKEN_EXPIRED);
             return null;
         }
         // 判断 sessionId 是否存在
         String sessionId = claims.getSubject();
         if (StrUtil.isBlank(sessionId)) {
-//            throw exception(AUTH_SESSION_ID_NOT_FOUND);
             return null;
         }
         return sessionId;
@@ -207,9 +213,25 @@ public class SysAuthServiceImpl implements SysAuthService {
         }
 
         // 刷新 LoginUser 缓存
+        loginUser.setDeptId(user.getDeptId());
         loginUser.setUpdateTime(new Date());
-        loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId()));
+        loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId(), loginUser.getDeptId()));
         loginUserRedisDAO.set(sessionId, loginUser);
     }
 
+    @Override
+    public SysAuthGetInfoRespVO getInfo(Long userId, Set<Long> roleIds) {
+        // 获得用户信息
+        SysUserDO user = userService.getUser(userId);
+        if (user == null) {
+            return null;
+        }
+        // 获得角色列表
+        List<SysRoleDO> roleList = roleService.listRolesFromCache(roleIds);
+        // 获得菜单列表
+        List<SysMenuDO> menuList = permissionService.listRoleMenusFromCache(roleIds);
+        // 拼接结果返回
+        return SysAuthConvert.INSTANCE.convert(user, roleList, menuList);
+    }
+
 }

+ 33 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysMenuService.java

@@ -0,0 +1,33 @@
+package cn.iocoder.dashboard.modules.system.service.permission;
+
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 菜单 Service 接口
+ */
+public interface SysMenuService {
+
+    /**
+     *
+     */
+    void init();
+
+    /**
+     * 获得所有菜单,从缓存中
+     *
+     * @return 菜单列表
+     */
+    List<SysMenuDO> listMenusFromCache();
+
+    /**
+     * 获得指定编号的菜单数组,从缓存中
+     *
+     * @param menuIds 菜单编号数组
+     * @return 菜单数组
+     */
+    List<SysMenuDO> listMenusFromCache(Collection<Long> menuIds);
+
+}

+ 46 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysPermissionService.java

@@ -0,0 +1,46 @@
+package cn.iocoder.dashboard.modules.system.service.permission;
+
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 权限 Service 接口
+ *
+ * 提供用户-角色、角色-菜单、角色-部门的关联权限处理
+ *
+ * @author 芋道源码
+ */
+public interface SysPermissionService {
+
+    /**
+     * 初始化
+     */
+    void init();
+
+    /**
+     * 获得角色们拥有的菜单列表,从缓存中获取
+     *
+     * @param roleIds 角色编号素组
+     * @return 菜单列表
+     */
+    List<SysMenuDO> listRoleMenusFromCache(Collection<Long> roleIds);
+
+    /**
+     * 获得用户拥有的角色编号数组
+     *
+     * @param userId 用户编号
+     * @return 角色编号数组
+     */
+    List<Long> listUserRoleIds(Long userId);
+
+    /**
+     * 获得部门拥有的角色编号
+     *
+     * @param deptId 部门编号
+     * @return 角色编号
+     */
+    Long getDeptRoleId(Long deptId);
+
+}

+ 36 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysRoleService.java

@@ -0,0 +1,36 @@
+package cn.iocoder.dashboard.modules.system.service.permission;
+
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 角色 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface SysRoleService {
+
+    /**
+     * 初始化
+     */
+    void init();
+
+    /**
+     * 获得角色数组,从缓存中
+     *
+     * @param roleIds 角色编号数组
+     * @return 角色数组
+     */
+    List<SysRoleDO> listRolesFromCache(Collection<Long> roleIds);
+
+    /**
+     * 判断角色数组中,是否有管理员
+     *
+     * @param roleList 角色数组
+     * @return 是否有管理员
+     */
+    boolean hasAnyAdmin(Collection<SysRoleDO> roleList);
+
+}

+ 81 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java

@@ -0,0 +1,81 @@
+package cn.iocoder.dashboard.modules.system.service.permission.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysMenuMapper;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
+import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 菜单 Service 实现
+ */
+@Service
+@Slf4j
+public class SysMenuServiceImpl implements SysMenuService {
+
+    /**
+     * 菜单缓存
+     * key:菜单编号
+     *
+     * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
+     */
+    private volatile Map<Long, SysMenuDO> menuCache;
+    /**
+     * 权限与菜单缓存
+     * key:权限 {@link SysMenuDO#getPerms()}
+     * value:SysMenuDO 数组,因为一个权限可能对应多个 SysMenuDO 对象
+     *
+     * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
+     */
+    private volatile Multimap<String, SysMenuDO> permMenuCache;
+
+    @Resource
+    private SysMenuMapper menuMapper;
+
+    /**
+     * 初始化 {@link #menuCache} 和 {@link #permMenuCache} 缓存
+     */
+    @Override
+    @PostConstruct
+    public void init() {
+        List<SysMenuDO> menuList = menuMapper.selectList(null);
+        ImmutableMap.Builder<Long, SysMenuDO> menuCacheBuilder = ImmutableMap.builder();
+        ImmutableMultimap.Builder<String, SysMenuDO> permMenuCacheBuilder = ImmutableMultimap.builder();
+        menuList.forEach(menuDO -> {
+            menuCacheBuilder.put(menuDO.getMenuId(), menuDO);
+            permMenuCacheBuilder.put(menuDO.getPerms(), menuDO);
+        });
+        menuCache = menuCacheBuilder.build();
+        permMenuCache = permMenuCacheBuilder.build();
+        log.info("[init][初始化菜单数量为 {}]", menuList.size());
+    }
+
+    @Override
+    public List<SysMenuDO> listMenusFromCache() {
+        // Guava ImmutableMap 对应的 value 类型为 ImmutableCollection
+        // 而 ImmutableList 在 copyof 时,如果入参类型为 ImmutableCollection 时,会进行包装,而不会进行复制。
+        return ImmutableList.copyOf(menuCache.values());
+    }
+
+    @Override
+    public List<SysMenuDO> listMenusFromCache(Collection<Long> menuIds) {
+        if (CollectionUtil.isEmpty(menuIds)) {
+            return Collections.emptyList();
+        }
+        return menuCache.values().stream().filter(menuDO -> menuIds.contains(menuDO.getMenuId()))
+                .collect(Collectors.toList());
+    }
+}

+ 104 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java

@@ -0,0 +1,104 @@
+package cn.iocoder.dashboard.modules.system.service.permission.impl;
+
+import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysRoleDeptMapper;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysRoleMenuMapper;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysUserRoleMapper;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.*;
+import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService;
+import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
+import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService;
+import cn.iocoder.dashboard.util.collection.CollectionUtils;
+import cn.iocoder.dashboard.util.collection.MapUtils;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 权限 Service 实现类
+ *
+ * @author 初始化
+ */
+@Service
+@Slf4j
+public class SysPermissionServiceImpl implements SysPermissionService {
+
+    /**
+     * 角色编号与菜单编号的缓存映射
+     * key:角色编号
+     * value:菜单编号的数组
+     *
+     * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
+     */
+    private volatile Multimap<Long, Long> roleMenuCache;
+    /**
+     * 菜单编号与角色编号的缓存映射
+     * key:菜单编号
+     * value:角色编号的数组
+     *
+     * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
+     */
+    private volatile Multimap<Long, Long> menuRoleCache;
+
+    @Resource
+    private SysRoleMenuMapper roleMenuMapper;
+    @Resource
+    private SysUserRoleMapper userRoleMapper;
+    @Resource
+    private SysRoleDeptMapper roleDeptMapper;
+
+    @Resource
+    private SysRoleService roleService;
+    @Resource
+    private SysMenuService menuService;
+
+    /**
+     * 初始化 {@link #roleMenuCache} 和 {@link #menuRoleCache} 缓存
+     */
+    @Override
+    @PostConstruct
+    public void init() {
+        // 初始化 roleMenuCache 和 menuRoleCache 缓存
+        List<SysRoleMenuDO> roleMenuList = roleMenuMapper.selectList(null);
+        ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder();
+        ImmutableMultimap.Builder<Long, Long> menuRoleCacheBuilder = ImmutableMultimap.builder();
+        roleMenuList.forEach(roleMenuDO -> {
+            roleMenuCacheBuilder.put(roleMenuDO.getRoleId(), roleMenuDO.getMenuId());
+            menuRoleCacheBuilder.put(roleMenuDO.getMenuId(), roleMenuDO.getRoleId());
+        });
+        roleMenuCache = roleMenuCacheBuilder.build();
+        menuRoleCache = menuRoleCacheBuilder.build();
+        log.info("[init][初始化角色与菜单的关联数量为 {}]", roleMenuList.size());
+    }
+
+    @Override
+    public List<SysMenuDO> listRoleMenusFromCache(Collection<Long> roleIds) {
+        // 判断角色是否包含管理员
+        List<SysRoleDO> roleList = roleService.listRolesFromCache(roleIds);
+        boolean hasAdmin = roleService.hasAnyAdmin(roleList);
+        // 获得角色拥有的菜单关联
+        if (hasAdmin) { // 管理员,获取到全部
+            return menuService.listMenusFromCache();
+        }
+        List<Long> menuIds = MapUtils.getList(roleMenuCache, roleIds);
+        return menuService.listMenusFromCache(menuIds);
+    }
+
+    @Override
+    public List<Long> listUserRoleIds(Long userId) {
+        List<SysUserRoleDO> userRoleList = userRoleMapper.selectListByUserId(userId);
+        return CollectionUtils.convertList(userRoleList, SysUserRoleDO::getRoleId);
+    }
+
+    @Override
+    public Long getDeptRoleId(Long deptId) {
+        SysRoleDeptDO roleDept = roleDeptMapper.selectById(deptId);
+        return roleDept != null ? roleDept.getRoleId() : null;
+    }
+
+}

+ 70 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java

@@ -0,0 +1,70 @@
+package cn.iocoder.dashboard.modules.system.service.permission.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysRoleMapper;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
+import cn.iocoder.dashboard.modules.system.enums.permission.RoleKeyEnum;
+import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService;
+import com.google.common.collect.ImmutableMap;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 角色 Service 实现类
+ */
+@Service
+@Slf4j
+public class SysRoleServiceImpl implements SysRoleService {
+
+    @Resource
+    private SysRoleMapper roleMapper;
+
+    /**
+     * 角色缓存
+     * key:角色编号 {@link SysRoleDO#getRoleId()}
+     *
+     * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
+     */
+    private volatile Map<Long, SysRoleDO> roleCache;
+
+    /**
+     * 初始化 {@link #roleCache} 缓存
+     */
+    @Override
+    @PostConstruct
+    public void init() {
+        // 从数据库中读取
+        List<SysRoleDO> roleDOList = roleMapper.selectList(null);
+        // 写入缓存
+        ImmutableMap.Builder<Long, SysRoleDO> builder = ImmutableMap.builder();
+        roleDOList.forEach(sysRoleDO -> builder.put(sysRoleDO.getRoleId(), sysRoleDO));
+        roleCache = builder.build();
+        log.info("[init][初始化 Role 数量为 {}]", roleDOList.size());
+    }
+
+    @Override
+    public List<SysRoleDO> listRolesFromCache(Collection<Long> roleIds) {
+        if (CollectionUtil.isEmpty(roleIds)) {
+            return Collections.emptyList();
+        }
+        return roleCache.values().stream().filter(roleDO -> roleIds.contains(roleDO.getRoleId()))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public boolean hasAnyAdmin(Collection<SysRoleDO> roleList) {
+        if (CollectionUtil.isEmpty(roleList)) {
+            return false;
+        }
+        return roleList.stream().anyMatch(roleDO -> RoleKeyEnum.ADMIN.getKey().equals(roleDO.getRoleKey()));
+    }
+
+}