Skip to content

接飞书云文档来源:每篇文档连同它的版本序列 #136

Description

@WaylandYang

接一个 feishu_docs 来源:同步一个飞书云文档空间,每篇文档连同它的版本序列
成为知识库里的文档。

判断依据与前两个工单来源共用一篇决策记录:
docs/decisions/0012
动手前先读它——核心判断是「别取现在,取变化」,只抓当前版本的话这个来源
就退化成了又一个抓网页的。

已有两个可照抄的实现

文件 形状
GitHub 工单 crates/utopia-server/src/github_issues.rs 三次拉取 + 逐工单事件
Jira 工单 crates/utopia-server/src/jira_issues.rs 一次调用取全(expand=changelog

两边都是同一个骨架,照着走即可:

  1. 纯函数 render() 把一条记录排成 Markdown——不联网,于是测得动
  2. fetch_all() 负责分页与鉴权
  3. ingest_sources.rs 里加一个 sync_* 分支,调 ingest_item()
    (身份、sha256 去重、版本记录都由它负责,不用自己写)
  4. sources::KINDS 白名单 + 前端三处(api.tsSourceView["kind"]
    Library.tsx 的建来源对话框、SourcesRail.tsx 的图标与 SYNCING_KINDS

第 4 步容易漏:漏了的症状是界面上选得到、建的时候报 kind must be one of…
而单元测试与 tsc 都看不见。只有端到端会撞上。

三条硬要求

一、正文要写成带日期的陈述句,不是键值对。 Opened by X on 2026-08-18 能抽出带
valid_from 的事实;created_at: 2026-08-18 则要模型自己猜这是什么意思。

二、截断要说出来。 分页有上限时,取回数小于服务端 total 就落一条 warn。
不报的话界面上「同步完成」是一句误导(Jira 那边是 500 / 14506)。

三、拿真实响应做夹具。 手写 JSON 只能证明「我以为的形状」。前两个来源的夹具
分别取自 deeplethe/utopiaissues.apache.org(都匿名可读),字段裁到声明的那些。
它当场抓到过一个手写测试全绿、真实数据报错的 bug。

飞书没有公开可匿名读的实例,所以这一条大概率做不到。那就明说「未经真实实例
验证」
,别跟前两个混为一谈——这是 0012 里写死的要求。

三个已知的未知,需要先答

  1. 凭据与租户:飞书是 tenant_access_token 那套,与前两个的 auth_header
    直传不同,可能要在 config 里存 app_id/app_secret 并自己换 token。
    注意凭据只进不出(编辑来源时留空 = 保留库里原值,见 sync_custom 的写法)。
  2. 富文本不是字符串:飞书文档是块树(docx 的 block),需要一个
    「块树 → 纯文本」的渲染器。Notion / Confluence Cloud(ADF)是同一类问题,
    Jira Cloud v3 也是——本轮走 v2 绕开了。做出来的渲染器最好能被复用。
  3. 文档的「变更史」是版本序列,不是事件流:这更接近 A corpus where the graph grows because the articles did #122 那份维基语料的做法。
    要不要每个版本都取?大概不要——那一份的经验是按「变了多少」采样
    而且要挡掉「有人把页面清空了」这种伪变更(见
    scripts/bench/fetch-wiki-history.mjspersists())。

验收

  • 单元测试覆盖 render()(含空评论不留空节这类边界)
  • 若能拿到真实响应,加夹具测试;拿不到就在 PR 里明说
  • 端到端:建来源 → 同步 → 文档带版本/变更信息 → 二次同步幂等(新增 0)
  • cargo clippy --workspace --all-targetsnpm run typecheck 干净

Confluence / Notion 是同一类,谁想接也欢迎——三个的第 2 条未知是共通的。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions