在芋道自定义数据权限
- 一、前言
- 二、查询文档
- 三、过程
-
- 1、确定数据权限字段
- [2、仿写 PermissionService](#2、仿写 PermissionService)
- [3、仿写 DeptDataPermissionRuleCustomizer 和 DeptDataPermissionRule](#3、仿写 DeptDataPermissionRuleCustomizer 和 DeptDataPermissionRule)
- [4、仿写 YudaoDeptDataPermissionAutoConfiguration](#4、仿写 YudaoDeptDataPermissionAutoConfiguration)
- 5、增加一下引用配置
- 6、打开日志文件打印
- 7、配置数据权限
- 8、测试
一、前言
最近在研究芋道的自定义数据权限规则,他的文档上写的又相对简单,折腾了好久,这里记录一下。
芋道版本
二、查询文档
可到这里查看芋道的数据权限文档,因为要加入芋道的社区,不是所以人都可以看,这里把图截下来了

三、过程
1、确定数据权限字段
确定自己要做数据权限的字段,这里通过 ent_id (企业id)和 receive_ent_id (接单企业id),用来筛选后面运单表(Waybill)数据,可在数据权限范围枚举类中新增

完整代码如下:
java
package cn.iocoder.yudao.module.system.enums.permission;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 数据范围枚举类
*
* 用于实现数据级别的权限
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum DataScopeEnum implements ArrayValuable<Integer> {
ALL(1), // 全部数据权限
DEPT_CUSTOM(2), // 指定部门数据权限
DEPT_ONLY(3), // 部门数据权限
DEPT_AND_CHILD(4), // 部门及以下数据权限
SELF(5), // 仅本人数据权限
ENT_CUSTOM(6), // 指定企业数据权限
ENT_ONLY(7); // 企业数据权限
/**
* 范围
*/
private final Integer scope;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DataScopeEnum::getScope).toArray(Integer[]::new);
@Override
public Integer[] array() {
return ARRAYS;
}
}
2、仿写 PermissionService
然后在 PermissionService 这里仿照芋道的部门权限方法,写一个运单的数据权限方法

在写方法体之前先仿照芋道 DeptDataPermissionRespDTO 写一个数据权限 WaybillDataPermissionRespDTO

仿写 WaybillDataPermissionRespDTO 代码如下:
java
package cn.iocoder.yudao.framework.common.biz.waybill.permission.dto;
import lombok.Data;
import java.util.HashSet;
import java.util.Set;
/**
* 运单的数据权限 Response DTO
*
* @author: gan
* @date: 2026-03-03 10:03
*/
@Data
public class WaybillDataPermissionRespDTO {
/**
* 是否可查看全部数据
*/
private Boolean all;
/**
* 是否可查看自己的数据
*/
private Boolean self;
/**
* 可查看的租户数组
*/
private Set<Long> tenantIds;
/**
* 可查看的企业数组
*/
private Set<Long> entIds;
public WaybillDataPermissionRespDTO() {
this.all = false;
this.self = false;
this.tenantIds = new HashSet<>();
this.entIds = new HashSet<>();
}
}
接口代码如下:
java
package cn.iocoder.yudao.module.system.service.permission;
import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.framework.common.biz.waybill.permission.dto.WaybillDataPermissionRespDTO;
import java.util.Collection;
import java.util.Set;
import static java.util.Collections.singleton;
/**
* 权限 Service 接口
* <p>
* 提供用户-角色、角色-菜单、角色-部门的关联权限处理
*
* @author 芋道源码
*/
public interface PermissionService {
/**
* 获得登陆用户的部门数据权限
*
* @param userId 用户编号
* @return 部门数据权限
*/
DeptDataPermissionRespDTO getDeptDataPermission(Long userId);
/**
* 获得登陆用户的运单数据权限
*
* @param userId 用户编号
* @return 部门数据权限
*/
WaybillDataPermissionRespDTO getWaybillDataPermission(Long userId);
}
接口实现如下:
java
package cn.iocoder.yudao.module.system.service.permission;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.biz.waybill.permission.dto.WaybillDataPermissionRespDTO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Suppliers;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.*;
import java.util.function.Supplier;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
/**
* 权限 Service 实现类
*
* @author 芋道源码
*/
@Service
@Slf4j
public class PermissionServiceImpl implements PermissionService {
@Resource
private RoleMenuMapper roleMenuMapper;
@Resource
private UserRoleMapper userRoleMapper;
@Resource
private RoleService roleService;
@Resource
private MenuService menuService;
@Resource
private DeptService deptService;
@Resource
private AdminUserService userService;
@Override
@DataPermission(enable = false) // 关闭数据权限,不然就会出现递归获取数据权限的问题
public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) {
// 获得用户的角色
List<RoleDO> roles = getEnableUserRoleListByUserIdFromCache(userId);
// 如果角色为空,则只能查看自己
DeptDataPermissionRespDTO result = new DeptDataPermissionRespDTO();
if (CollUtil.isEmpty(roles)) {
result.setSelf(true);
return result;
}
// 获得用户的部门编号的缓存,通过 Guava 的 Suppliers 惰性求值,即有且仅有第一次发起 DB 的查询
Supplier<Long> userDeptId = Suppliers.memoize(() -> userService.getUser(userId).getDeptId());
// 遍历每个角色,计算
for (RoleDO role : roles) {
// 为空时,跳过
if (role.getDataScope() == null) {
continue;
}
// 情况一,ALL
if (Objects.equals(role.getDataScope(), DataScopeEnum.ALL.getScope())) {
result.setAll(true);
continue;
}
// 情况二,DEPT_CUSTOM
if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_CUSTOM.getScope())) {
CollUtil.addAll(result.getDeptIds(), role.getDataScopeDeptIds());
// 自定义可见部门时,保证可以看到自己所在的部门。否则,一些场景下可能会有问题。
// 例如说,登录时,基于 t_user 的 username 查询会可能被 dept_id 过滤掉
CollUtil.addAll(result.getDeptIds(), userDeptId.get());
continue;
}
// 情况三,DEPT_ONLY
if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_ONLY.getScope())) {
CollectionUtils.addIfNotNull(result.getDeptIds(), userDeptId.get());
continue;
}
// 情况四,DEPT_DEPT_AND_CHILD
if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_AND_CHILD.getScope())) {
CollUtil.addAll(result.getDeptIds(), deptService.getChildDeptIdListFromCache(userDeptId.get()));
// 添加本身部门编号
CollUtil.addAll(result.getDeptIds(), userDeptId.get());
continue;
}
// 情况五,SELF
if (Objects.equals(role.getDataScope(), DataScopeEnum.SELF.getScope())) {
result.setSelf(true);
continue;
}
// 未知情况,error log 即可
log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", userId, toJsonString(result));
}
return result;
}
/**
* 获得自身的代理对象,解决 AOP 生效问题
*
* @return 自己
*/
private PermissionServiceImpl getSelf() {
return SpringUtil.getBean(getClass());
}
@Override
@DataPermission(enable = false) // 关闭数据权限,不然就会出现递归获取数据权限的问题
public WaybillDataPermissionRespDTO getWaybillDataPermission(Long userId) {
// 获得用户的角色
List<RoleDO> roles = getEnableUserRoleListByUserIdFromCache(userId);
// 如果角色为空,则只能查看自己
WaybillDataPermissionRespDTO result = new WaybillDataPermissionRespDTO();
if (CollUtil.isEmpty(roles)) {
result.setSelf(true);
return result;
}
// 获得用户的部门编号的缓存,通过 Guava 的 Suppliers 惰性求值,即有且仅有第一次发起 DB 的查询
AdminUserDO adminUserDO = userService.getUser(userId);
//Supplier<Long> userTenantId = Suppliers.memoize(() -> adminUserDO.getTenantId()); //租户信息可以不要了,继承了BaseDO会拼接
Supplier<Long> userEntId = Suppliers.memoize(() -> adminUserDO.getEntId());
// 遍历每个角色,计算
for (RoleDO role : roles) {
// 为空时,跳过
if (role.getDataScope() == null) {
continue;
}
// 情况一,ALL
if (Objects.equals(role.getDataScope(), DataScopeEnum.ALL.getScope())) {
result.setAll(true);
continue;
}
// 情况二,ENT_CUSTOM
if (Objects.equals(role.getDataScope(), DataScopeEnum.ENT_CUSTOM.getScope())) {
CollUtil.addAll(result.getEntIds(), role.getDataScopeEntIds());
// 自定义可见部门时,保证可以看到自己所在的部门。否则,一些场景下可能会有问题。
// 例如说,登录时,基于 t_user 的 username 查询会可能被 dept_id 过滤掉
CollUtil.addAll(result.getEntIds(), userEntId.get());
continue;
}
// 情况三,ENT_ONLY
if (Objects.equals(role.getDataScope(), DataScopeEnum.ENT_ONLY.getScope())) {
CollectionUtils.addIfNotNull(result.getEntIds(), userEntId.get());
continue;
}
// 情况四,SELF
if (Objects.equals(role.getDataScope(), DataScopeEnum.SELF.getScope())) {
result.setSelf(true);
continue;
}
// 未知情况,error log 即可
log.error("[getWaybillDataPermission][LoginUser({}) role({}) 无法处理]", userId, toJsonString(result));
}
return result;
}
}
3、仿写 DeptDataPermissionRuleCustomizer 和 DeptDataPermissionRule
接着仿照芋道的部门表写一个 DataPermissionRuleCustomizer 和 DataPermissionRule ,这里是 Waybill 运单表,写成了 WaybillDataPermissionRuleCustomizer 和 WaybillDataPermissionRule

WaybillDataPermissionRuleCustomizer 代码如下:
java
package cn.iocoder.yudao.framework.datapermission.core.rule.waybill;
import cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataPermissionRule;
/**
* {@link DeptDataPermissionRule} 的自定义配置接口
*
* @author: gan
* @date: 2026-03-06 09:19
*/
@FunctionalInterface
public interface WaybillDataPermissionRuleCustomizer {
/**
* 自定义该权限规则
* 1. 调用 {@link WaybillDataPermissionRule#addEntColumn(Class)} (Class, Long)} 方法,配置基于 ent_id 的过滤规则
* 2. 调用 {@link WaybillDataPermissionRule#addUserColumn(Class, String)} 方法,配置基于 user_id 的过滤规则
*
* @param rule 权限规则
*/
void customize(WaybillDataPermissionRule rule);
}
WaybillDataPermissionRuleCustomizer 的实现类代码如下,其中 WaybillDO 是我的业务表,里面包含要数据权限需要的字段 ent_id 和 receive_ent_id
java
package cn.iocoder.yudao.module.waybill.framework.datapermission.config;
import cn.iocoder.yudao.framework.datapermission.core.rule.waybill.WaybillDataPermissionRuleCustomizer;
import cn.iocoder.yudao.module.waybill.dal.dataobject.waybill.WaybillDO;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* waybill 模块的数据权限 Configuration
*
* @author: gan
* @date: 2026-03-06 09:25
*/
@Configuration(value = "waybillDataPermissionConfiguration", proxyBeanMethods = false)
public class DataPermissionConfiguration {
@Bean
public WaybillDataPermissionRuleCustomizer sysWaybillDataPermissionRuleCustomizer() {
return rule -> {
// waybill
rule.addEntColumn(WaybillDO.class);
};
}
}
WaybillDataPermissionRule 代码如下:
java
package cn.iocoder.yudao.framework.datapermission.core.rule.waybill;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;
import cn.iocoder.yudao.framework.common.biz.waybill.permission.dto.WaybillDataPermissionRespDTO;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import org.springframework.stereotype.Component;
import java.util.*;
/**
* 基于运单的DataPermissionRule
*
* @author: gan
* @date: 2026-03-03 09:16
*/
@AllArgsConstructor
@Slf4j
@Component
public class WaybillDataPermissionRule implements DataPermissionRule {
/**
* LoginUser 的 Context 缓存 Key
*/
protected static final String CONTEXT_KEY = WaybillDataPermissionRule.class.getSimpleName();
/**
* 基于运单的企业id字段配置
* 一般情况下,每个表的部门编号字段是 dept_id,通过该配置自定义。
*
* key:表名
* value:字段名
*/
private final Map<String, Set<String>> entColumns = new HashMap<>();
/**
* 基于运单的租户id字段配置
* 一般情况下,每个表的部门编号字段是 dept_id,通过该配置自定义。
*
* key:表名
* value:字段名
*/
private final Map<String, String> tenantColumns = new HashMap<>();
/**
* 基于用户的用户id字段配置
* 一般情况下,每个表的部门编号字段是 dept_id,通过该配置自定义。
*
* key:表名
* value:字段名
*/
private final Map<String, String> userColumns = new HashMap<>();
static final Expression EXPRESSION_NULL = new NullValue();
private final PermissionCommonApi permissionApi;
/**
* 所有表名,是 {@link #entColumns} 、{@link #tenantColumns} 和 {@link #userColumns} 的合集
*/
private final Set<String> TABLE_NAMES = new HashSet<>();
@Override
public Set<String> getTableNames() {
return TABLE_NAMES;
}
@Override
public Expression getExpression(String tableName, Alias tableAlias) {
// 只有有登陆用户的情况下,才进行数据权限的处理
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
if (loginUser == null) {
return null;
}
// 获得数据权限
WaybillDataPermissionRespDTO waybillDataPermission = loginUser.getContext(CONTEXT_KEY, WaybillDataPermissionRespDTO.class);
// 从上下文中拿不到,则调用逻辑进行获取
if (waybillDataPermission == null) {
waybillDataPermission = permissionApi.getWaybillDataPermission(loginUser.getId());
if (waybillDataPermission == null) {
log.error("[getExpression][LoginUser({}) 获取数据权限为 null]", JsonUtils.toJsonString(loginUser));
throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 未返回数据权限",
loginUser.getId(), tableName, tableAlias.getName()));
}
// 添加到上下文中,避免重复计算
loginUser.setContext(CONTEXT_KEY, waybillDataPermission);
}
// 情况一,如果是 ALL 可查看全部,则无需拼接条件
if (waybillDataPermission.getAll()) {
return null;
}
// 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限
if (CollUtil.isEmpty(waybillDataPermission.getEntIds())
&& Boolean.FALSE.equals(waybillDataPermission.getSelf())) {
return new EqualsTo(null, null); // WHERE null = null,可以保证返回的数据为空
}
// 情况三,拼接 企业 和 User 的条件,最后组合
Expression entExpression = buildEntIdExpression(tableName, tableAlias, waybillDataPermission.getEntIds());
//Expression userExpression = buildUserExpression(tableName, tableAlias, waybillDataPermission.getSelf(), loginUser.getId());
if (entExpression == null) {
// TODO 芋艿:获得不到条件的时候,暂时不抛出异常,而是不返回数据
log.warn("[getExpression][LoginUser({}) Table({}/{}) WaybillDataPermissionRule({}) 构建的条件为空]",
JsonUtils.toJsonString(loginUser), tableName, tableAlias, JsonUtils.toJsonString(entExpression));
// throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 构建的条件为空",
// loginUser.getId(), tableName, tableAlias.getName()));
return EXPRESSION_NULL;
}
// 目前,如果有指定部门 + 可查看自己,采用 OR 条件。即,WHERE (ent_id IN ? OR user_id = ?)
List<Expression> condList = Arrays.asList(entExpression);
return new ParenthesedExpressionList(buildMultiOrExpression(condList));
}
/**
* 构建企业的 in 规则
* @param tableName
* @param tableAlias
* @param entIds
* @return
*/
private Expression buildEntIdExpression(String tableName, Alias tableAlias, Set<Long> entIds) {
// 如果不存在配置,则无需作为条件
Set<String> columnNameSet = entColumns.get(tableName);
if (CollUtil.isEmpty(columnNameSet)) {
return null;
}
// 如果为空,则无条件
if (CollUtil.isEmpty(entIds)) {
return null;
}
// 拼接条件
List<Expression> expressions = new ArrayList<>(columnNameSet.size());
// Parenthesis 的目的,是提供 (1,2,3) 的 () 左右括号
ParenthesedExpressionList entParenthesedExpressionList = new ParenthesedExpressionList(new ExpressionList<LongValue>(CollectionUtils.convertList(entIds, LongValue::new)));
for (String columnName : columnNameSet) {
Expression item = new InExpression(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), entParenthesedExpressionList);
expressions.add(item);
}
return buildMultiOrExpression(expressions);
}
/**
* 构建用户的
* @param tableName
* @param tableAlias
* @param userId
* @return
*/
private Expression buildUserExpression(String tableName, Alias tableAlias, Boolean self, Long userId) {
// 如果不查看自己,则无需作为条件
if (Boolean.FALSE.equals(self)) {
return null;
}
String columnName = userColumns.get(tableName);
if (StrUtil.isEmpty(columnName)) {
return null;
}
// 拼接条件
return new EqualsTo(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), new LongValue(userId));
}
/**
* 通用工具方法:拼接任意数量的OR条件(支持2个及以上)
* @param expressions 条件列表(Expression类型)
* @return 拼接后的OR表达式
*/
public static Expression buildMultiOrExpression(List<Expression> expressions) {
// 边界校验:空列表/单条件直接返回
if (expressions == null || expressions.isEmpty()) {
return null;
}
if (expressions.size() == 1) {
return expressions.get(0);
}
// 从第一个条件开始,逐个拼接OR
Expression result = expressions.get(0);
for (int i = 1; i < expressions.size(); i++) {
result = new OrExpression(result, expressions.get(i));
}
return result;
}
// ==================== 添加配置 ====================
public void addEntColumn(Class<? extends BaseDO> entityClass) {
addEntColumn(entityClass, "ent_id");
addEntColumn(entityClass, "receive_ent_id");
}
public void addEntColumn(Class<? extends BaseDO> entityClass, String columnName) {
String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();
addEntColumn(tableName, columnName);
}
public void addEntColumn(String tableName, String columnName) {
Set<String> columnNameSet = entColumns.get(tableName);
if (CollUtil.isEmpty(columnNameSet)) {
columnNameSet = new HashSet<>();
}
columnNameSet.add(columnName);
entColumns.put(tableName, columnNameSet);
TABLE_NAMES.add(tableName);
}
public void addTenantColumn(Class<? extends BaseDO> entityClass) {
addTenantColumn(entityClass, "tenant_id");
addTenantColumn(entityClass, "receive_tenant_id");
}
public void addTenantColumn(Class<? extends BaseDO> entityClass, String columnName) {
String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();
addTenantColumn(tableName, columnName);
}
public void addTenantColumn(String tableName, String columnName) {
tenantColumns.put(tableName, columnName);
TABLE_NAMES.add(tableName);
}
public void addUserColumn(Class<? extends BaseDO> entityClass) {
addUserColumn(entityClass, "user_id");
}
public void addUserColumn(Class<? extends BaseDO> entityClass, String columnName) {
String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();
addUserColumn(tableName, columnName);
}
public void addUserColumn(String tableName, String columnName) {
userColumns.put(tableName, columnName);
TABLE_NAMES.add(tableName);
}
}
4、仿写 YudaoDeptDataPermissionAutoConfiguration
仿照芋道的 YudaoDeptDataPermissionAutoConfiguration 写一个 YudaoWaybillDataPermissionAutoConfiguration

YudaoWaybillDataPermissionAutoConfiguration 代码如下
java
package cn.iocoder.yudao.framework.datapermission.config;
import cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;
import cn.iocoder.yudao.framework.datapermission.core.rule.waybill.WaybillDataPermissionRule;
import cn.iocoder.yudao.framework.datapermission.core.rule.waybill.WaybillDataPermissionRuleCustomizer;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import java.util.List;
/**
* 基于运单的数据权限 AutoConfiguration
*
* @author: gan
* @date: 2026-03-06 09:42
*/
@AutoConfiguration
@ConditionalOnClass(LoginUser.class)
@ConditionalOnBean(value = {PermissionCommonApi.class, WaybillDataPermissionRuleCustomizer.class})
public class YudaoWaybillDataPermissionAutoConfiguration {
@Bean
public WaybillDataPermissionRule waybillDataPermissionRule(PermissionCommonApi permissionApi,
List<WaybillDataPermissionRuleCustomizer> customizers) {
// 创建 WaybillDataPermissionRule 对象
WaybillDataPermissionRule rule = new WaybillDataPermissionRule(permissionApi);
// 补全表配置
customizers.forEach(customizer -> customizer.customize(rule));
return rule;
}
}
5、增加一下引用配置
在 YudaoDeptDataPermissionAutoConfiguration 配置下面增加 4 中仿写的 YudaoDeptDataPermissionAutoConfiguration 完整路径

6、打开日志文件打印
这个看你的 mapper 是否在芋道原有的包下,如果在原有的包下则不需要修改,如果不在则需要仿照 system 在 yaml 文件中添加对应包名的的日志输出

7、配置数据权限
在要使用自定义的数据权限规则的方法中加上 DataPermission 注解

最后需要注意的是,自定义的数据权限规则要使用 mybatis plus 生成的 selectPage 方法才会有效,手写的 xml 是不会拼接的

8、测试
启动项目,调用对应接口

可以看到已经拼接了对应字段