Back to skills
extension
Category: Data & AnalyticsNo API key required

物料信息查询

针对汽车"散件数据库"(4-散件数据库汇总清单-2025)做实物图 + 12 字段抓取: 给一份含"型号"列的 xlsx 物料清单 + 总库 bitable 链接, 自动按 3 位前缀路由到 1HS / 1CO / 1LK / 1TI / 1CL / 1GR 六个子库, 下载每个型号的"实物图"(attachment)插入 xlsx 的图示列, 同时抓 12 字段——v1 6 字段(类型/公母/颜色/孔数/防水/是否有相似件) + v2 6 字段(TPA/CPA/后盖/推杆/密封圈/2D图纸), 最后把"图已填 + 字段已填"xlsx 上传飞书云盘并附行级报告。 触发关键词:散件数据库、4-散件数据库汇总清单、1HS 1CO 1LK 1TI 1CL 1GR 实物图、 护套 端帽 扎带 连接器定位件 数据库查图、附件字段 + 6 字段抓取、零件实物画册插图、 物料编码 查图 字段抓取、汽车物料数据库 实物图 字段、字段匹配方法论、字段名映射、 子库字段覆盖矩阵、contains + 精确匹配 字段查询、相似件 attachment 与 select 差异、 TPA CPA 后盖 推杆 密封圈 2D图纸、护套配套组件、12 字段扩展抓取、14 列表头相似件最后、 是否含有配套组件、形态 A B C 矩阵、PDF 转 PNG 实物图、insert_cols 表头扩展、 图片随单元格缩放、anchor 默认值、TwoCellAnchor 默认锚定、图片默认属性。 本 Skill 范围严格限定在"图+字段"两步双抓取;不做 BOM/订单/价格/工艺。

personAuthor: user_d3db0987hubcommunity

散件数据库实物图与字段抓取

目标

把"按型号在飞书散件数据库里查实物图 + 12 字段、然后写回 xlsx"这件事做成可复用的流水线。 用户给一个物料清单 xlsx(型号列)+ 总库 bitable 链接,Skill 帮你跑完整套并把成品 xlsx 上传飞书云盘。v1 6 字段是基础(2026-08-18 11:29 落地),v2 6 字段(TPA/CPA/后盖/推杆/密封圈/2D图纸)是扩展(2026-08-18 12:11 落地),两个版本共享同一份匹配流程,区别只在抓取字段集合。

字段匹配方法论(核心章节)

抓 6 字段的"逻辑"是有方法论的——不是把 6 个字段名硬编码进脚本就完事。本节是 Skill 内"方法论"的总览,详细规则在两份 reference 里:

方法论 4 个核心要点:

  1. 6 字段在 6 子库间的存储差异巨大——同一字段语义在 1HS / 1CO / 1TI / 1CL / 1GR 的字段名、field_id、类型都不同。SSOT 是 references/subdb_field_map.md,精简版摘要见 field_matching_logic.md §1
  2. 同义 vs 不等价的 4 个常见误判6/25 红线):
    • 1TI "实物图" ≠ 1TI "实物照片"——同库两个不同字段,本 Skill 用"实物图" fldS0arWBJ
    • 1HS "实物图" ≠ 1TI "实物图"——字段名同但 field_id 不同(fldEzskWkn vs fldS0arWBJ
    • 1CL "相似件" ≠ 1HS "相似件"——1CL 是 attachment 字段("相似件实物照片对比差异点" fld3nWcF3Y),存的是对比照片而非编码
    • 1GR "相似件" ≠ 1HS "相似件"——虽然类型都是 select 但 field_id 不同(fldz2GHFcv vs fldZj3VNoE
  3. 匹配用 contains + 精确匹配双步--filter-jsoncontains 探(filter 是粗筛),再用 record 列表"物料编码"列做精确匹配(精确才是真相)。详见 field_matching_logic.md §4
  4. 4 种字段无值标签("-" / "子库无此字段" / "子库未收录" / "子库无权")——区分依据 + 典型场景见 field_matching_logic.md §6注意区分"未维护"(hit_no_image + 6 字段全"-")和"未收录"(not_in_subdb + 6 字段全"子库未收录")

范围(严格限定,不外扩)

  • :按 3 位前缀(1HS / 1CO / 1LK / 1TI / 1CL / 1GR)路由到 6 个子库 bitable;查实物图(attachment 类型字段)下载后插图;抓 6 字段(类型 / 公母 / 颜色 / 孔数 / 防水 / 是否有相似件)写回 xlsx。
  • v2 扩展抓取(2026-08-18):在 v1 6 字段基础上追加 6 字段(TPA / CPA / 后盖 / 推杆 / 密封圈 / 2D图纸)——v2 是纯加法,v1 工作流不变,详见下一节 v2 扩展references/subdb_field_map.md v2 扩展字段 section。
  • 不做:BOM、订单、价格、工艺路线、供应商关系、相似件编码反查——这些都不在本 Skill 范围;用户要这些要新开 Skill。
  • 覆盖 6 子库 app_token + table_id:见 references/subdb_field_map.md,该 reference 是 SSOT,跑前可以校对一次。

v2 扩展(2026-08-18):TPA / CPA / 后盖 / 推杆 / 密封圈 / 2D图纸

v1 6 字段(类型/公母/颜色/孔数/防水/相似件)抓取已稳定(2026-08-18 11:29 落地)。本节是 v1 → v2 扩展的 6 个抓取内容,沉淀 6 子库覆盖矩阵、14 列表头、insert_cols 实现、2D图纸 PDF 转 PNG 路径、形态 A/B/C 处理逻辑。 详细 SSOT 在 references/subdb_field_map.md v2 扩展字段 section。

14 列表头(v1 7 字段 + v2 6 字段 + 末列"相似件")

实现方式:ws.insert_cols(idx=8, amount=6) 在原 7 列和"相似件"之间插入 6 新列 → 末列"是否有相似件"天然保持第 14 列(王健硬要求,错位即返工)。

| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |---|---|---|---|---|---|---|---|---|---|---|---|---|---| | 型号 | 图示 | 类型 | 公母 | 颜色 | 孔数 | 防水 | TPA | CPA | 后盖 | 推杆 | 密封圈 | 2D图纸 | 是否有相似件 | | text | image | v1 select | v1 select | v1 select | v1 select | v1 text | v2 select | v2 select | v2 select | v2 select | v2 select | v2 attachment | v1 select |

"相似件"必须保持在第 14 列(最后一列)——这是 v2 扩展的硬约束。insert_cols(idx=8) 在第 8 列位置前插入 6 新列,原第 8 列"是否有相似件"自动右移到第 14 列,末列天然保持

形态 A / B / C 三类处理逻辑

| 形态 | 含义 | 处理方式 | 本批调研结果 | |------|------|---------|------------| | A | 子库有独立字段(type=attachment / select / text / number) | 直接按字段抓取(attachment 下载插图;select/text 写值) | 6 子库 × 6 字段共 30 格中 11 格是形态 A(1HS 6 字段全 A + 5 子库 2D图纸 各 A) | | B | 实物图字段下分多张图(按 file_name 关键词分类) | 从"实物图"字段多 attachment 中按 file_name 关键词(TPA/CPA/后盖/推杆/密封圈/2D图纸)分类抓取 | 本批调研中形态 B 未出现(6 子库的 v2 字段都是独立字段) | | C | 子库根本没建此字段 | 单元格标"子库无此字段" | 19 格形态 C(5 子库 × 5 select 字段 = 25 格,其中 1HS 5 select 字段是 A → 余 5 × 4 = 20 格形态 C) | | 🔒 | 子库无权访问 | 短路标"子库无权" | 1LK 6 格全 🔒(user/bot 双重 91403/221700) |

**本批 6 子库 v2 字段存在矩阵(形态 A/B/C)**见 references/subdb_field_map.md v2 扩展字段 section。

2D图纸 字段处理路径

5 个子库(1HS/1CO/1TI/1CL/1GR)的 2D图纸 字段都是 attachment(PDF 格式),下载后处理:

  1. lark-cli base +record-download-attachment --as user 下载 PDF 到本地(./images/<型号>_2d.pdf
  2. PDF 转 PNG非 JPG——保留矢量线宽和尺寸标注清晰度):
    • 推荐 pymupdffitz):page = fitz.open(path); page.get_pixmap(dpi=200).save(out_path) — 单文件依赖、快
    • 备选 pdf2image(依赖 poppler-utils):convert_from_path(path, dpi=200) — 多页支持好
  3. openpyxl 插图:用 TwoCellAnchor(editAs='oneCell')(v1.0.7 起所有插图统一默认 anchor,详见 图片属性默认规则 章节)
  4. 行高 / 列宽:与 v1 实物图一致(行 120 磅、列 28 字符)

v1.0.2 章节的"v2 2D图纸 用 OneCellAnchor 而 v1 实物图用 TwoCellAnchor"(v1.0.2 分支式历史脉络)和 v1.0.3-v1.0.6 章节"统一 OneCellAnchor"(含 cx=0 cy=0 渲染陷阱历史)都已被 v1.0.7 升级到 TwoCellAnchor(editAs='oneCell') 替代为当前默认(详见 [图片属性默认规则] 章节):

  • 2D图纸 是工程图(含尺寸标注/技术要求),图大小不应随单元格拉伸变形——用 TwoCellAnchor(editAs='oneCell') 由 from→to 单元格差决定尺寸,单元格移动时图跟着走
  • v1.0.7 起所有图片(实物图 + 2D图纸)统一默认 TwoCellAnchor(editAs='oneCell')——v1.0.3-v1.0.6 期间"统一 OneCellAnchor"分支消解(详见 图片属性默认规则 章节

v1 抓 6 字段 → v2 抓 12 字段的工作流差异

v2 扩展是纯加法——v1 的工作流(路由 → 查图 → 下载 → 插图 → 写字段 → 上传 → 报告)完全不变,区别只在:

  1. 第 2 步(6 子库字段定义抓取):从 v1 的"实物图 + 6 字段"扩展为"实物图 + 12 字段",v2 6 字段的 field_id 见 references/subdb_field_map.md v2 扩展字段 section
  2. 第 3 步(对每行型号做查询)--field-id 列表加上 v2 6 字段 id;命中状态判断逻辑不变(v2 6 字段也走 contains + 精确匹配 + 4 种无值标签)
  3. 第 5 步(openpyxl 插图):v1.0.7 起所有图片(实物图 + 2D图纸)统一默认 TwoCellAnchor(editAs='oneCell')——v1.0.3-v1.0.6 期间"统一 OneCellAnchor"分支也已消解(OneCellAnchor 有 cx=0 cy=0 渲染陷阱被 v1.0.7 替换)。详见 图片属性默认规则 章节
  4. 第 6 步(写字段到 xlsx):v1 6 字段写 3-8 列;v2 6 字段在 insert_cols(idx=8) 后写 8-13 列;14 列"相似件"由 v1 字段流写入,末列位置不变
  5. 第 8 步(行级报告):v2 6 字段也写 4 种无值标签("-" / "子库无此字段" / "子库未收录" / "子库无权"),与 v1 一致

图片属性默认规则:2026-08-18 v1.0.3 升级 v1.0.4 修脚本 v1.0.7 升级到 TwoCellAnchor

王健 13:46 追加硬要求:「将图片属性默认为,大小和位置随单元格而变,沉淀进 Skill」。本节是图片 anchor 默认行为的演化总览:

  • v1.0.2 及更早:分支式——v1 实物图 用 TwoCellAnchor、v2 2D图纸 用 OneCellAnchor(按字段语义决定 anchor 类型)
  • v1.0.3 升级(2026-08-18 13:46):合并为统一默认 OneCellAnchor——无分支无例外(设计意图:图片随单元格而变)
  • v1.0.4 修脚本 bug(2026-08-18 14:08):v1.0.3 写的 OneCellAnchor(_from=..., ext=None, editAs='oneCell') 在 openpyxl 3.x 抛 TypeErroreditAsTwoCellAnchor 专属),v1.0.4 去掉 editAs kwarg,anchor 仍是 OneCellAnchor
  • v1.0.5 / v1.0.6 复发(v1 实物图 + 2D图纸 都用 OneCellAnchor):但 OneCellAnchor(_from=..., ext=None) 被 openpyxl 序列化为 <ext cx="0" cy="0"/>,Excel 看到 0 像素图直接不画——数据全在、视觉上完全没有(v1.0.5/v1.0.6 各修一处别的 bug 都没碰到这个,2 个版本被反复误报"图不见了")
  • v1.0.7 升级(2026-08-18 16:30,当前默认):改用 TwoCellAnchor(editAs='oneCell', _from=..., to=...)——不需要 ext,尺寸由 from→to 单元格差决定,"图片随单元格而变" 真正在 Excel/WPS 视觉上生效;import 同步补上 TwoCellAnchor;drawing1.xml 校验稳定(<twoCellAnchor editAs="oneCell"> 节点数 = 图片数,无 cx="0" cy="0" 出现)

设计意图没变:"图片随单元格而变"——只是从 v1.0.3-v1.0.6 时期"想用 OneCellAnchor 但渲染陷阱"换成 v1.0.7"TwoCellAnchor(editAs='oneCell') 真正实现"。禁止回退到 OneCellAnchor 模式(v1.0.3-v1.0.6 渲染陷阱会复发)。

1. 默认 anchor

TwoCellAnchor(editAs='oneCell', _from=AnchorMarker(col, colOff, row, rowOff), to=AnchorMarker(col, colOff, row, rowOff))(v1.0.7 起所有图片统一默认 anchor)

2. 行为

  • 大小随单元格而变:图片尺寸由 from→to 单元格差决定(不写 ext);cell 行高/列宽变化时图片跟着拉伸/缩放
  • 位置随单元格而变:图片锚定在 from 单元格,cell 移动/复制时图片跟着走
  • editAs='oneCell' 锁定比例:to 端可被用户拖动缩放,但拖动后仍按"与 from 端等比缩放"行为(不出现"图被 cell 拉宽变扁")
  • 不会渲染成 0 像素:v1.0.3-v1.0.6 OneCellAnchor 时期图片大小被 openpyxl 写成 cx="0" cy="0"(视觉完全消失),v1.0.7 TwoCellAnchor 真正按 from→to 决定尺寸

3. 无分支

v1.0.2 时期有"v1 实物图 TwoCellAnchor / v2 2D图纸 OneCellAnchor"的分支选择;v1.0.3-v1.0.6 升级为统一默认 OneCellAnchor(但有 cx="0" cy="0" 渲染陷阱导致图片视觉消失);v1.0.7 起所有图片(实物图 + 2D图纸)统一默认 TwoCellAnchor(editAs='oneCell')——不要再加任何分支条件("行高>200 用 OneCellAnchor"、"实物图用 OneCellAnchor"、"2D图纸用 OneCellAnchor"、"用回 OneCellAnchor 减少 ext 字段" 都属于分支,禁用)。

4. 脚本实现

scripts/fetch_image_and_fields.pyinsert_image() 函数 v1.0.7 起(v1.0.3-v1.0.6 时期默认 OneCellAnchor,v1.0.7 改 TwoCellAnchor 修复 0 像素图 bug):

def insert_image(ws, row_idx, img_path, target_col=1):
    """默认锚定:随单元格而变(TwoCellAnchor,v1.0.7 起)。

    v1.0.7 起所有插图(实物图 + 2D图纸)统一用 TwoCellAnchor(editAs='oneCell'),
    无分支无例外。图片位置由 from 端锚定,大小由 from→to 单元格差决定,
    单元格行高/列宽变化时图片跟着拉伸/缩放。

    v1.0.7 升级背景:v1.0.3-v1.0.6 时期用 OneCellAnchor(_from=..., ext=None),
    openpyxl 序列化为 <ext cx="0" cy="0"/> → Excel 看到 0 像素图直接不画。
    v1.0.5 修下载路径 / v1.0.6 修 v2 字段都没碰到这个,2 个版本被反复误报
    "图不见了"。v1.0.7 改用 TwoCellAnchor(editAs='oneCell', _from=..., to=...)
    真正实现"图片随单元格而变"——尺寸由 from→to 单元格差决定,无需 ext 字段。
    import 同步补上 TwoCellAnchor。
    """
    img = Image(img_path)
    target_h = 130
    ratio = target_h / img.height
    img.width = int(img.width * ratio)
    img.height = target_h

    # 默认锚定:TwoCellAnchor(editAs='oneCell')(v1.0.7 起,图片随单元格而变,无分支)
    # to 端 = from 端下方一格(约图高度 130px 对应的行),让图占满数据行
    anchor = TwoCellAnchor(
        editAs='oneCell',
        _from=AnchorMarker(col=target_col, colOff=0, row=row_idx - 1, rowOff=0),
        to=AnchorMarker(col=target_col + 1, colOff=0, row=row_idx, rowOff=0),
    )
    img.anchor = anchor
    ws.add_image(img)

5. 触发关键词扩充

description 触发关键词末尾追加(v1.0.3 时期写、v1.0.7 升级时同步改成 TwoCellAnchor):

图片随单元格缩放、anchor 默认值、TwoCellAnchor 默认锚定、图片默认属性

6. v1.0.2 / v1.0.3-v1.0.6 历史红线保留

v1.0.2 红线 8.7「v2 2D图纸 用 OneCellAnchor——与 v1 实物图 TwoCellAnchor 不同」以及 v1.0.3-v1.0.6 期间"统一默认 OneCellAnchor"这两条都保留为本节历史脉络的子项——v1.0.7 升级到 TwoCellAnchor 后:

  • "实物图 vs 2D图纸 的 anchor 分支" 在 v1.0.3 就已消解(统一 OneCellAnchor)
  • "OneCellAnchor 统一默认" 在 v1.0.7 又被 TwoCellAnchor 替代(解决 0 像素图 bug)
  • 现在 v1 实物图 + v2 2D图纸 都用 TwoCellAnchor(editAs='oneCell')"统一 TwoCellAnchor"成为当前红线

v1.0.2 / v1.0.3-v1.0.6 那两条红线的具体描述见 v2 扩展 §2D图纸 字段处理路径 章节末尾的子项说明。

7. 边界红线(v1.0.3 升级 OneCellAnchor → v1.0.4 修脚本 → v1.0.7 升级到 TwoCellAnchor,当前为 v1.0.7 状态

图片属性默认为 TwoCellAnchor(editAs='oneCell'),无分支无例外。——任何对 anchor 的修改请求,必须先讨论是否需要回到 v1.0.3-v1.0.6 OneCellAnchor 模式(禁止:v1.0.3-v1.0.6 时期 OneCellAnchor(_from=..., ext=None) 被 openpyxl 序列化为 cx="0" cy="0",导致图片视觉完全消失);在 Skill 内不允许临时增加分支。editAs='oneCell'TwoCellAnchor 专属参数,正是 v1.0.7 用来锁定"图片随单元格而变"行为的关键,不要去掉。

边界红线(必须遵守,写死在这里)

  1. 绝不编造字段值:找不到 / 子库未收录 / 子库无权 / 字段空 / 子库无此字段 → 老实标对应状态标签。状态标签集合见 references/status_codes.md
  2. 1LK 子库双重无权限lark-cli base +record-list --as user--as bot 都会报 91403/221700,不要硬试其他身份。直接标"子库无权"。
  3. 不暴露本地路径:任何用户可见的产出(评论、报告、xlsx 引用)只能出现飞书云端链接(https://...feishu.cn/file/<token>)或 file/record token;本地沙箱路径(/home/...)一律不出现在交付里。
  4. 实物图字段名按子库不同:1HS 实物图字段是"实物图"(fldEzskWkn);1TI 实物图字段是"实物图"(fldS0arWBJ不是"实物照片";1CO 用"展示图";1CL/1GR 用"实物照片"。别用错字段名。详细见 field_matching_logic.md §2 红线 1
  5. "是否有相似件"字段语义差异:1HS / 1GR 是单选("是"/"否"/"相似件");1CL 的"相似件"是 attachment 字段("相似件实物照片对比差异点"),不是编码字段,只能标"有附件(无编码)";1CO / 1TI / 1LK 子库无此字段。详见 references/subdb_field_map.mdfield_matching_logic.md §2 红线 3
  6. 6 字段在不同子库缺失是正常现象:例如 1CO / 1TI 只有"类型+颜色"两个字段,别的字段就要老老实实标"子库无此字段",不要脑补。
  7. 匹配用 contains + 精确匹配双步:先 +record-list --filter-jsoncontains 探,再用 record 列表里"物料编码"字段值做精确匹配;filter 不可靠(可能匹配到子串或同前缀兄弟料号),精确匹配是真相。详见 field_matching_logic.md §4
  8. v2 扩展红线(2026-08-18 新增,详见 v2 扩展 章节):
    • 1LK 3 型号 6 v2 字段全"子库无权"——不换身份硬试
    • 1GR-01841 6 v2 字段全"子库未收录"——不脑补
    • 形态 B(实物图分类)本批未出现——不预设案例
    • 1HS 5 个 select 字段名带"是否含有配套组件-"前缀——别简化成"TPA"
    • "相似件"必须保持在第 14 列(最后一列)——insert_cols(idx=8) 实现,错位即返工
    • 2D图纸 是 attachment 字段,下载后全 PDF 转 PNG——非 JPG
    • v1.0.7 起所有图片统一默认 TwoCellAnchor(editAs='oneCell')——无分支无例外,v1 实物图 + v2 2D图纸 都用 TwoCellAnchor(editAs='oneCell')(v1.0.3-v1.0.6 统一 OneCellAnchor 时期有 cx=0 cy=0 渲染陷阱,v1.0.7 升级到 TwoCellAnchor 才真正实现"图片随单元格而变",详见 图片属性默认规则

工作流(按顺序执行)

0. 输入校验

  • 输入 xlsx 必须有"型号"列(或首列),从 row 2 开始到 max_row,每行一个物料编码,形如 1HS-08847(前缀 + 中划线 + 数字)。
  • 总库链接必须是飞书 bitable 链接(https://...feishu.cn/base/<base_token>?table=<table_id>...),从链接里 parse 出 base_token + table_id
  • 6 子库 app_token + table_id 默认走 references/subdb_field_map.md;如果用户给了新总库,必须先 lark-cli base +table-list 重新解析子库,不能盲用。
  • v2 抓取开关:默认 v1 6 字段;如果用户说"扩展"、"12 字段"、"TPA/CPA/后盖/推杆/密封圈/2D图纸"等关键词,启用 v2 12 字段抓取。

1. 拉总库"任务描述"列

lark-cli base +record-list \
  --as user \
  --base-token <TOTAL_BASE_TOKEN> \
  --table-id <TOTAL_TABLE_ID> \
  --field-id 任务描述 \
  --output ./total_db.ndjson

从"任务描述"列的 markdown 文本里正则解析 [1XX-xxx-数据库](https://...feishu.cn/base/<app_token>?table=<table_id>...) 形式的链接,写入 subdb_registry 字典(key = 前缀,value = (app_token, table_id))。

2. 6 子库字段定义抓取

对每个子库跑一次(8 路并发 OK,+field-list 不会触发限流):

lark-cli base +field-list --as user \
  --base-token <APP_TOKEN> --table-id <TABLE_ID> \
  --output ./fields_<PREFIX>.ndjson

解析后,对每个子库分别确定

  • 实物图字段 id(type=attachment,name 带"图")—— 1TI 必须确认是"实物图"(fldS0arWBJ)不是"实物照片"
  • 6 字段 id(类型 / 公母 / 颜色 / 孔数或PIN数 / 防水 / 是否有相似件)—— 字段名 / 字段类型 / 字段语义在 6 子库间差异很大,对照 references/field_matching_logic.md §1 覆盖矩阵
  • v2 扩展时追加:v2 6 字段 id(TPA/CPA/后盖/推杆/密封圈/2D图纸)——SSOT 见 references/subdb_field_map.md v2 扩展字段 section;1HS 字段名带"是否含有配套组件-"前缀,别简化

把映射写进 subdb_field_map 字典,对 1TI 要特别确认是"实物图"不是"实物照片"。详见 references/subdb_field_map.md

3. 对每行型号做查询

对 xlsx 每个型号(行 i)执行:

lark-cli base +record-list --as user \
  --base-token <APP_TOKEN> --table-id <TABLE_ID> \
  --field-id 物料编码 --field-id <实物图字段> --field-id <6 字段 id> ... \
  [--field-id <v2 6 字段 id> ...]  # v2 扩展时追加
  --filter-json '{"logic":"and","conditions":[["物料编码","contains","<型号>"]]}' \
  --limit 5

判断状态(按这个顺序短路):

  • 报 91403 / 221700 → unauthorized_subdb("子库无权")
  • 返回空 / filter 无命中 → not_in_subdb("子库未收录")
  • 有记录但物料编码不等于型号(精确匹配失败)→ 仍按 not_in_subdb 处理(contains 是粗筛,精确才是真相)
  • 有记录且精确匹配成功 → 拿实物图字段、6 字段值
    • 实物图 attachment 为空 → hit_no_image(图空,6 字段继续写)
    • 实物图有值 → hit_with_image
  • v2 扩展时:v2 6 字段也按字段级状态(filled / empty / field_missing)走,与 v1 同逻辑

把每行的状态、实物图 file_token、6 字段值、v2 6 字段值汇总到 row_results[i]。匹配流程的完整说明见 field_matching_logic.md §4-§5

4. 下载实物图

对每个 hit_with_image 行跑:

lark-cli base +record-download-attachment --as user \
  --base-token <APP_TOKEN> --table-id <TABLE_ID> \
  --record-id <RECORD_ID> --file-token <FILE_TOKEN> \
  --output ./images/<型号>.png --overwrite

下载后用 os.path.getsize() 校验(< 1000 字节视为下载失败,按图空处理)。

v2 扩展时:2D图纸 字段下载走同一命令,输出到 ./images/<型号>_2d.pdf;下载后做 PDF 转 PNG(pymupdf / pdf2image,详见 v2 扩展 §2D图纸 处理路径)。

5. openpyxl 插图(关键细节)

  • 加载源 xlsx(不要data_only=True,否则图片丢失)
  • B 列(图示列)宽 28 字符
  • 每个数据行(row 2..max_row)行高 120 磅
  • TwoCellAnchor(editAs='oneCell')(v1.0.7 起所有图片统一默认 anchor,详见 图片属性默认规则 章节)—— 单元格移动/大小变时图片跟着动
  • 图片高度统一 130 像素,宽度按原图比例缩放
  • 图片插入用 ws.add_image(img)img.anchor = anchor

v1.0.7 起所有图片(实物图 + 2D图纸)统一默认 TwoCellAnchor(editAs='oneCell')——图大小由 from→to 单元格差决定,单元格移动/大小变时图片跟着动。详见 图片属性默认规则 章节。

参考 scripts/fetch_image_and_fields.pyinsert_image() 函数(v1.0.7 起该函数默认 TwoCellAnchor(editAs='oneCell')editAs='oneCell' 锁定 from→to 等比缩放行为)。

6. 写字段到 xlsx

按 xlsx 列号写入:

  • v1 默认(7 字段 + 末列相似件):列 3=类型、4=公母、5=颜色、6=孔数、7=防水、8=是否有相似件
  • v2 扩展时(14 列):先 ws.insert_cols(idx=8, amount=6) 插入 6 新列 → 原 8 列"相似件"自动右移到第 14 列;再按列 3=类型、4=公母、5=颜色、6=孔数、7=防水、8=TPA、9=CPA、10=后盖、11=推杆、12=密封圈、13=2D图纸、14=是否有相似件写入

值取自 row_results[i]['fields']

  • 子库无此字段 → "子库无此字段"
  • 子库无权 → "子库无权"
  • 子库未收录 → "子库未收录"
  • 字段空(子库有但未填)→ "-"
  • 字段有值 → 写入 select 字段的 option name / text 字段的 raw_value

各字段的写入规则(option name 原样写 / 字符串 vs int / text 料号 vs select 是否类)见 field_matching_logic.md §3

7. 上传飞书云盘

lark-cli drive +upload --as user \
  --file ./<输出文件名>.xlsx --name "<输出文件名>.xlsx"

取返回的 file_token,产出链接 https://<tenant>.feishu.cn/file/<file_token>

8. 行级报告

  • 总行数 + 命中/未维护/未收录/无权/部分缺失 几类计数
  • 每行 row 号 + 型号 + 状态 + 关键备注(如 1TI 字段名差异、1LK 跳过原因)
  • 子库字段覆盖差异表(v1 6 字段 × 6 子库 的"有/无"矩阵 + v2 6 字段 × 6 子库 的形态 A/B/C 矩阵)
  • 在报告里暴露本地路径;所有引用都是飞书云端链接

一键脚本

直接跑主脚本(接受 xlsx 路径和总库链接作为参数):

# v1 模式(默认,6 字段)
python3 scripts/fetch_image_and_fields.py \
  --input <物料清单.xlsx> \
  --total-base <TOTAL_BASE_TOKEN> \
  --total-table <TOTAL_TABLE_ID> \
  --output <输出文件名>.xlsx

# v2 模式(12 字段,含 TPA/CPA/后盖/推杆/密封圈/2D图纸)
python3 scripts/fetch_image_and_fields.py \
  --input <物料清单.xlsx> \
  --total-base <TOTAL_BASE_TOKEN> \
  --total-table <TOTAL_TABLE_ID> \
  --output <输出文件名>.xlsx \
  --v2-extended  # 启用 v2 6 字段扩展

主脚本会按上面的工作流跑完整套,输出:

  • <输出文件名>.xlsx(图已填 + 字段已填)
  • row_report.md(行级 markdown 报告)
  • images/(下载的实物图 + 2D图纸 PNG,可选保留)

触发示例

"我用 5工作表.xlsx(44 个型号,集中在 1HS)+ 4-散件数据库汇总清单-2025 跑一次,期望产出 5工作表_图已填_字段已填.xlsx 传到飞书云盘。"

具体触发应该长这样:

  • 用户消息里出现 "散件数据库"、"1HS-护套" 这种前缀关键词
  • 用户给一个 xlsx 链接 + 总库 bitable 链接
  • 用户明确说"查实物图"或"抓字段"或"双抓取"
  • 用户问"6 字段在不同子库是怎么存的"、"1TI 实物图字段叫什么"、"1CL 相似件字段是 select 还是 attachment"——这些都触发方法论章节
  • v2 扩展触发(2026-08-18 新增):用户说"TPA"、"CPA"、"后盖"、"推杆"、"密封圈"、"2D图纸"、"12 字段"、"14 列表头"、"相似件最后一列"、"PDF 转 PNG"等关键词时,启用 v2 12 字段抓取(自动 insert_cols + 2D图纸 PDF 处理 + 1HS 字段名带前缀)

资源

scripts/

  • fetch_image_and_fields.py — 主入口:跑完整套(解析 → 查图 → 下载 → 插图 → 写字段 → 报告);支持 --v2-extended 参数启用 12 字段抓取

references/

  • field_matching_logic.md字段匹配方法论(精简版):覆盖矩阵摘要 + 同义/不等价判断 + 字段值写入规则 + contains+精确匹配双步法 + 4 种无值标法
  • subdb_field_map.md — 6 子库 app_token + table_id + 实物图字段 + v1 6 字段(SSOT)+ v2 扩展字段(6 子库 × 6 新字段 形态 A/B/C 矩阵 + 1HS 5 select 字段名带"是否含有配套组件-"前缀 + 5 子库 2D图纸 attachment 字段 + 14 列表头 + 44 行命中统计 + 1LK/1GR-01841 红线)
  • filter_and_attach.md--filter-json tuple 语法、+record-download-attachment 模板、限流处理
  • status_codes.md — 5 类状态码(hit_with_image / hit_no_image / not_in_subdb / unauthorized_subdb / partial_missing)的语义与写入映射

云文档(完整版方法论)

  • 《散件数据库字段匹配方法论》 — 6 字段语义定义 + 覆盖矩阵 + 字段名映射规则 + 值标准化 + 匹配优先级 + 4 种无值标法 + 已知陷阱(飞书云文档,4941 字 / 430 blocks / 11 tables)

assets/

(无,本 Skill 不产模板/字体/icon)

红线复述

  • ❌ 找不到的图不要用别的图凑
  • ❌ 字段空不要脑补("实物图"≠"实物照片"、"相似件"字段有 attachment / select / 无字段 3 种)
  • ❌ 1LK 双重无权限不要换身份硬试
  • ❌ 本地路径不进交付
  • ❌ 不接 BOM/订单/价格/工艺的活——那是别的 Skill
  • ❌ 1TI 实物图字段用"实物图"(fldS0arWBJ),绝不用"实物照片"(6/25 红线)
  • v2 扩展新增红线(2026-08-18):
    • ❌ 1HS 5 select 字段简化成"TPA"——带"是否含有配套组件-"前缀
    • ❌ "相似件"列错位——必须用 insert_cols(idx=8, amount=6) 让末列保持第 14 列
    • ❌ 2D图纸 下载后当 JPG 存——必须 PDF 转 PNG
    • ❌ v2 2D图纸 回到 v1.0.3-v1.0.6 OneCellAnchor 模式——必须 TwoCellAnchor(editAs='oneCell')(v1.0.7 起统一)
    • ❌ 形态 B 预设案例——本批调研中形态 B 未出现
    • ❌ 1GR-01841 6 v2 字段脑补——标"子库未收录"即可
  • v1.0.7 升级新增红线(2026-08-18 16:30,当前红线):图片属性默认为 TwoCellAnchor(editAs='oneCell'),无分支无例外——不再区分"实物图 vs 2D图纸"用不同 anchor,全部统一默认;不允许临时加分支(如"行高>200 用 OneCellAnchor"、"用回 OneCellAnchor 减少 ext 字段")。editAs='oneCell' 是 v1.0.7 用来锁定"图片随单元格而变"行为的关键参数,必须保留;回退到 v1.0.3-v1.0.6 OneCellAnchor(_from=..., ext=None) 模式会触发 cx=0 cy=0 渲染陷阱(Excel 看到 0 像素图直接不画)。