status: propose created: 2026-06-21 complexity: 🔴复杂
当前 ForgeAdmin 已经具备低代码应用中心、业务对象设计器、模型导入、发布运行和动态 CRUD 能力。用户希望在已有成熟第三方业务系统已长期生产运行的前提下,用 ForgeAdmin 承接新的低代码二开功能,两套系统平行运行,ForgeAdmin 尽量零侵入旧系统。
现有尝试的目标场景可以拆成三类:
/ai/crud 动态低代码应用:根据动态页面配置中的业务对象或数据模型,读写该业务对象绑定的数据源。forge-business 手写复杂业务模块:根据当前租户绑定的默认业务数据源执行复杂业务逻辑。application.yml 配置的主数据源。目前低代码模型导入已经可以从旧系统数据源读取表结构,但后续 DDL 同步和运行态 CRUD 仍使用 Forge 主数据源,导致:
forge_admin。forge_admin。本变更目标:
forge-business 手写业务模块可按“租户默认业务数据源”切换。| 术语 | 含义 | 数据来源 |
|---|---|---|
| 平台主数据源 | ForgeAdmin 自身运行库,保存系统用户、权限、菜单、低代码元数据等 | spring.datasource.dynamic.datasource.master |
| 数据源注册表 | Forge 管理的外部数据库连接配置 | gen_datasource |
| 低代码运行数据源 | 低代码业务对象发布后,动态 CRUD 实际读写的业务数据源 | ai_lowcode_model / ai_crud_config 绑定 |
| 租户默认业务数据源 | 手写 forge-business 模块按当前租户解析的默认业务库 |
sys_tenant 绑定 |
| 业务对象绑定数据源 | 某个低代码模型或对象自己的运行数据源,优先级高于租户默认业务数据源 | 模型/应用发布配置 |
本期必须完成:
forge-business 提供租户默认业务数据源解析能力和配置入口。本期不做:
AiCrudPage 和动态 CRUD 的产品形态。LowcodeModelImportService#previewDbTableModel 会根据请求的数据源读取表结构,并把数据源信息写入 LowcodeModelSchema.sourceTable:
forge-server/forge-framework/forge-plugin-parent/forge-plugin-generator/src/main/java/com/mdframe/forge/plugin/generator/service/lowcode/LowcodeModelImportService.javapreviewDbTableModelschema.setTableMode("EXISTING")schema.setTableName(context.table().getTableName())schema.setSourceTable(buildSourceTable(context.datasource(), context.table()))LowcodeSourceTableRef 已有字段:
datasourceIddatasourceCodedatasourceNamedbTypetableNametableComment结论:模型导入阶段已经具备“来自哪个数据源”的元数据基础,但后续发布和运行链路没有继续使用。
LowcodeDdlRepository 当前注入的是 Spring 主上下文中的 JdbcTemplate:
forge-server/forge-framework/forge-plugin-parent/forge-plugin-generator/src/main/java/com/mdframe/forge/plugin/generator/service/lowcode/LowcodeDdlRepository.javaprivate final JdbcTemplate jdbcTemplatetableExists、listColumns、listColumnMetadata、hasAutoIncrementPrimaryId、executeDdl这些方法都使用 DATABASE() 检查当前连接所在库,因此实际检查和执行的是 Forge 主数据源。
结论:即使模型来自旧系统数据源,在线建表、字段同步和发布校验仍会落到 forge_admin。
DynamicCrudRepository 当前注入的是单一 NamedParameterJdbcTemplate:
forge-server/forge-framework/forge-plugin-parent/forge-plugin-generator/src/main/java/com/mdframe/forge/plugin/generator/service/DynamicCrudRepository.javaprivate final NamedParameterJdbcTemplate namedJdbcTemplate关键方法:
selectPageselectByIdinsertReturningIdupdateByIddeleteByIdtableExistsgetColumnMappinggetTableColumns结论:动态 CRUD 的运行时数据读写没有数据源上下文,天然写入主数据源。
LowcodePublishService#publish 发布时调用:
ensureTableReady(modelSchema, dto)policyService.validatePublishedPolicies(modelSchema, ddlService.listColumns(modelSchema.getTableName()))ddlService.executeCreateTable(modelSchema)相关文件:
forge-server/forge-framework/forge-plugin-parent/forge-plugin-generator/src/main/java/com/mdframe/forge/plugin/generator/service/lowcode/LowcodePublishService.java结论:发布阶段只知道表名,不知道表在哪个数据源里;发布成功后的 ai_crud_config 也没有冗余运行数据源字段。
DynamicCrudRepository 会根据表字段自动追加部分条件和审计字段:
tenant_id,追加租户条件;如果存在 del_flag,追加逻辑删除条件。tenant_id、create_by、create_time、create_dept、update_by、update_time,自动填充。appendTenantCondition 直接追加 AND tenant_id = :tenantId,未先判断目标表是否存在 tenant_id。相关文件:
forge-server/forge-framework/forge-plugin-parent/forge-plugin-generator/src/main/java/com/mdframe/forge/plugin/generator/service/DynamicCrudRepository.javaappendBaseQueryConditions、appendTenantCondition、fillInsertAuditFields、fillUpdateAuditFields结论:旧系统表若没有 Forge 标准审计字段,部分操作会报错或无法满足零侵入诉求;需要显式建模租户、审计、逻辑删除策略。
DynamicDataSourceUtil 已支持按 GenDatasource 创建并缓存 Hikari 数据源:
forge-server/forge-framework/forge-plugin-parent/forge-plugin-generator/src/main/java/com/mdframe/forge/plugin/generator/util/DynamicDataSourceUtil.javagetConnection、testConnection、removeDataSource、clearAllGenDatasourceServiceImpl 已使用该工具读取指定数据源表结构:
selectDbTableListselectDbTableByNameselectDbTableColumnsByName结论:可复用现有数据源注册表和连接创建能力,但需要封装成 Spring 可注入的运行时 JDBC 模板提供器,避免业务代码散落静态工具调用。
SysTenant 当前保存租户名称、联系人、状态、主题配置等字段,没有业务数据源绑定字段:
forge-server/forge-framework/forge-plugin-parent/forge-plugin-system/src/main/java/com/mdframe/forge/plugin/system/entity/SysTenant.javaforge-server/forge-framework/forge-plugin-parent/forge-plugin-system/src/main/resources/mapper/SysTenantMapper.xml结论:forge-business 按租户默认业务数据源切换需要新增租户配置字段和解析器。
forge-starter-orm 已依赖:
com.baomidou:dynamic-datasource-spring-boot3-starter本地配置中主数据源为:
spring.datasource.dynamic.primary=masterspring.datasource.dynamic.datasource.master结论:工程基础已支持 dynamic-datasource 生态。低代码动态 CRUD 由于本身是动态 SQL/JdbcTemplate 场景,可以继续由低代码运行上下文选择目标 JDBC 模板;但 forge-business 手写业务模块不能采用独立 JDBC 模板路由,必须接入 dynamic-datasource,让 MyBatis-Plus Mapper/XML 在同一个 ORM 链路内完成数据源切换。
官方 dynamic-datasource 使用模型参考:https://baomidou.com/guides/dynamic-datasource/ 。本变更按以下边界设计:
spring.datasource.dynamic.primary=master。spring.datasource.dynamic.strict 和业务侧全局开关共同控制;业务模块默认要求解析不到租户数据源时回退 master,但解析到已禁用/不可用数据源时失败。gen_datasource 注册业务库;forge-business 只校验并切换租户表中配置的 dynamic-datasource dsKey,不再维护一套绕开 MyBatis-Plus 的 JdbcTemplate 路由。forge-business 多数据源实现偏离目标纠偏前 forge-business-core 已新增:
BusinessTenantDataSourceResolverBusinessDataSourceContextHolderBusinessDataSourceExecutorBusinessJdbcTemplateProvider现有实现存在三个关键问题:
BusinessJdbcTemplateProvider 复用低代码 RuntimeJdbcTemplateProvider,只能影响手写 JdbcTemplate/NamedParameterJdbcTemplate,不能影响业务侧 MyBatis-Plus Mapper/XML。后续 forge-business 真实复杂业务如果按项目规范走 Mapper/XML,仍会落到当前 dynamic-datasource 默认主库。BusinessDataSourceContextHolder 是独立 ThreadLocal,和 dynamic-datasource 的上下文不是同一个体系。即使 Service 外层设置了该上下文,MyBatis-Plus 获取连接时也不会读取它。结论:阶段五的 forge-business 方案需要纠偏。保留 sys_tenant.default_business_datasource_id/code 作为“租户选择哪个数据源”的配置,其中 default_business_datasource_code 必须对应 baomidou dynamic-datasource 已配置的数据源名称;新增全局开关、AOP/执行器和异步上下文传播,确保业务侧 ORM 层仍由 MyBatis-Plus 控制。
gen_datasource 增加运行用途配置,用于区分低代码运行、租户业务模块、开发者导入等场景。建议用途枚举:
| 编码 | 说明 |
|---|---|
LOWCODE_RUNTIME |
允许低代码应用运行时读写 |
TENANT_BUSINESS |
允许作为租户默认业务数据源 |
DEVELOPER_IMPORT |
仅用于开发者导入表结构或代码生成 |
BOTH |
兼容历史数据,可同时用于低代码和租户业务 |
建议运行控制字段:
| 字段 | 含义 |
|---|---|
usage_scope |
数据源用途范围 |
allow_runtime_write |
是否允许运行时写入 |
allow_runtime_ddl |
是否允许低代码执行 DDL |
readonly |
是否只读 |
risk_level |
风险等级,例如 LOW、MEDIUM、HIGH |
LowcodeModelSchema 增加运行数据源快照,避免数据源名称修改后历史模型显示不一致。建议模型协议增加:
{
"runtimeDatasource": {
"datasourceId": 1,
"datasourceCode": "legacy_crm",
"datasourceName": "旧CRM生产库",
"dbType": "MySQL",
"tableName": "crm_customer",
"tableMode": "EXISTING",
"allowDdl": false,
"allowWrite": true
},
"primaryKey": {
"field": "id",
"columnName": "id",
"dataType": "bigint",
"autoIncrement": true
},
"tenantStrategy": {
"mode": "FORGE_TENANT_ID",
"columnName": "tenant_id"
},
"auditStrategy": {
"mode": "FORGE_COLUMNS"
},
"logicDeleteStrategy": {
"mode": "DEL_FLAG",
"columnName": "del_flag",
"activeValue": "0",
"deletedValue": "1"
}
}
策略枚举:
| 策略 | 支持值 | 说明 |
|---|---|---|
tableMode |
CREATE、EXISTING |
CREATE 表示在绑定数据源建表;EXISTING 表示绑定旧表 |
tenantStrategy.mode |
FORGE_TENANT_ID、NONE、CUSTOM_COLUMN |
旧表无租户字段时必须配置 NONE 或自定义字段 |
auditStrategy.mode |
FORGE_COLUMNS、NONE、CUSTOM_MAPPING |
控制是否写入 create_by/update_time 等字段 |
logicDeleteStrategy.mode |
DEL_FLAG、NONE、CUSTOM_COLUMN |
控制删除方式 |
ai_crud_config。ai_crud_config 冗余保存运行数据源 ID、编码、快照、表名、主键策略、租户策略、审计策略、逻辑删除策略。ai_crud_config_version 同步保存上述运行时字段,回滚时按版本快照恢复。ai_crud_config 的运行数据源,不依赖浏览器传参。master,保持兼容。LowcodeDdlRepository 改造成按数据源执行,不再固定使用主库 JdbcTemplate。LowcodeDdlService.previewCreateTable、executeCreateTable、tableExists、listColumns、hasAutoIncrementPrimaryId 都必须接收运行数据源上下文。LowcodePublishService#ensureTableReady 基于模型绑定数据源校验表结构。policyService.validatePublishedPolicies 使用绑定数据源列清单校验权限字段。tableMode=CREATE 且数据源 allow_runtime_ddl=1 时执行。发布部署模式建议:
| 模式 | 说明 |
|---|---|
VERIFY_ONLY |
只校验绑定数据源中表结构是否满足运行条件 |
ONLINE_CREATE_TABLE |
在绑定数据源中新建 Forge 托管表 |
SYNC_EXISTING_TABLE |
对已有表追加缺失字段或索引,必须二次确认 |
SKIP_DDL |
跳过 DDL,但仍做基础表存在校验 |
LowcodeRuntimeDataSourceResolver,根据 configKey 或 AiCrudConfig 解析运行数据源。RuntimeJdbcTemplateProvider,根据数据源 ID 返回 JdbcTemplate 和 NamedParameterJdbcTemplate。DynamicCrudRepository 所有查询、写入、元数据读取方法都必须使用运行数据源对应的 JDBC 模板。DynamicCrudService 读取配置后构建 DynamicCrudRuntimeContext,包含数据源、表名、主键、租户、审计、逻辑删除策略。tableName 改为 datasourceId + schemaName + tableName,避免不同库同名表污染。id。AiCrudPage 运行配置下发 rowKey,表格详情、编辑、删除按配置主键取值。/{id} 路径参数改为字符串接收,按主键类型转换。tenant_id 时,tenantStrategy=NONE 不追加租户条件。del_flag 时,logicDeleteStrategy=NONE 执行物理删除;若数据源标记高风险,默认禁止物理删除。auditStrategy=NONE 不写入 Forge 审计字段。forge-business + MyBatis-Plus)阶段五改造目标调整为:forge-business 业务侧仍按项目标准使用 MyBatis-Plus Mapper/XML,不引导业务模块改用 JdbcTemplate。数据源切换由 baomidou dynamic-datasource 接管,业务代码只声明“当前方法需要进入租户默认业务数据源”。
forge.business.datasource.enabled 配置项,默认 false,用于灰度控制租户业务数据源路由能力。business.datasource.tenant-routing-enabled,运行期可关闭租户业务数据源路由;关闭后即使租户配置了默认业务数据源,forge-business 显式声明方法也必须走 master。sys_tenant.default_business_datasource_id / default_business_datasource_code,只负责记录“当前租户默认使用哪个业务数据源”。TenantBusinessDataSourceResolver,按 TenantContextHolder / SessionHelper 获取当前租户,读取 sys_tenant.default_business_datasource_code。default_business_datasource_code 只表示业务租户要使用的 dynamic-datasource dsKey,不复用低代码 gen_datasource 连接配置。DynamicRoutingDataSource#getDataSources();不存在时抛业务异常,避免静默写错库。master;租户配置了未注册的 dsKey 时失败。@TenantBusinessDataSource 注解,建议标在 forge-business Service 方法或类上,不建议标在 Mapper 上。push(dsKey);finally 中必须 poll() 清理,避免线程复用串库。@Transactional,确保事务开启前 dynamic-datasource 已经选中正确数据源。BusinessJdbcTemplateProvider 执行主路径。建议开发模型:
@TenantBusinessDataSource
public Page<CustomerVO> selectCustomerPage(Page<CustomerVO> page, CustomerQuery query) {
return customerMapper.selectCustomerPage(page, query);
}
或用于需要显式指定租户的后台任务:
tenantBusinessDataSourceExecutor.execute(tenantId, () -> {
return customerMapper.selectCustomerPage(page, query);
});
@Async、线程池、事件监听、定时任务必须显式传播租户和数据源上下文。TenantBusinessDataSourceTaskDecorator,在提交任务时捕获 tenantId 和当前 dynamic-datasource key,在异步线程执行前重新设置,finally 中清理。forge.business.datasource.enabled=true 时对系统级线程池统一配置 TaskDecorator;对手动创建的异步任务必须使用 TenantBusinessDataSourceExecutor.execute(tenantId, action),禁止直接调用业务 Mapper。tenantId;缺少租户上下文时只允许走 master 或直接失败,不能猜测租户。BusinessJdbcTemplateProvider 作为业务模块主路径;如保留,只能用于少量非 Mapper 的工具型 SQL,并且必须显式标注不参与业务 ORM 主链路。BusinessDataSourceContextHolder 不再作为最终数据源选择依据;最终连接选择必须进入 dynamic-datasource 上下文。SysTenantBusinessDataSourceResolver 不再返回低代码 LowcodeRuntimeDataSourceContext,应返回业务数据源 key、数据源摘要和开关状态。lowcode-models 和 app-center/object-designer 的 DDL 预览调用目标数据源检查接口。ai:lowcode:deploy-ddl,并增加数据源级 allow_runtime_ddl 校验。/ai/crud/{configKey} 的数据源只由已发布配置决定,前端不能传入或覆盖数据源。forge-business 手写业务模块只有显式声明时才按租户默认业务数据源切换。forge-business 手写业务模块解析不到租户默认业务数据源时,必须回退平台主数据源 master。forge-business 租户业务数据源路由受全局开关控制;开关关闭时所有业务模块显式声明都必须降级到 master。forge-business 业务侧 ORM 主路径必须使用 MyBatis-Plus Mapper/XML,数据源选择必须进入 dynamic-datasource 上下文;禁止把 BusinessJdbcTemplateProvider 当作复杂业务模块的主访问方式。@TenantBusinessDataSource 切面必须早于事务切面执行;已经开启主库事务的方法不得中途切换到租户业务库。tenantId 和 dynamic-datasource key;没有租户上下文的异步业务 Mapper 调用禁止默认猜测数据源。NONE 或自定义映射,不能默认追加字段条件。| 操作 | 表名 | 字段/索引 | 说明 |
|---|---|---|---|
| 修改 | gen_datasource |
usage_scope varchar(32) DEFAULT 'BOTH' |
数据源用途范围 |
| 修改 | gen_datasource |
allow_runtime_write tinyint DEFAULT 1 |
是否允许低代码运行写入 |
| 修改 | gen_datasource |
allow_runtime_ddl tinyint DEFAULT 0 |
是否允许低代码 DDL |
| 修改 | gen_datasource |
readonly tinyint DEFAULT 0 |
是否只读 |
| 修改 | gen_datasource |
risk_level varchar(16) DEFAULT 'MEDIUM' |
风险等级 |
| 修改 | ai_lowcode_model |
runtime_datasource_id bigint DEFAULT NULL |
模型运行数据源 ID,便于查询筛选 |
| 修改 | ai_lowcode_model |
runtime_datasource_code varchar(64) DEFAULT NULL |
模型运行数据源编码 |
| 修改 | ai_lowcode_model |
runtime_table_name varchar(128) DEFAULT NULL |
模型运行表名 |
| 修改 | ai_lowcode_model |
table_mode varchar(16) DEFAULT NULL |
CREATE / EXISTING |
| 修改 | ai_lowcode_model |
idx_lowcode_model_runtime_ds(tenant_id, runtime_datasource_id, status) |
按数据源筛选模型 |
| 修改 | ai_crud_config |
runtime_datasource_id bigint DEFAULT NULL |
已发布运行配置实际数据源 ID |
| 修改 | ai_crud_config |
runtime_datasource_code varchar(64) DEFAULT NULL |
已发布运行配置实际数据源编码 |
| 修改 | ai_crud_config |
runtime_datasource_snapshot json DEFAULT NULL |
发布时数据源快照,不含密码 |
| 修改 | ai_crud_config |
runtime_table_name varchar(128) DEFAULT NULL |
运行表名 |
| 修改 | ai_crud_config |
primary_key_field varchar(64) DEFAULT 'id' |
主键字段名 |
| 修改 | ai_crud_config |
primary_key_column varchar(64) DEFAULT 'id' |
主键列名 |
| 修改 | ai_crud_config |
primary_key_type varchar(32) DEFAULT 'bigint' |
主键类型 |
| 修改 | ai_crud_config |
tenant_strategy json DEFAULT NULL |
租户隔离策略 |
| 修改 | ai_crud_config |
audit_strategy json DEFAULT NULL |
审计字段策略 |
| 修改 | ai_crud_config |
logic_delete_strategy json DEFAULT NULL |
逻辑删除策略 |
| 修改 | ai_crud_config |
idx_ai_crud_runtime_ds(tenant_id, runtime_datasource_id, publish_status) |
按运行数据源筛选配置 |
| 修改 | ai_crud_config_version |
同步 ai_crud_config 运行数据源相关字段 |
发布版本和回滚保持一致 |
| 修改 | sys_tenant |
default_business_datasource_id bigint DEFAULT NULL |
租户默认业务数据源 |
| 修改 | sys_tenant |
default_business_datasource_code varchar(64) DEFAULT NULL |
租户默认业务数据源编码 |
| 新增 | sys_config |
business.datasource.tenant-routing-enabled=false |
租户业务数据源全局开关,关闭时 forge-business 统一走 master |
| 修改 | sys_resource |
新增或补齐权限资源 | 运行数据源绑定、租户业务数据源配置 |
数据迁移规则:
gen_datasource 默认 usage_scope='BOTH',避免升级后列表为空。model_schema.sourceTable.datasourceId 存在,则回填 ai_lowcode_model.runtime_datasource_id。ai_crud_config 如果 model_schema.sourceTable.datasourceId 存在,可选择迁移为运行数据源;默认不自动改变运行库,避免历史应用行为变化。ai_crud_config 无运行数据源时,运行时按主数据源处理。information_schema 防重复,内置数据 tenant_id=1。| 操作 | 接口 | 方法 | 变更内容 |
|---|---|---|---|
| 修改 | /generator/datasource/list |
GET | 返回用途、是否只读、是否允许写入、是否允许 DDL、风险等级 |
| 修改 | /generator/datasource/enabled |
GET | 支持 usageScope 参数 |
| 修改 | /generator/datasource/add |
POST | 保存数据源用途和运行控制字段 |
| 修改 | /generator/datasource/edit |
POST | 修改后清理连接池和元数据缓存 |
| 新增 | /generator/datasource/{id}/runtime-capability |
GET | 返回低代码运行能力摘要 |
| 操作 | 接口 | 方法 | 变更内容 |
|---|---|---|---|
| 修改 | /ai/lowcode/model/preview-db-table |
POST | 返回运行数据源快照、主键、租户/审计/删除策略建议 |
| 修改 | /ai/lowcode/model/import-db-table |
POST | 保存运行数据源字段 |
| 修改 | /ai/lowcode/model |
POST/PUT | 支持空白模型选择运行数据源 |
| 修改 | /ai/lowcode/model/{id} |
GET | 返回运行数据源和表结构就绪摘要 |
| 新增 | /ai/lowcode/model/{id}/ddl/preview |
GET/POST | 基于模型绑定数据源预览 DDL |
| 新增 | /ai/lowcode/model/{id}/datasource/check |
GET | 检查数据源可用性、表、主键、字段策略 |
| 操作 | 接口 | 方法 | 变更内容 |
|---|---|---|---|
| 修改 | /ai/lowcode/app/{id}/publish |
POST | 发布时写入运行数据源冗余字段;部署模式按绑定数据源执行 |
| 修改 | /ai/lowcode/app/{id} |
GET | 返回运行数据源快照、发布数据源风险 |
| 修改 | /ai/lowcode/app/{id}/versions |
GET | 版本返回运行数据源摘要 |
| 修改 | /ai/lowcode/app/{id}/rollback/{versionId} |
POST | 回滚恢复版本中的运行数据源字段 |
| 操作 | 接口 | 方法 | 变更内容 |
|---|---|---|---|
| 修改 | /ai/crud/{configKey}/page |
GET | 后端按配置解析运行数据源;前端无感 |
| 修改 | /ai/crud/{configKey}/{id} |
GET | id 按配置主键类型解析,不再强制 Long |
| 修改 | /ai/crud/{configKey} |
POST | 写入绑定数据源,按审计策略填充字段 |
| 修改 | /ai/crud/{configKey} |
PUT | 更新绑定数据源,按主键策略定位记录 |
| 修改 | /ai/crud/{configKey}/{id} |
DELETE | 删除绑定数据源记录,按删除策略执行 |
| 修改 | /ai/crud/{configKey}/import |
POST | 导入写入绑定数据源 |
| 修改 | /ai/crud/{configKey}/export |
POST | 导出读取绑定数据源 |
| 新增 | /ai/crud/{configKey}/runtime-info |
GET | 返回运行表、主键、只读、可写、导入导出能力,不返回敏感连接信息 |
| 操作 | 接口 | 方法 | 变更内容 |
|---|---|---|---|
| 修改 | /system/tenant/page |
GET | 返回默认业务数据源摘要 |
| 修改 | /system/tenant/{id} |
GET | 返回默认业务数据源字段 |
| 修改 | /system/tenant |
POST/PUT | 支持保存默认业务数据源 |
| 新增 | /system/tenant/{id}/business-datasource/check |
GET | 检查租户默认业务数据源可用性 |
| 新增 | /system/tenant/business-datasource/config |
GET/PUT | 查询和修改租户业务数据源全局开关 |
forge-plugin-generator
LowcodeModelImportServiceLowcodeDataModelServiceLowcodeDdlServiceLowcodeDdlRepositoryLowcodePublishServiceLowcodeRuntimeConfigBuilderDynamicCrudControllerDynamicCrudServiceDynamicCrudRepositoryDynamicCrudExcelServiceBusinessObjectReadinessServiceforge-plugin-system
SysTenantSysTenantDTOSysTenantMapper.xmlSysTenantServiceImplSysTenantBusinessDataSourceResolverforge-starter-tenant
@TenantBusinessDataSourceTenantBusinessDataSourceInfoBusinessDataSourcePropertiesTenantBusinessDataSourceAspectTenantBusinessDataSourceResolverTenantBusinessDataSourceExecutorTenantBusinessDataSourceTaskDecoratorTaskDecorator 后处理器forge-business-core
business/datasource-demo 后端验证用例BusinessJdbcTemplateProvider、BusinessDataSourceContextHolder、旧 BusinessDataSourceExecutorforge-admin-ui/src/views/generator/datasource.vueforge-admin-ui/src/views/ai/lowcode-models.vueforge-admin-ui/src/views/ai/lowcode-apps.vueforge-admin-ui/src/views/ai/lowcode-builder.vueforge-admin-ui/src/views/app-center/components/BusinessObjectWizardDrawer.vueforge-admin-ui/src/views/app-center/object.[objectCode].vueforge-admin-ui/src/views/app-center/components/designer/BusinessObjectDesignerShell.vueforge-admin-ui/src/views/system/tenant.vueforge-admin-ui/src/api/lowcode-crud.jsforge-server/db/migration/。gen_datasource、ai_lowcode_model、ai_crud_config 数据。configKey、数据源编码和表名。风险:低代码应用发布或运行时误写旧系统生产表。
控制:
风险:旧表无 id、tenant_id、del_flag、审计字段,导致查询或更新失败。
控制:
风险:业务表写入外部库成功,但平台事件、触发器、消息记录失败。
控制:
风险:不同数据源存在同名表,当前按表名缓存会串数据。
控制:
风险:旧系统表无租户字段,Forge 租户切换后看到同一外部表全量数据。
控制:
tenantStrategy=NONE 必须在发布检查中提示风险。CUSTOM_COLUMN 映射旧系统租户字段。风险:当前 DDL 和元数据查询主要按 MySQL 编写,但首期需要支持 MySQL、PostgreSQL、Oracle,分页、主键回填、表/字段元数据和 DDL 语法存在差异。
控制:
RuntimeDatabaseDialect 适配层,按 GenDatasource.dbType 选择 MySQL、PostgreSQL、Oracle 实现。本变更涉及数据源绑定、DDL、租户默认业务数据源配置,属于权限敏感能力。
控制:
forge-business ORM 路由失效风险:业务侧代码按规范使用 MyBatis-Plus Mapper/XML,但数据源切换只发生在自定义 JDBC 模板或独立 ThreadLocal 中,导致 Mapper 仍访问 master。
控制:
forge-business 主路径必须通过 dynamic-datasource 选择数据源,不再依赖低代码 RuntimeJdbcTemplateProvider。@TenantBusinessDataSource 切面必须在事务开启前 push(dsKey),并在 finally 中 poll()。风险:@Async、线程池、定时任务或事件监听中没有登录态和 ThreadLocal,导致业务 Mapper 回退主库或串到上一次线程残留数据源。
控制:
TenantBusinessDataSourceTaskDecorator。tenantId,由执行器重新解析并设置 dynamic-datasource key。forge-business 单元测试:全局开关、租户默认业务数据源解析、用途校验、dynamic-datasource key 生成、开关关闭回退 master。tenant_id 表更新删除不追加租户条件。forge-business 集成测试:Service 标注 @TenantBusinessDataSource 后,MyBatis-Plus Mapper/XML 查询命中租户默认业务库;同一 Mapper 在不同租户下命中不同库。@Async/线程池任务显式传入租户后能命中租户业务库,任务结束后 dynamic-datasource 上下文清理干净。/ai/crud 按绑定数据源读写。FORGE_TENANT_ID、NONE、DEL_FLAG、NONE 删除策略。/test 前需要新增 test-spec.md,并按 code-copilot/rules/automated-testing-standard.md 记录执行日志。建议验证命令:
cd forge-server && mvn -pl forge-framework/forge-plugin-parent/forge-plugin-generator -am test
cd forge-server && mvn -pl forge-framework/forge-plugin-parent/forge-plugin-system -am test
cd forge-server && mvn -pl forge-business/forge-business-core -am test
cd forge-admin-ui && pnpm exec eslint src/views/generator/datasource.vue src/views/system/tenant.vue src/views/app-center/components/BusinessObjectWizardDrawer.vue
cd forge-admin-ui && pnpm build
id 主键表的可写 CRUD:支持单字段自定义主键;复合主键第一阶段不支持可写动态 CRUD。forge-business 模块统一回退平台主数据源 master。forge-business 多数据源必须基于 dynamic-datasource + MyBatis-Plus ORM 链路,不能以 JdbcTemplate 路由作为主方案。当前无待澄清项。
ai_crud_config,运行时只读发布配置。NamedParameterJdbcTemplate”改为“运行上下文驱动的 JDBC 模板”。forge-business 手写模块,未配置时回退主数据源 master。forge-business 业务侧 ORM 主路径必须使用 dynamic-datasource 选择数据源,让 MyBatis-Plus Mapper/XML 透明落到租户业务库;BusinessJdbcTemplateProvider 不作为复杂业务模块主链路。master。@TenantBusinessDataSource 切面必须先于事务切面执行;异步任务必须通过 TaskDecorator 或显式执行器传播租户和数据源上下文。| Task | 状态 | 实际改动文件 | 备注 |
|---|---|---|---|
| Spec | completed | code-copilot/changes/lowcode-runtime-datasource-isolation/spec.md |
初版需求和技术方案 |
| Clarification | completed | code-copilot/changes/lowcode-runtime-datasource-isolation/spec.md |
补充自定义主键、主库回退、高风险只读、MySQL/PostgreSQL/Oracle 支持范围 |
| Runtime datasource adaptation | completed | forge-plugin-generator、forge-plugin-system、forge-business-core、forge-admin-ui、forge-server/db/migration/V1.0.75__add_lowcode_runtime_datasource_fields.sql |
已完成元数据、DDL、动态 CRUD、租户默认业务数据源、触发器/公式外围链路和发布目标库展示;流程审批 Long recordId 与多库集成测试后续单独处理 |
| Business datasource correction | implemented | forge-starter-tenant、forge-plugin-system、forge-business-core、forge-server/db/migration/V1.0.76__add_business_datasource_routing_config.sql、application.yml、spec.md、tasks.md、test-spec.md |
已落地 dynamic-datasource + MyBatis-Plus 主链路、全局开关、通用注解/AOP/显式执行器/异步上下文传播、系统侧租户 resolver 和 business/datasource-demo 验证用例;业务侧只选择已配置 dsKey,不监听或注册低代码 gen_datasource;真实多库 Mapper/XML 集成测试后续补齐 |
待 review。