666 lines
12 KiB
Markdown
666 lines
12 KiB
Markdown
# 01. Domain Model v0
|
||
|
||
更新时间:2026-04-11
|
||
适用范围:`/mnt/Data1T/mnote-rust`
|
||
|
||
---
|
||
|
||
## 1. 目标
|
||
|
||
本文件定义 `mnote-rust` 的**核心领域模型**。
|
||
|
||
它要解决的问题不是“前端怎么渲染”,而是:
|
||
|
||
- 系统里到底有哪些一等对象
|
||
- 哪些对象是事实层真相,哪些只是派生视图
|
||
- AI / CLI / Web / Desktop 应该围绕什么稳定对象工作
|
||
- OnlyOffice、Mindmap、OCR、RAG 这类能力应挂在哪一层
|
||
|
||
本文件优先保证:
|
||
|
||
- 长期稳定
|
||
- 脱离 UI 依赖
|
||
- 适合 Rust 内核实现
|
||
- 适合 AI 通过结构化协议读写
|
||
|
||
---
|
||
|
||
## 2. 建模原则
|
||
|
||
### 2.1 事实与视图分离
|
||
|
||
以下对象属于**事实层**:
|
||
|
||
- Workspace
|
||
- Page
|
||
- Block
|
||
- Asset
|
||
- Reference
|
||
- Task
|
||
- CommandLog
|
||
- Event
|
||
- AgentSession
|
||
|
||
以下对象默认属于**派生层 / 视图层**:
|
||
|
||
- 目录(TOC)
|
||
- 反链聚合
|
||
- 搜索结果列表
|
||
- Mindmap 视图树
|
||
- OnlyOffice 当前光标/当前页/当前选区
|
||
- AI 面板当前消息列表 UI 状态
|
||
|
||
原则:
|
||
|
||
> 派生层可以重建;事实层必须稳定、可审计、可迁移。
|
||
|
||
### 2.2 人与 AI 共用同一套领域对象
|
||
|
||
不能做人类一套模型、AI 一套模型。
|
||
|
||
要求:
|
||
|
||
- 人类编辑页面,本质是修改 `Page / Block / Asset / Reference`
|
||
- AI 编辑页面,本质也是修改同样的对象
|
||
- CLI 批处理、同步任务、导入器也修改同样的对象
|
||
|
||
### 2.3 第三方编辑器不是事实源
|
||
|
||
Block editor、OnlyOffice、mindmap editor 都不是系统真相。
|
||
|
||
它们只能是:
|
||
|
||
- 某类对象的视图/交互适配器
|
||
- 输入输出变换器
|
||
- 外部文档能力宿主
|
||
|
||
### 2.4 结构化修改优先
|
||
|
||
优先通过:
|
||
|
||
- 显式对象
|
||
- 显式字段
|
||
- 显式命令
|
||
- 显式 ops
|
||
|
||
避免通过:
|
||
|
||
- UI 状态猜测
|
||
- 整文全文字符串替换
|
||
- 第三方编辑器内部瞬时结构直接落库
|
||
|
||
---
|
||
|
||
## 3. 对象总览
|
||
|
||
```text
|
||
Workspace
|
||
├─ Page
|
||
│ ├─ DocumentBody
|
||
│ │ └─ Block*
|
||
│ ├─ PageProperty*
|
||
│ ├─ PageLink*
|
||
│ └─ Snapshot*
|
||
├─ Asset*
|
||
├─ Task*
|
||
├─ AgentSession*
|
||
├─ CommandLog*
|
||
└─ Event*
|
||
|
||
Reference
|
||
├─ block -> block
|
||
├─ block -> page
|
||
├─ block -> asset-fragment
|
||
├─ page -> asset
|
||
└─ task -> page/block/asset
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Workspace
|
||
|
||
`Workspace` 是系统的顶层容器。
|
||
|
||
建议字段:
|
||
|
||
- `workspace_id`
|
||
- `slug`
|
||
- `title`
|
||
- `created_at`
|
||
- `updated_at`
|
||
- `owner_actor_id`
|
||
- `default_locale`
|
||
- `storage_policy`
|
||
- `sync_policy`
|
||
- `feature_flags`
|
||
- `archived_at`
|
||
|
||
职责:
|
||
|
||
- 隔离页面、资产、任务、日志
|
||
- 挂载存储策略与同步策略
|
||
- 作为权限边界与导出边界
|
||
|
||
不负责:
|
||
|
||
- 具体页面内容
|
||
- UI 布局
|
||
- 当前用户会话态
|
||
|
||
---
|
||
|
||
## 5. Page
|
||
|
||
`Page` 是笔记系统的主内容容器。
|
||
|
||
### 5.1 Page 的定位
|
||
|
||
它不是“某个编辑器文件”,而是:
|
||
|
||
- 一条知识对象
|
||
- 一个文档入口
|
||
- 一个块树正文容器
|
||
- 一个可被引用、索引、审计的实体
|
||
|
||
### 5.2 建议字段
|
||
|
||
- `page_id`
|
||
- `workspace_id`
|
||
- `parent_page_id`(允许页面树)
|
||
- `title`
|
||
- `slug`
|
||
- `icon`
|
||
- `cover_asset_id`
|
||
- `body_root_block_id`
|
||
- `page_type`(`note | doc | database_record | inbox | template | system`)
|
||
- `status`(`active | archived | deleted`)
|
||
- `created_at`
|
||
- `updated_at`
|
||
- `last_edited_at`
|
||
- `last_edited_by`
|
||
- `current_revision`
|
||
|
||
### 5.3 PageProperty
|
||
|
||
页面属性不应和前端表格视图绑死。
|
||
|
||
建议独立对象:
|
||
|
||
- `property_key`
|
||
- `value_type`
|
||
- `value`
|
||
- `display_hint`
|
||
- `source`
|
||
|
||
可用于:
|
||
|
||
- 标签
|
||
- 时间
|
||
- 状态
|
||
- 优先级
|
||
- 自定义结构化元数据
|
||
|
||
---
|
||
|
||
## 6. DocumentBody 与 Block
|
||
|
||
### 6.1 为什么以 Block 为正文主模型
|
||
|
||
因为从既有 `mnote` / `mnote-next` 的稳定人层语义看,真正稳定的是:
|
||
|
||
- 单块编辑
|
||
- 在某块后插入块
|
||
- 删除块
|
||
- 重排块
|
||
- 缩进层级
|
||
- 页面引用与块引用占位
|
||
- 复杂块占位
|
||
|
||
这说明“块”比“整篇富文本 JSON”更接近系统真实操作单元。
|
||
|
||
### 6.2 Block 建议字段
|
||
|
||
- `block_id`
|
||
- `workspace_id`
|
||
- `page_id`
|
||
- `parent_block_id`
|
||
- `prev_block_id`
|
||
- `next_block_id`
|
||
- `sort_key`
|
||
- `block_type`
|
||
- `content`
|
||
- `props`
|
||
- `annotations`
|
||
- `status`
|
||
- `created_at`
|
||
- `updated_at`
|
||
- `created_by`
|
||
- `updated_by`
|
||
- `revision`
|
||
|
||
### 6.3 BlockType 建议
|
||
|
||
最小集合建议:
|
||
|
||
- `paragraph`
|
||
- `heading`
|
||
- `bulleted_list_item`
|
||
- `numbered_list_item`
|
||
- `todo`
|
||
- `quote`
|
||
- `code_block`
|
||
- `divider`
|
||
- `callout`
|
||
- `page_reference`
|
||
- `block_reference`
|
||
- `embed_asset`
|
||
- `embed_view`
|
||
- `embed_onlyoffice`
|
||
- `embed_mindmap`
|
||
- `embed_table`
|
||
- `system_placeholder`
|
||
|
||
原则:
|
||
|
||
- 类型应描述语义,不描述某个前端组件名
|
||
- `embed_*` 表示“外部能力挂件”,不是事实层自己变成那个系统
|
||
|
||
### 6.4 Block.content
|
||
|
||
建议:
|
||
|
||
- 文本类块使用结构化 inline span 序列
|
||
- 避免只存单纯 HTML
|
||
- 避免直接依赖第三方编辑器私有 schema
|
||
|
||
建议形态:
|
||
|
||
```json
|
||
{
|
||
"spans": [
|
||
{ "type": "text", "text": "hello" },
|
||
{ "type": "page_ref", "page_id": "page_xxx", "title": "项目计划" },
|
||
{ "type": "text", "text": " world" }
|
||
]
|
||
}
|
||
```
|
||
|
||
### 6.5 Block.props
|
||
|
||
只放结构化、有限、可校验字段,例如:
|
||
|
||
- `level`
|
||
- `checked`
|
||
- `collapsed`
|
||
- `indent`
|
||
- `language`
|
||
- `align`
|
||
- `width`
|
||
- `height`
|
||
- `asset_id`
|
||
- `view_id`
|
||
|
||
避免把任意 UI 状态塞进 props。
|
||
|
||
---
|
||
|
||
## 7. Asset
|
||
|
||
`Asset` 是所有非正文主块树内容的统一挂载对象。
|
||
|
||
### 7.1 Asset 范围
|
||
|
||
包括:
|
||
|
||
- 图片
|
||
- PDF
|
||
- docx
|
||
- xlsx
|
||
- pptx
|
||
- 音频
|
||
- 视频
|
||
- 导出文件
|
||
- OCR 中间产物
|
||
- 结构提取结果
|
||
|
||
### 7.2 建议字段
|
||
|
||
- `asset_id`
|
||
- `workspace_id`
|
||
- `storage_key`
|
||
- `original_name`
|
||
- `mime_type`
|
||
- `size_bytes`
|
||
- `checksum`
|
||
- `origin`(upload / import / generated / synced)
|
||
- `asset_kind`(image / pdf / office / audio / video / binary / extracted_text)
|
||
- `created_at`
|
||
- `updated_at`
|
||
- `created_by`
|
||
- `status`
|
||
- `latest_version_id`
|
||
|
||
### 7.3 AssetVersion
|
||
|
||
附件应支持版本化。
|
||
|
||
建议:
|
||
|
||
- `asset_version_id`
|
||
- `asset_id`
|
||
- `version_no`
|
||
- `storage_key`
|
||
- `checksum`
|
||
- `derived_from_version_id`
|
||
- `created_at`
|
||
- `created_by`
|
||
- `change_reason`
|
||
|
||
这样才能支撑:
|
||
|
||
- OnlyOffice 编辑回写
|
||
- OCR 重跑
|
||
- 导入转换
|
||
- AI 修改资产派生内容
|
||
|
||
---
|
||
|
||
## 8. Reference
|
||
|
||
`Reference` 是知识型系统的关键对象,不能只做正文里的临时 token。
|
||
|
||
### 8.1 Reference 范围
|
||
|
||
- 块引用块
|
||
- 页面引用
|
||
- 资产片段引用
|
||
- PDF 页码引用
|
||
- Office 书签/段落锚点引用
|
||
- URL 引用
|
||
- 检索结果引用
|
||
|
||
### 8.2 建议字段
|
||
|
||
- `reference_id`
|
||
- `workspace_id`
|
||
- `source_object_type`
|
||
- `source_object_id`
|
||
- `target_object_type`
|
||
- `target_object_id`
|
||
- `anchor`
|
||
- `label`
|
||
- `snippet`
|
||
- `confidence`
|
||
- `created_at`
|
||
- `created_by`
|
||
- `ref_kind`
|
||
|
||
### 8.3 Anchor
|
||
|
||
`anchor` 建议作为独立结构:
|
||
|
||
```json
|
||
{
|
||
"kind": "pdf_page",
|
||
"page": 12,
|
||
"bbox": null,
|
||
"text_quote": "..."
|
||
}
|
||
```
|
||
|
||
或:
|
||
|
||
```json
|
||
{
|
||
"kind": "office_bookmark",
|
||
"bookmark": "Heading_3",
|
||
"text_quote": "..."
|
||
}
|
||
```
|
||
|
||
原则:
|
||
|
||
- 锚点是系统对象,不是编辑器私有游标
|
||
- 锚点失效时要能标记 stale,而不是静默消失
|
||
|
||
---
|
||
|
||
## 9. SelectionAnchor
|
||
|
||
`SelectionAnchor` 不是长期事实主对象,但在 Agent / 编辑器桥接中有价值。
|
||
|
||
它表示:
|
||
|
||
- 当前选区
|
||
- 当前光标
|
||
- 当前活动页
|
||
- 当前资产中的定位点
|
||
|
||
建议定位为:
|
||
|
||
- **短生命周期上下文对象**
|
||
- 可存入 AgentSession / UI Session
|
||
- 默认不直接作为主事实落库
|
||
|
||
因为:
|
||
|
||
- 它变化太频繁
|
||
- 容易和编辑器宿主耦合
|
||
- 更适合作为命令输入上下文
|
||
|
||
---
|
||
|
||
## 10. Task
|
||
|
||
`Task` 既是用户任务,也是 AI 工作单元。
|
||
|
||
### 10.1 建议字段
|
||
|
||
- `task_id`
|
||
- `workspace_id`
|
||
- `title`
|
||
- `description`
|
||
- `task_type`
|
||
- `status`
|
||
- `priority`
|
||
- `assignee_actor_id`
|
||
- `source_page_id`
|
||
- `source_block_id`
|
||
- `input_payload`
|
||
- `output_payload`
|
||
- `created_at`
|
||
- `updated_at`
|
||
- `due_at`
|
||
- `completed_at`
|
||
|
||
### 10.2 为什么纳入领域模型
|
||
|
||
因为 AI 长期介入后,很多能力不是即时编辑,而是:
|
||
|
||
- 导入任务
|
||
- 提取任务
|
||
- OCR 任务
|
||
- 摘要任务
|
||
- 索引重建任务
|
||
- 同步任务
|
||
|
||
这些都应进入系统内核,而不是散在外部脚本里。
|
||
|
||
---
|
||
|
||
## 11. AgentSession
|
||
|
||
`AgentSession` 表示某次 AI 介入的上下文单元。
|
||
|
||
### 11.1 建议字段
|
||
|
||
- `agent_session_id`
|
||
- `workspace_id`
|
||
- `provider`
|
||
- `model`
|
||
- `initiator_actor_id`
|
||
- `status`
|
||
- `started_at`
|
||
- `updated_at`
|
||
- `ended_at`
|
||
- `tool_policy`
|
||
- `confirmation_policy`
|
||
- `summary`
|
||
|
||
### 11.2 不应存什么
|
||
|
||
不应把整份聊天 UI 状态直接当作领域模型核心。
|
||
|
||
建议分开:
|
||
|
||
- 领域层只保留会话元信息、工具调用摘要、事件链路
|
||
- 详细消息可作为附属日志或导出工件保存
|
||
|
||
---
|
||
|
||
## 12. CommandLog 与 Event
|
||
|
||
### 12.1 CommandLog
|
||
|
||
代表一次显式操作请求。
|
||
|
||
建议字段:
|
||
|
||
- `command_id`
|
||
- `workspace_id`
|
||
- `command_name`
|
||
- `actor`
|
||
- `source`
|
||
- `target`
|
||
- `payload_hash`
|
||
- `reason`
|
||
- `refs`
|
||
- `idempotency_key`
|
||
- `dry_run`
|
||
- `status`
|
||
- `requested_at`
|
||
- `finished_at`
|
||
- `error_code`
|
||
- `error_message`
|
||
|
||
### 12.2 Event
|
||
|
||
代表命令执行后产生的事实变化。
|
||
|
||
建议字段:
|
||
|
||
- `event_id`
|
||
- `workspace_id`
|
||
- `command_id`
|
||
- `event_type`
|
||
- `object_type`
|
||
- `object_id`
|
||
- `before_revision`
|
||
- `after_revision`
|
||
- `payload`
|
||
- `created_at`
|
||
|
||
原则:
|
||
|
||
- 命令与事件分离
|
||
- 先有意图,再有变化
|
||
- 审计必须能回答“谁因为什么改了什么”
|
||
|
||
---
|
||
|
||
## 13. View / Derived Object
|
||
|
||
以下对象建议明确标注为派生对象,不直接当真相:
|
||
|
||
- TOCView
|
||
- BacklinkView
|
||
- SearchHit
|
||
- KnowledgeGraphView
|
||
- MindmapProjection
|
||
- OnlyOfficeProjection
|
||
- PagePreview
|
||
- DailyDigest
|
||
|
||
它们的共同特点:
|
||
|
||
- 来自事实层投影
|
||
- 可以缓存
|
||
- 可以失效
|
||
- 可以重建
|
||
|
||
---
|
||
|
||
## 14. Mindmap 的正确定位
|
||
|
||
从既有设计看,Mindmap 很适合走结构化 `ops` 协议。
|
||
|
||
但在 `mnote-rust` 中,建议将其定义为:
|
||
|
||
- `Asset` 或 `ViewProjection` 的一种
|
||
- 由 `Page / Block / Reference / Asset` 投影生成或承载
|
||
- 支持独立存储其节点视图结构
|
||
- 但不让它成为全系统唯一知识主模型
|
||
|
||
建议:
|
||
|
||
- 若是“页面内导图块”,则作为 `embed_mindmap` block 挂载
|
||
- 若是“独立导图资产”,则作为 `Asset(kind=mindmap)`
|
||
- AI 修改导图时,走 `MindmapOp[]`,但最终仍写入系统命令日志
|
||
|
||
---
|
||
|
||
## 15. OnlyOffice 的正确定位
|
||
|
||
OnlyOffice 不应被建模为“Page 本体”,应被建模为:
|
||
|
||
- `Asset(kind=office)`
|
||
- `AssetVersion`
|
||
- `OnlyOfficeAdapterSession`
|
||
- `SelectionAnchor(kind=office_*)`
|
||
- `Reference(anchor=office_*)`
|
||
|
||
也就是说:
|
||
|
||
- Office 文档是系统资产
|
||
- OnlyOffice 是该资产的一种编辑适配器
|
||
- 编辑产生新版本与事件
|
||
- AI 可以通过插件/适配器读当前选区、插入内容、替换内容
|
||
- 但系统真相依然在你的领域模型与资产版本链里
|
||
|
||
---
|
||
|
||
## 16. v0 必须避免的错误
|
||
|
||
### 16.1 不要把前端 store 当领域模型
|
||
|
||
例如:
|
||
|
||
- 当前页面 UI 选中态
|
||
- 面板展开态
|
||
- 聊天输入框草稿
|
||
|
||
这些不应进入核心领域。
|
||
|
||
### 16.2 不要把第三方编辑器 JSON 当系统法典
|
||
|
||
否则未来:
|
||
|
||
- 替换编辑器会非常痛苦
|
||
- AI 只能学编辑器私有结构
|
||
- CLI 难以稳定操作
|
||
|
||
### 16.3 不要把全文字符串替换当主修改方式
|
||
|
||
必须保留结构化修改能力。
|
||
|
||
### 16.4 不要把向量库结果当事实对象
|
||
|
||
向量结果是检索派生物,不是知识真相。
|
||
|
||
---
|
||
|
||
## 17. 一句话结论
|
||
|
||
`mnote-rust` 的领域模型应以 **Workspace / Page / Block / Asset / Reference / Task / AgentSession / CommandLog / Event** 为核心;
|
||
Mindmap、OnlyOffice、搜索结果、AI 面板都只是围绕这些核心对象形成的投影、适配器或任务系统,而不能反客为主。
|