1. admin-web 接入菜单

2. system 迁移菜单接口
This commit is contained in:
YunaiV
2020-04-27 00:08:28 +08:00
parent 18180b3a01
commit 983c01d709
27 changed files with 323 additions and 103 deletions

View File

@@ -0,0 +1,17 @@
package cn.iocoder.mall.system.biz.enums.authorization;
public enum ResourceIdEnum {
ROOT(0);
private final Integer id;
ResourceIdEnum(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.mall.system.biz.enums.authorization;
public enum RoleTypeEnum {
/**
* 内置角色
*/
SYSTEM(1),
/**
* 自定义角色
*/
CUSTOM(2);
private final Integer type;
RoleTypeEnum(Integer type) {
this.type = type;
}
public Integer getType() {
return type;
}
}

View File

@@ -4,20 +4,14 @@ import cn.iocoder.common.framework.mybatis.QueryWrapperX;
import cn.iocoder.mall.system.biz.dataobject.authorization.ResourceDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@Repository
public interface ResourceMapper extends BaseMapper<ResourceDO> {
// TODO 芋艿,后续改造。
List<ResourceDO> selectListByTypeAndRoleIds(@Param("type") Integer type,
@Param("roleIds") Set<Integer> roleIds);
default ResourceDO selectByPermission(String permission) {
return selectOne(new QueryWrapper<ResourceDO>().eq("permission", permission));
}
@@ -26,8 +20,9 @@ public interface ResourceMapper extends BaseMapper<ResourceDO> {
return selectList(new QueryWrapper<ResourceDO>().in("permission", permissions));
}
default List<ResourceDO> selectListByType(Integer type) {
return selectList(new QueryWrapperX<ResourceDO>().eqIfPresent("type", type));
default List<ResourceDO> selectListByIdsAndType(Collection<Integer> ids, Integer type) {
return selectList(new QueryWrapperX<ResourceDO>().inIfPresent("id", ids)
.eqIfPresent("type", type));
}
default int selectCountByPid(Integer pid) {

View File

@@ -19,14 +19,22 @@ public interface RoleResourceMapper extends BaseMapper<RoleResourceDO> {
*/
int insertList(@Param("roleResources") List<RoleResourceDO> roleResources);
default List<RoleResourceDO> selectListByResourceId(Integer resourceId) {
default List<RoleResourceDO> selectListByResourceId(Integer resourceId) {
return selectList(new QueryWrapper<RoleResourceDO>().eq("resource_id", resourceId));
}
default List<RoleResourceDO> selectListByResourceIds(Collection<Integer> resourceIds) {
default List<RoleResourceDO> selectListByResourceIds(Collection<Integer> resourceIds) {
return selectList(new QueryWrapper<RoleResourceDO>().in("resource_id", resourceIds));
}
default List<RoleResourceDO> selectListByRoleId(Integer roleId) {
return selectList(new QueryWrapper<RoleResourceDO>().eq("role_id", roleId));
}
default List<RoleResourceDO> selectListByRoleIds(Collection<Integer> roleIds) {
return selectList(new QueryWrapper<RoleResourceDO>().in("role_id", roleIds));
}
default int deleteByResourceId(Integer resourceId) {
return delete(new QueryWrapper<RoleResourceDO>().eq("resource_id", resourceId));
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.mall.system.biz.dto.authorization;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
/**
* 授权模块 - 获得账号所拥有的资源 DTO
*/
@Data
@Accessors(chain = true)
public class AuthorizationGetResourcesByAccountIdDTO {
@NotNull(message = "账号编号不能为空")
private Integer accountId;
/**
* 资源类型
*/
private Integer type;
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.mall.system.biz.dto.authorization;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Collection;
/**
* 资源模块 - 获得资源列表 DTO
*/
@Data
@Accessors(chain = true)
public class ResourceGetListDTO {
/**
* 资源编号数组
*/
private Collection<Integer> ids;
/**
* 资源类型
*/
private Integer type;
}

View File

@@ -1,9 +1,32 @@
package cn.iocoder.mall.system.biz.service.authorization;
import cn.iocoder.common.framework.exception.ServiceException;
import cn.iocoder.mall.system.biz.bo.authorization.ResourceBO;
import cn.iocoder.mall.system.biz.dto.authorization.AuthorizationCheckPermissionsDTO;
import cn.iocoder.mall.system.biz.dto.authorization.AuthorizationGetResourcesByAccountIdDTO;
import java.util.List;
/**
* 授权模块 - Service 接口
*/
public interface AuthorizationService {
/**
* 校验指定账号是否有指定权限。如果没有,则抛出 {@link ServiceException} 异常
*
* @param checkPermissionsDTO 校验权限 DTO
*/
void checkPermissions(AuthorizationCheckPermissionsDTO checkPermissionsDTO);
/**
* 获得指定账号的资源列表
*
* 如果该账号为超级管理员,则返回所有资源
*
* @param getResourcesByAccountIdDTO 查询条件 DTO
* @return 列表
*/
List<ResourceBO> getResourcesByAccountId(AuthorizationGetResourcesByAccountIdDTO getResourcesByAccountIdDTO);
}

View File

@@ -8,16 +8,22 @@ import cn.iocoder.mall.system.biz.dao.authorization.RoleResourceMapper;
import cn.iocoder.mall.system.biz.dataobject.authorization.AccountRoleDO;
import cn.iocoder.mall.system.biz.dataobject.authorization.RoleResourceDO;
import cn.iocoder.mall.system.biz.dto.authorization.AuthorizationCheckPermissionsDTO;
import cn.iocoder.mall.system.biz.dto.authorization.AuthorizationGetResourcesByAccountIdDTO;
import cn.iocoder.mall.system.biz.dto.authorization.ResourceGetListDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.mall.system.biz.enums.SystemErrorCodeEnum.AUTHORIZATION_PERMISSION_DENY;
/**
* 授权模块 - Service 实现类
*/
@Service
@Slf4j
public class AuthorizationServiceImpl implements AuthorizationService {
@@ -45,7 +51,7 @@ public class AuthorizationServiceImpl implements AuthorizationService {
return;
}
// 查询权限对应资源
List<ResourceBO> resourceBOs = resourceService.getListByPermissions(checkPermissionsDTO.getPermissions());
List<ResourceBO> resourceBOs = resourceService.getResourcesByPermissions(checkPermissionsDTO.getPermissions());
if (CollectionUtil.isEmpty(resourceBOs)) { // 无对应资源,则认为无需权限验证
log.warn("[checkPermissions][permission({}) 未配置对应资源]", checkPermissionsDTO.getPermissions());
return;
@@ -65,4 +71,26 @@ public class AuthorizationServiceImpl implements AuthorizationService {
}
}
@Override
public List<ResourceBO> getResourcesByAccountId(AuthorizationGetResourcesByAccountIdDTO getResourcesByAccountIdDTO) {
// 查询管理员拥有的角色关联数据
List<AccountRoleDO> accountRoleDOs = accountRoleMapper.selectByAccountId(getResourcesByAccountIdDTO.getAccountId());
if (CollectionUtil.isEmpty(accountRoleDOs)) {
return Collections.emptyList();
}
Set<Integer> roleIds = CollectionUtil.convertSet(accountRoleDOs, AccountRoleDO::getRoleId);
// 判断是否为超管。若是超管,默认有所有权限
if (roleService.hasSuperAdmin(roleIds)) {
return resourceService.getResources(new ResourceGetListDTO().setType(getResourcesByAccountIdDTO.getType()));
}
// 查询角色拥有的资源关联数据
List<RoleResourceDO> roleResourceDOs = roleResourceMapper.selectListByRoleIds(roleIds);
if (CollectionUtil.isEmpty(roleResourceDOs)) {
return Collections.emptyList();
}
Set<Integer> resourceIds = CollectionUtil.convertSet(roleResourceDOs, RoleResourceDO::getResourceId);
// 查询对应资源
return resourceService.getResources(new ResourceGetListDTO().setIds(resourceIds).setType(getResourcesByAccountIdDTO.getType()));
}
}

View File

@@ -1,12 +1,15 @@
package cn.iocoder.mall.system.biz.service.authorization;
import cn.iocoder.mall.system.biz.bo.authorization.ResourceBO;
import cn.iocoder.mall.system.biz.dto.authorization.ResourceGetListDTO;
import java.util.Collection;
import java.util.List;
public interface ResourceService {
List<ResourceBO> getListByPermissions(Collection<String> permissions);
List<ResourceBO> getResourcesByPermissions(Collection<String> permissions);
List<ResourceBO> getResources(ResourceGetListDTO getListDTO);
}

View File

@@ -4,6 +4,7 @@ import cn.iocoder.mall.system.biz.bo.authorization.ResourceBO;
import cn.iocoder.mall.system.biz.convert.authorization.ResourceConvert;
import cn.iocoder.mall.system.biz.dao.authorization.ResourceMapper;
import cn.iocoder.mall.system.biz.dataobject.authorization.ResourceDO;
import cn.iocoder.mall.system.biz.dto.authorization.ResourceGetListDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -17,9 +18,15 @@ public class ResourceServiceImpl implements ResourceService {
private ResourceMapper resourceMapper;
@Override
public List<ResourceBO> getListByPermissions(Collection<String> permissions) {
public List<ResourceBO> getResourcesByPermissions(Collection<String> permissions) {
List<ResourceDO> resourceDOs = resourceMapper.selectListByPermissions(permissions);
return ResourceConvert.INSTANCE.convertList(resourceDOs);
}
@Override
public List<ResourceBO> getResources(ResourceGetListDTO getListDTO) {
List<ResourceDO> resourceDOs = resourceMapper.selectListByIdsAndType(getListDTO.getIds(), getListDTO.getType());
return ResourceConvert.INSTANCE.convertList(resourceDOs);
}
}

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.mall.admin.dao.ResourceMapper">
<sql id="FIELDS">
id, type, sort, display_name, icon, permissions,
create_time, pid, handler
</sql>
<select id="selectListByTypeAndRoleIds" resultType="ResourceDO">
SELECT
r.id, r.type, r.sort, r.display_name,
r.create_time, r.pid, r.handler
FROM resource r, role_resource rr
WHERE r.deleted = 0
AND rr.deleted = 0
<if test="type != null">
AND r.type = #{type}
</if>
AND rr.role_id IN
<foreach item="roleId" collection="roleIds" separator="," open="(" close=")" index="">
#{roleId}
</foreach>
AND r.id = rr.resource_id
</select>
</mapper>

View File

@@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(MallConstants.ROOT_PATH_ADMIN + "/admin")
@Api(tags = "管理员 - 管理员 API")
public class AdminsOAdminController {
public class AdminsAdminController {

View File

@@ -0,0 +1,66 @@
package cn.iocoder.mall.system.rest.controller.authorization;
import cn.iocoder.common.framework.constant.MallConstants;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.security.core.context.AdminSecurityContextHolder;
import cn.iocoder.mall.system.biz.bo.authorization.ResourceBO;
import cn.iocoder.mall.system.biz.dto.authorization.AuthorizationGetResourcesByAccountIdDTO;
import cn.iocoder.mall.system.biz.enums.authorization.ResourceIdEnum;
import cn.iocoder.mall.system.biz.enums.authorization.ResourceTypeEnum;
import cn.iocoder.mall.system.biz.service.authorization.AuthorizationService;
import cn.iocoder.mall.system.rest.convert.authorization.AdminsAuthorizationConvert;
import cn.iocoder.mall.system.rest.response.authorization.AdminsAuthorizationMenuTreeResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
import java.util.stream.Collectors;
@RestController
@RequestMapping(MallConstants.ROOT_PATH_ADMIN + "/authorization")
@Api(tags = "管理员 - 授权 API")
@Slf4j
public class AdminsAuthorizationController {
@Autowired
private AuthorizationService authorizationService;
@GetMapping("/menu-resource-tree")
@ApiOperation(value = "获得当前账号的菜单资源树", notes = "以树结构返回")
public CommonResult<List<AdminsAuthorizationMenuTreeResponse>> menuResourceTree() {
List<ResourceBO> resources = authorizationService.getResourcesByAccountId(new AuthorizationGetResourcesByAccountIdDTO()
.setAccountId(AdminSecurityContextHolder.getAccountId()).setType(ResourceTypeEnum.MENU.getValue()));
// 创建 AdminMenuTreeNodeVO Map
// 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。
Map<Integer, AdminsAuthorizationMenuTreeResponse> treeNodeMap = new LinkedHashMap<>();
resources.stream().sorted(Comparator.comparing(ResourceBO::getSort))
.forEach(resourceBO -> treeNodeMap.put(resourceBO.getId(), AdminsAuthorizationConvert.INSTANCE.convert(resourceBO)));
// 处理父子关系
treeNodeMap.values().stream()
.filter(node -> !node.getPid().equals(ResourceIdEnum.ROOT.getId()))
.forEach((childNode) -> {
// 获得父节点
AdminsAuthorizationMenuTreeResponse parentNode = treeNodeMap.get(childNode.getPid());
if (parentNode == null) {
log.error("[menuResourceTree][resource({}) 找不到父资源({})]", childNode.getId(), childNode.getPid());
return;
}
if (parentNode.getChildren() == null) { // 初始化 children 数组
parentNode.setChildren(new ArrayList<>());
}
// 将自己添加到父节点中
parentNode.getChildren().add(childNode);
});
// 获得到所有的根节点
List<AdminsAuthorizationMenuTreeResponse> rootNodes = treeNodeMap.values().stream()
.filter(node -> node.getPid().equals(ResourceIdEnum.ROOT.getId()))
.collect(Collectors.toList());
return CommonResult.success(rootNodes);
}
}

View File

@@ -0,0 +1,15 @@
package cn.iocoder.mall.system.rest.convert.authorization;
import cn.iocoder.mall.system.biz.bo.authorization.ResourceBO;
import cn.iocoder.mall.system.rest.response.authorization.AdminsAuthorizationMenuTreeResponse;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface AdminsAuthorizationConvert {
AdminsAuthorizationConvert INSTANCE = Mappers.getMapper(AdminsAuthorizationConvert.class);
AdminsAuthorizationMenuTreeResponse convert(ResourceBO bean);
}

View File

@@ -1,4 +1,4 @@
package cn.iocoder.mall.system.application.vo.admin;
package cn.iocoder.mall.system.rest.response.authorization;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@@ -7,24 +7,23 @@ import lombok.experimental.Accessors;
import java.util.List;
@ApiModel("管理员拥有的菜单 VO")
@ApiModel("管理员 - 授权模块 - 菜单资源树")
@Data
@Accessors(chain = true)
public class AdminMenuTreeNodeVO {
public class AdminsAuthorizationMenuTreeResponse {
@ApiModelProperty(value = "菜单编号", required = true, example = "1")
private Integer id;
// @ApiModelProperty(value = "菜单名", required = true, example = "商品管理")
// private String name;
@ApiModelProperty(value = "菜单操作", required = true, example = "/order/list")
private String handler;
@ApiModelProperty(value = "父菜单编号", required = true, example = "1", notes = "如果无父菜单,则值为 0")
private Integer pid;
@ApiModelProperty(value = "菜单名", required = true, example = "商品管理")
private String name;
@ApiModelProperty(value = "排序", required = true, example = "1")
private Integer sort;
@ApiModelProperty(value = "菜单展示名", required = true, example = "商品管理")
private String displayName;
@ApiModelProperty(value = "菜单编号", required = true, example = "1", notes = "如果无父菜单,则值为 0")
private Integer pid;
@ApiModelProperty(value = "route", required = true, example = "/order/list")
private String route;
@ApiModelProperty(value = "子节点数组")
private List<AdminMenuTreeNodeVO> children;
private List<AdminsAuthorizationMenuTreeResponse> children;
}

View File

@@ -54,35 +54,6 @@ public class AdminController {
// TODO 功能:当前管理员
@SuppressWarnings("Duplicates")
@GetMapping("/menu_resource_tree")
@ApiOperation(value = "获得当前登陆的管理员拥有的菜单权限", notes = "以树结构返回")
public CommonResult<List<AdminMenuTreeNodeVO>> menuResourceTree() {
List<ResourceBO> resources = resourceService.getResourcesByTypeAndRoleIds(ResourceConstants.TYPE_MENU,
AdminSecurityContextHolder.getContext().getRoleIds());
// 创建 AdminMenuTreeNodeVO Map
Map<Integer, AdminMenuTreeNodeVO> treeNodeMap = new LinkedHashMap<>(); // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。
resources.stream().sorted(Comparator.comparing(ResourceBO::getSort)).forEach(resourceBO -> treeNodeMap.put(resourceBO.getId(), ResourceConvert.INSTANCE.convert(resourceBO)));
// 处理父子关系
treeNodeMap.values().stream()
.filter(node -> !node.getPid().equals(ResourceConstants.PID_ROOT))
.forEach((childNode) -> {
// 获得父节点
AdminMenuTreeNodeVO parentNode = treeNodeMap.get(childNode.getPid());
if (parentNode.getChildren() == null) { // 初始化 children 数组
parentNode.setChildren(new ArrayList<>());
}
// 将自己添加到父节点中
parentNode.getChildren().add(childNode);
});
// 获得到所有的根节点
List<AdminMenuTreeNodeVO> rootNodes = treeNodeMap.values().stream()
.filter(node -> node.getPid().equals(ResourceConstants.PID_ROOT))
// .sorted(Comparator.comparing(AdminMenuTreeNodeVO::getSort))
.collect(Collectors.toList());
return success(rootNodes);
}
@GetMapping("/url_resource_list")
@ApiOperation(value = "获得当前登陆的管理员拥有的 URL 权限列表")
public CommonResult<Set<String>> urlResourceList() {