From 36853594c52513c0ba62dc7022ef8868300e6c07 Mon Sep 17 00:00:00 2001 From: xiaoban Date: Wed, 17 Jun 2026 22:16:00 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20GSV=E8=81=9A=E5=90=88=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20=E2=80=94=20=E8=AE=A2=E5=8D=95=E6=B1=87=E6=80=BB=E6=94=B9?= =?UTF-8?q?=E7=94=A8aggregate=5Fvalid=5Forders=E7=B4=AF=E5=8A=A0=E5=A4=9A?= =?UTF-8?q?=E7=AC=94=E6=9C=89=E6=95=88=E8=AE=A2=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 aggregate_valid_orders() 函数:同一账户多笔有效订单 GSV/GMV/退款累加 - 订单号取未退款那笔(多笔未退款取最新) - 产品列多单用+拼接 - Step 4 线索绑单仍用 pick_valid_order() 不变 - 修复 13 个账户漏 GSV ¥23,185 的问题 --- scripts/sales_leads_full_refresh.py | 90 +++++++++++++++++++++++++---- skills/full-data-refresh/SKILL.md | 10 +++- 2 files changed, 87 insertions(+), 13 deletions(-) diff --git a/scripts/sales_leads_full_refresh.py b/scripts/sales_leads_full_refresh.py index 2584d23..52361b4 100644 --- a/scripts/sales_leads_full_refresh.py +++ b/scripts/sales_leads_full_refresh.py @@ -533,6 +533,71 @@ def pick_valid_order(orders, clue_date): return valid[0], True +def aggregate_valid_orders(orders, clue_date): + """ + 聚合所有有效订单:GSV/GMV/退款 累加,订单号取未退款那笔。 + 规则: GSV>0 · 非全额退 · K≥C + 返回: dict 含 aggregated_gsv/gmv/refund, best_trade_no, latest_order + 或 (None, False) + """ + if not orders: + return None, False + valid = [] + for o in orders: + gsv = o["gsv"] + pay_amount = o["pay_amount"] + refund_amount = o["refund_amount"] + is_full_refund = (pay_amount > 0 and pay_amount == refund_amount) + if gsv <= 0 or is_full_refund: + continue + pay_dt = o["pay_dt"] + if pay_dt and clue_date: + if pay_dt.strftime("%Y-%m-%d %H:%M:%S") < clue_date: + continue + valid.append(o) + if not valid: + return None, False + + # 按时间排序 + valid.sort(key=lambda o: o["pay_dt"] or datetime.min, reverse=True) + + # GSV/GMV/退款 累加 + total_gsv = sum(o["gsv"] for o in valid) + total_gmv = sum(o["pay_amount"] for o in valid) + total_refund = sum(o["refund_amount"] for o in valid) + + # 订单号:优先取未退款的(refund_amount==0),多笔取最新 + no_refund = [o for o in valid if o["refund_amount"] == 0] + if no_refund: + best_trade_no = no_refund[0]["trade_no"] + else: + # 全部有退款,取退款最少的那笔 + valid_by_refund = sorted(valid, key=lambda o: o["refund_amount"]) + best_trade_no = valid_by_refund[0]["trade_no"] + + # 最新订单信息用于 K/L/M 列 + latest = valid[0] + + # 产品:去重拼接 + products = [] + seen_p = set() + for o in valid: + p = o.get("product", "") + if p and p not in seen_p: + products.append(p) + seen_p.add(p) + + return { + "aggregated_gsv": total_gsv, + "aggregated_gmv": total_gmv, + "aggregated_refund": total_refund, + "best_trade_no": best_trade_no, + "latest_order": latest, + "products": "+".join(products) if products else latest.get("product", ""), + "valid_count": len(valid), + }, True + + def write_sales_sheets(token, all_entries, phone_map, db_info): now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S") @@ -738,13 +803,14 @@ def write_summary_sheet(token, all_entries, phone_map, db_info): if not orders: continue - # 用 pick_valid_order 判断该线索是否有有效单 - valid_order, is_valid = pick_valid_order(orders, clue_date) - if not is_valid or not valid_order: + # 用 aggregate_valid_orders 聚合所有有效订单 + agg_result, is_valid = aggregate_valid_orders(orders, clue_date) + if not is_valid or not agg_result: continue - trade_no = valid_order["trade_no"] - pay_dt = valid_order["pay_dt"] + trade_no = agg_result["best_trade_no"] + latest = agg_result["latest_order"] + pay_dt = latest["pay_dt"] order_date = f"{pay_dt.month}月{pay_dt.day}日 {pay_dt.strftime('%H:%M:%S')}" if pay_dt else "" row_data = [ @@ -759,18 +825,18 @@ def write_summary_sheet(token, all_entries, phone_map, db_info): di.get("reg_date", ""), # I: 注册日期 di.get("download_channel", ""), # J: 下载渠道 order_date, # K: 下单日期 - valid_order["key_from"], # L: 成交渠道 - valid_order["product"], # M: 产品 - int(valid_order["pay_amount"]) if valid_order["pay_amount"] > 0 else "", # N: GMV - int(valid_order["refund_amount"]) if valid_order["refund_amount"] > 0 else "", # O: 退款 - int(valid_order["gsv"]) if valid_order["gsv"] > 0 else "", # P: GSV + latest["key_from"], # L: 成交渠道 + agg_result["products"], # M: 产品(多单用+拼接) + int(agg_result["aggregated_gmv"]) if agg_result["aggregated_gmv"] > 0 else "", # N: GMV(聚合) + int(agg_result["aggregated_refund"]) if agg_result["aggregated_refund"] > 0 else "", # O: 退款(聚合) + int(agg_result["aggregated_gsv"]) if agg_result["aggregated_gsv"] > 0 else "", # P: GSV(聚合) (f"{di['activation']}体验课" if di.get("activation") in ("A1", "A2") else di.get("activation", "")), # Q: 激活课程 di.get("lesson_progress", ""), # R: 行课进度 di.get("lesson_time", ""), # S: 最近行课时间 di.get("lesson_minutes", 0) or "", # T: 学习时长 datetime.now().strftime("%Y-%m-%d %H:%M:%S"), # U: 更新时间 - classify_channel(valid_order["key_from"]), # V: 渠道归属 - trade_no, # W: 订单号 + classify_channel(latest["key_from"]), # V: 渠道归属 + trade_no, # W: 订单号(取未退款那笔) ] gate_rows.append((trade_no, e["row"], row_data)) diff --git a/skills/full-data-refresh/SKILL.md b/skills/full-data-refresh/SKILL.md index f497bab..5e97315 100644 --- a/skills/full-data-refresh/SKILL.md +++ b/skills/full-data-refresh/SKILL.md @@ -13,9 +13,16 @@ metadata: # 细水入海 — 销售数据全量刷新 -> **版本:** v2(2026-06-16 定稿 · 陈逸鸫 + Cursor 对齐) +> **版本:** v2.1(2026-06-17 · GSV 聚合修复) > **协作契约:** `xhs-ark-dashboard/docs/bot-full-refresh-v2.md` > **大麦侧主文档:** `xhs-ark-dashboard/docs/damai-full-refresh-skill.md` +> +> ### v2.1 变更(2026-06-17) +> - **GSV 聚合修复:** 订单汇总 Step 5 改用 `aggregate_valid_orders()` 替代 `pick_valid_order()` +> - 同一账户多笔有效订单 → GSV/GMV/退款 全部累加(不再只取最新一笔) +> - 订单号取未退款那笔(多笔未退款取最新) +> - 产品列多单用 `+` 拼接 +> - 线索绑单(Step 4)仍用 `pick_valid_order()` 不变 ## 核心架构规则(写死,不可漂移) @@ -134,6 +141,7 @@ PASS 条件: ### 订单汇总进表条件 - Y=1(已在三表筛选,汇总默认全是有效单) - GSV>0 · 非全额退 · K≥C +- **同一账户多笔有效订单 → GSV/GMV/退款 累加,订单号取未退款那笔**(v2.1 修复) - 同 X 多进线 → 汇总只保留 1 行 ### 渠道归属分类 (Z列) [王虹茗确认 2026-06-15]