#!/usr/bin/env python3 """查询小龙名下 4/21-5/20 落单的订单详情(含手机号和用户ID)""" import json, urllib.request, re, sys APP_ID = 'cli_a92fc074fb5edcb5' APP_SECRET = 'jzQ8UoNb06rX8147V52icdWF7XN8Su2K' SPREADSHEET_TOKEN = 'ERCFsFo4MhnF0ytGeCrc0Bb8n5f' SHEET_FILL = '1RFMqc' # 全量销售填表(含手机号/用户ID) SHEET_ORDER = 'e12fb9' # 订单汇总(仅成单字段) def get_token(): url = 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal' data = json.dumps({'app_id': APP_ID, 'app_secret': APP_SECRET}).encode() req = urllib.request.Request(url, data=data, headers={'Content-Type': 'application/json'}) with urllib.request.urlopen(req) as resp: return json.loads(resp.read())['tenant_access_token'] def read_sheet(token, sheet_id, cols_range): url = f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values/{sheet_id}!{cols_range}?valueRenderOption=ToString&dateTimeRenderOption=FormattedString' req = urllib.request.Request(url, headers={ 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json', }) with urllib.request.urlopen(req) as resp: result = json.loads(resp.read()) if result.get('code') != 0: return None return result['data'].get('valueRange', {}).get('values', []) def parse_date(s): if not s: return None s = str(s).strip() m = re.match(r'(\d+)月(\d+)日', s) if m: return (int(m.group(1)), int(m.group(2))) m = re.match(r'(\d{4})[-/](\d{1,2})[-/](\d{1,2})', s) if m: return (int(m.group(2)), int(m.group(3))) return None START, END = (4, 21), (5, 20) token = get_token() # Step 1: Read order summary to get the list of matched orders (for row ordering) print("Step 1: Reading order summary...", file=sys.stderr) order_rows = read_sheet(token, SHEET_ORDER, 'A:N') matched_orders = [] for i, row in enumerate(order_rows): if len(row) < 3: continue owner = str(row[0]).strip() if row[0] else '' if owner != '小龙': continue d = parse_date(row[2]) if d and START <= d <= END: vals = [str(c) if c else '' for c in row] while len(vals) < 14: vals.append('') matched_orders.append({ 'row': i+1, 'nickname': vals[3], 'order_date': vals[2], 'vals': vals }) print(f"Orders: {len(matched_orders)}", file=sys.stderr) # Step 2: Read full fill sheet for 小龙 to get phone and UID print("Step 2: Reading fill sheet for phones/UIDs...", file=sys.stderr) fill_rows = read_sheet(token, SHEET_FILL, 'A:M') # A-M: 销售归属~退款金额 # Build lookup: (nickname, order_date) -> (phone, uid) fill_lookup = {} for row in fill_rows: if len(row) < 3: continue owner = str(row[0]).strip() if row[0] else '' if owner != '小龙': continue nickname = str(row[3]).strip() if len(row) > 3 and row[3] else '' order_date = str(row[2]).strip() if row[2] else '' phone = str(row[4]).strip() if len(row) > 4 and row[4] else '' uid = str(row[8]).strip() if len(row) > 8 and row[8] else '' key = (nickname, order_date) if key[1] and (key[1] not in fill_lookup or (phone or uid)): fill_lookup[key] = (phone, uid) # Step 3: Merge and output print("Step 3: Merging...", file=sys.stderr) # Stats total_revenue = 0 valid_count = 0 print(f"## 小龙名下订单详情(落单:2026-04-21 ~ 2026-05-20)\n") print(f"共 {len(matched_orders)} 条 · 数据来源:销转客户主表\n") for idx, o in enumerate(matched_orders, 1): v = o['vals'] key = (o['nickname'], o['order_date']) phone, uid = fill_lookup.get(key, ('', '')) amount = float(v[6]) if v[6] else 0 is_valid = "✅" if v[11] == '1' else ("⚠️退" if v[11] == '0' else v[11]) total_revenue += amount if v[11] == '1': valid_count += 1 phone_str = f"📱{phone}" if phone else "📱—" uid_str = f"🆔{uid}" if uid else "🆔—" amt_str = f"{int(float(v[6])):,}" if v[6] else "0" print(f"{idx}. {v[3]} {phone_str} {uid_str}") print(f" 进线 {v[1]} 落单 {v[2]} {v[5]} ¥{amt_str} {v[4]} · {v[9]} {is_valid}") print() print(f"**汇总:{len(matched_orders)} 单 | 有效 {valid_count} 单 | 总额 ¥{total_revenue:,.0f}**")