Преглед на файлове

pms 设备责任人数据权限增强 在原有的数据权限上拼接 设备责任人数据权限规则 排查重复生成查询语句的错误

zhangcl преди 1 седмица
родител
ревизия
bf2aec1ff1

+ 14 - 7
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/YudaoDataPermissionAutoConfiguration.java

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.datapermission.core.db.DataPermissionRuleHandl
 import cn.iocoder.yudao.framework.datapermission.core.rule.CombinedDataPermissionRuleFactory;
 import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;
 import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactory;
+import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactoryImpl;
 import cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataPermissionRule;
 import cn.iocoder.yudao.framework.datapermission.core.rule.device.DevicePersonDataPermissionRule;
 import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
@@ -42,13 +43,19 @@ public class YudaoDataPermissionAutoConfiguration {
         CombinedPermissionConfig combinedConfig = combinedConfigProvider.getIfAvailable();
         DevicePermissionConfig devicePermissionConfig = devicePermissionConfigProvider.getIfAvailable();
 
-        return new CombinedDataPermissionRuleFactory(
-                rules,
-                deptRule,
-                deviceRule,
-                combinedConfig,
-                devicePermissionConfig
-        );
+        // 关键修改:只有所有依赖都存在时才使用组合工厂
+        if (deviceRule != null && combinedConfig != null && devicePermissionConfig != null) {
+            return new CombinedDataPermissionRuleFactory(
+                    rules,
+                    deptRule,
+                    deviceRule,
+                    combinedConfig,
+                    devicePermissionConfig
+            );
+        } else {
+            // 否则使用默认工厂
+            return new DataPermissionRuleFactoryImpl(rules);
+        }
     }
 
     @Bean

+ 18 - 0
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionRuleHandler.java

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFac
 import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
 import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import net.sf.jsqlparser.expression.Alias;
 import net.sf.jsqlparser.expression.Expression;
 import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
@@ -23,6 +24,7 @@ import java.util.List;
  * @author 芋道源码
  */
 @RequiredArgsConstructor
+@Slf4j
 public class DataPermissionRuleHandler implements MultiDataPermissionHandler {
 
     private final DataPermissionRuleFactory ruleFactory;
@@ -31,12 +33,28 @@ public class DataPermissionRuleHandler implements MultiDataPermissionHandler {
     public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {
         // 获得 Mapper 对应的数据权限的规则
         List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(mappedStatementId);
+
+        // 调试日志
+        // if (log.isDebugEnabled()) {
+            // String tableName = MyBatisUtils.getTableName(table);
+            // log.debug("[DataPermission] MappedStatement: {}, Table: {}, Rules count: {}",
+               //     mappedStatementId, tableName, rules.size());
+
+            for (int i = 0; i < rules.size(); i++) {
+                log.debug("[DataPermission] Rule {}: {}", i + 1, rules.get(i).getClass().getSimpleName());
+            }
+        // }
+
         if (CollUtil.isEmpty(rules)) {
             return where;
         }
 
         // 2. 生成条件表达式
         String tableName = MyBatisUtils.getTableName(table);
+
+        log.debug("[DataPermission] MappedStatement: {}, Table: {}, Rules count: {}",
+                mappedStatementId, tableName, rules.size());
+
         Expression permissionExpression = buildPermissionExpression(tableName, table.getAlias(), rules);
 
         // 3. 组合到原始WHERE条件

+ 66 - 7
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/CombinedDataPermissionRuleFactory.java

@@ -6,10 +6,12 @@ import cn.iocoder.yudao.framework.datapermission.core.config.CombinedPermissionC
 import cn.iocoder.yudao.framework.datapermission.core.config.DevicePermissionConfig;
 import cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataPermissionRule;
 import cn.iocoder.yudao.framework.datapermission.core.rule.device.DevicePersonDataPermissionRule;
+import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
 import net.sf.jsqlparser.expression.Alias;
 import net.sf.jsqlparser.expression.Expression;
 import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -43,13 +45,69 @@ public class CombinedDataPermissionRuleFactory extends DataPermissionRuleFactory
     public List<DataPermissionRule> getDataPermissionRule(String mappedStatementId) {
         List<DataPermissionRule> rules = super.getDataPermissionRule(mappedStatementId);
 
-        // 关键修改:当设备规则不存在时,直接返回原始规则
-        if (deviceRule == null || combinedConfig == null || devicePermissionConfig == null) {
+        // 关键修改:只在需要组合规则的表上应用设备规则
+        if (shouldApplyCombinedRule(mappedStatementId)) {
+            return combineRules(rules);
+        } else {
+            // 非组合表:移除设备规则
+            return filterDeviceRule(rules);
+        }
+    }
+
+    private List<DataPermissionRule> filterDeviceRule(List<DataPermissionRule> rules) {
+        if (deviceRule == null) return rules;
+        List<DataPermissionRule> filtered = new ArrayList<>(rules);
+        filtered.removeIf(rule -> rule == deviceRule);
+        return filtered;
+    }
+
+    private List<DataPermissionRule> createCombinedRules(List<DataPermissionRule> originalRules) {
+        // 1. 复制原始规则列表,避免修改原始集合
+        List<DataPermissionRule> rules = new ArrayList<>(originalRules);
+        Set<String> combinedTables = combinedConfig.getCombinedTables();
+
+        if (CollUtil.isEmpty(combinedTables)) {
             return rules;
         }
 
-        // 特殊处理:将部门规则和设备规则组合为OR关系
-        return combineRules(rules);
+        // 2. 检查是否需要组合
+        boolean needCombine = false;
+        for (DataPermissionRule rule : rules) {
+            if (rule == deptRule || rule == deviceRule) {
+                for (String table : rule.getTableNames()) {
+                    if (combinedTables.contains(table)) {
+                        needCombine = true;
+                        break;
+                    }
+                }
+                if (needCombine) break;
+            }
+        }
+
+        if (!needCombine) {
+            return rules;
+        }
+
+        // 3. 创建组合规则并替换
+        CombinedPermissionRule combinedRule = new CombinedPermissionRule(
+                deptRule, deviceRule, combinedTables, devicePermissionConfig
+        );
+
+        // 4. 移除原有的部门规则和设备规则
+        rules.removeIf(rule -> rule == deptRule || rule == deviceRule);
+
+        // 5. 添加组合规则
+        rules.add(combinedRule);
+        return rules;
+    }
+
+    private boolean shouldApplyCombinedRule(String mappedStatementId) {
+        if (deviceRule == null || combinedConfig == null || devicePermissionConfig == null) {
+            return false;
+        }
+
+        String tableName = MyBatisUtils.getTableName(mappedStatementId);
+        return tableName != null && combinedConfig.isCombinedTable(tableName);
     }
 
     private List<DataPermissionRule> combineRules(List<DataPermissionRule> rules) {
@@ -83,11 +141,12 @@ public class CombinedDataPermissionRuleFactory extends DataPermissionRuleFactory
         );
 
         // 4. 移除原有的部门规则和设备规则
-        rules.removeIf(rule -> rule == deptRule || rule == deviceRule);
+        List<DataPermissionRule> newRules = new ArrayList<>(rules);
+        newRules.removeIf(rule -> rule == deptRule || rule == deviceRule);
 
         // 5. 添加组合规则
-        rules.add(combinedRule);
-        return rules;
+        newRules.add(combinedRule);
+        return newRules;
     }
 
     /**

+ 47 - 6
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/device/DevicePersonDataPermissionRule.java

@@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.security.core.LoginUser;
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import cn.iocoder.yudao.module.pms.api.permission.DevicePersonPermissionApi;
 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;
@@ -20,11 +19,10 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
- * 基于 设备责任人 的数据权限
+ * 基于 设备责任人 的数据权限规则
  *
  * @author ruiqi
  */
-@AllArgsConstructor
 @Slf4j
 public class DevicePersonDataPermissionRule implements DataPermissionRule {
 
@@ -36,6 +34,9 @@ public class DevicePersonDataPermissionRule implements DataPermissionRule {
     // 存储表配置:key=表名, value=关联字段配置
     private final Map<String, TableConfig> tableConfigs = new ConcurrentHashMap<>();
 
+    // 只对特定表生效
+    private Set<String> enabledTables = new HashSet<>();
+
     private final DevicePersonPermissionApi devicePersonService;
 
     private final DevicePermissionConfig devicePermissionConfig;
@@ -45,15 +46,55 @@ public class DevicePersonDataPermissionRule implements DataPermissionRule {
      */
     private final Set<String> TABLE_NAMES = new HashSet<>();
 
+    /**
+     * 简化构造函数
+     */
+    public DevicePersonDataPermissionRule(DevicePersonPermissionApi devicePersonService,
+                                          DevicePermissionConfig devicePermissionConfig) {
+        this.devicePersonService = devicePersonService;
+        this.devicePermissionConfig = devicePermissionConfig;
+        // 初始化配置
+        initTableConfigs();
+    }
+
+    /**
+     * 初始化表配置
+     */
+    private void initTableConfigs() {
+        for (Class<?> entity : devicePermissionConfig.getMainEntities()) {
+            DevicePermissionConfig.SubTableConfig subConfig =
+                    devicePermissionConfig.getSubTableConfig(entity);
+            if (subConfig != null) {
+                String mainTable = TableInfoHelper.getTableInfo(entity).getTableName();
+                tableConfigs.put(mainTable, new TableConfig(
+                        "id", // 主键默认为id
+                        subConfig.getSubTable(),
+                        subConfig.getJoinColumn(),
+                        subConfig.getDeviceColumn()
+                ));
+            }
+        }
+    }
+
     @Override
     public Set<String> getTableNames() {
-        return TABLE_NAMES;
+        return enabledTables;
+    }
+
+    public void setEnabledTables(Set<String> enabledTables) {
+        this.enabledTables = enabledTables;
     }
 
     @Override
     public Expression getExpression(String tableName, Alias tableAlias) {
+
+        // 只对配置的表生效
+        if (!enabledTables.contains(tableName)) {
+            return null;
+        }
+
         // 1. 获取表配置
-        TableConfig config = getTableConfig(tableName);
+        TableConfig config = tableConfigs.get(tableName);
         if (config == null) return null;
 
         // 1. 获取当前登录用户
@@ -171,7 +212,7 @@ public class DevicePersonDataPermissionRule implements DataPermissionRule {
     }
 
     /**
-     * 表配置类(替代record)
+     * 表配置类
      */
     private static class TableConfig {
         private final String primaryKey;

+ 69 - 0
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java

@@ -5,7 +5,10 @@ import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.SortingField;
 import cn.iocoder.yudao.framework.mybatis.core.enums.DbTypeEnum;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.OrderItem;
 import com.baomidou.mybatisplus.core.toolkit.StringPool;
 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
@@ -15,6 +18,8 @@ import net.sf.jsqlparser.expression.Alias;
 import net.sf.jsqlparser.schema.Column;
 import net.sf.jsqlparser.schema.Table;
 
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -74,6 +79,70 @@ public class MyBatisUtils {
         return tableName;
     }
 
+    /**
+     * 从Mapper接口获取实体类
+     */
+    private static Class<?> getEntityClass(Class<?> mapperClass) {
+        // 检查是否是BaseMapperX的子类
+        for (Type type : mapperClass.getGenericInterfaces()) {
+            if (type instanceof ParameterizedType) {
+                ParameterizedType pt = (ParameterizedType) type;
+                if (pt.getRawType() == BaseMapperX.class || pt.getRawType() == BaseMapper.class) {
+                    Type[] actualTypeArguments = pt.getActualTypeArguments();
+                    if (actualTypeArguments.length > 0 && actualTypeArguments[0] instanceof Class) {
+                        return (Class<?>) actualTypeArguments[0];
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 从mappedStatementId解析表名
+     *
+     * @param mappedStatementId MyBatis映射语句ID
+     * @return 表名或null(如果无法确定)
+     */
+    public static String getTableName(String mappedStatementId) {
+        try {
+            // 获取Mapper接口类名
+            String className = mappedStatementId.substring(0, mappedStatementId.lastIndexOf("."));
+            Class<?> mapperClass = Class.forName(className);
+
+            // 获取Mapper接口对应的实体类
+            Class<?> entityClass = getEntityClass(mapperClass);
+            if (entityClass == null) {
+                return null;
+            }
+
+            // 从实体类获取表名
+            return getTableNameFromEntity(entityClass);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     * 从实体类获取表名
+     */
+    private static String getTableNameFromEntity(Class<?> entityClass) {
+        // 优先使用MyBatis Plus的@TableName注解
+        TableName tableNameAnnotation = entityClass.getAnnotation(TableName.class);
+        if (tableNameAnnotation != null) {
+            return tableNameAnnotation.value();
+        }
+
+        // 其次使用JPA的@Table注解
+        /* javax.persistence.Table tableAnnotation = entityClass.getAnnotation(javax.persistence.Table.class);
+        if (tableAnnotation != null && !tableAnnotation.name().isEmpty()) {
+            return tableAnnotation.name();
+        } */
+
+        // 最后使用类名转换(驼峰转下划线)
+        return StrUtil.toUnderlineCase(entityClass.getSimpleName());
+    }
+
     /**
      * 构建 Column 对象
      *

+ 11 - 1
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/config/PmsDataPermissionConfiguration.java

@@ -22,6 +22,7 @@ import cn.iocoder.yudao.module.pms.dal.dataobject.maintenance.IotMaintenancePlan
 import cn.iocoder.yudao.module.supplier.dal.dataobject.product.SupplierDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
@@ -31,6 +32,7 @@ import org.springframework.context.annotation.Configuration;
  * @author 芋道源码
  */
 @Configuration(proxyBeanMethods = false)
+@Slf4j
 public class PmsDataPermissionConfiguration {
 
     @Bean(name = "pmsDeptDataPermissionRuleCustomizer")
@@ -64,7 +66,15 @@ public class PmsDataPermissionConfiguration {
     public DevicePersonDataPermissionRule devicePersonDataPermissionRule(
             DevicePersonPermissionApi devicePersonService,
             DevicePermissionConfig devicePermissionConfig) {
-        return new DevicePersonDataPermissionRule(devicePersonService, devicePermissionConfig);
+        // 只对特定表生效
+        DevicePersonDataPermissionRule rule = new DevicePersonDataPermissionRule(devicePersonService, devicePermissionConfig);
+
+        // 设置只对配置的表生效
+        rule.setEnabledTables(devicePermissionConfig.getMainTableNames());
+
+        log.info("[devicePersonDataPermissionRule] 设备数据权限规则初始化完成,生效表: {}",
+                devicePermissionConfig.getMainTableNames());
+        return rule;
     }
 
     @Bean