Files
workspace/code/fms/软件需求.md
T
2026-07-19 21:57:40 +08:00

21 KiB
Raw Blame History

FMS 货代物流管理系统需求规范

1. 项目定位

  • FMS 是一个前后端分离、以模块为中心的多机构货代物流管理系统。
  • 一个机构对应一个独立数据库,机构码统一命名为 orgid。
  • 登录时必须提交机构码、账号和密码。后端根据机构码连接对应数据库并验证用户。
  • 登录成功后,登录凭证与机构码绑定。后续请求只操作当前机构的数据库,切换机构必须重新登录。
  • 后端负责登录、机构数据库路由、通用数据接口、事务控制以及少量确有必要的特殊业务接口。
  • 普通业务优先复用通用查询和保存能力,避免为每张表单独创建 Controller、DTO 和实体类。
  • 模块负责组织查询数据源、保存对象、字段展示、查询条件、编辑方式和权限。

2. 现有系统过渡期约定

以下约定用于当前系统的开发过渡阶段,不作为新数据库重构后的最终安全模型。

  • 页面、查询条件、SQL、数据组织、校验和普通业务流程目前主要由前端完成。
  • 当前阶段暂不重点考虑客户端直接提交查询条件和 SQL 带来的安全风险。
  • 后端通用接口完成后应尽量保持稳定,普通业务优先修改前端配置和数据库配置。
  • 文件、报表、导入和导出等能力也应建设为稳定的平台级通用接口,后续不得为具体业务模块增加特殊接口或后端代码。
  • 新数据库启用后,前端应逐步改为只提交 module_id 和结构化数据,不再直接指定任意表名或 SQL。
  • G3HY2025 旧数据库仅用于参考表结构和业务含义,新代码只面向 FMS 新数据库,不保留旧表结构的运行时兼容分支。

3. 现有通用接口

方法 路径 用途
POST /api/auth/login 机构登录
POST /api/data/loaddata 按表或视图加载数据
POST /api/data/loaddatabysql 执行完整查询 SQL
POST /api/data/page 执行分页查询
POST /api/data/saveobjt 多表事务保存
GET /api/data/nextid 获取下一个雪花 ID

3.1 登录

{
  "orgid": "G3HD",
  "userid": "admin",
  "password": "123456"
}

登录成功后返回登录凭证和用户信息。后续请求携带登录凭证,不再重复提交 orgid。

{
  "code": 0,
  "message": "操作成功",
  "data": {
    "token": "<jwt>",
    "orgid": "G3HD",
    "user": {
      "id": "admin",
      "name": "管理员"
    }
  }
}

3.2 loaddata

用于表或视图的普通查询,不执行分页。

{
  "view_name": "v_bs_business",
  "search_columns": ["b_id", "b_state", "v_customer_company"],
  "search_condition": "b_state = '草拟'",
  "order_by": "b_id desc"
}
  • view_name 必填,表示表名或视图名。
  • search_columns 可省略,省略时返回全部字段。
  • search_condition 可省略,由前端直接拼接,不包含 WHERE 关键字。
  • order_by 可省略,只支持一个排序字段及可选的 ASC 或 DESC。

成功响应:

{
  "code": 0,
  "message": "操作成功",
  "data": []
}

3.3 loaddatabysql

用于关联、聚合、统计等完整 SQL 查询,不执行分页。过渡期内 SQL 由前端直接拼接。

{
  "sql": "select customer_id, sum(amount) as total from v_fee where fee_date >= '2026-01-01' group by customer_id"
}

成功响应与 loaddata 相同,data 直接为查询结果数组。

3.4 page

所有分页查询统一使用本接口。

{
  "view_name": "v_bs_business",
  "search_columns": ["b_id", "b_state", "v_customer_company"],
  "search_condition": "b_state = '草拟'",
  "order_by": "b_id desc",
  "page_no": 1,
  "page_size": 20
}
  • view_name 必填,表示分页查询使用的表或视图名称。
  • search_columns 可省略,省略时返回全部字段。
  • search_condition 可省略,由前端直接拼接,不包含 WHERE 关键字。
  • order_by 必填,用于保证分页顺序稳定;支持使用逗号分隔多个查询结果字段。
  • page_no 从 1 开始,默认值为 1。
  • page_size 默认值为 20。
  • SQL Server 2022 使用 COUNT_BIG(*) OVER() 与 OFFSET/FETCH 在一次查询中返回当前页和总记录数;仅页码越界且当前页为空时补充执行总数查询。

成功响应:

{
  "code": 0,
  "message": "操作成功",
  "data": {
    "rows": [],
    "row_count": 0,
    "total": 0,
    "page_no": 1,
    "page_size": 20
  }
}

3.5 saveobjt

用于一个或多个表的新增、修改和删除。

[
  {
    "table": "bs_business",
    "key_field": "b_id",
    "inserts": [],
    "updates": [
      {
        "b_id": "10001",
        "b_state": "草拟"
      }
    ],
    "deletes": []
  },
  {
    "table": "bs_business_container",
    "key_field": "subid",
    "inserts": [],
    "updates": [],
    "deletes": [
      {
        "subid": "20001"
      }
    ]
  }
]
  • 请求体必须是非空数组。
  • table 是目标表名,key_field 是该表的单个主键字段。
  • inserts、updates 和 deletes 均为可选数组,省略时按空数组处理。
  • 修改和删除的数据必须包含 key_field 对应的主键值。
  • 数字字段的空字符串不做特殊转换,按原值提交给数据库。
  • 每张表按照删除、修改、新增的顺序执行,表与表之间按照请求数组顺序执行。
  • 一次请求中的所有表和所有操作使用同一个数据库连接和事务。
  • 全部操作成功后提交,任意操作失败时全部回滚。
  • 保存成功只返回成功状态,不返回每张表的操作数量。

成功响应:

{
  "code": 0,
  "message": "保存成功",
  "data": null
}

保存失败时可以返回失败位置:

{
  "code": 2001,
  "message": "保存失败",
  "data": {
    "table": "bs_business_container",
    "action": "update",
    "index": 2
  }
}

3.6 nextid

无请求体。成功时返回一个雪花 ID:

{
  "code": 0,
  "message": "操作成功",
  "data": 1946123456789012345
}

4. 统一约定

  • JavaScript 和 Java 内部函数、方法及变量使用小驼峰命名法。
  • JSON 请求与响应字段使用 snake_case,在 API 边界与数据库字段显式映射。
  • 数据库字段保留现有命名风格:主表普通字段使用 b_ 前缀,主键固定为 b_id;子表普通字段使用 mx_ 前缀,主键固定为 subid。
  • API 路径使用全小写名称,JSON 多单词字段使用 snake_case。
  • code = 0 表示成功,非零值表示失败。
  • 前端根据 code 生成 success 字段,code = 0 时 success = true。
  • loaddata 和 loaddatabysql 的查询结果直接放在 data 数组中;page 的分页记录放在 data.rows 中。
  • 过渡期查询条件和 SQL 由前端拼接,不使用参数占位符。
  • 不增加功能相同但名称不同的接口。
  • 不使用 V2、V3、new、old、test 或 bak 等临时名称。
  • 接口发生不兼容升级时,统一通过 /api/v2 等版本路径升级。
  • 前端和后端代码修改完成后默认不执行构建或测试命令,只进行静态检查;仅在用户明确要求时运行构建或测试。

5. 新数据库重构方案

5.1 重构目标

  • 保持以模块为中心的开发方式。
  • 将业务数据约束与页面配置分离。
  • 所有业务页面、页面布局、按钮、标签页、弹窗和业务流程仍由前端手工开发。
  • 模块配置只服务于通用 Table 和 Form 组件,不根据配置自动生成完整页面、路由或业务流程。
  • Table 根据模块配置渲染列、格式、查询项和排序;Form 根据模块配置渲染字段、控件、校验和分组。
  • 模块定义查询对象、保存对象、可选业务主键和字段配置。
  • 前端只提交 module_id、结构化查询条件和待保存数据。
  • 后端根据模块配置解析允许访问的对象和字段,并使用参数化 SQL。
  • 单表和主子表保存都复用 saveobjt;手写页面负责组织多张表的变更,后端在一个事务中统一提交或回滚。
  • 后端只保留稳定的通用接口,不为模块分别新增查询、新增、修改、删除接口或处理器代码。
  • 复杂业务优先通过手写页面、数据库约束和通用事务保存组合实现,不建立模块处理器注册与分派机制。
  • 不将全部业务逻辑塞入配置表或动态 SQL。

5.2 数据库版本与基础规范

  • 新数据库建议使用 SQL Server 2019 或 SQL Server 2022,不再使用 SQL Server 2008 R2。
  • 继续采用一个机构对应一个独立业务数据库的隔离方式,业务表不重复保存 orgid。
  • 技术主键统一使用雪花 ID,数据库类型为 bigint。主表主键固定为 b_id,子表主键固定为 subid。
  • 中文和多语言文本使用 nvarchar,长文本使用 nvarchar(max),不使用 text。
  • 标志和状态字段使用 tinyint 等整数类型,不使用 bit 或字符串类型。二态字段仍以数字 0、1 表示,业务需要时允许扩展为 2、3、4 等状态;Java 端按数字处理,不映射为 Boolean。
  • 时间字段使用 datetime2。
  • 金额、单价、数量、汇率和税率不强制统一精度,根据每个字段的业务含义分别定义 decimal 的精度和小数位数。
  • 迁移旧字段时保留原有精度;新增字段必须在需求或表结构中明确精度,不使用 float 代替精确金额。
  • 字段命名延续现有风格:主表字段使用 b_ 前缀,子表字段使用 mx_ 前缀。
  • 主表与子表的外键字段也遵循对应前缀,例如子表引用主表时使用 mx_<业务对象>_id。
  • 数字字段的空字符串不自动转换成 0,由调用方提交正确的数据类型。

5.3 dbo 与表名前缀

  • 新数据库的所有业务表、配置表和视图统一创建在 dbo 下,不创建其他 Schema。
  • 领域划分继续使用现有表名前缀,避免增加对象名称和通用接口的复杂度。
  • 模块配置中的数据库对象只保存裸对象名,不保存 dbo. 或其他架构前缀,例如 bs_business、cw_bill 和 v_bs_business。
表名前缀 用途
s_ 模块、字段、菜单、权限和其他系统配置
b_ 用户、客户、联系人、港口、币种等基础资料
bs_ 业务单、箱信息、提单和运输业务
cw_ 费用、账单、发票和收付款
bc_ 合同、价目表和价格明细
bf_ 文件和附件
oa_ 通知、提醒等办公功能
sh_ 审批流程
v_ 面向查询和模块展示的视图

5.4 核心配置表

第一阶段只建立必要的配置表,避免过早建设复杂的低代码平台。

表 用途
dbo.s_module 模块定义,主表字段使用 b_ 前缀
dbo.s_module_field 模块字段子表,字段使用 mx_ 前缀
dbo.s_module_group Form 分组子表,字段使用 mx_ 前缀
dbo.s_option_source 下拉框和字典数据源主表
dbo.s_option_item 固定选项子表,字段使用 mx_ 前缀
dbo.s_function_node 统一功能树,组织目录、页面、分组和模块位置

角色和权限仍使用独立系统表,不属于 Table/Form 渲染配置。菜单是功能树中 b_show_in_menu = 1 的目录和页面节点投影,页面节点可以直接绑定主模块;隐藏子模块可以挂在页面或其他模块下。主子页面关系仍由手写页面负责组织,功能树不自动生成页面或 Tab。

5.5 模块定义

dbo.s_module 建议包含以下核心字段:

字段 用途
b_id 模块唯一标识,主键
b_name 模块名称
b_query_object 查询表或稳定查询视图
b_save_object 保存目标表,只读模块允许为空
b_key_field 系统主键字段,平台内部使用,普通主表固定为 b_id
b_business_key_field 可选业务主键字段,例如业务单号
b_default_order 默认排序
b_enabled 是否启用
b_sort_order 模块排序

查询对象与保存对象必须分开:查询视图负责组合展示数据,保存表负责持久化可写字段。系统主键固定由后端生成雪花 ID,不在配置页面重复设置;业务主键是带唯一约束的业务字段,例如 HY20270601,通过 b_business_key_field 标识但不代替系统主键。

5.6 模块字段配置

dbo.s_module_field 直接归属于模块,使用 subid 作为主键,并为 (mx_module_id, mx_field_code) 建立唯一索引。同一个查询视图被多个模块复用时,各模块可以拥有独立的显示和编辑配置。

配置分组 建议字段
主键与归属 subid、mx_module_id
字段映射 mx_field_code、mx_query_field、mx_save_field、mx_caption
列表显示 mx_list_visible、mx_list_order、mx_list_width、mx_sortable、mx_display_format
查询设置 mx_searchable、mx_search_order
表单编辑 mx_form_visible、mx_form_group_code、mx_form_order、mx_form_span、mx_editable、mx_required、mx_component_type
数据来源 mx_option_source_id、mx_default_rule、mx_validation_rule
  • mx_field_code 是模块对前端暴露的稳定字段名。
  • mx_query_field 对应查询对象返回的字段。
  • mx_save_field 对应保存表字段,计算字段和只读字段可以为空。
  • 字段配置不保存固定查询操作符。高级查询运行时根据字段类型提供 eq、like、between、in 等合法操作符,由用户为每条条件选择。
  • 主要配置使用明确字段保存,只有少量不稳定的组件扩展属性才考虑使用 JSON。

5.7 表单分组与选项来源

  • dbo.s_module_group 使用 subid 作为主键,其余字段使用 mx_ 前缀。
  • 分组配置包含 mx_module_id、mx_group_code、mx_group_name、mx_sort_order、mx_column_count、mx_collapsible 和 mx_default_collapsed。
  • dbo.s_module_field.mx_form_group_code 指向对应分组,Form 按分组顺序和字段顺序渲染。
  • 没有分组的字段放入默认分组,简单表单不强制配置分组数据。
  • 固定下拉选项保存在 dbo.s_option_item。
  • 数据库字典选项通过 dbo.s_option_source 指定允许查询的对象、值字段、显示字段和排序字段。
  • 非固定、非模块查询的复杂选项由手写页面组件负责,不在配置表保存任意可执行 SQL,也不增加后端选项处理器。

5.8 功能树、菜单与模块

dbo.s_function_node 是技术管理和业务菜单共用的功能结构,不是页面生成配置。节点类型固定为 directory、page、group 和 module:

节点类型 用途
directory 业务域或功能目录,可以显示在菜单中
page 已注册的手写页面,可以配置路由并显示在菜单中
group 页面内部的技术分组,只在配置管理中显示
module 指向一个 s_module,只在配置管理中显示
  • 功能节点使用 b_parent_id 形成树,并包含 b_name、b_page_code、b_module_id、b_route、b_icon、b_show_in_menu、b_enabled 和 b_sort_order。
  • module 节点必须指定 b_module_id;page 节点也可以直接绑定主模块,同一个模块允许被多个功能节点引用。
  • b_show_in_menu 只允许用于 directory 和 page 节点。菜单隐藏不代表模块停用,也不代替权限判断。
  • 功能树中的模块父子关系只用于技术人员定位和分类,不定义保存事务、主子表外键或业务流程。
  • 页面和 Tab 继续手写。功能树只帮助定位模块和投影菜单,不根据树节点自动生成组件。
  • 迁移期间,功能树中的可见目录和页面按节点编码或路由与静态菜单合并;未登记菜单继续保留,完成全部路由核对后再移除静态回退。

模块管理页面左侧按功能树展示完整路径,并提供“未归档模块”虚拟节点。搜索至少匹配功能节点名称、模块编码、模块名称、查询对象和保存对象,结果必须保留祖先路径。左侧可以分别显示或隐藏模块节点和菜单节点;页面支持直接维护目录、页面、模块绑定和隐藏子模块位置。

5.9 通用组件与手写页面的边界

层级 负责内容
手写页面 页面布局、工具栏、按钮、标签页、弹窗、主子区域、业务流程和接口调用时机
ModuleTable 加载模块字段配置,渲染列、格式化内容、查询条件、排序、分页和选择状态
ModuleForm 加载模块字段配置,渲染输入控件、字段校验、表单分组和编辑状态
模块配置 描述数据源和字段元数据,不描述完整页面

推荐的组件使用方式:

<ModuleTable module-id="bs_business">
  <template #toolbar>
    <!-- 页面自己实现按钮和业务操作 -->
  </template>
  <template #column-b_state="{ record }">
    <!-- 页面可以覆盖特殊列的渲染 -->
  </template>
</ModuleTable>

<ModuleForm
  v-model="formData"
  module-id="bs_business"
  mode="edit"
  @validate="handleValidate"
/>
  • ModuleTable 可以接收 module-id 或 view-name,优先使用 module-id。
  • 只有一个视图唯一对应一个模块时才允许只传 view-name;同一视图被多个模块复用时必须传 module-id。
  • ModuleForm 应以 module-id 为入口,以便确定保存字段、表单分组和校验规则。
  • Table 和 Form 必须提供 props、slots 和事件,允许手写页面覆盖默认列、字段、工具栏和交互。
  • ModuleForm 负责渲染、收集数据和校验,但不自动决定保存时机;页面验证业务条件后再调用保存。
  • 特殊控件和复杂区域可以通过插槽由页面自行实现,不要求全部配置化。

5.10 查询与保存边界

模块化后的推荐调用关系:

module_id
    -> 加载模块定义
    -> 检查用户模块权限
    -> 确定查询对象、保存对象和主键
    -> 加载模块字段配置
    -> 生成参数化查询或保存语句
    -> 返回模块定义和业务数据
  • 前端不再提交任意 view_name、table、key_field 或完整 SQL。
  • 查询条件使用结构化格式提交,字段和操作符必须来自模块配置白名单。
  • 雪花 ID 由后端保存流程生成,不依赖前端提前申请。
  • 配置 b_save_object 的模块可以参与通用保存,未配置保存对象的模块只读。
  • 手写主子页面可以将多张表的新增、修改和删除一次提交给 saveobjt,整个请求使用同一事务。
  • 后端不按模块编码分派自定义处理器,也不为具体页面增加特殊保存接口。

5.11 业务表设计原则

  • 每张业务表必须定义真实主键:主表固定使用 b_id,子表固定使用 subid。
  • 主表普通字段使用 b_ 前缀,子表普通字段使用 mx_ 前缀。
  • 有明确关系的主子表必须建立外键,外键字段同时建立普通索引。
  • 业务编号与技术主键分离,例如主表的 b_business_no 建立唯一索引但不作为主键。
  • 唯一性、非空、默认值和可校验的状态范围优先使用数据库约束保证。
  • 不使用空字符串同时表达未知、未填写和数值零。
  • 重要且允许多人编辑的业务表可以增加 b_row_version 或 mx_row_version,用于乐观锁控制。
  • 创建人、创建时间、修改人和修改时间字段仅在业务确有审计需求时添加,不作为所有表的固定字段;例如 b_user 不添加这些字段。
  • 删除策略根据业务确定;财务和审批数据通常使用作废或状态流转,不直接物理删除。

5.12 权限原则

  • 权限至少控制模块的查看、新增、修改、删除和导出操作。
  • 菜单是功能树可见目录和页面节点的投影;页面菜单可以绑定模块,后续权限和多语言都以菜单节点为入口扩展。
  • 后端必须根据登录用户和 module_id 校验权限,不能只依赖前端隐藏按钮。
  • 字段级权限仅在确有业务需求时增加,第一阶段不提前复杂化。
  • g3soft 是内置超级管理员账号,初始密码为空。超级管理员身份由固定账号名识别,不在 b_user 中增加超级管理员或隐藏标志字段。
  • 普通 SQL 允许直接查询物理表 dbo.b_user,但数据库行级安全策略必须自动排除 g3soft;只有认证流程可以在当前数据库连接内临时读取该账号,且查询结束后必须立即清除临时权限。

5.13 重构实施顺序

  1. 确定数据库版本、字段命名和表名前缀;系统主键固定使用雪花 ID,业务主键使用独立唯一业务字段。
  2. 建立模块、功能树、模块字段、表单分组和选项来源等最小配置表。
  3. 先实现一个简单基础资料页面,验证 ModuleTable、ModuleForm、通用查询和单表保存。
  4. 手工实现一个业务主子页面,验证组件组合、事务和自定义保存。
  5. 实现一个只读报表模块,验证查询、分页和导出边界。
  6. 建立配置校验工具,检查查询对象、保存对象、字段、主键、重复排序和选项来源。
  7. 三类模块验证通过后,再迁移现有模块及字段配置。
  8. 迁移期间保留现有通用接口,按模块逐步切换,避免一次性替换全部功能。