# 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 面板都只是围绕这些核心对象形成的投影、适配器或任务系统,而不能反客为主。