diff --git a/src/main/java/cn/iocoder/dashboard/framework/redis/core/RedisKeyDefine.java b/src/main/java/cn/iocoder/dashboard/framework/redis/core/RedisKeyDefine.java new file mode 100644 index 000000000..ffae0ec46 --- /dev/null +++ b/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; + } + +} diff --git a/src/main/java/cn/iocoder/dashboard/framework/security/core/LoginUser.java b/src/main/java/cn/iocoder/dashboard/framework/security/core/LoginUser.java index 316a12ba0..aac7916d5 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/security/core/LoginUser.java +++ b/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 roleIds; + private Set roleIds; /** * 最后更新时间 */ diff --git a/src/main/java/cn/iocoder/dashboard/framework/security/core/util/SecurityUtils.java b/src/main/java/cn/iocoder/dashboard/framework/security/core/util/SecurityUtils.java index 52989c05f..5d333ed84 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/security/core/util/SecurityUtils.java +++ b/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 getLoginUserRoleIds() { + return getLoginUser().getRoleIds(); + } + /** * 设置当前用户 * diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.http b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.http new file mode 100644 index 000000000..7d0b4c388 --- /dev/null +++ b/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}} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java new file mode 100644 index 000000000..6de13e8f6 --- /dev/null +++ b/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 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 getInfo() { + SysAuthGetInfoRespVO respVO = authService.getInfo(getLoginUserId(), getLoginUserRoleIds()); + return success(respVO); + } + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthGetInfoRespVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthGetInfoRespVO.java new file mode 100644 index 000000000..c80f4f3aa --- /dev/null +++ b/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 roles; + + @ApiModelProperty(value = "菜单权限数组", required = true) + private Set permissions; + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthLoginReqVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthLoginReqVO.java new file mode 100644 index 000000000..affbf3ced --- /dev/null +++ b/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; + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthLoginRespVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthLoginRespVO.java new file mode 100644 index 000000000..5a1caeb6e --- /dev/null +++ b/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; + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java b/src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java index 789bef1c6..5d4488cb8 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java +++ b/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 roleList, List menuList) { + SysAuthGetInfoRespVO respVO = new SysAuthGetInfoRespVO(); + respVO.setRoles(CollectionUtils.convertSet(roleList, SysRoleDO::getRoleKey)); + respVO.setPermissions(CollectionUtils.convertSet(menuList, SysMenuDO::getPerms)); + return respVO; + } + } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/package-info.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/package-info.java deleted file mode 100644 index 5d6e72f0e..000000000 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.dashboard.modules.system.dal.mysql.dao; diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysMenuMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysMenuMapper.java new file mode 100644 index 000000000..6825d7cd4 --- /dev/null +++ b/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 { +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleDeptMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleDeptMapper.java new file mode 100644 index 000000000..dd9052c1b --- /dev/null +++ b/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 { +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMapper.java new file mode 100644 index 000000000..e148e2485 --- /dev/null +++ b/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 { +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMenuMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMenuMapper.java new file mode 100644 index 000000000..07d5157ac --- /dev/null +++ b/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 { +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysUserRoleMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysUserRoleMapper.java new file mode 100644 index 000000000..d6056eaac --- /dev/null +++ b/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 { + + default List selectListByUserId(Long userId) { + return selectList(new QueryWrapper().eq("user_id", userId)); + } + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/dept/SysDept.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/dept/SysDept.java new file mode 100644 index 000000000..4ec9af7d3 --- /dev/null +++ b/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 children = new ArrayList(); + + @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; + } + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/package-info.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/package-info.java deleted file mode 100644 index ddbb0e623..000000000 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject; diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysMenuDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysMenuDO.java new file mode 100644 index 000000000..b0dfad84a --- /dev/null +++ b/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 children = new ArrayList(); + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysRoleDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysRoleDO.java new file mode 100644 index 000000000..fdf369e3d --- /dev/null +++ b/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; + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysRoleDeptDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysRoleDeptDO.java new file mode 100644 index 000000000..e5334d287 --- /dev/null +++ b/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; + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysRoleMenuDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysRoleMenuDO.java new file mode 100644 index 000000000..7a49732b7 --- /dev/null +++ b/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; + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysUserRoleDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysUserRoleDO.java new file mode 100644 index 000000000..f01253688 --- /dev/null +++ b/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; + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/user/SysUserDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/user/SysUserDO.java index 76b8743d9..91b4f3afc 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/user/SysUserDO.java +++ b/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 roles; + private List 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; - } - } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/enums/permission/RoleKeyEnum.java b/src/main/java/cn/iocoder/dashboard/modules/system/enums/permission/RoleKeyEnum.java new file mode 100644 index 000000000..77415a94c --- /dev/null +++ b/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; + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java index e212554a9..b97447837 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java +++ b/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 roleIds); + } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java index 9b2065d30..0649f4ef9 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java +++ b/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 getUserRoleIds(Long userId) { - // TODO 芋艿:读取角色编号 - return Collections.emptySet(); + private Set getUserRoleIds(Long userId, Long deptId) { + // 用户拥有的角色 + Set 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 roleIds) { + // 获得用户信息 + SysUserDO user = userService.getUser(userId); + if (user == null) { + return null; + } + // 获得角色列表 + List roleList = roleService.listRolesFromCache(roleIds); + // 获得菜单列表 + List menuList = permissionService.listRoleMenusFromCache(roleIds); + // 拼接结果返回 + return SysAuthConvert.INSTANCE.convert(user, roleList, menuList); + } + } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysMenuService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysMenuService.java new file mode 100644 index 000000000..09898e124 --- /dev/null +++ b/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 listMenusFromCache(); + + /** + * 获得指定编号的菜单数组,从缓存中 + * + * @param menuIds 菜单编号数组 + * @return 菜单数组 + */ + List listMenusFromCache(Collection menuIds); + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysPermissionService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysPermissionService.java new file mode 100644 index 000000000..51be3c7ac --- /dev/null +++ b/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 listRoleMenusFromCache(Collection roleIds); + + /** + * 获得用户拥有的角色编号数组 + * + * @param userId 用户编号 + * @return 角色编号数组 + */ + List listUserRoleIds(Long userId); + + /** + * 获得部门拥有的角色编号 + * + * @param deptId 部门编号 + * @return 角色编号 + */ + Long getDeptRoleId(Long deptId); + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysRoleService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysRoleService.java new file mode 100644 index 000000000..fadb809a9 --- /dev/null +++ b/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 listRolesFromCache(Collection roleIds); + + /** + * 判断角色数组中,是否有管理员 + * + * @param roleList 角色数组 + * @return 是否有管理员 + */ + boolean hasAnyAdmin(Collection roleList); + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java new file mode 100644 index 000000000..5f536ac61 --- /dev/null +++ b/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 menuCache; + /** + * 权限与菜单缓存 + * key:权限 {@link SysMenuDO#getPerms()} + * value:SysMenuDO 数组,因为一个权限可能对应多个 SysMenuDO 对象 + * + * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 + */ + private volatile Multimap permMenuCache; + + @Resource + private SysMenuMapper menuMapper; + + /** + * 初始化 {@link #menuCache} 和 {@link #permMenuCache} 缓存 + */ + @Override + @PostConstruct + public void init() { + List menuList = menuMapper.selectList(null); + ImmutableMap.Builder menuCacheBuilder = ImmutableMap.builder(); + ImmutableMultimap.Builder 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 listMenusFromCache() { + // Guava ImmutableMap 对应的 value 类型为 ImmutableCollection + // 而 ImmutableList 在 copyof 时,如果入参类型为 ImmutableCollection 时,会进行包装,而不会进行复制。 + return ImmutableList.copyOf(menuCache.values()); + } + + @Override + public List listMenusFromCache(Collection menuIds) { + if (CollectionUtil.isEmpty(menuIds)) { + return Collections.emptyList(); + } + return menuCache.values().stream().filter(menuDO -> menuIds.contains(menuDO.getMenuId())) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java new file mode 100644 index 000000000..815a6f26b --- /dev/null +++ b/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 roleMenuCache; + /** + * 菜单编号与角色编号的缓存映射 + * key:菜单编号 + * value:角色编号的数组 + * + * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 + */ + private volatile Multimap 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 roleMenuList = roleMenuMapper.selectList(null); + ImmutableMultimap.Builder roleMenuCacheBuilder = ImmutableMultimap.builder(); + ImmutableMultimap.Builder 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 listRoleMenusFromCache(Collection roleIds) { + // 判断角色是否包含管理员 + List roleList = roleService.listRolesFromCache(roleIds); + boolean hasAdmin = roleService.hasAnyAdmin(roleList); + // 获得角色拥有的菜单关联 + if (hasAdmin) { // 管理员,获取到全部 + return menuService.listMenusFromCache(); + } + List menuIds = MapUtils.getList(roleMenuCache, roleIds); + return menuService.listMenusFromCache(menuIds); + } + + @Override + public List listUserRoleIds(Long userId) { + List 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; + } + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java new file mode 100644 index 000000000..578c7227b --- /dev/null +++ b/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 roleCache; + + /** + * 初始化 {@link #roleCache} 缓存 + */ + @Override + @PostConstruct + public void init() { + // 从数据库中读取 + List roleDOList = roleMapper.selectList(null); + // 写入缓存 + ImmutableMap.Builder builder = ImmutableMap.builder(); + roleDOList.forEach(sysRoleDO -> builder.put(sysRoleDO.getRoleId(), sysRoleDO)); + roleCache = builder.build(); + log.info("[init][初始化 Role 数量为 {}]", roleDOList.size()); + } + + @Override + public List listRolesFromCache(Collection 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 roleList) { + if (CollectionUtil.isEmpty(roleList)) { + return false; + } + return roleList.stream().anyMatch(roleDO -> RoleKeyEnum.ADMIN.getKey().equals(roleDO.getRoleKey())); + } + +}