
NocoBase 数据建模实战用普通表 树表 多对一关联搭建工单系统【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase本篇技术指南是 NocoBase 工单系统系列教程的第 2 章聚焦数据建模如何在 NocoBase 中创建数据表Collection、配置字段Field并建立表间关联。你将学会用一张普通表承载工单数据、用一张树表承载层级分类、再用三个多对一M2O关系字段把工单、分类和用户串起来——这套两张表搞定工单系统的建模思路可以直接复用到订单、客户、任务等绝大多数业务场景。数据模型是整个系统的地基先想清楚要存哪些数据、数据之间有什么关系后面搭建界面、配置权限、编写工作流才能水到渠成。文中所有操作都基于当前仓库的 NocoBase 开源代码并会穿插对应的源码实现细节帮助你不仅会点而且懂原理。什么是数据表和字段如果你用过 Excel理解 NocoBase 的数据表概念就很容易Excel 概念NocoBase 概念说明工作表数据表Collection一类数据的容器列标题字段Field描述数据的属性每一行记录Record一条具体的数据比如我们要做的工单表就像一张 Excel 表格——每一列是一个字段标题、状态、优先级……每一行是一条工单记录。在源码层面NocoBase 用CollectionOptions来描述一张数据表的核心配置其中name是表的标识名、title是界面显示名、fields是字段列表定义位于 packages/core/database/src/collection.tsexport interface CollectionOptions extends OmitModelOptions, name | hooks { name: string; // 数据表标识名如 tickets title?: string; // 界面显示名如 工单 tableName?: string; // 物理表名 fields?: FieldOptions[]; tree?: string; // 树表专用声明树的实现方式 autoGenId?: boolean; // 是否自动生成自增主键默认 true ... }不过NocoBase 比 Excel 强大得多。它支持多种数据表类型不同类型自带不同的能力。官方文档 数据表 中列出了完整的表结构类型表类型适合场景举例普通表大多数业务数据工单、订单、客户树表有层级关系的数据分类目录、部门组织架构、商品分类、地区层级日历表带时间范围的事件会议室预约、项目排期、排班评论表围绕业务记录的讨论任务评论、审批意见、客户反馈文件表附件元信息管理合同附件、发票文件、产品图片数据库视图连接已有数据库 view财务报表视图、聚合视图SQL 表把 SQL 查询结果结构化为数据表销售汇总、库存预警继承表多类对象共享公共字段资产父表派生电脑、车辆、家具今天我们会用到普通表和树表其他类型以后用到再学。进入数据源管理点击左下角「数据源管理」图标齿轮旁边的数据库图标你会看到「主数据源」——我们所有的表都建在这里。NocoBase 是一个数据模型驱动的平台数据源可以是主数据库、外部数据库、REST API 等详见 数据源概述工单系统的所有表都建立在主数据源NocoBase 主数据库支持 PostgreSQL、MySQL、MariaDB 等中。创建核心表工单我们直奔主题先创建系统的核心——工单表。创建表在数据源管理页面点击主数据源进入。点击「创建数据表」选择「普通表」。数据表名称tickets数据表标题工单。创建表时系统会默认勾选一组系统字段它们会自动记录每条数据的元信息字段说明ID主键分布式唯一标识创建日期记录的创建时间创建人谁创建了这条记录最后修改日期最后一次更新时间最后修改人最后一次更新的用户这些系统字段保持默认即可不需要手动管理。如果某些场景不需要也可以取消勾选。在源码层面普通表默认开启autoGenId自动生成id主键同时由 NocoBase 框架在创建时挂载创建人、更新人等审计字段这类由平台维护的字段在官方文档中统称为系统字段。添加基础字段表创建好了接下来添加字段。点击工单表的「配置字段Configure fields」你会看到刚才默认的系统字段已经在列表中了。点击右上角的「添加字段Add field」按钮会展开一个下拉字段类型列表——从中选择你要添加的字段类型。我们先添加工单自身的字段关联字段稍后再加。1. 标题单行文本每条工单都需要一个简短的标题来概括问题。点击「添加字段」→ 选择「单行文本」对应官方文档的文本字段字段名称title字段标题标题点击「设置验证规则」添加一条「必填」规则字段创建时NocoBase 会让你同时确认字段标识名Field name和界面显示名Field display name标识名用于 API、关系字段、权限、工作流等内部引用创建后通常不再修改只支持字母、数字和下划线并且必须以字母开头显示名则是业务人员在界面上看到的名字。这种标识名 / 显示名分离的设计让数据库结构稳定、界面文案灵活。2. 描述Markdown(Vditor)用来详细描述问题支持格式排版方便贴图、贴代码。在「添加字段」→「Media」分类下有三种可选字段类型特点Markdown基本 Markdown简单样式Rich Text富文本简单样式 附件上传Markdown(Vditor)功能最丰富支持所见即所得、即时渲染、源码编辑三种模式我们选Markdown(Vditor)字段名称description字段标题描述。富文本/长文本类字段适合保存正文、说明文档、处理方案、代码片段等较复杂内容是工单问题描述的理想载体。3. 状态下拉菜单 - 单选工单从提交到完成需要一个状态来跟踪进度。选择「下拉单选」字段类型字段名称status字段标题状态添加选项值每个选项需要填写「选项值」和「选项标签」颜色可选选项值选项标签颜色pending待处理Orange日暮in_progress处理中Blue拂晓蓝completed已完成Green极光绿先填好选项并保存。然后再次点击该字段的「编辑Edit」这时就能在「默认值」里选择「待处理」了。首次创建时还没有选项数据所以默认值选不了——需要保存后再回来设置。为什么用下拉单选因为状态是固定的几个值下拉单选字段可以防止用户随意填写保证数据规范。下拉单选特别适合状态、等级、类型、来源这类固定范围的业务字段——比如订单状态、工单状态、审批状态、客户等级、优先级。它的默认数据类型是string保存选中的选项值每个选项都可以配置显示名称、选项值和颜色颜色会在后续界面展示中直接生效。4. 优先级下拉菜单 - 单选区分工单的紧急程度方便处理人员按优先级排序。同样是「下拉单选」字段名称priority字段标题优先级添加选项值选项值选项标签颜色low低medium中high高Orange日暮urgent紧急Red薄暮到这里工单表有了 4 个基础字段。但是——工单应该有个分类吧比如网络问题软件故障如果把分类做成下拉菜单当然也行。但你很快会发现分类可能有子分类硬件问题下面还有显示器键盘打印机下拉菜单就不够用了。我们需要另一张表来专门管理分类。而且这张表用 NocoBase 的树表来建最合适。创建分类树表让分类有层级什么是树表树表是一种特殊的数据表它自带父子关系——每条记录可以有一个父节点。这天然适合有层级结构的数据硬件问题 ← 一级分类 ├── 显示器 ← 二级分类 ├── 键盘鼠标 └── 打印机 软件故障 ├── 办公软件 └── 系统问题 网络问题 账号权限如果用普通表你需要自己手动建一个父分类字段来实现这种关系。而树表会自动帮你处理好还支持树形展示、添加子记录等操作省心很多。在源码层面NocoBase 的树表采用**邻接表adjacency list**结构保存父子关系每条记录都通过一个外键指向自己的父节点。看 packages/core/database/src/listeners/adjacency-list.ts 的实现当 Collection 配置了tree选项时所有标记为treeParent/treeChildren的字段会自动把目标指向当前表、并把外键命名为parentIdexport const beforeDefineAdjacencyListCollection (options: CollectionOptions) { if (!options.tree) { return; } (options.fields || []).forEach((field) { if (field.treeParent || field.treeChildren) { if (!field.target) { field.target options.name; // 父子都指向自身 } if (!field.foreignKey) { field.foreignKey parentId; // 默认外键 } } }); };而在 packages/core/database/src/collection.ts 中treeParentField和treeChildrenField两个 getter 会从字段列表里识别出标记为treeParent多对一指向父节点和treeChildren一对多指向子节点的关系字段供树形查询与树形展示使用。对应的测试用例packages/core/database/src/tests/eager-loading/eager-loading-tree.test.ts也验证了这种配置方式tree: adjacency-list配合treeParent: true、treeChildren: true两个关系字段即可得到完整的树模型。创建表回到数据源管理点击「创建数据表」。这次选择「树表」不是普通表。数据表名称categories数据表标题工单分类。注意创建后表里除了系统字段外还会自动出现「Parent」和「Children」两个关系字段——这就是树表的特殊能力。通过 Parent 可以访问父节点通过 Children 可以访问所有子节点不需要你手动添加。根据官方文档 树表树表创建后内置字段通常包括id主键、createdAt/createdBy/updatedAt/updatedBy系统字段以及parentId保存父节点 ID根节点通常为空、parent多对一关系字段指向父节点、children一对多关系字段表示子节点。其中parentId就是邻接表结构中的外键列。需要注意两点一是树表只能通过主数据库页面创建外部数据库、REST API 数据源和外部 NocoBase 数据源不支持创建树表二是树表数据要避免形成循环关系如 A 的父节点是 B、B 的父节点又是 A循环会让树形展示和筛选结果异常。添加字段点击「配置字段」进入字段列表可以看到系统字段和自动生成的 Parent、Children 字段。点击右上角「添加字段」字段一分类名称选择「单行文本」字段名称name字段标题分类名称点击「设置验证规则」添加「必填」规则字段二颜色选择「颜色」字段名称color字段标题颜色颜色字段可以让每个分类有自己的标识色后面在界面上展示时会更直观。到这里两张数据表的基础字段就配好了。接下来我们把它们关联起来。回到工单表添加关联字段关系字段初次接触可能有点抽象。如果你觉得不太好理解可以先跳到 第 3 章搭建页面在实际的页面操作中感受一下数据是怎么展示的再回来补上关联字段。工单需要关联到分类、提交人和处理人。这类字段叫做关系字段——它不像标题那样直接存一段文字而是存了另一张表里某条记录的 ID通过这个 ID 找到对应的记录。用一条具体的工单来看——工单的各个属性中分类和提交人存的不是文字而是一个 ID。系统通过这个 ID从对应的表里精准找到那条记录。你在界面上看到的是名称网络问题张三背后就是通过 ID 关联的。多条工单可以指向同一个分类或同一个用户——这种关系叫做多对一M2O对应 Sequelize/数据库中的 BelongsTo 关系。在源码层面多对一字段对应 packages/core/database/src/interfaces/many-to-one-interface.ts 中的ManyToOneInterface底层由 packages/core/database/src/fields/belongs-to-field.ts 的BelongsToField实现。有几个值得了解的细节外键自动生成规则如果配置时不显式填写外键BelongsToField会自动按${字段名}_${目标键}的驼峰规则生成例如category_id见 belongs-to-field.ts。目标键默认主键targetKey未指定时默认取目标表的主键primaryKeyAttribute也就是id。类型匹配校验绑定关联时会检查外键与目标键的数据类型是否一致不一致会直接抛出错误见 belongs-to-field.ts从底层保证关联不会产生脏数据。添加关系字段回到工单表的「配置字段」→「添加字段」选择「多对一」。创建时你会看到这些配置项配置项说明怎么填源数据表当前表自动填好不用改目标数据表要关联到哪张表选择对应的表外键存在当前表里的关联列名填一个有意义的名字目标数据表标识字段默认id保持默认即可ON DELETE目标记录被删除时的处理方式保持默认即可外键默认会自动生成一个随机名如f_xxxxx建议改成有意义的名字方便日后维护。命名用小写字母加下划线如category_id不用大小写混合。关于配置项官方文档 多对一 给出了更完整的说明Source collection源表当前字段所在表Target collection目标表与哪张表关联Foreign key外键源表中的字段用于建立两张表之间的关联Target key目标键外键约束引用的字段必须具备唯一性通常就是主键idON DELETE删除目标父表记录时对子表外键引用的处理规则——CASCADE级联删除关联子记录、SET NULL把子表外键置为 NULL、RESTRICT存在关联子记录时拒绝删除父记录默认选项、NO ACTION与 RESTRICT 类似。按这个方式依次添加三个字段5. 分类 → 工单分类表字段标题分类目标数据表选择「工单分类」如果列表中没有直接输入表名会自动创建外键category_id6. 提交人 → 用户表记录是谁提交了这条工单。NocoBase 内置了用户表直接关联即可。字段标题提交人目标数据表选择「用户」外键submitter_id7. 处理人 → 用户表记录谁在负责处理这条工单。字段标题处理人目标数据表选择「用户」外键assignee_id数据模型全貌回顾一下我们搭建的完整数据模型tickets工单title、description、status、priority4 个基础字段外加category_id → categories、submitter_id → users、assignee_id → users3 个多对一关联categories工单分类name、color2 个自定义字段加上系统自动生成的parentId/parent/children树形字段天然支持多级分类users用户NocoBase 内置用户表工单的提交人和处理人都指向它。用 ER 图符号表示tickets }o--|| categories多条工单属于一个分类、tickets }o--|| users多条工单由同一用户提交/处理。}o--||表示多对一关系左边多右边一。值得一提的是从源码结构看关系字段并不是在数据表里存一列对象而是在源表中存一个外键列如category_idNocoBase 通过关系元数据把它与目标表的主键默认id绑定起来并在查询时自动做关联加载。这也是为什么多条工单指向同一个分类时只需要在分类表里保存一条记录——这正是数据建模消除冗余的核心价值。小结这一章我们完成了数据建模——整个工单系统的骨架工单表tickets4 个基础字段 3 个关联字段用普通表创建工单分类表categories2 个自定义字段 自动的 Parent/Children 字段用树表创建天然支持层级分类我们学到了几个重要概念数据表Collection 一类数据的容器由name标识名、title显示名、fields字段等选项定义数据表类型 不同场景选不同类型普通表、树表、日历表、文件表……树表基于邻接表结构实现自动生成parentId/parent/children字段Field 数据的属性通过「配置字段」→「添加字段」来创建每个字段同时有标识名与显示名系统字段 ID、创建日期、创建人等建表时自动勾选关系字段多对一 指向另一张表的记录通过外键建立表与表之间的关联外键命名建议用有业务含义的小写下划线形式你可能注意到后续的截图中已经有数据了——这些测试数据是我们为了演示效果提前录入的别着急。在 NocoBase 中数据的增删改查都是通过前端页面完成的。第 3 章我们会搭建表格来展示数据第 4 章会搭建表单来录入数据一步步揭晓。相关资源数据源概述 — NocoBase 数据建模核心概念数据表 — 全部数据表类型详解数据表字段 — 所有字段类型详解下拉单选 — 固定选项字段的配置说明树表 — 树表的邻接表实现与使用约束多对一关联 — 关联关系配置说明第 3 章搭建页面 — 下一章让数据真正展示出来【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考