diff --git a/skills/feishu-bitable/CONTRIBUTORS b/skills/feishu-bitable/CONTRIBUTORS new file mode 100644 index 0000000..d8649da --- /dev/null +++ b/skills/feishu-bitable/CONTRIBUTORS @@ -0,0 +1 @@ +root diff --git a/skills/feishu-bitable/SKILL.md b/skills/feishu-bitable/SKILL.md new file mode 100644 index 0000000..998af16 --- /dev/null +++ b/skills/feishu-bitable/SKILL.md @@ -0,0 +1,101 @@ +--- +name: "feishu-bitable" +description: "飞书多维表格(bitable)全流程操作:建表、字段/记录 CRUD、批量写入,含主字段改名与删字段防丢数据等踩坑要点。" +metadata: + author: xf + version: "1.0.0" +--- + +# 飞书多维表格(Bitable)操作 + +创建/维护飞书多维表格的完整流程。基于 2026-08-03 创建「雨云 API 接口管理表」(437 条记录)实战验证。 + +## 前置条件 + +- 应用已开通 `bitable:app` 权限(未开通会报 `99991672 Access denied`,需去开放平台申请) +- 可复用 `feishu-wiki-tools` 的 `init_token.py` 获取 tenant_access_token: + +```bash +cd ~/.openclaw/workspace/skills/feishu-wiki-tools +python3 - <<'EOF' +import json, subprocess, requests +r = subprocess.run(['python3', 'scripts/init_token.py'], capture_output=True, text=True) +token = json.loads(r.stdout)['token'] +h = {'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'} +EOF +``` + +## 核心 API + +| 操作 | 端点 | 说明 | +|------|------|------| +| 创建应用 | `POST /open-apis/bitable/v1/apps` | body `{name}`,返回 `app_token` + `default_table_id` | +| 建字段 | `POST /apps/{APP}/tables/{TBL}/fields` | body `{field_name, type}` | +| 改字段 | `PUT /apps/{APP}/tables/{TBL}/fields/{fid}` | **改主字段名必须带 `type` + `ui_type`** | +| 删字段 | `DELETE /apps/{APP}/tables/{TBL}/fields/{fid}` | ⚠️ 连带删除该列全部数据 | +| 列字段 | `GET /apps/{APP}/tables/{TBL}/fields?page_size=100` | 响应含 `is_primary` 标记 | +| 批量写入 | `POST /apps/{APP}/tables/{TBL}/records/batch_create` | body `{records:[{fields:{...}}]}`,≤100/批稳妥 | +| 批量更新 | `POST /apps/{APP}/tables/{TBL}/records/batch_update` | 同上 | +| 批量删除 | `POST /apps/{APP}/tables/{TBL}/records/batch_delete` | body `{records:[record_id...]}` | +| 列表分页 | `GET /apps/{APP}/tables/{TBL}/records?page_size=500&page_token=...` | 循环直到 `has_more=false` | +| 搜索统计 | `POST /apps/{APP}/tables/{TBL}/records/search` | body `{page_size:1}`,`data.total` 拿总数 | + +## 字段类型速查 + +`1=Text`、`3=SingleSelect`、`5=DateTime`、`17=Attachment` + +## ⚠️ 建表标准流程(顺序很重要) + +1. **创建应用** → 拿到 `app_token`、`default_table_id` +2. **先清默认行**:新建表自带 10 条空记录,`batch_delete` 删掉 +3. **先删默认字段**:自带「文本/单选/日期/附件」4 列中,**「文本」是主字段删不掉**,其余 3 个直接删掉 +4. **改造主字段**:把删不掉的主字段「文本」改造成自己需要的列(如「接口路径」)——`PUT /fields/{fid}`,body 必须带 `type` + `ui_type`(如 `{"field_name":"接口路径","type":1,"ui_type":"Text"}`) +5. **添加剩余自己的字段**:`POST /fields` 逐个建(方法、摘要、参数…) +6. **写入数据**:`batch_create` 分批(100/批 + `time.sleep(0.3)` 限速) +7. **验证**:`records/search` 拿 total 核对 == 写入条数 + +## 🕳️ 踩坑记录(全部实战踩过) + +1. **主字段(Primary Field)不可删除**(报 `1254046 The Primary Field cannot be deleted`)。只能改名复用:`PUT /fields/{fid}`,**body 必须带 `type` + `ui_type`**(如 `{"field_name":"接口路径","type":1,"ui_type":"Text"}`),否则报 `99992402 field validation failed`。所以正确姿势是**先把它改造成自己的字段**,而不是新建一个同义字段再删旧的。 +2. **删字段 = 删数据**:删除字段会把该列所有记录值一起删掉,不可恢复。删之前必须:① 确认本地有备份,或 ② 先把数据迁到别的字段(batch_update 逐批迁移后再删)。 +3. **改名失败后不要连锁删字段**:字段改名失败(99992402)时,后续依赖新字段名的操作会全部失败(1254045 field not found),此时若顺手把旧字段删了会造成数据丢失。改名前先验证字段存在。 +4. **批量写入上限**:官方上限 500/次,实测 100/批最稳;批量间加 0.3s 间隔避免限流。 +5. **记录顺序对应**:批量创建后按列表顺序取回,与源数据顺序一致,可用于按序回填/迁移。 + +## 完整示例(标准流程) + +```python +APP = 'xxx' # 创建返回的 app_token +TBL = 'xxx' # default_table_id +h = {'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'} + +# 1. 读默认空记录并删除(10 条自带空行) +recs = ... # GET records 分页拉全 +empty = [r['record_id'] for r in recs if not r['fields']] +requests.post(f'.../records/batch_delete', headers=h, json={'records': empty}) + +# 2. 删默认字段(跳过 is_primary 的「文本」) +for f in fields: + if not f.get('is_primary') and f['field_name'] in ('单选','日期','附件'): + requests.delete(f'.../fields/{f["field_id"]}', headers=h) + +# 3. 改造主字段「文本」→ 自己的字段 +requests.put(f'.../fields/{primary_fid}', headers=h, + json={'field_name': '接口路径', 'type': 1, 'ui_type': 'Text'}) + +# 4. 添加剩余字段 +for name, ftype in [('方法',3),('接口摘要',1),('参数',1)]: + requests.post(f'.../apps/{APP}/tables/{TBL}/fields', headers=h, + json={'field_name': name, 'type': ftype}) + +# 5. 批量写数据 +rows = [...] # [{fields: {...}}, ...] +for i in range(0, len(rows), 100): + requests.post(f'.../records/batch_create', headers=h, json={'records': rows[i:i+100]}) + time.sleep(0.3) +``` + +## 关联 + +- `feishu-wiki-tools` — 知识库/docx 写入,本 skill 专注 bitable 多维表格 +- 权限申请链接格式:`https://open.feishu.cn/app/{app_id}/auth?q=bitable:app,...`