fix: GSV聚合修复 — 订单汇总改用aggregate_valid_orders累加多笔有效订单
- 新增 aggregate_valid_orders() 函数:同一账户多笔有效订单 GSV/GMV/退款累加 - 订单号取未退款那笔(多笔未退款取最新) - 产品列多单用+拼接 - Step 4 线索绑单仍用 pick_valid_order() 不变 - 修复 13 个账户漏 GSV ¥23,185 的问题
This commit is contained in:
parent
547bce0c90
commit
36853594c5
@ -533,6 +533,71 @@ def pick_valid_order(orders, clue_date):
|
|||||||
return valid[0], True
|
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):
|
def write_sales_sheets(token, all_entries, phone_map, db_info):
|
||||||
now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
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:
|
if not orders:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 用 pick_valid_order 判断该线索是否有有效单
|
# 用 aggregate_valid_orders 聚合所有有效订单
|
||||||
valid_order, is_valid = pick_valid_order(orders, clue_date)
|
agg_result, is_valid = aggregate_valid_orders(orders, clue_date)
|
||||||
if not is_valid or not valid_order:
|
if not is_valid or not agg_result:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
trade_no = valid_order["trade_no"]
|
trade_no = agg_result["best_trade_no"]
|
||||||
pay_dt = valid_order["pay_dt"]
|
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 ""
|
order_date = f"{pay_dt.month}月{pay_dt.day}日 {pay_dt.strftime('%H:%M:%S')}" if pay_dt else ""
|
||||||
|
|
||||||
row_data = [
|
row_data = [
|
||||||
@ -759,18 +825,18 @@ def write_summary_sheet(token, all_entries, phone_map, db_info):
|
|||||||
di.get("reg_date", ""), # I: 注册日期
|
di.get("reg_date", ""), # I: 注册日期
|
||||||
di.get("download_channel", ""), # J: 下载渠道
|
di.get("download_channel", ""), # J: 下载渠道
|
||||||
order_date, # K: 下单日期
|
order_date, # K: 下单日期
|
||||||
valid_order["key_from"], # L: 成交渠道
|
latest["key_from"], # L: 成交渠道
|
||||||
valid_order["product"], # M: 产品
|
agg_result["products"], # M: 产品(多单用+拼接)
|
||||||
int(valid_order["pay_amount"]) if valid_order["pay_amount"] > 0 else "", # N: GMV
|
int(agg_result["aggregated_gmv"]) if agg_result["aggregated_gmv"] > 0 else "", # N: GMV(聚合)
|
||||||
int(valid_order["refund_amount"]) if valid_order["refund_amount"] > 0 else "", # O: 退款
|
int(agg_result["aggregated_refund"]) if agg_result["aggregated_refund"] > 0 else "", # O: 退款(聚合)
|
||||||
int(valid_order["gsv"]) if valid_order["gsv"] > 0 else "", # P: GSV
|
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: 激活课程
|
(f"{di['activation']}体验课" if di.get("activation") in ("A1", "A2") else di.get("activation", "")), # Q: 激活课程
|
||||||
di.get("lesson_progress", ""), # R: 行课进度
|
di.get("lesson_progress", ""), # R: 行课进度
|
||||||
di.get("lesson_time", ""), # S: 最近行课时间
|
di.get("lesson_time", ""), # S: 最近行课时间
|
||||||
di.get("lesson_minutes", 0) or "", # T: 学习时长
|
di.get("lesson_minutes", 0) or "", # T: 学习时长
|
||||||
datetime.now().strftime("%Y-%m-%d %H:%M:%S"), # U: 更新时间
|
datetime.now().strftime("%Y-%m-%d %H:%M:%S"), # U: 更新时间
|
||||||
classify_channel(valid_order["key_from"]), # V: 渠道归属
|
classify_channel(latest["key_from"]), # V: 渠道归属
|
||||||
trade_no, # W: 订单号
|
trade_no, # W: 订单号(取未退款那笔)
|
||||||
]
|
]
|
||||||
gate_rows.append((trade_no, e["row"], row_data))
|
gate_rows.append((trade_no, e["row"], row_data))
|
||||||
|
|
||||||
|
|||||||
@ -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/bot-full-refresh-v2.md`
|
||||||
> **大麦侧主文档:** `xhs-ark-dashboard/docs/damai-full-refresh-skill.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(已在三表筛选,汇总默认全是有效单)
|
- Y=1(已在三表筛选,汇总默认全是有效单)
|
||||||
- GSV>0 · 非全额退 · K≥C
|
- GSV>0 · 非全额退 · K≥C
|
||||||
|
- **同一账户多笔有效订单 → GSV/GMV/退款 累加,订单号取未退款那笔**(v2.1 修复)
|
||||||
- 同 X 多进线 → 汇总只保留 1 行
|
- 同 X 多进线 → 汇总只保留 1 行
|
||||||
|
|
||||||
### 渠道归属分类 (Z列) [王虹茗确认 2026-06-15]
|
### 渠道归属分类 (Z列) [王虹茗确认 2026-06-15]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user