From 4135e93b17ff206e8c2ce0b886d74237b9370302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=BA=AA?= Date: Mon, 15 Jun 2026 08:00:01 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20=E6=AF=8F=E6=97=A5=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=A4=87=E4=BB=BD=20-=202026-06-15=2008:00:01?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- memory/.dreams/short-term-recall.json | 89 ++++++++++-- scripts/check_gap_57.py | 189 ++++++++++++++++++++++++ scripts/check_gap_57_v2.py | 173 ++++++++++++++++++++++ scripts/check_gap_57_v3.py | 197 ++++++++++++++++++++++++++ scripts/check_gap_57_v4.py | 193 +++++++++++++++++++++++++ scripts/check_gap_57_v5.py | 147 +++++++++++++++++++ scripts/check_gap_57_v6.py | 172 ++++++++++++++++++++++ 7 files changed, 1149 insertions(+), 11 deletions(-) create mode 100644 scripts/check_gap_57.py create mode 100644 scripts/check_gap_57_v2.py create mode 100644 scripts/check_gap_57_v3.py create mode 100644 scripts/check_gap_57_v4.py create mode 100644 scripts/check_gap_57_v5.py create mode 100644 scripts/check_gap_57_v6.py diff --git a/memory/.dreams/short-term-recall.json b/memory/.dreams/short-term-recall.json index 738e9e5..755f7cf 100644 --- a/memory/.dreams/short-term-recall.json +++ b/memory/.dreams/short-term-recall.json @@ -1,6 +1,6 @@ { "version": 1, - "updatedAt": "2026-06-09T07:36:53.566Z", + "updatedAt": "2026-06-14T07:14:40.970Z", "entries": { "memory:memory/2026-05-06.md:1:20": { "key": "memory:memory/2026-05-06.md:1:20", @@ -462,22 +462,25 @@ "endLine": 30, "source": "memory", "snippet": "# 2026-06-02 工作日志 ## 微伴线索更新 - [陈逸鸫] 提供微伴导出数据,更新6/1-6/2线索 - 映射确认:益达老师 = 小龙 - 写入结果:小龙表+50条(6/1 25+6/2 25),吴迪表+15条(6/1) - ⚠️ 首次写入时重复了(小龙24行+吴迪2行),已清理 - 每日线索汇总已修正:6/1 小龙25 吴迪15 / 6/2 小龙25 ## Tom/Bob 6月不接小红书线索 - [陈逸鸫] 告知 Tom 和 Bob 6月开始不接小红书线索了 - 微伴数据确认6/1-6/2 Tom/Bob 新增为0 ## 订单汇总 2smjwA 全量覆盖进展 - Cursor 确认方案:数据库 bi_vala_order 为唯一源,全量覆盖 2smjwA - 数据库匹配到 390 单(销转团队线索关联),按月:3月88/4月158/5月138/6月6 - 看板去重后 406 单,差异 40 单已分类: - 有uid无订单 8单 - 金额对不上 6单 - 未注册用户 10单 - 昵称为空 2单 - 2025年订单 6单(不纳入) - 重复录入 1单(孙婧) - 已有但key比对误差 7单 - 26 单待杨羽确认,清单已发群 - 闸门:杨羽确认 → v2 数据 → 备份→清空→覆盖→挂定时 ## 看板全量审计 - 13 张 sheet 全部扫完,公式引用正确,无硬值 - Cursor 补修:关键投放数据 Row131 裸IF包IFERROR、销售结算 M1/M2 废弃清空、每日线索月合计行金色", - "recallCount": 4, + "recallCount": 6, "dailyCount": 0, "groundedCount": 0, - "totalScore": 4, + "totalScore": 6, "maxScore": 1, "firstRecalledAt": "2026-06-03T00:01:27.128Z", - "lastRecalledAt": "2026-06-05T14:06:12.562Z", + "lastRecalledAt": "2026-06-14T07:14:40.970Z", "queryHashes": [ "5f57ba3c20a6", "b76e8457b2ee", "4354c1e39886", - "70f4bbaea7b1" + "70f4bbaea7b1", + "d26345ebc273", + "cd28f28f1a7c" ], "recallDays": [ "2026-06-03", - "2026-06-05" + "2026-06-05", + "2026-06-14" ], "conceptTags": [ "备份", @@ -601,18 +604,20 @@ "endLine": 33, "source": "memory", "snippet": "- 抖音+联报退费率最高49.2%(也是最大组合124单) - **分析脚本:** `scripts/channel_lead_refund_analysis.py` ## 王虹茗 - V2修正:纳入多角色因素 - **反馈:** 指出 Sheet2 一个用户可能创建多个角色,要求重新分析 - **关键修正:** - 按用户聚合时保留角色数量、创建节奏、行课覆盖等信息 - 行课行为以\"任一角色完成即算\"聚合 - **新增发现:** - 1个角色退费率46.7% > 2个角色34.8% > ≥4个角色28.6%(角色越多退费越低) - 同日多角色45.7% vs 1周以上多角色28.6%(跨时间创建=持续使用信号) - 全角色无行课退费率75.9%(最高危) - 1角色+无行课=76.0%(最危险组合),≥4角色+有行课=16.7%(最安全) - **输出文件:** `output/渠道4-5月线索_退款相关性分析_v2_20260519_202711.xlsx`(20个维度+明细) - **分析脚本:** `scripts/channel_lead_refund_analysis_v2.py`", - "recallCount": 1, + "recallCount": 2, "dailyCount": 0, "groundedCount": 0, - "totalScore": 1, + "totalScore": 2, "maxScore": 1, "firstRecalledAt": "2026-06-05T00:46:22.699Z", - "lastRecalledAt": "2026-06-05T00:46:22.699Z", + "lastRecalledAt": "2026-06-14T06:58:06.164Z", "queryHashes": [ - "34106cf619c4" + "34106cf619c4", + "6769ba9ebb36" ], "recallDays": [ - "2026-06-05" + "2026-06-05", + "2026-06-14" ], "conceptTags": [ "联报退费率最高49.2", @@ -624,6 +629,68 @@ "全角色无行课退费率75.9", "76.0" ] + }, + "memory:memory/2026-06-12.md:18:38": { + "key": "memory:memory/2026-06-12.md:18:38", + "path": "memory/2026-06-12.md", + "startLine": 18, + "endLine": 38, + "source": "memory", + "snippet": "| 6 | 小乖大人 | 16158 | 13944890221 | 3/1 | 无订单 | - | 0 | 0 | DB无任何订单 | | 7 | 潘潘 | 16150 | 18610935696 | 3/1 | 无订单 | - | 0 | 0 | DB无任何订单 | | 8 | 张滢ya | 17894 | 13799768340 | 3/7 | 无订单 | - | 0 | 0 | DB无任何订单 | | 9 | sallywu | 17816 | 15998103065 | 3/7 | 无订单 | - | 0 | 0 | DB无任何订单 | | 10 | 🦁萨摩 | 21858 | 13685553716 | 3/8 | 4/8 | ✓ | 1999 | 1999 | 达人-学霸三人行 | ### Group B (有手机,4笔) - phone_encrypt查UID | # | 昵称 | 手机 | 加密结果 | DB匹配 | |---|------|------|---------|--------| | 11 | 潘提提 | 13427741613 | IiShdIaiY1oy7B_Xn4EH3g.. | 无匹配 | | 12 | 狸小路 | 18622850293 | YPAQ-740vKwxroqZGkeGyQ.. | 无匹配 | | 13 | 希小希 | 18086665321 | c8zfpqBrN1nikMkwAj64aQ.. | 无匹配 | | 14 | 曼 | 13520255515 | NBVtGuxEge7f7hdkyK3y7Q.. | 无匹配", + "recallCount": 1, + "dailyCount": 0, + "groundedCount": 0, + "totalScore": 1, + "maxScore": 1, + "firstRecalledAt": "2026-06-14T06:58:06.164Z", + "lastRecalledAt": "2026-06-14T06:58:06.164Z", + "queryHashes": [ + "6769ba9ebb36" + ], + "recallDays": [ + "2026-06-14" + ], + "conceptTags": [ + "3/1", + "3/7", + "3/8", + "4/8", + "达人-学霸三人行", + "phone-encrypt查uid", + "iishdiaiy1oy7b-xn4eh3g", + "ypaq-740vkwxroqzgkegyq" + ] + }, + "memory:memory/2026-06-12.md:35:56": { + "key": "memory:memory/2026-06-12.md:35:56", + "path": "memory/2026-06-12.md", + "startLine": 35, + "endLine": 56, + "source": "memory", + "snippet": "| 15 | Rachel | 3/5 | 10994 | 13510564547 | 3/7 | ✓ | sales-adp-bj-jxl-0, GMV=1999 | | 16 | soul | 3/2 | 17387 | 15640464255 | 3/12 | ✓ | sales-adp-bj-jxl-0, GMV=1999 | | 17 | 红 | 3/7 | 17025 | 13533955004 | 3/14 | ✓ | sales-adp-bj-jxl-0, GMV=1999 | | 18 | 一笑轩渠 | 3/8 | 17425 | 15017528458 | 3/11 | ✓ | sales-adp-bj-jxl-0, GMV=1999 | | 19 | 蜗牛 | 3/2 | ❓ | ❓ | ❓ | - | 晚柠5/15订单数百笔,无手机/UID无法定位 | | 20 | c_瑶 | 3/6 | ❓ | ❓ | ❓ | - | \"直购\"渠道DB不存在,3/14无3998/1999匹配 | ### 关键发现 1. Group A 中 7/10 用户 DB 中无任何订单(pre汇总有GMV但DB不存在) 2. Group B 4个手机号全部未注册(H=未注册 确认正确) 3. Group C #15-#18 4笔 jxl-0 均 L≥C,pre怀疑#18 L= '2026-03-01' AND o.pay_success_date < '2026-06-01' + AND o.order_status IN (3, 4) + AND o.pay_amount_int IN (59900, 199900, 359800) + AND ({date_conditions}) +ORDER BY o.pay_success_date, o.pay_amount_int, o.key_from; +""" + +result = subprocess.run([ + "psql", "-h", "bj-postgres-16pob4sg.sql.tencentcdb.com", + "-p", "28591", "-U", "ai_member", "-d", "vala_bi", + "-c", sql +], env={**os.environ, "PGPASSWORD": PGPASS}, capture_output=True, text=True, timeout=30) + +# Parse results +orders = [] +for line in result.stdout.strip().split("\n")[2:-1]: # skip header and footer + parts = line.split("|") + if len(parts) >= 9: + orders.append({ + "id": parts[0].strip(), + "trade_no": parts[1].strip(), + "amount": int(parts[2].strip()) if parts[2].strip() else 0, + "pay_date": parts[3].strip(), + "key_from": parts[4].strip(), + "order_status": parts[5].strip(), + "account_id": parts[6].strip(), + "tel": parts[7].strip(), + "reg_date": parts[8].strip() if len(parts) > 8 else "", + }) + +print(f"Total orders found: {len(orders)}") + +# Now match each gap +for i, g in enumerate(gaps): + month, src, sales, nick, date_str, amount, channel, phone, clue, keyfrom, is_direct = g + idx = i + 1 + d = parse_date(date_str) + amount_int = amount * 100 + + # If phone provided, match by phone + if phone: + matched = [o for o in orders if o["tel"].replace("*","")[-4:] == phone[-4:] and o["pay_date"] == d and o["amount"] == amount_int] if d else [] + if not matched and d: + matched = [o for o in orders if o["tel"].replace("*","")[-4:] == phone[-4:]] + else: + # Match by date + amount + channel pattern + pat = keyfrom if keyfrom else channel_to_pattern(channel) + if d and pat: + if "%" in pat: + matched = [o for o in orders if o["pay_date"] == d and o["amount"] == amount_int and pat.replace("%","") in o["key_from"]] + else: + matched = [o for o in orders if o["pay_date"] == d and o["amount"] == amount_int and o["key_from"] == pat] + elif d: + matched = [o for o in orders if o["pay_date"] == d and o["amount"] == amount_int] + else: + matched = [] + + status = "有" if matched else "无" + print(f"{idx}. [{month}] {sales} {nick} | {date_str} ¥{amount} {channel} | {status} | {len(matched)} matches") + if matched: + for m in matched: + print(f" -> order_id={m['id']} {m['pay_date']} ¥{m['amount']/100} {m['key_from'][:60]} status={m['order_status']} tel={m['tel']} reg={m['reg_date']}") diff --git a/scripts/check_gap_57_v2.py b/scripts/check_gap_57_v2.py new file mode 100644 index 0000000..df51a7c --- /dev/null +++ b/scripts/check_gap_57_v2.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +"""Check 57 gap orders - efficient approach""" +import subprocess, os, re + +PGPASS = "LdfjdjL83h3h3^$&**YGG*" + +# Gap data extracted from chat history +gaps = [ + # 3月 (20) + (1,"3月","qX7oJ6","小龙","雪珂💗","3月6日",1999,"微信小店","","sales-adp-bj-jxl-0"), + (2,"3月","qX7oJ6","小龙","Mars(燚)","3月7日",3598,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0"), + (3,"3月","qX7oJ6","小龙","薇薇","3月",1999,"小红书直购","13520306626","newmedia-dianpu-xhs-0-0"), + (4,"3月","qX7oJ6","小龙","Yeah~^_^","3月20日",3598,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0"), + (5,"3月","qX7oJ6","小龙","TutuTu","3月",1999,"小红书直购","18107332677","newmedia-dianpu-xhs-0-0"), + (6,"3月","qX7oJ6","小龙","EFFIE","3月25日",3598,"微信小店","","newmedia-dianpu-xhs-0-0"), + (7,"3月","sSCT22","Tom","JeanneLᴇᴇ🦄","3月4日",3598,"微信小店","","newmedia-dianpu-wxxd-0-0"), + (8,"3月","sSCT22","Tom","薇妮姐","3月8日",599,"端内","","app-active-h5-0-0"), + (9,"3月","sSCT22","Tom","欣","3月7日",599,"端内","","app-active-h5-0-0"), + (10,"3月","sSCT22","Bob","Mogu","3月6日",599,"端内","","app-active-h5-0-0"), + (11,"3月","sSCT22","Bob","L. 一颗夹星糖🌟","3月12日",3598,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0"), + (12,"3月","sSCT22","Tom","阿雅呀","3月15日",3598,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0"), + (13,"3月","sSCT22","Tom","Anna","3月4日",1999,"抖音","","newmedia-dianpu-douyin-0-0"), + (14,"3月","sSCT22","Bob","Echo Liang","3月15日",1999,"微信小店","","sales-adp-cd-zjf-0"), + (15,"3月","sSCT22","Tom","幼兒園高材生🍼","3月23日",3598,"微信小店","13055770067","sales-adp-cd-yy-0"), + (16,"3月","sSCT22","Tom","Nancy","3月26日",599,"端内","","app-active-h5-0-0"), + (17,"3月","sSCT22","Tom","依米","3月30日",3598,"微信小店","","sales-adp-cd-zjf-0"), + (18,"3月","sSCT22","Bob","zhouyun","3月6日",1999,"小红书-学霸老王","13588706769","newmedia-daren-xhs-学霸老王讲真话-0"), + (19,"3月","sSCT22","Tom","💗小超人棒棒哒🍭","3月29日",3598,"小红书-晚柠","18630368296","newmedia-daren-xhs-晚柠也是个妈妈了-0"), + (20,"3月","sSCT22","Bob","鹿","3月14日",3598,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0"), + # 4月 (15) + (21,"4月","qX7oJ6","小龙","琳溪","4月19日",3598,"微信小店","","sales-adp-bj-jxl-0"), + (22,"4月","qX7oJ6","小龙","小丽","4月26日",599,"端内","","app-active-h5-0-0"), + (23,"4月","qX7oJ6","小龙","子曦","4月16日",3598,"晚柠-小红书","","newmedia-daren-xhs-晚柠也是个妈妈了-0"), + (24,"4月","qX7oJ6","小龙","Gaᴗao","4月24日",3598,"微信小店","","sales-adp-bj-jxl-0"), + (25,"4月","XqxgjP","吴迪","ཚེ་རིང་མཚོ彩让措","2026/4/26",599,"端内","","app-active-h5-0-0"), + (26,"4月","XqxgjP","吴迪","莉筱雅","2026/4/27",1999,"晚柠-小红书","","newmedia-daren-xhs-晚柠也是个妈妈了-0"), + (27,"4月","XqxgjP","吴迪","爱吃巧克力","2026/4/24",1999,"微信小店","","sales-adp-bj-wd-0"), + (28,"4月","XqxgjP","吴迪","Aa~Jessie💝","2026/4/27",1999,"抖音","","newmedia-dianpu-douyin-0-0"), + (29,"4月","XqxgjP","吴迪","🐷","2026/4/26",3598,"宣儿妈妈-小红书","","sales-adp-cd-yy-0"), + (30,"4月","XqxgjP","吴迪","🐷","2026/4/26",3598,"宣儿妈妈-小红书","","sales-adp-cd-yy-0"), + (31,"4月","sSCT22","Tom","静静是我🍃","4月8日",1999,"小红书-学霸老王","15975769851","partner-actives-0-0-0"), + (32,"4月","sSCT22","Bob","胆大鬼","4月8日",3598,"小红书-学霸老王","15262255267","newmedia-daren-xhs-学霸老王讲真话-0"), + (33,"4月","sSCT22","Tom","希Cissy-427","4月27日",3598,"微信小店","","sales-adp-cd-zjf-0"), + (34,"4月","sSCT22","Tom","Kerry","4月9日",3598,"微信小店","18328334683","sales-adp-cd-yy-0"), + (35,"4月","sSCT22","Tom","SHAN_Q_Q","4月1日",3598,"小红书-官店","","newmedia-dianpu-xhs-0-0"), + # 5月 (22) + (36,"5月","qX7oJ6","小龙","Sia","5月9日",599,"微信小店","","sales-adp-bj-jxl-0"), + (37,"5月","XqxgjP","吴迪","璐","2026/5/9",1999,"微信小店","","sales-adp-bj-wd-0"), + (38,"5月","XqxgjP","吴迪","^_^","2026/5/16",3598,"晚柠-小红书","","newmedia-daren-xhs-晚柠也是个妈妈了-0"), + (39,"5月","XqxgjP","吴迪","🌸白色铃兰🌸","2026/5/17",3598,"抖音","","newmedia-dianpu-douyin-0-0"), + (40,"5月","XqxgjP","吴迪","咔咔","2026/5/7",3598,"微信小店","","sales-adp-bj-wd-0"), + (41,"5月","XqxgjP","吴迪","梦马","2026/5/21",3598,"小红书","","newmedia-dianpu-xhs-0-0"), + (42,"5月","XqxgjP","吴迪","梦马","2026/5/21",3598,"小红书","","newmedia-dianpu-xhs-0-0"), + (43,"5月","XqxgjP","吴迪","小西瓜","2026/5/30",1999,"微信小店","","sales-adp-bj-wd-0"), + (44,"5月","XqxgjP","吴迪","璐","2026/5/9",1999,"微信小店","","sales-adp-bj-wd-0"), + (45,"5月","sSCT22","Bob","Yuki-515","5月15日",3598,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0"), + (46,"5月","sSCT22","Tom","冬-515","5月15日",1999,"小红书-晚柠","","newmedia-daren-xhs-晚柠也是个妈妈了-0"), + (47,"5月","sSCT22","Bob","微笑向暖","5月4日",1999,"微信小店","","sales-adp-bj-wd-0"), + (48,"5月","sSCT22","Bob","cici-511","5月11日",1999,"微信小店","","sales-adp-cd-xsy-0"), + (49,"5月","sSCT22","Tom","🐷-519","5月19日",1999,"小红书-宣儿妈妈","","newmedia-daren-xhs-宣儿妈妈%"), + (50,"5月","sSCT22","Bob","妃-516","5月16日",1999,"微信小店","","sales-adp-cd-zjf-0"), + (51,"5月","sSCT22","Tom","黄晔-516","5月16日",3598,"微信小店","","sales-adp-bj-wd-0"), + (52,"5月","sSCT22","Tom","朵朵呀!","5月25日",1999,"微信小店","","sales-adp-cd-zjf-0"), + (53,"5月","sSCT22","Tom","雷鸣-414","5月14日",599,"微信小店","","sales-adp-bj-wd-0"), + (54,"5月","sSCT22","Tom","毛阿毛🐱-520","5月20日",1999,"小红书-官店","","newmedia-dianpu-xhs-0-0"), + (55,"5月","sSCT22","Tom","____Miss_y-519","5月19日",599,"微信小店","","sales-adp-bj-wd-0"), + (56,"5月","sSCT22","Tom","Mandy-526","5月26日",1999,"微信小店","","sales-adp-bj-wd-0"), + (57,"5月","sSCT22","Bob","Yoki-529","5月29日",1999,"微信小店","","sales-adp-cd-zjf-0"), +] + +# Parse date +def parse_date(d): + d = d.replace("日","").replace("月","/").replace(" ","").strip() + if "/" in d: + parts = d.split("/") + if len(parts) >= 2 and parts[1]: + m = int(parts[0]) + day = int(parts[1]) + return f"2026-{m:02d}-{day:02d}" + return None + +# Encrypt phones +from scripts.phone_encrypt import encrypt_phone +phone_map = {} +for g in gaps: + phone = g[7] + if phone: + phone_map[phone] = encrypt_phone(phone) + +# Look up accounts by encrypted phone +if phone_map: + phone_conds = " OR ".join([f"a.tel_encrypt LIKE '{v}%'" for v in phone_map.values()]) + sql = f"SELECT a.id, a.tel, a.tel_encrypt FROM bi_vala_app_account a WHERE a.status=1 AND ({phone_conds})" + r = subprocess.run(["psql","-h","bj-postgres-16pob4sg.sql.tencentcdb.com","-p","28591","-U","ai_member","-d","vala_bi","-c",sql], + env={**os.environ,"PGPASSWORD":PGPASS}, capture_output=True, text=True, timeout=15) + # Build account_id -> tel mapping + acct_tel = {} + for line in r.stdout.strip().split("\n")[2:-1]: + parts = [p.strip() for p in line.split("|")] + if len(parts) >= 3: + acct_tel[parts[0]] = parts[1] +else: + acct_tel = {} + +# Now query orders by key_from + date for each gap +results = [] +for g in gaps: + idx, month, src, sales, nick, date_str, amount, channel, phone, keyfrom = g + d = parse_date(date_str) + amt = amount * 100 + + found = None + # If we have phone match + if phone and phone in phone_map: + enc = phone_map[phone] + # Find account_id + sql2 = f"SELECT a.id FROM bi_vala_app_account a WHERE a.status=1 AND a.tel_encrypt LIKE '{enc}%'" + r2 = subprocess.run(["psql","-h","bj-postgres-16pob4sg.sql.tencentcdb.com","-p","28591","-U","ai_member","-d","vala_bi","-c",sql2], + env={**os.environ,"PGPASSWORD":PGPASS}, capture_output=True, text=True, timeout=10) + acct_ids = [] + for line in r2.stdout.strip().split("\n")[2:-1]: + p = line.split("|")[0].strip() + if p.isdigit(): + acct_ids.append(p) + if acct_ids: + acct_list = ",".join(acct_ids) + sql3 = 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 WHERE o.account_id IN ({acct_list}) AND o.pay_amount_int={amt} AND o.order_status IN (3,4) AND o.pay_success_date IS NOT NULL ORDER BY o.pay_success_date" + r3 = subprocess.run(["psql","-h","bj-postgres-16pob4sg.sql.tencentcdb.com","-p","28591","-U","ai_member","-d","vala_bi","-c",sql3], + env={**os.environ,"PGPASSWORD":PGPASS}, capture_output=True, text=True, timeout=10) + for line in r3.stdout.strip().split("\n")[2:-1]: + parts = [p.strip() for p in line.split("|")] + if len(parts) >= 7: + found = {"order_id":parts[0],"trade_no":parts[1],"amount":int(parts[2]),"pay_date":parts[3],"key_from":parts[4],"status":parts[5],"account_id":parts[6]} + break + + # If no phone match, try keyfrom + date + amount + if not found and d and keyfrom: + if "%" in keyfrom: + like = keyfrom.replace("%","") + sql4 = 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.pay_success_date::date='{d}' AND o.pay_amount_int={amt} AND o.key_from LIKE '%{like}%' AND o.order_status IN (3,4) LIMIT 3" + else: + sql4 = 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.pay_success_date::date='{d}' AND o.pay_amount_int={amt} AND o.key_from='{keyfrom}' AND o.order_status IN (3,4) LIMIT 3" + r4 = subprocess.run(["psql","-h","bj-postgres-16pob4sg.sql.tencentcdb.com","-p","28591","-U","ai_member","-d","vala_bi","-c",sql4], + env={**os.environ,"PGPASSWORD":PGPASS}, capture_output=True, text=True, timeout=10) + matches = [] + for line in r4.stdout.strip().split("\n")[2:-1]: + parts = [p.strip() for p in line.split("|")] + if len(parts) >= 8: + matches.append({"order_id":parts[0],"trade_no":parts[1],"amount":int(parts[2]),"pay_date":parts[3],"key_from":parts[4],"status":parts[5],"account_id":parts[6],"tel":parts[7]}) + if matches: + found = matches[0] + + # Check refunds + gsv = 0 + if found: + aid = found.get("account_id","") + sql5 = f"SELECT 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={aid} AND r.status=3" + r5 = subprocess.run(["psql","-h","bj-postgres-16pob4sg.sql.tencentcdb.com","-p","28591","-U","ai_member","-d","vala_bi","-c",sql5], + env={**os.environ,"PGPASSWORD":PGPASS}, capture_output=True, text=True, timeout=10) + refund = 0 + for line in r5.stdout.strip().split("\n")[2:-1]: + val = line.strip() + if val and val.isdigit(): + refund = int(val) + gsv = found["amount"] - refund + + status = "有" if found else "无" + is_direct = keyfrom in ("newmedia-dianpu-xhs-0-0","newmedia-dianpu-douyin-0-0","newmedia-dianpu-wxxd-0-0","app-active-h5-0-0") + should_enter = "是" 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_enter}") + if found: + print(f" order_id={found['order_id']} {found.get('pay_date','')} ¥{found['amount']/100:.0f} {found.get('key_from','')[:50]} status={found.get('status','')}") diff --git a/scripts/check_gap_57_v3.py b/scripts/check_gap_57_v3.py new file mode 100644 index 0000000..7550508 --- /dev/null +++ b/scripts/check_gap_57_v3.py @@ -0,0 +1,197 @@ +#!/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','')}") diff --git a/scripts/check_gap_57_v4.py b/scripts/check_gap_57_v4.py new file mode 100644 index 0000000..ef7e400 --- /dev/null +++ b/scripts/check_gap_57_v4.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +"""Check 57 gap orders v4 - broader 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=30) + 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 from chat - exact keyfrom from gap messages +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 + +# Step 1: Encrypt phones +phone_enc = {} +for g in gaps: + if g[7]: + phone_enc[g[7]] = encrypt_phone(g[7]) + +# Step 2: 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] + +# Step 3: Get all orders for phone-matched accounts +phone_orders = {} +if phone_to_aid: + ids = ",".join(phone_to_aid.values()) + for r in 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"): + if len(r) >= 7: + aid = r[6] + phone_orders.setdefault(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: Get ALL orders for all gap dates (broader query, no keyfrom filter) +all_dates = set() +for g in gaps: + d = parse_date(g[4]) + if d: + all_dates.add(d) + +date_orders = {} # (date, amount) -> list of orders +if all_dates: + date_list = ",".join([f"'{d}'" for d in sorted(all_dates)]) + 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::date IN ({date_list}) AND o.pay_amount_int IN (59900,199900,359800)" + for r in q(sql): + if len(r) >= 8: + key = (r[3], int(r[2])) + date_orders.setdefault(key, []).append({"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 refunds for all matched accounts +all_aids = set() +for aid, orders in phone_orders.items(): + all_aids.add(aid) +for orders in date_orders.values(): + for o in orders: + all_aids.add(o["account_id"]) + +refunds = {} +if all_aids: + ids = ",".join(all_aids) + for r in q(f"SELECT o.account_id, SUM(rf.refund_amount_int) FROM bi_refund_order rf JOIN bi_vala_order o ON rf.trade_no=o.trade_no WHERE o.account_id IN ({ids}) AND rf.status=3 GROUP BY o.account_id"): + if len(r) >= 2 and r[1]: + refunds[r[0]] = int(r[1]) + +# Step 6: Match each gap +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 + if phone and phone in phone_enc: + enc = phone_enc[phone] + aid = phone_to_aid.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 + if not found and d and keyfrom: + candidates = date_orders.get((d, amt), []) + if "%" in keyfrom: + like = keyfrom.replace("%","") + for o in candidates: + if like in o["key_from"]: + found = o + break + else: + for o in candidates: + if o["key_from"] == keyfrom: + found = o + break + + # If still not found, try date+amount only (any keyfrom) + if not found and d: + candidates = date_orders.get((d, amt), []) + if candidates: + found = candidates[0] # Take first match + + 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','')[:60]} status={found.get('status','')}") diff --git a/scripts/check_gap_57_v5.py b/scripts/check_gap_57_v5.py new file mode 100644 index 0000000..3d8debf --- /dev/null +++ b/scripts/check_gap_57_v5.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +"""Check 57 gaps - one query per gap to avoid truncation""" +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 gap, query individually +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 + + # Phone match + if phone and phone in phone_enc: + enc = phone_enc[phone] + aid = phone_to_aid.get(enc) + if aid: + 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 FROM bi_vala_order o WHERE o.account_id={aid} AND o.pay_amount_int={amt} AND o.order_status IN (3,4) AND o.pay_success_date IS NOT NULL ORDER BY o.pay_success_date LIMIT 5") + for r in rows: + if len(r) >= 6: + 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} + break + + # Keyfrom + date match (±1 day) + 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] + 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":r[6]} + + # Get refund + gsv = 0 + if found: + aid = found.get("account_id","") + rows = q(f"SELECT SUM(rf.refund_amount_int) FROM bi_refund_order rf JOIN bi_vala_order o ON rf.trade_no=o.trade_no WHERE o.account_id={aid} AND rf.status=3") + refund = 0 + if rows and rows[0] and rows[0][0] and rows[0][0].isdigit(): + refund = int(rows[0][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','')[:60]} status={found.get('status','')}") diff --git a/scripts/check_gap_57_v6.py b/scripts/check_gap_57_v6.py new file mode 100644 index 0000000..e15f853 --- /dev/null +++ b/scripts/check_gap_57_v6.py @@ -0,0 +1,172 @@ +#!/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','')}")