feat(permission): 添加菜单数据权限功能

- 新增菜单数据规则表和角色菜单数据规则关联表
- 实现菜单数据权限切面和处理器
- 添加数据规则条件和变量枚举
- 实现变量替换工具类和规则构建逻辑
- 在权限分配中集成菜单数据规则关联功能
- 优化部门ID解析逻辑,支持从用户信息中获取默认部门
- 添加菜单组件查询方法和公司访问上下文拦截器改进
This commit is contained in:
wuzongyong
2026-01-28 09:13:23 +08:00
parent 6ea653ca43
commit 2227271d08
37 changed files with 2288 additions and 1 deletions

View File

@@ -4,12 +4,15 @@ import com.zt.plat.framework.datapermission.core.aop.CompanyDataPermissionIgnore
import com.zt.plat.framework.datapermission.core.aop.DataPermissionAnnotationAdvisor;
import com.zt.plat.framework.datapermission.core.aop.DeptDataPermissionIgnoreAspect;
import com.zt.plat.framework.datapermission.core.db.DataPermissionRuleHandler;
import com.zt.plat.framework.datapermission.core.menudatapermission.aop.MenuDataPermissionAspect;
import com.zt.plat.framework.datapermission.core.menudatapermission.handler.MenuDataPermissionHandler;
import com.zt.plat.framework.datapermission.core.rule.DataPermissionRule;
import com.zt.plat.framework.datapermission.core.rule.DataPermissionRuleFactory;
import com.zt.plat.framework.datapermission.core.rule.DataPermissionRuleFactoryImpl;
import com.zt.plat.framework.mybatis.core.util.MyBatisUtils;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
@@ -21,6 +24,7 @@ import java.util.List;
* @author ZT
*/
@AutoConfiguration
@MapperScan("com.zt.plat.framework.datapermission.core.menudatapermission.dal.mapper")
public class ZtDataPermissionAutoConfiguration {
@Bean
@@ -40,6 +44,21 @@ public class ZtDataPermissionAutoConfiguration {
return handler;
}
@Bean
public MenuDataPermissionHandler menuDataPermissionHandler(MybatisPlusInterceptor interceptor) {
// 创建菜单数据权限处理器
MenuDataPermissionHandler handler = new MenuDataPermissionHandler();
DataPermissionInterceptor inner = new DataPermissionInterceptor(handler);
// 添加到 interceptor 中,放在部门数据权限之后
MyBatisUtils.addInterceptor(interceptor, inner, 1);
return handler;
}
@Bean
public MenuDataPermissionAspect menuDataPermissionAspect() {
return new MenuDataPermissionAspect();
}
@Bean
public DataPermissionAnnotationAdvisor dataPermissionAnnotationAdvisor() {
return new DataPermissionAnnotationAdvisor();

View File

@@ -0,0 +1,29 @@
package com.zt.plat.framework.datapermission.core.menudatapermission.annotation;
import java.lang.annotation.*;
/**
* 数据权限注解
* 标注在Controller方法上表示该方法需要应用菜单数据规则
*
* 参考JeecgBoot的实现方式
*
* @author ZT
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PermissionData {
/**
* 页面组件路径
* 用于匹配菜单表中的component字段
* 例如system/role 对应角色管理菜单
*/
String pageComponent();
/**
* 是否启用
*/
boolean enable() default true;
}

View File

@@ -0,0 +1,89 @@
package com.zt.plat.framework.datapermission.core.menudatapermission.aop;
import cn.hutool.core.collection.CollUtil;
import com.zt.plat.framework.datapermission.core.menudatapermission.annotation.PermissionData;
import com.zt.plat.framework.datapermission.core.menudatapermission.context.MenuDataRuleContextHolder;
import com.zt.plat.framework.datapermission.core.menudatapermission.model.MenuDataRuleDTO;
import com.zt.plat.framework.datapermission.core.menudatapermission.service.MenuDataRuleLoader;
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.List;
/**
* 菜单数据权限切面
* 拦截 @PermissionData 注解的方法,加载并应用菜单数据规则
*
* @author ZT
*/
@Aspect
@Component
@Slf4j
public class MenuDataPermissionAspect {
@Resource
private MenuDataRuleLoader menuDataRuleLoader;
@Around("@annotation(com.zt.plat.framework.datapermission.core.menudatapermission.annotation.PermissionData)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
PermissionData annotation = method.getAnnotation(PermissionData.class);
// 如果未启用,直接执行
if (!annotation.enable()) {
return joinPoint.proceed();
}
try {
// 获取当前用户ID
Long userId = SecurityFrameworkUtils.getLoginUserId();
if (userId == null) {
log.debug("[MenuDataPermissionAspect][未登录,跳过菜单数据权限]");
return joinPoint.proceed();
}
// 从注解获取pageComponent
String pageComponent = annotation.pageComponent();
if (pageComponent == null || pageComponent.isEmpty()) {
log.warn("[MenuDataPermissionAspect][未指定pageComponent跳过菜单数据权限]");
return joinPoint.proceed();
}
// 根据pageComponent查询菜单ID
Long menuId = menuDataRuleLoader.getMenuIdByPageComponent(pageComponent);
if (menuId == null) {
log.warn("[MenuDataPermissionAspect][未找到匹配的菜单: {},跳过菜单数据权限]", pageComponent);
return joinPoint.proceed();
}
log.debug("[MenuDataPermissionAspect][pageComponent: {} 对应菜单ID: {}]", pageComponent, menuId);
// 加载用户的菜单数据规则
List<MenuDataRuleDTO> rules = menuDataRuleLoader.getUserMenuDataRules(userId, menuId);
if (CollUtil.isEmpty(rules)) {
log.debug("[MenuDataPermissionAspect][用户 {} 在菜单 {} 下无数据规则]", userId, menuId);
} else {
log.debug("[MenuDataPermissionAspect][用户 {} 在菜单 {} 下加载了 {} 条数据规则]",
userId, menuId, rules.size());
// 将规则存入 ThreadLocal
MenuDataRuleContextHolder.setRules(rules);
}
// 执行目标方法
return joinPoint.proceed();
} finally {
// 清理 ThreadLocal
MenuDataRuleContextHolder.clear();
}
}
}

View File

@@ -0,0 +1,36 @@
package com.zt.plat.framework.datapermission.core.menudatapermission.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
import com.zt.plat.framework.datapermission.core.menudatapermission.handler.MenuDataPermissionHandler;
import com.zt.plat.framework.mybatis.core.util.MyBatisUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* 菜单数据权限配置类
*
* @author ZT
*/
@Configuration
@ComponentScan("com.zt.plat.framework.datapermission.core.menudatapermission")
public class MenuDataPermissionConfiguration {
@Bean
@ConditionalOnBean(MybatisPlusInterceptor.class)
public MenuDataPermissionHandler menuDataPermissionHandler(MybatisPlusInterceptor interceptor) {
// 创建菜单数据权限处理器
MenuDataPermissionHandler handler = new MenuDataPermissionHandler();
// 创建 DataPermissionInterceptor 拦截器
DataPermissionInterceptor inner = new DataPermissionInterceptor(handler);
// 添加到 interceptor 中
// 添加在索引1的位置在部门数据权限之后但在分页插件之前
MyBatisUtils.addInterceptor(interceptor, inner, 1);
return handler;
}
}

View File

@@ -0,0 +1,41 @@
package com.zt.plat.framework.datapermission.core.menudatapermission.context;
import com.zt.plat.framework.datapermission.core.menudatapermission.model.MenuDataRuleDTO;
import java.util.List;
/**
* 菜单数据规则上下文持有者
* 使用 ThreadLocal 存储当前请求的菜单数据规则
*
* @author ZT
*/
public class MenuDataRuleContextHolder {
private static final ThreadLocal<List<MenuDataRuleDTO>> CONTEXT = new ThreadLocal<>();
/**
* 设置当前请求的菜单数据规则
*
* @param rules 规则列表
*/
public static void setRules(List<MenuDataRuleDTO> rules) {
CONTEXT.set(rules);
}
/**
* 获取当前请求的菜单数据规则
*
* @return 规则列表
*/
public static List<MenuDataRuleDTO> getRules() {
return CONTEXT.get();
}
/**
* 清除当前请求的菜单数据规则
*/
public static void clear() {
CONTEXT.remove();
}
}

View File

@@ -0,0 +1,51 @@
package com.zt.plat.framework.datapermission.core.menudatapermission.dal.mapper;
import com.zt.plat.framework.datapermission.core.menudatapermission.model.MenuDataRuleDTO;
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 菜单数据权限 Mapper
* 用于查询菜单和菜单数据规则
*
* @author ZT
*/
@Mapper
public interface MenuDataPermissionMapper {
/**
* 根据页面组件路径获取菜单ID
*
* @param component 页面组件路径system/role/index
* @return 菜单ID如果未找到返回null
*/
@Select("SELECT id FROM system_menu WHERE component = #{component} AND deleted = 0 LIMIT 1")
Long selectMenuIdByComponent(@Param("component") String component);
/**
* 获取用户在指定菜单下的有效数据规则
*
* @param userId 用户ID
* @param menuId 菜单ID
* @return 数据规则列表
*/
@TenantIgnore
@Select("<script>" +
"SELECT mdr.rule_column, mdr.rule_conditions, mdr.rule_value, mdr.status " +
"FROM system_menu_data_rule mdr " +
"INNER JOIN system_role_menu_data_rule rmdr ON mdr.id = rmdr.data_rule_id " +
"INNER JOIN system_user_role ur ON rmdr.role_id = ur.role_id " +
"WHERE mdr.menu_id = #{menuId} " +
"AND ur.user_id = #{userId} " +
"AND mdr.status = 1 " +
"AND mdr.deleted = 0 " +
"AND rmdr.deleted = 0 " +
"AND ur.deleted = 0" +
"</script>")
List<MenuDataRuleDTO> selectUserMenuDataRules(@Param("userId") Long userId,
@Param("menuId") Long menuId);
}

View File

@@ -0,0 +1,41 @@
package com.zt.plat.framework.datapermission.core.menudatapermission.handler;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;
import com.zt.plat.framework.datapermission.core.menudatapermission.util.MenuDataPermissionRule;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
/**
* 菜单数据权限处理器
* 基于 MyBatis Plus 的数据权限插件,应用菜单数据规则到 SQL 查询
*
* @author ZT
*/
@Slf4j
public class MenuDataPermissionHandler implements MultiDataPermissionHandler {
@Override
public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {
try {
// 从 ThreadLocal 获取菜单数据规则并构建 SQL 条件
String sqlCondition = MenuDataPermissionRule.buildSqlCondition();
if (StrUtil.isBlank(sqlCondition)) {
return null;
}
// 将 SQL 字符串解析为 Expression 对象
Expression expression = CCJSqlParserUtil.parseCondExpression(sqlCondition);
log.debug("[MenuDataPermissionHandler][表: {}, 添加条件: {}]", table.getName(), sqlCondition);
return expression;
} catch (JSQLParserException e) {
log.error("[MenuDataPermissionHandler][解析 SQL 条件失败]", e);
return null;
}
}
}

View File

@@ -0,0 +1,33 @@
package com.zt.plat.framework.datapermission.core.menudatapermission.model;
import lombok.Data;
/**
* 菜单数据规则 DTO
* 用于在框架层传递菜单数据规则信息,避免依赖业务模块的数据库实体
*
* @author ZT
*/
@Data
public class MenuDataRuleDTO {
/**
* 规则字段(数据库列名)
*/
private String ruleColumn;
/**
* 规则条件(=、>、<、IN、LIKE等
*/
private String ruleConditions;
/**
* 规则值(支持变量如#{userId}、#{deptId}
*/
private String ruleValue;
/**
* 状态0=禁用 1=启用)
*/
private Integer status;
}

View File

@@ -0,0 +1,31 @@
package com.zt.plat.framework.datapermission.core.menudatapermission.service;
import com.zt.plat.framework.datapermission.core.menudatapermission.model.MenuDataRuleDTO;
import java.util.List;
/**
* 菜单数据规则加载器接口
* 负责加载菜单和菜单数据规则
*
* @author ZT
*/
public interface MenuDataRuleLoader {
/**
* 根据页面组件路径获取菜单ID
*
* @param pageComponent 页面组件路径system/role/index
* @return 菜单ID如果未找到返回null
*/
Long getMenuIdByPageComponent(String pageComponent);
/**
* 获取用户在指定菜单下的数据规则
*
* @param userId 用户ID
* @param menuId 菜单ID
* @return 数据规则列表
*/
List<MenuDataRuleDTO> getUserMenuDataRules(Long userId, Long menuId);
}

View File

@@ -0,0 +1,45 @@
package com.zt.plat.framework.datapermission.core.menudatapermission.service.impl;
import com.zt.plat.framework.datapermission.core.menudatapermission.dal.mapper.MenuDataPermissionMapper;
import com.zt.plat.framework.datapermission.core.menudatapermission.model.MenuDataRuleDTO;
import com.zt.plat.framework.datapermission.core.menudatapermission.service.MenuDataRuleLoader;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 菜单数据规则加载器默认实现
* 直接从数据库加载菜单数据规则
*
* @author ZT
*/
@Component
@Slf4j
public class MenuDataRuleLoaderImpl implements MenuDataRuleLoader {
@Resource
private MenuDataPermissionMapper menuDataPermissionMapper;
@Override
public Long getMenuIdByPageComponent(String pageComponent) {
try {
return menuDataPermissionMapper.selectMenuIdByComponent(pageComponent);
} catch (Exception e) {
log.error("[MenuDataRuleLoaderImpl][根据pageComponent查询菜单ID失败: {}]", pageComponent, e);
return null;
}
}
@Override
public List<MenuDataRuleDTO> getUserMenuDataRules(Long userId, Long menuId) {
try {
return menuDataPermissionMapper.selectUserMenuDataRules(userId, menuId);
} catch (Exception e) {
log.error("[MenuDataRuleLoaderImpl][查询用户菜单数据规则失败: userId={}, menuId={}]",
userId, menuId, e);
return List.of();
}
}
}

View File

@@ -0,0 +1,92 @@
package com.zt.plat.framework.datapermission.core.menudatapermission.util;
import cn.hutool.core.util.StrUtil;
import com.zt.plat.framework.security.core.LoginUser;
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 数据规则变量替换工具类
*
* @author ZT
*/
@Slf4j
public class DataRuleVariableUtils {
private static final Pattern VARIABLE_PATTERN = Pattern.compile("#\\{([^}]+)}");
/**
* 替换规则值中的变量
*
* @param ruleValue 规则值,如 "#{userId}" 或 "#{deptId}"
* @return 替换后的值
*/
public static String replaceVariables(String ruleValue) {
if (StrUtil.isBlank(ruleValue)) {
return ruleValue;
}
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
if (loginUser == null) {
return ruleValue;
}
Matcher matcher = VARIABLE_PATTERN.matcher(ruleValue);
StringBuffer result = new StringBuffer();
while (matcher.find()) {
String variable = matcher.group(1);
String replacement = getVariableValue(variable, loginUser);
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
}
matcher.appendTail(result);
return result.toString();
}
/**
* 获取变量对应的值
*
* @param variable 变量名,如 "userId", "deptId"
* @param loginUser 当前登录用户
* @return 变量值
*/
private static String getVariableValue(String variable, LoginUser loginUser) {
if (loginUser == null) {
return "";
}
switch (variable) {
case "userId":
return loginUser.getId() != null ? loginUser.getId().toString() : "";
case "username":
return loginUser.getInfo() != null ?
loginUser.getInfo().getOrDefault(LoginUser.INFO_KEY_USERNAME, "") : "";
case "deptId":
return loginUser.getVisitDeptId() != null ?
loginUser.getVisitDeptId().toString() : "";
case "companyId":
return loginUser.getVisitCompanyId() != null ?
loginUser.getVisitCompanyId().toString() : "";
case "tenantId":
return loginUser.getTenantId() != null ?
loginUser.getTenantId().toString() : "";
case "deptIds":
return loginUser.getInfo() != null ?
loginUser.getInfo().getOrDefault(LoginUser.INFO_KEY_DEPT_IDS, "") : "";
case "companyIds":
return loginUser.getInfo() != null ?
loginUser.getInfo().getOrDefault(LoginUser.INFO_KEY_COMPANY_IDS, "") : "";
case "postIds":
return loginUser.getInfo() != null ?
loginUser.getInfo().getOrDefault(LoginUser.INFO_KEY_POST_IDS, "") : "";
default:
// 未知变量,记录警告并返回空字符串
log.warn("[DataRuleVariableUtils][未知的变量: {},请检查数据规则配置]", variable);
return "";
}
}
}

View File

@@ -0,0 +1,156 @@
package com.zt.plat.framework.datapermission.core.menudatapermission.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.zt.plat.framework.datapermission.core.menudatapermission.context.MenuDataRuleContextHolder;
import com.zt.plat.framework.datapermission.core.menudatapermission.model.MenuDataRuleDTO;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.stream.Collectors;
/**
* 菜单数据权限规则
* 用于构建 SQL WHERE 条件
*
* @author ZT
*/
@Slf4j
public class MenuDataPermissionRule {
/**
* 构建 SQL WHERE 条件
*
* @return SQL 条件字符串,如 "dept_id = 1 AND status = 1"
*/
public static String buildSqlCondition() {
List<MenuDataRuleDTO> rules = MenuDataRuleContextHolder.getRules();
if (CollUtil.isEmpty(rules)) {
return null;
}
List<String> conditions = rules.stream()
.filter(rule -> rule.getStatus() == 1) // 只处理启用的规则
.map(MenuDataPermissionRule::buildSingleCondition)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toList());
if (CollUtil.isEmpty(conditions)) {
return null;
}
// 多个规则用 AND 连接
return "(" + String.join(" AND ", conditions) + ")";
}
/**
* 构建单个规则的 SQL 条件
*
* @param rule 规则
* @return SQL 条件字符串
*/
private static String buildSingleCondition(MenuDataRuleDTO rule) {
String ruleColumn = rule.getRuleColumn();
String ruleConditions = rule.getRuleConditions();
String ruleValue = rule.getRuleValue();
// 替换变量
String actualValue = DataRuleVariableUtils.replaceVariables(ruleValue);
if (StrUtil.isBlank(ruleColumn) || StrUtil.isBlank(ruleConditions)) {
return null;
}
// 处理 SQL_RULE 类型(自定义 SQL
if ("SQL_RULE".equals(ruleConditions)) {
return actualValue;
}
// 处理 IS_NULL 和 IS_NOT_NULL
if ("IS_NULL".equals(ruleConditions)) {
return ruleColumn + " IS NULL";
}
if ("IS_NOT_NULL".equals(ruleConditions)) {
return ruleColumn + " IS NOT NULL";
}
// 其他条件需要有值
if (StrUtil.isBlank(actualValue)) {
return null;
}
// 构建条件
switch (ruleConditions) {
case "=":
return ruleColumn + " = " + formatValue(actualValue);
case "!=":
return ruleColumn + " != " + formatValue(actualValue);
case ">":
return ruleColumn + " > " + formatValue(actualValue);
case "<":
return ruleColumn + " < " + formatValue(actualValue);
case ">=":
return ruleColumn + " >= " + formatValue(actualValue);
case "<=":
return ruleColumn + " <= " + formatValue(actualValue);
case "IN":
return ruleColumn + " IN (" + formatInValues(actualValue) + ")";
case "NOT_IN":
return ruleColumn + " NOT IN (" + formatInValues(actualValue) + ")";
case "LIKE":
return ruleColumn + " LIKE '%" + escapeSql(actualValue) + "%'";
case "NOT_LIKE":
return ruleColumn + " NOT LIKE '%" + escapeSql(actualValue) + "%'";
case "BETWEEN":
return buildBetweenCondition(ruleColumn, actualValue);
case "NOT_BETWEEN":
return "NOT " + buildBetweenCondition(ruleColumn, actualValue);
default:
log.warn("[buildSingleCondition][未知的规则条件: {}]", ruleConditions);
return null;
}
}
/**
* 格式化值(添加引号)
* 统一给所有值加引号,数据库会自动处理类型转换
*/
private static String formatValue(String value) {
// 统一添加单引号,避免达梦数据库等对字符串类型字段的类型转换错误
return "'" + escapeSql(value) + "'";
}
/**
* 格式化 IN 条件的值
*/
private static String formatInValues(String value) {
String[] values = value.split(",");
return java.util.Arrays.stream(values)
.map(String::trim)
.filter(StrUtil::isNotBlank)
.map(MenuDataPermissionRule::formatValue)
.collect(Collectors.joining(", "));
}
/**
* 构建 BETWEEN 条件
*/
private static String buildBetweenCondition(String column, String value) {
String[] values = value.split(",");
if (values.length != 2) {
log.warn("[buildBetweenCondition][BETWEEN 条件需要两个值,用逗号分隔: {}]", value);
return null;
}
return column + " BETWEEN " + formatValue(values[0].trim()) + " AND " + formatValue(values[1].trim());
}
/**
* SQL 转义,防止 SQL 注入
*/
private static String escapeSql(String value) {
if (StrUtil.isBlank(value)) {
return value;
}
return value.replace("'", "''");
}
}

View File

@@ -2,3 +2,4 @@ com.zt.plat.framework.datapermission.config.ZtDataPermissionAutoConfiguration
com.zt.plat.framework.datapermission.config.ZtDeptDataPermissionAutoConfiguration
com.zt.plat.framework.datapermission.config.ZtBusinessDataPermissionAutoConfiguration
com.zt.plat.framework.datapermission.config.ZtDataPermissionRpcAutoConfiguration
com.zt.plat.framework.datapermission.core.menudatapermission.config.MenuDataPermissionConfiguration

View File

@@ -11,6 +11,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.Map;
/**
* @author chenbowen
*/
@@ -45,13 +47,31 @@ public class CompanyVisitContextInterceptor implements HandlerInterceptor {
}
Long deptId = WebFrameworkUtils.getDeptId(request);
// 部门信息同样遵循请求头 -> 请求属性 -> 登录缓存的回退顺序
// 部门信息同样遵循"请求头 -> 请求属性 -> 登录缓存"的回退顺序
if (deptId == null || deptId <= 0L) {
Long attrDeptId = resolveLong(request.getAttribute(WebFrameworkUtils.HEADER_VISIT_DEPT_ID));
if (attrDeptId != null && attrDeptId > 0L) {
deptId = attrDeptId;
} else if (loginUser != null && loginUser.getVisitDeptId() != null && loginUser.getVisitDeptId() > 0L) {
deptId = loginUser.getVisitDeptId();
} else if (loginUser != null) {
// 如果以上都没有尝试从用户info中获取第一个部门作为默认值
Map<String, String> info = loginUser.getInfo();
if (info != null) {
String deptIdsStr = info.get(LoginUser.INFO_KEY_DEPT_IDS);
if (deptIdsStr != null && !deptIdsStr.isEmpty() && !"[]".equals(deptIdsStr)) {
try {
// 解析JSON数组取第一个部门ID
deptIdsStr = deptIdsStr.trim();
if (deptIdsStr.startsWith("[") && deptIdsStr.length() > 2) {
String firstId = deptIdsStr.substring(1, deptIdsStr.indexOf(']')).split(",")[0].trim();
deptId = Long.parseLong(firstId);
}
} catch (Exception e) {
log.warn("[CompanyVisitContextInterceptor][解析用户默认部门失败: {}]", deptIdsStr, e);
}
}
}
}
}