#!/usr/bin/env python3 """Check 57 gaps v6 - correct refund per-order, proper matching""" import subprocess, os, 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 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 from scripts.phone_encrypt import encrypt_phone # Pre-encrypt phones phone_enc = {} for g in gaps: if g[7]: phone_enc[g[7]] = encrypt_phone(g[7]) # Get account IDs for phones phone_to_aid = {} if phone_enc: conds = " OR ".join([f"tel_encrypt LIKE '{v}%'" for v in phone_enc.values()]) for r in q(f"SELECT id, tel_encrypt FROM bi_vala_app_account WHERE status=1 AND ({conds})"): if len(r) >= 2: phone_to_aid[r[1]] = r[0] # For each phone-matched account, get ALL orders + refunds phone_all = {} # aid -> {orders: [...], refunds_by_trade: {...}} for aid in phone_to_aid.values(): orders = [] for r in q(f"SELECT id, trade_no, pay_amount_int, to_char(pay_success_date,'YYYY-MM-DD'), key_from, order_status FROM bi_vala_order WHERE account_id={aid} 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"): if len(r) >= 6: orders.append({"id":r[0],"trade_no":r[1],"amount":int(r[2]),"pay_date":r[3],"key_from":r[4],"status":r[5]}) refunds = {} if orders: trades = ",".join([f"'{o['trade_no']}'" for o in orders]) for r in q(f"SELECT trade_no, SUM(refund_amount_int) FROM bi_refund_order WHERE trade_no IN ({trades}) AND status=3 GROUP BY trade_no"): if len(r) >= 2 and r[1]: refunds[r[0]] = int(r[1]) phone_all[aid] = {"orders": orders, "refunds": refunds} # For each gap, query 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 gsv = 0 # Phone match - get all matching orders if phone and phone in phone_enc: enc = phone_enc[phone] aid = phone_to_aid.get(enc) if aid and aid in phone_all: matching = [o for o in phone_all[aid]["orders"] if o["amount"] == amt] if matching: # Calculate total GSV: sum all order amounts minus refunds total_amt = sum(o["amount"] for o in phone_all[aid]["orders"]) total_refund = sum(phone_all[aid]["refunds"].values()) gsv = total_amt - total_refund found = matching[0] # Use first match for display # Show all orders found["all_orders"] = phone_all[aid]["orders"] found["all_refunds"] = phone_all[aid]["refunds"] # Keyfrom + date match if not found and d and keyfrom: if "%" in keyfrom: like = keyfrom.replace("%","") rows = q(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 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::date BETWEEN '{d}'::date-1 AND '{d}'::date+1 AND o.pay_amount_int={amt} AND o.key_from LIKE '%{like}%' LIMIT 3") else: rows = q(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 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::date BETWEEN '{d}'::date-1 AND '{d}'::date+1 AND o.pay_amount_int={amt} AND o.key_from='{keyfrom}' LIMIT 3") if rows and len(rows[0]) >= 7: r = rows[0] aid = r[6] trade = r[1] # Get refund for this specific trade ref_rows = q(f"SELECT SUM(refund_amount_int) FROM bi_refund_order WHERE trade_no='{trade}' AND status=3") refund = 0 if ref_rows and ref_rows[0] and ref_rows[0][0] and ref_rows[0][0].isdigit(): refund = int(ref_rows[0][0]) gsv = int(r[2]) - refund found = {"id":r[0],"trade_no":r[1],"amount":int(r[2]),"pay_date":r[3],"key_from":r[4],"status":r[5],"account_id":aid} 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: if "all_orders" in found: for o in found["all_orders"]: ref = found["all_refunds"].get(o["trade_no"], 0) net = o["amount"] - ref print(f" order_id={o['id']} {o['pay_date']} ¥{o['amount']/100:.0f} {o['key_from'][:50]} status={o['status']} 退={ref/100:.0f} 净={net/100:.0f}") else: print(f" order_id={found['id']} {found.get('pay_date','')} ¥{found['amount']/100:.0f} {found.get('key_from','')[:60]} status={found.get('status','')}")