扩展 NocoBase 用户数据同步数据源:SyncSource 接口实现与注册全流程
2026/9/18 12:35:40 网站建设 项目流程

扩展 NocoBase 用户数据同步数据源:SyncSource 接口实现与注册全流程

【免费下载链接】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 的用户数据同步插件(@nocobase/plugin-user-data-sync)提供了从外部系统拉取用户与部门数据的扩展点。本文围绕官方文档「扩展同步数据源」展开,完整覆盖SyncSource抽象类的pull()接口、UserData返回结构、服务端registerType类型注册以及客户端AdminSettingsForm配置表单的注册方式,并结合插件源码验证了options配置的实际来源、任务生命周期与同步落库流程,读完即可在自研插件中接入一个完整的自定义同步数据源。

概述:数据源类型如何被管理和使用

用户数据同步插件的服务端入口在 PluginUserDataSyncServer。它在beforeLoad阶段注册SyncSourceModel并创建SyncSourceManager(数据源类型注册与管理)与UserDataResourceManager(同步数据落库),在load阶段创建UserDataSyncService并对外定义userData资源,挂载了四个核心 action:

Action作用
userData:listSyncTypes列出已注册的同步数据源类型(客户端「新增」下拉列表的数据来源)
userData:pull按数据源名称触发一次拉取同步
userData:push推送同步
userData:retry重试失败的同步任务

从源码结构看,扩展一个数据源需要「两条腿走路」:

  1. 服务端:实现SyncSource子类并向sourceManager注册类型,负责真正去外部系统pull()数据;
  2. 客户端:向客户端插件的sourceTypes注册AdminSettingsForm组件,负责在管理界面渲染该类型的自定义配置表单。

数据源实例本身持久化在 userDataSyncSources 集合中,核心字段包括:name(唯一的数据源名称)、sourceType(对应注册的类型标识)、enabled(是否启用,默认false)、options(json 类型自定义配置,默认{}),以及指向userDataSyncTaskstasks一对多关联。这正是下面服务端与客户端两半代码衔接起来的「中间层」。

服务端扩展

数据源接口:继承 SyncSource 抽象类

内置的用户数据同步插件提供了数据源类型的注册和管理。扩展数据源类型,需要继承插件提供的SyncSource抽象类,并实现抽象方法pull()

import { SyncSource, UserData } from '@nocobase/plugin-user-data-sync'; class CustomSyncSource extends SyncSource { async pull(): Promise<UserData[]> { return []; } }

对照 SyncSource 抽象类源码,可以看到它通过构造参数SyncSourceConfig(包含sourceInstanceoptionsctx)注入了三个实例属性:

属性说明
instance当前数据源的SyncSourceModel实例(即userDataSyncSources集合中的一条记录),因此可以直接读取this.instance.name等字段
options数据源的自定义配置对象,即userDataSyncSources.options字段中的 json 内容
ctx请求上下文,可用于访问数据库、日志等应用上下文能力

除抽象方法pull()外,SyncSource还内置了同步任务生命周期辅助方法,扩展方无需自行管理任务状态:

方法行为源码位置
newTask()创建一个status: 'init'的任务,batch为时间戳+随机数生成sync-source.ts#L43-L46
beginTask(taskId)校验任务处于init后置为processing,否则抛错sync-source.ts#L48-L59
endTask({ taskId, success, cost, message })校验任务处于processing后置为success/failed,并记录耗时与错误信息sync-source.ts#L61-L75
retryTask(taskId)仅当任务为failed时重置为processingsync-source.ts#L77-L90

读取自定义配置 options

SyncSource提供了options属性,用于获取数据源的自定义配置(如第三方系统的appidsecret):

import { SyncSource, UserData } from '@nocobase/plugin-user-data-sync'; class CustomSyncSource extends SyncSource { async pull(): Promise<UserData[]> { //... const { appid, secret } = this.options; //... return []; } }

options的流转链路可以从源码得到印证:SyncSourceManager.create() 在实例化数据源时,将集合记录的sourceInstance.options原样传入构造函数,即new syncSource({ sourceInstance, options: sourceInstance.options, ctx })。这些options的值由客户端配置表单提交后写入userDataSyncSources.options(json 字段,默认{}),因此服务端this.options上能取到的键名必须与客户端AdminSettingsForm收集并提交的字段保持一致。

UserData 字段说明

pull()的返回值是UserData[],其通用字段说明如下:

字段说明
dataType数据类型,可选值为userdepartment
uniqueKey唯一标识字段
records数据记录
sourceName数据源名称

dataTypeuser,则records包含以下字段:

字段说明
id用户 ID
nickname用户昵称
avatar用户头像
email邮箱
phone手机号
departments所属部门 ID 数组

dataTypedepartment,则records包含以下字段:

字段说明
id部门 ID
name部门名称
parentId父级部门 ID

源码核对提示:对照当前仓库中的类型定义 UserData,matchKey为可选字段,且落库前会校验每条记录必须包含uid字段——saveOriginRecords 中,若record.uid === undefined会直接抛出record must has uid错误。当前源码中的 FormatUser 与 FormatDepartment 分别以uid(唯一标识)、nicknameemailphonedepartments以及uidtitleparentUid等字段承载数据,并额外支持isDeleted软删除标记。实际开发时建议以源码类型定义为准:保证每条记录携带uidsourceName使用this.instance.name

数据源接口实现示例

结合第三方 API 的完整实现示例(来自官方文档):

import { SyncSource, UserData } from '@nocobase/plugin-user-data-sync'; class CustomSyncSource extends SyncSource { async pull(): Promise<UserData[]> { // ... const ThirdClientApi = new ThirdClientApi( this.options.appid, this.options.secret, ); const departments = await this.clientapi.getDepartments(); const users = await this.clientapi.getUsers(); // ... return [ { dataType: 'department', uniqueKey: 'id', records: departments, sourceName: this.instance.name, }, { dataType: 'user', uniqueKey: 'id', records: users, sourceName: this.instance.name, }, ]; } }

要点说明:

  • 一次pull()可以同时返回部门与用户两类UserData,插件会按dataType分发给不同的目标资源处理;
  • sourceName使用this.instance.name,即该数据源实例在userDataSyncSources中注册的唯一名称,同步记录表(userDataSyncRecords)会按sourceName + 唯一键 + dataType三元组做增量比对与更新;
  • 外部 API 客户端(示例中的ThirdClientApi)由扩展方自行实现,认证凭据从this.options读取。

数据源类型注册

扩展的数据源需要向数据管理模块注册:

import UserDataSyncPlugin from '@nocobase/plugin-user-data-sync'; class CustomSourcePlugin extends Plugin { async load() { const syncPlugin = this.app.pm.get( UserDataSyncPlugin, ) as UserDataSyncPlugin; if (syncPlugin) { syncPlugin.sourceManager.registerType('custom-source-type', { syncSource: CustomSyncSource, title: 'Custom Source', }); } } }

注:官方文档原文示例中写的是reigsterType,结合 SyncSourceManager 源码 核对,实际方法名为registerType,文档示例存在拼写笔误,以源码为准。

registerType(syncSourceType, { syncSource, title })内部将配置写入一个Registry,注册后有两个直接收益:

  • title会随 listTypes() 一起返回,经userData:listSyncTypesaction 提供给前端,成为「新增数据源」下拉菜单中的展示名;
  • 管理界面按name/id查找数据源并触发同步时,SyncSourceManager.getByName/getById 会先按enabled: true过滤出启用状态的记录,再用注册表中的构造函数实例化SyncSource子类——若类型未注册会抛出SyncSourceType [...] is not found

注册动作放在插件的load()生命周期中,通过this.app.pm.get(UserDataSyncPlugin)获取已启用的同步插件实例;由于依赖同步插件存在,源码示例中保留了if (syncPlugin)的空值保护,这一写法建议沿用。

客户端扩展

registerType 注册类型与 AdminSettingsForm

客户端用户界面通过用户数据同步插件客户端提供的registerType接口注册:

import SyncPlugin from '@nocobase/plugin-user-data-sync/client'; class CustomSourcePlugin extends Plugin { async load() { const sync = this.app.pm.get(SyncPlugin); sync.registerType('custom-source-type', { components: { AdminSettingsForm, // 后台管理表单 }, }); } }

从 客户端插件源码 看,PluginUserDataSyncClient内部维护了一个sourceTypes注册表(Registry<SourceOptions>),SourceOptions的类型定义为{ components: Partial<{ AdminSettingsForm: ComponentType }> }。注册时的类型标识字符串必须与服务端registerType的第一个参数一致(示例中均为'custom-source-type'),这样客户端表单才能与服务端拉取逻辑对应到同一个数据源类型。

该组件的渲染时机可在 Options.tsx 中确认:useAdminSettingsForm(sourceType)按当前表单的sourceType(新增)或记录的sourceType(编辑)从注册表取出AdminSettingsForm组件,Options组件在其存在时渲染之,否则不渲染任何内容——因此未注册客户端组件的类型只能走通用配置,无法收集自定义options

后台管理表单

管理界面中,数据源编辑表单的结构是「上通用 + 下自定义」:

  • 上方为通用的数据源配置,对应userDataSyncSources集合的基础字段:数据源名称name、类型sourceType、显示名displayName、是否启用enabled等,由插件内置的userDataSyncSourcesSchema表单 Schema 渲染(见 UserDataSyncSource.tsx 中挂载的 SchemaComponent);
  • 下方为可注册的自定义配置表单部分,即扩展方注册的AdminSettingsForm组件,用于收集该类型的专属配置(如appidsecret),提交后写入options字段,并在服务端pull()时通过this.options取用。

此外,客户端插件在load()中还会通过app.pluginSettingsManager.add('users-permissions.sync', ...)UserDataSyncSource页面挂到「用户与权限 → 同步」设置入口下(client/index.tsx#L32-L40),并绑定 ACL 片段pm.user-data-sync,与服务端 plugin.ts 中注册的userData:*userDataSyncSources:*userDataSyncTasks:*权限片段配套。

同步执行流程:pull 之后的落库与任务管理

理解扩展接口时,有必要知道pull()返回的数据在插件内部如何流转(可结合 UserDataResourceManager 与测试用例 resource-manager.test.ts、api.test.ts 验证):

  1. 落原始记录saveOriginRecords(data)将每条记录按sourceName + uid + dataType查重,存在则把旧数据存入lastMetaData再覆盖metaData,不存在则新建,形成同步记录表(userDataSyncRecords)的增量基线;
  2. 分发给目标资源updateOrCreate(data)遍历注册的UserDataResource节点(按拓扑排序,天然支持「先部门后用户」这类依赖顺序),对accepts匹配dataType的资源逐条执行update()create()——已有本地映射走更新,无映射走创建;
  3. 维护映射关系:目标资源返回的资源主键会写回/移除userDataSyncRecordsResources映射表,供下次同步判断「更新还是创建」;
  4. 任务与重试:任务状态机为init → processing → success/failed,失败后可在管理界面「Tasks」面板对failed状态任务点击重试(客户端对应userData:retryaction),任务耗时与错误信息记录在任务表的costmessage字段中。

这也解释了为什么扩展SyncSource时只需要关心pull():任务状态、记录比对、资源分发、失败重试均由插件框架统一接管。

关键文件索引

文件内容
src/server/sync-source.tsSyncSource抽象类、任务生命周期方法
src/server/sync-source-manager.ts服务端类型注册表registerType/listTypes/实例化逻辑
src/server/plugin.ts服务端插件入口,userData资源与 ACL 片段注册
src/server/user-data-resource-manager.tsUserData/FormatUser/FormatDepartment类型与落库分发逻辑
src/server/collections/user-data-sync-sources.ts数据源集合字段定义(optionsenabled等)
src/client/index.tsx客户端插件入口与registerType
src/client/Options.tsxAdminSettingsForm组件的查找与渲染
src/client/UserDataSyncSource.tsx管理界面:新增/同步/任务/重试交互
docs/docs/cn/users-permissions/sync/dev/source.md官方原始文档

小结

扩展一个 NocoBase 用户数据同步数据源的最小闭环是:实现SyncSource子类并在load()中调用服务端sourceManager.registerType;实现AdminSettingsForm并调用客户端插件registerType;两端类型标识一致后,管理界面即可新增该类型数据源、填写自定义options,点击同步后由userData:pull触发pull(),框架自动完成任务管理、记录比对与向用户/部门等目标资源的分发。实现时注意以当前源码类型定义核对记录字段(尤其uid必填约束),并保留对同步插件实例的空值保护。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询