Files
workspace/code/fms/FMS新系统核心表结构设计.md
T
2026-09-08 22:39:22 +08:00

510 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FMS 新系统核心表结构
> 目标结构基于 2026-09-04 只读拉取的 `FMS-NEW` 数据库整理。
> 平台配置使用字符串业务键;未来业务数据表使用 `bigint` 雪花 ID。
## 0. SQL 驱动与动态配置
本系统是 SQL 驱动的内部 ERP。能通过 SQL 完成的查询、关联、过滤、排序、分页、聚合、权限过滤和数据范围过滤,优先在 SQL 层完成,不在业务代码中加载大量数据后再处理。
- 模块、字段、查询条件、排序、字段权限和数据范围等规则,优先从本文件定义的配置表读取,由通用查询逻辑动态生成 SQL;
- 动态 SQL 的表名、字段名和操作符只能来自受控的模块元数据、字段定义和白名单,业务值使用参数传递;
- 配置驱动的动态 SQL 不等于保存任意 SQL 字符串。不得把未审查的前端字符串或整段 SQL 直接保存后执行;
- 固定 SQL 仅用于稳定且明确的特殊业务逻辑,且应保持通用,不为单个页面或单个用户复制一套查询接口;
- 前端权限控制只负责界面展示,模块操作、字段访问和数据范围必须在后端查询或写入 SQL 中落实;用户个性化只能调整布局,不能放宽权限。
## 1. 用户表
```sql
create table dbo.b_user (
b_id varchar(50) not null primary key, -- 用户账号本身
b_name nvarchar(100) null,
b_bz nvarchar(400) null,
b_password nvarchar(255) not null,
b_canuse tinyint not null default 1,
b_logintime tinyint not null default 0
);
```
## 2. 模块
```sql
create table dbo.s_module (
b_id varchar(50) not null primary key, -- 模块编码本身,创建后不可修改
b_parent_id varchar(50) null,
b_name nvarchar(200) not null,
b_i18n varchar(150) null,
b_module_type varchar(20) not null,
b_viewtable varchar(128) null,
b_savetable varchar(128) null,
b_keyfield varchar(50) null,
b_orderfield varchar(500) null,
b_delete_policy varchar(20) null,
b_canuse tinyint not null default 1,
b_xh int not null default 0,
b_bz nvarchar(2000) null,
b_edit_mode varchar(10) null
);
create index ix_s_module_parent
on dbo.s_module (b_parent_id, b_xh, b_canuse, b_id);
create index ix_s_module_order
on dbo.s_module (b_xh, b_id);
create index ix_s_module_savetable
on dbo.s_module (b_savetable);
```
## 3. 字段和字段布局
> 状态字段统一使用 `tinyint` 保存 0/1,由业务层负责校验取值。`b_canuse` 表示记录或配置是否启用、是否参与运行;`b_visible` 表示当前列表或编辑场景是否展示,二者语义不同,不互相替代。
```sql
create table dbo.s_field (
b_module_id varchar(50) not null,
b_field varchar(50) not null,
b_name nvarchar(200) not null,
b_i18n varchar(150) null,
b_type varchar(30) not null,
b_dbtype varchar(30) not null,
b_length int null,
b_precision int null,
b_scale int null,
b_nullable tinyint not null default 1,
b_default_value nvarchar(1000) null,
b_db_default nvarchar(1000) null,
b_writemode varchar(20) not null default 'direct',
b_options nvarchar(max) null,
b_canuse tinyint not null default 1,
b_xh int not null default 0, -- 字段定义顺序:模块「同步字段」时按查询视图列序(10 起步)重排
primary key (b_module_id, b_field)
);
create index ix_s_field_module_order
on dbo.s_field (b_module_id, b_xh, b_field);
create table dbo.s_field_group (
b_id varchar(50) not null, -- 分组编码本身,模块内唯一,创建后不可修改
b_module_id varchar(50) not null,
b_parent_id varchar(50) null, -- 父分组编码,NULL 表示根分组
b_title nvarchar(200) not null,
b_i18n varchar(150) null,
b_xh int not null default 0,
b_canuse tinyint not null default 1,
b_colspan int not null default 48,
b_layout_direction varchar(10) null default 'vertical',
primary key (b_module_id, b_id)
);
create index ix_s_field_group_parent
on dbo.s_field_group (b_module_id, b_parent_id, b_xh, b_id);
create index ix_s_field_group_order
on dbo.s_field_group (b_module_id, b_xh, b_id);
create table dbo.s_field_view (
b_module_id varchar(50) not null,
b_field varchar(50) not null,
b_group_id varchar(50) null, -- 引用同模块 s_field_group.b_id
b_visible tinyint not null default 1,
b_xh int not null default 0,
b_width int null,
primary key (b_module_id, b_field)
);
create index ix_s_field_view_order
on dbo.s_field_view (b_module_id, b_xh, b_field);
create table dbo.s_field_edit (
b_module_id varchar(50) not null,
b_field varchar(50) not null,
b_group_id varchar(50) null, -- 引用同模块 s_field_group.b_id
b_visible tinyint not null default 1,
b_required tinyint not null default 0,
b_readonly tinyint not null default 0,
b_disabled tinyint not null default 0,
b_xh int not null default 0,
b_width int null,
b_placeholder nvarchar(400) null,
b_colspan int not null default 12,
b_height int null,
primary key (b_module_id, b_field)
);
create index ix_s_field_edit_order
on dbo.s_field_edit (b_module_id, b_xh, b_field);
create table dbo.s_field_query (
b_module_id varchar(50) not null,
b_field varchar(50) not null,
b_component varchar(30) null,
b_condition_no int not null default 1,
b_operator varchar(20) not null,
b_default_value nvarchar(1000) null,
b_canuse tinyint not null default 1,
b_xh int not null default 0,
primary key (b_module_id, b_field, b_condition_no)
);
create index ix_s_field_query_order
on dbo.s_field_query (b_module_id, b_xh, b_field, b_condition_no);
```
## 4. 用户个性化
> 用户个性化只作用于列表布局和普通查询收起态,不改变模块级默认配置。权限过滤优先于个人偏好,个人偏好不能越过字段权限或字段 `b_canuse` 限制。
```sql
create table dbo.s_user_field_pref (
b_user_id varchar(50) not null,
b_module_id varchar(50) not null,
b_field varchar(50) not null,
b_mode varchar(10) not null, -- 'view' 查看列表 / 'edit' 可编辑列表
b_visible tinyint not null default 1,
b_xh int not null default 0,
b_width int null,
b_updatedatetime datetime2 null,
primary key (b_user_id, b_module_id, b_field, b_mode)
);
create index ix_s_user_field_pref_order
on dbo.s_user_field_pref (b_user_id, b_module_id, b_mode, b_xh, b_field);
create table dbo.s_user_query_field_pref (
b_user_id varchar(50) not null,
b_module_id varchar(50) not null,
b_field varchar(50) not null,
b_quick_visible tinyint not null default 1, -- 普通查询收起态是否显示
b_xh int not null default 0, -- 普通查询字段顺序
b_updatedatetime datetime2 null,
primary key (b_user_id, b_module_id, b_field)
);
create index ix_s_user_query_field_pref_order
on dbo.s_user_query_field_pref (b_user_id, b_module_id, b_xh, b_field);
```
说明:
- `s_user_field_pref` 的 `b_mode` 区分查看列表和可编辑列表两套个人布局;
- `s_user_query_field_pref` 不区分普通查询/高级查询模式,因为两者使用同一批查询字段;
- 查询偏好只影响普通查询收起态,展开态和高级查询仍展示所有有权限且启用的查询字段;
- 没有个人偏好时回落到对应的 `s_field_view` / `s_field_edit` / `s_field_query` 默认配置;
- “恢复默认”删除当前用户、当前模块对应的个人偏好记录。
## 5. 自动编码
```sql
create table dbo.s_autocode (
b_module_id varchar(50) not null,
b_field varchar(50) not null,
b_prefix nvarchar(200) not null default N'',
b_dateformat varchar(30) not null default 'yyyyMM',
b_separator nvarchar(20) not null default N'-',
b_seqwidth int not null default 5,
b_resettype varchar(10) not null default 'month',
b_startvalue bigint not null default 1,
b_currentperiod varchar(8) null,
b_currentvalue bigint not null default 0,
b_canuse tinyint not null default 1,
primary key (b_module_id, b_field)
);
```
## 6. 模块关系
```sql
create table dbo.s_relation (
b_module_id varchar(50) not null,
b_target_module_id varchar(50) not null,
b_relation_type varchar(20) not null,
b_field varchar(50) not null,
b_target_field varchar(50) not null,
b_foreign_side varchar(10) null,
b_junction_module_id varchar(50) null,
b_junction_field varchar(50) null,
b_junction_target_field varchar(50) null,
b_ownership varchar(20) not null default 'reference',
b_owner_side varchar(10) null,
b_delete_policy varchar(20) null,
b_order_module_id varchar(50) null,
b_order_field varchar(50) null,
b_canuse tinyint not null default 1,
b_xh int not null default 0,
primary key (b_module_id, b_field, b_target_module_id, b_target_field)
);
create index ix_s_relation_module
on dbo.s_relation (b_module_id, b_canuse, b_xh, b_field, b_target_module_id);
create index ix_s_relation_target
on dbo.s_relation (b_target_module_id);
```
## 7. 菜单
```sql
create table dbo.s_menu (
b_id varchar(50) not null primary key, -- 菜单编码本身
b_parent_id varchar(50) null,
b_name nvarchar(200) not null,
b_i18n varchar(150) null,
b_menu_type varchar(20) not null,
b_route varchar(200) null,
b_module_id varchar(50) null,
b_icon varchar(50) null,
b_xh int not null default 0,
b_canuse tinyint not null default 1
);
create index ix_s_menu_parent
on dbo.s_menu (b_parent_id, b_xh, b_canuse, b_id);
create index ix_s_menu_order
on dbo.s_menu (b_xh, b_id);
create index ix_s_menu_module
on dbo.s_menu (b_module_id);
create index ix_s_menu_route
on dbo.s_menu (b_route, b_canuse, b_id);
```
## 8. 权限
> 权限直接授予用户,不引入角色继承。`s_power.b_id` 是权限编码本身(业务键即主键),格式 `类型.对象[.能力]`,例如 `menu.cw_fee`、`module.cw_fee`、`action.cw_fee.audit`;完整规范见《字段权限与用户个性化设计.md》「权限编码规范」。字段授权使用独立的字段访问策略表。
```sql
create table dbo.s_power (
b_id varchar(100) not null, -- 权限编码即主键:menu.cw_fee / module.cw_fee / action.cw_fee.audit
b_name nvarchar(200) not null,
b_i18n varchar(150) null,
b_power_type varchar(20) not null, -- page/menu/module/action/report
b_owner_type varchar(20) not null, -- 归属对象类型:page/menu/module/report;action 时为所属模块或页面
b_owner_id varchar(50) not null, -- 归属对象编码:菜单/模块/页面/报表编码
b_capability varchar(30) null, -- action 的操作或动作编码:create/update/delete/export/audit...;入口类为 null
b_operation varchar(30) null, -- 兼容列:access/execute,由 b_power_type 派生
b_canuse tinyint not null default 1,
b_xh int not null default 0,
primary key (b_id)
);
create index ix_s_power_owner
on dbo.s_power (b_power_type, b_owner_type, b_owner_id, b_capability, b_canuse);
create table dbo.s_user_power (
b_user_id varchar(50) not null,
b_power_id varchar(100) not null, -- s_power.b_id,即权限编码
b_canuse tinyint not null default 1,
b_updatedatetime datetime2 null,
primary key (b_user_id, b_power_id)
);
create index ix_s_user_power_user
on dbo.s_user_power (b_user_id, b_canuse, b_power_id);
create table dbo.s_user_field_permission (
b_user_id varchar(50) not null,
b_module_id varchar(50) not null,
b_field varchar(128) not null,
b_access_mode varchar(10) not null, -- hidden / view / edit
b_allow_query tinyint not null default 0,
b_allow_export tinyint not null default 0,
b_canuse tinyint not null default 1,
b_updatedatetime datetime2 null,
primary key (b_user_id, b_module_id, b_field)
);
create index ix_s_user_field_permission_module
on dbo.s_user_field_permission (b_user_id, b_module_id, b_canuse, b_field);
create table dbo.s_user_data_scope (
b_user_id varchar(50) not null,
b_module_id varchar(50) not null,
b_operation varchar(30) not null, -- * / read / create / update / delete / export;* 表示模块默认范围
b_scope_level varchar(10) not null default 'default', -- default / override
b_scope_no int not null default 1,
b_scope_type varchar(30) not null, -- own / department / department_tree / all / custom
b_scope_field varchar(50) null, -- 业务数据中的归属用户/部门字段
b_scope_value varchar(100) null, -- custom 时每个用户/部门值一行
b_updatedatetime datetime2 null,
primary key (b_user_id, b_module_id, b_operation, b_scope_level, b_scope_no),
constraint ck_s_user_data_scope_level check (b_scope_level in ('default','override')),
constraint ck_s_user_data_scope_operation check (
(b_scope_level = 'default' and b_operation = '*')
or (b_scope_level = 'override' and b_operation in ('read','create','update','delete','export'))
)
);
create index ix_s_user_data_scope_module
on dbo.s_user_data_scope (b_user_id, b_module_id, b_operation, b_scope_level, b_scope_type);
```
权限点约定:
- `page.<页面编码>` / `menu.<菜单编码>` / `report.<报表编码>`:入口访问权限,报表列表中的每个报表可作为一个资源;
- `module.<模块编码>`:模块访问(≈ read,能否查看该模块数据);
- `action.<模块编码>.<操作或动作>`:数据操作(`create` / `update` / `delete` / `export`)与业务动作(`audit` / `settle` 等)统一为动作权限;
- `s_user_field_permission`:字段 `hidden/view/edit` 访问级别,以及独立的查询/导出能力;
- `b_power_type` 和 `b_capability` 是可扩展编码,新增资源类型不新增权限表,但需要补充权限定义和前端/后端校验规则;
- 动作必须带所属模块命名空间,例如 `action.cw_fee.audit`,不得使用裸 `action.audit`;**动作归属模块而非菜单**,避免同一个动作在不同菜单下被重复配置。
权限计算约定:
- 入口权限默认拒绝:`page.*`、`menu.*`、`report.*` 与 `action.*` 必须存在有效的 `s_user_power` 授权记录;停用的权限点或授权记录不生效;
- 模块操作 `action.<模块编码>.<操作>` 在没有授权记录时**默认放行还是默认拒绝尚未最终确定**,见《字段权限与用户个性化设计.md》「待确认事项」第 1 条;`s_power` 权限点或用户授权记录存在 `b_canuse = 0` 时视为明确禁用;
- 字段没有有效用户策略记录时不增加限制,按 `s_field`、`s_field_view`、`s_field_edit`、`s_field_query` 等模块默认配置执行;有效的 `s_user_field_permission` 只能收紧权限;`hidden` 表示不可见,`view` 表示可见只读,`edit` 表示可见可编辑;
- `hidden` 字段不可查询、不可导出;`b_allow_query` / `b_allow_export` 不能突破字段访问级别;
- 模块数据操作权限与数据范围同时生效:先判断操作权限,再按 `s_user_data_scope` 限制数据行;数据范围没有配置时视为 `all`;
- 数据范围优先使用当前操作的 `override`,没有 `override` 时使用 `operation = '*'` 的 `default`;同一层级的多条范围按 OR 合并;
- `read`、`export`、`update`、`delete` 在 SQL 行条件中应用数据范围;`create` 在写入前校验新增数据是否符合数据范围;
- 菜单/页面/报表访问权限只控制入口和访问,不能替代模块、字段或数据范围权限。
## 9. 多语言
```sql
-- b_i18n / b_key 保存完整资源键,不追加 .name 或 .title 后缀:
-- s_module.b_i18n = module.<module_id>
-- s_field.b_i18n = field.<module_id>.<b_field>
-- s_field_group.b_i18n = field_group.<module_id>.<b_id>
-- s_menu.b_i18n = menu.<b_id>
create table dbo.s_i18n_type (
b_id varchar(20) not null primary key, -- 语言编码本身
b_name nvarchar(200) not null,
b_canuse tinyint not null default 1,
b_default tinyint not null default 0,
b_xh int not null default 0
);
create table dbo.s_i18n (
b_id varchar(200) not null primary key, -- b_key + ':' + b_locale
b_key varchar(150) not null,
b_locale varchar(20) not null,
b_value nvarchar(1000) not null,
b_i18ntype varchar(20) null,
b_canuse tinyint not null default 1,
b_module_id varchar(50) null,
unique (b_key, b_locale)
);
create index ix_s_i18n_module_key
on dbo.s_i18n (b_module_id, b_key, b_locale);
create index ix_s_i18n_locale
on dbo.s_i18n (b_locale, b_canuse);
```
## 10. 日志
> 日志主键由业务层统一生成 UUIDv7。UUID 主键使用非聚集索引;技术、登录、审计日志按发生时间建立聚集索引,审计字段明细按审计事件建立聚集索引。日志顺序以 `b_occurdatetime` 为准,不依赖 UUID 排序。
```sql
create table dbo.s_log_technical (
b_id uniqueidentifier not null, -- 业务层生成的 UUIDv7
b_user_id varchar(50) null, -- 稳定用户/账号标识;未认证请求时为 NULL
b_sourcetype varchar(20) null,
b_loglevel varchar(16) not null,
b_logcategory varchar(64) null,
b_operation varchar(100) null,
b_module_id varchar(50) null,
b_request_id varchar(64) null,
b_trace_id varchar(64) null,
b_server_node varchar(128) null,
b_duration_ms bigint null,
b_result_code varchar(50) null,
b_error_code varchar(50) null,
b_error_message nvarchar(4000) null,
b_exception_text nvarchar(max) null,
b_context_text nvarchar(max) null,
b_occurdatetime datetime2 default sysutcdatetime(),
constraint pk_s_log_technical primary key nonclustered (b_id)
);
create table dbo.s_log_login (
b_id uniqueidentifier not null, -- 业务层生成的 UUIDv7
b_user_id varchar(50) null, -- 当前系统中账号即用户稳定标识;登录失败且账号不存在时为 NULL
b_login_type varchar(20) not null,
b_login_result tinyint not null default 0,
b_fail_reason nvarchar(400) null,
b_ip varchar(50) null,
b_ip_location nvarchar(400) null,
b_browser varchar(100) null,
b_browser_version varchar(50) null,
b_os varchar(100) null,
b_os_version varchar(50) null,
b_device_type varchar(20) null,
b_user_agent nvarchar(max) null,
b_session_id varchar(50) null,
b_request_id varchar(64) null,
b_trace_id varchar(64) null,
b_occurdatetime datetime2 default sysutcdatetime(),
constraint pk_s_log_login primary key nonclustered (b_id)
);
create table dbo.s_log_audit (
b_id uniqueidentifier not null, -- 业务层生成的 UUIDv7
b_event_code varchar(50) not null,
b_module_id varchar(50) null,
b_data_id varchar(50) null,
b_business_no varchar(50) null,
b_operation varchar(100) null,
b_operator_id varchar(50) null,
b_operator_name nvarchar(200) null,
b_source_type varchar(20) not null default 'user',
b_visibility varchar(20) not null default 'internal',
b_request_id varchar(64) null,
b_trace_id varchar(64) null,
b_payload_text nvarchar(max) null,
b_occurdatetime datetime2 default sysutcdatetime(),
constraint pk_s_log_audit primary key nonclustered (b_id)
);
create table dbo.s_log_audit_field (
b_id uniqueidentifier not null, -- 业务层生成的 UUIDv7
b_event_id uniqueidentifier not null,
b_module_id varchar(50) null,
b_data_id varchar(50) null,
b_field varchar(50) not null,
b_field_label nvarchar(200) null,
b_before_value nvarchar(max) null,
b_after_value nvarchar(max) null,
b_before_display nvarchar(1000) null,
b_after_display nvarchar(1000) null,
b_visibility varchar(20) not null default 'internal',
constraint pk_s_log_audit_field primary key nonclustered (b_id)
);
create clustered index cx_s_log_technical_occur
on dbo.s_log_technical (b_occurdatetime, b_id);
create clustered index cx_s_log_login_occur
on dbo.s_log_login (b_occurdatetime, b_id);
create clustered index cx_s_log_audit_occur
on dbo.s_log_audit (b_occurdatetime, b_id);
create clustered index cx_s_log_audit_field_event
on dbo.s_log_audit_field (b_event_id, b_id);
create index ix_s_log_technical_trace
on dbo.s_log_technical (b_trace_id, b_occurdatetime);
create index ix_s_log_login_user
on dbo.s_log_login (b_user_id, b_occurdatetime);
create index ix_s_log_audit_module
on dbo.s_log_audit (b_module_id, b_data_id, b_occurdatetime);
```
## 11. 业务表主键约定
```sql
create table dbo.b_example (
b_id bigint not null primary key, -- 应用层雪花 ID
b_inputuser_id varchar(50) null, -- 引用 b_user.b_id
b_inputdatetime datetime2 default sysutcdatetime()
);
```