import os
import json
import requests
import sys
from typing import Dict, Any, List, Tuple, Set, Optional
import urllib.parse
import time
import datetime
import re
from dotenv import load_dotenv
from collections import defaultdict
# === 辅助工具 ===
class Timer:
def __init__(self): self._start = None
def start(self): self._start = time.perf_counter()
def stop(self, msg=""):
if self._start: print(f" - [计时] {msg}耗时: {time.perf_counter() - self._start:.4f}s"); self._start = None
# --- 加载配置 ---
try: script_dir = os.path.dirname(os.path.realpath(__file__))
except: script_dir = os.path.abspath(os.getcwd())
dotenv_path = os.path.join(script_dir, '.env')
load_dotenv(dotenv_path) if os.path.exists(dotenv_path) else None
APP_ID = os.getenv("APP_ID")
APP_SECRET = os.getenv("APP_SECRET")
SPREADSHEET_TOKEN = os.getenv("SPREADSHEET_TOKEN")
REDIRECT_URI = os.getenv("REDIRECT_URI", "http://localhost:8080/auth")
TOKEN_FILE = "feishu_token.json"
# 列配置
ATTR_COL = os.getenv("ATTRIBUTE_COL", "B") # (新) 属性列
NAME_COL = os.getenv("MATERIAL_NAME_COL", "A") # 物料名称
S1_CODE_COL = os.getenv("SUPPLIER_1_CODE_COL", "C")
S1_SUP_COL = os.getenv("SUPPLIER_1_SUPPLIER_COL", "D")
S2_CODE_COL = os.getenv("SUPPLIER_2_CODE_COL", "E")
S2_SUP_COL = os.getenv("SUPPLIER_2_SUPPLIER_COL", "F")
START_ROW = int(os.getenv("START_ROW", "2"))
END_ROW = int(os.getenv("END_ROW", "500"))
SUMMARY_SHEET_NAME = os.getenv("SUMMARY_SHEET_NAME", "零部件比对汇总(新)")
# === 1. 认证模块 (保持不变) ===
def save_token(data):
try:
with open(os.path.join(script_dir, TOKEN_FILE), 'w') as f: json.dump(data, f, indent=4)
except Exception as e: print(f"Error saving token: {e}")
def load_token():
path = os.path.join(script_dir, TOKEN_FILE)
if not os.path.exists(path): return {}
try:
with open(path, 'r') as f:
d = json.load(f)
if 'expires_at' in d: d['expires_at'] = datetime.datetime.fromisoformat(d['expires_at'])
return d
except: return {}
def get_access_token():
data = load_token()
if data.get("access_token") and data.get("expires_at", datetime.datetime.min) > datetime.datetime.now():
return data["access_token"]
if data.get("refresh_token"):
try:
url = "https://open.feishu.cn/open-apis/authen/v2/oauth/token"
resp = requests.post(url, json={"grant_type": "refresh_token", "client_id": APP_ID, "client_secret": APP_SECRET, "refresh_token": data["refresh_token"]})
resp.raise_for_status()
res = resp.json()
if res.get("code") == 0:
new_data = {"access_token": res["access_token"], "refresh_token": res["refresh_token"],
"expires_at": (datetime.datetime.now() + datetime.timedelta(seconds=res["expires_in"]-300)).isoformat()}
save_token({**new_data, "expires_at": new_data["expires_at"]})
return res["access_token"]
except: pass
scope = "sheets:spreadsheet sheets:spreadsheet:read sheets:spreadsheet:write_only sheets:spreadsheet.meta:read sheets:spreadsheet.meta:write"
url = f"https://accounts.feishu.cn/open-apis/authen/v1/authorize?client_id={APP_ID}&response_type=code&redirect_uri={urllib.parse.quote(REDIRECT_URI)}&scope={scope}"
print(f"请打开链接授权:\n{url}")
code = input("请输入Code: ").strip()
resp = requests.post("https://open.feishu.cn/open-apis/authen/v2/oauth/token", json={
"grant_type": "authorization_code", "client_id": APP_ID, "client_secret": APP_SECRET, "code": code, "redirect_uri": REDIRECT_URI})
res = resp.json()
if res.get("code") != 0: raise Exception(f"Auth failed: {res}")
token_data = {"access_token": res["access_token"], "refresh_token": res.get("refresh_token"),
"expires_at": (datetime.datetime.now() + datetime.timedelta(seconds=res["expires_in"]-300)).isoformat()}
save_token(token_data)
return res["access_token"]
# === 2. 飞书 API 基础操作 ===
def call_api(method, url, json_data=None, params=None):
token = get_access_token()
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json; charset=utf-8"}
# 添加调试日志
print(f" - [API 调用] {method} {url}")
resp = requests.request(method, url, headers=headers, json=json_data, params=params)
try:
resp.raise_for_status()
except requests.exceptions.HTTPError as e:
# 打印更详细的错误
print(f"API 请求失败: {e}")
if e.response is not None:
try:
print(f"错误详情: {e.response.json()}")
except json.JSONDecodeError:
print(f"错误详情 (text): {e.response.text}")
raise e # 重新抛出异常
res = resp.json()
if res.get("code", 0) != 0:
print(f"API 业务错误: {res}")
raise Exception(f"API Error: {res}")
return res
def get_sheet_id_by_name(sp_token, name):
res = call_api("GET", f"https://open.feishu.cn/open-apis/sheets/v3/spreadsheets/{sp_token}/sheets/query")
for sheet in res["data"]["sheets"]:
if sheet["title"] == name: return sheet["sheet_id"]
return None
def create_sheet(sp_token, name):
exist = get_sheet_id_by_name(sp_token, name)
if exist: return exist
# create_sheet 仍然使用 .../sheets_batch_update (这没错, V2的批量更新)
res = call_api("POST", f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{sp_token}/sheets_batch_update",
json_data={"requests": [{"addSheet": {"properties": {"title": name}}}]})
return res["data"]["replies"][0]["addSheet"]["properties"]["sheetId"]
def read_range(sp_token, sheet_id, col, s_row, e_row):
rng = f"{sheet_id}!{col}{s_row}:{col}{e_row}"
# read_range 使用 .../values_batch_get (这也没错, V2的读取)
res = call_api("GET", f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{sp_token}/values_batch_get", params={"ranges": rng})
vals = res["data"]["valueRanges"][0]["values"]
result = []
for i, row in enumerate(vals):
val = ""
if row:
cell = row[0]
if isinstance(cell, dict): val = cell.get("text", "")
else: val = str(cell)
result.append({"row": s_row + i, "val": val.strip()})
return result
# === 3. 工具函数:(保持不变) ===
def _col_str_to_idx(col_str: str) -> int:
idx = 0
for char in col_str:
idx = idx * 26 + (ord(char) - ord('A')) + 1
return idx - 1
def num_to_col_char(n):
string = ""
while n > 0:
n, remainder = divmod(n - 1, 26)
string = chr(65 + remainder) + string
return string
ProjectData = Dict[str, Dict[str, Tuple[str, str]]] # This type is unused, but fine to leave
def fetch_all_data(sp_token, sheets) -> Dict[str, Dict[str, Any]]: # ★ 优化: 添加返回类型
# ★ 优化: 为 aligned_data 添加明确的类型提示
aligned_data: Dict[str, Dict[str, Any]] = defaultdict(lambda: {"attr": "", "projects": {}})
for sheet in sheets:
title, sid = sheet["title"], sheet["sheet_id"]
print(f" - 读取项目: {title} ...")
names = read_range(sp_token, sid, NAME_COL, START_ROW, END_ROW)
attrs = read_range(sp_token, sid, ATTR_COL, START_ROW, END_ROW)
s1_codes = read_range(sp_token, sid, S1_CODE_COL, START_ROW, END_ROW)
s1_sups = read_range(sp_token, sid, S1_SUP_COL, START_ROW, END_ROW)
s2_codes = read_range(sp_token, sid, S2_CODE_COL, START_ROW, END_ROW)
s2_sups = read_range(sp_token, sid, S2_SUP_COL, START_ROW, END_ROW)
for i in range(len(names)):
name = names[i]["val"]
if not name: continue
attr = attrs[i]["val"] if i < len(attrs) else ""
s1_c = s1_codes[i]["val"] if i < len(s1_codes) else ""
s1_s = s1_sups[i]["val"] if i < len(s1_sups) else ""
s2_c = s2_codes[i]["val"] if i < len(s2_codes) else ""
s2_s = s2_sups[i]["val"] if i < len(s2_sups) else ""
if not aligned_data[name]["attr"] and attr:
aligned_data[name]["attr"] = attr
aligned_data[name]["projects"][title] = {
"s1": (s1_s, s1_c), # (供应商, 料号)
"s2": (s2_s, s2_c)
}
return aligned_data
def main():
print("=== 开始执行:多项目零部件比对与样式生成 ===")
# 1. 获取所有 Sheet
all_sheets_meta = call_api("GET", f"https://open.feishu.cn/open-apis/sheets/v3/spreadsheets/{SPREADSHEET_TOKEN}/sheets/query")["data"]["sheets"]
valid_sheets = [s for s in all_sheets_meta if s["title"] != SUMMARY_SHEET_NAME]
print("发现以下项目:")
for i, s in enumerate(valid_sheets): print(f" [{i+1}] {s['title']}")
sel_idx = input("请输入要比对的项目序号 (如 1,2,3 或 all): ").strip()
if sel_idx.lower() == "all": selected = valid_sheets
else: selected = [valid_sheets[int(x)-1] for x in sel_idx.split(",") if x.strip().isdigit()]
if len(selected) < 2: print("至少选择两个项目!"); return
proj_names = [s["title"] for s in selected]
# 2. 读取并对齐数据
timer = Timer()
timer.start()
data_map = fetch_all_data(SPREADSHEET_TOKEN, selected)
timer.stop("数据读取与对齐")
# 3. 准备写入数据矩阵
sorted_names = sorted(data_map.keys())
matrix = []
# --- 构建表头行 ---
row1 = ["项目", "属性"]
for p in proj_names:
row1.extend([p, "", "", ""])
matrix.append(row1)
row2 = ["", ""]
for _ in proj_names:
row2.extend(["一供", "", "二供", ""])
matrix.append(row2)
row3 = ["物料名称", ""]
for _ in proj_names:
row3.extend(["供应商", "物料料号", "供应商", "物料料号"])
matrix.append(row3)
# --- 构建数据行与样式计算 ---
COLOR_GREEN = "#b7e1cd"
COLOR_RED = "#f4c7c3"
# WELCOME_MSG_ROW_IDX = 0 <-- ★ 修复: 删除这一行 (第 278 行), 它的缩进错误导致了 "main" 函数中断
green_cells = []
red_cells = []
current_row_idx = 4
for name in sorted_names:
item = data_map[name]
attr = item["attr"]
row_data = [name, attr]
pair_counts = defaultdict(int)
for p_name in proj_names:
p_data = item["projects"].get(p_name, {"s1":("",""), "s2":("","")})
s1 = p_data["s1"]
s2 = p_data["s2"]
if s1[0] and s1[1]: pair_counts[s1] += 1
if s2[0] and s2[1]: pair_counts[s2] += 1
col_idx = 3
for p_name in proj_names:
p_data = item["projects"].get(p_name, {"s1":("",""), "s2":("","")})
s1 = p_data["s1"]
s2 = p_data["s2"]
row_data.extend([s1[0], s1[1]])
if s1[0] and s1[1]:
cell_sup = f"{num_to_col_char(col_idx)}{current_row_idx}"
cell_code = f"{num_to_col_char(col_idx+1)}{current_row_idx}"
if pair_counts[s1] > 1:
green_cells.extend([cell_sup, cell_code])
else:
red_cells.extend([cell_sup, cell_code])
col_idx += 2
row_data.extend([s2[0], s2[1]])
if s2[0] and s2[1]:
cell_sup = f"{num_to_col_char(col_idx)}{current_row_idx}"
cell_code = f"{num_to_col_char(col_idx+1)}{current_row_idx}"
if pair_counts[s2] > 1:
green_cells.extend([cell_sup, cell_code])
else:
red_cells.extend([cell_sup, cell_code])
col_idx += 2
matrix.append(row_data)
current_row_idx += 1
# 4. 写入表格
timer.start()
sum_sid = create_sheet(SPREADSHEET_TOKEN, SUMMARY_SHEET_NAME)
print(f"目标Sheet: {SUMMARY_SHEET_NAME} (ID: {sum_sid})")
if matrix and len(matrix) > 0:
num_rows = len(matrix)
num_cols = len(matrix[0])
end_col_letter = num_to_col_char(num_cols)
full_range = f"{sum_sid}!A1:{end_col_letter}{num_rows}"
print(f" - 正在写入范围: {full_range} ...")
call_api("PUT", f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values",
json_data={"valueRange": {"range": full_range, "values": matrix}})
else:
print(" - 警告: 没有数据需要写入。")
print(" - 数据写入完成")
# 5. 准备合并请求
print(" - 正在准备单元格合并 (表头)...")
merge_list = []
merge_list.append(f"{sum_sid}!A1:A2") # 项目 (FIX: A1:A2)
merge_list.append(f"{sum_sid}!B1:B3") # 属性
current_col = 3 # C列
for _ in proj_names:
start = num_to_col_char(current_col)
end = num_to_col_char(current_col + 3)
merge_list.append(f"{sum_sid}!{start}1:{end}1") # Proj Name
s1_start = num_to_col_char(current_col)
s1_end = num_to_col_char(current_col + 1)
merge_list.append(f"{sum_sid}!{s1_start}2:{s1_end}2") # 一供
s2_start = num_to_col_char(current_col + 2)
s2_end = num_to_col_char(current_col + 3)
merge_list.append(f"{sum_sid}!{s2_start}2:{s2_end}2") # 二供
current_col += 4
# ★★★ 使用专用的 .../merge_cells API (V2, 这个没问题) ★★★
API_V2_MERGE_URL = f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/merge_cells"
if merge_list:
print(f" - 正在批量执行 {len(merge_list)} 个合并请求...")
# 这个接口不是批量的,我们必须循环调用
for rng_str in merge_list:
payload = {
"range": rng_str, # A1 字符串格式,带 sheetId
"mergeType": "MERGE_ALL"
}
try:
call_api("POST", API_V2_MERGE_URL, json_data=payload)
except Exception as e:
print(f"WARN: 合并 {rng_str} 失败: {e}")
print(" - 表头合并完成")
# 6. 准备上色请求
print(f" - 正在准备应用样式 (红: {len(red_cells)/2}组, 绿: {len(green_cells)/2}组)...")
# ★★★ 核心修复: 采用 V4 最终方案 ★★★
# 1. URL: 使用 V4 端点 ( .../styles/batch_update )
API_V4_STYLE_URL = f"https://open.feishu.cn/open-apis/sheets/v4/spreadsheets/{SPREADSHEET_TOKEN}/styles/batch_update"
# 2. Payload: 构造 V4 'data' 列表
style_data_list = []
# 红色单元格
if red_cells:
# 3. Range 格式: "sheetId!A1,B2,C3" (逗号分隔的单一字符串)
red_range_string = f"{sum_sid}!{','.join(red_cells)}"
style_data_list.append({
"range": red_range_string,
"style": {"back_color": COLOR_RED} # 4. Style Key: back_color (snake_case)
})
# 绿色单元格
if green_cells:
# 3. Range 格式: "sheetId!A1,B2,C3"
green_range_string = f"{sum_sid}!{','.join(green_cells)}"
style_data_list.append({
"range": green_range_string,
"style": {"back_color": COLOR_GREEN} # 4. Style Key: back_color (snake_case)
})
if style_data_list:
# 5. Payload: 最终 V4 payload 结构
payload = {"data": style_data_list}
print(f" - 正在提交 {len(style_data_list)} 组样式更新请求 (V4接口)...")
if(len(json.dumps(payload)) > 5000):
print(" - [调试] Payload 过长,仅显示前 5000 字符: " + json.dumps(payload)[:5000] + "...")
else:
print(f" - [调试] Payload: {json.dumps(payload)}")
try:
# 6. Call: 调用 V4 API
call_api("POST", API_V4_STYLE_URL, json_data=payload)
print(" - 样式更新成功")
except Exception as e:
# call_api 函数现在会打印详细错误
print(f" - 样式更新失败: {e}")
else:
print(" - 没有样式需要更新")
timer.stop("写入、合并与上色")
print("任务完成!请查看飞书文档。")
if __name__ == "__main__":
try: main()
except Exception as e:
import traceback
traceback.print_exc()
print(f"Error: {e}")
现在代码如上,报错如下:
请输入要比对的项目序号 (如 1,2,3 或 all): 7,8,12
- 读取项目: R510C超薄 ...
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- 读取项目: R520C半包 ...
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- 读取项目: R500C ...
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values_batch_get
- [计时] 数据读取与对齐耗时: 5.0121s
- [API 调用] GET https://open.feishu.cn/open-apis/sheets/v3/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/sheets/query
- [API 调用] POST https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/sheets_batch_update
目标Sheet: R500C&R510超薄&R520半包物料差异比对-2 (ID: IZtjq)
- 正在写入范围: IZtjq!A1:N72 ...
- [API 调用] PUT https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/values
- 数据写入完成
- 正在准备单元格合并 (表头)...
- 正在批量执行 11 个合并请求...
- [API 调用] POST https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/merge_cells
- [API 调用] POST https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/merge_cells
- [API 调用] POST https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/merge_cells
- [API 调用] POST https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/merge_cells
- [API 调用] POST https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/merge_cells
- [API 调用] POST https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/merge_cells
- [API 调用] POST https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/merge_cells
- [API 调用] POST https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/merge_cells
- [API 调用] POST https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/merge_cells
- [API 调用] POST https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/merge_cells
- [API 调用] POST https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/merge_cells
- 表头合并完成
- 正在准备应用样式 (红: 87.0组, 绿: 227.0组)...
- 正在提交 2 组样式更新请求 (V4接口)...
- [调试] Payload: {"data": [{"range": "IZtjq!K4,L4,M4,N4,K6,L6,M6,N6,C7,D7,E7,F7,G11,H11,I11,J11,C16,D16,E16,F16,G16,H16,I16,J16,G20,H20,I20,J20,C22,D22,E22,F22,C28,D28,G28,H28,K30,L30,M30,N30,C31,D31,E31,F31,K31,L31,M31,N31,E32,F32,M32,N32,C33,D33,E33,F33,C34,D34,E34,F34,K35,L35,M35,N35,C36,D36,E36,F36,C40,D40,E40,F40,K41,L41,M41,N41,G43,H43,I43,J43,K44,L44,M44,N44,C47,D47,G47,H47,K47,L47,M47,N47,K49,L49,M49,N49,C50,D50,E50,F50,C52,D52,E52,F52,G52,H52,K52,L52,K53,L53,M53,N53,C60,D60,E60,F60,K62,L62,M62,N62,C63,D63,E63,F63,K63,L63,M63,N63,C64,D64,E64,F64,G64,H64,I64,J64,C65,D65,E65,F65,G65,H65,I65,J65,K65,L65,M65,N65,C66,D66,E66,F66,C67,D67,E67,F67,C68,D68,E68,F68,G69,H69,I69,J69,G70,H70,C72,D72,G72,H72,I72,J72,K72,L72", "style": {"back_color": "#f4c7c3"}}, {"range": "IZtjq!C5,D5,E5,F5,G5,H5,I5,J5,K5,L5,M5,N5,C8,D8,E8,F8,G8,H8,I8,J8,K8,L8,M8,N8,C9,D9,E9,F9,G9,H9,I9,J9,K9,L9,M9,N9,C10,D10,E10,F10,G10,H10,I10,J10,K10,L10,M10,N10,C11,D11,E11,F11,K11,L11,M11,N11,C12,D12,E12,F12,G12,H12,I12,J12,K12,L12,M12,N12,C13,D13,E13,F13,G13,H13,I13,J13,K13,L13,M13,N13,C14,D14,E14,F14,G14,H14,I14,J14,K14,L14,M14,N14,G15,H15,I15,J15,K15,L15,M15,N15,C17,D17,E17,F17,G17,H17,I17,J17,K17,L17,M17,N17,C18,D18,E18,F18,G18,H18,I18,J18,K18,L18,M18,N18,C19,D19,E19,F19,G19,H19,I19,J19,K19,L19,M19,N19,C20,D20,E20,F20,K20,L20,M20,N20,C21,D21,E21,F21,G21,H21,I21,J21,K21,L21,M21,N21,C23,D23,E23,F23,G23,H23,I23,J23,K23,L23,M23,N23,C24,D24,E24,F24,G24,H24,I24,J24,K24,L24,M24,N24,C25,D25,E25,F25,G25,H25,I25,J25,K25,L25,M25,N25,C26,D26,E26,F26,G26,H26,I26,J26,K26,L26,M26,N26,C27,D27,E27,F27,G27,H27,I27,J27,K27,L27,M27,N27,E28,F28,I28,J28,C29,D29,E29,F29,G29,H29,I29,J29,K29,L29,M29,N29,C32,D32,K32,L32,G34,H34,I34,J34,C37,D37,E37,F37,G37,H37,I37,J37,K37,L37,M37,N37,C38,D38,E38,F38,G38,H38,I38,J38,K38,L38,M38,N38,C39,D39,E39,F39,G39,H39,I39,J39,K39,L39,M39,N39,C42,D42,E42,F42,G42,H42,I42,J42,K42,L42,M42,N42,C45,D45,E45,F45,G45,H45,I45,J45,K45,L45,M45,N45,C46,D46,E46,F46,G46,H46,I46,J46,K46,L46,M46,N46,E47,F47,I47,J47,C48,D48,E48,F48,G48,H48,I48,J48,K48,L48,M48,N48,C51,D51,E51,F51,G51,H51,I51,J51,K51,L51,M51,N51,I52,J52,M52,N52,C54,D54,E54,F54,G54,H54,I54,J54,K54,L54,M54,N54,C55,D55,E55,F55,G55,H55,I55,J55,K55,L55,M55,N55,C56,D56,E56,F56,G56,H56,I56,J56,K56,L56,M56,N56,C57,D57,E57,F57,G57,H57,I57,J57,K57,L57,M57,N57,C58,D58,E58,F58,G58,H58,I58,J58,K58,L58,M58,N58,C59,D59,E59,F59,G59,H59,I59,J59,K59,L59,M59,N59,C61,D61,E61,F61,G61,H61,I61,J61,K61,L61,M61,N61,C70,D70,E70,F70,I70,J70,K70,L70,M70,N70,C71,D71,E71,F71,G71,H71,I71,J71,K71,L71,M71,N71,E72,F72,M72,N72", "style": {"back_color": "#b7e1cd"}}]}
- [API 调用] POST https://open.feishu.cn/open-apis/sheets/v4/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/styles/batch_update
API 请求失败: 404 Client Error: Not Found for url: https://open.feishu.cn/open-apis/sheets/v4/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/styles/batch_update
错误详情 (text): 404 page not found
- 样式更新失败: 404 Client Error: Not Found for url: https://open.feishu.cn/open-apis/sheets/v4/spreadsheets/EYRysBZWkhbA4vts3yLcwitNn1g/styles/batch_update
- [计时] 写入、合并与上色耗时: 6.6437s
任务完成!请查看飞书文档。
进程已结束,退出代码为 0