开发一个 NocoBase 应用,就是在已有的应用结构和能力上实现业务规则:数据怎样保存,谁可以执行操作,页面怎样把结果呈现给用户。本节按主题介绍这些工作,供你开发时查阅;如果想先从头完成一个功能,可以阅读搭建第一个应用。
以下使用独立的“项目与任务”示例说明各部分职责。它与入门示例的表名、接口和测试配置不同;运行专题代码前,先按数据模型与迁移准备对应版本。只了解职责时,不必先运行示例。
应用代码、模板和插件
创建应用后,你得到的是一份可以修改和独立维护的应用源码。模板提供初始结构,Core 的包提供运行与开发能力,插件提供可复用的功能。
| 部分 | 提供什么 | 开发时怎样使用 |
|---|---|---|
| 应用代码 | 自己的页面、接口、数据模型、业务服务和测试 | 在应用仓库中直接修改,随应用发布 |
| 应用模板 | 启动入口、页面布局、组件、配置和开发约定 | 创建时生成到项目中;以后升级时合并模板变化 |
| Core 的基础包 | 客户端和服务端运行时、数据访问、配置等 | 通过公开 API 使用,由项目依赖管理版本 |
| 插件 | 认证、用户、权限、文件等可复用能力 | 使用应用已注册插件的公开接口、组件和扩展方式 |
例如,项目名称和任务归属是应用自己的业务;登录和角色权限可以使用已有插件。默认模板已经带有多种插件,开发功能时先检查现有能力,无需把“安装插件”当作每个功能的第一步。
任务页面也不必包装成插件。只有需要在多个应用之间复用、独立发布和管理版本时,才需要考虑插件边界。不要修改 node_modules/ 中的插件实现来定制应用。
模板生成的布局和主题同样属于应用源码。优先使用路由声明、组件组合和主题配置;确实需要改动框架结构时,记录原因,方便以后升级 NocoBase Core时保留业务定制。
一个功能由哪些部分组成
以“把任务归入项目”为例,完整交付不只有一个选择框:
| 部分 | 要解决的问题 | 示例中的位置 |
|---|---|---|
| 数据模型 | 任务怎样保存项目,旧任务怎样保留,项目不存在时怎么办 | database/main/migrations/ |
| 业务服务 | 能否修改这条任务,目标项目是否可选,怎样保存关联 | server/providers/tutorial-project-service.ts |
| HTTP 接口 | 谁在调用,输入是否合法,返回什么结果 | server/routes/tutorial-projects.ts |
| 权限声明与服务注册 | 有哪些业务操作,服务怎样供接口使用 | server/providers/tutorial-projects.ts |
| 页面路由与导航 | 从哪里进入,哪些用户可以打开页面 | client/routes.ts |
| 页面与交互 | 展示当前项目,选择候选项,处理加载和保存结果 | client/pages/tasks/[taskId]/project.tsx |
| 文案 | 中英文名称、字段提示和错误反馈 | client/locales/ |
| 验证 | 数据是否保留,允许的操作能否成功,越权是否被拒绝 | tests/ |
这些位置是一份分工表,不是每次都要新增一套文件。只调整表单文案不需要迁移,只增加字段也不必重新编写登录模块。先找出需求改变了什么,再修改对应部分。完整目录说明见项目结构。
一次保存怎样到达数据库
用户在“修改项目”弹窗中选好项目并点击保存后,请求沿着下面的路径处理:
- 页面收集输入。 页面通过
useApiClient()请求PATCH tutorialTasks/:id/project,提交项目 ID;解除关联则提交null。客户端使用相对于应用 API 根路径的地址,不写死服务器地址或/main。 - 接口处理请求边界。 路由验证登录状态、修改归属的操作权限、任务 ID 和请求内容,并取得本次操作在任务与项目两边的数据权限策略。
- 服务执行业务规则。 接口调用
TutorialProjectService.assignTask()。服务通过带策略的 Repository 查找任务和目标项目,确认允许操作后更新关联;本例把这些数据库操作放在同一事务中。 - 数据库保存结果。 迁移已经建立字段和外键,数据库拒绝指向不存在项目的关联。迁移负责事先改变结构,不会在每次保存时重新运行。
- 结果返回页面。 接口返回保存结果,页面更新任务列表并给出反馈。失败时保留用户输入,把字段错误放回选择框;任务已经不存在时停止编辑。
服务处理业务规则,Provider 负责把服务注册到应用容器中,两者不是同一个概念。接口通过服务 token 取得服务实例,服务本身不依赖页面,也不需要读取 HTTP Context。以后有其他调用入口时,可以复用业务规则,但仍要明确该入口的身份、权限和输入校验。
权限贯穿整条路径
页面访问权限决定能否进入页面;业务操作权限决定能否执行动作;数据范围决定这次操作可以处理哪些记录。隐藏按钮只是界面反馈,不能替代服务端检查。
“修改任务项目”尤其需要检查两边:用户既要能修改这条任务,也要能把任务分配到目标项目。能查看项目不代表能进行分配。外键只能证明项目存在,不能证明当前用户有权使用它。
文件需要接入应用
新建文件后,还要通过应用已有入口注册:
client/routes.ts声明页面及导航;页面组件放在client/pages/。server/routes/index.ts收集应用接口。server/providers/index.ts收集服务 Provider。client/runtime.ts和server/runtime.ts组合各自的路由、插件、配置和服务。
通常添加业务功能只需要修改这些收集入口,不需要改写客户端或服务端的启动流程。不要把“文件已经存在”当作“功能已经注册”。
怎样拆分一次业务改动
先用业务语言写清楚结果。例如:“用户可以把一条任务归入自己有权选择的项目,旧任务保留原样,保存失败时不丢失输入。”再依次回答:
- 数据如何演进? 新字段是否允许为空,旧数据如何处理,哪些规则需要数据库约束。
- 操作如何授权? 谁能打开页面、调用接口,各自可以处理哪些任务和项目。
- 接口如何表达? 输入、输出、失败情况是什么,哪些业务规则需要复用。
- 页面如何反馈? 加载、空结果、无权限、保存成功和失败时分别展示什么。
- 怎样验证? 在独立测试数据上验证正常路径、拒绝路径和原有功能。
这份顺序也适用于和 Agent 合作。业务决定由你提供,项目约定和具体实现可以交给 Agent 查阅、实现和验证,见和 Agent 一起开发。
按当前问题继续阅读
| 当前要处理的问题 | 文档 |
|---|---|
| 给现有数据增加字段、关系或约束 | 数据模型与迁移 |
| 校验请求、复用业务规则、检查权限并声明 API | 业务服务与 HTTP 接口 |
| 实现列表、表单、弹窗及加载和错误反馈 | UI 组件与交互 |
| 编写数据查询、批量操作与事务 | 查询与操作数据 |
| 组织页面、菜单与访问条件 | 页面、路由与导航 |
| 配置运行参数与业务管理入口 | 应用配置与系统设置 |
| 调整语言和应用外观 | 多语言与主题 |
| 查看、更新或扩展已注册能力 | 管理插件 |
| 选择验证方式并完成验收 | 测试与验收 |
| 想先跑通一个完整功能 | 搭建第一个应用 |
专题文章使用同一个项目示例说明约定,便于相互对照。阅读时可以直接进入需要的主题;运行其中的代码时,按该篇的示例前提准备相应模型、接口和权限。
