""" 口径验证脚本:从真实订单案例出发,交叉验证 DWD 层金额/时长/绩效字段。 策略: 1. 自动挑选 5 类有代表性的订单样本 2. 对每个订单,拉取所有关联子表的实际数值 3. 验证金额等式是否成立,标注差异 4. 输出 Markdown 报告到 SYSTEM_LOG_ROOT """ import os import sys from datetime import datetime from decimal import Decimal, ROUND_HALF_UP from pathlib import Path import psycopg2 import psycopg2.extras from dotenv import load_dotenv # ── 环境 ── load_dotenv(Path(__file__).resolve().parents[2] / ".env") DSN = os.environ.get("TEST_DB_DSN") or os.environ.get("PG_DSN") if not DSN: raise RuntimeError("TEST_DB_DSN / PG_DSN 未配置") OUTPUT_DIR = Path(os.environ.get("SYSTEM_LOG_ROOT", "")) if not OUTPUT_DIR.is_dir(): raise RuntimeError(f"SYSTEM_LOG_ROOT 不存在: {OUTPUT_DIR}") D2 = lambda v: Decimal(str(v or 0)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) NOW_STR = datetime.now().strftime("%Y-%m-%d %H:%M") TODAY = datetime.now().strftime("%Y-%m-%d") def q(cur, sql, params=None): """执行查询,返回 dict 列表""" cur.execute(sql, params) if cur.description is None: return [] cols = [d[0] for d in cur.description] return [dict(zip(cols, row)) for row in cur.fetchall()] def fmt(v): """格式化值用于 Markdown 输出""" if v is None: return "NULL" if isinstance(v, Decimal): return f"{v:,.2f}" if isinstance(v, (int,)): return f"{v:,}" return str(v) def secs_to_hm(s): """秒数转 'Xh Ym' 格式""" if s is None: return "NULL" h, m = divmod(int(s), 3600) m = m // 60 return f"{h}h {m}m" if h else f"{m}m" # ── 第一步:挑选样本订单 ── SAMPLE_QUERIES = { "A_纯台费": """ SELECT sh.order_settle_id FROM dwd.dwd_settlement_head sh WHERE sh.table_charge_money > 0 AND sh.goods_money = 0 AND sh.assistant_pd_money = 0 AND sh.assistant_cx_money = 0 AND sh.settle_type = 1 ORDER BY sh.pay_time DESC NULLS LAST LIMIT 1 """, "B_台费加商品": """ SELECT sh.order_settle_id FROM dwd.dwd_settlement_head sh WHERE sh.table_charge_money > 0 AND sh.goods_money > 0 AND sh.assistant_pd_money = 0 AND sh.settle_type = 1 ORDER BY sh.pay_time DESC NULLS LAST LIMIT 1 """, "C_台费加助教": """ SELECT sh.order_settle_id FROM dwd.dwd_settlement_head sh WHERE sh.table_charge_money > 0 AND sh.assistant_pd_money > 0 AND sh.goods_money = 0 AND sh.settle_type = 1 ORDER BY sh.pay_time DESC NULLS LAST LIMIT 1 """, "D_全要素": """ SELECT sh.order_settle_id FROM dwd.dwd_settlement_head sh WHERE sh.table_charge_money > 0 AND sh.goods_money > 0 AND sh.assistant_pd_money > 0 AND sh.settle_type = 1 ORDER BY sh.pay_time DESC NULLS LAST LIMIT 1 """, "E_含退款": """ SELECT DISTINCT r.relate_id AS order_settle_id FROM dwd.dwd_refund r ORDER BY r.relate_id DESC LIMIT 1 """, } # ── 第二步:拉取各子表数据 ── SUB_TABLE_QUERIES = { "settlement_head": """ SELECT * FROM dwd.dwd_settlement_head WHERE order_settle_id = %s """, "settlement_head_ex": """ SELECT * FROM dwd.dwd_settlement_head_ex WHERE order_settle_id = %s """, "table_fee_log": """ SELECT * FROM dwd.dwd_table_fee_log WHERE order_settle_id = %s ORDER BY table_fee_log_id """, "table_fee_log_ex": """ SELECT ex.* FROM dwd.dwd_table_fee_log_ex ex JOIN dwd.dwd_table_fee_log t ON t.table_fee_log_id = ex.table_fee_log_id WHERE t.order_settle_id = %s ORDER BY ex.table_fee_log_id """, "assistant_service_log": """ SELECT * FROM dwd.dwd_assistant_service_log WHERE order_settle_id = %s ORDER BY assistant_service_id """, "assistant_service_log_ex": """ SELECT ex.* FROM dwd.dwd_assistant_service_log_ex ex JOIN dwd.dwd_assistant_service_log a ON a.assistant_service_id = ex.assistant_service_id WHERE a.order_settle_id = %s ORDER BY ex.assistant_service_id """, "store_goods_sale": """ SELECT * FROM dwd.dwd_store_goods_sale WHERE order_settle_id = %s ORDER BY store_goods_sale_id """, "store_goods_sale_ex": """ SELECT ex.* FROM dwd.dwd_store_goods_sale_ex ex JOIN dwd.dwd_store_goods_sale g ON g.store_goods_sale_id = ex.store_goods_sale_id WHERE g.order_settle_id = %s ORDER BY ex.store_goods_sale_id """, "payment": """ SELECT * FROM dwd.dwd_payment WHERE relate_id = %s ORDER BY payment_id """, "refund": """ SELECT * FROM dwd.dwd_refund WHERE relate_id = %s ORDER BY refund_id """, "refund_ex": """ SELECT ex.* FROM dwd.dwd_refund_ex ex JOIN dwd.dwd_refund r ON r.refund_id = ex.refund_id WHERE r.relate_id = %s ORDER BY ex.refund_id """, "table_fee_adjust": """ SELECT * FROM dwd.dwd_table_fee_adjust WHERE order_settle_id = %s ORDER BY table_fee_adjust_id """, "table_fee_adjust_ex": """ SELECT ex.* FROM dwd.dwd_table_fee_adjust_ex ex JOIN dwd.dwd_table_fee_adjust a ON a.table_fee_adjust_id = ex.table_fee_adjust_id WHERE a.order_settle_id = %s ORDER BY ex.table_fee_adjust_id """, "groupbuy_redemption": """ SELECT * FROM dwd.dwd_groupbuy_redemption WHERE order_settle_id = %s ORDER BY redemption_id """, "member_balance_change": """ SELECT * FROM dwd.dwd_member_balance_change WHERE balance_change_id IN ( SELECT p.payment_id FROM dwd.dwd_payment p WHERE p.relate_id = %s ) ORDER BY balance_change_id """, } # ── 第三步:等式验证逻辑 ── def verify_settlement_equation(sh, sh_ex, tfls, asls, gss, pays, refunds, adjusts): """验证结算单级金额等式,返回 (检查项列表, 通过数, 失败数)""" checks = [] # 等式1: consume_money = table_charge_money + goods_money + assistant_pd_money + assistant_cx_money lhs = D2(sh.get("consume_money")) rhs = D2(sh.get("table_charge_money")) + D2(sh.get("goods_money")) + \ D2(sh.get("assistant_pd_money")) + D2(sh.get("assistant_cx_money")) diff = lhs - rhs checks.append({ "name": "消费总额 = 台费 + 商品 + 助教陪打 + 助教促销", "lhs": f"consume_money = {fmt(lhs)}", "rhs": f"table_charge + goods + asst_pd + asst_cx = {fmt(D2(sh.get('table_charge_money')))} + {fmt(D2(sh.get('goods_money')))} + {fmt(D2(sh.get('assistant_pd_money')))} + {fmt(D2(sh.get('assistant_cx_money')))} = {fmt(rhs)}", "diff": diff, "pass": diff == 0, }) # 等式2: 台费明细合计 vs settlement_head.table_charge_money tfl_sum = sum(D2(t.get("ledger_amount")) for t in tfls) sh_table = D2(sh.get("table_charge_money")) diff2 = sh_table - tfl_sum checks.append({ "name": "结算单台费 vs 台费明细合计(ledger_amount)", "lhs": f"sh.table_charge_money = {fmt(sh_table)}", "rhs": f"SUM(tfl.ledger_amount) = {fmt(tfl_sum)} ({len(tfls)} 条)", "diff": diff2, "pass": diff2 == 0, }) # 等式3: 助教明细合计 vs settlement_head.assistant_pd_money + assistant_cx_money asl_pd = sum(D2(a.get("ledger_amount")) for a in asls if a.get("order_assistant_type") == 1) asl_cx = sum(D2(a.get("ledger_amount")) for a in asls if a.get("order_assistant_type") == 2) asl_null = sum(D2(a.get("ledger_amount")) for a in asls if a.get("order_assistant_type") is None) sh_pd = D2(sh.get("assistant_pd_money")) sh_cx = D2(sh.get("assistant_cx_money")) diff3_pd = sh_pd - asl_pd diff3_cx = sh_cx - asl_cx checks.append({ "name": "结算单助教陪打 vs 助教明细(type=1)合计", "lhs": f"sh.assistant_pd_money = {fmt(sh_pd)}", "rhs": f"SUM(asl.ledger_amount WHERE type=1) = {fmt(asl_pd)} ({sum(1 for a in asls if a.get('order_assistant_type')==1)} 条)" + (f", type=NULL: {fmt(asl_null)} ({sum(1 for a in asls if a.get('order_assistant_type') is None)} 条)" if asl_null else ""), "diff": diff3_pd, "pass": diff3_pd == 0, }) if sh_cx > 0 or asl_cx > 0: checks.append({ "name": "结算单助教促销 vs 助教明细(type=2)合计", "lhs": f"sh.assistant_cx_money = {fmt(sh_cx)}", "rhs": f"SUM(asl.ledger_amount WHERE type=2) = {fmt(asl_cx)}", "diff": diff3_cx, "pass": diff3_cx == 0, }) # 等式4: 商品明细合计 vs settlement_head.goods_money gs_sum = sum(D2(g.get("ledger_amount")) for g in gss) sh_goods = D2(sh.get("goods_money")) diff4 = sh_goods - gs_sum checks.append({ "name": "结算单商品 vs 商品明细合计(ledger_amount)", "lhs": f"sh.goods_money = {fmt(sh_goods)}", "rhs": f"SUM(gs.ledger_amount) = {fmt(gs_sum)} ({len(gss)} 条)", "diff": diff4, "pass": diff4 == 0, }) # 等式5: 支付合计 vs settlement_head.pay_amount + balance_amount + gift_card_amount pay_sum = sum(D2(p.get("pay_amount")) for p in pays) sh_pay_total = D2(sh.get("pay_amount")) + D2(sh.get("balance_amount")) + D2(sh.get("gift_card_amount")) diff5 = sh_pay_total - pay_sum checks.append({ "name": "结算单(pay+balance+gift) vs 支付流水合计", "lhs": f"sh.(pay+balance+gift) = {fmt(D2(sh.get('pay_amount')))} + {fmt(D2(sh.get('balance_amount')))} + {fmt(D2(sh.get('gift_card_amount')))} = {fmt(sh_pay_total)}", "rhs": f"SUM(payment.pay_amount) = {fmt(pay_sum)} ({len(pays)} 笔)", "diff": diff5, "pass": diff5 == 0, }) # 等式6: 台费调整合计 vs settlement_head.adjust_amount adj_sum = sum(D2(a.get("ledger_amount")) for a in adjusts) sh_adj = D2(sh.get("adjust_amount")) diff6 = sh_adj - adj_sum checks.append({ "name": "结算单调整额 vs 台费调整明细合计", "lhs": f"sh.adjust_amount = {fmt(sh_adj)}", "rhs": f"SUM(adj.ledger_amount) = {fmt(adj_sum)} ({len(adjusts)} 条)", "diff": diff6, "pass": diff6 == 0, }) passed = sum(1 for c in checks if c["pass"]) failed = len(checks) - passed return checks, passed, failed def verify_table_fee_equations(tfls): """验证台费明细级等式,返回检查列表""" checks = [] for i, t in enumerate(tfls): # ledger_amount ≈ ledger_unit_price × real_table_use_seconds / 3600 price = D2(t.get("ledger_unit_price")) secs = int(t.get("real_table_use_seconds") or 0) calc = D2(price * secs / 3600) actual = D2(t.get("ledger_amount")) diff = actual - calc checks.append({ "name": f"台费#{i+1} ledger_amount vs 单价×时长/3600", "lhs": f"ledger_amount = {fmt(actual)}", "rhs": f"{fmt(price)} × {secs}s / 3600 = {fmt(calc)}", "diff": diff, "pass": abs(diff) <= Decimal("0.02"), # 允许 ±0.02 四舍五入误差 }) # real_table_charge_money = ledger_amount - adjust_amount - ... real = D2(t.get("real_table_charge_money")) adj = D2(t.get("adjust_amount")) coup = D2(t.get("coupon_promotion_amount")) mem = D2(t.get("member_discount_amount")) act_disc = D2(t.get("activity_discount_amount")) calc_real = actual - adj - coup - mem - act_disc diff_r = real - calc_real checks.append({ "name": f"台费#{i+1} real_charge vs ledger-adj-coup-mem-act", "lhs": f"real_table_charge_money = {fmt(real)}", "rhs": f"{fmt(actual)} - {fmt(adj)} - {fmt(coup)} - {fmt(mem)} - {fmt(act_disc)} = {fmt(calc_real)}", "diff": diff_r, "pass": abs(diff_r) <= Decimal("0.02"), }) # ledger_count vs real_table_use_seconds lc = int(t.get("ledger_count") or 0) rts = int(t.get("real_table_use_seconds") or 0) checks.append({ "name": f"台费#{i+1} ledger_count vs real_table_use_seconds", "lhs": f"ledger_count = {lc} ({secs_to_hm(lc)})", "rhs": f"real_table_use_seconds = {rts} ({secs_to_hm(rts)})", "diff": lc - rts, "pass": True, # 仅记录,不判定 "info_only": True, }) return checks def verify_assistant_equations(asls): """验证助教服务级等式""" checks = [] for i, a in enumerate(asls): price = D2(a.get("ledger_unit_price")) inc_secs = int(a.get("income_seconds") or 0) calc = D2(price * inc_secs / 3600) actual = D2(a.get("ledger_amount")) diff = actual - calc checks.append({ "name": f"助教#{i+1} ledger_amount vs 单价×计费秒/3600", "lhs": f"ledger_amount = {fmt(actual)}", "rhs": f"{fmt(price)} × {inc_secs}s / 3600 = {fmt(calc)}", "diff": diff, "pass": abs(diff) <= Decimal("0.02"), }) # income_seconds vs real_use_seconds real_secs = int(a.get("real_use_seconds") or 0) checks.append({ "name": f"助教#{i+1} income_seconds vs real_use_seconds", "lhs": f"income_seconds = {inc_secs} ({secs_to_hm(inc_secs)})", "rhs": f"real_use_seconds = {real_secs} ({secs_to_hm(real_secs)})", "diff": inc_secs - real_secs, "pass": True, "info_only": True, }) # projected_income / ledger_amount = 分成比例 proj = D2(a.get("projected_income")) if actual > 0: ratio = proj / actual checks.append({ "name": f"助教#{i+1} 分成比例 projected_income/ledger_amount", "lhs": f"projected_income = {fmt(proj)}", "rhs": f"ledger_amount = {fmt(actual)}, 比例 = {ratio:.4f}", "diff": Decimal("0"), "pass": True, "info_only": True, }) return checks def verify_goods_equations(gss): """验证商品销售级等式""" checks = [] for i, g in enumerate(gss): price = D2(g.get("ledger_unit_price")) count = int(g.get("ledger_count") or 0) calc = D2(price * count) actual = D2(g.get("ledger_amount")) diff = actual - calc checks.append({ "name": f"商品#{i+1}({g.get('ledger_name','?')}) ledger_amount vs 单价×数量", "lhs": f"ledger_amount = {fmt(actual)}", "rhs": f"{fmt(price)} × {count} = {fmt(calc)}", "diff": diff, "pass": abs(diff) <= Decimal("0.02"), }) real = D2(g.get("real_goods_money")) disc = D2(g.get("discount_money")) coup = D2(g.get("coupon_share_money")) calc_real = actual - disc - coup diff_r = real - calc_real checks.append({ "name": f"商品#{i+1} real_goods_money vs ledger-disc-coup", "lhs": f"real_goods_money = {fmt(real)}", "rhs": f"{fmt(actual)} - {fmt(disc)} - {fmt(coup)} = {fmt(calc_real)}", "diff": diff_r, "pass": abs(diff_r) <= Decimal("0.02"), }) return checks # ── 第四步:数据新鲜度查询 ── FRESHNESS_SQL = """ SELECT 'dwd_settlement_head' AS tbl, MAX(create_time) AS max_ct, MAX(pay_time) AS max_biz, COUNT(*) AS cnt FROM dwd.dwd_settlement_head UNION ALL SELECT 'dwd_table_fee_log', MAX(create_time), MAX(start_use_time), COUNT(*) FROM dwd.dwd_table_fee_log UNION ALL SELECT 'dwd_assistant_service_log', MAX(create_time), MAX(start_use_time), COUNT(*) FROM dwd.dwd_assistant_service_log UNION ALL SELECT 'dwd_store_goods_sale', MAX(create_time), NULL, COUNT(*) FROM dwd.dwd_store_goods_sale UNION ALL SELECT 'dwd_payment', MAX(create_time), MAX(pay_time), COUNT(*) FROM dwd.dwd_payment UNION ALL SELECT 'dwd_refund', MAX(create_time), MAX(pay_time), COUNT(*) FROM dwd.dwd_refund UNION ALL SELECT 'dwd_groupbuy_redemption', MAX(create_time), NULL, COUNT(*) FROM dwd.dwd_groupbuy_redemption UNION ALL SELECT 'dwd_table_fee_adjust', MAX(adjust_time), NULL, COUNT(*) FROM dwd.dwd_table_fee_adjust UNION ALL SELECT 'dwd_recharge_order', MAX(create_time), MAX(pay_time), COUNT(*) FROM dwd.dwd_recharge_order UNION ALL SELECT 'dwd_member_balance_change', MAX(change_time), NULL, COUNT(*) FROM dwd.dwd_member_balance_change ORDER BY tbl """ # ── 第五步:生成报告 ── def render_checks_table(checks): """渲染检查结果为 Markdown 表格""" lines = [] lines.append("| # | 检查项 | 左侧 | 右侧 | 差异 | 结果 |") lines.append("|---|--------|------|------|------|------|") for i, c in enumerate(checks, 1): info = c.get("info_only", False) if info: result = "ℹ️ 信息" elif c["pass"]: result = "✅ 通过" else: result = "❌ 不等" diff_str = fmt(c["diff"]) if not info else str(c["diff"]) lines.append(f"| {i} | {c['name']} | {c['lhs']} | {c['rhs']} | {diff_str} | {result} |") return "\n".join(lines) def render_row_detail(label, rows, key_fields): """渲染子表数据明细""" if not rows: return f"\n**{label}**:无数据\n" lines = [f"\n**{label}**({len(rows)} 条)\n"] # 只输出 key_fields 中存在的字段 cols = [f for f in key_fields if f in rows[0]] lines.append("| " + " | ".join(cols) + " |") lines.append("| " + " | ".join(["---"] * len(cols)) + " |") for r in rows: vals = [fmt(r.get(c)) for c in cols] lines.append("| " + " | ".join(vals) + " |") return "\n".join(lines) # ── 主流程 ── def main(): conn = psycopg2.connect(DSN) conn.set_session(readonly=True) cur = conn.cursor() report = [] report.append(f"# DWD 口径验证报告(订单案例分析)\n") report.append(f"> 生成时间:{NOW_STR}") report.append(f"> 数据来源:`test_etl_feiqiu` 测试库 `dwd` schema") report.append(f"> 方法:从真实订单出发,拉取各子表实际数值,交叉验证金额等式\n") # ── 数据新鲜度 ── report.append("---\n\n## 零、数据新鲜度\n") freshness = q(cur, FRESHNESS_SQL) report.append("| 表 | 记录数 | 最晚 create_time | 最晚业务时间 |") report.append("|---|--------|-----------------|-------------|") for f in freshness: ct = str(f["max_ct"])[:19] if f["max_ct"] else "—" bt = str(f["max_biz"])[:19] if f["max_biz"] else "—" report.append(f"| `{f['tbl']}` | {f['cnt']:,} | {ct} | {bt} |") # ── 挑选样本 ── samples = {} for label, sql in SAMPLE_QUERIES.items(): rows = q(cur, sql) if rows: samples[label] = rows[0]["order_settle_id"] print(f" 样本 {label}: order_settle_id = {rows[0]['order_settle_id']}") else: print(f" 样本 {label}: 未找到匹配订单") total_pass = 0 total_fail = 0 for label, settle_id in samples.items(): report.append(f"\n---\n\n## 样本 {label}(order_settle_id = `{settle_id}`)\n") # 拉取所有子表 data = {} for tbl, sql in SUB_TABLE_QUERIES.items(): data[tbl] = q(cur, sql, (settle_id,)) sh = data["settlement_head"][0] if data["settlement_head"] else {} sh_ex = data["settlement_head_ex"][0] if data["settlement_head_ex"] else {} if not sh: report.append("> ⚠️ 结算单主表无数据,跳过\n") continue # 概览 report.append(f"- 门店:`{sh.get('site_name', '?')}`") report.append(f"- 支付时间:{str(sh.get('pay_time', '?'))[:19]}") report.append(f"- 结算类型:{sh.get('settle_type')}") report.append(f"- 会员:{sh.get('member_name', '无')} ({sh.get('member_phone', '')})") report.append(f"- 消费总额:{fmt(sh.get('consume_money'))}") report.append(f"- 台费明细:{len(data['table_fee_log'])} 条") report.append(f"- 助教服务:{len(data['assistant_service_log'])} 条") report.append(f"- 商品销售:{len(data['store_goods_sale'])} 条") report.append(f"- 支付流水:{len(data['payment'])} 笔") report.append(f"- 退款流水:{len(data['refund'])} 笔") report.append(f"- 台费调整:{len(data['table_fee_adjust'])} 条") report.append(f"- 团购核销:{len(data['groupbuy_redemption'])} 条\n") # 结算单级金额全貌 report.append("### 结算单金额全貌\n") amt_fields = [ ("consume_money", "消费总额"), ("table_charge_money", "台费"), ("goods_money", "商品原价"), ("real_goods_money", "商品实收"), ("assistant_pd_money", "助教陪打"), ("assistant_cx_money", "助教促销"), ("adjust_amount", "调整额"), ("pay_amount", "实付"), ("balance_amount", "余额支付"), ("gift_card_amount", "赠送卡"), ("recharge_card_amount", "充值卡"), ("coupon_amount", "优惠券"), ("point_amount", "积分"), ("member_discount_amount", "会员折扣"), ("rounding_amount", "抹零"), ] report.append("| 字段 | 含义 | 值 |") report.append("|------|------|-----|") for field, desc in amt_fields: report.append(f"| `{field}` | {desc} | {fmt(sh.get(field))} |") if sh_ex: ex_fields = [ ("service_money", "服务费"), ("cash_amount", "现金"), ("card_amount", "银行卡"), ("online_amount", "线上"), ("refund_amount", "退款"), ("prepay_money", "预付"), ] report.append("\n**扩展表(_ex)**\n") report.append("| 字段 | 含义 | 值 |") report.append("|------|------|-----|") for field, desc in ex_fields: report.append(f"| `{field}` | {desc} | {fmt(sh_ex.get(field))} |") # 子表明细 tfl_keys = ["table_fee_log_id", "ledger_name", "ledger_unit_price", "ledger_count", "real_table_use_seconds", "ledger_amount", "real_table_charge_money", "adjust_amount", "start_use_time", "ledger_end_time", "add_clock_seconds"] report.append(render_row_detail("台费明细", data["table_fee_log"], tfl_keys)) asl_keys = ["assistant_service_id", "nickname", "skill_name", "order_assistant_type", "level_name", "ledger_unit_price", "income_seconds", "real_use_seconds", "ledger_amount", "projected_income", "add_clock", "start_use_time", "last_use_time"] report.append(render_row_detail("助教服务明细", data["assistant_service_log"], asl_keys)) gs_keys = ["store_goods_sale_id", "ledger_name", "ledger_unit_price", "ledger_count", "ledger_amount", "discount_money", "real_goods_money", "coupon_share_money", "cost_money"] report.append(render_row_detail("商品销售明细", data["store_goods_sale"], gs_keys)) pay_keys = ["payment_id", "pay_amount", "payment_method", "pay_time"] report.append(render_row_detail("支付流水", data["payment"], pay_keys)) ref_keys = ["refund_id", "pay_amount", "payment_method", "pay_time", "channel_fee"] report.append(render_row_detail("退款流水", data["refund"], ref_keys)) adj_keys = ["table_fee_adjust_id", "ledger_amount", "table_name"] report.append(render_row_detail("台费调整", data["table_fee_adjust"], adj_keys)) # 等式验证 report.append("\n### 等式验证\n") report.append("#### 结算单级\n") checks, p, f = verify_settlement_equation( sh, sh_ex, data["table_fee_log"], data["assistant_service_log"], data["store_goods_sale"], data["payment"], data["refund"], data["table_fee_adjust"] ) report.append(render_checks_table(checks)) total_pass += p total_fail += f if data["table_fee_log"]: report.append("\n#### 台费明细级\n") tc = verify_table_fee_equations(data["table_fee_log"]) report.append(render_checks_table(tc)) total_pass += sum(1 for c in tc if c["pass"] and not c.get("info_only")) total_fail += sum(1 for c in tc if not c["pass"] and not c.get("info_only")) if data["assistant_service_log"]: report.append("\n#### 助教服务级\n") ac = verify_assistant_equations(data["assistant_service_log"]) report.append(render_checks_table(ac)) total_pass += sum(1 for c in ac if c["pass"] and not c.get("info_only")) total_fail += sum(1 for c in ac if not c["pass"] and not c.get("info_only")) if data["store_goods_sale"]: report.append("\n#### 商品销售级\n") gc = verify_goods_equations(data["store_goods_sale"]) report.append(render_checks_table(gc)) total_pass += sum(1 for c in gc if c["pass"] and not c.get("info_only")) total_fail += sum(1 for c in gc if not c["pass"] and not c.get("info_only")) # 汇总 report.append(f"\n---\n\n## 汇总\n") report.append(f"- 样本数:{len(samples)}") report.append(f"- 等式检查通过:{total_pass}") report.append(f"- 等式检查失败:{total_fail}") if total_fail > 0: report.append(f"\n> ⚠️ 存在 {total_fail} 项等式不成立,需进一步排查口径定义。") else: report.append(f"\n> ✅ 所有等式验证通过。") cur.close() conn.close() # 写入文件 out_path = OUTPUT_DIR / f"{TODAY}__calibration_order_analysis.md" out_path.write_text("\n".join(report), encoding="utf-8") print(f"\n报告已写入: {out_path}") # 同时写入 docs/reports/ 作为文档归档 doc_path = Path(__file__).resolve().parents[2] / "docs" / "reports" / "dwd-calibration-order-analysis.md" doc_path.write_text("\n".join(report), encoding="utf-8") print(f"文档归档: {doc_path}") if __name__ == "__main__": main()