Files
Neo-ZQYY/scripts/ops/monthly_revenue_report.py
Neo 779b2f6d52 chore: v1 整理 — 清理历史文件、DDL 合并、文档归档
- 清理 1155 个已删除的历史文件(废弃 prompt_logs、tmp、旧 ops 脚本)
- export/ 数据文件从 git 移除(已在 .gitignore)
- demo-miniprogram 从 tmp/ 移入 apps/,添加 CLAUDE.md 注解
- DDL 合并:完整 schema 定义填充到 db/*/schemas/(从 docs/database/ddl/ 复制)
- 39 个 v1 迁移脚本归档到 db/_archived/migrations_v1_merged/
- 4 个迁移变更类 BD_Manual 文档归档到 docs/database/_archived/
- .gitignore 补充 .vite/ 和 apps/*.zip
- settings.json 添加 effortLevel 默认配置
- scripts/ops/ 新增运维脚本入库

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 00:39:27 +08:00

151 lines
5.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""月度经营数据统计报告
按月汇总 2026年1月~3月 的实际收入进项,
日切 08:00 ~ 次日 08:00输出 markdown 表格到 docs/reports/monthly-revenue-q1.md
"""
import os, sys
from pathlib import Path
from datetime import date, timedelta
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")
os.environ.setdefault("PGCLIENTENCODING", "UTF8")
import psycopg2
today = date.today()
# 三个月的起止日期
months = [
("1月", date(2026, 1, 1), date(2026, 1, 31)),
("2月", date(2026, 2, 1), date(2026, 2, 28)),
("3月", date(2026, 3, 1), min(today, date(2026, 3, 31))),
]
sql = """
WITH params AS (
SELECT
%(label)s AS month_label,
(%(start)s::text || ' 08:00:00+08')::timestamptz AS period_start,
((%(end)s::date + interval '1 day')::date::text || ' 08:00:00+08')::timestamptz AS period_end
),
cash_online AS (
SELECT COALESCE(SUM(h.pay_amount), 0) AS v
FROM params p, dwd.dwd_settlement_head h
WHERE h.create_time >= p.period_start AND h.create_time < p.period_end
AND h.settle_type IN (1, 3)
),
recharge AS (
SELECT COALESCE(SUM(ro.pay_amount), 0) AS v
FROM params p, dwd.dwd_recharge_order ro
WHERE ro.create_time >= p.period_start AND ro.create_time < p.period_end
AND ro.settle_type = 5
),
groupbuy AS (
SELECT COALESCE(SUM(pcr.sale_price * 0.75), 0) AS v
FROM params p, dwd.dwd_platform_coupon_redemption pcr
WHERE pcr.create_time >= p.period_start AND pcr.create_time < p.period_end
AND pcr.is_delete = 0
),
member_guest AS (
SELECT
COUNT(*) FILTER (WHERE h.member_id > 0) AS member_v,
COUNT(*) FILTER (WHERE h.member_id = 0) AS guest_v
FROM params p, dwd.dwd_settlement_head h
WHERE h.create_time >= p.period_start AND h.create_time < p.period_end
AND h.settle_type IN (1, 3)
),
new_member AS (
SELECT COUNT(DISTINCT m.member_id) AS v
FROM params p, dwd.dim_member m
WHERE m.create_time >= p.period_start AND m.create_time < p.period_end
AND m.scd2_is_current = 1
),
old_recharge AS (
SELECT
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 params p, dwd.dwd_recharge_order ro
WHERE ro.create_time >= p.period_start AND ro.create_time < p.period_end
AND ro.settle_type = 5
)
SELECT
(SELECT month_label FROM params),
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 cash_online co, recharge r, groupbuy gb, member_guest mg, new_member nm, old_recharge omr;
"""
conn = psycopg2.connect(dsn, client_encoding="UTF8")
cur = conn.cursor()
results = []
for label, start, end in months:
cur.execute(sql, {"label": label, "start": start.isoformat(), "end": end.isoformat()})
results.append(cur.fetchone())
conn.close()
def fmt_money(v):
return f"{v:,.2f}"
def fmt_int(v):
return str(int(v))
# 生成 markdown
lines = []
lines.append(f"# 月度经营数据统计2026年1月~3月")
lines.append("")
lines.append("> 数据来源test_etl_feiqiu (DWD schema)")
lines.append(f"> 统计时间:{today.isoformat()}")
lines.append("> 日切规则:每天 08:00 ~ 次日 08:00Asia/Shanghai")
lines.append(f"> 3月数据截至{months[2][2].isoformat()}")
lines.append("")
lines.append("## 月度汇总")
lines.append("")
lines.append("| 月份 | 实收合计 | 实收(纸币+线上) | 充值 | 团购结算 | 会员到店人次 | 散客到店人次 | 新会员注册数 | 老会员充值人数 | 老会员充值总额 |")
lines.append("|------|--------:|-----------------:|-----:|---------:|------------:|------------:|------------:|--------------:|--------------:|")
totals = [0] * 9 # 9 个数值列的合计
for row in results:
label, total, cash_online, recharge, groupbuy, member_v, guest_v, new_members, old_persons, old_amount = row
vals = [float(total), float(cash_online), float(recharge), float(groupbuy),
int(member_v), int(guest_v), int(new_members), int(old_persons), float(old_amount)]
for i in range(9):
totals[i] += vals[i]
lines.append(
f"| {label} | {fmt_money(vals[0])} | {fmt_money(vals[1])} | {fmt_money(vals[2])} | {fmt_money(vals[3])} "
f"| {fmt_int(vals[4])} | {fmt_int(vals[5])} | {fmt_int(vals[6])} | {fmt_int(vals[7])} | {fmt_money(vals[8])} |"
)
# 合计行
lines.append(
f"| **Q1合计** | {fmt_money(totals[0])} | {fmt_money(totals[1])} | {fmt_money(totals[2])} | {fmt_money(totals[3])} "
f"| {fmt_int(totals[4])} | {fmt_int(totals[5])} | {fmt_int(totals[6])} | {fmt_int(totals[7])} | {fmt_money(totals[8])} |"
)
lines.append("")
lines.append('> 金额单位:元。实收合计 = 实收(纸币+线上) + 充值 + 团购结算')
lines.append('> 口径定义与 daily-revenue-latest.md 一致')
repo_root = Path(__file__).resolve().parents[2]
out_path = repo_root / "docs" / "reports" / "monthly-revenue-q1.md"
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text("\n".join(lines), encoding="utf-8")
print(f"报告已生成: {out_path}")