20260820173056
This commit is contained in:
1 parent
8e75c7989a
commit
68ac5db038
24 files changed
+975
-6035
No files matched your search
+100
-28
@@ -5,12 +5,13 @@
|
||||
|
||||
## 1. 设计原则
|
||||
|
||||
针对旧系统 `s_module` 职责混杂的问题,本方案坚持四个分离:
|
||||
针对旧系统 `s_module` 职责混杂的问题,本方案坚持五项原则:
|
||||
|
||||
1. **模块与实体分离**:`s_module` 只表达业务能力边界与依赖关系,不承载表、路由、菜单;数据归属由 `s_entity` 承担。
|
||||
2. **实体与字段分离**:实体是数据所有者,声明查询源/保存源/主键字段;字段挂实体,同时携带数据库列定义与 UI 行为。
|
||||
3. **菜单与模块分离**:`s_menu` 是纯导航树,只引用实体或固定页面,不掺入数据源配置。
|
||||
4. **字段定义与视图配置合并**:旧系统把字段拆成 `s_module_field` + `_edit` + `_list` + `_query` 四张表,维护极易失配;本方案合并为单表 `s_field`,用标志列表达列表/表单/查询行为。
|
||||
5. **配置入口与存储职责分离**:管理员始终从菜单树定位页面,在一个配置工作台内完成数据源、字段、列表、表单、查询、编码和权限配置;底层数据仍按模块、实体、菜单和权限分别存储。
|
||||
|
||||
其余约定:
|
||||
|
||||
@@ -20,6 +21,8 @@
|
||||
- 时间使用 `datetime2`,默认 `sysdatetime()`。
|
||||
- 审计字段:定义类表保留 `b_inputuser_id / b_inputdatetime / b_updateuser_id / b_updatedatetime`;关联类表只保留 `b_inputdatetime`。
|
||||
- 模块归属保留树形(`b_parent_id`,仅用于业务分组),模块间依赖使用独立的 DAG 表,两者语义互不混淆。
|
||||
- 菜单树是管理端的统一配置入口;模块树只用于业务归属和依赖管理,不要求管理员在两棵树之间来回切换。
|
||||
- 实体字段定义保留一套规范元数据;同一实体被多个菜单复用时,通过页面配置档保存菜单级布局和行为差异。
|
||||
|
||||
## 2. 表清单
|
||||
|
||||
@@ -31,17 +34,19 @@
|
||||
| 4 | `s_field` | 字段(列定义 + 列表/表单/查询行为合并单表) |
|
||||
| 5 | `s_field_group` | 字段分组(实体级) |
|
||||
| 6 | `s_relation` | 实体关系(引用/主子,驱动外键校验与级联) |
|
||||
| 7 | `s_menu` | 菜单/页面(纯导航,挂模块做组织维度) |
|
||||
| 8 | `s_autocode` | 自动编码规则 |
|
||||
| 9 | `s_role` | 角色 |
|
||||
| 10 | `s_user_role` | 用户-角色 |
|
||||
| 11 | `s_power` | 权限点(menu/entity/action/field) |
|
||||
| 12 | `s_role_power` | 角色-权限 |
|
||||
| 13 | `s_i18n` | 多语言资源 |
|
||||
| 14 | `s_log_technical` | 技术日志(请求链路/SQL/异常/性能) |
|
||||
| 15 | `s_log_login` | 登录日志(登录/退出/失败/锁定) |
|
||||
| 16 | `s_log_audit` | 业务审计事件 |
|
||||
| 17 | `s_log_audit_field` | 审计字段变更明细 |
|
||||
| 7 | `s_menu` | 菜单/页面导航(同时作为统一配置入口) |
|
||||
| 8 | `s_page_profile` | 菜单页面配置档(一个菜单入口的一套页面行为) |
|
||||
| 9 | `s_page_field` | 页面字段覆盖(菜单级显示、排序、表单/查询覆盖) |
|
||||
| 10 | `s_autocode` | 自动编码规则 |
|
||||
| 11 | `s_role` | 角色 |
|
||||
| 12 | `s_user_role` | 用户-角色 |
|
||||
| 13 | `s_power` | 权限点(menu/entity/action/field) |
|
||||
| 14 | `s_role_power` | 角色-权限 |
|
||||
| 15 | `s_i18n` | 多语言资源 |
|
||||
| 16 | `s_log_technical` | 技术日志(请求链路/SQL/异常/性能) |
|
||||
| 17 | `s_log_login` | 登录日志(登录/退出/失败/锁定) |
|
||||
| 18 | `s_log_audit` | 业务审计事件 |
|
||||
| 19 | `s_log_audit_field` | 审计字段变更明细 |
|
||||
## 3. 各表结构
|
||||
|
||||
### 3.1 s_module 模块
|
||||
@@ -144,7 +149,7 @@ hard 临时记录:校验权限后直接物理删除
|
||||
|
||||
### 3.4 s_field 字段
|
||||
|
||||
合并旧系统 `s_module_field` + `_edit` + `_list` + `_query` 四张表,字段增删改只需维护一处。
|
||||
合并旧系统 `s_module_field` + `_edit` + `_list` + `_query` 四张表,保存实体级默认定义。菜单页面的个性化显示、排序和交互覆盖放在 `s_page_field`,避免同一实体被多个菜单引用时互相影响。
|
||||
|
||||
```sql
|
||||
create table s_field (
|
||||
@@ -234,7 +239,7 @@ create index ix_s_relation_target on s_relation (b_target_entity_id);
|
||||
|
||||
### 3.7 s_menu 菜单/页面
|
||||
|
||||
纯导航树,通过 `b_module_id` 挂到模块做组织维度,通过 `b_entity_id` 引用实体;模块与菜单可互相独立演进(一个模块多个菜单入口、一个菜单也可属于无模块的公共区)。
|
||||
数据层的菜单仍是导航树,不直接保存字段、列表或表单配置;管理端把它作为统一配置入口。通过 `b_module_id` 挂到模块做组织维度,通过 `b_entity_id` 引用实体;模块与菜单可互相独立演进(一个模块多个菜单入口、一个菜单也可属于无模块的公共区)。选中菜单后,配置工作台聚合展示其页面档、实体、字段、关联实体、自动编码和权限。
|
||||
|
||||
```sql
|
||||
create table s_menu (
|
||||
@@ -265,7 +270,67 @@ create index ix_s_menu_entity on s_menu (b_entity_id);
|
||||
|
||||
菜单类型约束由元数据校验器执行:`directory` 不填业务路由;`page` 必须填 `b_route`;`data` 必须填 `b_entity_id`;`external` 必须填 `b_route` 和 `b_power_code`。目录仅在存在至少一个可访问子页面时显示。
|
||||
|
||||
### 3.8 s_autocode 自动编码
|
||||
### 3.8 s_page_profile 页面配置档
|
||||
|
||||
页面配置档是菜单入口与实体默认元数据之间的配置上下文。一个 `data` 菜单通常对应一个默认配置档;同一实体被多个菜单复用时,每个菜单可以拥有独立配置档。配置档不复制实体字段定义,只保存页面级行为。
|
||||
|
||||
```sql
|
||||
create table s_page_profile (
|
||||
b_id bigint identity(1, 1) not null,
|
||||
b_menu_id bigint not null, -- 配置入口
|
||||
b_entity_id bigint not null, -- 页面主实体
|
||||
b_code varchar(50) not null,
|
||||
b_name nvarchar(100) not null,
|
||||
b_list_enabled int not null default 1,
|
||||
b_edit_enabled int not null default 1,
|
||||
b_query_enabled int not null default 1,
|
||||
b_canuse int not null default 1,
|
||||
b_inputuser_id bigint null,
|
||||
b_inputdatetime datetime2 null default sysdatetime(),
|
||||
b_updateuser_id bigint null,
|
||||
b_updatedatetime datetime2 null,
|
||||
constraint pk_s_page_profile primary key (b_id),
|
||||
constraint ux_s_page_profile_menu unique (b_menu_id),
|
||||
constraint ux_s_page_profile_code unique (b_code),
|
||||
constraint fk_s_page_profile_menu foreign key (b_menu_id) references s_menu (b_id),
|
||||
constraint fk_s_page_profile_entity foreign key (b_entity_id) references s_entity (b_id)
|
||||
);
|
||||
```
|
||||
|
||||
配置工作台按以下顺序呈现:页面基本信息 → 数据源/实体 → 字段与分组 → 列表 → 表单 → 查询 → 关联实体 → 自动编码 → 权限。保存时由服务层校验菜单类型、实体类型和模块依赖关系。
|
||||
|
||||
没有独立菜单的子实体、费用明细、选择器和公共字典,通过主实体的 `s_relation` 或页面关联配置挂在所属页面下;它们不需要重复创建菜单,也可以在同一个配置工作台中维护。
|
||||
|
||||
### 3.9 s_page_field 页面字段覆盖
|
||||
|
||||
只记录相对于 `s_field` 默认值的页面级覆盖;为空表示继承实体字段默认配置。这样既保留旧系统“进入菜单即可完整配置”的体验,又避免不同菜单修改同一实体时产生配置串扰。
|
||||
|
||||
```sql
|
||||
create table s_page_field (
|
||||
b_id bigint identity(1, 1) not null,
|
||||
b_page_profile_id bigint not null,
|
||||
b_field_id bigint not null,
|
||||
b_group_id bigint null,
|
||||
b_visible int null,
|
||||
b_xh int null,
|
||||
b_width int null,
|
||||
b_required int null,
|
||||
b_readonly int null,
|
||||
b_disabled int null,
|
||||
b_queryable int null,
|
||||
b_querytype varchar(30) null,
|
||||
b_queryop varchar(20) null,
|
||||
constraint pk_s_page_field primary key (b_id),
|
||||
constraint ux_s_page_field unique (b_page_profile_id, b_field_id),
|
||||
constraint fk_s_page_field_profile foreign key (b_page_profile_id) references s_page_profile (b_id),
|
||||
constraint fk_s_page_field_field foreign key (b_field_id) references s_field (b_id),
|
||||
constraint fk_s_page_field_group foreign key (b_group_id) references s_field_group (b_id)
|
||||
);
|
||||
|
||||
create index ix_s_page_field_profile on s_page_field (b_page_profile_id, b_xh, b_id);
|
||||
```
|
||||
|
||||
### 3.10 s_autocode 自动编码
|
||||
|
||||
独立于模块,挂实体 + 目标字段,规则可复用。并发控制:应用层使用原子更新取号(`update ... set b_currentvalue = b_currentvalue + 1 output inserted.b_currentvalue`),避免自增竞争。
|
||||
|
||||
@@ -289,7 +354,7 @@ create table s_autocode (
|
||||
);
|
||||
```
|
||||
|
||||
### 3.9 s_role 角色
|
||||
### 3.11 s_role 角色
|
||||
|
||||
```sql
|
||||
create table s_role (
|
||||
@@ -308,7 +373,7 @@ create table s_role (
|
||||
);
|
||||
```
|
||||
|
||||
### 3.10 s_user_role 用户-角色
|
||||
### 3.12 s_user_role 用户-角色
|
||||
|
||||
```sql
|
||||
create table s_user_role (
|
||||
@@ -325,7 +390,7 @@ create table s_user_role (
|
||||
create index ix_s_user_role_role on s_user_role (b_role_id, b_canuse);
|
||||
```
|
||||
|
||||
### 3.11 s_power 权限点
|
||||
### 3.13 s_power 权限点
|
||||
|
||||
```sql
|
||||
create table s_power (
|
||||
@@ -344,7 +409,7 @@ create table s_power (
|
||||
create index ix_s_power_resource on s_power (b_power_type, b_resource, b_canuse);
|
||||
```
|
||||
|
||||
### 3.12 s_role_power 角色-权限
|
||||
### 3.14 s_role_power 角色-权限
|
||||
|
||||
```sql
|
||||
create table s_role_power (
|
||||
@@ -364,7 +429,7 @@ create index ix_s_role_power_power on s_role_power (b_power_id, b_canuse);
|
||||
|
||||
菜单显示继承页面访问权限:用户拥有 `s_menu.b_power_code` 对应权限时菜单才显示;前端只控制显示,后端运行时必须再次校验同一个权限编码。
|
||||
|
||||
### 3.13 s_i18n 多语言
|
||||
### 3.15 s_i18n 多语言
|
||||
|
||||
```sql
|
||||
create table s_i18n (
|
||||
@@ -381,7 +446,7 @@ create table s_i18n (
|
||||
create index ix_s_i18n_locale on s_i18n (b_locale, b_canuse);
|
||||
```
|
||||
|
||||
### 3.14 s_log_technical 技术日志
|
||||
### 3.16 s_log_technical 技术日志
|
||||
|
||||
用于请求链路、SQL、异常、性能与外部接口排查。高频日志推荐写入 Loki/OpenSearch/Elasticsearch 等日志平台,本表用于集中日志库或短期数据库兜底,不建议长期写入租户业务库。
|
||||
|
||||
@@ -414,7 +479,7 @@ create index ix_s_log_technical_org on s_log_technical (b_org_id, b_occurdatetim
|
||||
create index ix_s_log_technical_level on s_log_technical (b_loglevel, b_occurdatetime desc);
|
||||
```
|
||||
|
||||
### 3.15 s_log_login 登录日志
|
||||
### 3.17 s_log_login 登录日志
|
||||
|
||||
记录登录成功、失败、退出、刷新令牌与账号锁定等安全事件,可通过 `b_request_id` / `b_trace_id` 与技术日志关联。
|
||||
|
||||
@@ -447,7 +512,7 @@ create index ix_s_log_login_account on s_log_login (b_account, b_occurdatetime d
|
||||
create index ix_s_log_login_result on s_log_login (b_login_result, b_occurdatetime desc);
|
||||
```
|
||||
|
||||
### 3.16 业务审计事件与字段变更
|
||||
### 3.18 业务审计事件与字段变更
|
||||
|
||||
审计事件与字段变更分表存储:事件一条、字段变更多条,通过 `b_event_id` 关联。字段级明细是**面向客户的硬需求**(客户可在门户查看某个单号发生过哪些字段变更),**字段一旦发生修改即写入明细**,不按字段配置开关;`b_visibility` 控制内部/客户可见。
|
||||
|
||||
@@ -507,18 +572,21 @@ create index ix_s_log_audit_field_customer on s_log_audit_field (b_entity_code,
|
||||
树形(`b_parent_id`)表达"业务分组归属"(基础资料 > 海运 > 订舱),是货代业务的真实组织诉求,且操作直观;但树无法表达"finance 依赖 master-data"这类跨分支依赖,所以依赖单独用 `s_module_dependency` 表达并强制 DAG。树归树、依赖归依赖,语义不再混淆。
|
||||
|
||||
### 4.3 为什么字段合并单表
|
||||
旧系统字段配置拆 4 张表,字段新增要同步维护 4 处、分组标题另存一份,配置漂移是常态。合并后字段的"定义 + 列表 + 表单 + 查询"行为集中在一行,增删改一处完成;代价是行较宽,但对元数据表(数量级远小于业务表)完全可接受。
|
||||
旧系统字段配置拆 4 张表,字段新增要同步维护 4 处、分组标题另存一份,配置漂移是常态。合并后字段的定义与默认列表/表单/查询行为集中在一行,实体字段增删改只需维护一处;菜单级差异由 `s_page_field` 覆盖,既避免重复定义,又保留不同页面的独立布局。代价是行较宽,但对元数据表(数量级远小于业务表)完全可接受。
|
||||
|
||||
### 4.4 为什么菜单独立且引用实体
|
||||
导航是展示层概念,数据是业务层概念。`s_menu` 引用 `b_entity_id` 而非模块,同一实体可在多个菜单入口出现(如"订舱列表"与"订舱审核"共用 `ocean_booking` 实体但绑定不同权限)。
|
||||
导航是展示层概念,数据是业务层概念。`s_menu` 引用 `b_entity_id` 而非模块,同一实体可在多个菜单入口出现(如"订舱列表"与"订舱审核"共用 `ocean_booking` 实体但绑定不同权限)。菜单树同时是管理员的统一配置入口,配置工作台通过菜单聚合其页面档、字段覆盖、关联实体和权限;因此数据库职责分离不会牺牲旧系统的定位效率。
|
||||
|
||||
### 4.5 为什么权限引入角色层
|
||||
### 4.5 为什么增加页面配置档
|
||||
实体是可复用的数据定义,菜单是具体的使用入口。同一实体可能需要不同的列表列、表单可编辑性或查询条件,不能把这些差异继续写回实体字段默认值。`s_page_profile` 表示一个菜单入口的页面上下文,`s_page_field` 只记录覆盖项;管理员仍然只需进入对应菜单即可完成完整配置。
|
||||
|
||||
### 4.6 为什么权限引入角色层
|
||||
旧系统 `b_user_power` 是用户直挂权限,用户一多授权矩阵爆炸。本方案 `s_role` + `s_user_role` + `s_role_power` 三层,权限点 `s_power` 支持到 menu/entity/action/field 四级粒度,为后续数据范围、字段脱敏策略留出扩展位。
|
||||
|
||||
### 4.6 自动编码的并发处理
|
||||
### 4.7 自动编码的并发处理
|
||||
`b_currentvalue` 若采用"读-改-写"必然在并发下产生重号。方案要求应用层用原子自增语句取号(`update ... set b_currentvalue = b_currentvalue + 1 output inserted.b_currentvalue`),并把取号与业务保存放同一事务,确保单号不重不漏。
|
||||
|
||||
### 4.7 日志的写入去向
|
||||
### 4.8 日志的写入去向
|
||||
三类日志职责不同、量级不同,不建议全部落租户业务库:
|
||||
- 技术日志高频(每条请求、SQL、异常),推荐写 Loki/ES 等日志平台,`s_log_technical` 仅作集中日志库或短期兜底。
|
||||
- 登录日志与业务审计属于安全与合规数据,低频但必须长期留存,落库保存;审计字段明细在字段发生修改时全量写入,不做按字段开关,数据量按变更频率自然收敛。
|
||||
@@ -532,6 +600,9 @@ create index ix_s_log_audit_field_customer on s_log_audit_field (b_entity_code,
|
||||
| --- | --- | --- |
|
||||
| `s_user_role` | `b_user_id` | 当前身份系统的用户 `b_id` |
|
||||
| `s_menu` | `b_module_id` | `s_module.b_id` |
|
||||
| `s_page_profile` | `b_entity_id` | 必须与所属 `s_menu.b_entity_id` 一致,且实体已启用 |
|
||||
| `s_page_field` | `b_field_id` | 必须属于所属 `s_page_profile.b_entity_id` |
|
||||
| `s_page_field` | `b_group_id` | 必须属于所属实体的 `s_field_group.b_id` |
|
||||
| `s_field` | `b_group_id` | `s_field_group.b_id` |
|
||||
| `s_menu` | `b_power_code` | `s_power.b_code` |
|
||||
| `s_entity` | `b_viewtable` / `b_savetable` | Schema Compiler 生成的物理表/视图 |
|
||||
@@ -558,6 +629,7 @@ create index ix_s_log_audit_field_customer on s_log_audit_field (b_entity_code,
|
||||
| `s_module_field_group` | `s_field_group` | 分组挂实体 |
|
||||
| `s_module_auto_code` | `s_autocode` | 挂实体+字段,原子取号 |
|
||||
| `s_module_power` + `b_user_power` | `s_power` + `s_role` + `s_user_role` + `s_role_power` | 引入角色层 |
|
||||
| (旧系统由 module 直接承载) | `s_page_profile` + `s_page_field` | 菜单级页面配置与字段覆盖,保留集中配置体验 |
|
||||
| `b_i18n` | `s_i18n` | 增加资源类型维度 |
|
||||
| `s_login_log` | `s_log_login` | 字段扩充(设备/UA/请求链路) |
|
||||
| (无) | `s_module_dependency` | 新增依赖 DAG |
|
||||
|
||||
Reference in new issue
Block a user