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

54 KiB
Raw Blame History

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 查询来源优先级

数据加载时按以下规则确定默认查询来源:

  1. 配置 b_query_sql 时,使用查询 SQL 或 SQL 模板;
  2. 未配置 b_query_sql 时,使用 b_view_table;
  3. 两者均未配置时,由虚拟模块的业务实现负责提供数据;
  4. 调用方传入的筛选、字段、排序和分页配置在默认查询来源上继续组合。

保存时按以下规则确定默认保存方式:

  1. data 模块配置 b_save_table 时,默认写入该表;
  2. 未配置 b_save_table 或需要特殊处理时,由业务层使用模块对应的 SQL 保存逻辑或业务处理器负责保存;
  3. 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",
      "title": "费用信息",
      "i18n": "group.cw_fee.amount",
      "children": [
        { "field": "mx_amount", "width": 120, "format": "money" },
        { "field": "mx_currency", "width": 90 }
      ]
    }
  ]
}

6.2 表单配置示例

{
  "schemaVersion": 1,
  "children": [
    {
      "type": "group",
      "title": "基础信息",
      "i18n": "group.sea_main.basic",
      "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" }
          ]
        }
      ]
    }
  ]
}

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 }
}

合并规则(两层依次应用):

  1. 读取 s_module_schema 系统默认(query 布局由 quick 列表 + 条件推导);
  2. 读取当前用户的 s_user_module_pref.b_schema_json,按 schema 类型叠加个人偏离量,得到最终配置;
  3. 已停用、已删除或无权限的字段(s_user_field_power)不得因配置重新出现;权限过滤在配置合并之后执行,永远优先于配置;
  4. 恢复默认(恢复通用)= 删除 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.basic
action. 动作动词(按钮 + 权限共用) action.insert

资源键一旦投入使用原则上不可修改;模块、菜单或字段删除时,由业务层根据已知资源键清理对应翻译行。

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

判定规则:

  1. 无记录即无权限(白名单):s_user_power 中不存在该用户该权限点的记录时视为无权限;
  2. 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

判定规则:

  1. 无记录时取模块默认行为:可查看、可查询、可导出,不可编辑;
  2. 有记录时按 b_view、b_edit、b_query、b_export 逐项取值,四项相互独立;
  3. b_view = 0 的字段在列表、表单、查询和导出中统一移除,且不得因用户偏好重新出现(与第 7 节偏好合并规则一致);
  4. 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 部门树,用户未归属部门时该规则不生效。

判定规则:

  1. 按 b_operation 精确匹配优先,未命中时使用 b_operation = '*' 的规则;
  2. 同一用户、同一模块、同一操作的多条生效规则之间按 OR 组合,最终整体再与其他来源条件按 AND 组合;
  3. 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 行)