协同扩展模块
理解 History、Thread Comment、Worktree、File 与服务端 Office Exchange 如何扩展核心协同服务。
History、Thread Comment、File 与 Worktree 是独立的协同扩展模块,可按产品需求启用。它们可以复用 Transport、身份系统和 物理数据库,但各自保留 Service、Middleware、Event、Database Adapter 与生命周期边界。
服务端 Office Exchange 组合 File 与 Collaboration Service:它在 Office 文件与协同 Unit 之间转换,拥有自己的 Service、 Endpoint 与 Middleware。
模块关系
| 扩展模块 | 与 Core Collaboration 的关系 |
|---|---|
| History | 监听 Core Event,建立派生历史索引 |
| Thread Comment | 复用 Core Unit Room;评论 anchor 属于 Core 内容 |
| File | 保存 Unit 引用的图片、附件与导入导出文件,Unit 数据只记录文件 ID |
| Office Exchange | 通过 Core Service 创建或读取 Unit,通过 File 读写文件 |
| Worktree | 复用 Core Unit 与 OT 能力,并将 draft 合入 trunk |
History、Thread Comment、File 与 Worktree 分别配置自己的 Middleware、Event 与 Database Adapter。
版本历史
History 把 Unit 创建和 confirmed changeset 分组为面向用户的历史条目。它是派生索引;Core Collaboration Service 保存的 confirmed changeset 才是 Unit 内容的权威来源。
const historyService = new UniverHistoryService({ collabService, dbAdapter: historyDatabase, userProvider,});transport.register(new UniverHistoryEndpoint(historyService));构造 History Service 时即订阅 Core Service 的 unitCreated 和 changesetCommitted Event 并更新索引。
dispose() 会解除这些订阅。默认策略按时间窗口和特殊 mutation 分段,而不是每条 changeset 都创建一个历史项。
History 提供 getHistoryList、listHistoryCreators 和 getHistoryChangesets 读取 Middleware。索引由
Service 在内部维护。userProvider 只补全姓名、头像等展示信息,不负责认证或授权。Event Listener 失败
不会回滚已经确认的 Core changeset,因此 History 始终应被理解为可重建的派生数据。
Thread Comment
Comment Service 管理 Sheet/Doc 的评论正文、回复、编辑、删除和 solved 状态:
const commentService = new UniverCommentService({ database: commentDatabase, userProvider,});transport.register( new UniverCommentEndpoint({ service: commentService, roomHost: collabEndpoint, }),);评论正文属于 Comment Adapter。随 Sheet 或 Doc 内容变化的 anchor 仍属于 Core snapshot/changeset。
Comment Endpoint 可以复用主 Collaboration Endpoint 的 Unit Room 发布 comment_update,但实时消息不是
评论数据的权威来源;Client 可以重新 list 评论恢复状态。
Comment Service 提供 add、list、reply、solve/reopen、edit 与 delete Middleware,以及
commentCommitted Event。它不会继承 Core Service 的读取或编辑策略。
文件存储
File 保存 Univer 客户端上传的二进制文件:插入 Unit 的图片和附件,以及导入导出的源文件与结果文件。Unit 数据只记录 文件 ID,加载时客户端用 ID 换取下载地址,snapshot 与 changeset 保持精简。
const fileService = new UniverFileService({ database: fileDatabase, blobStore,});transport.register(new UniverFileEndpoint(fileService));元数据与内容是两个独立合同:IFileDatabaseAdapter 保存文件记录,IFileBlobStore 保存内容。导入导出的临时文件只有
上传者可以访问,并由 Service 定期清理;Unit 的图片与附件通过 upload、signUrl、download Middleware 按 Unit 权限控制。
服务端 Office Exchange
Exchange Service 把 Office 文件导入为协同 Unit,并把 Unit 导出为文件。它依赖 @univerjs-pro/exchange-node 转换,
通过 Collaboration Service 创建和读取 Unit,通过 File Service 保存源文件与结果文件:
const exchangeService = new UniverExchangeService({ collabService, fileService });transport.register(new UniverExchangeEndpoint(exchangeService));导入与导出都是 task:客户端创建 task 后轮询结果。导入经过 Core Service 的 createUnit Middleware,导出经过
readUnitData Middleware;Exchange 自己的 import 与 export Middleware 用于限流、配额与审计。
| Unit | 导入 | 导出 |
|---|---|---|
| Sheet | xls、xlsx、csv、tsv | xlsx、csv、tsv |
| Doc | doc、docx | docx |
| Slide | ppt、pptx | pptx |
| Base | xls、xlsx、csv、tsv | xlsx、csv |
task 保存在 Service 实例内,导入导出节点部署一个即可,并把 Exchange 的三条路由都发到这个节点。
Worktree 草稿与合并
Worktree 为一个或多个 Unit 提供隔离的 draft、冻结、合入评估和逐 Unit merge:
create → draft → ready → merging → merged ↑ │ └ reopen┘draft / ready → discardedconst worktreeService = new UniverCollabWorktreeService({ trunk: { service: collabService, dbAdapter: coreDatabase, }, dbAdapter: worktreeDatabase,});Worktree 以 (worktreeID, unitID) 保存独立 draft changeset,并复用 Core Service 的 Unit、OT 和提交
引擎。只有 draft 状态允许继续修改;markReady() 冻结当前 draft revision;mergeWorktree() 把
Unit 逐个合入 trunk。
Worktree Service 拥有独立的生命周期、读取、draft submit/apply/commit Middleware,以及创建、状态变化、 合入结果和 draft changeset commit Event。最终写入 trunk 时,仍会进入 Core Service 自己的 Middleware。
多个 Unit 的 merge 不保证跨 Unit 原子性,应用应展示每个 Unit 的合入结果。
独立扩展点
启用扩展模块时,应逐项检查:
| 边界 | Core | History | Comment | File | Worktree |
|---|---|---|---|---|---|
| Service | 独立 | 独立 | 独立 | 独立 | 独立 |
| Middleware | 不共享 | 不继承 Core | 不继承 Core | 不继承 Core | 不继承 Core |
| Event | Core lifecycle | 监听 Core Event | commentCommitted | — | Worktree lifecycle/commit |
| Database Adapter | Core Adapter | History Adapter | Comment Adapter | File Adapter 与 Blob Store | Worktree Adapter |
Office Exchange 复用 Core 与 File 的存储:它自己的 import、export Middleware 执行后,导入导出仍进入 Core 与 File 的 Middleware。
这些 Adapter 可以使用同一个数据库,但仍是独立对象和合同。应用释放资源时也应分别释放 Service 与 Adapter。
选择能力
| 产品需求 | 模块 | 首个示例 |
|---|---|---|
| 面向用户的版本历史 | History Service、Endpoint、Adapter | pnpm example:history |
| Sheet 或 Doc 批注 | Comment Service、Endpoint、Adapter | pnpm example:comments |
| 图片、附件与文件存储 | File Service、Endpoint、Adapter、Blob Store | pnpm example:file-service |
| 服务端 Office 导入导出 | Exchange Service、Endpoint + File | pnpm example:exchange |
| 隔离草稿、评审与合并 | Worktree Service、Endpoint、Client、Adapter | pnpm example:worktree |
完整运行方法与页面地址见示例索引。
连接 Web SDK 历史界面
将 History Endpoint 与协同端点一起挂载。Web SDK 的 historyServerUrl 是 /{unitId}/list、/{unitId}/creators 和 /{unitId}/cs 的共同前缀,默认值为 /universer-api/history。如果应用使用其他前缀,请为宿主 History 插件和品类 History UI 插件配置相同的地址。
恢复版本通过协同 mutation 提交,不是调用独立的历史恢复 REST 接口。请保留协同加载、提交与授权配置。createUnitComparisonEngine() 对两份完整快照进行只读比较,不需要运行历史服务,也不负责保存或恢复版本。
你觉得这篇文档如何?