56 KiB
FMS 新系统核心表结构设计 V2
本文用于记录新版核心表结构方案,权限设计见第 14 节。旧版文档保留作为历史参考。
本系统以 SQL 为核心驱动。设计时应优先保证查询和数据处理的灵活性,并在各模块预留 SQL 扩展能力,支持传入 SQL、动态拼接查询条件、组合查询及自定义数据处理逻辑,以便通过 SQL 快速适配和解决复杂业务场景。
1. 数据表规范
本规范用于指导 AI 设计、新增和调整数据库表结构。除非已有表结构或业务要求明确指定,否则应优先遵循以下约定。
1.1 表名规范
- 统一使用小写
snake_case命名,单词之间使用下划线分隔。 - 系统配置、元数据和权限相关表使用
s_前缀,例如s_module、s_field。 - 业务数据表使用
b_前缀或明确的业务域前缀,例如cw_fee;组织与用户相关的基础主数据沿用现有b_前缀,例如b_user、b_dept。 - 表名应表达稳定的业务含义,避免使用页面名称、路由名称或临时功能名称作为表名。
1.2 表职责规范
- 一张表应围绕一个明确的业务对象、配置对象或关系对象设计。
- 系统表用于保存系统配置、元数据、权限和运行记录;业务表用于保存具体业务数据;关系表用于表达对象之间的关联关系。
- 不要为了适配单个页面重复创建业务表;页面差异优先通过模块配置、JSON 布局和 SQL 组合实现。
- 核心、稳定且需要被 SQL 查询的数据使用关系表;层级化、变化频繁的界面配置和用户偏好使用 JSON。
1.3 主键和关联字段规范
- 每张表必须定义稳定、明确的主键。业务对象优先使用
b_id作为业务编码主键;关系表可根据业务关系使用联合主键。 - 表之间的关联字段使用
<对象>_id或已有的b_module_id等明确命名,不使用无法判断来源的通用字段名。 - 主键和关联字段的数据类型、长度必须保持一致。
- 本设计不依赖数据库外键约束保证关联完整性,关联存在性和删除规则由业务层处理。
1.4 字段命名规范
- 字段统一使用小写
snake_case命名。 - 主表或主业务对象字段使用
b_前缀;明细、子表或扩展业务字段使用mx_前缀。 - 系统公共字段沿用现有命名,例如
b_id、b_name、b_bz、b_canuse、b_xh。 - 字段名应表达业务含义,避免使用
value、data、type1等无法说明用途的名称;同一含义在不同表中应使用统一命名。
1.5 通用字段规范
- 启用和停用统一使用
b_canuse,取值为0/1。 - 排序字段统一使用
b_xh,默认值为0;需要稳定排序时增加业务主键作为次排序字段。 - 名称、备注和多语言文本使用
nvarchar;编码、状态、类型和 SQL 标识类字段使用varchar。 - 日期时间优先使用
datetime2;金额、数量等数值字段根据业务精度明确设置precision和scale。 - 是否允许为空及默认值应根据业务语义明确设置,不能无理由全部允许为空或全部设置默认值。
1.6 通用系统审计字段
- 通用创建人使用
b_created_by,保存用户编码,不保存用户名称。 - 通用创建时间使用
b_created_at,类型使用datetime2。 - 通用最后修改人使用
b_updated_by,保存最后修改用户编码,不保存用户名称。 - 通用最后修改时间使用
b_updated_at,类型使用datetime2。 - 以上字段按表的实际审计需求选用;创建人、修改人和对应时间应保持成对出现。
- 审计字段不是所有表的必选项,由客户维护、需要追溯变更的业务表才需要;系统内部使用的关联表、权限表等可以不加。
- 例外:授权类表(
s_user_power、s_user_field_power、s_user_data_power)由用户在授权界面操作,需要追溯授权变更人,因此保留审计字段;s_power属于开发期维护的系统元数据,不设审计字段。 - 这些字段只表示系统层面的创建和修改信息,不替代业务字段中的录入人、审核人、负责人或当前操作人。
1.7 树结构规范
- 需要维护层级、支持新增和移动的树,统一采用“邻接表为主 +
b_depth、b_path冗余”的方案。 b_parent_id是树结构的权威关系,表示当前节点的直接父节点;b_depth和b_path是由业务层维护的派生字段。- 根节点的
b_parent_id为NULL,b_depth从0开始计算。 b_path使用稳定节点编码组成,并在每个节点编码前后保留/分隔符,例如/sea/container/;节点编码创建后原则上不可修改。b_path用于前缀查询子树,b_parent_id配合递归 CTE 用于查询父级、祖先和复杂层级关系。- 新增和移动节点时,必须在同一业务事务中同步更新当前节点及其全部后代的
b_depth和b_path。 - 业务层必须校验节点不能指向自身,不能形成循环;这些规则不使用数据库
CHECK或触发器约束。 - 当冗余字段可能不一致时,以
b_parent_id为准,并提供按父子关系重建b_depth、b_path的维护能力。 - 模块树、菜单树和部门树使用该规范;JSON 内的布局树只在 JSON 内维护,不额外建立树表;主子表和引用关系使用
s_relation,多对多通过中间表模块加两条one_to_many表达,都不使用树字段表达。
1.8 JSON 配置规范
- SQL Server 使用
nvarchar(max)保存 JSON,由业务层负责序列化、反序列化和结构校验。 - JSON 配置如果存在结构升级需求,可以在顶层包含
schemaVersion,用于业务层迁移;不单独在数据库表中保存重复的版本字段。 - JSON 中通过字段编码引用
s_field,不得重复定义数据库类型、长度、精度等核心字段元数据。 - 布局 JSON 保存完整的模块默认配置;用户偏好 JSON 只保存相对默认配置的偏离量。
- 表单显示、只读、必填、禁用、条件显示、默认值、选项、格式化和字段计算等界面行为配置放在对应的布局 JSON 中,不放入
s_field。 - 读取用户配置时,先加载模块默认配置,再应用用户偏好覆盖;恢复默认时删除对应偏好或清空偏离配置。
- JSON 可以扩展新属性,但不得随意改变已有属性语义;不再使用的属性由业务层兼容或迁移。
1.9 SQL 驱动规范
- 查询和数据处理优先采用可组合的 SQL 方案,避免把业务逻辑固化在单个页面或固定查询中。
- 各模块应预留 SQL 扩展入口,支持传入 SQL、动态拼接
where条件、选择字段、排序、分页、关联查询和组合查询。 - 通用配置无法覆盖业务场景时,优先通过 SQL 视图、查询 SQL、条件片段或数据处理 SQL 扩展,不要立即新增重复表或重复模块。
- SQL 参数、查询字段、排序字段和关联关系应由明确的模块元数据描述,使 AI 能根据模块定义生成和组合 SQL。
- SQL 配置只描述可复用的数据处理能力,具体运行参数由调用方传入。
1.10 索引和约束规范
- 主键、关联字段、常用查询条件、排序字段和唯一业务键应根据实际查询场景建立索引。
- 索引应服务于明确的查询或关联场景,避免无依据地为每个字段单独建立索引。
- 数据库层只保留主键、联合主键、索引和必要的唯一约束;唯一约束可用于保证业务键或组合业务键不重复。
- 不增加
CHECK、触发器等业务规则约束。字段取值、字段组合、状态流转、关联完整性和其他业务校验统一交给业务层处理。 - 非空和默认值属于字段定义的一部分,应根据业务语义设置,不使用数据库约束代替业务逻辑。
2. 总体设计
新版结构强化“模块”概念,同时避免为字段布局和表单结构建立过多关系表。
s_module
├── module 业务模块,可包含下级 module、data 或 virtual
├── data 数据模块,对应表、视图或查询 SQL,拥有字段定义
└── virtual 虚拟模块,不要求对应标准数据表,可由 SQL 或业务实现驱动
“子模块”不是模块类型,由 b_parent_id 表达任意层级的父子关系。
核心存储原则:
| 内容 | 存储方式 | 说明 |
|---|---|---|
| 模块定义 | 关系表 | 系统核心业务结构 |
| 字段定义 | 关系表 | 参与 SQL 生成、保存和数据处理 |
| 列表布局 | JSON | 支持列分组、冻结、格式化和扩展属性 |
| 表单布局 | JSON | 支持分组、栅格、选项卡和条件显示 |
| 查询配置 | JSON | 支持同字段多条件、条件组和嵌套逻辑 |
| 字段分组 | 布局 JSON 节点 | 不再建立独立字段分组表 |
| 用户偏好 | JSON 偏离量 | 只保存用户相对系统默认的修改 |
| 多语言 | 关系表 | 支持按语言查询、导入、导出和回退 |
| 用户与部门 | 关系表 | 用户归属部门,部门支持层级树 |
| 权限 | 关系表 | 见第 14 节,不与界面布局混合 |
3. 用户与部门
3.1 用户
-- 用户表
create table dbo.b_user (
b_id varchar(50) not null primary key, -- 用户编码(业务键主键)
b_name nvarchar(100) null, -- 用户名
b_dept_id varchar(50) null, -- 所属部门编码
b_bz nvarchar(400) null, -- 备注
b_password nvarchar(255) not null, -- 密码(明文保存)
b_canuse tinyint not null default 1, -- 是否启用(0/1)
b_logincount int not null default 0, -- 登录次数
b_created_by varchar(50) null, -- 创建人
b_created_at datetime2 null, -- 创建时间
b_updated_by varchar(50) null, -- 最后修改人
b_updated_at datetime2 null -- 最后修改时间
);
create index ix_b_user_dept
on dbo.b_user (b_dept_id, b_canuse, b_id);
3.2 部门
-- 部门表
create table dbo.b_dept (
b_id varchar(50) not null primary key, -- 部门编码(业务键主键)
b_parent_id varchar(50) null, -- 父部门编码,NULL 表示根节点
b_depth int not null default 0, -- 树层级,根节点为 0
b_path varchar(1000) not null, -- 根到当前部门的路径,例如 /cn/east/
b_name nvarchar(200) not null, -- 部门名称
b_i18n varchar(150) null, -- 多语言资源键
b_canuse tinyint not null default 1, -- 是否启用(0/1)
b_xh int not null default 0, -- 同级显示顺序
b_bz nvarchar(2000) null, -- 备注
b_created_by varchar(50) null, -- 创建人
b_created_at datetime2 null, -- 创建时间
b_updated_by varchar(50) null, -- 最后修改人
b_updated_at datetime2 null -- 最后修改时间
);
create index ix_b_dept_parent
on dbo.b_dept (b_parent_id, b_canuse, b_depth, b_xh, b_id);
create index ix_b_dept_path
on dbo.b_dept (b_path, b_canuse, b_id);
部门树使用 1.7 的树结构规范,b_parent_id 是权威关系,b_depth、b_path 是派生字段;b_path 的节点编码前缀用于查询下级部门。部门编码创建后原则上不可修改。b_user.b_dept_id 指向 b_dept.b_id,数据范围中的 dept、dept_tree 依赖该归属关系。
4. 模块
4.1 模块定义
-- 模块定义表
create table dbo.s_module (
b_id varchar(50) not null primary key, -- 模块编码(业务键主键)
b_parent_id varchar(50) null, -- 父模块编码,NULL 表示根节点
b_depth int not null default 0, -- 树层级,根节点为 0
b_path varchar(1000) not null, -- 根到当前节点的路径,例如 /sea/container/
b_name nvarchar(200) not null, -- 模块名称
b_i18n varchar(150) null, -- 多语言资源键
b_module_type varchar(20) not null, -- module / data / virtual
b_view_table varchar(128) null, -- 默认查询表或视图
b_save_table varchar(128) null, -- 默认保存目标表
b_key_field varchar(50) null, -- 主键字段
b_order_sql varchar(500) null, -- 默认排序 SQL
b_query_sql nvarchar(max) null, -- 可选查询 SQL 或 SQL 模板
b_config_json nvarchar(max) null, -- 低频模块扩展配置
b_canuse tinyint not null default 1, -- 是否启用(0/1)
b_xh int not null default 0, -- 同级显示顺序
b_bz nvarchar(2000) null -- 备注
);
create index ix_s_module_parent
on dbo.s_module (b_parent_id, b_canuse, b_depth, b_xh, b_id);
create index ix_s_module_path
on dbo.s_module (b_path, b_canuse, b_id);
create index ix_s_module_type
on dbo.s_module (b_module_type, b_canuse, b_xh, b_id);
create index ix_s_module_view_table
on dbo.s_module (b_view_table, b_canuse, b_id);
create index ix_s_module_save_table
on dbo.s_module (b_save_table, b_canuse, b_id);
4.2 模块类型约定
module
- 表示业务模块或业务域,例如海运、空运、财务、客户管理。
- 可以包含下级
module、data和virtual。 - 默认不配置字段、查询表、保存表和主键字段。
- 可作为模块级配置、统计、导航上下文和权限继承的边界。
data
- 表示具体数据对象,例如海运主单、集装箱、费用明细。
- 必须配置
b_view_table或b_query_sql作为查询来源。 - 可以配置
b_save_table作为默认保存目标。 b_key_field表示该数据对象的主键字段;没有稳定主键的查询数据可以为空。- 字段定义保存在
s_field,界面配置保存在s_module_schema。 - 主子表和引用关系通过
s_relation表达,不使用模块树表达。
virtual
- 表示不适合使用标准数据表和通用 CRUD 表达的业务能力。
- 可以由查询 SQL、组合 SQL、专用接口或业务处理器实现。
- 可以拥有字段定义和界面配置,但不强制配置
b_save_table。 - 没有标准保存表时,保存和业务动作由 SQL 或业务处理器实现。
模块类型、父子组合、字段适用规则和 SQL 配置的完整性由业务层校验,不建立 CHECK 约束。
4.3 模块树与数据关系边界
b_parent_id、b_depth、b_path共同构成模块树,其中b_parent_id是权威关系,后两者是冗余查询字段。b_path使用/包裹节点编码,例如/sea/、/sea/container/;查询模块子树时使用路径前缀匹配。module可以包含下级module、data和virtual;data和virtual默认作为叶子节点。- 新增或移动模块时,业务层必须在同一事务中更新当前模块及全部后代的
b_depth和b_path。 - 不允许模块指向自身或形成循环;
b_id创建后原则上不可修改;这些规则由业务层校验。 b_depth、b_path与b_parent_id不一致时,以b_parent_id为准并重建冗余字段。- 数据之间的主子表、引用和多对多关系统一由
s_relation表达。 - 模块级扩展配置使用
b_config_json,只保存低频、非核心的扩展属性,不重复保存字段定义、权限或界面布局。
4.4 查询来源优先级
数据加载时按以下规则确定默认查询来源:
- 配置
b_query_sql时,使用查询 SQL 或 SQL 模板; - 未配置
b_query_sql时,使用b_view_table; - 两者均未配置时,由虚拟模块的业务实现负责提供数据;
- 调用方传入的筛选、字段、排序和分页配置在默认查询来源上继续组合。
保存时按以下规则确定默认保存方式:
data模块配置b_save_table时,默认写入该表;- 未配置
b_save_table或需要特殊处理时,由业务层使用模块对应的 SQL 保存逻辑或业务处理器负责保存; virtual模块不要求具备通用保存能力。
5. 字段定义
字段定义保持关系表结构。数据库表或视图是物理字段结构的来源,s_field 只保存模块使用字段时所需的业务元数据。
当前阶段不在 s_field 中保存数据库类型、长度、精度、小数位等物理结构信息。保存器或 SQL 生成器需要这些信息时,直接读取数据库表或视图的实际结构;后续确定元数据同步方案后再补充持久化字段。
-- 字段定义表
create table dbo.s_field (
b_module_id varchar(50) not null, -- 所属 data / virtual 模块编码
b_field varchar(50) not null, -- 模块字段编码或查询结果别名
b_name nvarchar(200) not null, -- 字段名称
b_i18n varchar(150) null, -- 多语言资源键
b_type varchar(30) not null, -- 业务类型
b_canuse tinyint not null default 1, -- 是否启用(0/1)
b_xh int not null default 0, -- 默认顺序
primary key (b_module_id, b_field)
);
create index ix_s_field_module_order
on dbo.s_field (b_module_id, b_canuse, b_xh, b_field);
字段同步约定:
data模块可以从b_view_table或b_query_sql的结果结构增量同步字段。- 同步负责按来源表或视图的列结构对齐字段定义,不覆盖人工维护的字段名称和业务类型。
- 数据库中已删除的字段不直接物理删除,可由业务层标记为停用并提示人工确认。
- 计算字段、关联显示字段和虚拟字段可以人工登记,并通过模块查询 SQL 或界面配置 JSON 产生。
- 字段是否可以直接写入、是否只读以及是否需要自定义保存逻辑,不在
s_field中增加固定模式字段;由表单 JSON、模块保存配置或业务处理器根据具体场景处理。 b_type的取值集合由业务层维护,本版不做固定枚举。
6. 模块界面配置
列表布局、表单布局、查询配置和字段分组统一保存为 JSON,不再建立以下独立表:
s_field_group
s_field_view
s_field_edit
s_field_query
-- 模块界面配置表
create table dbo.s_module_schema (
b_module_id varchar(50) not null, -- 所属 data / virtual 模块编码
b_schema_type varchar(20) not null, -- view / edit / query
b_schema_json nvarchar(max) not null, -- 完整 JSON 配置
b_canuse tinyint not null default 1, -- 是否启用(0/1)
b_updated_by varchar(50) null, -- 最后修改人
b_updated_at datetime2 null, -- 最后修改时间
primary key (b_module_id, b_schema_type)
);
create index ix_s_module_schema_type
on dbo.s_module_schema (
b_module_id,
b_schema_type,
b_canuse
);
每个模块默认维护三行配置,分别对应列表、表单和查询:
view
edit
query
当前阶段不维护同一类型的多套命名配置。若未来确实需要多套列表、表单或查询方案,再增加配置编码字段,或在 JSON 内增加方案节点。
6.0 配置两层继承(系统默认 → 个人)
模块 UI 配置采用两层继承,由两张字符结构一致的表(s_module_schema 系统默认 + s_user_module_pref 个人覆盖)表达。配置层级由表本身体现,不在 JSON 内部用 default、system、user 等节点表示层级。
系统默认(s_module_schema) 产品/开发者提供的标准 Schema
↓ 覆盖
个人配置(s_user_module_pref) 用户在系统默认之上的个人调整
view、edit、query是三种一致的b_schema_type,两层共用同一命名,不拆成独立表,也不引入部门/角色/租户配置层。- 优先级:个人 > 系统默认。个人层无记录时即系统默认。
- 保存为通用:把当前查询布局回写为新的系统默认——直接更新
s_module_schema.query的 quick 顺序并标记隐藏字段,同时清空该用户的个人覆盖,所有用户默认即在改后配置。 - 覆盖方式:
s_module_schema保存完整默认 Schema;s_user_module_pref只保存相对系统默认的增量覆盖,不全量复制,因此系统默认修改后能自然继承。
6.1 列表配置示例
{
"schemaVersion": 1,
"columns": [
{
"field": "b_no",
"visible": true,
"width": 140,
"fixed": "left"
},
{
"type": "group",
"id": "group1",
"title": "费用信息",
"i18n": "group.cw_fee.view.group1",
"children": [
{ "field": "mx_amount", "width": 120, "format": "money" },
{ "field": "mx_currency", "width": 90 }
]
}
]
}
6.2 表单配置示例
{
"schemaVersion": 1,
"children": [
{
"type": "group",
"id": "group1",
"title": "基础信息",
"i18n": "group.sea_main.edit.group1",
"children": [
{
"type": "row",
"children": [
{ "field": "b_no", "span": 12, "readonly": true },
{ "field": "b_name", "span": 12, "required": true }
]
}
]
}
]
}
表单 JSON 可以描述字段的展示和交互行为,例如:
{
"field": "mx_amount",
"readonly": true,
"visibleWhen": { "field": "mx_type", "operator": "eq", "value": "tax" },
"formula": "mx_qty * mx_price"
}
其中 readonly、required、disabled、visibleWhen、defaultValue、options、format 和 formula 都属于界面配置,不放入 s_field。如果计算字段需要作为查询结果参与 SQL、排序或筛选,应在模块的 b_query_sql 中定义,而不是依赖表单 JSON。
6.3 查询配置示例
{
"schemaVersion": 1,
"quick": ["b_no", "b_name", "b_status"],
"groups": [
{
"logic": "and",
"conditions": [
{ "field": "b_status", "operator": "eq" },
{
"logic": "or",
"conditions": [
{ "field": "b_no", "operator": "like" },
{ "field": "b_name", "operator": "like" }
]
}
]
}
]
}
6.4 分组节点 id 与资源键
view、edit 的 JSON 里,分组节点(type: "group")用 id 表达稳定身份;层级由 children 嵌套表达,顺序由数组顺序表达,分组不建独立表(见本节开头的 s_field_group)。
"id": "group1" 按 schema 类型独立编号(view 与 edit 各自从 group1 开始)
- 唯一范围:分组 id 只在单个
b_schema_json内唯一。列表的group1与表单的group1是两处不同分组,互不引用,因此也不做模块内跨 schema 全局唯一。 - 稳定优先:编号一旦分配不再重排,删除分组留下的空洞允许存在(重排会改身份,进而改资源键)。
- 取号规则:新建分组取该配置内已用编号里最小的未用正整数;同时保留
s_i18n中该模块同 schema 已存在的分组键编号,避免新分组捡到已删除分组的编号、连带继承其残留译文。 - 老数据兼容:
id缺失或重复的节点由归一化补齐(先序、取最小可用编号),非group{n}的自定义 id 视为已占用并原样保留。归一化必须是确定性纯函数——加载后要与基线比较,若补号依赖外部上下文,同一份数据两次归一化结果会不同而被误判为未保存改动。 - 资源键:分组标题的资源键为
group.{模块编码}.{schema 类型}.{分组编号}(如group.cw_fee.view.group1),由 id 派生而非人工填写;节点上的i18n手填优先,未填时按此规则派生,使分组标题无需手工登记即进入多语言受管清单。schema 类型必须入键,否则列表与表单的同号分组会互相覆盖译文。
7. 用户个性化(个人层)
个人配置是两层继承的最上层,只保存相对系统默认(s_module_schema)的偏离量,不复制完整模块配置。与 s_module_schema 统一使用 b_schema_type + b_schema_json,按 schema 类型分别存一条。
-- 用户模块偏好表(个人层)
create table dbo.s_user_module_pref (
b_user_id varchar(50) not null, -- 用户编码(关联 b_user.b_id)
b_module_id varchar(50) not null, -- 模块编码
b_schema_type varchar(20) not null, -- view / edit / query
b_schema_json nvarchar(max) not null, -- 个人覆盖 JSON(相对系统默认)
b_updated_by varchar(50) null, -- 最后修改人
b_updated_at datetime2 null, -- 最后修改时间
primary key (b_user_id, b_module_id, b_schema_type)
);
create index ix_s_user_module_pref_module
on dbo.s_user_module_pref (b_module_id, b_user_id);
查询布局偏好示例(平铺、增量,不使用 view.default / query.default 节点):
{
"schemaVersion": 1,
"area": { "b_no": "quick", "b_bz": "hidden" },
"order": { "b_no": 10, "b_name": 20 }
}
合并规则(两层依次应用):
- 读取
s_module_schema系统默认(query 布局由quick列表 + 条件推导); - 读取当前用户的
s_user_module_pref.b_schema_json,按 schema 类型叠加个人偏离量,得到最终配置; - 已停用、已删除或无权限的字段(
s_user_field_power)不得因配置重新出现;权限过滤在配置合并之后执行,永远优先于配置; - 恢复默认(恢复通用)= 删除
s_user_module_pref对应行,回落到系统默认;「恢复默认」是删除覆盖,不是新增配置层。
8. 自动编码
-- 自动编码表
create table dbo.s_autocode (
b_module_id varchar(50) not null, -- data 模块编码
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(20) null, -- 当前周期
b_currentvalue bigint not null default 0, -- 当前值
b_canuse tinyint not null default 1, -- 是否启用(0/1)
primary key (b_module_id, b_field)
);
编码格式、重置类型和字段适用性由业务层校验。
9. 模块关系
模块树只表达业务模块层级,主子表、引用和多对多关系统一由 s_relation 表达。
-- 模块关系表
create table dbo.s_relation (
b_source_module_id varchar(50) not null, -- 源模块
b_source_field varchar(50) not null, -- 源字段
b_target_module_id varchar(50) not null, -- 目标模块
b_target_field varchar(50) not null, -- 目标字段
b_relation_type varchar(20) not null, -- one_to_one / one_to_many / many_to_one
b_canuse tinyint not null default 1, -- 是否启用(0/1)
b_xh int not null default 0, -- 显示顺序
primary key (
b_source_module_id,
b_source_field,
b_target_module_id,
b_target_field
)
);
create index ix_s_relation_source
on dbo.s_relation (b_source_module_id, b_canuse, b_xh, b_source_field, b_target_module_id);
create index ix_s_relation_target
on dbo.s_relation (b_target_module_id, b_canuse, b_source_module_id);
关系类型取值由业务层校验;主子表和引用关系都通过"源字段 → 目标字段"这一组映射表达。暂时不提供 many_to_many,多对多通过中间表模块加两条 one_to_many 关系表达。
10. 菜单
菜单负责导航和页面入口,模块负责业务结构和数据能力。一个菜单可以使用多个 data 或 virtual 模块,一个模块也可以被多个菜单复用。
-- 菜单表
create table dbo.s_menu (
b_id varchar(50) not null primary key, -- 菜单编码(业务键主键)
b_parent_id varchar(50) null, -- 父菜单编码,NULL 表示根节点
b_depth int not null default 0, -- 树层级,根节点为 0
b_path varchar(1000) not null, -- 根到当前菜单的路径,例如 /system/user/
b_name nvarchar(200) not null, -- 菜单名称
b_i18n varchar(150) null, -- 多语言资源键
b_menu_type varchar(20) not null, -- directory / page / external
b_route varchar(500) null, -- 页面路由或外链地址
b_icon varchar(50) null, -- 图标
b_xh int not null default 0, -- 显示顺序
b_canuse tinyint not null default 1 -- 是否启用(0/1)
);
create index ix_s_menu_parent
on dbo.s_menu (b_parent_id, b_canuse, b_depth, b_xh, b_id);
create index ix_s_menu_path
on dbo.s_menu (b_path, b_canuse, b_id);
create index ix_s_menu_route
on dbo.s_menu (b_route, b_canuse, b_id);
-- 菜单使用模块关系表
create table dbo.s_menu_module (
b_menu_id varchar(50) not null, -- 菜单编码
b_module_id varchar(50) not null, -- 页面实际使用的 data / virtual 模块编码
b_xh int not null default 0, -- 页面内模块顺序
b_canuse tinyint not null default 1, -- 是否启用(0/1)
primary key (b_menu_id, b_module_id)
);
create index ix_s_menu_module_module
on dbo.s_menu_module (b_module_id, b_canuse, b_menu_id);
菜单树同样使用 b_parent_id 作为权威关系,并冗余维护 b_depth、b_path。菜单节点新增、移动和删除时,由业务层同步处理后代节点路径和层级;菜单编码创建后原则上不可修改。
绑定规则:
directory(目录)和external(外链)不绑定模块;page可绑定一个或多个模块;- 关系表只登记
data/virtual模块:权限按data模块判定,module类型只是模块树上的分类节点,不参与权限,也不进入关系表; - 菜单管理界面按模块树勾选,勾中分类节点时自动展开为其下全部
data模块逐条登记(界面级快捷操作,落库的始终是明确的模块清单,之后可单独增删); - 菜单编码创建后原则上不可修改,绑定关系随菜单保存一并提交。
业务模块上下文可以根据所绑定数据模块的父级模块获得;暂不增加独立的菜单上下文字段。
菜单权限(menu.*)只控制页面入口;页面内各 data、virtual 模块的操作权限按模块编码独立判定,一个菜单绑定多个模块时不做权限合并。
11. 多语言
多语言继续使用关系表。s_i18n_type 保存系统支持的语言区域(例如 zh-CN、en),模块、菜单、字段和 JSON 布局节点只保存资源键,具体语言文本统一保存到 s_i18n。资源来源由资源键命名空间表达,例如 module.sea.name、menu.main.title、field.sea.b_no;不在翻译表中重复保存多态来源字段。
-- 多语言类型表
create table dbo.s_i18n_type (
b_id varchar(20) not null primary key, -- 语言区域编码,例如 zh-CN / en
b_name nvarchar(200) not null, -- 语言名称
b_canuse tinyint not null default 1, -- 是否启用(0/1)
b_default tinyint not null default 0, -- 是否默认(0/1)
b_xh int not null default 0 -- 显示顺序
);
-- 多语言资源表
create table dbo.s_i18n (
b_key varchar(150) not null, -- 资源键
b_locale varchar(20) not null, -- 语言区域
b_value nvarchar(1000) not null, -- 翻译值
b_canuse tinyint not null default 1, -- 是否启用(0/1)
primary key (b_key, b_locale)
);
create index ix_s_i18n_locale
on dbo.s_i18n (b_locale, b_canuse, b_key);
s_i18n.b_locale 的取值应存在于 s_i18n_type.b_id,由业务层校验,不建立外键。b_default = 1 表示默认语言,默认语言只能有一个有效项,由业务层维护。资源清理按资源键或资源键前缀执行,不依赖 b_source_id。
资源键命名应保持稳定并体现来源边界,推荐使用以下前缀:
| 前缀 | 用途 | 示例 |
|---|---|---|
module. |
模块名称或模块业务文案 | module.sea.name |
menu. |
菜单名称 | menu.main.title |
field. |
字段名称 | field.sea.b_no |
group. |
列表/表单布局分组标题 | group.sea.view.group1 |
action. |
动作动词(按钮 + 权限共用) | action.insert |
资源键一旦投入使用原则上不可修改;模块、菜单或字段删除时,由业务层根据已知资源键清理对应翻译行。
module.*、menu.*、field.*、action.* 由业务编码派生,不需要人工填写;group.* 的编号段取自界面配置里分组节点的 id,未手填节点 i18n 时同样由 id 派生,规则见 6.4 节。模块管理页登记的分组译文在表单渲染时按同一规则解析(节点缺失 id 的老数据由归一化补齐后同样能取到译文)。
action.* 保存通用的动作动词资源键(例如 action.insert、action.update、action.audit),按钮文案和权限点名称共用同一翻译,同一动词只维护一份;动作动词是通用词,不按模块或对象分段。
12. 日志
-- 技术日志表
create table dbo.s_log_technical (
b_id uniqueidentifier not null primary key, -- 日志 ID(UUIDv7)
b_user_id varchar(50) 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, -- 请求 ID
b_trace_id varchar(64) null, -- 链路 ID
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() -- 发生时间
);
-- 登录日志表
create table dbo.s_log_login (
b_id uniqueidentifier not null primary key, -- 日志 ID(UUIDv7)
b_user_id varchar(50) null, -- 用户编码
b_login_type varchar(20) not null, -- 登录类型
b_login_result tinyint not null default 0, -- 登录结果(0/1)
b_fail_reason nvarchar(400) null, -- 失败原因
b_ip varchar(50) null, -- IP
b_ip_location nvarchar(400) null, -- IP 归属地
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, -- UserAgent
b_session_id varchar(50) null, -- 会话 ID
b_request_id varchar(64) null, -- 请求 ID
b_trace_id varchar(64) null, -- 链路 ID
b_occurdatetime datetime2 default sysutcdatetime() -- 发生时间
);
-- 审计日志表
create table dbo.s_log_audit (
b_id uniqueidentifier not null primary key, -- 日志 ID(UUIDv7)
b_event_code varchar(50) not null, -- 事件编码
b_module_id varchar(50) null, -- 模块编码
b_data_id varchar(50) null, -- 数据 ID
b_business_no varchar(50) null, -- 业务单号
b_operation varchar(100) null, -- 操作
b_operator_id varchar(50) null, -- 操作人 ID
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, -- 请求 ID
b_trace_id varchar(64) null, -- 链路 ID
b_payload_text nvarchar(max) null, -- 载荷文本
b_occurdatetime datetime2 default sysutcdatetime() -- 发生时间
);
-- 审计字段明细表
create table dbo.s_log_audit_field (
b_id uniqueidentifier not null primary key, -- 日志 ID(UUIDv7)
b_event_id uniqueidentifier not null, -- 审计事件 ID
b_module_id varchar(50) null, -- 模块编码
b_data_id varchar(50) null, -- 数据 ID
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' -- 可见性
);
create index ix_s_log_technical_occur
on dbo.s_log_technical (b_occurdatetime, 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_occur
on dbo.s_log_login (b_occurdatetime, b_id);
create index ix_s_log_login_user
on dbo.s_log_login (b_user_id, b_occurdatetime);
create index ix_s_log_audit_occur
on dbo.s_log_audit (b_occurdatetime, b_id);
create index ix_s_log_audit_module
on dbo.s_log_audit (b_module_id, b_data_id, b_occurdatetime);
create index ix_s_log_audit_field_event
on dbo.s_log_audit_field (b_event_id, b_id);
审计日志中的 b_data_id 统一使用 varchar(50) 保存业务主键:业务表使用 bigint 雪花 ID 时按字符串写入,不额外记录主键原始类型;用户、模块等 varchar(50) 主键直接原样保存。
13. 业务表主键约定
业务表使用应用层生成的雪花 ID。需要跨系统公开、离线生成或按时间有序的标识时,可以单独评估 UUIDv7,但同一业务域内应保持一致。
-- 业务表示例
create table dbo.b_example (
b_id bigint not null primary key, -- 主键(应用层雪花 ID)
b_inputuser_id varchar(50) null, -- 录入用户 ID
b_inputdatetime datetime2 default sysutcdatetime() -- 录入时间
);
14. 权限设计
权限部分只保留四张表,分别回答四个问题:"系统有哪些权限点""用户拥有哪些权限点""用户对字段能做什么""用户能看到哪些数据"。
| 表 | 职责 |
|---|---|
s_power |
定义系统有哪些权限点(菜单、模块、动作) |
s_user_power |
定义用户拥有哪几个权限点 |
s_user_field_power |
定义用户对字段能做什么 |
s_user_data_power |
定义用户能看到/操作哪些数据 |
边界约定:
- 菜单、模块和动作权限统一由
s_power+s_user_power表达,不按权限类型拆分独立表; - 字段权限是"用户 + 模块 + 字段"的能力矩阵,不展开成
field.xxx.read、field.xxx.edit这类权限点; - 数据范围由
s_user_data_power单独表达,不写入s_power; - 当前阶段采用用户直接授权,不引入角色、权限模板和用户组;授权记录只表达"拥有",不做拒绝授权;后续如需角色继承或权限模板,可在不改变本表语义的前提下扩展来源字段或另建角色授权表;
- 权限的加载、合并、继承、条件生成和 SQL 拼接由业务层权限引擎处理,不建立外键、触发器或数据库约束。
权限判定的整体顺序:
用户
├── 菜单权限 s_user_power (menu.*) → 能否进入页面
├── 模块权限 s_user_power (module.*) → 能否使用该模块(访问模块数据)
├── 动作权限 s_user_power (action.*) → 能否审核 / 反审 / 核销 / 结算
├── 字段权限 s_user_field_power → 哪些字段能看 / 编辑 / 查询 / 导出
└── 数据范围 s_user_data_power → 哪些数据行可操作
14.1 权限点定义
create table dbo.s_power (
b_id varchar(150) not null primary key, -- 权限编码,如 module.sea、menu.sea_list
b_name nvarchar(200) not null,
b_i18n varchar(150) null, -- 多语言资源键
b_type varchar(20) not null, -- menu / module / action(权限点性质)
b_object_type varchar(20) not null, -- menu / module(挂在谁身上)
b_object_id varchar(50) not null, -- s_menu.b_id 或 s_module.b_id
b_action varchar(30) not null, -- access(menu / module 使用权限)或具体业务动作(action)
b_canuse tinyint not null default 1,
b_xh int not null default 0,
b_bz nvarchar(500) null
);
create index ix_s_power_type
on dbo.s_power (b_type, b_canuse, b_xh, b_id);
create index ix_s_power_object
on dbo.s_power (b_object_type, b_object_id, b_canuse, b_action, b_id);
create unique index ux_s_power_object_action
on dbo.s_power (b_object_type, b_object_id, b_action);
b_type 只保留三类。权限编码分段命名:menu、module 为 <类型>.<对象编码> 两段,表达"能否使用";action 为 <类型>.<对象编码>.<动作> 三段:
| b_type | 含义 | 示例 |
|---|---|---|
menu |
菜单/页面访问权限 | menu.sea_list |
module |
模块使用权限 | module.sea |
action |
业务动作权限 | action.sea.audit |
menu.sea_list
menu.sea_edit
module.sea
action.sea.audit
action.sea.unaudit
权限编码创建后原则上不可修改;编码分段规则和 b_type 取值由业务层校验。字段权限和数据范围不进入 s_power。
s_power.b_i18n 保存权限点名称的多语言资源键,全部复用既有资源键命名空间,不设独立的 power.* 前缀。action 类型权限点使用通用动作动词资源键,例如 action.insert、action.update、action.audit,与按钮文案共用同一翻译;menu、module 类型权限点的名称与对应菜单、模块名称一致,直接复用 menu.*、module.* 名称资源键,不再单独保存权限点名称翻译。资源键的稳定性、清理和回退规则见第 11 节。
14.2 用户功能权限
-- 用户权限表
create table dbo.s_user_power (
b_user_id varchar(50) not null, -- 用户编码
b_power_id varchar(150) not null, -- 权限编码
b_canuse tinyint not null default 1, -- 是否启用(0/1)
b_created_by varchar(50) null, -- 创建人
b_created_at datetime2 null, -- 创建时间
b_updated_by varchar(50) null, -- 最后修改人
b_updated_at datetime2 null, -- 最后修改时间
primary key (b_user_id, b_power_id)
);
create index ix_s_user_power_power
on dbo.s_user_power (b_power_id, b_user_id);
示例:
| b_user_id | b_power_id | b_canuse |
|---|---|---|
| u001 | menu.sea_list |
1 |
| u001 | module.sea |
1 |
| u001 | action.sea.audit |
1 |
判定规则:
- 无记录即无权限(白名单):
s_user_power中不存在该用户该权限点的记录时视为无权限; b_canuse = 0或对应s_power.b_canuse = 0时,该授权不生效。
14.3 用户字段权限
字段权限是能力矩阵,直接以"用户 + 模块 + 字段"记录,不拆分为多个权限点。
-- 用户字段权限表
create table dbo.s_user_field_power (
b_user_id varchar(50) not null, -- 用户编码
b_module_id varchar(50) not null, -- 模块编码
b_field_id varchar(50) not null, -- 字段编码,对应 s_field.b_field
b_view tinyint not null default 1, -- 查看(0/1)
b_edit tinyint not null default 0, -- 编辑(0/1)
b_query tinyint not null default 1, -- 查询(0/1)
b_export tinyint not null default 1, -- 导出(0/1)
b_canuse tinyint not null default 1, -- 是否启用(0/1)
b_created_by varchar(50) null, -- 创建人
b_created_at datetime2 null, -- 创建时间
b_updated_by varchar(50) null, -- 最后修改人
b_updated_at datetime2 null, -- 最后修改时间
primary key (b_user_id, b_module_id, b_field_id)
);
create index ix_s_user_field_power_module
on dbo.s_user_field_power (b_module_id, b_user_id, b_field_id);
示例:
| 用户 | 模块 | 字段 | view | edit | query | export |
|---|---|---|---|---|---|---|
| u001 | sea | b_amount |
1 | 1 | 1 | 1 |
| u001 | sea | b_cost |
1 | 0 | 0 | 0 |
| u001 | sea | b_profit |
0 | 0 | 0 | 0 |
判定规则:
- 无记录时取模块默认行为:可查看、可查询、可导出,不可编辑;
- 有记录时按
b_view、b_edit、b_query、b_export逐项取值,四项相互独立; b_view = 0的字段在列表、表单、查询和导出中统一移除,且不得因用户偏好重新出现(与第 7 节偏好合并规则一致);b_query = 0的字段不出现在查询配置和快捷搜索中,也不得作为调用方传入的筛选条件。
14.4 用户数据范围
-- 用户数据范围表
create table dbo.s_user_data_power (
b_user_id varchar(50) not null, -- 用户编码
b_module_id varchar(50) not null, -- 模块编码
b_operation varchar(30) not null default '*', -- 操作,* 表示全部操作
b_scope_type varchar(20) not null, -- all / self / dept / dept_tree / custom
b_scope_field varchar(50) null, -- 模块中用于范围判断的字段
b_condition_sql nvarchar(max) null, -- 自定义条件 SQL(custom 时使用)
b_xh int not null default 0, -- 同操作内规则顺序
b_canuse tinyint not null default 1, -- 是否启用(0/1)
b_created_by varchar(50) null, -- 创建人
b_created_at datetime2 null, -- 创建时间
b_updated_by varchar(50) null, -- 最后修改人
b_updated_at datetime2 null, -- 最后修改时间
primary key (b_user_id, b_module_id, b_operation, b_xh)
);
create index ix_s_user_data_power_module
on dbo.s_user_data_power (b_module_id, b_user_id, b_operation, b_xh);
b_scope_type 先固定五类:
| 类型 | 含义 | 说明 |
|---|---|---|
all |
全部数据 | 不追加范围条件 |
self |
仅本人 | b_scope_field = 当前用户编码 |
dept |
本部门 | b_scope_field = b_user.b_dept_id |
dept_tree |
本部门及下级部门 | 用当前用户部门的 b_dept.b_path 前缀匹配出部门集合,再过滤 b_scope_field |
custom |
自定义规则 | 由 b_condition_sql 提供条件 |
b_operation 取值:read、create、update、delete、export 为通用模块操作;业务动作使用对应的 action 权限编码(例如 action.sea.audit);* 表示对该模块的所有操作生效,精确值优先于 *。
示例:
b_user_id = u001
b_module_id = sea
b_operation = read
b_scope_type = self
b_scope_field = b_inputuser_id
表示 u001 查看海运单时只能看 b_inputuser_id = 当前用户 的数据。
b_scope_field 表示当前模块中用于执行数据范围判断的字段(例如 sea.b_inputuser_id、sea.b_department_id),不是用户表的字段。self、dept、dept_tree 必须配置该字段;b_scope_field 应是模块 s_field 中已登记且可直接参与 SQL 过滤的字段。dept、dept_tree 依赖 b_user.b_dept_id 与 b_dept 部门树,用户未归属部门时该规则不生效。
判定规则:
- 按
b_operation精确匹配优先,未命中时使用b_operation = '*'的规则; - 同一用户、同一模块、同一操作的多条生效规则之间按 OR 组合,最终整体再与其他来源条件按 AND 组合;
b_canuse = 0的规则不参与组合;无任何生效规则时,视为该模块无数据范围限制。
14.5 b_condition_sql 的使用边界
- 该字段保留用于复杂场景的出口,只由管理员配置,不开放给普通用户直接编写 SQL;
- 运行时必须由后端权限引擎处理参数替换(例如
{userId}、{deptId}),并以预编译参数或受控片段方式拼装,不得直接拼接前端传入内容; - 仅当
b_scope_type = 'custom'时生效; - 后续如规则体系成熟,优先将
b_condition_sql升级为结构化条件(字段 / 操作符 / 值的 AST),避免让 SQL 成为权限系统的核心表达方式。
示例:
b_scope_type = 'custom'
b_condition_sql = 'b_status = ''正常'''
b_scope_type = 'custom'
b_condition_sql = 'b_salesman_id = {userId}'
14.6 与模块、菜单和关系的关系
s_power中的menu.*对应s_menu,module.*对应s_module,action.*对应业务动作;权限点编码与对象编码的对应关系由业务层维护,不建立外键;s_user_field_power.b_module_id、s_user_data_power.b_module_id指向s_module.b_id,字段还需在s_field中存在;- 数据范围只作用于本表的
b_user_id + b_module_id + b_operation;是否向关联模块延伸(例如子表、明细模块)由权限引擎根据业务语义决定,不依赖s_relation自动传播; - 权限缓存、批量校验和 SQL 条件注入点由权限引擎实现,业务 SQL 生成时统一追加范围条件。
15. 文件
文件统一由文件服务托管:业务表不保存文件二进制,只通过 bf_files.father 关联宿主业务主键,
按 father + mx_moduleid + mx_cate_id 聚合出某个业务对象、某模块、某分类下的附件列表。
存储方式由配置决定(本地磁盘 / 阿里云 OSS),两种模式共用同一张表:本地模式由后端写盘后落库,
OSS 模式由前端取号直传后回调落库。
-- 文件表
create table dbo.bf_files (
subid bigint not null primary key, -- 文件主键(雪花,应用层取号:本地由后端、OSS 直传由前端先取号)
father bigint null, -- 宿主业务主键(业务表 b_id),列表按它聚合
mx_filename nvarchar(500) null, -- 原始文件名(含扩展名,展示与下载用)
mx_filesize varchar(50) null, -- 文件大小(字节,字符串存储)
mx_fileext varchar(50) null, -- 扩展名(小写、不含点)
mx_mapfilename nvarchar(500) null, -- 访问地址:本地为 {url-prefix}/{org}/{yyyy}/{MM}/{dd}/{uuid}.{ext},OSS 为完整 URL
mx_moduleid varchar(50) null, -- 业务模块编码(s_module.b_id)
mx_cate_id bigint null, -- 文件分类(bf_files_category.b_id,雪花主键)
mx_xh int not null default 0, -- 排序号(保留;当前实现固定 0,列表按 subid 倒序)
mx_sh tinyint not null default 0, -- 审核标识(保留;当前实现固定 0)
mx_shuser_id varchar(50) null, -- 审核人(保留)
mx_shdatetime datetime2 null, -- 审核时间(保留)
mx_cutfilename nvarchar(500) null, -- 缩略图地址(保留)
mx_cutfilesize varchar(50) null, -- 缩略图大小(保留)
mx_orderid varchar(50) null, -- 排序组(保留)
mx_bz nvarchar(500) null, -- 备注(保留)
mx_inputuser_id varchar(50) null, -- 上传人(用户账号)
mx_inputdatetime datetime2 null, -- 上传时间
mx_updateuser_id varchar(50) null, -- 最后修改人
mx_updatedatetime datetime2 null -- 最后修改时间
);
create index ix_bf_files_host
on dbo.bf_files (father, mx_moduleid, mx_cate_id, mx_xh, subid);
-- 文件分类表(按模块划分;b_module_id 为空表示跨模块通用分类)
create table dbo.bf_files_category (
b_id bigint not null primary key, -- 主键(雪花,新增行由前端 nextIdApi 生成)
b_module_id varchar(50) null, -- 归属模块(s_module.b_id);NULL = 通用分类
b_name nvarchar(50) not null, -- 分类名称
b_i18n varchar(150) null, -- 多语言资源键(s_i18n.b_key)
b_files_type varchar(50) null, -- 允许的扩展名(逗号分隔,空 = 不限;预留)
b_xh int not null default 0, -- 显示顺序
b_canuse tinyint not null default 1, -- 是否启用(0/1)
b_bz nvarchar(200) null, -- 备注
b_created_by varchar(50) null, -- 创建人(用户账号)
b_created_at datetime2 null, -- 创建时间
b_updated_by varchar(50) null, -- 最后修改人
b_updated_at datetime2 null -- 最后修改时间
);
create index ix_bf_files_category_module
on dbo.bf_files_category (b_module_id, b_canuse, b_xh, b_id);
要点:
- 主键:
subid为应用层雪花 ID,不使用identity。本地模式由后端在写盘前取号;OSS 模式由前端在上传前取号(nextIdApi),直传完成后回调/file/save-record落库 —— 两条链路都不能依赖数据库自增。 - 宿主关联:
father存宿主业务主键(业务表b_id,bigint 雪花),不建外键;删除业务数据时由业务层级联清理附件(先删存储、后删行)。 - 存储地址:
mx_mapfilename即访问地址,前端预览 / 下载直接使用。本地模式写入{fms.file.local.url-prefix}/{orgId}/{yyyy}/{MM}/{dd}/{uuid}.{ext},物理文件落在{fms.file.local.storage-dir}下的同路径;OSS 模式写入 g3oss 返回的完整 URL。 - 保留字段:
mx_sh*(审核)、mx_cut*(缩略图)、mx_orderid、mx_bz、mx_xh沿用旧库结构,当前实现未使用(写 0 / 空值),保留以兼容历史数据迁移。 - 分类归属模块:
bf_files_category.b_module_id指定分类所属模块,不填(NULL)为跨模块通用分类;分类树按当前模块过滤(b_module_id IS NULL OR b_module_id = 当前模块)。分类主键与bf_files.mx_cate_id类型保持一致(bigint 雪花)。 - 风格:分类表按新版字典表风格(应用层雪花主键、
b_created_*审计列、b_canuse tinyint);bf_files的subid/father/mx_*列名已被前后端实现引用,保持现状。表名bf_*属于"明确的业务域前缀",符合第 1 节表名规范。 - 旧库遗留:
bf_files_type(模块文件类型 / 数量配置)在旧库存在,但新旧系统均未使用,不迁移。
存储配置(application.yaml):
fms:
file:
storage-type: local # local / aliyun-oss
local:
storage-dir: ./data/files # 物理根目录
url-prefix: /api/file/static # 访问前缀
oss:
g3oss-url: http://g3oss.g3soft.cn:8082
业务接入(前端 FmsFile 组件):
<FmsFile :father="业务主键" :module-id="模块编码" :cate-id="分类编码(可空)" />
├── 列表:bf_files 按 father + mx_moduleid + mx_cate_id 过滤,mx_xh ASC, subid DESC 排序
├── 上传:local → POST /file/upload(后端写盘 + 落库)
│ aliyun-oss → 取号 → 凭证直传 → POST /file/save-record 落库
└── 删除:POST /file/delete(删存储 → 删 DB 行)