feat: 飞书文档工具支持原生表格 - #274
Conversation
新增表格转换能力,为飞书文档写入原生 table 铺路: - parseMarkdownTable: 解析 GFM 表格,识别对齐分隔符、检测表头、 补齐不规则行、支持无表头表格 - markdownToSegments: 将 markdown 拆成有序 segment,普通 block 成组, 每个表格独立成 table segment(供 descendant API 使用) - buildTableDescendants: 构建 table→table_cell→text 的嵌套 descendant 结构(临时 block id),空单元格用空 elements 数组 - 新增 TABLE=31 / TABLE_CELL=32 常量 - markdownToBlocks 保持向后兼容:表格降级为 plaintext 代码块 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
为 parseMarkdownTable、markdownToSegments、buildTableDescendants 增加 单元测试:表头检测、对齐分隔符、不规则行补齐、无表头表格、文本/表格 交错分段、相邻表格独立成段、单元格行内 markdown、空单元格、以及 markdownToBlocks 表格降级为代码块的向后兼容行为。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 新增 writeMarkdownContent 助手:普通 block 走 documentBlockChildren.create (分批),表格走 documentBlockDescendant.create 创建原生飞书表格; 按 segment 顺序写入,支持 insert 时按顶层 block 数递增 index - write/append/create/insert_blocks 统一改用该助手,不再把表格降级为代码块 - 修复 BLOCK_TYPE_NAMES 16–33 段整体错位一位的历史 bug (之前 todo/divider/table/table_cell 等在 read/list_blocks 显示错误名) - 修复 read_blocks 中 divider 判断 21→22 - 工具描述补充表格支持说明 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @lishuceo's task in 3m 17s —— View job Claude Code Review
|
真实飞书 API 验证发现:table_cell 内的 text block 若 elements 为空数组
([]),descendant.create 返回 1770001 invalid param。改为始终走
parseInlineMarkdown(content)——空串会返回 [{text_run:{content:''}}],
飞书接受。已用真实文档端到端验证:原生 table(31)+table_cell(32) 正确
创建,空单元格渲染为空、行内 markdown 生效、与其它 block 混排顺序正确。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
✅ 已完成真实飞书 API 端到端验证用 DevBot 凭证直连飞书创建真实文档验证,发现并修复了一个离线无法确认的问题: Bug:空单元格用 验证结果(真实文档读回):
typecheck ✓ / 表格单测 52 passed ✓(含更新后的空单元格回归用例) |
|
Claude finished @lishuceo's task in 1m 44s —— View job Claude Code Review (re-run on synchronize)
|
处理 PR review 反馈(confidence 85 的真实回归):write 覆盖时用 documentBlock.list 的扁平总数作为 batchDelete 的 end_index,但 list 返回的是含嵌套 table_cell/cell-text 的全部 block,而 batchDelete 删的是 page 的直接子节点。文档一旦写过原生表格(嵌套结构),end_index 就会 远超 page 直接子块数,导致再次 write 覆盖被飞书拒绝 (1770001)。 改为按 page block 的 children 数组长度(直接子块数)计算 end_index。 补充 doc.test.ts 回归用例:含原生表格的文档(page 4 直接子块 / 12 扁平 block)覆盖时 end_index=4 而非 12;空文档跳过 batchDelete。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Fixed (commit 根因确认: 修复:改用 page block 的 const directChildCount = (pageBlock?.children as string[] | undefined)?.length ?? 0;
if (directChildCount > 0) {
await client.docx.documentBlockChildren.batchDelete({
path: { document_id: args.doc_token, block_id: pageBlockId },
data: { start_index: 0, end_index: directChildCount },
});
}回归测试: 复核提到的另一点(空单元格 |
|
Claude finished @lishuceo's task in 1m 16s —— View job Claude Code Review (re-run on synchronize)
✅ Approved — 上一轮的
|
| 问题 | 状态 |
|---|---|
空单元格 elements: [] 被飞书拒绝 (1770001) |
✅ 已修 (b9a25aa),作者真实 API 端到端验证通过 |
write 覆盖含原生表格文档越界失败 |
✅ 已修 (3a22947),含精准回归测试 |
BLOCK_TYPE_NAMES 段整体错位 + divider 22 |
✅ 上一轮已核对无误 |
说明:因权限限制本次未能在 CI 环境跑测试套件,代码审查基于源码静态复核;作者已报告
npx vitest run全套 1776 通过(唯一失败为无关的 memory 检索基准)。
结论
✅ Approved — 2 个此前提出的问题均已修复并补齐回归测试,实现清晰、边界处理完整。可以合并。
背景
feishu_doc工具此前遇到 Markdown 表格时,会把整张表降级渲染成 plaintext 代码块(markdown-to-blocks.ts里 “Feishu API doesn't support inline table creation” 的注释)。实际上飞书文档 API 支持通过documentBlockDescendant.create一次性提交嵌套结构来创建原生表格,本 PR 实现这条路径。改动
markdown-to-blocks.tsparseMarkdownTable:解析 GFM 表格 —— 识别:--/--:/:-:对齐分隔符、检测表头、补齐不规则行、支持无表头表格markdownToSegments:把 Markdown 拆成有序 segment,普通 block 成组,每个表格独立成tablesegmentbuildTableDescendants:构建table(31)→table_cell(32)→text(2)的嵌套 descendant 结构(临时 block id);空单元格用空elements数组markdownToBlocks保持向后兼容(表格仍降级为代码块,供扁平 API 使用)doc.tswriteMarkdownContent:普通 block 走documentBlockChildren.create(分批),表格走documentBlockDescendant.create;按 segment 顺序写入,insert_blocks传 index 时按顶层 block 数递增write/append/create/insert_blocks统一改用该助手BLOCK_TYPE_NAMES映射表 16–33 段整体错位一位(导致 read/list_blocks 里 todo/divider/table/table_cell 等显示成错误的类型名);read_blocks里 divider 判断21→22(权威枚举 Divider=22、Table=31、TableCell=32,已核对飞书开放平台文档)测试
npm run typecheck✓npm run lint(改动文件)✓npx vitest run:1774/1775 通过。唯一失败是memory/quality.test.ts的语义检索质量基准(依赖本地记忆库内容,与本改动无代码交集,属既有/环境性失败)代码在离线环境完成,未对真实飞书文档做过端到端写入。建议 review 后跑一次真实建表,重点确认:空单元格用空
elements: []是否被 Feishu 接受(这是唯一无法离线确认的 API 行为选择)。🤖 Generated with Claude Code