微信小程序页面迁移校验之前 P5任务处理之前
This commit is contained in:
176
scripts/ops/daily_revenue_report.py
Normal file
176
scripts/ops/daily_revenue_report.py
Normal file
@@ -0,0 +1,176 @@
|
||||
"""每日经营数据统计报告生成器
|
||||
|
||||
统计 3月1日至当天的每日经营数据,按日切 08:00 ~ 次日 08:00,
|
||||
输出 markdown 表格到 docs/reports/daily-revenue-latest.md
|
||||
"""
|
||||
import os, sys
|
||||
from pathlib import Path
|
||||
from datetime import date, timedelta
|
||||
from decimal import Decimal
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv(Path(__file__).resolve().parents[2] / ".env")
|
||||
|
||||
dsn = os.environ.get("TEST_DB_DSN")
|
||||
if not dsn:
|
||||
dsn = os.environ.get("PG_DSN", "")
|
||||
if "etl_feiqiu" in dsn:
|
||||
dsn = dsn.replace("etl_feiqiu", "test_etl_feiqiu")
|
||||
if not dsn:
|
||||
sys.exit("ERROR: TEST_DB_DSN / PG_DSN not set")
|
||||
|
||||
import psycopg2
|
||||
|
||||
# 起始日期固定 3月1日,结束日期为今天+1(覆盖当天营业日)
|
||||
START_DATE = date(2026, 3, 1)
|
||||
today = date.today()
|
||||
END_DATE = today + timedelta(days=1)
|
||||
|
||||
# 生成日期序列
|
||||
days = []
|
||||
d = START_DATE
|
||||
while d <= today:
|
||||
days.append(d)
|
||||
d += timedelta(days=1)
|
||||
|
||||
sql = """
|
||||
WITH day_series AS (
|
||||
SELECT unnest(%(dates)s::date[]) AS day_date
|
||||
),
|
||||
day_label AS (
|
||||
SELECT
|
||||
day_date,
|
||||
to_char(day_date, 'MM月DD日') AS day_name,
|
||||
(day_date::text || ' 08:00:00+08')::timestamptz AS day_start,
|
||||
((day_date + interval '1 day')::date::text || ' 08:00:00+08')::timestamptz AS day_end
|
||||
FROM day_series
|
||||
),
|
||||
cash_online AS (
|
||||
SELECT dl.day_date,
|
||||
COALESCE(SUM(h.pay_amount), 0) AS v
|
||||
FROM day_label dl
|
||||
LEFT JOIN dwd.dwd_settlement_head h
|
||||
ON h.create_time >= dl.day_start AND h.create_time < dl.day_end
|
||||
AND h.settle_type = 1
|
||||
GROUP BY dl.day_date
|
||||
),
|
||||
recharge AS (
|
||||
SELECT dl.day_date,
|
||||
COALESCE(SUM(ro.pay_amount), 0) AS v
|
||||
FROM day_label dl
|
||||
LEFT JOIN dwd.dwd_recharge_order ro
|
||||
ON ro.create_time >= dl.day_start AND ro.create_time < dl.day_end
|
||||
AND ro.settle_type = 5
|
||||
GROUP BY dl.day_date
|
||||
),
|
||||
groupbuy AS (
|
||||
SELECT dl.day_date,
|
||||
COALESCE(SUM(pcr.sale_price * 0.75), 0) AS v
|
||||
FROM day_label dl
|
||||
LEFT JOIN dwd.dwd_platform_coupon_redemption pcr
|
||||
ON pcr.create_time >= dl.day_start AND pcr.create_time < dl.day_end
|
||||
AND pcr.is_delete = 0
|
||||
GROUP BY dl.day_date
|
||||
),
|
||||
member_guest AS (
|
||||
SELECT dl.day_date,
|
||||
COUNT(*) FILTER (WHERE h.member_id > 0) AS member_v,
|
||||
COUNT(*) FILTER (WHERE h.member_id = 0) AS guest_v
|
||||
FROM day_label dl
|
||||
LEFT JOIN dwd.dwd_settlement_head h
|
||||
ON h.create_time >= dl.day_start AND h.create_time < dl.day_end
|
||||
AND h.settle_type = 1
|
||||
GROUP BY dl.day_date
|
||||
),
|
||||
new_member AS (
|
||||
SELECT dl.day_date,
|
||||
COUNT(DISTINCT m.member_id) AS v
|
||||
FROM day_label dl
|
||||
LEFT JOIN dwd.dim_member m
|
||||
ON m.create_time >= dl.day_start AND m.create_time < dl.day_end
|
||||
AND m.scd2_is_current = 1
|
||||
GROUP BY dl.day_date
|
||||
),
|
||||
old_recharge AS (
|
||||
SELECT dl.day_date,
|
||||
COUNT(DISTINCT ro.member_id) FILTER (WHERE ro.member_id > 0) AS persons,
|
||||
COALESCE(SUM(ro.pay_amount) FILTER (WHERE ro.member_id > 0), 0) AS amount
|
||||
FROM day_label dl
|
||||
LEFT JOIN dwd.dwd_recharge_order ro
|
||||
ON ro.create_time >= dl.day_start AND ro.create_time < dl.day_end
|
||||
AND ro.settle_type = 5
|
||||
GROUP BY dl.day_date
|
||||
)
|
||||
SELECT dl.day_name,
|
||||
ROUND(co.v + r.v + gb.v, 2) AS total,
|
||||
co.v AS cash_online,
|
||||
r.v AS recharge,
|
||||
ROUND(gb.v, 2) AS groupbuy,
|
||||
mg.member_v, mg.guest_v,
|
||||
nm.v AS new_members,
|
||||
omr.persons AS old_recharge_persons,
|
||||
omr.amount AS old_recharge_amount
|
||||
FROM day_label dl
|
||||
JOIN cash_online co USING (day_date)
|
||||
JOIN recharge r USING (day_date)
|
||||
JOIN groupbuy gb USING (day_date)
|
||||
JOIN member_guest mg USING (day_date)
|
||||
JOIN new_member nm USING (day_date)
|
||||
JOIN old_recharge omr USING (day_date)
|
||||
ORDER BY dl.day_date;
|
||||
"""
|
||||
|
||||
conn = psycopg2.connect(dsn)
|
||||
cur = conn.cursor()
|
||||
cur.execute(sql, {"dates": [d.isoformat() for d in days]})
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
|
||||
def fmt_money(v):
|
||||
"""格式化金额:千分位,两位小数"""
|
||||
return f"{v:,.2f}"
|
||||
|
||||
def fmt_int(v):
|
||||
return str(int(v))
|
||||
|
||||
# 生成 markdown
|
||||
lines = []
|
||||
lines.append(f"# 每日经营数据统计(2026-03-01 ~ {today.strftime('%Y-%m-%d')})")
|
||||
lines.append("")
|
||||
lines.append("> 数据来源:test_etl_feiqiu (DWD schema)")
|
||||
lines.append(f"> 统计时间:{today.isoformat()}")
|
||||
lines.append("> 日切规则:每天 08:00 ~ 次日 08:00(Asia/Shanghai)")
|
||||
lines.append(f"> 统计范围:2026-03-01 08:00 ~ {(today + timedelta(days=1)).isoformat()} 08:00(共 {len(days)} 个营业日)")
|
||||
lines.append("")
|
||||
lines.append("## 统计结果")
|
||||
lines.append("")
|
||||
lines.append("| 日期 | 实收合计 | 实收(纸币+线上) | 充值 | 团购结算 | 会员到店人次 | 散客到店人次 | 新会员注册数 | 老会员充值人数 | 老会员充值总额 |")
|
||||
lines.append("|------|--------:|-----------------:|-----:|---------:|------------:|------------:|------------:|--------------:|--------------:|")
|
||||
|
||||
for row in rows:
|
||||
day_name, total, cash_online, recharge, groupbuy, member_v, guest_v, new_members, old_persons, old_amount = row
|
||||
lines.append(
|
||||
f"| {day_name} | {fmt_money(total)} | {fmt_money(cash_online)} | {fmt_money(recharge)} | {fmt_money(groupbuy)} "
|
||||
f"| {fmt_int(member_v)} | {fmt_int(guest_v)} | {fmt_int(new_members)} | {fmt_int(old_persons)} | {fmt_money(old_amount)} |"
|
||||
)
|
||||
|
||||
lines.append("")
|
||||
lines.append('> 金额单位:元。实收合计 = 实收(纸币+线上) + 充值 + 团购结算')
|
||||
lines.append("")
|
||||
lines.append('## "实收"口径定义')
|
||||
lines.append("")
|
||||
lines.append("实收 = 外部渠道实际汇入门店的资金,包括三类:")
|
||||
lines.append("1. 纸币+线上:客户通过银行卡、微信、支付宝、现金直接支付的消费款")
|
||||
lines.append("2. 充值:客户通过外部渠道充入储值卡的金额")
|
||||
lines.append("3. 团购结算:团购平台(美团/抖音)结算给门店的金额")
|
||||
lines.append("")
|
||||
lines.append("以下不计入实收:")
|
||||
lines.append("- 储值卡余额扣款(`balance_amount`)— 平台内部划转,资金在充值时已入账")
|
||||
lines.append("- 团购券面值抵扣(`coupon_amount`)— 券面值 ≠ 实际结算额,门店收到的是平台结算价")
|
||||
lines.append("- 会员折扣、台费调整、抹零 — 减免项,无资金流入")
|
||||
|
||||
# 写入文件
|
||||
repo_root = Path(__file__).resolve().parents[2]
|
||||
out_path = repo_root / "docs" / "reports" / "daily-revenue-latest.md"
|
||||
out_path.write_text("\n".join(lines), encoding="utf-8")
|
||||
print(f"报告已生成: {out_path}")
|
||||
Reference in New Issue
Block a user