Install
openclaw skills install forces-reanalyze-smartClawHub Security found sensitive or high-impact capabilities. Review the scan results before using.
完成智慧门店工单复盘全流程。触发词:工单复盘、复盘工单、拉取工单数据、创建复盘文档、工单分析、创建复盘。完整覆盖三个阶段:(1)拉取Furcas工单数据并导入多维表格 (2)创建/更新工单复盘总文档 (3)创建开发复盘文档并填充TOP5统计。当用户要求进行工单复盘时使用。
openclaw skills install forces-reanalyze-smart一次完整的工单复盘涉及 3个文档,按顺序执行:
Step 0:复盘跟踪表(前置检查)
检查/创建 目标月份记录 → 落数据基线
阶段一:工单数据表(飞书多维表格)
Step 1 → Step 2 → Step 3 → Step 4 → Step 5 → Step 5b
解析月份 → 获取Cookie → 拉取Furcas数据 → 创建/复用月份bitable(模板:J4tawf06GijvEmkBTSCcdyNsnyd)→ 导入CSV → 更新mention-doc链接
阶段二:工单复盘总文档(知识库文档)
Step 6 → Step 7
创建文档(模板:GEqawooPYiDXLWkRKZHcJnLcnFc)→ 填充变量
阶段三:开发复盘文档(知识库文档)
Step 8
创建文档(模板:Secqwa7deiaRwTkUsG6cJ7xpnzj)→ 填充TOP5统计
| 类型 | 链接/Token |
|---|---|
| 工单数据Bitable模板 | J4tawf06GijvEmkBTSCcdyNsnyd (table: tblXtjn9QHTcB5oa, view: vew8A2EB9c) |
| 复盘总文档模板 | GEqawooPYiDXLWkRKZHcJnLcnFc |
| 开发复盘文档模板 | Secqwa7deiaRwTkUsG6cJ7xpnzj |
| 复盘跟踪表(前置数据源) | YMQjwQeWCipABCkl8y7ckZlunQe (table: tblpygt9n8dqZcsw,参见 Step 0) |
| Bot App ID | cli_a97b6a0ffc399cc0 |
在创建任何文档之前,先检查复盘跟踪表是否已有目标月份的记录。
https://sqb.feishu.cn/wiki/YMQjwQeWCipABCkl8y7ckZlunQe?table=tblpygt9n8dqZcsw&view=vewme7V2C9
YMQjwQeWCipABCkl8y7ckZlunQetblpygt9n8dqZcsw这条规则是全局最高优先级。任何创建操作前必须先查,查到已存在则直接复用,绝不允许创建第二条同名记录。
操作:feishu_bitable_app_table_record list
参数:
app_token: "YMQjwQeWCipABCkl8y7ckZlunQe"
table_id: "tblpygt9n8dqZcsw"
filter: {
conjunction: "and",
conditions: [
{field_name: "复盘范围", operator: "is", value: ["2026-05"]}
]
}
注意 field_name 必须用中文名「复盘范围」(是文本类型的主字段),不要用 field_id。
判断是否已有目标月份:
record_id 及其包含的所有数据(工单总数、复盘工单数、各分类统计等),进入阶段一绝对禁止:查到已有记录后,再创建第二条同名或同周期的记录。
新建记录(仅在确认不存在时执行):
操作:feishu_bitable_app_table_record create
参数:
app_token: "YMQjwQeWCipABCkl8y7ckZlunQe"
table_id: "tblpygt9n8dqZcsw"
fields:
复盘范围: "{月份标签}" // 整月如 "2026-05"(不要加 "-整" 后缀)
开始时间: {毫秒时间戳} // 范围起始日期 00:00:00 UTC+8
结束时间: {毫秒时间戳} // 范围结束日期 23:59:59 UTC+8
日期: {毫秒时间戳} // 当前操作日期(执行复盘的当天)
| 字段 | 类型 | 填写规则 |
|---|---|---|
复盘范围 | 文本 (主字段) | 整月:2026-05(不要写 2026-05-整);上半月:2026-05-上;下半月:2026-05-下 |
开始时间 | 日期(毫秒时间戳) | 范围起始日 00:00:00 UTC+8,如2026年5月整月 → 1777593600000(对应 2026-05-01T00:00:00+08:00) |
结束时间 | 日期(毫秒时间戳) | 范围结束日 23:59:59 UTC+8,如2026年5月整月 → 1780271999000(对应 2026-05-31T23:59:59+08:00) |
日期 | 日期(毫秒时间戳) | 当前操作日期,即执行复盘的当天,如2026年5月6日 → 1777910400000(2026-05-06T00:00:00+08:00) |
⏰ 时间戳必须二次确认:创建记录后立即读取该记录,检查时间戳对应的年份是否与目标年份一致(2026年)。一个常见的错误是毫秒时间戳算成了2025年的日期。如果时间戳不对,删除该记录,重新计算并创建。
注意:其余字段(工单总数、复盘工单、超时工单数、各解决类别统计等)均为 Lookup/Formula 自动计算字段,无需手动填写,数据导入后会自行更新。
record_id,后续阶段二填充文档第三节表格时需要用户说"做X月工单复盘"或"复盘X月"时:
2026-04)2026-04-01 ~ 2026-04-302026-04如果用户提到"拉取X到X的工单数据",直接使用指定的起止日期。
本阶段需要有效的 Furcas Cookie。
https://furcas.shouqianba.comscripts/fetch_furcas.py 的 cookie = "" 变量中python scripts/fetch_furcas.py -s "2026-04-01" -n "2026-04-30" -o /workspace/furcas.csv
输出字段:问题描述、问题链接、工单状态|修复情况、问题原因、责任人、解决类别、解决模块、超时时间、超时备注
⚠️ 必须检查:先确认目标月份目录下是否已有同名 bitable。用 feishu_wiki_space_node list 列出目录内容,看是否已有工单数据表。如有则直接复用,跳过创建。
使用 feishu_wiki_space_node copy 将模板节点直接复制到目标月份目录:
操作:feishu_wiki_space_node copy
参数:
node_token: "J4tawf06GijvEmkBTSCcdyNynsd" // 工单数据表模板
space_id: "7510046829784662017"
target_parent_token: "{目标月份目录节点token}"
target_space_id: "7510046829784662017"
为什么必须用 wiki node copy?
feishu_bitable_app copy → 创建独立 bitable(不在 wiki 目录中)feishu_wiki_space_node create → 创建空白 bitable(表名 "Table",字段全英文)feishu_wiki_space_node copy → 直接在 wiki 内创建模板副本,字段/视图全部正确智慧门店-2026-5月工单数据fields: {}),用 batch_delete 清空文本(Text, primary)、问题链接(Url)、工单状态|修复情况(Text)、问题原因(Text)责任人(Text)、解决类别(SingleSelect)、解决模块(SingleSelect)超时时间(Text)、超时备注(Text)关键约束:
工单状态|修复情况 含竖线 |(CSV中为 /,脚本会自动映射)问题链接 是 URL 类型,导入时用 {"link": "URL", "text": "查看工单"}责任人 是文本类型(type=1),不是人员类型推荐:使用导入脚本
APP_TOKEN={月份bitable的app_token} \
TABLE_ID={月份bitable的table_id} \
CSV_PATH=/workspace/furcas.csv \
BATCH_SIZE=15 \
node scripts/import_to_bitable.mjs --name "数据表"
⚠️
--name参数必须传:模板复制出来的表名叫数据表,但脚本默认校验名为工单数据。必须用--name "数据表"或TABLE_NAME="数据表"让校验通过。不传的话校验会警告但不阻止导入。
脚本自动:校验表名 → 清空旧数据 → 分批创建记录 → 创建看板视图 → 验证 → 去重检查。
⚠️
batch_delete参数名是record_ids(不是records):这是飞书 API 的规范。如果写错,清空静默失败,新数据追加到旧数据上,导致重复。
⚠️ 去重检查是最后一道防线:即使清空步骤因意外失败(如网络超时、API 限流),去重步骤也会自动删除重复工单,保证每个工单 ID 唯一。
⚠️ 表名校验:脚本在导入前会调用 API 读取目标表的实际名称,与
TABLE_NAME环境变量比对。如果名称不匹配会给出警告,防止数据写入错误的表。
手动替代方案:使用 feishu_bitable_app_table_record 工具 action=batch_create,records 参数用 string="false"。
导入数据后,立即更新复盘总文档中的 mention-doc 链接,确保指向当前月份的正确表格和开发复盘文档。
为什么必须做: 复盘总文档从模板复制后,mention-doc 链接指向的是模板的占位对象(如旧月份的表格或模板文档)。不更新的话,点击链接会看到错误的数据。
工单数据表 mention-doc:token → 当前月份的 bitable token,obj_type → 8(bitable)开发复盘文档 mention-doc:token → 当前月份的 wiki 节点 token,obj_type → 16(wiki)99992402// PATCH /docx/v1/documents/{docId}/blocks/{blockId}
// Body: { update_text_elements: { elements: [originalElements] } }
// 只改 mention_doc.token/obj_type/url,保持 text_run 不变
发送 wiki 节点 token 后,飞书 API 会自动解析为实际文档的 obj_token,类型也会从 wiki(16) 变为具体类型(如 docx=22, bitable=8)。修改后 fetch-doc 验证即可。
在创建之前,先检查目标月度目录下是否已存在同名的文档。
使用 feishu_wiki_space_node list 列出月度目录下的所有节点。如果已有同名文档,直接进入 Step 7 更新即可,不要重复创建。
GEqawooPYiDXLWkRKZHcJnLcnFc 复制创建新文档智慧门店-2026-05月-工单复盘复制文档的两种方式:
feishu_wiki_space_node 的 copy 操作(如果模板是 wiki 节点),传入 space_id 和 target_parent_tokenfeishu_create_doc 从模板创建(传入 wiki 节点 token)feishu_drive_file 的 copy 操作模板中有大量 x 占位符(橙色高亮),需要按以下规则替换为实际数据。
不要破坏模板原有的换行和段落结构。 模板中每个变量的位置和间距都有意义。以下从实际模板 GEqawooPYiDXLWkRKZHcJnLcnFc 获取的精确结构:
# 一、会议信息
会议主题:智慧门店-<text color="orange">2026-</text><text color="orange">10</text>月工单复盘
会议时间:<text color="orange">2026.05.07(周四) 16:00 - 16:30</text>
# 二、会议议程
**复盘时间:**<text color="orange">**2026.05.07**</text>
**复盘范围:**<text color="orange">**2026.04.01 00:00:00-2026.04.30 23:59:59**</text>
**参与人员:**
- **开发:朱栋泉、**<text color="orange">**李文茂(28)、付雷(12)、王佳明(10)、赵杭琪(9)**</text>
- **产品:**
- **测试:**
- **技术支持:崔文思**
- **运营:蒋达周**
# 三、工单总计<text color="orange">XX</text>个(按工单数统计)
[Table: 10行×11列,3个月数据(2026-02/03/04),每33格一组=110格]
⚠️ 2026-04 组(最近一个月)使用整体橙色格式,与 02/03 组的逐字橙色不同
<text color="orange">2026-04</text>月具体工单请查看飞书表格:<mention-doc token="J4tawf06GijvEmkBTSCcdyNynsd" type="wiki">2026-xx月-智慧门店工单数据</mention-doc>
开发复盘文档:<mention-doc token="Secqwa7deiaRwTkUsG6cJ7xpnzj" type="wiki">开发复盘文档模板</mention-doc>
# 四、工单总结
## 工单数量分析
- <text color="orange">2026-04</text>月共产生工单<text color="orange">646</text>个,本次复盘筛选出<text color="orange">154</text>个工单。同比<text color="orange">2026-03</text>月<text color="orange">增加25.19%</text>
- 从**解决类别**看,工单主要集中在:<text color="orange">外部原因-无需技术排查(208)、设计如此-无需优化(160)、外部原因-需技术排查(81)</text>
- 从**解决模块**看:工单主要集中在:<text color="orange">收银系统-餐饮(197)、打印机(150)、扫码点单(117)</text>
## 工单时效分析
<text color="orange">90</text>个超时工单(其中产研介入<text color="orange">59</text>个),时效内解决占比<text color="orange">84.67</text>%,较3月<text color="orange">82%</text>有所提高。
主要超时原因:
## 问题总结
立即更改内容跟进:
同步客服&技术支持:
# 五、历史跟进事项
<text color="orange">**2026-03**</text>**月跟进事项**:
...
<text color="orange">**2026-02**</text>**月跟进事项**:
...
| 位置 | 说明 |
|---|---|
| Section 3 标题 | # 三、工单总计<text color="orange">XX</text>个(按工单数统计),无 callout 块 |
| Section 3 末尾 | Mention-doc(工单数据表 bitable + 开发复盘文档 wiki) |
| Section 4: 工单数量分析 | 含同比上月数据行 <text color="orange">同比...增加xx%</text> |
| Section 4: 工单时效分析 | 主要超时原因:后为填空区域 |
| Section 5 | 月份为「目标月-1」和「目标月-2」,如05月复盘则显示04和03月 |
| 表格最后一组(最近月) | 使用整体橙色 <text color="orange">已解决x(产研介x)</text> 格式,与前面两组不同 |
**2026-04**月跟进事项 和 **2026-03**月跟进事项<text color="orange">**2026-03**</text>**月跟进事项**:创建文档时禁止直接使用模板的名称。每次创建必须按以下规范重命名:
| 文档类型 | 模板节点 | 创建后命名规范 | 示例(2026-05月) |
|---|---|---|---|
| 工单数据跟踪表(多维表格) | J4tawf06GijvEmkBTSCcdyNynsd | 智慧门店-2026-xx月-工单数据 | 智慧门店-2026-05月-工单数据 |
| 工单复盘文档(知识库文档) | GEqawooPYiDXLWkRKZHcJnLcnFc | 智慧门店-2026-xx月-工单复盘 | 智慧门店-2026-05月-工单复盘 |
| 开发复盘文档(知识库文档) | Secqwa7deiaRwTkUsG6cJ7xpnzj | 智慧门店-2026-xx月-开发复盘文档 | 智慧门店-2026-05月-开发复盘文档 |
重命名方式(API 不支持,必须手工):
⚠️ Section 3 的表格是
<lark-table>格式(带 rowspan/colspan 的复杂表格),不能用 markdown 表格替代。 使用overwrite模式重写文档时,表格结构会丢失。因此 Section 3 必须保留原始<lark-table>代码。
以下3条规则已经被用户多次重复强调。每次出错都会导致文档数据混乱。阅读此文件时请逐条仔细核对!
复盘总文档中 # 二、会议议程 → 参与人员: → 开发: 后面的橙色部分,必须填入开发复盘文档 Section 2 中前5名责任人 的姓名和工单数量。
李文茂(28)、付雷(12)、王佳明(10)、赵杭琪(9))李文茂(5)、蒋达周(3)、赵杭琪(2)、金海轩(2)、王金楠(2)模板中所有带 x 的数字占位符(包括 Section 3 标题 XX、表格中所有 x)的填充值,必须全部从 复盘跟踪表 Bitable 获取:
复盘跟踪表: https://sqb.feishu.cn/wiki/YMQjwQeWCipABCkl8y7ckZlunQe?table=tblpygt9n8dqZcsw
三、工单总计XX个 → X 填复盘跟踪表中当月记录的 工单总数 字段# 四、工单总结 下所有带数字的橙色文本,数据来源均为复盘跟踪表(不是工单数据表):
X月共产生工单X个 → 跟踪表 工单总数本次复盘筛选出X个工单 → 跟踪表 复盘工单同比上月增加/减少XX% → 对比跟踪表中上月的 工单总数解决类别TOP3 → 跟踪表中各解决类别字段(外部原因-无需技术排查、无需优化、外部原因-需技术排查 等)解决模块TOP3 → 跟踪表中各模块字段(收银系统-餐饮、扫码点单、打印机 等)超时工单数、超时工单数-产研介入1 - 超时工单数/工单总数 × 100%| 数据 | 来源 |
|---|---|
| 会议信息、议程 | 用户提供或从上月文档沿用 |
| 参与人员(开发TOP5) | 开发复盘文档 Section 2 的前5名责任人 |
| 第三节标题 XX | 复盘跟踪表 工单总数 |
| 第三节表格(所有数字) | 复盘跟踪表(全部字段) |
| 工单数量分析(所有数字) | 复盘跟踪表 |
| 工单时效分析(所有数字) | 复盘跟踪表 |
| 历史跟进事项 | 从上月复盘文档复制 |
使用 scripts/fill_review_cells.cjs 填充嵌套表格单元格。详细操作见 references/data_mapping.md。
配置脚本顶部:
const DOC_ID = '...'; // 新创建的复盘总文档 ID
const MONTH = '2026-04'; // 目标月份
const CELL_DATA = {
total: '644',
noOpt: '160(产研介入94)',
prodOpt: '42(20)',
techWork: '0',
techCheck:'79(48)',
noTech: '208(28)',
internal: '138(产研介入87)',
solved: '已解决54(27)',
deferred: '延期处理66(46)',
noRepro: '无法重现:18(14)',
timeout: '88(58)',
};
执行:node scripts/fill_review_cells.cjs
模板中 # 三、工单总计XX个(按工单数统计) 下方自带一个 <lark-table> 格式的嵌套表格。此表格必须保留,不能删除或替换为文本。
表格内应填充最近3次工单复盘的数据(包含目标月份+前2个月)。例如做 2026-05 月复盘时,表格应包含:
| 行 | 内容 |
|---|---|
| 第一行(表头) | 复盘范围、时间、工单总数等列名 |
| 第一数据行(最旧) | 2026-03 数据(跟踪表记录) |
| 第二数据行(中间) | 2026-04 数据(跟踪表记录) |
| 第三数据行(最新,在最下方) | 2026-05 数据(跟踪表记录) |
⚠️ 顺序规则:
2026-02 行应改为 2026-03 数据,2026-03 行改为 2026-04 数据,2026-04 行改为 2026-05 数据list 全部记录2026-05 / 2026-04 / 2026-03)使用 scripts/fill_review_cells.cjs 逐个填充表格单元格。详细操作见 references/data_mapping.md。
配置脚本顶部:
const DOC_ID = '...'; // 新创建的复盘总文档 ID
const MONTH = '2026-05'; // 目标月份(用于查找表格中的对应单元格)
const CELL_DATA = {
// 以下数据来自复盘跟踪表,key 见 data_mapping.md
total: '21',
noOpt: '11(产研介入7)',
prodOpt: '0',
techWork: '0',
techCheck: '0',
noTech: '0',
internal: '10(产研介入3)',
solved: '已解决4(1)',
deferred: '延期处理6(2)',
noRepro: '无法重现:0',
timeout: '11(3)',
};
如果目标月份在跟踪表中尚无数据(月未结束),表格中该行留空或填 "-",用附注说明。
复盘总文档第三节表格的数据不直接来自工单数据表,而是来自复盘跟踪表。跟踪表中各字段是 Lookup 公式,会自动根据 Step 0 创建的记录中的时间范围和已导入的工单数据计算汇总值。
因此复盘的正常顺序是:
⚠️ 所有数字均来自复盘跟踪表,不是工单数据表
模板中 Section 4 的每一条文本都有橙色标记的数字占位符。所有数值必须从复盘跟踪表获取,不能从刚导入的工单数据表(24条)统计。
工单数量分析需要以下统计数据(全部来自复盘跟踪表 filter 复盘范围=当月):
| 变量 | 跟踪表字段 | 取值方式 |
|---|---|---|
| 当月工单总数 | 工单总数 | 直接取值 |
| 复盘工单数 | 复盘工单 | 直接取值 |
| 同比上月增减% | 对比上月 工单总数 | (本月-上月)/上月 × 100% |
| 解决类别TOP3 | 外部原因-无需技术排查、无需优化、待产品优化、外部原因-需技术排查 等 | 按字段值排序取TOP3 |
| 解决模块TOP3 | 收银系统-餐饮、扫码点单、打印机、收银系统-零售 等 | 按字段值排序取TOP3 |
工单时效分析需要(全部来自跟踪表):
| 变量 | 跟踪表字段 | 取值方式 |
|---|---|---|
| 超时工单数 | 超时工单数 | 直接取值 |
| 超时产研介入 | 超时工单数-产研介入 | 直接取值 |
| 时效内解决占比% | 工单总数 + 超时工单数 | (1 - 超时工单数/工单总数 × 100%),保留两位小数 |
获取方式:feishu_bitable_app_table_record list 查询 复盘范围 = 当月,然后从返回的 fields 中提取所需字段。
注意:Section 4 的文本包含大量橙色标记(<text color="orange">),无法通过简单的 replace_range 或 selection_with_ellipsis 更新(因为标记打断了文本连续性)。
25个超时工单)可以通过 block PATCH 更新(type 2 块)feishu_update_doc replace_all(但会破坏 callout)或手动编辑问题总结和历史跟进事项从上月文档复制,或由用户填写。
同样先检查目标月度目录下是否已有同名文档。 使用 feishu_wiki_space_node list 确认后再操作。
Secqwa7deiaRwTkUsG6cJ7xpnzj 复制创建智慧门店-2026-05月-开发复盘文档不要破坏模板原有的换行和段落结构。 完整模板结构如下:
# 一、开发复盘文档
- 工单复盘文档:<mention-doc token="..." type="wiki">智慧门店工单复盘模版</mention-doc> ← 需修改 ①
- 复盘表格内容:<mention-doc token="..." type="wiki">2026-xx月-智慧门店工单数据</mention-doc> ← 需修改 ②
# 二、主要人员总结材料
<text color="orange">开发A(</text>工单总数<text color="orange">x</text>个)(超时工单<text color="orange">x</text>个) ← 需修改 ③
[callout] ← 保持模板原样!不要改动!
目标:指向本次创建的复盘总文档 wiki 节点
{ mention_doc: { token: "{复盘总文档的wiki节点token}", obj_type: 16, // wiki
title: "智慧门店-2026-05月-工单复盘",
url: "https://sqb.feishu.cn/wiki/{wiki节点token}" } }
目标:指向当前月份的工单数据 bitable
{ mention_doc: { token: "{月份bitable的app_token}", obj_type: 8, // bitable
title: "智慧门店-2026-5月工单数据",
url: "https://sqb.feishu.cn/base/{app_token}?table={table_id}&view={view_id}" } }
这是最容易出错的地方!严格按照以下步骤:
| 位置 | 原来 | 改为 | 示例(李文茂) |
|---|---|---|---|
| 第一个 text_run | 开发A( | 责任人姓名+( | 李文茂( |
中间 text_run(内容为x) | x | 该人工单总数 | 5 |
末尾 text_run(内容为x) | x | 该人超时工单数 | 4 |
元素结构参考(以开发A为例,其余开发B~E结构相同但元素数可能不同):
// 开发A 的 elements(8个元素):
[ {text_run: "开发A("}, // 改为人名
{text_run: "工单总"}, // 不动
{text_run: "数"}, // 不动
{text_run: "x"}, // 改为工单总数
{text_run: "个"}, // 不动
{text_run: ")(超时工单"}, // 不动
{text_run: "x"}, // 改为超时工单数
{text_run: "个)"} // 不动
]
// 开发B~E 的 elements(6个元素):
[ {text_run: "开发X("}, // 改为人名
{text_run: "工单总数"}, // 不动
{text_run: "x"}, // 改为工单总数
{text_run: "个)(超时工单"}, // 不动
{text_run: "x"}, // 改为超时工单数
{text_run: "个)"} // 不动
]
// 替换后效果:
[ {text_run: "李文茂("},
{text_run: "工单总"},
{text_run: "数"},
{text_run: "5"}, // 原来是 x
{text_run: "个"},
{text_run: ")(超时工单"},
{text_run: "4"}, // 原来是 x
{text_run: "个)"}
]
✅ 最终橙色高亮区的效果(模板 → 正确):
<text color="orange">开发A(</text>工单总数<text color="orange">x</text>个)(超时工单<text color="orange">x</text>个)
↓ 替换为
<text color="orange">李文茂(</text>工单总数<text color="orange">5</text>个)(超时工单<text color="orange">4</text>个)
| ❌ 错误 | 后果 |
|---|---|
只替换数字 x,不把开发A改成责任人姓名 | 橙色区显示 "开发A(工单总数5个)" 而非 "李文茂(工单总数5个)" |
| 把分析总结填入 callout 内容 | callout 应该保持模板占位符状态,留给用户手动填写 |
写入多行文本到 callout 块(如 工单原因分析:\n1. xxx) | 后续无法一次性清空,需要逐元素删除残留行 |
用 feishu_update_doc replace_all 更新文档内容 | 会破坏 <callout> 块结构 |
崔文思 和 蒋达周超时时间 不为空的工单数数据获取方式:
list 全部记录责任人 字段分组计数填充方式:
feishu_update_doc(会破坏 <callout> 块)update_text_elements 替换橙色占位符的 x,保留 <text color="orange"> 样式每个责任人段落的 callout 区域包含需要用户填写的分析总结(无法自动生成):
分析总结:
超时工单分析:
工单原因分析:
需更改需跟进事项:
需同步产品:
需同步开发:
需同步测试:
需同步客服:
需同步技术支持:
其他:
在填充数据后告知用户这些区域需要手动填写。
scripts/fetch_furcas.py从 Furcas API 拉取数据。参数:-s 开始日期、-n 结束日期、-o 输出路径。
scripts/import_to_bitable.mjsCSV → 多维表格导入一体化脚本。环境变量:APP_TOKEN, TABLE_ID, CSV_PATH, BATCH_SIZE, TABLE_NAME。
scripts/generate_batches.pyCSV → JSON 批处理文件。参数:--csv, --output-dir, --batch-size。
scripts/fill_review_cells.cjs填充复盘总文档第三节嵌套表格单元格。直接调飞书 Docx PATCH API,用用户 OAuth UAT。顶部配置区设置月份和数据。
kanban_field_idfill_review_cells.cjs 逐个填充x 变量:开发复盘文档的 <text color="orange">x</text> 占位符必须用 UAT PATCH API 逐元素更新(保留橙色样式),禁止用 feishu_update_doc replace_all(会破坏 <callout> 块)。复盘总文档的 x 同样用 PATCH API 更新。永远不要合并或删除模板中的换行。模板的每个段落、列表都是精心设计的结构。特别是:
会议主题 / 会议时间 各占一行**复盘时间:** / **复盘范围:** / **参与人员:** 各自独立成段- 无序列表格式> 前缀必须保留<lark-table>feishu_update_doc 禁止用于含表格的文档:复盘总文档的 Section 3 包含 <lark-table>,任何模式(overwrite/replace_all/append/insert_after/replace_range/delete_range)都会摧毁表格属性(rowspan、colspan 丢失,cols 数变化)。
feishu_update_doc 会先序列化文档为 markdown 再反序列化,过程中 lark-table 的 HTML 属性丢失PATCH /blocks/{cell_child_text_id} 用 update_text_elementsPATCH /blocks/{block_id} 用 update_text_elementsfeishu_update_doc 禁止用于含 callout 块的文档:开发复盘文档不含 lark-table,但含有 <callout> 块(type=19),feishu_update_doc 的 markdown 序列化/反序列化会:
<callout> → > blockquote(结构被破坏,callout 框消失)<text color="orange"> → 普通文本(颜色标记丢失)# 三、总结)替换开发 A/B/C/D/E 标记(模板现含5个槽位):
<text color="orange">开发A(</text>工单总数<text color="orange">x</text>个)(超时工单<text color="orange">x</text>个)(B/C/D/E同理)**张姝(工单总数12个)(超时工单5个)**模板已有 5 个开发者槽位(开发A-E):最新模板 Secqwa7deiaRwTkUsG6cJ7xpnzj 已直接包含 5 组(开发A/B/C/D/E),每组含 <text color="orange">开发X(工单总数x个)(超时工单x个)</text> + <callout> 块。
/children 追加开发D和E复盘总文档的 Section 3 包含 <lark-table>(带 rowspan/colspan),feishu_update_doc 的任何模式(replace_all、insert_after、delete_range、replace_range、overwrite)都会摧毁表格属性。
原因:这些模式先将文档序列化为 markdown 再反序列化,反序列化时 lark-table 的 HTML 属性丢失。
后果示例:
cols="11" → cols="10"(colspan=2 的合列丢失)rowspan="3" → 消失(每月 3 行不再合并)PATCH /open-apis/docx/v1/documents/{document_id}/blocks/{block_id}
Authorization: Bearer {user_access_token}
Body: {
"update_text_elements": {
"elements": [{
"text_run": {
"content": "新文本内容",
"text_element_style": {}
}
}]
}
}
⚠️ update_text_elements 会丢失格式(bold/orange):新的 text_run 使用空的 text_element_style: {},所以加粗、橙色标记等格式会丢失。如需保留格式,需在 text_element_style 中显式设置 bold: {} 或 inline_code: {color: 25, background_color: 2}(橙色)。
模板拷贝 → 填表(fill_review_cells.cjs)→ 更新文本(PATCH API)
↑ ↑
必须先做 后做
填表(表格单元格级别的 block 操作)必须在任何文本替换之前完成。如果先做文本替换再填表,feishu_update_doc 已经破坏了表格结构。
通过 PATCH API 读取/更新文本时,不同 block type 的文本存在不同属性中,不是都在 block.text 下:
| Block Type | 描述 | 文本所在属性 |
|---|---|---|
| 2 | 文本段落 | block.text.elements |
| 3 | 标题1(#) | block.heading1.elements |
| 4 | 标题2(##) | block.heading2.elements |
| 5 | 标题3(###) | block.heading3.elements |
| 12 | 无序列表项(-) | block.bullet.elements |
| 14 | 有序列表项(1.) | block.ordered.elements |
PATCH API 写入统一使用 update_text_elements,不分类型。
飞书文档中,标题块(block_type=3/4/5)可以有 子 children(如 Section 4 的 ## 工单数量分析 就包含 bullet 子节点)。Section 4 的文本不是根级节点,而是 heading 的 children:
// Section 4 的数据需要遍历 heading 的 children 获取:
const rootBlocks = getChildren(document_block_id);
for (const b of rootBlocks) {
// b.block_type === 4 (heading2)
// b.txt === '工单数量分析'
const sectionKids = getChildren(b.block_id); // ← ✅ 能拿到子节点
// sectionKids[0].block_type === 12 (bullet)
// sectionKids[0].txt === '2026-05月共产生工单81个...'
}
但是:表格(type=31)和 callout(type=19)不是任何块的 children,它们始终是文档根层的同级节点。即使它们在视觉上位于某个 heading 下方,API 读取时它们和 heading 是平级关系。
PATCH API 需要 User Access Token(UAT),用 OAuth 方式获取。UAT 加密存储,解密方式:
const d = path.join(process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local', 'share'), 'openclaw-feishu-uat');
const enc = fs.readFileSync(path.join(d, '{appId}_{userOpenId}.enc'));
const masterKey = fs.readFileSync(path.join(d, 'master.key'));
// AES-256-GCM: IV 12 bytes + tag 16 bytes + ciphertext
const iv = enc.subarray(0, 12), tag = enc.subarray(12, 28), cipher = enc.subarray(28);
const decipher = crypto.createDecipheriv('aes-256-gcm', masterKey, iv);
decipher.setAuthTag(tag);
const uat = JSON.parse(Buffer.concat([decipher.update(cipher), decipher.final()]).toString('utf8')).accessToken;
位置:scripts/fill_review_cells.cjs
配置顶部变量:
const DOC_ID = '新文档 obj_token';
const APP_ID = 'cli_a97b6a0ffc399cc0';
const SENDER_OPEN_ID = 'ou_229ec432e6d99a781896afb23e386ee1';
const CELL_DATA = {
total: '21',
noOpt: '11(产研介入7)',
prodOpt: '0',
techWork: '0',
techCheck: '0',
noTech: '0',
internal: '10(产研介入3)',
solved: '已解决4(1)',
deferred: '延期处理6(2)',
noRepro: '无法重现:0',
timeout: '11(3)',
};
脚本会自动填充最近 3 个月(本月 + 前 2 个月)的数据到表格中的对应位置。填写规则见 references/data_mapping.md。
// readText 必须检查所有 block-type 属性
function readText(block) {
const src = block?.text || block?.heading1 || block?.heading2 ||
block?.heading3 || block?.bullet || block?.ordered || block?.todo;
return src?.elements?.map(e => e.text_run?.content || '').join('') || '';
}
// 写入统一用 update_text_elements
async function patchBlock(uat, blockId, newText) {
const body = {
update_text_elements: {
elements: [{ text_run: { content: newText, text_element_style: {} } }]
}
};
const url = `${DOCX_BASE}/${DOC_ID}/blocks/${blockId}`;
return apiRetry('PATCH', url, { Authorization: `Bearer ${uat}` }, JSON.stringify(body));
}
UAT 解密方式:
/state/share/openclaw-feishu-uat/{appId}_{userOpenId}.enc/state/share/openclaw-feishu-uat/master.key(32 字节原始密钥,不需要 SHA256 哈希)accessToken(驼峰,不是 access_token)fill_review_cells.cjs 已正确实现(fs.readFileSync 直接读 Buffer)mention-doc 的 token 解析:
<mention-doc token="wiki节点token" type="wiki"> 时,飞书 API 会自动解析为实际文档的 obj_tokenimport_to_bitable.mjs 看板创建 bug:
createKanbanView("按责任人分组") 创建的看板视图没有返回正确的 view_id(返回 undefined)kanban_field_id 时会因 view_id=undefined 而失败/views/{viewId} 设置 kanban_field_id责任人 的 field_id(可通过 list_fields 获取)FIELD_MAP 硬编码:CSV 列名 "工单状态/修复情况" → bitable 字段名 "工单状态|修复情况"(竖线)。如果 bitable 字段名用了斜杠,需要修改 FIELD_MAP 或重命名字段。
以下为 2026-05 月工单复盘全流程中遇到并解决的问题汇总,下次工单复盘前请先通读,避免重复踩坑。
Furcas API 登录态不可持久化。每次执行 fetch_furcas.py 前:
acw_tc 和 furcas 字段)HEADERS["Cookie"] 变量acw_tc 每次都会变拉取数据后 fetch_furcas.py 会按解决类别分类写入 CSV,ID 映射如下:
| ID | 类别 |
|---|---|
| 1 | 设计如此-无需优化 |
| 2 | 外部原因-无需优化 |
| 3 | 内部原因-已解决 |
| 4 | 内部原因-延期处理 |
| 5 | 内部原因-无法重现 |
创建复盘跟踪表记录时:
app_token = YMQjwQeWCipABCkl8y7ckZlunQetable_id = tblpygt9n8dqZcsw工单总数 = 该月 Furcas 所有工单的总数(从查询结果 count 获取)复盘工单 = 筛选后需要复盘的工单数(排除设计如此、外部原因-无需优化后)| 字段名 | field_id |
|---|---|
| 工单总数 | fldg7k4Zb |
| 复盘工单 | fldBxBSm3x |
| 无需优化 | fld2Fi7oVD |
| 待产品优化 | fldQ45Kqj8 |
| 外部原因-技术派工 | fldW8sKSSx |
| 外部原因-需技术排查 | fldRerqDIp |
| 外部原因-无需技术排查 | fldVefvMG2 |
| 内部原因 | fldIor3W9R |
| 已解决 | flde2eaOu |
| 延期处理 | fldZqWk3G |
| 无法复现 | fldYplayCE |
| 超时工单数 | fldTQkMXsW |
工单总数 字段值来自 Furcas API 返回的总数,不是从导入 bitable 的记录数获取,不是从 CSV 行数获取。导入脚本只导入需要复盘的工单(筛选后的子集),不能当作总数。
feishu_update_doc 的 overwrite、replace_all、append 模式都会损坏 lark-table(返回 3000/4000515 等错误)。
feishu_update_doc 的任何模式每个复盘表格有 110 个单元格:
每个月份组 33 格,单元格偏移:
| 偏移(组内) | 字段 | 行 |
|---|---|---|
| 0 | 月份标签 | row1 |
| 1 | 工单总数 | row1 |
| 2-3 | 无需优化(colspan=2) | row1 |
| 4 | 待产品优化 | row1 |
| 5 | 外部原因-技术派工 | row1 |
| 6 | 外部原因-需技术排查 | row1 |
| 7 | 外部原因-无需技术排查 | row1 |
| 8 | 内部原因 | row1 |
| 9 | 已解决 | row1 |
| 10 | 超时(rowspan=3) | row1 |
| 20 | 延期处理 | row2 |
| 31 | 无法复现 | row3 |
数据数组顺序必须匹配 offset 顺序:
// 正确顺序:total(1), noOpt(2), prodOpt(4), techWork(5), techCheck(6), noTech(7), internal(8), solved(9), timeout(10), deferred(20), noRepro(31)
const data = ['516', '147(产研介入55)', '48(产研介入17)', '3(产研介入3)', '72(产研介入53)', '147(产研介入25)', '96(产研介入56)', '已解决38(产研介入16)', '49(产研介入28)', '延期处理47(产研介入31)', '无法复现:11(产研介入9)'];
不能按顺序逐个改多个月份标签!
更新 heading1 的 工单总计XX个(按工单数统计) 后,前缀 三、 会丢失。
update_text_elements 完全替换了所有元素,前缀在另一个 element 中# 三、工单总计x个(按工单数统计)update_heading1 / update_heading2 / update_heading3 → 返回 1770001 invalid paramupdate_text_elements → 对 heading1/2/3/text/bullet/ordered 都有效update_text_elements: { elements: [...] }Section 4(工单数量分析等)的 bullet 文本是 heading 的 children,不是根级块。 需要:
gC(docId, heading.block_id))当 template 有 <text color="orange">x</text> 样式时,不能简单替换整个文本字符串——会丢失颜色样式。
elements[],只改 content 不变 style10→05,保留其余所有 element 的 style 属性DELETE /blocks/{block_id} 对 callout 无效elements[] 数组中的一个 elementtext_run 和 mention_doc 两个属性PATCH 时需传入完整 element,必须带的字段:
{
"text_run": { "content": "显示文字", "text_element_style": {} },
"mention_doc": {
"obj_type": 16, // 整數!不能传字符串 "wiki"
"text_element_style": {},
"token": "UJdQwNdkPidcvKkXZzbcL1tDnaf", // wiki 节点 token
"title": "显示标题",
"url": "https://sqb.feishu.cn/wiki/UJdQwNdk..."
}
}
obj_type 是整数(16=wiki节点),传字符串会报 99992402 field validation failed发送 wiki 节点 token 后,飞书 API 会自动解析为实际文档的 obj_token:
UJdQwNdk... (wiki) → UtJCdfO5Iotw... (docx obj_token)mention 在文档中显示的文字由文档标题决定,不是由你写入的 text_run.content 或 title 字段决定。
模板 Secqwa7deiaRwTkUsG6cJ7xpnzj 已在 2026-05-07 更新,直接包含开发A-E 五个槽位。旧版只有3个时:
POST /blocks/{root_doc_id}/children 添加第 4、5 个POST /blocks/{root_doc_id}/children 添加第 4、5 个要插入兄弟块不是 POST 到目标块,而是 POST 到父块(根文档 block_id = doc_id):
// ✅ 正确:POST 到父块(文档本身),指定 index
POST /documents/{docId}/blocks/{docId}/children
{ index: 金海轩的索引+1, children: [{ block_type: 2, text: { elements: [...] } }] }
// ❌ 错误:POST 到目标块自己(会把新内容作为金海轩的子块)
POST /documents/{docId}/blocks/{金海轩的block_id}/children
模板中的占位符是 x(如 工单总数x个),不是 xxx。替换时注意匹配模式。
开发复盘 TOP5 必须排除崔文思(技术支持)和蒋达周(运营)。统计开发者时:
wiki_space_node action=copy 返回的 obj_token 可能字符错位(如 UtJCdfO5Iotw 与 UtJCdfO5Iotw 中的 5 和 O 互换)。复制后:
wiki_space_node action=get 验证新节点的实际 obj_tokenfeishu_fetch_doc 直接读节点 token 验证文档是否可访问复制创建的文档不会立即就绪。如果立即调用 PATCH 操作,可能返回 4000515 resource not found。
✅ 建议做法:创建后等待 2-3 秒再操作。
PATCH /docx/v1/documents/{docId} → 1770001 invalid param(不支持改标题)PATCH /wiki/v2/spaces/{spaceId}/nodes/{nodeToken} → 404(没有 rename 端点)const LINUX_UAT_DIR = path.join(
process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local', 'share'),
'openclaw-feishu-uat'
);
const encFile = path.join(LINUX_UAT_DIR, `${APP_ID}_${OPEN_ID}.enc`);
const masterKey = fs.readFileSync(path.join(LINUX_UAT_DIR, 'master.key'));
const uatToken = JSON.parse(
decrypt(fs.readFileSync(encFile), masterKey)
).accessToken; // 驼峰!不是 access_token
/root/.local/share/,用 XDG_DATA_HOME 环境变量{appId}_{userOpenId}.enc(冒号替换为下划线)accessToken 是驼峰,不是下划线格式| 复盘类型 | 时间范围 |
|---|---|
| 整月复盘 | 当月1日 00:00:00 - 当月最后一日 23:59:59 |
| 上半月复盘 | — 当月15日 23:59:59 |
| 下半月复盘 | 当月16日 00:00:00 — |
文档中 color="orange" 的复盘范围文本要保持橙底样式,仅改日期文字。
| 文档类型 | 模板 | 命名规范(5月示例) |
|---|---|---|
| 工单数据跟踪表(Bitable) | J4tawf06GijvEmkBTSCcdyNynsd | 智慧门店-2026-05月-工单数据 |
| 工单复盘总文档 | GEqawooPYiDXLWkRKZHcJnLcnFc (wiki节点) | 智慧门店-2026-05月-工单复盘 |
| 开发复盘文档 | Secqwa7deiaRwTkUsG6cJ7xpnzj (wiki节点) | 智慧门店-2026-05月-开发复盘文档 |
update_text_elementselements.map(e=>e.text_run?.content).join('')(完整文本),修改用单个element的contentupdate_text_elements对所有block_type通用.bullet.elements中feishu_update_doc 禁止用于含 callout(type=19) 的文档(序列化会破坏callout结构)update_text_elements更新,不要替换整个文档title参数设置| 项目 | 值 |
|---|---|
| 工单数据(含Furcas) app_token | YMQjwQeWCipABCkl8y7ckZlunQe |
| 工单汇总表 table_id | tblpygt9n8dqZcsw |
| Furcas工单表 table_id | tbln4MvksjBRkGGn |
| 工单数据跟踪表模板(Bitable) | J4tawf06GijvEmkBTSCcdyNynsd |
| 2026目录节点(space 7510046829784662017 下) | MDK5w5CcGi8RK6kRFMKcuBKLnNg |
| 复盘总文档模板 | wiki 节点 GEqawooPYiDXLWkRKZHcJnLcnFc |
| 开发复盘文档模板 | wiki 节点 Secqwa7deiaRwTkUsG6cJ7xpnzj |
| 用户 open_id | ou_229ec432e6d99a781896afb23e386ee1 |
| App ID | cli_a97b6a0ffc399cc0 |
| Space ID | 7510046829784662017 |
| 05月目录节点 | KAXEwRivKinTfTkshDucvSiGn4g |
以下为 2026-05-07 修复 4 月工单数据时发现并解决的问题。
import_to_bitable.mjs 清空步骤有静默失败 bug(致命!)bug 描述:清空旧数据的 batch_delete API 参数名写错,用的 records 但正确参数是 record_ids。且 try-catch 吞掉了错误,导致清空失败时不停机:
// ❌ 错误(旧代码)
await api("POST", `.../records/batch_delete`, { records: ids });
// 清空失败,错误被 catch 吃掉
// 旧数据继续留在表中,新数据追加写入
// → 每条工单出现 2 遍(304条 = 152 × 2)
修复:
record_ids ✅智慧门店-2026-05月-工单数据 × 4)原因:首次创建 bitable 时没有检查知识库目录下是否已有同名表格。
修复方式:后续复盘时,在创建新 bitable 前必须:
feishu_wiki_space_node list 列出目录下所有节点,检查名称是否包含目标月份原因:从模板复制复盘总文档后,文档内的 mention-doc 指向的是模板的占位对象(4月表或模板),不是当前月份的表。
修复方式(已添加 Step 5b 到流程中):
Raw API 调用 /bitable/v1/apps/{token}/tables/{table_id}/records 返回 99991679 Unauthorized:
bitable:app:readonly 或 base:record:readfeishu_bitable_app_table_record 工具去重脚本一次删 152 条记录(逐条 DELETE)因超时被 SIGTERM:
await new Promise(r => setTimeout(r, 50)) 限速现象:使用 feishu_update_doc replace_all 更新开发复盘文档后:
<callout> 块 → > markdown blockquote(callout 框消失)<text color="orange"> → 普通文本(橙色丢失)# 三、总结 等异常段落根因:feishu_update_doc 先将文档序列化为 markdown,再反序列化回飞书文档格式。反序列化时:
<callout> 被当作 > blockquote 处理# 三、总结 等标题,会被当作新段落加入修复:
模板 Secqwa7deiaRwTkUsG6cJ7xpnzj 在 2026-05-07 更新,"主要人员总结材料"下从3个槽位(开发A/B/C)扩展到5个(开发A/B/C/D/E)。
影响:
/children 追加开发D/E复制模板后,开发复盘文档的 Section 1 包含两个指向模板/占位对象的 mention-doc:
# 一、开发复盘文档
- 工单复盘文档:<mention-doc token="GEqawooPYiDXLWkRKZHcJnLcnFc" type="wiki">智慧门店工单复盘模版</mention-doc>
- 复盘表格内容:<mention-doc token="J4tawf06GijvEmkBTSCcdyNynsd" type="wiki">2026-xx月-智慧门店工单数据</mention-doc>
必须替换为:
工单复盘文档 mention-doc: token → 本次创建的 复盘总文档 wiki 节点 token复盘表格内容 mention-doc: token → 当前月份工单数据 bitable token(含 table_id 和 view_id)变更方式:PATCH 对应 block 的 update_text_elements,修改 mention_doc 的 token、obj_type、url。
错误做法(之前踩过的坑):
feishu_bitable_app copy 从模板复制 → 创建独立 bitable(不在 wiki 目录中)feishu_wiki_space_node create 传 obj_token → 又新建一个空白 bitable(表名 "Table",字段全英文)
→ 导致 wiki 链接指向空表,数据在独立表里正确做法(仅需一步):
feishu_wiki_space_node copy 直接将模板节点复制到目标月份目录
node_token: "J4tawf06GijvEmkBTSCcdyNynsd"(工单数据表模板)target_parent_token: "{目标月份目录节点token}"space_id: "7510046829784662017"
→ 直接在 wiki 内创建副本,bitable 结构、字段、视图全部正确复制后的处理:
batch_delete 空记录的 record_ids)导入脚本注意事项:
--name "数据表" 参数view_id 可能返回 undefined(脚本返回值解析 bug)
→ 视图实际已创建,可通过 feishu_bitable_app_table_view list 获取 view_id
→ 手工设置 property: { kanban_field_id: "责任人字段ID" }开发复盘文档人名占位符规则(2026-05-07):
开发A( 开发B( 等是人名占位符,不是固定标签李文茂(),不是仅替换数字开发复盘文档 callout 内容规则(2026-05-07):