"""门店财务相关 sandbox 重算实现。 F1-6 沙箱时光机阶段 B Sprint 2 #5 新建。 覆盖指标: - P1-13 累计 GMV(get_total_gmv)— sprint 2 #5 设计要点: - **门店级**指标(不是会员级),函数签名无 member_id 参数 - 数据源:`dws_finance_daily_summary`(门店日度财务汇总,daily 累计) - sandbox 语义:SUM(gross_amount) WHERE stat_date <= ctx.business_date - 与 #1/#2(会员级单行查询)不同,本指标是多行 SUM 聚合 - 无原 fdw_queries.get_total_gmv 函数 → **不写 thin wrapper**(改动最小化) 口径(BD_manual_dws_finance_daily_summary §字段说明): gross_amount = table_fee_amount + goods_amount + assistant_pd_amount + assistant_cx_amount # 注意:**不含 electricity_money**(与会员级 items_sum 不同) # 当前生产 electricity_money 全 0(DWD-DOC 第 5 条规则),实际数值无差, # 但语义层文档化,避免与 #2 累计消费总额做语义混淆/交叉验证 与 board_service.get_finance_overview 的关系: `board_service.get_finance_overview` 已用 SUM(gross_amount) 但是按 start_date / end_date 区间聚合(BOARD-3 经营一览面板),语义是"区间 GMV"。 本指标是"开店至 ref_date 累计 GMV",**两者并存,语义不同,本指标不影响那个**。 无数据语义: 开店前 / 无 dws 数据时 SUM=NULL,SQL 层用 COALESCE(SUM(...), 0) 兜底 返回 Decimal('0'),不返回 None。业务语义:开店前累计 GMV = 0(没营收), 不是"未知"。 """ from __future__ import annotations from decimal import Decimal from typing import Any from app.services.runtime_context import RuntimeContext from app.services.sandbox_replay._decorator import runtime_aware from app.trace.decorators import trace_service @trace_service( description_zh="获取累计 GMV(sandbox_replay,门店级)", description_en="Get cumulative GMV (sandbox_replay, store level)", ) @runtime_aware(metric="total_gmv") def get_total_gmv( conn: Any, site_id: int, *, etl_conn: Any = None, ctx: RuntimeContext, ) -> Decimal: """查询门店从开店至 ref_date 的累计 GMV(sandbox_replay 版本)。 设计要点: - **门店级**指标(无 member_id 参数,与 #1-#4 会员级粒度不同) - 取业务日为基准(ctx.business_date,sandbox 模式下为 sandbox_date) - 仅 SUM stat_date <= business_date 的快照行(显式上界,与视图过滤一致) - SQL 层 COALESCE 兜底,无数据返回 Decimal('0')(非 None) Args: conn: zqyy_app 业务库连接 site_id: 门店 ID etl_conn: 可选,显式传 ETL 连接(便于测试 mock) ctx: RuntimeContext(由 @runtime_aware 自动注入) Returns: Decimal 累计 GMV(无数据时返回 Decimal('0'),不返回 None) 使用方:暂未接入(Sprint 2 #5 留给 AI app7 + 新版本迭代场景直接 import)。 """ from app.services.fdw_queries import _fdw_context ref_date = ctx.business_date with _fdw_context(conn, site_id, etl_conn=etl_conn) as cur: cur.execute( """ SELECT COALESCE(SUM(gross_amount), 0) AS total_gmv FROM app.v_dws_finance_daily_summary WHERE stat_date <= %s """, (ref_date,), ) row = cur.fetchone() return Decimal(str(row[0])) if row and row[0] is not None else Decimal("0")