From d26d2b14a542fca196bf7dad908eb9ab0699e35d Mon Sep 17 00:00:00 2001 From: chenchuchuan <13554600537@163.com> Date: Fri, 24 Jul 2026 10:00:30 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(online):=20=E6=B7=BB=E5=8A=A0=E4=BB=8E?= =?UTF-8?q?=E8=A1=A8=E5=85=B3=E8=81=94=E5=AD=97=E6=AE=B5=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=92=8C=E8=A1=A8=E5=8D=95=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在OnlCgreportItem模型中新增isSelectShow字段用于控制下拉显示 - 在OnlineFormController中实现主表/从表已有字段的refPropert同步更新逻辑 - 修改saveField方法以支持refPropert参数传递 - 新增mapFieldNameToWidget方法构建字段名到组件的映射关系 - 添加queryFormByType接口按表单类型查询表单列表 - 实现OnlineFormService的queryListByFormType方法和OnlinePageService的queryByEnableList方法 - 添加Guava Lists依赖并优化相关工具类导入 --- .../common/generator/model/OnlCgreportItem.java | 5 + .../online/controller/OnlineFormController.java | 110 ++++++++++++++++++++- .../common/online/service/OnlineFormService.java | 8 ++ .../common/online/service/OnlinePageService.java | 7 ++ .../online/service/impl/OnlineFormServiceImpl.java | 20 +++- .../online/service/impl/OnlinePageServiceImpl.java | 5 + 6 files changed, 150 insertions(+), 5 deletions(-) diff --git a/common/common-generator/src/main/java/apelet/common/generator/model/OnlCgreportItem.java b/common/common-generator/src/main/java/apelet/common/generator/model/OnlCgreportItem.java index 1b8a0e7..7dd8ca1 100644 --- a/common/common-generator/src/main/java/apelet/common/generator/model/OnlCgreportItem.java +++ b/common/common-generator/src/main/java/apelet/common/generator/model/OnlCgreportItem.java @@ -56,6 +56,11 @@ public class OnlCgreportItem extends BaseModel implements Serializable { @Schema(description = "是否显示 0否,1显示") private Integer isShow; + + @Schema(description = "是否下拉显示 0否,1显示") + private Integer isSelectShow; + + @Schema(description = "排序") private Integer orderNum; diff --git a/common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java b/common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java index f450dbd..622f04f 100644 --- a/common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java +++ b/common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java @@ -40,6 +40,7 @@ import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.github.pagehelper.page.PageMethod; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import com.google.common.collect.Lists; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -735,7 +736,23 @@ public class OnlineFormController { .map(OnlFormField::getFieldName).collect(Collectors.toSet()); Set newColumnNames = new HashSet<>(widgetColumnNames); newColumnNames.removeAll(existingFieldNames); - + // 主表/单表已有字段:如果绑定了从表(slaveTableId),同步更新 refPropert + Map fieldNameToWidget = mapFieldNameToWidget(widgetJson); + for (OnlFormField existingField : existingFields) { + JSONObject widget = fieldNameToWidget.get(existingField.getFieldName()); + if (widget == null) { + continue; + } + JSONObject bindData = widget.getJSONObject("bindData"); + if (bindData == null) { + continue; + } + String slaveTableId = bindData.getString("slaveTableId"); + if (StrUtil.isNotEmpty(slaveTableId)) { + existingField.setRefPropert(Long.valueOf(slaveTableId)); + onlFormFieldService.updateById(existingField); + } + } if (!newColumnNames.isEmpty()) { Map nameToWidget = mapColumnNameToWidget(widgetJson); OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId()); @@ -758,7 +775,8 @@ public class OnlineFormController { JSONObject widget = nameToWidget.get(fieldName); // 从 widget 的 bindData.columnComment 设置字段备注 JSONObject bindData = widget.getJSONObject("bindData"); - this.saveField(widget.getInteger("widgetType"), bindData.getString("columnComment"), widget.getString("showName"), onlFormHead, fieldName); + String ref = bindData.containsKey("slaveTableId") ? bindData.getString("slaveTableId") : null; + this.saveField(widget.getInteger("widgetType"), bindData.getString("columnComment"), widget.getString("showName"), onlFormHead, fieldName, ref); } // ALTER TABLE ADD COLUMN @@ -841,8 +859,9 @@ public class OnlineFormController { JSONObject columnInfoJson = nameToWidget.get(newSubColumnName); //step 1 : 先保存 field 表 String showName = columnInfoJson.getString("showName"); + String ref = columnInfoJson.containsKey("slaveTableId") ? columnInfoJson.getString("slaveTableId") : null; // 默认是字符串类型 - this.saveField(750, showName, null, entryHead, newSubColumnName); + this.saveField(750, showName, null, entryHead, newSubColumnName, ref); } onlFormHeadService.syncDB(entryHead, 0L); //step 2 :在保存 OnlineColumn 表 @@ -873,6 +892,49 @@ public class OnlineFormController { metaCacheService.removeData(tableName); changed = true; } + + // 附表已有字段:检查 tableColumnList 中 comType=402 的列是否有 slaveTableId,同步更新 refPropert + for (Long tableId : existingSubTableIdSet) { + String tableName = tableIdAndNameMap.get(tableId); + if (tableName == null) { + continue; + } + OnlFormHead subHead = tableNameAndHeadIdMap.get(tableName); + if (subHead == null) { + continue; + } + List subFields = onlFormFieldService.list( + new LambdaQueryWrapper().eq(OnlFormField::getHeadId, subHead.getId())); + JSONObject props = tableIdAndPropMap.get(String.valueOf(tableId)); + JSONArray tableColumnList = props != null ? props.getJSONArray("tableColumnList") : null; + if (tableColumnList == null) { + continue; + } + for (OnlFormField field : subFields) { + for (int i = 0; i < tableColumnList.size(); i++) { + JSONObject col = tableColumnList.getJSONObject(i); + // 匹配 showFieldName,兼容 showName + String showFieldName = col.getString("showFieldName"); + if (StrUtil.isEmpty(showFieldName)) { + showFieldName = col.getString("showName"); + } + if (!field.getFieldName().equals(showFieldName)) { + continue; + } + // 只有 comType=402(关联选择器)才有 slaveTableId + Integer comType = col.getInteger("comType"); + if (comType != null && comType == 402) { + String slaveTableId = col.getString("slaveTableId"); + if (StrUtil.isNotEmpty(slaveTableId)) { + field.setRefPropert(Long.valueOf(slaveTableId)); + onlFormFieldService.updateById(field); + changed = true; + } + } + break; + } + } + } } //step2 : 不存在的附表,新增 if (newSubTableNames.isEmpty()) { @@ -906,7 +968,7 @@ public class OnlineFormController { } - private void saveField(Integer widgetType, String columnComment, String showName, OnlFormHead onlFormHead, String fieldName) { + private void saveField(Integer widgetType, String columnComment, String showName, OnlFormHead onlFormHead, String fieldName, String refPropert) { OnlFormField field = new OnlFormField(); field.setHeadId(onlFormHead.getId()); field.setFieldName(fieldName); @@ -915,6 +977,9 @@ public class OnlineFormController { if (length != null) { field.setFieldLength(length); } + if (StringUtils.isNotBlank(refPropert)) { + field.setRefPropert(Long.valueOf(refPropert)); + } field.setIsEmpty(1); field.setIsDefault(0); field.setRowDisabled(0); @@ -1025,6 +1090,28 @@ public class OnlineFormController { return map; } + /** + * 构建 fieldName → widget 的映射(用于主表已有字段匹配)。 + * key 优先取 bindData.columnFieldName,兼容 bindData.columnName。 + */ + private Map mapFieldNameToWidget(JSONObject widgetJson) { + Map map = new HashMap<>(); + walkAllModes(widgetJson, widget -> { + JSONObject bindData = widget.getJSONObject("bindData"); + if (bindData == null) { + return; + } + String fieldName = bindData.getString("columnFieldName"); + if (StrUtil.isEmpty(fieldName)) { + fieldName = bindData.getString("columnName"); + } + if (StrUtil.isNotEmpty(fieldName)) { + map.put(fieldName, widget); + } + }); + return map; + } + private Map mapColumnIdAndWidget(JSONObject widgetJson) { Map resultMap = new HashMap<>(); walkAllModes(widgetJson, widget -> { @@ -1104,4 +1191,19 @@ public class OnlineFormController { } } } + + /** + * 根据表单类型查询表单列表 + * + * @param formType 表单类型 + * @return 表单列表 + * @see apelet.common.online.model.constant.FormType + */ + @PostMapping(value = "/queryFormByType") + public ResponseResult> queryFormByType(@MyRequestBody Integer formType) { + if (formType == null) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + return ResponseResult.success(onlineFormService.queryListByFormType(Lists.newArrayList(formType))); + } } diff --git a/common/common-online/src/main/java/apelet/common/online/service/OnlineFormService.java b/common/common-online/src/main/java/apelet/common/online/service/OnlineFormService.java index 4bd603d..7159ae0 100644 --- a/common/common-online/src/main/java/apelet/common/online/service/OnlineFormService.java +++ b/common/common-online/src/main/java/apelet/common/online/service/OnlineFormService.java @@ -139,4 +139,12 @@ public interface OnlineFormService extends IBaseService { * @return */ ObjectValue getObjectValue(Map masterData, Map savedata, String tableName); + + /** + * 根据表单类型查询表单列表 + * + * @param formType 表单类型 + * @return 表单列表 + */ + List queryListByFormType(List formType); } diff --git a/common/common-online/src/main/java/apelet/common/online/service/OnlinePageService.java b/common/common-online/src/main/java/apelet/common/online/service/OnlinePageService.java index 88b9fa0..b15c644 100644 --- a/common/common-online/src/main/java/apelet/common/online/service/OnlinePageService.java +++ b/common/common-online/src/main/java/apelet/common/online/service/OnlinePageService.java @@ -135,4 +135,11 @@ public interface OnlinePageService extends IBaseService { * @return 在线表单页面列表。 */ List getInListWithNonTenant(List pageIds, String orderBy); + + /** + * 查询启用的在线表单页面列表。 + * + * @return 在线表单页面列表。 + */ + List queryByEnableList(); } diff --git a/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineFormServiceImpl.java b/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineFormServiceImpl.java index 2898c9e..4e099f5 100644 --- a/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineFormServiceImpl.java +++ b/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineFormServiceImpl.java @@ -26,6 +26,7 @@ import apelet.common.orm.impl.LinkEntityColumn; import apelet.common.redis.util.CommonRedisUtil; import apelet.common.sequence.wrapper.IdGeneratorWrapper; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; @@ -35,7 +36,6 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.github.pagehelper.Page; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.redisson.api.RBucket; import org.redisson.api.RedissonClient; @@ -460,6 +460,24 @@ public class OnlineFormServiceImpl extends BaseService impleme return objectValue; } + + @Override + public List queryListByFormType(List formType) { + // 查询已发布的 page + List pageList = onlinePageService.queryByEnableList(); + if (CollectionUtil.isEmpty(pageList)) { + return Collections.emptyList(); + } + Set pageIdSet = pageList.stream().map(OnlinePage::getPageId).collect(Collectors.toSet()); + List formList = onlineFormMapper.selectList(new LambdaQueryWrapper().in(OnlineForm::getFormType, formType).in(OnlineForm::getPageId, pageIdSet)); + if (CollectionUtil.isNotEmpty(formList)) { + formList.forEach(item -> { + item.setWidgetJson(null); + }); + } + return formList; + } + /** * 设置关联表字段转换为对象 * diff --git a/common/common-online/src/main/java/apelet/common/online/service/impl/OnlinePageServiceImpl.java b/common/common-online/src/main/java/apelet/common/online/service/impl/OnlinePageServiceImpl.java index 2a8486a..a27f08b 100644 --- a/common/common-online/src/main/java/apelet/common/online/service/impl/OnlinePageServiceImpl.java +++ b/common/common-online/src/main/java/apelet/common/online/service/impl/OnlinePageServiceImpl.java @@ -296,4 +296,9 @@ public class OnlinePageServiceImpl extends BaseService impleme } return onlinePageMapper.selectList(queryWrapper); } + + @Override + public List queryByEnableList() { + return onlinePageMapper.selectList(new LambdaQueryWrapper().eq(OnlinePage::getPublished, true)); + } } From 446eeba6be3c15e6da05fdf5535b7bbe5a557364 Mon Sep 17 00:00:00 2001 From: chenchuchuan <13554600537@163.com> Date: Fri, 24 Jul 2026 17:55:12 +0800 Subject: [PATCH 2/3] =?UTF-8?q?docs(common):=20=E6=9B=B4=E6=96=B0=E5=9C=A8?= =?UTF-8?q?=E7=BA=BF=E8=A1=A8=E5=8D=95=E6=8E=A7=E5=88=B6=E5=99=A8=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E5=B9=B6=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E6=80=BB?= =?UTF-8?q?=E8=A7=88=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 OnlineFormController 中字段名匹配逻辑,移除不必要的 toLowerCase 转换 - 添加子表名称到表头记录的 childName 字段中,完善表间关联关系 - 新增 PROJECT_OVERVIEW.md 文件,详细描述 Apelet SaaS 平台架构 - 文档涵盖技术栈、模块结构、数据库架构、表关系ER图等内容 - 包含多租户数据同步流程和服务启动顺序说明 - 提供关键设计说明和权限模型介绍 --- PROJECT_OVERVIEW.md | 494 +++++++++++++++++++++ .../online/controller/OnlineFormController.java | 11 +- 2 files changed, 504 insertions(+), 1 deletion(-) create mode 100644 PROJECT_OVERVIEW.md diff --git a/PROJECT_OVERVIEW.md b/PROJECT_OVERVIEW.md new file mode 100644 index 0000000..43fbd83 --- /dev/null +++ b/PROJECT_OVERVIEW.md @@ -0,0 +1,494 @@ +# Apelet SaaS 多租户低代码平台 - 项目总览 + +## 一、项目简介 + +**Apelet SaaS** 是一个基于 **Spring Cloud 微服务架构** 的**多租户低代码开发平台**(橙单开源框架)。平台提供用户权限管理(UPMS)、在线表单设计、数据报表、工作流引擎、代码生成器、大屏可视化等核心能力,支持多租户隔离与数据同步。 + +### 技术栈 + +| 分类 | 技术 | 版本 | +|------|------|------| +| 基础框架 | Spring Boot | 2.7.10 | +| 微服务 | Spring Cloud | 2021.0.6 | +| 微服务 | Spring Cloud Alibaba | 2021.1 | +| 注册/配置中心 | Nacos | 1.3.1 | +| 网关 | Spring Cloud Gateway | - | +| 认证授权 | Spring Security + OAuth2 (UAA) | - | +| ORM | MyBatis Plus | 3.5.4.1 | +| 数据库 | MySQL | 8.0.22 | +| 缓存 | Redis 4 + Caffeine | 2.9.3 | +| 消息队列 | Kafka + RocketMQ | 2.4.0 / 4.7.0 | +| 工作流 | Flowable | 6.8.0 | +| 分布式事务 | Seata (AT模式) | 1.8.0 | +| 数据同步 | Canal | 1.1.4 | +| 任务调度 | XXL-Job | 2.2.0 | +| 链路追踪 | SkyWalking | 8.1.x | +| 对象存储 | MinIO | 8.4.5 | +| 接口文档 | Knife4j / Swagger | 4.5.0 | + +--- + +## 二、项目模块结构 + +``` +apelet_saas +├── application/ # 业务应用服务 +│ ├── gateway/ # API 网关 (Spring Cloud Gateway) +│ ├── upms/ # 用户权限管理系统 (UPMS) +│ │ ├── upms-api/ # UPMS 对外 API 接口定义 +│ │ └── upms-service/ # UPMS 服务实现 +│ └── baseservice/ # 基础业务服务 +│ └── baseservice-service/ # 基础服务实现 +│ +├── application-tenant/ # 租户管理 +│ ├── tenant-admin/ # 租户运营管理后台 +│ │ # 管理所有租户、租户菜单/权限/角色、数据源配置 +│ └── tenant-sync/ # 租户数据同步服务 +│ # 将 tenant-admin 的配置同步到各租户业务库 +│ +├── common/ # 公共模块 +│ ├── common-generator/ # 代码生成器 +│ │ # 在线表单开发、反向工程、CGReport 报表 +│ ├── common-online/ # 在线表单引擎核心 +│ │ # 动态数据源、在线页面/表单/字典、字段规则 +│ ├── common-tenant/ # 租户通用模块 +│ └── common-tenant-online/ # 租户在线表单通用模块 +│ +├── framework/ # 框架基础设施 +│ ├── uaa/ # 统一认证授权中心 (UAA) +│ │ ├── uaa-admin/ # UAA 管理端 (客户端管理、操作员管理) +│ │ └── uaa-auth/ # UAA 认证服务 (OAuth2 授权) +│ ├── admin-monitor/ # Spring Boot Admin 监控 +│ └── canal-cache-sync/ # Canal 缓存同步 +│ +└── zz-resource/ # 资源文件 + ├── db-scripts/ # 数据库初始化脚本 + └── docker-files/ # Docker Compose 部署文件 +``` + +--- + +## 三、数据库架构 + +系统划分多个数据库,按职责分离: + +| 数据库名 | 用途 | 主要服务 | +|----------|------|----------| +| `apelet_admin` | 租户管理系统库 | tenant-admin | +| `apelet` | 租户业务运营库(UPMS + 在线表单 + 报表) | upms-service, baseservice, online, report | +| `uaa` | 统一认证授权库 | uaa-admin, uaa-auth | + +> **多租户隔离**:每个租户可配置独立的数据源(`xy_sys_tenant_datasource`),租户业务数据存储在自己的数据库中,通过 `tenant-sync` 服务将租户管理端的配置同步到各租户业务库。 + +--- + +## 四、数据库表总览 + +### 4.1 租户管理系统库 (`apelet_admin`) + +#### 4.1.1 基础组织架构 (无租户隔离,管理租户平台自身) + +| 表名 | 说明 | +|------|------| +| `xy_sys_dept` | 部门管理表 | +| `xy_sys_dept_relation` | 部门关联关系表(闭包表) | +| `xy_sys_dept_post` | 部门岗位关联表 | +| `xy_sys_post` | 岗位表 | +| `xy_sys_user_post` | 用户岗位关联表 | +| `xy_sys_user` | 系统用户表(含密码) | +| `xy_sys_user_role` | 用户角色关联表 | +| `xy_sys_role` | 角色表 | +| `xy_sys_role_menu` | 角色菜单关联表 | +| `xy_sys_menu` | 菜单表 | + +#### 4.1.2 权限管理 + +| 表名 | 说明 | +|------|------| +| `xy_sys_perm_code` | 权限字表(权限标识,含父子层级) | +| `xy_sys_menu_perm_code` | 菜单与权限字关联表 | +| `xy_sys_perm_module` | 权限资源模块表 | +| `xy_sys_perm` | 权限资源表(URL级别) | +| `xy_sys_perm_code_perm` | 权限字与权限资源关联表 | +| `xy_sys_perm_whitelist` | 权限白名单表(认证用户即可访问的URL) | +| `xy_sys_data_perm` | 数据权限表 | +| `xy_sys_data_perm_user` | 数据权限用户关联表 | +| `xy_sys_data_perm_dept` | 数据权限部门关联表 | +| `xy_sys_data_perm_menu` | 数据权限菜单关联表 | + +#### 4.1.3 租户管理 + +| 表名 | 说明 | +|------|------| +| `xy_sys_tenant` | 租户管理表 | +| `xy_sys_tenant_ext` | 租户扩展表(关联在线表单页面) | +| `xy_sys_tenant_datasource` | 租户数据源配置表 | +| `xy_sys_tenant_role` | 租户角色表 | +| `xy_sys_tenant_role_tenant` | 租户角色与租户关联表 | +| `xy_sys_tenant_role_menu` | 租户角色与租户菜单关联表 | +| `xy_sys_tenant_role_online_page` | 租户角色与在线表单页面关联表 | +| `xy_sys_tenant_role_report_ds` | 租户角色与报表数据集关联表 | + +#### 4.1.4 租户权限模板(同步到租户库的模板) + +| 表名 | 说明 | +|------|------| +| `xy_sys_tenant_menu` | 租户菜单模板表 | +| `xy_sys_tenant_menu_perm_code` | 租户菜单与权限字关联表 | +| `xy_sys_tenant_perm_code` | 租户权限字模板表 | +| `xy_sys_tenant_perm_code_perm` | 租户权限字与权限资源关联表 | +| `xy_sys_tenant_perm` | 租户权限资源模板表 | +| `xy_sys_tenant_perm_module` | 租户权限模块模板表 | + +#### 4.1.5 消息与同步 + +| 表名 | 说明 | +|------|------| +| `zz_producer_transaction_msg` | 事务型消息生产者发送流水表 | +| `zz_data_sync_producer_trans` | 数据同步生产者消息流水表 | +| `zz_data_sync_producer_mark` | 数据同步生产者高水标记表 | + +--- + +### 4.2 租户业务运营库 (`apelet`) + +#### 4.2.1 UPMS 业务库(含 tenant_id 隔离) + +| 表名 | 说明 | +|------|------| +| `xy_sys_tenant` | 租户表(业务库副本) | +| `xy_sys_dept` | 部门管理表(含 tenant_id) | +| `xy_sys_dept_relation` | 部门关联关系表 | +| `xy_sys_dept_post` | 部门岗位关联表 | +| `xy_sys_post` | 岗位表(含 tenant_id) | +| `xy_sys_user_post` | 用户岗位关联表 | +| `xy_sys_user` | 用户表(含 tenant_id,**无密码字段**) | +| `xy_sys_user_role` | 用户角色关联表 | +| `xy_sys_role` | 角色表(含 tenant_id) | +| `xy_sys_role_menu` | 角色菜单关联表 | +| `xy_sys_menu` | 菜单表(含 tenant_id, tenant_available, tenant_custom) | +| `xy_sys_perm_code` | 权限字表 | +| `xy_sys_menu_perm_code` | 菜单与权限字关联表 | +| `xy_sys_perm_module` | 权限资源模块表 | +| `xy_sys_perm` | 权限资源表 | +| `xy_sys_perm_code_perm` | 权限字与权限资源关联表 | +| `xy_sys_perm_whitelist` | 权限白名单表 | +| `xy_sys_data_perm` | 数据权限表(含 tenant_id) | +| `xy_sys_data_perm_user` | 数据权限用户关联表 | +| `xy_sys_data_perm_dept` | 数据权限部门关联表 | +| `xy_sys_data_perm_menu` | 数据权限菜单关联表 | + +> **注意**:UPMS 业务库与租户管理库表结构高度相似,但关键区别在于: +> - 业务库的表含 `tenant_id` 字段实现**租户数据隔离** +> - 业务库 `xy_sys_user` **不含密码字段**(密码由 UAA 统一管理) +> - 业务库 `xy_sys_menu` 含 `tenant_available`(租户可用标记)和 `tenant_custom`(租户自定义标记) + +#### 4.2.2 消息与同步 + +| 表名 | 说明 | +|------|------| +| `zz_producer_transaction_msg` | 事务型消息生产者流水表 | +| `zz_data_sync_consumer_trans` | 数据同步消费者消息流水表 | +| `zz_data_sync_producer_trans` | 数据同步生产者消息流水表 | +| `zz_data_sync_producer_mark` | 数据同步生产者高水标记表 | + +--- + +### 4.3 在线表单引擎 (`zz_online_*`) + +> **核心概念**:在线表单引擎允许用户通过可视化界面动态创建数据表、表单页面、数据源,无需编写代码。 + +| 表名 | 说明 | +|------|------| +| `zz_online_dblink` | 数据库链接配置表(支持多数据库) | +| `zz_online_table` | 在线数据表定义(映射到物理表) | +| `zz_online_column` | 在线数据表字段定义 | +| `zz_online_virtual_column` | 虚拟字段表(聚合字段,如 SUM/COUNT) | +| `zz_online_datasource` | 数据源定义(关联主表,用于表单和页面的数据存取) | +| `zz_online_datasource_relation` | 数据源关联定义(一对一/一对多/多对多) | +| `zz_online_datasource_table` | 数据源与数据表关联中间表 | +| `zz_online_page` | 在线页面表(列表页/表单页/审批页等) | +| `zz_online_page_datasource` | 页面与数据源关联中间表 | +| `zz_online_form` | 在线表单表(含 widget_json 组件描述) | +| `zz_online_form_datasource` | 表单与数据源关联中间表 | +| `zz_online_dict` | 在线字典表(支持表字典/URL字典/全局字典) | +| `zz_online_rule` | 字段验证规则表(整数/数字/邮箱/手机/范围等) | +| `zz_online_column_rule` | 字段与规则关联中间表 | + +--- + +### 4.4 代码生成器 (`onl_*`) + +| 表名 | 说明 | +|------|------| +| `onl_form_head` | 在线表单开发主表(表定义头) | +| `onl_form_field` | 表单开发字段属性表 | +| `onl_form_check` | 表单字段校验规则表 | +| `onl_form_find` | 表单查询条件配置表 | +| `onl_form_index` | 表单索引配置表 | +| `onl_form_key` | 表单主键/外键配置表 | +| `onl_form_page` | 表单页面配置表 | +| `onl_cgreport_head` | CGReport 报表头表 | +| `onl_cgreport_item` | CGReport 报表项表 | +| `onl_cgreport_param` | CGReport 报表参数表 | +| `reverse_writing_business` | 反向写入业务配置表 | +| `reverse_writing_formula` | 反向写入公式配置表 | +| `reverse_writing_rules` | 反向写入规则表 | + +--- + +### 4.5 报表引擎 (`zz_report_*`) + +| 表名 | 说明 | +|------|------| +| `zz_report_tenant_dataset` | 租户数据集关联表(多租户) | +| `zz_report_dataset` | 数据集定义表 | +| `zz_report_dataset_column` | 数据集字段表 | +| `zz_report_dataset_group` | 数据集分组表(树形) | +| `zz_report_dataset_relation` | 数据集关联表(一对一/一对多) | +| `zz_report_dblink` | 报表数据库链接表 | +| `zz_report_dict` | 报表字典表 | +| `zz_report_page` | 统计报表页面表(含 page_json 配置) | +| `zz_report_page_group` | 统计页面分组表(树形) | +| `zz_report_print` | 打印模板表 | +| `zz_report_print_group` | 打印模板分组表(树形) | +| `zz_report_visualization` | 大屏可视化数据表 | +| `zz_report_visualization_asset` | 大屏可视化素材表 | + +--- + +### 4.6 UAA 认证授权库 (`uaa`) + +| 表名 | 说明 | +|------|------| +| `xy_auth_client_details` | OAuth2 客户端详情表(client_id/secret/授权方式) | +| `xy_sys_uaa_user` | UAA 认证用户表(username/password,用于业务用户登录) | +| `xy_sys_uaa_operator` | UAA 操作员表(管理 UAA 本身的后台用户) | + +--- + +### 4.7 公共支撑表 + +| 表名 | 所属脚本 | 说明 | +|------|----------|------| +| `zz_global_dict` | global-dict | 全局字典表(跨租户共享) | +| `zz_global_dict_item` | global-dict | 全局字典数据项表 | +| `zz_sys_operation_log` | operation-log | 系统操作日志表(所有微服务共享) | +| `zz_mobile_entry` | mobile | 移动端入口表 | +| `zz_mobile_entry_data_perm` | mobile | 移动端入口数据权限关联表 | +| `zz_mobile_entry_role` | mobile | 移动端入口角色关联表 | +| `undo_log` | seata | Seata AT模式事务回滚日志表 | + +--- + +## 五、表关系核心 ER 图 + +### 5.1 租户管理端 — 权限体系关系 + +``` +┌──────────────────────────────────────────────────────────────┐ +│ 权限资源体系 │ +│ │ +│ xy_sys_perm_module (权限模块) │ +│ │ 1:N │ +│ ▼ │ +│ xy_sys_perm (权限资源-URL级) │ +│ │ N:M (xy_sys_perm_code_perm) │ +│ ▼ │ +│ xy_sys_perm_code (权限字-树形) ◄── N:M ── xy_sys_menu │ +│ │ xy_sys_menu_perm_code │ +│ │ │ +│ xy_sys_perm_whitelist (白名单URL,认证即可访问) │ +└──────────────────────────────────────────────────────────────┘ + +┌──────────────────────────────────────────────────────────────┐ +│ 菜单角色体系 │ +│ │ +│ xy_sys_menu (菜单-树形) │ +│ │ N:M (xy_sys_role_menu) │ +│ ▼ │ +│ xy_sys_role (角色) │ +│ │ N:M (xy_sys_user_role) │ +│ ▼ │ +│ xy_sys_user (用户) │ +│ │ N:M (xy_sys_user_post) │ +│ ▼ │ +│ xy_sys_post (岗位) ── N:M ── xy_sys_dept (部门-树形) │ +│ xy_sys_dept_post │ +│ │ +│ xy_sys_dept ── 闭包表 ── xy_sys_dept_relation │ +└──────────────────────────────────────────────────────────────┘ + +┌──────────────────────────────────────────────────────────────┐ +│ 数据权限体系 │ +│ │ +│ xy_sys_data_perm (数据权限规则) │ +│ │ N:M │ N:M │ +│ ▼ ▼ │ +│ xy_sys_data_perm_user xy_sys_data_perm_dept │ +│ (关联用户) (关联部门) │ +│ │ │ +│ │ N:M │ +│ ▼ │ +│ xy_sys_data_perm_menu (关联菜单) │ +└──────────────────────────────────────────────────────────────┘ +``` + +### 5.2 租户体系关系 + +``` +┌──────────────────────────────────────────────────────────────┐ +│ xy_sys_tenant_datasource (租户数据源) │ +│ │ 1:1 (datasource_type) │ +│ ▼ │ +│ xy_sys_tenant (租户) │ +│ │ 1:1 │ N:M │ +│ ▼ ▼ │ +│ xy_sys_tenant_ext xy_sys_tenant_role_tenant │ +│ (扩展信息) (租户-租户角色) │ +│ │ │ +│ ▼ │ +│ xy_sys_tenant_role (租户角色) │ +│ │ │ │ │ +│ ┌─────────┘ │ └─────────┐ │ +│ ▼ ▼ ▼ │ +│ tenant_role_menu tenant_role_ tenant_role_ │ +│ (菜单权限) online_page report_ds │ +│ (在线页面权限) (报表数据集权限) │ +│ │ +│ === 以下为租户权限模板表(同步到各租户业务库) === │ +│ │ +│ xy_sys_tenant_menu ── xy_sys_tenant_menu_perm_code │ +│ │ │ │ +│ ▼ ▼ │ +│ xy_sys_tenant_perm_code ── xy_sys_tenant_perm_code_perm │ +│ │ │ +│ ▼ │ +│ xy_sys_tenant_perm │ +│ │ │ +│ ▼ │ +│ xy_sys_tenant_perm_module │ +└──────────────────────────────────────────────────────────────┘ +``` + +### 5.3 在线表单引擎核心关系 + +``` +┌──────────────────────────────────────────────────────────────┐ +│ zz_online_dblink (数据库链接) │ +│ │ │ +│ ├── 1:N ── zz_online_table (数据表定义) │ +│ │ │ 1:N │ +│ │ ├── zz_online_column (物理字段) │ +│ │ │ │ N:M ── zz_online_column_rule │ +│ │ │ │ │ │ +│ │ │ │ ▼ │ +│ │ │ │ zz_online_rule (验证规则) │ +│ │ │ │ │ +│ │ │ └── zz_online_dict (字典) │ +│ │ │ │ +│ │ └── zz_online_virtual_column (虚拟字段) │ +│ │ │ +│ └── 1:N ── zz_online_datasource (数据源) │ +│ │ 1:N │ +│ ├── zz_online_datasource_relation │ +│ │ (关联: 1:1 / 1:N / N:M) │ +│ │ │ +│ ├── zz_online_datasource_table │ +│ │ (数据源包含的数据表) │ +│ │ │ +│ └── Page/Form 引用: │ +│ ├── zz_online_page_datasource │ +│ │ │ │ +│ │ ▼ │ +│ │ zz_online_page (页面) │ +│ │ │ 1:N │ +│ │ ▼ │ +│ │ zz_online_form (表单) │ +│ │ │ │ +│ │ └── zz_online_form_datasource│ +│ │ │ +│ └── zz_online_dict (字典) │ +└──────────────────────────────────────────────────────────────┘ +``` + +### 5.4 多租户数据同步流程 + +``` +tenant-admin (管理库 apelet_admin) + │ + │ 修改租户配置(菜单/权限/角色) + │ + ▼ +zz_data_sync_producer_trans (消息流水表) + │ + │ Kafka 消息 + │ + ▼ +tenant-sync (同步服务) + │ + │ 消费消息,写入租户业务库 + │ + ▼ +租户业务库 (apelet 或独立租户库) + ├── xy_sys_menu (tenant_available, tenant_custom) + ├── xy_sys_role + ├── xy_sys_user + └── ... (其他 UPMS 表) +``` + +### 5.5 UAA 认证流程 + +``` +用户登录请求 + │ + ▼ +Gateway (网关) + │ + ▼ +uaa-auth (OAuth2 认证服务) + │ + ├── xy_auth_client_details (验证客户端) + ├── xy_sys_uaa_user (验证用户密码) + │ + ▼ +颁发 JWT Token (含用户信息 + 权限) + │ + ▼ +Gateway 解析 Token → 转发到业务微服务 + │ + ▼ +upms-service (读取用户权限) + ├── xy_sys_user (用户信息) + ├── xy_sys_user_role → xy_sys_role → xy_sys_role_menu → xy_sys_menu + ├── xy_sys_menu_perm_code → xy_sys_perm_code → xy_sys_perm + └── xy_sys_data_perm → 数据权限规则 +``` + +--- + +## 六、服务启动顺序 + +1. **基础设施**: Redis → Zookeeper → Kafka → Nacos +2. **数据同步**: Canal-Server +3. **租户管理**: tenant-admin → tenant-sync +4. **认证服务**: uaa-auth +5. **权限服务**: upms-service +6. **业务服务**: baseservice 及其他业务微服务 +7. **网关**: gateway(最后启动,确保能发现所有微服务) + +--- + +## 七、关键设计说明 + +1. **多租户数据隔离**:租户管理端在 `apelet_admin` 库,各租户业务数据可在同一个 `apelet` 库(通过 `tenant_id` 隔离),也可配置独立数据源实现物理隔离。 + +2. **权限模型**:采用 **RBAC**(基于角色的访问控制)+ **权限字(PermCode)** 双层模型。菜单关联权限字,权限字再关联 URL 级权限资源。 + +3. **数据权限**:支持6种规则类型(全部可见/只看自己/只看本部门/本部门及子部门/多部门及子部门/自定义部门列表)。 + +4. **在线表单引擎**:通过 `数据源(Datasource)` → `数据表(Table)` → `字段(Column)` 三层抽象,实现动态表单的 CRUD 操作。支持表关联、虚拟聚合字段、字段验证规则。 + +5. **租户配置同步**:通过 `tenant-sync` 服务 + Kafka 消息,将 tenant-admin 端配置的菜单/权限/角色同步到各租户的业务数据库中。 + +6. **密码统一管理**:用户密码存储在 UAA 的 `xy_sys_uaa_user` 表,业务库的 `xy_sys_user` 不含密码字段,实现认证与授权的分离。 diff --git a/common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java b/common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java index 622f04f..9367d8f 100644 --- a/common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java +++ b/common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java @@ -872,7 +872,7 @@ public class OnlineFormController { columnMap = allColumns.stream().collect(Collectors.toMap(SqlTableColumn::getColumnName, Function.identity())); } for (String fieldName : newSubColumnNameSet) { - SqlTableColumn sqlCol = columnMap.get(fieldName.toLowerCase()); + SqlTableColumn sqlCol = columnMap.get(fieldName); if (sqlCol != null) { // 回填 columnId 到 widgetJson JSONObject widget = nameToWidget.get(fieldName); @@ -961,6 +961,15 @@ public class OnlineFormController { backfillRelationId(widgetJson, subTableName, subOnlineTable.getTableId()); } } + + //这里要将 所有的附表名称 加到 apelet.common.generator.model.OnlFormHead.childName 后面 + String newSubTableName = String.join(",", newSubTableNames); + if (StringUtils.isNotBlank(onlFormHead.getChildName())) { + onlFormHead.setChildName(onlFormHead.getChildName() + "," + newSubTableName); + } else { + onlFormHead.setChildName(newSubTableName); + } + onlFormHeadService.updateById(onlFormHead); changed = true; } } From 8d491ff2160c2a14c497890892d729af525c137c Mon Sep 17 00:00:00 2001 From: chenchuchuan <13554600537@163.com> Date: Fri, 24 Jul 2026 17:55:54 +0800 Subject: [PATCH 3/3] =?UTF-8?q?docs(common):=20=E6=9B=B4=E6=96=B0=E5=9C=A8?= =?UTF-8?q?=E7=BA=BF=E8=A1=A8=E5=8D=95=E6=8E=A7=E5=88=B6=E5=99=A8=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E5=B9=B6=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E6=80=BB?= =?UTF-8?q?=E8=A7=88=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 OnlineFormController 中字段名匹配逻辑,移除不必要的 toLowerCase 转换 - 添加子表名称到表头记录的 childName 字段中,完善表间关联关系 - 新增 PROJECT_OVERVIEW.md 文件,详细描述 Apelet SaaS 平台架构 - 文档涵盖技术栈、模块结构、数据库架构、表关系ER图等内容 - 包含多租户数据同步流程和服务启动顺序说明 - 提供关键设计说明和权限模型介绍 --- PROJECT_OVERVIEW.md | 494 ---------------------------------------------------- 1 file changed, 494 deletions(-) delete mode 100644 PROJECT_OVERVIEW.md diff --git a/PROJECT_OVERVIEW.md b/PROJECT_OVERVIEW.md deleted file mode 100644 index 43fbd83..0000000 --- a/PROJECT_OVERVIEW.md +++ /dev/null @@ -1,494 +0,0 @@ -# Apelet SaaS 多租户低代码平台 - 项目总览 - -## 一、项目简介 - -**Apelet SaaS** 是一个基于 **Spring Cloud 微服务架构** 的**多租户低代码开发平台**(橙单开源框架)。平台提供用户权限管理(UPMS)、在线表单设计、数据报表、工作流引擎、代码生成器、大屏可视化等核心能力,支持多租户隔离与数据同步。 - -### 技术栈 - -| 分类 | 技术 | 版本 | -|------|------|------| -| 基础框架 | Spring Boot | 2.7.10 | -| 微服务 | Spring Cloud | 2021.0.6 | -| 微服务 | Spring Cloud Alibaba | 2021.1 | -| 注册/配置中心 | Nacos | 1.3.1 | -| 网关 | Spring Cloud Gateway | - | -| 认证授权 | Spring Security + OAuth2 (UAA) | - | -| ORM | MyBatis Plus | 3.5.4.1 | -| 数据库 | MySQL | 8.0.22 | -| 缓存 | Redis 4 + Caffeine | 2.9.3 | -| 消息队列 | Kafka + RocketMQ | 2.4.0 / 4.7.0 | -| 工作流 | Flowable | 6.8.0 | -| 分布式事务 | Seata (AT模式) | 1.8.0 | -| 数据同步 | Canal | 1.1.4 | -| 任务调度 | XXL-Job | 2.2.0 | -| 链路追踪 | SkyWalking | 8.1.x | -| 对象存储 | MinIO | 8.4.5 | -| 接口文档 | Knife4j / Swagger | 4.5.0 | - ---- - -## 二、项目模块结构 - -``` -apelet_saas -├── application/ # 业务应用服务 -│ ├── gateway/ # API 网关 (Spring Cloud Gateway) -│ ├── upms/ # 用户权限管理系统 (UPMS) -│ │ ├── upms-api/ # UPMS 对外 API 接口定义 -│ │ └── upms-service/ # UPMS 服务实现 -│ └── baseservice/ # 基础业务服务 -│ └── baseservice-service/ # 基础服务实现 -│ -├── application-tenant/ # 租户管理 -│ ├── tenant-admin/ # 租户运营管理后台 -│ │ # 管理所有租户、租户菜单/权限/角色、数据源配置 -│ └── tenant-sync/ # 租户数据同步服务 -│ # 将 tenant-admin 的配置同步到各租户业务库 -│ -├── common/ # 公共模块 -│ ├── common-generator/ # 代码生成器 -│ │ # 在线表单开发、反向工程、CGReport 报表 -│ ├── common-online/ # 在线表单引擎核心 -│ │ # 动态数据源、在线页面/表单/字典、字段规则 -│ ├── common-tenant/ # 租户通用模块 -│ └── common-tenant-online/ # 租户在线表单通用模块 -│ -├── framework/ # 框架基础设施 -│ ├── uaa/ # 统一认证授权中心 (UAA) -│ │ ├── uaa-admin/ # UAA 管理端 (客户端管理、操作员管理) -│ │ └── uaa-auth/ # UAA 认证服务 (OAuth2 授权) -│ ├── admin-monitor/ # Spring Boot Admin 监控 -│ └── canal-cache-sync/ # Canal 缓存同步 -│ -└── zz-resource/ # 资源文件 - ├── db-scripts/ # 数据库初始化脚本 - └── docker-files/ # Docker Compose 部署文件 -``` - ---- - -## 三、数据库架构 - -系统划分多个数据库,按职责分离: - -| 数据库名 | 用途 | 主要服务 | -|----------|------|----------| -| `apelet_admin` | 租户管理系统库 | tenant-admin | -| `apelet` | 租户业务运营库(UPMS + 在线表单 + 报表) | upms-service, baseservice, online, report | -| `uaa` | 统一认证授权库 | uaa-admin, uaa-auth | - -> **多租户隔离**:每个租户可配置独立的数据源(`xy_sys_tenant_datasource`),租户业务数据存储在自己的数据库中,通过 `tenant-sync` 服务将租户管理端的配置同步到各租户业务库。 - ---- - -## 四、数据库表总览 - -### 4.1 租户管理系统库 (`apelet_admin`) - -#### 4.1.1 基础组织架构 (无租户隔离,管理租户平台自身) - -| 表名 | 说明 | -|------|------| -| `xy_sys_dept` | 部门管理表 | -| `xy_sys_dept_relation` | 部门关联关系表(闭包表) | -| `xy_sys_dept_post` | 部门岗位关联表 | -| `xy_sys_post` | 岗位表 | -| `xy_sys_user_post` | 用户岗位关联表 | -| `xy_sys_user` | 系统用户表(含密码) | -| `xy_sys_user_role` | 用户角色关联表 | -| `xy_sys_role` | 角色表 | -| `xy_sys_role_menu` | 角色菜单关联表 | -| `xy_sys_menu` | 菜单表 | - -#### 4.1.2 权限管理 - -| 表名 | 说明 | -|------|------| -| `xy_sys_perm_code` | 权限字表(权限标识,含父子层级) | -| `xy_sys_menu_perm_code` | 菜单与权限字关联表 | -| `xy_sys_perm_module` | 权限资源模块表 | -| `xy_sys_perm` | 权限资源表(URL级别) | -| `xy_sys_perm_code_perm` | 权限字与权限资源关联表 | -| `xy_sys_perm_whitelist` | 权限白名单表(认证用户即可访问的URL) | -| `xy_sys_data_perm` | 数据权限表 | -| `xy_sys_data_perm_user` | 数据权限用户关联表 | -| `xy_sys_data_perm_dept` | 数据权限部门关联表 | -| `xy_sys_data_perm_menu` | 数据权限菜单关联表 | - -#### 4.1.3 租户管理 - -| 表名 | 说明 | -|------|------| -| `xy_sys_tenant` | 租户管理表 | -| `xy_sys_tenant_ext` | 租户扩展表(关联在线表单页面) | -| `xy_sys_tenant_datasource` | 租户数据源配置表 | -| `xy_sys_tenant_role` | 租户角色表 | -| `xy_sys_tenant_role_tenant` | 租户角色与租户关联表 | -| `xy_sys_tenant_role_menu` | 租户角色与租户菜单关联表 | -| `xy_sys_tenant_role_online_page` | 租户角色与在线表单页面关联表 | -| `xy_sys_tenant_role_report_ds` | 租户角色与报表数据集关联表 | - -#### 4.1.4 租户权限模板(同步到租户库的模板) - -| 表名 | 说明 | -|------|------| -| `xy_sys_tenant_menu` | 租户菜单模板表 | -| `xy_sys_tenant_menu_perm_code` | 租户菜单与权限字关联表 | -| `xy_sys_tenant_perm_code` | 租户权限字模板表 | -| `xy_sys_tenant_perm_code_perm` | 租户权限字与权限资源关联表 | -| `xy_sys_tenant_perm` | 租户权限资源模板表 | -| `xy_sys_tenant_perm_module` | 租户权限模块模板表 | - -#### 4.1.5 消息与同步 - -| 表名 | 说明 | -|------|------| -| `zz_producer_transaction_msg` | 事务型消息生产者发送流水表 | -| `zz_data_sync_producer_trans` | 数据同步生产者消息流水表 | -| `zz_data_sync_producer_mark` | 数据同步生产者高水标记表 | - ---- - -### 4.2 租户业务运营库 (`apelet`) - -#### 4.2.1 UPMS 业务库(含 tenant_id 隔离) - -| 表名 | 说明 | -|------|------| -| `xy_sys_tenant` | 租户表(业务库副本) | -| `xy_sys_dept` | 部门管理表(含 tenant_id) | -| `xy_sys_dept_relation` | 部门关联关系表 | -| `xy_sys_dept_post` | 部门岗位关联表 | -| `xy_sys_post` | 岗位表(含 tenant_id) | -| `xy_sys_user_post` | 用户岗位关联表 | -| `xy_sys_user` | 用户表(含 tenant_id,**无密码字段**) | -| `xy_sys_user_role` | 用户角色关联表 | -| `xy_sys_role` | 角色表(含 tenant_id) | -| `xy_sys_role_menu` | 角色菜单关联表 | -| `xy_sys_menu` | 菜单表(含 tenant_id, tenant_available, tenant_custom) | -| `xy_sys_perm_code` | 权限字表 | -| `xy_sys_menu_perm_code` | 菜单与权限字关联表 | -| `xy_sys_perm_module` | 权限资源模块表 | -| `xy_sys_perm` | 权限资源表 | -| `xy_sys_perm_code_perm` | 权限字与权限资源关联表 | -| `xy_sys_perm_whitelist` | 权限白名单表 | -| `xy_sys_data_perm` | 数据权限表(含 tenant_id) | -| `xy_sys_data_perm_user` | 数据权限用户关联表 | -| `xy_sys_data_perm_dept` | 数据权限部门关联表 | -| `xy_sys_data_perm_menu` | 数据权限菜单关联表 | - -> **注意**:UPMS 业务库与租户管理库表结构高度相似,但关键区别在于: -> - 业务库的表含 `tenant_id` 字段实现**租户数据隔离** -> - 业务库 `xy_sys_user` **不含密码字段**(密码由 UAA 统一管理) -> - 业务库 `xy_sys_menu` 含 `tenant_available`(租户可用标记)和 `tenant_custom`(租户自定义标记) - -#### 4.2.2 消息与同步 - -| 表名 | 说明 | -|------|------| -| `zz_producer_transaction_msg` | 事务型消息生产者流水表 | -| `zz_data_sync_consumer_trans` | 数据同步消费者消息流水表 | -| `zz_data_sync_producer_trans` | 数据同步生产者消息流水表 | -| `zz_data_sync_producer_mark` | 数据同步生产者高水标记表 | - ---- - -### 4.3 在线表单引擎 (`zz_online_*`) - -> **核心概念**:在线表单引擎允许用户通过可视化界面动态创建数据表、表单页面、数据源,无需编写代码。 - -| 表名 | 说明 | -|------|------| -| `zz_online_dblink` | 数据库链接配置表(支持多数据库) | -| `zz_online_table` | 在线数据表定义(映射到物理表) | -| `zz_online_column` | 在线数据表字段定义 | -| `zz_online_virtual_column` | 虚拟字段表(聚合字段,如 SUM/COUNT) | -| `zz_online_datasource` | 数据源定义(关联主表,用于表单和页面的数据存取) | -| `zz_online_datasource_relation` | 数据源关联定义(一对一/一对多/多对多) | -| `zz_online_datasource_table` | 数据源与数据表关联中间表 | -| `zz_online_page` | 在线页面表(列表页/表单页/审批页等) | -| `zz_online_page_datasource` | 页面与数据源关联中间表 | -| `zz_online_form` | 在线表单表(含 widget_json 组件描述) | -| `zz_online_form_datasource` | 表单与数据源关联中间表 | -| `zz_online_dict` | 在线字典表(支持表字典/URL字典/全局字典) | -| `zz_online_rule` | 字段验证规则表(整数/数字/邮箱/手机/范围等) | -| `zz_online_column_rule` | 字段与规则关联中间表 | - ---- - -### 4.4 代码生成器 (`onl_*`) - -| 表名 | 说明 | -|------|------| -| `onl_form_head` | 在线表单开发主表(表定义头) | -| `onl_form_field` | 表单开发字段属性表 | -| `onl_form_check` | 表单字段校验规则表 | -| `onl_form_find` | 表单查询条件配置表 | -| `onl_form_index` | 表单索引配置表 | -| `onl_form_key` | 表单主键/外键配置表 | -| `onl_form_page` | 表单页面配置表 | -| `onl_cgreport_head` | CGReport 报表头表 | -| `onl_cgreport_item` | CGReport 报表项表 | -| `onl_cgreport_param` | CGReport 报表参数表 | -| `reverse_writing_business` | 反向写入业务配置表 | -| `reverse_writing_formula` | 反向写入公式配置表 | -| `reverse_writing_rules` | 反向写入规则表 | - ---- - -### 4.5 报表引擎 (`zz_report_*`) - -| 表名 | 说明 | -|------|------| -| `zz_report_tenant_dataset` | 租户数据集关联表(多租户) | -| `zz_report_dataset` | 数据集定义表 | -| `zz_report_dataset_column` | 数据集字段表 | -| `zz_report_dataset_group` | 数据集分组表(树形) | -| `zz_report_dataset_relation` | 数据集关联表(一对一/一对多) | -| `zz_report_dblink` | 报表数据库链接表 | -| `zz_report_dict` | 报表字典表 | -| `zz_report_page` | 统计报表页面表(含 page_json 配置) | -| `zz_report_page_group` | 统计页面分组表(树形) | -| `zz_report_print` | 打印模板表 | -| `zz_report_print_group` | 打印模板分组表(树形) | -| `zz_report_visualization` | 大屏可视化数据表 | -| `zz_report_visualization_asset` | 大屏可视化素材表 | - ---- - -### 4.6 UAA 认证授权库 (`uaa`) - -| 表名 | 说明 | -|------|------| -| `xy_auth_client_details` | OAuth2 客户端详情表(client_id/secret/授权方式) | -| `xy_sys_uaa_user` | UAA 认证用户表(username/password,用于业务用户登录) | -| `xy_sys_uaa_operator` | UAA 操作员表(管理 UAA 本身的后台用户) | - ---- - -### 4.7 公共支撑表 - -| 表名 | 所属脚本 | 说明 | -|------|----------|------| -| `zz_global_dict` | global-dict | 全局字典表(跨租户共享) | -| `zz_global_dict_item` | global-dict | 全局字典数据项表 | -| `zz_sys_operation_log` | operation-log | 系统操作日志表(所有微服务共享) | -| `zz_mobile_entry` | mobile | 移动端入口表 | -| `zz_mobile_entry_data_perm` | mobile | 移动端入口数据权限关联表 | -| `zz_mobile_entry_role` | mobile | 移动端入口角色关联表 | -| `undo_log` | seata | Seata AT模式事务回滚日志表 | - ---- - -## 五、表关系核心 ER 图 - -### 5.1 租户管理端 — 权限体系关系 - -``` -┌──────────────────────────────────────────────────────────────┐ -│ 权限资源体系 │ -│ │ -│ xy_sys_perm_module (权限模块) │ -│ │ 1:N │ -│ ▼ │ -│ xy_sys_perm (权限资源-URL级) │ -│ │ N:M (xy_sys_perm_code_perm) │ -│ ▼ │ -│ xy_sys_perm_code (权限字-树形) ◄── N:M ── xy_sys_menu │ -│ │ xy_sys_menu_perm_code │ -│ │ │ -│ xy_sys_perm_whitelist (白名单URL,认证即可访问) │ -└──────────────────────────────────────────────────────────────┘ - -┌──────────────────────────────────────────────────────────────┐ -│ 菜单角色体系 │ -│ │ -│ xy_sys_menu (菜单-树形) │ -│ │ N:M (xy_sys_role_menu) │ -│ ▼ │ -│ xy_sys_role (角色) │ -│ │ N:M (xy_sys_user_role) │ -│ ▼ │ -│ xy_sys_user (用户) │ -│ │ N:M (xy_sys_user_post) │ -│ ▼ │ -│ xy_sys_post (岗位) ── N:M ── xy_sys_dept (部门-树形) │ -│ xy_sys_dept_post │ -│ │ -│ xy_sys_dept ── 闭包表 ── xy_sys_dept_relation │ -└──────────────────────────────────────────────────────────────┘ - -┌──────────────────────────────────────────────────────────────┐ -│ 数据权限体系 │ -│ │ -│ xy_sys_data_perm (数据权限规则) │ -│ │ N:M │ N:M │ -│ ▼ ▼ │ -│ xy_sys_data_perm_user xy_sys_data_perm_dept │ -│ (关联用户) (关联部门) │ -│ │ │ -│ │ N:M │ -│ ▼ │ -│ xy_sys_data_perm_menu (关联菜单) │ -└──────────────────────────────────────────────────────────────┘ -``` - -### 5.2 租户体系关系 - -``` -┌──────────────────────────────────────────────────────────────┐ -│ xy_sys_tenant_datasource (租户数据源) │ -│ │ 1:1 (datasource_type) │ -│ ▼ │ -│ xy_sys_tenant (租户) │ -│ │ 1:1 │ N:M │ -│ ▼ ▼ │ -│ xy_sys_tenant_ext xy_sys_tenant_role_tenant │ -│ (扩展信息) (租户-租户角色) │ -│ │ │ -│ ▼ │ -│ xy_sys_tenant_role (租户角色) │ -│ │ │ │ │ -│ ┌─────────┘ │ └─────────┐ │ -│ ▼ ▼ ▼ │ -│ tenant_role_menu tenant_role_ tenant_role_ │ -│ (菜单权限) online_page report_ds │ -│ (在线页面权限) (报表数据集权限) │ -│ │ -│ === 以下为租户权限模板表(同步到各租户业务库) === │ -│ │ -│ xy_sys_tenant_menu ── xy_sys_tenant_menu_perm_code │ -│ │ │ │ -│ ▼ ▼ │ -│ xy_sys_tenant_perm_code ── xy_sys_tenant_perm_code_perm │ -│ │ │ -│ ▼ │ -│ xy_sys_tenant_perm │ -│ │ │ -│ ▼ │ -│ xy_sys_tenant_perm_module │ -└──────────────────────────────────────────────────────────────┘ -``` - -### 5.3 在线表单引擎核心关系 - -``` -┌──────────────────────────────────────────────────────────────┐ -│ zz_online_dblink (数据库链接) │ -│ │ │ -│ ├── 1:N ── zz_online_table (数据表定义) │ -│ │ │ 1:N │ -│ │ ├── zz_online_column (物理字段) │ -│ │ │ │ N:M ── zz_online_column_rule │ -│ │ │ │ │ │ -│ │ │ │ ▼ │ -│ │ │ │ zz_online_rule (验证规则) │ -│ │ │ │ │ -│ │ │ └── zz_online_dict (字典) │ -│ │ │ │ -│ │ └── zz_online_virtual_column (虚拟字段) │ -│ │ │ -│ └── 1:N ── zz_online_datasource (数据源) │ -│ │ 1:N │ -│ ├── zz_online_datasource_relation │ -│ │ (关联: 1:1 / 1:N / N:M) │ -│ │ │ -│ ├── zz_online_datasource_table │ -│ │ (数据源包含的数据表) │ -│ │ │ -│ └── Page/Form 引用: │ -│ ├── zz_online_page_datasource │ -│ │ │ │ -│ │ ▼ │ -│ │ zz_online_page (页面) │ -│ │ │ 1:N │ -│ │ ▼ │ -│ │ zz_online_form (表单) │ -│ │ │ │ -│ │ └── zz_online_form_datasource│ -│ │ │ -│ └── zz_online_dict (字典) │ -└──────────────────────────────────────────────────────────────┘ -``` - -### 5.4 多租户数据同步流程 - -``` -tenant-admin (管理库 apelet_admin) - │ - │ 修改租户配置(菜单/权限/角色) - │ - ▼ -zz_data_sync_producer_trans (消息流水表) - │ - │ Kafka 消息 - │ - ▼ -tenant-sync (同步服务) - │ - │ 消费消息,写入租户业务库 - │ - ▼ -租户业务库 (apelet 或独立租户库) - ├── xy_sys_menu (tenant_available, tenant_custom) - ├── xy_sys_role - ├── xy_sys_user - └── ... (其他 UPMS 表) -``` - -### 5.5 UAA 认证流程 - -``` -用户登录请求 - │ - ▼ -Gateway (网关) - │ - ▼ -uaa-auth (OAuth2 认证服务) - │ - ├── xy_auth_client_details (验证客户端) - ├── xy_sys_uaa_user (验证用户密码) - │ - ▼ -颁发 JWT Token (含用户信息 + 权限) - │ - ▼ -Gateway 解析 Token → 转发到业务微服务 - │ - ▼ -upms-service (读取用户权限) - ├── xy_sys_user (用户信息) - ├── xy_sys_user_role → xy_sys_role → xy_sys_role_menu → xy_sys_menu - ├── xy_sys_menu_perm_code → xy_sys_perm_code → xy_sys_perm - └── xy_sys_data_perm → 数据权限规则 -``` - ---- - -## 六、服务启动顺序 - -1. **基础设施**: Redis → Zookeeper → Kafka → Nacos -2. **数据同步**: Canal-Server -3. **租户管理**: tenant-admin → tenant-sync -4. **认证服务**: uaa-auth -5. **权限服务**: upms-service -6. **业务服务**: baseservice 及其他业务微服务 -7. **网关**: gateway(最后启动,确保能发现所有微服务) - ---- - -## 七、关键设计说明 - -1. **多租户数据隔离**:租户管理端在 `apelet_admin` 库,各租户业务数据可在同一个 `apelet` 库(通过 `tenant_id` 隔离),也可配置独立数据源实现物理隔离。 - -2. **权限模型**:采用 **RBAC**(基于角色的访问控制)+ **权限字(PermCode)** 双层模型。菜单关联权限字,权限字再关联 URL 级权限资源。 - -3. **数据权限**:支持6种规则类型(全部可见/只看自己/只看本部门/本部门及子部门/多部门及子部门/自定义部门列表)。 - -4. **在线表单引擎**:通过 `数据源(Datasource)` → `数据表(Table)` → `字段(Column)` 三层抽象,实现动态表单的 CRUD 操作。支持表关联、虚拟聚合字段、字段验证规则。 - -5. **租户配置同步**:通过 `tenant-sync` 服务 + Kafka 消息,将 tenant-admin 端配置的菜单/权限/角色同步到各租户的业务数据库中。 - -6. **密码统一管理**:用户密码存储在 UAA 的 `xy_sys_uaa_user` 表,业务库的 `xy_sys_user` 不含密码字段,实现认证与授权的分离。