196 lines
8.8 KiB
Python
196 lines
8.8 KiB
Python
#!/usr/bin/env python3
|
||
"""迁移旧销售线索表 → 新表"""
|
||
import openpyxl, json, requests, os, sys
|
||
|
||
OLD_FILE = "/root/.openclaw/media/inbound/é_è½_å_æ_ä_è_-ç_ä_ç_ï¼_è_æ_ï¼_å_溪å_å_ç---7af347cb-3646-4ebc-97a4-a70b9165c363.xlsx"
|
||
SPREADSHEET_TOKEN = "NoZqsFi47hIOHEt9j8WcfRtbnug"
|
||
SHEET_IDS = {"吴迪": "f975f0", "小龙": "qJF4I", "成都": "qJF4J"}
|
||
CRED_DIR = "/root/.openclaw/credentials/xiaoxi"
|
||
|
||
def get_fs_token():
|
||
with open(os.path.join(CRED_DIR, "config.json")) as f:
|
||
cfg = json.load(f)
|
||
resp = requests.post(
|
||
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
|
||
json={"app_id": cfg["apps"][0]["appId"], "app_secret": cfg["apps"][0]["appSecret"]},
|
||
timeout=15
|
||
)
|
||
return resp.json()["tenant_access_token"]
|
||
|
||
def put_values(token, sheet_id, range_str, values):
|
||
url = f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values"
|
||
body = {"valueRange": {"range": f"{sheet_id}!{range_str}", "values": values}}
|
||
resp = requests.put(url, headers={
|
||
"Authorization": f"Bearer {token}",
|
||
"Content-Type": "application/json"
|
||
}, json=body, timeout=30)
|
||
r = resp.json()
|
||
if r.get("code") != 0:
|
||
print(f" 写入失败 {range_str}: {r}")
|
||
return r
|
||
|
||
def safe_str(v):
|
||
if v is None:
|
||
return ""
|
||
s = str(v).strip()
|
||
# 处理公式残留
|
||
if s.startswith("=") or s.startswith("IF("):
|
||
return ""
|
||
return s
|
||
|
||
def main():
|
||
wb = openpyxl.load_workbook(OLD_FILE, data_only=True)
|
||
token = get_fs_token()
|
||
|
||
for sheet_name in ["吴迪", "小龙", "成都"]:
|
||
if sheet_name not in wb.sheetnames:
|
||
print(f"⚠️ 未找到 sheet: {sheet_name}")
|
||
continue
|
||
ws = wb[sheet_name]
|
||
sheet_id = SHEET_IDS[sheet_name]
|
||
print(f"\n{'='*40}\n迁移 [{sheet_name}] → {sheet_id}")
|
||
|
||
# 收集有效数据行(从第4行开始,跳过表头/标注/说明)
|
||
rows_out = []
|
||
for row_idx in range(4, ws.max_row + 1):
|
||
a = safe_str(ws.cell(row=row_idx, column=1).value) # 销售归属
|
||
b = safe_str(ws.cell(row=row_idx, column=2).value) # 微信昵称
|
||
c = safe_str(ws.cell(row=row_idx, column=3).value) # 进线日期
|
||
e = safe_str(ws.cell(row=row_idx, column=5).value) # 手机号
|
||
if not a and not b and not e:
|
||
continue # 空行跳过
|
||
|
||
# 旧列 → 新列映射
|
||
# A:销售归属 B:微信昵称 C:进线日期 D:体验节数 E:手机号 F:用户年级 G:课史/跟进
|
||
d_old = safe_str(ws.cell(row=row_idx, column=4).value) # 体验节数(小溪)
|
||
f = safe_str(ws.cell(row=row_idx, column=6).value) # 用户年级
|
||
g = safe_str(ws.cell(row=row_idx, column=7).value) # 课史/跟进
|
||
h_old = safe_str(ws.cell(row=row_idx, column=8).value) # 用户ID(选填)
|
||
w_col = safe_str(ws.cell(row=row_idx, column=23).value) # 匹配uid
|
||
|
||
# H:用户ID — 优先用匹配uid(W列),其次用户ID(H列)
|
||
uid = w_col if w_col else h_old
|
||
|
||
# 下单信息
|
||
i_old = safe_str(ws.cell(row=row_idx, column=9).value) # 下单日期 → L
|
||
j_old = safe_str(ws.cell(row=row_idx, column=10).value) # 成交渠道 → M
|
||
k_old = safe_str(ws.cell(row=row_idx, column=11).value) # 产品 → N
|
||
l_old = safe_str(ws.cell(row=row_idx, column=12).value) # 实付金额 → O
|
||
m_old = safe_str(ws.cell(row=row_idx, column=13).value) # 退款金额 → P
|
||
|
||
# 行课信息
|
||
n_old = safe_str(ws.cell(row=row_idx, column=14).value) # 行课状态(自动) → S
|
||
o_old = safe_str(ws.cell(row=row_idx, column=15).value) # 最近行课 → T
|
||
|
||
# 同步时间
|
||
y_old = safe_str(ws.cell(row=row_idx, column=25).value) # 同步时间 → V
|
||
|
||
# 构建新行 (22列 A-V)
|
||
new_row = [
|
||
a, # A: 销售归属
|
||
b, # B: 微信昵称
|
||
c, # C: 进线日期
|
||
d_old, # D: 体验节数 (保留旧值)
|
||
e, # E: 手机号
|
||
f, # F: 用户年级
|
||
g, # G: 课史/跟进
|
||
uid, # H: 用户ID
|
||
"", # I: 注册日期 (自动回填)
|
||
"", # J: 下载渠道 (自动回填)
|
||
"", # K: 是否下单 (自动回填)
|
||
i_old, # L: 下单日期
|
||
j_old, # M: 成交渠道
|
||
k_old, # N: 产品
|
||
l_old, # O: 下单金额(GMV)
|
||
m_old, # P: 退款金额
|
||
"", # Q: 实际收入(GSV) (自动回填)
|
||
"", # R: 激活课程 (自动回填)
|
||
n_old, # S: 当前行课进度 (旧行课状态)
|
||
o_old, # T: 最近行课时间
|
||
"", # U: 累计学习时长 (自动回填)
|
||
y_old, # V: 更新时间
|
||
]
|
||
rows_out.append(new_row)
|
||
|
||
print(f" 有效数据: {len(rows_out)} 行")
|
||
|
||
if not rows_out:
|
||
continue
|
||
|
||
# 分批写入(飞书API限制,每批最多写一定行数)
|
||
BATCH = 50
|
||
for batch_start in range(0, len(rows_out), BATCH):
|
||
batch = rows_out[batch_start:batch_start + BATCH]
|
||
start_row = 3 + batch_start # 从第3行开始(跳过表头和标注行)
|
||
end_row = start_row + len(batch) - 1
|
||
range_str = f"A{start_row}:V{end_row}"
|
||
put_values(token, sheet_id, range_str, batch)
|
||
print(f" 写入 {range_str} ({len(batch)} 行)")
|
||
|
||
# ── 订单汇总 sheet ──
|
||
if "订单汇总" in wb.sheetnames:
|
||
print(f"\n{'='*40}\n迁移 [订单汇总]")
|
||
ws = wb["订单汇总"]
|
||
rows_out = []
|
||
for row_idx in range(4, ws.max_row + 1):
|
||
a = safe_str(ws.cell(row=row_idx, column=1).value) # 销售归属
|
||
b = safe_str(ws.cell(row=row_idx, column=2).value) # 进线日期
|
||
c = safe_str(ws.cell(row=row_idx, column=3).value) # 下单日期
|
||
d = safe_str(ws.cell(row=row_idx, column=4).value) # 微信昵称
|
||
if not a and not d:
|
||
continue
|
||
|
||
e = safe_str(ws.cell(row=row_idx, column=5).value) # 成交渠道
|
||
f = safe_str(ws.cell(row=row_idx, column=6).value) # 产品
|
||
g = safe_str(ws.cell(row=row_idx, column=7).value) # 实付金额
|
||
h = safe_str(ws.cell(row=row_idx, column=8).value) # 退款金额
|
||
i = safe_str(ws.cell(row=row_idx, column=9).value) # 落单渠道
|
||
j = safe_str(ws.cell(row=row_idx, column=10).value) # 渠道归属
|
||
k = safe_str(ws.cell(row=row_idx, column=11).value) # 订单状态
|
||
l = safe_str(ws.cell(row=row_idx, column=12).value) # 有效成单
|
||
|
||
new_row = [a, b, c, d, e, f, g, h, i, j, k, l]
|
||
rows_out.append(new_row)
|
||
|
||
print(f" 有效数据: {len(rows_out)} 行")
|
||
|
||
if rows_out:
|
||
# 先创建订单汇总 sheet
|
||
# 先看看有没有这个sheet,没有就创建
|
||
token2 = get_fs_token()
|
||
# 创建新 sheet
|
||
resp = requests.post(
|
||
f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/sheets_batch_update",
|
||
headers={"Authorization": f"Bearer {token2}", "Content-Type": "application/json"},
|
||
json={"requests": [{"addSheet": {"properties": {"title": "订单汇总", "index": 3}}}]},
|
||
timeout=15
|
||
)
|
||
r = resp.json()
|
||
print(f" 创建订单汇总sheet: {r.get('code')}")
|
||
if r.get("code") == 0:
|
||
summary_sheet_id = r["data"]["replies"][0]["addSheet"]["properties"]["sheetId"]
|
||
else:
|
||
print(f" 创建失败: {r}")
|
||
wb.close()
|
||
return
|
||
|
||
# 写表头
|
||
headers = [["销售归属","进线日期","下单日期","微信昵称","成交渠道","产品","实付金额(¥)","退款金额","落单渠道","渠道归属","订单状态","有效成单"]]
|
||
put_values(token2, summary_sheet_id, "A1:L1", headers)
|
||
|
||
# 写数据
|
||
BATCH = 50
|
||
for batch_start in range(0, len(rows_out), BATCH):
|
||
batch = rows_out[batch_start:batch_start + BATCH]
|
||
start_row = 2 + batch_start
|
||
end_row = start_row + len(batch) - 1
|
||
range_str = f"A{start_row}:L{end_row}"
|
||
put_values(token2, summary_sheet_id, range_str, batch)
|
||
print(f" 写入 {range_str} ({len(batch)} 行)")
|
||
|
||
wb.close()
|
||
print("\n✅ 迁移完成!")
|
||
|
||
if __name__ == "__main__":
|
||
main()
|