From 0fc1e550fe297e8f3b1b66bb5cbcb97f2e08f3ce Mon Sep 17 00:00:00 2001 From: karuo Date: Wed, 25 Feb 2026 12:13:09 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=84=20=E5=8D=A1=E8=8B=A5AI=20=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=202026-02-25=2012:13=20|=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=EF=BC=9A=E6=B0=B4=E6=A1=A5=E5=B9=B3=E5=8F=B0=E5=AF=B9=E6=8E=A5?= =?UTF-8?q?=E3=80=81=E8=BF=90=E8=90=A5=E4=B8=AD=E6=9E=A2=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E5=8F=B0=20|=20=E6=8E=92=E9=99=A4=20>20MB:=2013=20=E4=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../飞书管理/脚本/feishu_publish_blocks_with_images.py | 139 +++++++++++++----- 运营中枢/工作台/gitea_push_log.md | 1 + 运营中枢/工作台/代码管理.md | 1 + 3 files changed, 105 insertions(+), 36 deletions(-) diff --git a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/feishu_publish_blocks_with_images.py b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/feishu_publish_blocks_with_images.py index 5989c0f4..3a2e9a12 100644 --- a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/feishu_publish_blocks_with_images.py +++ b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/feishu_publish_blocks_with_images.py @@ -21,6 +21,7 @@ import sys import json import argparse import re +import time from pathlib import Path from datetime import datetime import requests @@ -320,46 +321,112 @@ def _post_children(doc_token: str, headers: dict, children: list, index: int | N return {"code": -1, "msg": f"non-json response: {wr.text[:200]}"} +def _col_letter(n: int) -> str: + s = "" + while True: + s = chr(65 + n % 26) + s + n = n // 26 - 1 + if n < 0: + break + return s + + +def _fill_sheet_block_values(headers: dict, sheet_block_token: str, values: list[list[str]]) -> bool: + if not sheet_block_token or "_" not in sheet_block_token or not values: + return False + spreadsheet_token, sheet_id = sheet_block_token.rsplit("_", 1) + rows = len(values) + cols = max((len(r) for r in values), default=0) + if rows <= 0 or cols <= 0: + return False + norm = [list(r) + [""] * (cols - len(r)) for r in values] + end_col = _col_letter(cols - 1) + range_str = f"{sheet_id}!A1:{end_col}{rows}" + url = f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{spreadsheet_token}/values" + payload = {"valueRange": {"range": range_str, "values": norm}} + r = requests.put(url, headers=headers, params={"valueInputOption": "RAW"}, json=payload, timeout=30) + try: + j = r.json() + except Exception: + j = {"code": -1, "msg": r.text[:160]} + if j.get("code") != 0: + print(f"⚠️ 表格数据写入失败: {j.get('msg')} range={range_str}") + return False + return True + + +def _write_block_with_sheet_data(doc_token: str, headers: dict, block: dict) -> bool: + values = block.get("__sheet_values") + post_block = dict(block) + post_block.pop("__sheet_values", None) + res = _post_children(doc_token, headers, [post_block], None) + if res.get("code") != 0: + print(f"⚠️ 表格块写入失败: code={res.get('code')} msg={res.get('msg')}") + return False + children = ((res.get("data") or {}).get("children") or []) + if not children: + print("⚠️ 表格块写入成功但未返回 children,无法回填单元格") + return False + sheet_token = ((children[0].get("sheet") or {}).get("token") or "") + if not sheet_token: + print("⚠️ 未拿到 sheet token,无法写入表格内容") + return False + ok = _fill_sheet_block_values(headers, sheet_token, values or []) + if ok: + print("✅ 表格块已写入并回填数据") + return ok + + +def _write_batch_with_fallback(doc_token: str, headers: dict, batch: list, total_valid_len: int) -> None: + res = _post_children(doc_token, headers, batch, None) + if res.get("code") != 0: + debug = res.get("debug", "") + print(f"⚠️ 写入失败: code={res.get('code')} msg={res.get('msg')} debug={debug}") + if any(b.get("block_type") in (12, 18) for b in batch): + safe = [b for b in batch if b.get("block_type") not in (12, 18)] + if safe: + res2 = _post_children(doc_token, headers, safe, None) + if res2.get("code") == 0: + print("⚠️ 图片块跳过,已写文本(图片已上传到文档素材)") + time.sleep(0.35) + return + + print("⚠️ 进入逐块写入降级模式:定位并跳过非法块") + for b in batch: + if b.get("block_type") in (12, 18): + continue + r1 = _post_children(doc_token, headers, [b], None) + if r1.get("code") == 0: + time.sleep(0.35) + continue + c = _get_text_content(b) + preview = (c[:60] + "...") if c and len(c) > 60 else (c or "") + print(f"⚠️ 跳过非法块: code={r1.get('code')} msg={r1.get('msg')} preview={preview!r}") + time.sleep(0.35) + return + if total_valid_len > 50: + time.sleep(0.35) + + def write_blocks(doc_token: str, headers: dict, blocks: list) -> None: valid = sanitize_blocks([b for b in blocks if b is not None]) - for i in range(0, len(valid), 50): - batch = valid[i : i + 50] - res = _post_children(doc_token, headers, batch, None) - if res.get("code") != 0: - # 含图片块时常见会失败;此处打印详情并降级为“只写文本块” - debug = res.get("debug", "") - print(f"⚠️ 写入失败: code={res.get('code')} msg={res.get('msg')} debug={debug}") - if any(b.get("block_type") in (12, 18) for b in batch): - safe = [b for b in batch if b.get("block_type") not in (12, 18)] - if safe: - res2 = _post_children(doc_token, headers, safe, None) - if res2.get("code") == 0: - print("⚠️ 图片块跳过,已写文本(图片已上传到文档素材)") - import time - time.sleep(0.35) - continue + pending = [] - # 仍失败:逐块写入,跳过坏块,保证整体可落地 - print("⚠️ 进入逐块写入降级模式:定位并跳过非法块") - for b in batch: - if b.get("block_type") in (12, 18): - # 图片块依然不强行写,避免整批失败 - continue - r1 = _post_children(doc_token, headers, [b], None) - if r1.get("code") == 0: - import time - time.sleep(0.35) - continue - # 这一个块不合法,跳过 - c = _get_text_content(b) - preview = (c[:60] + "...") if c and len(c) > 60 else (c or "") - print(f"⚠️ 跳过非法块: code={r1.get('code')} msg={r1.get('msg')} preview={preview!r}") - import time - time.sleep(0.35) - continue - if len(valid) > 50: - import time + def flush_pending(): + nonlocal pending + for i in range(0, len(pending), 50): + batch = pending[i : i + 50] + _write_batch_with_fallback(doc_token, headers, batch, len(valid)) + pending = [] + + for b in valid: + if isinstance(b, dict) and "__sheet_values" in b and b.get("block_type") == 30: + flush_pending() + _write_block_with_sheet_data(doc_token, headers, b) time.sleep(0.35) + continue + pending.append(b) + flush_pending() def send_webhook(webhook: str, text: str) -> None: diff --git a/运营中枢/工作台/gitea_push_log.md b/运营中枢/工作台/gitea_push_log.md index acd7ce2f..31cca5c6 100644 --- a/运营中枢/工作台/gitea_push_log.md +++ b/运营中枢/工作台/gitea_push_log.md @@ -142,3 +142,4 @@ | 2026-02-25 11:52:39 | 🔄 卡若AI 同步 2026-02-25 11:52 | 更新:水溪整理归档、运营中枢、运营中枢工作台 | 排除 >20MB: 13 个 | | 2026-02-25 12:07:57 | 🔄 卡若AI 同步 2026-02-25 12:07 | 更新:Cursor规则、水桥平台对接、运营中枢、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 13 个 | | 2026-02-25 12:09:29 | 🔄 卡若AI 同步 2026-02-25 12:09 | 更新:运营中枢、运营中枢工作台 | 排除 >20MB: 13 个 | +| 2026-02-25 12:11:44 | 🔄 卡若AI 同步 2026-02-25 12:11 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 13 个 | diff --git a/运营中枢/工作台/代码管理.md b/运营中枢/工作台/代码管理.md index 2ea75d14..7092096d 100644 --- a/运营中枢/工作台/代码管理.md +++ b/运营中枢/工作台/代码管理.md @@ -145,3 +145,4 @@ | 2026-02-25 11:52:39 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 11:52 | 更新:水溪整理归档、运营中枢、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-25 12:07:57 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 12:07 | 更新:Cursor规则、水桥平台对接、运营中枢、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-25 12:09:29 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 12:09 | 更新:运营中枢、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | +| 2026-02-25 12:11:44 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 12:11 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |