status: apply created: 2026-05-24 complexity: 🔴复杂
动态生成页面当前已经支持单表、树形单表、左树右表、自定义查询、导入导出等能力,但运行时数据查询主要走 NamedParameterJdbcTemplate,不会触发系统现有基于 MyBatis Mapper 的 DataScopeInterceptor。这会导致低代码页面即使有租户隔离,也不能自动跟随当前系统角色的数据范围。
目标:
forge/forge-framework/forge-starter-parent/forge-starter-datascope/src/main/java/com/mdframe/forge/starter/datascope/handler/DataScopeInterceptor.java:当前数据权限核心拦截器,只在 MyBatis beforeQuery 中根据 mapperId 查 sys_data_scope_config 并改写 SQL。forge/forge-framework/forge-starter-parent/forge-starter-datascope/src/main/java/com/mdframe/forge/starter/datascope/service/impl/DataScopeServiceImpl.java:通过当前登录用户、角色、组织、自定义组织、行政区划构建 DataScopeContext。forge/forge-framework/forge-starter-parent/forge-starter-datascope/src/main/java/com/mdframe/forge/starter/datascope/enums/DataScopeType.java:定义 ALL/SELF/ORG/ORG_AND_CHILD/CUSTOM/TENANT_ALL/REGION 等后端可执行数据范围。forge/forge-framework/forge-starter-parent/forge-starter-datascope/src/main/java/com/mdframe/forge/starter/datascope/entity/SysDataScopeConfig.java:Mapper 级权限字段配置,包含用户字段、组织字段、租户字段、区划字段和表别名。forge-admin-ui/src/views/system/dataScopeConfig.vue:系统侧数据权限配置管理页面,面向 Mapper 方法,不适合直接配置每个动态 CRUD 页面。forge/forge-framework/forge-plugin-parent/forge-plugin-generator/src/main/java/com/mdframe/forge/plugin/generator/controller/DynamicCrudController.java:动态页面统一入口 /ai/crud/{configKey},包含分页、树、详情、新增、修改、删除、导入、导出。forge/forge-framework/forge-plugin-parent/forge-plugin-generator/src/main/java/com/mdframe/forge/plugin/generator/service/DynamicCrudService.java:动态 CRUD 业务编排,负责读取配置、字段白名单、查询条件扩展、读链路解密/翻译/脱敏。forge/forge-framework/forge-plugin-parent/forge-plugin-generator/src/main/java/com/mdframe/forge/plugin/generator/service/DynamicCrudRepository.java:动态 SQL 仓储,使用 NamedParameterJdbcTemplate,当前 appendBaseQueryConditions 只追加租户和逻辑删除条件。forge/forge-framework/forge-plugin-parent/forge-plugin-generator/src/main/java/com/mdframe/forge/plugin/generator/service/DynamicCrudExcelService.java:导出通过 DynamicCrudService#selectExportRows 获取数据,后续只要查询链路统一加权限,导出可复用。forge-admin-ui/src/views/ai/crud-page.vue:动态渲染页,根据 configKey 拉取运行时配置并渲染 AiCrudPage 或页面模板。forge/forge-framework/forge-plugin-parent/forge-plugin-generator/src/main/java/com/mdframe/forge/plugin/generator/dto/lowcode/LowcodeModelSchema.java:模型协议已有 policies 字段,可继续承载数据权限策略。forge/forge-framework/forge-plugin-parent/forge-plugin-generator/src/main/java/com/mdframe/forge/plugin/generator/dto/lowcode/LowcodePolicySchema.java:当前只有 dataScope/regionField/tenantField/logicDeleteField 等基础字段,缺少用户字段、组织字段、自定义组织字段映射。forge-admin-ui/src/components/lowcode-builder/model/LowcodeModelDesigner.vue:模型设计器已有“数据范围”配置,但当前选项只有 TENANT/REGION/DEPT,不能表达“跟随系统角色数据权限”。forge-admin-ui/src/components/lowcode-builder/model/model-schema.js:默认模型已内置 tenantId/createBy/createDept 等系统字段,适合用作数据权限默认字段映射。sys_data_scope_config 里配置 mapper_method 不会对 /ai/crud/{configKey} 生效。DynamicCrudRepository 内部兜底,但没有组织、本人、行政区划范围过滤。DataScopeType 存在口径不一致迹象,动态适配层不能直接散落使用数字常量,必须先归一化为可执行范围。IDataScopeService#getCurrentUserDataScope() 获取当前登录人的系统数据权限上下文。LowcodePolicySchema,支持 SYSTEM_DATA_SCOPE 或 FOLLOW_SYSTEM 策略,并配置 userField/orgField/regionField/tenantField 等字段映射。createBy/create_by、orgId/org_id、createDept/create_dept、tenantId/tenant_id。TENANT:只做租户隔离和逻辑删除过滤,兼容历史配置。FOLLOW_SYSTEM 后,数据范围由当前用户角色决定,页面配置只负责字段映射。ALL 不追加组织/本人/区划限制,但仍保留租户拦截体系已有规则。tenantField。userField,默认 create_by。orgField,优先业务归属组织字段 org_id;未配置时降级为 create_dept。regionField,规则与现有系统保持一致:省级视为全部,市级及以下匹配本级及下级区划。FOLLOW_SYSTEM 发布或运行时应明确报错,不能自动放大为全部数据。| 操作 | 表名/协议 | 字段/索引 | 说明 |
|---|---|---|---|
| 修改 | model_schema.policies |
dataScope |
增加 FOLLOW_SYSTEM 或 SYSTEM_DATA_SCOPE,表示跟随系统角色数据权限 |
| 修改 | model_schema.policies |
userField/userColumn |
本人数据范围字段映射,默认 createBy/create_by |
| 修改 | model_schema.policies |
orgField/orgColumn |
组织数据范围字段映射,默认优先 orgId/org_id,否则 createDept/create_dept |
| 修改 | model_schema.policies |
regionField/regionColumn |
行政区划数据范围字段映射 |
| 修改 | model_schema.policies |
tenantField/tenantColumn |
租户字段映射,默认 tenantId/tenant_id |
| 不新增 | 业务表 | - | 第一阶段不新增系统表;权限策略随低代码模型协议保存 |
| 操作 | 接口 | 方法 | 变更内容 |
|---|---|---|---|
| 修改 | /ai/lowcode/app/draft |
POST | 保存草稿时接收扩展后的 modelSchema.policies |
| 修改 | /ai/lowcode/app/{id}/publish |
POST | 发布前校验 FOLLOW_SYSTEM 所需字段映射是否存在于模型字段或真实表列 |
| 修改 | /ai/crud/{configKey}/page |
GET | 查询时自动追加动态数据权限条件 |
| 修改 | /ai/crud/{configKey}/tree |
GET | 树形数据查询自动追加动态数据权限条件 |
| 修改 | /ai/crud/{configKey}/{id} |
GET | 详情查询自动追加动态数据权限条件 |
| 修改 | /ai/crud/{configKey} |
PUT | 更新时自动追加动态数据权限条件 |
| 修改 | /ai/crud/{configKey}/{id} |
DELETE | 删除时自动追加动态数据权限条件 |
| 修改 | /ai/crud/{configKey}/export |
POST | 导出时复用带权限的动态查询 |
| 修改 | /ai/custom-query/{configKey}/execute |
POST | 自定义查询执行自动追加动态数据权限条件 |
forge-plugin-generator:低代码模型协议、运行时配置构建、动态 CRUD Service/Repository、动态导出、自定义查询。forge-starter-datascope:复用 IDataScopeService 和 DataScopeContext;如角色数据范围编码不一致,需要增加兼容归一化方法或修正枚举/字典口径。forge-admin-ui:低代码模型设计器、默认模型协议、动态页面渲染说明、组织树查询条件。TENANT 保持兼容;启用 FOLLOW_SYSTEM 的页面开始按角色数据权限收敛数据。⚠️ 涉及权限变更,必须后端强制执行并重点回归。
ALL/TENANT_ALL/SELF/ORG/ORG_AND_CHILD/CUSTOM/REGION 至少覆盖主要 SQL 生成分支;写操作必须覆盖无权限场景。code-copilot/changes/dynamic-lowcode-data-scope/test-spec.md。JAVA_HOME=/opt/homebrew/Cellar/openjdk@17/17.0.13/libexec/openjdk.jdk/Contents/Home mvn -pl forge-admin-server -am compile -DskipTestssource ~/.nvm/nvm.sh && nvm use v20.19.0 && pnpm buildsource ~/.nvm/nvm.sh && nvm use v20.19.0 && pnpm exec eslint src/components/ai-form/AiCrudPage.vue src/components/lowcode-builder/model/model-schema.js src/components/lowcode-builder/model/LowcodeModelDesigner.vue src/views/ai/lowcode-builder.vue src/views/ai/lowcode-models.vue// 注释、动态导入提示和 chunk-size warning。1全部/2本租户/3本组织/4本组织及子组织/5个人/7行政区划;同时在 DataScopeType#getByRoleDataScope 兼容旧后端枚举 5自定义/6本租户,避免升级后历史角色被错误解释。sys_role_data_scope。当角色编码为 5 且存在自定义组织 ID 时按 CUSTOM 执行;否则按当前字典的“个人”执行。本次不新增独立低代码角色授权入口。_scopeAncestor=true 和 _dataScopeWritable=false 标识;前端行操作拦截编辑、删除、添加下级,后端写接口仍以动态数据权限条件强制校验。DataScopeInterceptor 处理动态 CRUD,因为动态 CRUD 使用 NamedParameterJdbcTemplate,不会触发 MyBatis Mapper 拦截。t0 追加主业务表权限条件。TENANT 兼容历史页面,只有明确选择 FOLLOW_SYSTEM 的模型才启用系统角色数据权限。FOLLOW_SYSTEM 作为低代码协议标准值,历史或外部传入的 SYSTEM_DATA_SCOPE 在保存、发布和运行时归一化为 FOLLOW_SYSTEM。DataScopeType#getByRoleDataScope 归一化,动态低代码和原 MyBatis 数据权限拦截器共用同一套解释逻辑。| Task | 状态 | 实际改动文件 | 备注 |
|---|---|---|---|
| Spec 确认 | done | code-copilot/changes/dynamic-lowcode-data-scope/spec.md |
角色数据范围编码按当前字典为主、旧枚举兼容处理 |
| Task 1: 策略协议与默认值归一化 | done | LowcodePolicySchema.java, LowcodePolicyService.java, LowcodeDataModelService.java, LowcodeAppService.java, LowcodePublishService.java, LowcodeRuntimeConfigBuilder.java, model-schema.js, lowcode-builder.vue, lowcode-models.vue |
统一 policies 默认值,SYSTEM_DATA_SCOPE 归一化为 FOLLOW_SYSTEM |
| Task 2: 发布校验 | done | LowcodePublishService.java, LowcodeDdlService.java, LowcodePolicyService.java |
发布时通过真实表列校验用户、组织、区划、租户字段映射 |
| Task 3: 角色数据范围编码兼容 | done | DataScopeType.java, DataScopeInterceptor.java |
当前角色字典与旧后端枚举统一归一化,避免历史角色升级后失真 |
| Task 4: 动态运行时数据权限 | done | DynamicDataScopeService.java, DynamicCrudRepository.java, DynamicCrudService.java, forge-plugin-generator/pom.xml |
分页、Join、自定义查询、树、详情、导出、编辑、删除统一追加参数化权限条件 |
| Task 5: 树形与组织筛选收敛 | done | DynamicCrudService.java, DynamicCrudRepository.java, CustomQueryConditionDTO.java, LowcodeTreeConfig.java |
支持选父级查子级并与系统权限取交集;补齐祖先节点但标记只读 |
| Task 6: 前端配置与运行态保护 | done | LowcodeModelDesigner.vue, model-schema.js, AiCrudPage.vue, AiCustomQuery.vue, crud-page.vue |
设计器支持跟随系统角色字段映射;运行态阻断补齐祖先节点的写操作 |
| Task 7: 验证 | done | test-spec.md、后端 Maven 编译、前端 build、前端定向 eslint |
补充权限矩阵和接口验收用例;编译/构建通过;前端 build 存在既有 warning,不阻断产物生成 |
编码实现已完成,核心权限条件由后端强制追加,写接口已纳入权限校验,默认 TENANT 策略保持历史低代码页面兼容。当前结论为可进入 Review 和角色矩阵接口验收;仍需在具备真实角色、组织、区划和业务数据的环境中完成接口回归。
sys_role_data_scope,不新增独立低代码角色权限体系。