ai_member_xiaoxi/scripts/check_gap_57_v3.py
2026-06-15 08:00:01 +08:00

198 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Check 57 gap orders - bulk approach"""
import subprocess, os, json, sys
sys.path.insert(0, '/root/.openclaw/workspace')
PGPASS = "LdfjdjL83h3h3^$&**YGG*"
DB = ["psql","-h","bj-postgres-16pob4sg.sql.tencentcdb.com","-p","28591","-U","ai_member","-d","vala_bi"]
ENV = {**os.environ, "PGPASSWORD": PGPASS}
def q(sql):
r = subprocess.run(DB + ["-c", sql], env=ENV, capture_output=True, text=True, timeout=15)
rows = []
for line in r.stdout.strip().split("\n")[2:-1]:
parts = [p.strip() for p in line.split("|")]
rows.append(parts)
return rows
# Gap data
gaps = [
(1,"3月","小龙","雪珂💗","3月6日",1999,"微信小店","","sales-adp-bj-jxl-0",False),
(2,"3月","小龙","Mars","3月7日",3598,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0",False),
(3,"3月","小龙","薇薇","3月",1999,"小红书直购","13520306626","newmedia-dianpu-xhs-0-0",True),
(4,"3月","小龙","Yeah^_^","3月20日",3598,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0",False),
(5,"3月","小龙","TutuTu","3月",1999,"小红书直购","18107332677","newmedia-dianpu-xhs-0-0",True),
(6,"3月","小龙","EFFIE","3月25日",3598,"微信小店","","newmedia-dianpu-xhs-0-0",False),
(7,"3月","Tom","JeanneLᴇᴇ🦄","3月4日",3598,"微信小店","","newmedia-dianpu-wxxd-0-0",False),
(8,"3月","Tom","薇妮姐","3月8日",599,"端内","","app-active-h5-0-0",False),
(9,"3月","Tom","","3月7日",599,"端内","","app-active-h5-0-0",False),
(10,"3月","Bob","Mogu","3月6日",599,"端内","","app-active-h5-0-0",False),
(11,"3月","Bob","L. 一颗夹星糖🌟","3月12日",3598,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0",False),
(12,"3月","Tom","阿雅呀","3月15日",3598,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0",False),
(13,"3月","Tom","Anna","3月4日",1999,"抖音","","newmedia-dianpu-douyin-0-0",True),
(14,"3月","Bob","Echo Liang","3月15日",1999,"微信小店","","sales-adp-cd-zjf-0",False),
(15,"3月","Tom","幼兒園高材生🍼","3月23日",3598,"微信小店","13055770067","sales-adp-cd-yy-0",False),
(16,"3月","Tom","Nancy","3月26日",599,"端内","","app-active-h5-0-0",False),
(17,"3月","Tom","依米","3月30日",3598,"微信小店","","sales-adp-cd-zjf-0",False),
(18,"3月","Bob","zhouyun","3月6日",1999,"小红书-学霸老王","13588706769","newmedia-daren-xhs-学霸老王讲真话-0",False),
(19,"3月","Tom","💗小超人棒棒哒🍭","3月29日",3598,"小红书-晚柠","18630368296","newmedia-daren-xhs-晚柠也是个妈妈了-0",False),
(20,"3月","Bob","鹿","3月14日",3598,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0",False),
(21,"4月","小龙","琳溪","4月19日",3598,"微信小店","","sales-adp-bj-jxl-0",False),
(22,"4月","小龙","小丽","4月26日",599,"端内","","app-active-h5-0-0",False),
(23,"4月","小龙","子曦","4月16日",3598,"晚柠-小红书","","newmedia-daren-xhs-晚柠也是个妈妈了-0",False),
(24,"4月","小龙","Gaᴗao","4月24日",3598,"微信小店","","sales-adp-bj-jxl-0",False),
(25,"4月","吴迪","ཚེ་རིང་མཚོ彩让措","2026/4/26",599,"端内","","app-active-h5-0-0",False),
(26,"4月","吴迪","莉筱雅","2026/4/27",1999,"晚柠-小红书","","newmedia-daren-xhs-晚柠也是个妈妈了-0",False),
(27,"4月","吴迪","爱吃巧克力","2026/4/24",1999,"微信小店","","sales-adp-bj-wd-0",False),
(28,"4月","吴迪","Aa~Jessie💝","2026/4/27",1999,"抖音","","newmedia-dianpu-douyin-0-0",True),
(29,"4月","吴迪","🐷","2026/4/26",3598,"宣儿妈妈-小红书","","sales-adp-cd-yy-0",False),
(30,"4月","吴迪","🐷","2026/4/26",3598,"宣儿妈妈-小红书","","sales-adp-cd-yy-0",False),
(31,"4月","Tom","静静是我🍃","4月8日",1999,"小红书-学霸老王","15975769851","partner-actives-0-0-0",False),
(32,"4月","Bob","胆大鬼","4月8日",3598,"小红书-学霸老王","15262255267","newmedia-daren-xhs-学霸老王讲真话-0",False),
(33,"4月","Tom","希Cissy-427","4月27日",3598,"微信小店","","sales-adp-cd-zjf-0",False),
(34,"4月","Tom","Kerry","4月9日",3598,"微信小店","18328334683","sales-adp-cd-yy-0",False),
(35,"4月","Tom","SHAN_Q_Q","4月1日",3598,"小红书-官店","","newmedia-dianpu-xhs-0-0",True),
(36,"5月","小龙","Sia","5月9日",599,"微信小店","","sales-adp-bj-jxl-0",False),
(37,"5月","吴迪","","2026/5/9",1999,"微信小店","","sales-adp-bj-wd-0",False),
(38,"5月","吴迪","^_^","2026/5/16",3598,"晚柠-小红书","","newmedia-daren-xhs-晚柠也是个妈妈了-0",False),
(39,"5月","吴迪","🌸白色铃兰🌸","2026/5/17",3598,"抖音","","newmedia-dianpu-douyin-0-0",True),
(40,"5月","吴迪","咔咔","2026/5/7",3598,"微信小店","","sales-adp-bj-wd-0",False),
(41,"5月","吴迪","梦马","2026/5/21",3598,"小红书","","newmedia-dianpu-xhs-0-0",False),
(42,"5月","吴迪","梦马","2026/5/21",3598,"小红书","","newmedia-dianpu-xhs-0-0",False),
(43,"5月","吴迪","小西瓜","2026/5/30",1999,"微信小店","","sales-adp-bj-wd-0",False),
(44,"5月","吴迪","","2026/5/9",1999,"微信小店","","sales-adp-bj-wd-0",False),
(45,"5月","Bob","Yuki-515","5月15日",3598,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0",False),
(46,"5月","Tom","冬-515","5月15日",1999,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0",False),
(47,"5月","Bob","微笑向暖","5月4日",1999,"微信小店","","sales-adp-bj-wd-0",False),
(48,"5月","Bob","cici-511","5月11日",1999,"微信小店","","sales-adp-cd-xsy-0",False),
(49,"5月","Tom","🐷-519","5月19日",1999,"小红书-宣儿妈妈","","newmedia-daren-xhs-宣儿妈妈%",False),
(50,"5月","Bob","妃-516","5月16日",1999,"微信小店","","sales-adp-cd-zjf-0",False),
(51,"5月","Tom","黄晔-516","5月16日",3598,"微信小店","","sales-adp-bj-wd-0",False),
(52,"5月","Tom","朵朵呀!","5月25日",1999,"微信小店","","sales-adp-cd-zjf-0",False),
(53,"5月","Tom","雷鸣-414","5月14日",599,"微信小店","","sales-adp-bj-wd-0",False),
(54,"5月","Tom","毛阿毛🐱-520","5月20日",1999,"小红书-官店","","newmedia-dianpu-xhs-0-0",True),
(55,"5月","Tom","____Miss_y-519","5月19日",599,"微信小店","","sales-adp-bj-wd-0",False),
(56,"5月","Tom","Mandy-526","5月26日",1999,"微信小店","","sales-adp-bj-wd-0",False),
(57,"5月","Bob","Yoki-529","5月29日",1999,"微信小店","","sales-adp-cd-zjf-0",False),
]
def parse_date(d):
d = d.replace("","").replace("","/").replace(" ","").strip()
if "/" in d:
parts = d.split("/")
if len(parts) >= 2 and parts[1]:
return f"2026-{int(parts[0]):02d}-{int(parts[1]):02d}"
return None
# Step 1: Encrypt all phones
from scripts.phone_encrypt import encrypt_phone
phone_to_enc = {}
for g in gaps:
if g[7]:
phone_to_enc[g[7]] = encrypt_phone(g[7])
# Step 2: Look up all accounts by encrypted phone
enc_to_acct = {}
if phone_to_enc:
conds = " OR ".join([f"tel_encrypt LIKE '{v}%'" for v in phone_to_enc.values()])
rows = q(f"SELECT id, tel, tel_encrypt FROM bi_vala_app_account WHERE status=1 AND ({conds})")
for r in rows:
if len(r) >= 3:
enc_to_acct[r[2]] = r[0]
# Step 3: Get all orders for matched accounts
phone_acct_ids = set(enc_to_acct.values())
phone_orders = {}
if phone_acct_ids:
ids = ",".join(phone_acct_ids)
rows = q(f"SELECT id, trade_no, pay_amount_int, to_char(pay_success_date,'YYYY-MM-DD'), key_from, order_status, account_id FROM bi_vala_order WHERE account_id IN ({ids}) AND pay_amount_int IN (59900,199900,359800) AND order_status IN (3,4) AND pay_success_date IS NOT NULL ORDER BY pay_success_date")
for r in rows:
if len(r) >= 7:
aid = r[6]
if aid not in phone_orders:
phone_orders[aid] = []
phone_orders[aid].append({"id":r[0],"trade_no":r[1],"amount":int(r[2]),"pay_date":r[3],"key_from":r[4],"status":r[5]})
# Step 4: Bulk query by keyfrom+date combos
# Collect unique (date, amount, keyfrom) combos
combos = set()
for g in gaps:
d = parse_date(g[4])
if d and g[8]:
combos.add((d, g[5]*100, g[8]))
# Build OR conditions
kf_conds = []
for d, amt, kf in combos:
if "%" in kf:
kf_conds.append(f"(pay_success_date::date='{d}' AND pay_amount_int={amt} AND key_from LIKE '%{kf.replace('%','')}%')")
else:
kf_conds.append(f"(pay_success_date::date='{d}' AND pay_amount_int={amt} AND key_from='{kf}')")
kf_orders = {}
if kf_conds:
# Split into batches of 50
for i in range(0, len(kf_conds), 50):
batch = kf_conds[i:i+50]
sql = f"SELECT o.id, o.trade_no, o.pay_amount_int, to_char(o.pay_success_date,'YYYY-MM-DD'), o.key_from, o.order_status, o.account_id, a.tel FROM bi_vala_order o JOIN bi_vala_app_account a ON o.account_id=a.id WHERE a.status=1 AND o.order_status IN (3,4) AND o.pay_success_date >= '2026-03-01' AND o.pay_success_date < '2026-06-01' AND ({' OR '.join(batch)})"
rows = q(sql)
for r in rows:
if len(r) >= 8:
kf_orders[(r[3], int(r[2]), r[4])] = {"id":r[0],"trade_no":r[1],"amount":int(r[2]),"pay_date":r[3],"key_from":r[4],"status":r[5],"account_id":r[6],"tel":r[7]}
# Step 5: Get all refunds for matched accounts
all_acct_ids = set()
for aid in phone_acct_ids:
all_acct_ids.add(aid)
for v in kf_orders.values():
all_acct_ids.add(v["account_id"])
refunds = {}
if all_acct_ids:
ids = ",".join(all_acct_ids)
rows = q(f"SELECT o.account_id, SUM(r.refund_amount_int) FROM bi_refund_order r JOIN bi_vala_order o ON r.trade_no=o.trade_no WHERE o.account_id IN ({ids}) AND r.status=3 GROUP BY o.account_id")
for r in rows:
if len(r) >= 2 and r[1]:
refunds[r[0]] = int(r[1])
# Step 6: Match
for g in gaps:
idx, month, sales, nick, date_str, amount, channel, phone, keyfrom, is_direct = g
d = parse_date(date_str)
amt = amount * 100
found = None
# Try phone match first
if phone and phone in phone_to_enc:
enc = phone_to_enc[phone]
aid = enc_to_acct.get(enc)
if aid and aid in phone_orders:
for o in phone_orders[aid]:
if o["amount"] == amt:
found = o
break
# Try keyfrom+date+amount match
if not found and d and keyfrom:
if "%" in keyfrom:
like = keyfrom.replace("%","")
for k, v in kf_orders.items():
if v["pay_date"] == d and v["amount"] == amt and like in v["key_from"]:
found = v
break
else:
found = kf_orders.get((d, amt, keyfrom))
gsv = 0
if found:
aid = found.get("account_id","")
refund = refunds.get(aid, 0)
gsv = found["amount"] - refund
status = "" if found else ""
should = "" if (found and gsv > 0 and not is_direct) else ""
print(f"{idx}. [{month}] {sales} {nick} | {date_str} ¥{amount} {channel} | {status} | GSV={gsv/100:.0f} | 应进Bot={should}")
if found:
print(f" order_id={found['id']} {found.get('pay_date','')} ¥{found['amount']/100:.0f} {found.get('key_from','')[:50]} status={found.get('status','')}")