Quellcode durchsuchen

pms 设备责任人数据权限增强 在原有的数据权限上拼接 设备责任人数据权限规则

zhangcl vor 1 Woche
Ursprung
Commit
8fbd56ec9b

+ 6 - 0
yudao-framework/yudao-spring-boot-starter-biz-data-permission/pom.xml

@@ -41,6 +41,12 @@
             <version>${revision}</version>
         </dependency>
 
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-pms-api</artifactId> <!-- 设备责任人数据权限 -->
+            <version>${revision}</version>
+        </dependency>
+
         <!-- Test 测试相关 -->
         <dependency>
             <groupId>cn.iocoder.boot</groupId>

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

@@ -1,14 +1,20 @@
 package cn.iocoder.yudao.framework.datapermission.config;
 
 import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionAnnotationAdvisor;
+import cn.iocoder.yudao.framework.datapermission.core.config.CombinedPermissionConfig;
+import cn.iocoder.yudao.framework.datapermission.core.config.DevicePermissionConfig;
 import cn.iocoder.yudao.framework.datapermission.core.db.DataPermissionRuleHandler;
+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;
 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
+import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
 
 import java.util.List;
@@ -22,8 +28,27 @@ import java.util.List;
 public class YudaoDataPermissionAutoConfiguration {
 
     @Bean
-    public DataPermissionRuleFactory dataPermissionRuleFactory(List<DataPermissionRule> rules) {
-        return new DataPermissionRuleFactoryImpl(rules);
+    @ConditionalOnMissingBean // 确保不重复创建
+    public DataPermissionRuleFactory dataPermissionRuleFactory(
+            List<DataPermissionRule> rules,
+            DeptDataPermissionRule deptRule,
+            // 使用Optional解决依赖问题
+            ObjectProvider<DevicePersonDataPermissionRule> deviceRuleProvider,
+            ObjectProvider<CombinedPermissionConfig> combinedConfigProvider,
+            ObjectProvider<DevicePermissionConfig> devicePermissionConfigProvider) {
+
+        // 使用ObjectProvider安全获取Bean
+        DevicePersonDataPermissionRule deviceRule = deviceRuleProvider.getIfAvailable();
+        CombinedPermissionConfig combinedConfig = combinedConfigProvider.getIfAvailable();
+        DevicePermissionConfig devicePermissionConfig = devicePermissionConfigProvider.getIfAvailable();
+
+        return new CombinedDataPermissionRuleFactory(
+                rules,
+                deptRule,
+                deviceRule,
+                combinedConfig,
+                devicePermissionConfig
+        );
     }
 
     @Bean

+ 50 - 0
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/config/CombinedPermissionConfig.java

@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.framework.datapermission.core.config;
+
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * 组合权限规则配置
+ */
+public class CombinedPermissionConfig {
+
+    /**
+     * 需要组合权限规则的表名集合
+     */
+    private final Set<String> combinedTables = new HashSet<>();
+
+    /**
+     * 添加需要组合权限规则的表
+     * @param tableName 表名
+     */
+    public void addCombinedTable(String tableName) {
+        combinedTables.add(tableName);
+    }
+
+    /**
+     * 添加需要组合权限规则的表(基于实体类)
+     * @param entityClass 实体类
+     */
+    public void addCombinedTable(Class<?> entityClass) {
+        String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();
+        combinedTables.add(tableName);
+    }
+
+    /**
+     * 检查表是否需要组合权限规则
+     * @param tableName 表名
+     * @return 是否需要组合规则
+     */
+    public boolean isCombinedTable(String tableName) {
+        return combinedTables.contains(tableName);
+    }
+
+    /**
+     * 获取所有需要组合权限规则的表名
+     */
+    public Set<String> getCombinedTables() {
+        return new HashSet<>(combinedTables);
+    }
+}

+ 103 - 0
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/config/DevicePermissionConfig.java

@@ -0,0 +1,103 @@
+package cn.iocoder.yudao.framework.datapermission.core.config;
+
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 主表-子表映射配置
+ */
+public class DevicePermissionConfig {
+
+    /**
+     * 主表配置映射
+     * key: 主表实体类
+     * value: 子表配置
+     */
+    private final Map<Class<?>, SubTableConfig> mainTableConfigs = new HashMap<>();
+
+    /**
+     * 添加主表配置
+     * 
+     * @param mainEntity 主表实体类
+     * @param subTable 子表名
+     * @param joinColumn 关联列
+     * @param deviceColumn 设备ID列
+     */
+    public void addConfig(Class<?> mainEntity, String subTable, 
+                         String joinColumn, String deviceColumn) {
+        mainTableConfigs.put(mainEntity, new SubTableConfig(subTable, joinColumn, deviceColumn));
+    }
+
+    /**
+     * 添加主表配置(自动获取表名)
+     * 
+     * @param mainEntity 主表实体类
+     * @param subEntity 子表实体类
+     * @param joinColumn 关联列
+     * @param deviceColumn 设备ID列
+     */
+    public void addConfig(Class<?> mainEntity, Class<?> subEntity,
+                         String joinColumn, String deviceColumn) {
+        String subTable = TableInfoHelper.getTableInfo(subEntity).getTableName();
+        mainTableConfigs.put(mainEntity, new SubTableConfig(subTable, joinColumn, deviceColumn));
+    }
+
+    /**
+     * 获取主表对应的子表配置
+     * 
+     * @param mainEntity 主表实体类
+     * @return 子表配置
+     */
+    public SubTableConfig getSubTableConfig(Class<?> mainEntity) {
+        return mainTableConfigs.get(mainEntity);
+    }
+
+    /**
+     * 获取所有配置的主表实体类
+     */
+    public Set<Class<?>> getMainEntities() {
+        return new HashSet<>(mainTableConfigs.keySet());
+    }
+
+    /**
+     * 获取所有配置的主表名
+     */
+    public Set<String> getMainTableNames() {
+        Set<String> tableNames = new HashSet<>();
+        for (Class<?> entity : mainTableConfigs.keySet()) {
+            tableNames.add(TableInfoHelper.getTableInfo(entity).getTableName());
+        }
+        return tableNames;
+    }
+
+    /**
+     * 子表配置
+     */
+    public static class SubTableConfig {
+        private final String subTable;
+        private final String joinColumn;
+        private final String deviceColumn;
+
+        public SubTableConfig(String subTable, String joinColumn, String deviceColumn) {
+            this.subTable = subTable;
+            this.joinColumn = joinColumn;
+            this.deviceColumn = deviceColumn;
+        }
+
+        public String getSubTable() {
+            return subTable;
+        }
+
+        public String getJoinColumn() {
+            return joinColumn;
+        }
+
+        public String getDeviceColumn() {
+            return deviceColumn;
+        }
+    }
+}

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

@@ -6,8 +6,10 @@ 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 net.sf.jsqlparser.expression.Alias;
 import net.sf.jsqlparser.expression.Expression;
 import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
+import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
 import net.sf.jsqlparser.schema.Table;
 
 import java.util.List;
@@ -30,28 +32,53 @@ public class DataPermissionRuleHandler implements MultiDataPermissionHandler {
         // 获得 Mapper 对应的数据权限的规则
         List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(mappedStatementId);
         if (CollUtil.isEmpty(rules)) {
-            return null;
+            return where;
         }
 
-        // 生成条件
-        Expression allExpression = null;
+        // 2. 生成条件表达式
+        String tableName = MyBatisUtils.getTableName(table);
+        Expression permissionExpression = buildPermissionExpression(tableName, table.getAlias(), rules);
+
+        // 3. 组合到原始WHERE条件
+        if (permissionExpression == null) {
+            return where;
+        }
+
+        return where == null ? permissionExpression
+                : new AndExpression(where, permissionExpression);
+
+    }
+
+    private Expression buildPermissionExpression(String tableName, Alias tableAlias,
+                                                 List<DataPermissionRule> rules) {
+        Expression result = null;
+
         for (DataPermissionRule rule : rules) {
-            // 判断表名是否匹配
-            String tableName = MyBatisUtils.getTableName(table);
             if (!rule.getTableNames().contains(tableName)) {
                 continue;
             }
 
-            // 单条规则的条件
-            Expression oneExpress = rule.getExpression(tableName, table.getAlias());
-            if (oneExpress == null) {
+            Expression expression = rule.getExpression(tableName, tableAlias);
+            if (expression == null) {
                 continue;
             }
-            // 拼接到 allExpression 中
-            allExpression = allExpression == null ? oneExpress
-                    : new AndExpression(allExpression, oneExpress);
+
+            // 组合规则条件
+            if (result == null) {
+                result = expression;
+            } else {
+                // 处理组合规则:保持其 OR 关系
+                if (expression instanceof OrExpression) {
+                    // 如果当前规则是组合规则,直接与之前结果 AND 连接
+                    result = new AndExpression(result, expression);
+                } else {
+                    // 普通规则使用 AND 连接
+                    result = new AndExpression(result, expression);
+                }
+            }
         }
-        return allExpression;
+
+        return result;
     }
 
 }

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

@@ -0,0 +1,145 @@
+package cn.iocoder.yudao.framework.datapermission.core.rule;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;
+import cn.iocoder.yudao.framework.datapermission.core.config.CombinedPermissionConfig;
+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 net.sf.jsqlparser.expression.Alias;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 支持通过 {@link DataPermissionContextHolder} 过滤数据权限
+ *
+ * @author ruiqi
+ */
+public class CombinedDataPermissionRuleFactory extends DataPermissionRuleFactoryImpl {
+
+    private final DeptDataPermissionRule deptRule;
+    private final DevicePersonDataPermissionRule deviceRule;
+    private final CombinedPermissionConfig combinedConfig;
+    private final DevicePermissionConfig devicePermissionConfig;
+
+    public CombinedDataPermissionRuleFactory(
+            List<DataPermissionRule> rules,
+            DeptDataPermissionRule deptRule,
+            DevicePersonDataPermissionRule deviceRule,
+            CombinedPermissionConfig combinedConfig,
+            DevicePermissionConfig devicePermissionConfig) {
+        super(rules);
+        this.deptRule = deptRule;
+        this.deviceRule = deviceRule;
+        this.combinedConfig = combinedConfig;
+        this.devicePermissionConfig = devicePermissionConfig;
+    }
+
+    @Override
+    public List<DataPermissionRule> getDataPermissionRule(String mappedStatementId) {
+        List<DataPermissionRule> rules = super.getDataPermissionRule(mappedStatementId);
+
+        // 关键修改:当设备规则不存在时,直接返回原始规则
+        if (deviceRule == null || combinedConfig == null || devicePermissionConfig == null) {
+            return rules;
+        }
+
+        // 特殊处理:将部门规则和设备规则组合为OR关系
+        return combineRules(rules);
+    }
+
+    private List<DataPermissionRule> combineRules(List<DataPermissionRule> rules) {
+        // 1. 获取所有需要组合规则的表
+        Set<String> combinedTables = combinedConfig.getCombinedTables();
+        if (CollUtil.isEmpty(combinedTables)) {
+            return 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 static class CombinedPermissionRule implements DataPermissionRule {
+        private final DeptDataPermissionRule deptRule;
+        private final DevicePersonDataPermissionRule deviceRule;
+        private final Set<String> combinedTables;
+        private final DevicePermissionConfig devicePermissionConfig;
+
+        public CombinedPermissionRule(
+                DeptDataPermissionRule deptRule,
+                DevicePersonDataPermissionRule deviceRule,
+                Set<String> combinedTables,
+                DevicePermissionConfig devicePermissionConfig) {
+            this.deptRule = deptRule;
+            this.deviceRule = deviceRule;
+            this.combinedTables = combinedTables;
+            this.devicePermissionConfig = devicePermissionConfig;
+        }
+
+        @Override
+        public Set<String> getTableNames() {
+            Set<String> tables = new HashSet<>();
+            tables.addAll(deptRule.getTableNames());
+            tables.addAll(deviceRule.getTableNames());
+            tables.retainAll(combinedTables); // 只保留需要组合规则的表
+            return tables;
+        }
+
+        @Override
+        public Expression getExpression(String tableName, Alias tableAlias) {
+            // 只对配置的表应用组合规则
+            if (!combinedTables.contains(tableName)) {
+                return null;
+            }
+
+            // 分别获取部门和设备规则的条件
+            Expression deptExpr = deptRule != null ? deptRule.getExpression(tableName, tableAlias) : null;
+            Expression deviceExpr = deviceRule != null ? deviceRule.getExpression(tableName, tableAlias) : null;
+
+            // 组合为OR关系
+            if (deptExpr != null && deviceExpr != null) {
+                return new OrExpression(deptExpr, deviceExpr);
+            } else if (deptExpr != null) {
+                return deptExpr;
+            } else if (deviceExpr != null) {
+                return deviceExpr;
+            }
+            return null;
+        }
+    }
+
+}

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

@@ -2,49 +2,30 @@ package cn.iocoder.yudao.framework.datapermission.core.rule.device;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.framework.datapermission.core.config.DevicePermissionConfig;
 import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;
 import cn.iocoder.yudao.framework.security.core.LoginUser;
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
-import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
+import cn.iocoder.yudao.module.pms.api.permission.DevicePersonPermissionApi;
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
 import lombok.AllArgsConstructor;
-import lombok.Getter;
 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.relational.*;
-import net.sf.jsqlparser.schema.Table;
-import net.sf.jsqlparser.statement.select.PlainSelect;
-import org.springframework.stereotype.Component;
-import net.sf.jsqlparser.schema.Column;
-
-import java.util.HashMap;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
- * 基于部门的 {@link DataPermissionRule} 数据权限规则实现
- *
- * 注意,使用 DeptDataPermissionRule 时,需要保证表中有 dept_id 部门编号的字段,可自定义。
+ * 基于 设备责任人 的数据权限
  *
- * 实际业务场景下,会存在一个经典的问题?当用户修改部门时,冗余的 dept_id 是否需要修改?
- * 1. 一般情况下,dept_id 不进行修改,则会导致用户看不到之前的数据。【yudao-server 采用该方案】
- * 2. 部分情况下,希望该用户还是能看到之前的数据,则有两种方式解决:【需要你改造该 DeptDataPermissionRule 的实现代码】
- *  1)编写洗数据的脚本,将 dept_id 修改成新部门的编号;【建议】
- *      最终过滤条件是 WHERE dept_id = ?
- *  2)洗数据的话,可能涉及的数据量较大,也可以采用 user_id 进行过滤的方式,此时需要获取到 dept_id 对应的所有 user_id 用户编号;
- *      最终过滤条件是 WHERE user_id IN (?, ?, ? ...)
- *  3)想要保证原 dept_id 和 user_id 都可以看的到,此时使用 dept_id 和 user_id 一起过滤;
- *      最终过滤条件是 WHERE dept_id = ? OR user_id IN (?, ?, ? ...)
- *
- * @author 芋道源码
+ * @author ruiqi
  */
 @AllArgsConstructor
 @Slf4j
-@Component
 public class DevicePersonDataPermissionRule implements DataPermissionRule {
 
     /**
@@ -52,34 +33,13 @@ public class DevicePersonDataPermissionRule implements DataPermissionRule {
      */
     protected static final String CONTEXT_KEY = DevicePersonDataPermissionRule.class.getSimpleName();
 
-    private static final String DEPT_COLUMN_NAME = "dept_id";
-    private static final String USER_COLUMN_NAME = "user_id";
-
-    static final Expression EXPRESSION_NULL = new NullValue();
-
-    private final PermissionApi permissionApi;
-
     // 存储表配置:key=表名, value=关联字段配置
-    private final Map<String, DeviceTableConfig> tableConfigs = new ConcurrentHashMap<>();
+    private final Map<String, TableConfig> tableConfigs = new ConcurrentHashMap<>();
 
-    private final DevicePersonService devicePersonService;
+    private final DevicePersonPermissionApi devicePersonService;
+
+    private final DevicePermissionConfig devicePermissionConfig;
 
-    /**
-     * 基于部门的表字段配置
-     * 一般情况下,每个表的部门编号字段是 dept_id,通过该配置自定义。
-     *
-     * key:表名
-     * value:字段名
-     */
-    private final Map<String, String> deptColumns = new HashMap<>();
-    /**
-     * 基于用户的表字段配置
-     * 一般情况下,每个表的部门编号字段是 dept_id,通过该配置自定义。
-     *
-     * key:表名
-     * value:字段名
-     */
-    private final Map<String, String> userColumns = new HashMap<>();
     /**
      * 所有表名,是 {@link #deptColumns} 和 {@link #userColumns} 的合集
      */
@@ -92,29 +52,74 @@ public class DevicePersonDataPermissionRule implements DataPermissionRule {
 
     @Override
     public Expression getExpression(String tableName, Alias tableAlias) {
+        // 1. 获取表配置
+        TableConfig config = getTableConfig(tableName);
+        if (config == null) return null;
+
         // 1. 获取当前登录用户
         LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
         if (loginUser == null) {
             return null;
         }
 
-        // 2. 获取表配置
-        DeviceTableConfig config = tableConfigs.get(tableName);
-        if (config == null) {
-            return null;
-        }
-
         // 3. 获取当前用户负责的设备ID集合
         Set<Long> responsibleDeviceIds = getResponsibleDeviceIds(loginUser);
+        if (CollUtil.isEmpty(responsibleDeviceIds)) return null;
+
+        // 5. 构建主表关联条件(EXISTS子查询)
+        return buildDataPermissionExpression(config, tableName, tableAlias, responsibleDeviceIds);
+    }
 
-        // 4. 构建设备责任人条件
-        Expression deviceExpression = buildDeviceExpression(config, tableAlias, responsibleDeviceIds);
-        if (deviceExpression == null) {
+    private Expression buildDataPermissionExpression(TableConfig config,
+                                                     String tableName,
+                                                     Alias tableAlias,
+                                                     Set<Long> deviceIds) {
+        try {
+            // 1. 构建子查询SQL
+            String subQuerySql = buildSubQuerySql(config, deviceIds);
+
+            // 2. 构建主表条件:主表关联字段 IN (子查询结果)
+            String inExpressionSql = buildInExpressionSql(config, tableName, tableAlias, subQuerySql);
+
+            // 3. 解析SQL为表达式
+            return CCJSqlParserUtil.parseCondExpression(inExpressionSql);
+        } catch (Exception e) {
+            log.error("[buildDataPermissionExpression]构建设备责任人条件失败", e);
             return null;
         }
+    }
 
-        // 5. 构建主表关联条件(EXISTS子查询)
-        return buildExistsExpression(config, tableAlias, deviceExpression);
+    private String buildSubQuerySql(TableConfig config, Set<Long> deviceIds) {
+        // 构建设备ID列表字符串
+        String deviceIdsStr = CollUtil.join(deviceIds, ", ");
+
+        // 构建子查询SQL
+        return String.format("(SELECT %s FROM %s WHERE %s IN (%s))",
+                config.getJoinColumn(),
+                config.getSubTable(),
+                config.getDeviceColumn(),
+                deviceIdsStr);
+    }
+
+    private String buildInExpressionSql(TableConfig config,
+                                        String tableName,
+                                        Alias tableAlias,
+                                        String subQuerySql) {
+        // 构建主表列名
+        String mainColumn = buildMainTableColumn(tableName, tableAlias, config.getPrimaryKey());
+
+        // 构建IN表达式SQL
+        return String.format("%s IN %s", mainColumn, subQuerySql);
+    }
+
+    private String buildMainTableColumn(String tableName, Alias tableAlias, String columnName) {
+        if (tableAlias != null) {
+            // 如果有别名,使用别名构建列
+            return tableAlias.getName() + "." + columnName;
+        } else {
+            // 没有别名,直接使用表名+列名
+            return tableName + "." + columnName;
+        }
     }
 
     private Set<Long> getResponsibleDeviceIds(LoginUser loginUser) {
@@ -129,100 +134,85 @@ public class DevicePersonDataPermissionRule implements DataPermissionRule {
         return deviceIds;
     }
 
-    private Expression buildDeviceExpression(DeviceTableConfig config,
-                                             Alias tableAlias,
-                                             Set<Long> deviceIds) {
-        if (CollUtil.isEmpty(deviceIds)) {
-            return null;
-        }
+    // 添加表配置
+    public void addConfig(String mainTable, String primaryKey,
+                          String subTable, String joinColumn, String deviceColumn) {
+        tableConfigs.put(mainTable, new TableConfig(primaryKey, subTable, joinColumn, deviceColumn));
+        TABLE_NAMES.add(mainTable);
+    }
 
-        // 构建子表设备ID IN条件: device_id IN (?, ?, ...)
-        Column deviceColumn = new Column(tableAlias != null ?
-                tableAlias.getName() + "." + config.getDeviceColumn() :
-                config.getDeviceColumn());
+    private TableConfig getTableConfig(String tableName) {
+        // 从统一配置中获取主表对应的实体类
+        Class<?> mainEntity = findMainEntityByTableName(tableName);
+        if (mainEntity == null) return null;
 
-        ExpressionList<LongValue> valueList = new ExpressionList<>();
-        for (Long deviceId : deviceIds) {
-            valueList.add(new LongValue(deviceId));
-        }
+        // 获取子表配置
+        DevicePermissionConfig.SubTableConfig subConfig =
+                devicePermissionConfig.getSubTableConfig(mainEntity);
 
-        return new InExpression(deviceColumn, valueList);
-    }
+        if (subConfig == null) return null;
 
-    private Expression buildExistsExpression(DeviceTableConfig config,
-                                             Alias tableAlias,
-                                             Expression deviceExpression) {
-        // 构建EXISTS子查询:
-        // EXISTS (SELECT 1 FROM 子表
-        //         WHERE 子表.主表关联列 = 主表.主键列
-        //         AND (设备条件))
-
-        // 1. 子查询表
-        Table subTable = new Table(config.getSubTable());
-        subTable.setAlias(new Alias("sub", false));
-
-        // 2. WHERE条件: 子表.主表关联列 = 主表.主键列
-        Column subTableJoinColumn = new Column("sub." + config.getSubTableJoinColumn());
-        Column mainTablePkColumn = new Column(
-                (tableAlias != null ? tableAlias.getName() + "." : "") + config.getMainTablePkColumn());
-        EqualsTo joinCondition = new EqualsTo(subTableJoinColumn, mainTablePkColumn);
-
-        // 3. 组合条件: 关联条件 AND 设备条件
-        ParenthesedExpressionList conditions = new ParenthesedExpressionList();
-        conditions.add(joinCondition);
-        conditions.add(deviceExpression);
-
-        // 4. 构建子查询
-        SubSelect subSelect = new SubSelect();
-        subSelect.setSelectBody(new PlainSelect()
-                .addSelectItems(new LongValue(1))
-                .withFromItem(subTable)
-                .withWhere(conditions));
-
-        return new ExistsExpression().withRightExpression(subSelect);
+        return new TableConfig(
+                "id", // 主键默认为id
+                subConfig.getSubTable(),
+                subConfig.getJoinColumn(),
+                subConfig.getDeviceColumn()
+        );
     }
 
-    /**
-     * 添加设备责任人权限配置
-     *
-     * @param mainTable 主表名
-     * @param mainTablePkColumn 主表主键列名
-     * @param subTable 子表名
-     * @param subTableJoinColumn 子表关联主表的列名
-     * @param deviceColumn 设备ID列名
-     */
-    public void addConfig(String mainTable,
-                          String mainTablePkColumn,
-                          String subTable,
-                          String subTableJoinColumn,
-                          String deviceColumn) {
-        tableConfigs.put(mainTable, new DeviceTableConfig(
-                mainTablePkColumn, subTable, subTableJoinColumn, deviceColumn
-        ));
+    private Class<?> findMainEntityByTableName(String tableName) {
+        for (Class<?> entity : devicePermissionConfig.getMainEntities()) {
+            String entityTable = TableInfoHelper.getTableInfo(entity).getTableName();
+            if (entityTable.equalsIgnoreCase(tableName)) {
+                return entity;
+            }
+        }
+        return null;
     }
 
     /**
-     * 表配置类
+     * 表配置类(替代record)
      */
-    @Getter
-    @AllArgsConstructor
-    private static class DeviceTableConfig {
-        /**
-         * 主表主键列名
-         */
-        private final String mainTablePkColumn;
-        /**
-         * 子表名
-         */
+    private static class TableConfig {
+        private final String primaryKey;
         private final String subTable;
+        private final String joinColumn;
+        private final String deviceColumn;
+
+        public TableConfig(String primaryKey, String subTable,
+                           String joinColumn, String deviceColumn) {
+            this.primaryKey = primaryKey;
+            this.subTable = subTable;
+            this.joinColumn = joinColumn;
+            this.deviceColumn = deviceColumn;
+        }
+
+        public String getPrimaryKey() {
+            return primaryKey;
+        }
+
+        public String getSubTable() {
+            return subTable;
+        }
+
+        public String getJoinColumn() {
+            return joinColumn;
+        }
+
+        public String getDeviceColumn() {
+            return deviceColumn;
+        }
+
         /**
-         * 子表关联主表的列名
-         */
-        private final String subTableJoinColumn;
-        /**
-         * 设备ID列名
+         * 生成安全的子表别名
          */
-        private final String deviceColumn;
+        public String getSubTableAlias() {
+            // 替换特殊字符,避免SQL注入和语法错误
+            String safeAlias = "sub_" + subTable.replaceAll("[^a-zA-Z0-9]", "_");
+
+            // 确保别名长度合理
+            return safeAlias.length() > 30 ? safeAlias.substring(0, 30) : safeAlias;
+        }
     }
 
 }

+ 3 - 2
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/api/permission/DevicePersonPermissionApiImpl.java

@@ -29,11 +29,12 @@ public class DevicePersonPermissionApiImpl implements DevicePersonPermissionApi
         personIds.add(userId);
         reqVO.setPersonIds(personIds);
         List<IotDevicePersonDO> devicePersons = devicePersonService.getByPersonIds(reqVO);
+        Set<Long> deviceIds = new HashSet<>();
         if (CollUtil.isNotEmpty(devicePersons)) {
             devicePersons.forEach(devicePerson -> {
-
+                deviceIds.add(devicePerson.getDeviceId());
             });
         }
-        return Set.of();
+        return deviceIds;
     }
 }

+ 38 - 3
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/config/PmsDataPermissionConfiguration.java

@@ -1,6 +1,10 @@
 package cn.iocoder.yudao.module.pms.config;
 
+import cn.iocoder.yudao.framework.datapermission.core.config.CombinedPermissionConfig;
+import cn.iocoder.yudao.framework.datapermission.core.config.DevicePermissionConfig;
 import cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataPermissionRuleCustomizer;
+import cn.iocoder.yudao.framework.datapermission.core.rule.device.DevicePersonDataPermissionRule;
+import cn.iocoder.yudao.module.pms.api.permission.DevicePersonPermissionApi;
 import cn.iocoder.yudao.module.pms.dal.dataobject.IotDeviceDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.IotInfoDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.IotTreeDO;
@@ -52,11 +56,42 @@ public class PmsDataPermissionConfiguration {
             rule.addDeptColumn(IotMaintenancePlanDO.class, "dept_id");
             rule.addDeptColumn(IotSapStockDO.class, "dept_id");
             // user
-//            rule.addUserColumn(SupplierDO.class);
             rule.addUserColumn(AdminUserDO.class, "id");
-//            rule.addUserColumn(SupplierDO.class, "creator");
-
         };
     }
 
+    @Bean
+    public DevicePersonDataPermissionRule devicePersonDataPermissionRule(
+            DevicePersonPermissionApi devicePersonService,
+            DevicePermissionConfig devicePermissionConfig) {
+        return new DevicePersonDataPermissionRule(devicePersonService, devicePermissionConfig);
+    }
+
+    @Bean
+    public DevicePermissionConfig devicePermissionConfig() {
+        DevicePermissionConfig config = new DevicePermissionConfig();
+
+        // 配置所有需要设备权限的表及其子表关系
+        config.addConfig(IotMainWorkOrderDO.class,
+                "rq_iot_main_work_order_bom",
+                "work_order_id",
+                "device_id");
+
+        // ... 配置其他表 ...
+
+        return config;
+    }
+
+    @Bean
+    public CombinedPermissionConfig combinedPermissionConfig(DevicePermissionConfig devicePermissionConfig) {
+        CombinedPermissionConfig config = new CombinedPermissionConfig();
+
+        // 自动添加所有配置了设备权限的表
+        for (Class<?> entity : devicePermissionConfig.getMainEntities()) {
+            config.addCombinedTable(entity);
+        }
+
+        return config;
+    }
+
 }