Browse Source

feat(security): 优化权限异常处理并添加模板下载功能

- 在AccessDeniedExceptionHandler和AccessDeniedHandler中透出具体缺少的权限信息,便于定位权限配置问题
- 重构PermissionService实现详细权限提示信息,包括缺少权限标识、归属角色及当前用户角色
- 在设备、备件、保养项控制器中添加导入模板下载功能
- 优化Excel导入工具类中车间信息处理逻辑,支持车间名称到ID的转换
- 在报修单服务中增强审批查询功能,支持按状态列表过滤
- 添加VSCode Java开发环境配置文件
dazhaxie 1 week ago
parent
commit
193583bb83

+ 4 - 0
.vscode/settings.json

@@ -0,0 +1,4 @@
+{
+  "java.compile.nullAnalysis.mode": "automatic",
+  "java.configuration.updateBuildConfiguration": "interactive"
+}

+ 12 - 2
platform-dao/src/main/java/com/platform/dao/dto/repair/RepairApplicationFormDTO.java

@@ -27,14 +27,19 @@ import java.util.List;
 @Accessors(chain = true)
 @EqualsAndHashCode(callSuper = true)
 public class RepairApplicationFormDTO extends BaseDTO implements Serializable {
-    private String refuseRemark;//拒单理由
+    private String refuseRemark;// 拒单理由
 
     /**
-     * 1:查询报修单,2:查询维修单
+     * 1:查询报修单,2:查询维修单,3:查询审批单
      */
     @Transient
     private Integer searchType;
     /**
+     * 是否为审批查询(仅显示待审批状态的工单)
+     */
+    @Transient
+    private Boolean approvalFlag;
+    /**
      * 多长时间修复好
      */
     private Double limitHours;
@@ -111,6 +116,11 @@ public class RepairApplicationFormDTO extends BaseDTO implements Serializable {
      */
     private Integer status;
     /**
+     * 查询列表:报修状态列表
+     */
+    @Transient
+    private List<Integer> statusList;
+    /**
      * 备注
      */
     private String remark;

+ 12 - 16
platform-dao/src/main/java/com/platform/dao/util/CustomExcelImportUtil.java

@@ -1008,14 +1008,12 @@ public class CustomExcelImportUtil {
                 }
                 record.setStatus(getSbInfoStatus(getCellValue(row.getCell(18))));
                 record.setRemark(getCellValue(row.getCell(19)));
-                // 主子设备和是否大屏显示
-                if (row.getPhysicalNumberOfCells() > 20) {
-                    record.setIsChild(getChild(getCellValue(row.getCell(20)).trim()));
-                    record.setIsShow(getShow(getCellValue(row.getCell(21)).trim()));
-                    record.setPositionId(getCellValue(row.getCell(22)).trim());
-                } else {
-                    record.setIsChild(SbInfoChildEnum.IS_NORMAL.getValue());
-                    record.setIsShow(SbInfoShowEnum.NOT_SHOW.getValue());
+                // 主子设备和是否大屏显示、所属车间(直接读取,不判断物理非空单元格数)
+                record.setIsChild(getChild(getCellValue(row.getCell(20))));
+                record.setIsShow(getShow(getCellValue(row.getCell(21))));
+                String posName = getCellValue(row.getCell(22));
+                if (StringUtils.isNotBlank(posName)) {
+                    record.setPositionName(posName.trim());
                 }
 
             } catch (Exception e) {
@@ -1751,14 +1749,12 @@ public class CustomExcelImportUtil {
                 }
                 record.setStatus(getSbInfoStatus(getCellValue(row.getCell(18))));
                 record.setRemark(getCellValue(row.getCell(19)));
-                // 主子设备和是否大屏显示
-                if (row.getPhysicalNumberOfCells() > 20) {
-                    record.setIsChild(getChild(getCellValue(row.getCell(20)).trim()));
-                    record.setIsShow(getShow(getCellValue(row.getCell(21)).trim()));
-                    record.setPositionName(getCellValue(row.getCell(22)).trim());
-                } else {
-                    record.setIsChild(SbInfoChildEnum.IS_NORMAL.getValue());
-                    record.setIsShow(SbInfoShowEnum.NOT_SHOW.getValue());
+                // 主子设备和是否大屏显示、所属车间(直接读取,不判断物理非空单元格数)
+                record.setIsChild(getChild(getCellValue(row.getCell(20))));
+                record.setIsShow(getShow(getCellValue(row.getCell(21))));
+                String posName = getCellValue(row.getCell(22));
+                if (StringUtils.isNotBlank(posName)) {
+                    record.setPositionName(posName.trim());
                 }
             } catch (Exception e) {
                 log.error("导入第" + i + "行异常", e);

+ 6 - 3
platform-dao/src/main/resources/mapper/repair/RepairApplicationFormMapper.xml

@@ -84,6 +84,12 @@
             <if test="status != null and status != ''">
                 and application.status = #{status}
             </if>
+            <if test="statusList != null and statusList.size > 0">
+                and application.status in
+                <foreach item="item" index="index" collection="statusList" open="(" close=")" separator=",">
+                    #{item}
+                </foreach>
+            </if>
             <if test="actualUser != null and actualUser != ''">
                 and application.actual_user = #{actualUser}
             </if>
@@ -96,9 +102,6 @@
             <if test="type != null and type != ''">
                 and application.type = #{type}
             </if>
-            <if test="status != null and status != ''">
-                and application.status = #{status}
-            </if>
             <if test="userId != null and userId != ''">
                 and application.user_id = #{userId}
             </if>

+ 4 - 1
platform-rest/src/main/java/com/platform/rest/config/security/handler/AccessDeniedExceptionHandler.java

@@ -2,6 +2,7 @@ package com.platform.rest.config.security.handler;
 
 import com.platform.common.exception.DeniedException;
 import com.platform.common.util.R;
+import cn.hutool.core.util.StrUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.access.AccessDeniedException;
@@ -31,7 +32,9 @@ public class AccessDeniedExceptionHandler {
     @ResponseStatus(HttpStatus.FORBIDDEN)
     public R exception(AccessDeniedException e) {
         log.error("权限异常信息,可忽略 ex={}", e.getMessage(), e);
-        return new R<>(new DeniedException("没有权限,禁止访问"));
+        // 透出具体缺少的权限信息,便于定位权限配置问题
+        String message = StrUtil.isNotBlank(e.getMessage()) ? e.getMessage() : "没有权限,禁止访问";
+        return new R<>(new DeniedException(message));
     }
 
 }

+ 5 - 2
platform-rest/src/main/java/com/platform/rest/config/security/handler/AccessDeniedHandler.java

@@ -1,5 +1,6 @@
 package com.platform.rest.config.security.handler;
 
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HttpStatus;
 import com.platform.common.constant.CommonConstants;
 import com.platform.common.exception.DeniedException;
@@ -38,10 +39,12 @@ public class AccessDeniedHandler extends OAuth2AccessDeniedHandler {
     @Override
     @SneakyThrows
     public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException authException) {
-        log.info("授权失败,禁止访问 {}", request.getRequestURI());
+        log.info("授权失败,禁止访问 {},原因:{}", request.getRequestURI(), authException.getMessage());
         response.setCharacterEncoding(CommonConstants.UTF8);
         response.setContentType(CommonConstants.CONTENT_TYPE);
-        R<String> result = new R<>(new DeniedException("授权失败,禁止访问"));
+        // 透出具体缺少的权限信息,便于定位权限配置问题
+        String message = StrUtil.isNotBlank(authException.getMessage()) ? authException.getMessage() : "授权失败,禁止访问";
+        R<String> result = new R<>(new DeniedException(message));
         response.setStatus(HttpStatus.HTTP_FORBIDDEN);
         PrintWriter printWriter = response.getWriter();
         printWriter.append(objectMapper.writeValueAsString(result));

+ 28 - 0
platform-rest/src/main/java/com/platform/rest/controller/check/CheckRequirementController.java

@@ -13,10 +13,15 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.util.FileCopyUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.servlet.http.HttpServletResponse;
 import javax.validation.constraints.NotBlank;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.net.URLEncoder;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -213,6 +218,29 @@ public class CheckRequirementController {
     }
 
     /**
+     * 保养项导入模板下载
+     *
+     * @param response 输出对象
+     */
+    @SysLog("保养项导入模板下载")
+    @GetMapping("/import/template")
+    @ApiOperation(value = "保养项导入模板下载", notes = "下载保养项导入模板 Excel 文件")
+    public void downloadImportTemplate(HttpServletResponse response) throws Exception {
+        response.setCharacterEncoding("UTF-8");
+        // 设置contentType为excel格式
+        response.setContentType("application/vnd.ms-excel;charset=utf-8");
+        response.setHeader("Content-Disposition",
+                "attachment;filename=" + URLEncoder.encode("保养项导入模板.xlsx", "UTF-8"));
+        try (InputStream in = getClass().getClassLoader().getResourceAsStream("templates/保养项导入模板.xlsx")) {
+            if (in == null) {
+                throw new FileNotFoundException("保养项导入模板文件不存在");
+            }
+            FileCopyUtils.copy(in, response.getOutputStream());
+            response.flushBuffer();
+        }
+    }
+
+    /**
      * 导入保养项(包括修改,新增,删除)
      *
      * @param

+ 25 - 0
platform-rest/src/main/java/com/platform/rest/controller/sb/SbInfoController.java

@@ -20,6 +20,7 @@ import com.platform.rest.log.annotation.SysLog;
 import com.platform.service.sb.SbInfoService;
 import lombok.AllArgsConstructor;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.util.FileCopyUtils;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
@@ -27,6 +28,8 @@ import org.springframework.web.multipart.MultipartFile;
 import javax.servlet.http.HttpServletResponse;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.net.URLEncoder;
 import java.util.Arrays;
 import java.util.List;
 
@@ -409,6 +412,28 @@ public class SbInfoController {
     }
 
     /**
+     * 设备导入模板下载
+     *
+     * @param response 输出对象
+     */
+    @SysLog("设备导入模板下载")
+    @GetMapping("/import/template")
+    public void downloadImportTemplate(HttpServletResponse response) throws Exception {
+        response.setCharacterEncoding("UTF-8");
+        // 设置contentType为excel格式
+        response.setContentType("application/vnd.ms-excel;charset=utf-8");
+        response.setHeader("Content-Disposition",
+                "attachment;filename=" + URLEncoder.encode("设备导入模板.xlsx", "UTF-8"));
+        try (InputStream in = getClass().getClassLoader().getResourceAsStream("templates/设备导入模板.xlsx")) {
+            if (in == null) {
+                throw new FileNotFoundException("设备导入模板文件不存在");
+            }
+            FileCopyUtils.copy(in, response.getOutputStream());
+            response.flushBuffer();
+        }
+    }
+
+    /**
      * 子设备数量
      *
      * @param sbInfoDTO

+ 26 - 0
platform-rest/src/main/java/com/platform/rest/controller/sqarepartmanage/SparePartInfoController.java

@@ -26,11 +26,15 @@ import com.platform.service.sb.SbModelSpareBomService;
 import com.platform.service.sqarepartmanage.SparePartInfoService;
 import lombok.AllArgsConstructor;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.util.FileCopyUtils;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletResponse;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.net.URLEncoder;
 import java.util.*;
 
 /**
@@ -349,6 +353,28 @@ public class SparePartInfoController {
     }
 
     /**
+     * 备件导入模板下载
+     *
+     * @param response 输出对象
+     */
+    @SysLog("备件导入模板下载")
+    @GetMapping("/import/template")
+    public void downloadImportTemplate(HttpServletResponse response) throws Exception {
+        response.setCharacterEncoding("UTF-8");
+        // 设置contentType为excel格式
+        response.setContentType("application/vnd.ms-excel;charset=utf-8");
+        response.setHeader("Content-Disposition",
+                "attachment;filename=" + URLEncoder.encode("备件导入模板.xlsx", "UTF-8"));
+        try (InputStream in = getClass().getClassLoader().getResourceAsStream("templates/备件导入模板.xlsx")) {
+            if (in == null) {
+                throw new FileNotFoundException("备件导入模板文件不存在");
+            }
+            FileCopyUtils.copy(in, response.getOutputStream());
+            response.flushBuffer();
+        }
+    }
+
+    /**
      * 根据设备的操作场景获取备件信息
      * 比如 维修,保养
      * @param pageNum 当前页码

BIN
platform-rest/src/main/resources/templates/保养项导入模板.xlsx


BIN
platform-rest/src/main/resources/templates/备件导入模板.xlsx


BIN
platform-rest/src/main/resources/templates/设备导入模板.xlsx


+ 16 - 10
platform-service/src/main/java/com/platform/service/repair/impl/RepairApplicationFormServiceImpl.java

@@ -264,19 +264,25 @@ public class RepairApplicationFormServiceImpl
                     record.setRepairUserId(userInfo.getUserId());
                 }
             }
-            // 获取验收人
+            // 审批查询:仅显示审核中和已驳回状态的工单
             if (record.getSearchType() == 3) {
-                if (record.getFilter() != null && DataFilterTypeEnum.SELF.getValue() == record.getFilter().intValue()) {
-                    record.setStatus(RepairApplicationFormStatusEnum.NOT_ACCEPTANCE.getValue());
-                    String verifyUser = ConfigCache
-                            .getLabelByValueAllowNull(SysConfigEnum.VERIFY_REPAIR_USER_LIST.name());
-                    UserInfo userInfo = SecurityUtils.getUserInfo();
-                    if (StringUtils.isNotBlank(verifyUser) && !verifyUser.contains(userInfo.getUsername())) {
-                        record.setCheckUserId(userInfo.getUserId());
-                    }
-                }
+                List<Integer> statusList = new ArrayList<>();
+                statusList.add(RepairApplicationFormStatusEnum.NOT_ACCEPTANCE.getValue());
+                statusList.add(RepairApplicationFormStatusEnum.REBACK.getValue());
+                record.setStatusList(statusList);
+                record.setStatus(null);
             }
         }
+
+        // 审批查询:仅显示审核中和已驳回状态的工单
+        if (record.getApprovalFlag() != null && record.getApprovalFlag()) {
+            List<Integer> statusList = new ArrayList<>();
+            statusList.add(RepairApplicationFormStatusEnum.NOT_ACCEPTANCE.getValue());
+            statusList.add(RepairApplicationFormStatusEnum.REBACK.getValue());
+            record.setStatusList(statusList);
+            record.setStatus(null);
+        }
+
         return new MyVOPage<>(mapper.selectPageList(record));
     }
 

+ 43 - 8
platform-service/src/main/java/com/platform/service/sb/impl/SbInfoServiceImpl.java

@@ -68,6 +68,7 @@ import com.platform.service.util.CodeFileUtils;
 import com.platform.service.util.ExecuteSql;
 import com.platform.service.util.SysFileUtils;
 import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.ibatis.exceptions.TooManyResultsException;
 import org.springframework.core.env.Environment;
 import org.springframework.stereotype.Service;
@@ -89,6 +90,7 @@ import java.util.stream.Collectors;
  * @Date 2020-04-21 21:05:46
  * @Version Copyright (c) 2019,北京乾元坤和科技有限公司 All rights reserved.
  */
+@Slf4j
 @AllArgsConstructor
 @Service("sbInfoService")
 public class SbInfoServiceImpl extends BaseServiceImpl<SbInfoMapper, SbInfo, SbInfoDTO> implements SbInfoService {
@@ -1636,6 +1638,7 @@ public class SbInfoServiceImpl extends BaseServiceImpl<SbInfoMapper, SbInfo, SbI
                 List<FirmProducer> producerList = new ArrayList<>(firmProducerMapper.selectAll());
                 // 用于缓存本次导入新增的生产商,避免同名重复新增
                 Map<String, FirmProducer> newProducerMap = new HashMap<>();
+                List<SbPosition> positionList = sbPositionMapper.selectAll();
                 LocalDateTime now = LocalDateTime.now();
                 for (SbInfoVO item : items) {
                     item.setUseArea(useArea);
@@ -1644,6 +1647,30 @@ public class SbInfoServiceImpl extends BaseServiceImpl<SbInfoMapper, SbInfo, SbI
                     item.setUseDept(useDept);
                     item.setUseGroup(useGroup);
                     item.setId(IdGeneratorUtils.getObjectId());
+
+                    // 处理所属车间:将车间名称转换为车间ID(匹配不到则留空,不报错)
+                    if (StringUtils.isNotBlank(item.getPositionName())) {
+                        log.info("导入第{}行,读取到车间名称:{}", item.getNo(), item.getPositionName());
+                        boolean findPosition = false;
+                        for (SbPosition position : positionList) {
+                            if (position.getName().equalsIgnoreCase(item.getPositionName().trim())) {
+                                item.setPositionId(position.getId());
+                                log.info("导入第{}行,匹配车间成功,车间ID:{}", item.getNo(), position.getId());
+                                findPosition = true;
+                                break;
+                            }
+                        }
+                        if (!findPosition) {
+                            log.warn("导入第{}行,系统找不到车间名称【{}】,车间ID留空", item.getNo(), item.getPositionName());
+                            item.setPositionId(null);
+                        }
+                    } else {
+                        log.warn("导入第{}行,车间名称为空,跳过车间匹配", item.getNo());
+                    }
+                    // 是否固定资产:默认为否
+                    if (item.getIsFinancing() == null) {
+                        item.setIsFinancing(SbInfoFinancingEnum.NOT.getValue());
+                    }
                     boolean findSaveUser = false;
                     if (StringUtils.isNotBlank(item.getSaveUserName())
                             && !"null".equalsIgnoreCase(item.getSaveUserName())) {
@@ -1773,16 +1800,24 @@ public class SbInfoServiceImpl extends BaseServiceImpl<SbInfoMapper, SbInfo, SbI
                     LocalDateTime now = LocalDateTime.now();
                     for (SbInfoVO item : items) {
 
-                        boolean findPosition = false;
-                        for (SbPosition position : positionList) {
-                            if (position.getName().equalsIgnoreCase(item.getPositionName())) {
-                                item.setPositionId(position.getId());
-                                findPosition = true;
-                                break;
+                        // 处理所属车间:将车间名称转换为车间ID(匹配不到则留空,不报错)
+                        if (StringUtils.isNotBlank(item.getPositionName())) {
+                            boolean findPosition = false;
+                            for (SbPosition position : positionList) {
+                                if (position.getName().equalsIgnoreCase(item.getPositionName().trim())) {
+                                    item.setPositionId(position.getId());
+                                    findPosition = true;
+                                    break;
+                                }
+                            }
+                            if (!findPosition) {
+                                log.warn("更新导入第{}行,系统找不到车间名称【{}】,车间ID留空", item.getNo(), item.getPositionName());
+                                item.setPositionId(null);
                             }
                         }
-                        if (!findPosition) {
-                            throw new BusinessException("系统找不到车间名称,请先添加车间, 车间名称" + item.getPositionName());
+                        // 是否固定资产:默认为否
+                        if (item.getIsFinancing() == null) {
+                            item.setIsFinancing(SbInfoFinancingEnum.NOT.getValue());
                         }
 
                         boolean findSaveUser = false;

+ 112 - 6
platform-service/src/main/java/com/platform/service/security/PermissionService.java

@@ -2,16 +2,29 @@ package com.platform.service.security;
 
 import cn.hutool.core.util.StrUtil;
 import com.platform.common.model.OauthUser;
+import com.platform.common.model.UserInfo;
 import com.platform.common.util.SecurityUtils;
+import com.platform.dao.entity.upms.SysMenu;
+import com.platform.dao.entity.upms.SysRole;
+import com.platform.dao.entity.upms.SysRoleMenu;
+import com.platform.dao.mapper.upms.SysMenuMapper;
+import com.platform.dao.mapper.upms.SysRoleMapper;
+import com.platform.dao.mapper.upms.SysRoleMenuMapper;
+import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
 import org.springframework.util.PatternMatchUtils;
 import org.springframework.util.StringUtils;
 
 import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+import tk.mybatis.mapper.entity.Example;
 
 /**
  * @Description 接口权限判断工具
@@ -21,7 +34,14 @@ import java.util.Collection;
  */
 @Slf4j
 @Component("pms")
+@AllArgsConstructor
 public class PermissionService {
+
+    private final SysMenuMapper sysMenuMapper;
+
+    private final SysRoleMenuMapper sysRoleMenuMapper;
+
+    private final SysRoleMapper sysRoleMapper;
     /**
      * 判断接口是否有xxx:xxx权限
      *
@@ -34,13 +54,94 @@ public class PermissionService {
         }
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
         if (authentication == null) {
-            return false;
+            throw new AccessDeniedException("用户未认证,无法访问,请先登录");
         }
         Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
-        return authorities.stream()
+        boolean has = authorities.stream()
                 .map(GrantedAuthority::getAuthority)
                 .filter(StringUtils::hasText)
                 .anyMatch(x -> PatternMatchUtils.simpleMatch(permission, x));
+        if (!has) {
+            // 提示具体缺少的权限标识、归属角色以及当前用户角色,便于定位权限配置问题
+            throw new AccessDeniedException(buildPermissionDeniedMessage(permission, authentication));
+        }
+        return true;
+    }
+
+    /**
+     * 拼装无权限提示信息:缺少的权限标识、该权限配置在哪些角色上、当前用户角色
+     *
+     * @param permission     缺少的权限标识
+     * @param authentication 当前登录认证信息
+     * @return 提示信息
+     */
+    private String buildPermissionDeniedMessage(String permission, Authentication authentication) {
+        StringBuilder message = new StringBuilder("缺少权限【").append(permission).append("】");
+        try {
+            // 反查该权限标识配置在哪些角色上,给管理员分配权限提供参考
+            String roleHint = queryRolesByPermission(permission);
+            if (StrUtil.isNotBlank(roleHint)) {
+                message.append(",该权限已配置在角色:").append(roleHint);
+            }
+            // 附带当前用户角色,便于确认该给哪个角色补充权限
+            String userRoleHint = currentUserRoleName(authentication);
+            if (StrUtil.isNotBlank(userRoleHint)) {
+                message.append(",当前用户角色:").append(userRoleHint);
+            }
+        } catch (Exception e) {
+            // 反查失败不影响主流程,仅提示缺少权限
+            log.warn("反查权限【{}】归属角色失败:{}", permission, e.getMessage());
+        }
+        message.append(",请联系管理员分配该权限");
+        return message.toString();
+    }
+
+    /**
+     * 反查指定权限标识配置在哪些角色上
+     *
+     * @param permission 权限标识
+     * @return 角色名称列表,逗号分隔;未配置时返回空
+     */
+    private String queryRolesByPermission(String permission) {
+        SysMenu menuQuery = new SysMenu();
+        menuQuery.setPermission(permission);
+        menuQuery.setDelFlag(0);
+        List<SysMenu> menus = sysMenuMapper.select(menuQuery);
+        if (CollectionUtils.isEmpty(menus)) {
+            return null;
+        }
+        List<String> menuIds = menus.stream().map(SysMenu::getMenuId).collect(Collectors.toList());
+        Example roleMenuExample = new Example(SysRoleMenu.class);
+        roleMenuExample.createCriteria().andIn("menuId", menuIds);
+        List<SysRoleMenu> roleMenus = sysRoleMenuMapper.selectByExample(roleMenuExample);
+        if (CollectionUtils.isEmpty(roleMenus)) {
+            return null;
+        }
+        List<String> roleIds = roleMenus.stream().map(SysRoleMenu::getRoleId).distinct().collect(Collectors.toList());
+        Example roleExample = new Example(SysRole.class);
+        roleExample.createCriteria().andIn("roleId", roleIds);
+        List<SysRole> roles = sysRoleMapper.selectByExample(roleExample);
+        if (CollectionUtils.isEmpty(roles)) {
+            return null;
+        }
+        return roles.stream().map(SysRole::getRoleName).distinct().collect(Collectors.joining("、"));
+    }
+
+    /**
+     * 获取当前登录用户的角色名称
+     *
+     * @param authentication 当前登录认证信息
+     * @return 角色名称,获取不到时返回空
+     */
+    private String currentUserRoleName(Authentication authentication) {
+        Object principal = authentication.getPrincipal();
+        if (principal instanceof OauthUser) {
+            UserInfo userInfo = ((OauthUser) principal).getUserInfo();
+            if (userInfo != null && StrUtil.isNotBlank(userInfo.getRoleName())) {
+                return userInfo.getRoleName();
+            }
+        }
+        return null;
     }
 
     /**
@@ -59,11 +160,16 @@ public class PermissionService {
         }
         try {
             String loginClientId = SecurityUtils.getClientId(authentication);
-            return clientId.equals(loginClientId);
+            boolean has = clientId.equals(loginClientId);
+            if (!has) {
+                // 提示具体缺少的客户端权限,便于定位权限配置问题
+                throw new AccessDeniedException("当前登录客户端【" + loginClientId + "】缺少权限【" + clientId + "】");
+            }
+            return true;
+        } catch (AccessDeniedException e) {
+            throw e;
         } catch (Exception e) {
-            return false;
+            throw new AccessDeniedException("缺少客户端权限【" + clientId + "】");
         }
-
-
     }
 }

BIN
设备导入模板.xlsx