#!/usr/bin/env python3
"""Build the canonical lifetime Data Analytics report artifact."""

from __future__ import annotations

import csv
import datetime as dt
import json
from pathlib import Path
from typing import Any


ROOT = Path(__file__).resolve().parent
ARTIFACT = ROOT / "artifact.json"
TITLE = "赛迪与首都机场：上线以来哪些功能真正被使用"
GENERATED_AT = dt.datetime.now().astimezone().isoformat(timespec="seconds")
PROJECT_SHORT = {"airport": "首都机场", "saidi": "赛迪物业"}


def read_csv(name: str) -> list[dict[str, Any]]:
    with (ROOT / name).open(encoding="utf-8-sig", newline="") as handle:
        rows = list(csv.DictReader(handle))
    numeric_fields = {
        "orders", "amount", "active_users", "active_days", "orders_per_user", "avg_order_value",
        "zero_amount_orders", "refunded_orders", "dimension_value", "order_share", "rank", "restaurant_id",
        "users", "weekday_number", "table_total_records", "lifetime_records", "current_30d_records",
        "previous_30d_records", "lifetime_distinct_actors", "events", "distinct_operators", "rows",
        "distinct_ids", "distinct_order_numbers", "missing_user_orders", "future_meal_orders", "not_paid",
        "not_completed", "deleted", "prepare_status", "records",
    }
    for row in rows:
        for key, value in list(row.items()):
            if key not in numeric_fields or value in (None, ""):
                continue
            number = float(value)
            row[key] = int(number) if number.is_integer() else number
    return rows


def pct_change(current: float, previous: float) -> float | None:
    return current / previous - 1 if previous else None


def fmt_int(value: float) -> str:
    return f"{int(value):,}"


def fmt_money(value: float) -> str:
    return f"¥{value:,.2f}"


def fmt_pct(value: float, digits: int = 2) -> str:
    return f"{value:.{digits}%}"


def by_period(rows: list[dict[str, Any]], project: str, period: str) -> dict[str, Any]:
    return next(row for row in rows if row["project_key"] == project and row["period"] == period)


def main() -> None:
    meta = read_csv("project_meta.csv")
    summary = read_csv("order_summary.csv")
    channels = read_csv("channel_mix.csv")
    payments = read_csv("payment_mix.csv")
    meals = read_csv("meal_mix.csv")
    monthly = read_csv("monthly_usage.csv")
    restaurants = read_csv("restaurant_rank.csv")
    frequency = read_csv("user_frequency.csv")
    features = read_csv("feature_activity.csv")
    management = read_csv("management_activity.csv")
    quality = read_csv("data_quality.csv")
    weekdays = read_csv("weekday_mix.csv")

    launch_dates = {row["project_key"]: row["observable_launch_date"] for row in meta}
    lifetime = {key: by_period(summary, key, "上线以来") for key in PROJECT_SHORT}
    recent = {key: by_period(summary, key, "近30天") for key in PROJECT_SHORT}
    previous = {key: by_period(summary, key, "前30天") for key in PROJECT_SHORT}

    headline_metrics: list[dict[str, Any]] = []
    channel_comparison: list[dict[str, Any]] = []
    lifetime_channel_share: list[dict[str, Any]] = []
    lifetime_payment_mix: list[dict[str, Any]] = []
    lifetime_meal_mix: list[dict[str, Any]] = []
    concentration: list[dict[str, Any]] = []
    top_restaurants: list[dict[str, Any]] = []
    feature_signals: list[dict[str, Any]] = []
    zero_use_features: list[dict[str, Any]] = []
    lifetime_management = [row for row in management if row["period"] == "上线以来"]
    monthly_datasets = {
        key: [
            {**row, "month_date": f"{row['month']}-01"}
            for row in monthly if row["project_key"] == key
        ]
        for key in PROJECT_SHORT
    }

    for project_key, project_label in PROJECT_SHORT.items():
        life = lifetime[project_key]
        cur = recent[project_key]
        prev = previous[project_key]
        headline_metrics.append({
            "project_key": project_key,
            "project": project_label,
            "orders": life["orders"],
            "recent_orders_delta": pct_change(cur["orders"], prev["orders"]),
            "active_users": life["active_users"],
            "orders_per_user": life["orders_per_user"],
            "amount": life["amount"],
            "avg_order_value": life["avg_order_value"],
            "launch_date": launch_dates[project_key],
        })

        life_rows = [row for row in channels if row["project_key"] == project_key and row["period"] == "上线以来"]
        cur_rows = [row for row in channels if row["project_key"] == project_key and row["period"] == "近30天"]
        prev_rows = [row for row in channels if row["project_key"] == project_key and row["period"] == "前30天"]
        for row in life_rows:
            cur_row = next((item for item in cur_rows if item["label"] == row["label"]), None)
            prev_row = next((item for item in prev_rows if item["label"] == row["label"]), None)
            cur_orders = cur_row["orders"] if cur_row else 0
            prev_orders = prev_row["orders"] if prev_row else 0
            item = {
                "project": project_label,
                "channel": row["label"],
                "project_channel": f"{project_label}｜{row['label']}",
                "lifetime_orders": row["orders"],
                "lifetime_active_users": row["active_users"],
                "lifetime_amount": row["amount"],
                "lifetime_share": row["order_share"],
                "current_30d_orders": cur_orders,
                "previous_30d_orders": prev_orders,
                "recent_delta": pct_change(cur_orders, prev_orders),
            }
            channel_comparison.append(item)
            lifetime_channel_share.append({
                "project": project_label,
                "channel": row["label"],
                "project_channel": f"{project_label}｜{row['label']}",
                "orders": row["orders"],
                "active_users": row["active_users"],
                "amount": row["amount"],
                "order_share": row["order_share"],
            })

        project_frequency = [
            row for row in frequency
            if row["project_key"] == project_key and row["period"] == "上线以来"
        ]
        high = next(row for row in project_frequency if row["frequency_bucket"] == "21次以上")
        project_restaurants = sorted(
            [row for row in restaurants if row["project_key"] == project_key], key=lambda row: row["rank"]
        )
        project_weekdays = [
            row for row in weekdays
            if row["project_key"] == project_key and row["period"] == "上线以来"
        ]
        total_orders = sum(row["orders"] for row in project_frequency)
        total_users = sum(row["users"] for row in project_frequency)
        restaurant_orders = sum(row["orders"] for row in project_restaurants)
        weekday_orders = sum(row["orders"] for row in project_weekdays)
        concentration.append({
            "project": project_label,
            "high_frequency_user_share": high["users"] / total_users,
            "high_frequency_order_share": high["orders"] / total_orders,
            "top3_restaurant_share": sum(row["orders"] for row in project_restaurants[:3]) / restaurant_orders,
            "weekend_order_share": sum(
                row["orders"] for row in project_weekdays if row["weekday"] in ("周六", "周日")
            ) / weekday_orders,
        })
        for row in project_restaurants[:5]:
            top_restaurants.append({
                "project": project_label,
                "rank": row["rank"],
                "restaurant": row["restaurant_name"],
                "orders": row["orders"],
                "active_users": row["active_users"],
                "amount": row["amount"],
                "order_share": row["order_share"],
            })

    for row in payments:
        if row["period"] == "上线以来":
            lifetime_payment_mix.append({
                "project": PROJECT_SHORT[row["project_key"]], "payment": row["label"],
                "orders": row["orders"], "active_users": row["active_users"],
                "amount": row["amount"], "order_share": row["order_share"],
            })
    for row in meals:
        if row["period"] == "上线以来":
            lifetime_meal_mix.append({
                "project": PROJECT_SHORT[row["project_key"]], "meal": row["label"],
                "project_meal": f"{PROJECT_SHORT[row['project_key']]}｜{row['label']}",
                "orders": row["orders"], "active_users": row["active_users"],
                "amount": row["amount"], "order_share": row["order_share"],
            })

    zero_watchlist = {
        "AI 营养对话", "健康测量会话", "健康数据记录", "餐单预约", "就餐考勤",
        "线上充值", "签到打卡", "称重设备事件", "采购单", "账单缴费", "取餐柜事件", "售货机订单",
    }
    for row in features:
        item = {
            "project": PROJECT_SHORT[row["project_key"]], "feature": row["feature"],
            "evidence_type": row["evidence_type"], "lifetime_records": row["lifetime_records"],
            "lifetime_distinct_actors": row["lifetime_distinct_actors"],
            "current_30d_records": row["current_30d_records"],
            "previous_30d_records": row["previous_30d_records"],
            "recent_delta": pct_change(row["current_30d_records"], row["previous_30d_records"]),
            "source_table": row["source_table"],
        }
        if row["lifetime_records"]:
            feature_signals.append(item)
        elif row["feature"] in zero_watchlist:
            zero_use_features.append(item)

    airport_life = lifetime["airport"]
    saidi_life = lifetime["saidi"]
    airport_channels = {row["label"]: row for row in channels if row["project_key"] == "airport" and row["period"] == "上线以来"}
    saidi_channels = {row["label"]: row for row in channels if row["project_key"] == "saidi" and row["period"] == "上线以来"}
    airport_conc = next(row for row in concentration if row["project"] == "首都机场")
    saidi_conc = next(row for row in concentration if row["project"] == "赛迪物业")
    airport_export = next(row for row in features if row["project_key"] == "airport" and row["feature"] == "后台报表导出")
    saidi_limit = next(row for row in features if row["project_key"] == "saidi" and row["feature"] == "消费限额拦截")
    saidi_pickup = next(row for row in features if row["project_key"] == "saidi" and row["feature"] == "订餐取餐流程")

    sources = [
        {
            "id": "orders-source", "label": "生产库只读聚合：上线以来有效订单", "path": "analyze_feature_usage.py",
            "query": {
                "engine": "MySQL", "description": "两个项目自首个有效订单日起的订单、渠道、支付、餐段、餐厅、频次与月趋势聚合。",
                "executed_at": GENERATED_AT, "language": "SQL",
                "sql": """-- 分别在 zhct_jc_new 与 zhct_saidi 的只读连接执行
SELECT MIN(meal_date) AS observable_launch_date
FROM ydy_meal_order
WHERE pay_status=20 AND order_status=30 AND is_delete=0 AND user_id>0;

SELECT COUNT(*) AS orders, ROUND(SUM(total_price),2) AS amount,
       COUNT(DISTINCT user_id) AS active_users, COUNT(DISTINCT meal_date) AS active_days
FROM ydy_meal_order
WHERE pay_status=20 AND order_status=30 AND is_delete=0 AND user_id>0
  AND meal_date BETWEEN :observable_launch_date AND '2026-08-03';""",
                "tables_used": ["zhct_jc_new.ydy_meal_order", "zhct_saidi.ydy_meal_order", "ydy_restaurants"],
                "filters": [
                    "首都机场：2026-01-20 至 2026-08-03；赛迪：2026-01-18 至 2026-08-03",
                    "pay_status=20；order_status=30；is_delete=0；user_id>0",
                    "近30天与前30天仅用于辅助趋势，不作为主规模口径",
                ],
                "metric_definitions": [
                    "可观察上线起点：生产订单库中首个满足有效订单条件的 meal_date；不等同于合同上线或验收日期。",
                    "有效订单：满足支付、完成、未删除和有效用户条件的订单记录数。",
                    "订单流水：有效订单 total_price 合计，不等于收入、回款或毛利。",
                ],
            },
        },
        {
            "id": "feature-source", "label": "生产库只读聚合：上线以来功能行为与管理操作", "path": "feature_activity.csv",
            "query": {
                "engine": "MySQL", "description": "退款、评价、取餐、报表、审计、卡片、消费限额及健康相关业务表的上线以来聚合。",
                "executed_at": GENERATED_AT, "language": "SQL",
                "sql": """-- 各业务表使用项目可观察上线起点至 2026-08-03 的业务日期或创建时间聚合
SELECT COUNT(*) FROM ydy_export_log WHERE status=1 AND create_time BETWEEN :start AND :end;
SELECT COUNT(*) FROM ydy_consume_limit_block_log WHERE consume_time BETWEEN :start AND :end;
SELECT prepare_status, COUNT(*) FROM ydy_meal_order_pickup
WHERE meal_date BETWEEN :start AND :end GROUP BY prepare_status;""",
                "tables_used": sorted({row["source_table"] for row in features}),
                "filters": ["仅保存项目级聚合；未导出人员、手机号、订单或日志明细"],
                "metric_definitions": [
                    "主动业务动作、流程记录、规则触发、配置/建档分开解释。",
                    "记录数不等于页面访问人数；0 表示指定业务表无记录，不证明页面从未被打开。",
                ],
            },
        },
        {
            "id": "quality-source", "label": "生产库只读聚合：数据质量检查", "path": "data_quality.csv",
            "query": {
                "engine": "MySQL", "description": "订单键唯一性、未来用餐日期、状态完整性和取餐流程状态检查。",
                "executed_at": GENERATED_AT, "language": "SQL",
                "sql": """SELECT COUNT(*) AS rows, COUNT(DISTINCT id), COUNT(DISTINCT order_no),
SUM(user_id=0), SUM(meal_date>'2026-08-03'), SUM(pay_status<>20),
SUM(order_status<>30), SUM(is_delete<>0), MIN(meal_date), MAX(meal_date)
FROM ydy_meal_order;""",
                "tables_used": ["zhct_jc_new.ydy_meal_order", "zhct_saidi.ydy_meal_order", "zhct_saidi.ydy_meal_order_pickup"],
                "filters": ["报告截止日 2026-08-03；未来用餐订单不计入累计完成量"],
                "metric_definitions": ["未来用餐订单可能是正常预约，不自动标记为错误。"],
            },
        },
    ]

    cards = []
    for project_key, project_label in PROJECT_SHORT.items():
        prefix = "airport" if project_key == "airport" else "saidi"
        cards.extend([
            {
                "id": f"{prefix}-orders", "description": f"{project_label}自首个有效订单日至2026-08-03的累计有效订单。",
                "dataset": "headline_metrics", "filter": {"project_key": project_key}, "sourceId": "orders-source",
                "metrics": [
                    {"label": f"{project_label}累计订单", "field": "orders", "format": "compact"},
                    {"label": "近30天环比", "field": "recent_orders_delta", "format": "percent", "signed": True},
                ],
            },
            {
                "id": f"{prefix}-users", "description": f"{project_label}上线以来有有效订单的去重用户与累计人均订单。",
                "dataset": "headline_metrics", "filter": {"project_key": project_key}, "sourceId": "orders-source",
                "metrics": [
                    {"label": f"{project_label}累计用户", "field": "active_users", "format": "compact"},
                    {"label": "累计人均订单", "field": "orders_per_user", "format": "number"},
                ],
            },
            {
                "id": f"{prefix}-amount", "description": f"{project_label}上线以来有效订单流水与平均订单金额，不等于收入。",
                "dataset": "headline_metrics", "filter": {"project_key": project_key}, "sourceId": "orders-source",
                "metrics": [
                    {"label": f"{project_label}累计流水", "field": "amount", "format": "currency"},
                    {"label": "平均订单金额", "field": "avg_order_value", "format": "currency"},
                ],
            },
        ])

    charts = [
        {
            "id": "channel-share", "title": "上线以来功能渠道占比",
            "subtitle": "每个项目内部累计有效订单结构；百分比不用于比较两项目绝对规模。", "showDescription": True,
            "intent": "composition", "question": "项目上线以来，哪些业务入口真正承载了订单？",
            "rationale": "水平条形图适合比较项目与长标签渠道组合。", "type": "horizontalBar",
            "dataset": "lifetime_channel_share", "sourceId": "orders-source",
            "encodings": {
                "x": {"field": "project_channel", "type": "nominal", "label": "项目｜渠道"},
                "y": {"field": "order_share", "type": "quantitative", "format": "percent", "label": "累计订单占比"},
                "tooltip": [
                    {"field": "orders", "type": "quantitative", "format": "number", "label": "累计订单"},
                    {"field": "active_users", "type": "quantitative", "format": "number", "label": "累计用户"},
                    {"field": "amount", "type": "quantitative", "format": "currency", "label": "累计流水"},
                ],
            },
            "valueFormat": "percent", "layout": "full", "palette": {"kind": "categorical", "name": "blue"},
            "settings": {"orientation": "horizontal", "sort": "descending", "showValues": True},
            "surface": {"surface": "explorer", "viewMode": "both"},
        },
        {
            "id": "airport-monthly", "title": "首都机场上线以来月度有效订单",
            "subtitle": "2026年1月从20日起、8月仅含1日至3日，两个不完整月不与整月直接比较。", "showDescription": True,
            "intent": "trend", "question": "机场上线以来的累计运行规模如何形成？",
            "rationale": "月度趋势覆盖完整上线周期，同时避免日度噪声。", "type": "line",
            "dataset": "monthly_airport", "sourceId": "orders-source",
            "encodings": {
                "x": {"field": "month_date", "type": "temporal", "label": "月份"},
                "y": {"field": "orders", "type": "quantitative", "format": "compact", "label": "有效订单"},
                "tooltip": [
                    {"field": "active_users", "type": "quantitative", "format": "number", "label": "活跃用户"},
                    {"field": "amount", "type": "quantitative", "format": "currency", "label": "订单流水"},
                ],
            },
            "layout": "full", "palette": {"kind": "identity", "name": "blue"},
            "surface": {"surface": "explorer", "viewMode": "both"},
        },
        {
            "id": "saidi-monthly", "title": "赛迪物业上线以来月度有效订单",
            "subtitle": "2026年1月从18日起、8月仅含1日至3日，两个不完整月不与整月直接比较。", "showDescription": True,
            "intent": "trend", "question": "赛迪上线以来的累计运行规模如何形成？",
            "rationale": "月度趋势覆盖完整上线周期，同时避免日度噪声。", "type": "line",
            "dataset": "monthly_saidi", "sourceId": "orders-source",
            "encodings": {
                "x": {"field": "month_date", "type": "temporal", "label": "月份"},
                "y": {"field": "orders", "type": "quantitative", "format": "compact", "label": "有效订单"},
                "tooltip": [
                    {"field": "active_users", "type": "quantitative", "format": "number", "label": "活跃用户"},
                    {"field": "amount", "type": "quantitative", "format": "currency", "label": "订单流水"},
                ],
            },
            "layout": "full", "palette": {"kind": "identity", "name": "orange"},
            "surface": {"surface": "explorer", "viewMode": "both"},
        },
        {
            "id": "meal-share", "title": "上线以来餐段结构",
            "subtitle": "每个项目内部累计有效订单占比。", "showDescription": True,
            "intent": "composition", "question": "上线以来使用集中在哪些餐段？",
            "rationale": "项目与餐段组合后的水平条形图便于看长期结构差异。", "type": "horizontalBar",
            "dataset": "lifetime_meal_mix", "sourceId": "orders-source",
            "encodings": {
                "x": {"field": "project_meal", "type": "nominal", "label": "项目｜餐段"},
                "y": {"field": "order_share", "type": "quantitative", "format": "percent", "label": "累计订单占比"},
                "tooltip": [
                    {"field": "orders", "type": "quantitative", "format": "number", "label": "累计订单"},
                    {"field": "active_users", "type": "quantitative", "format": "number", "label": "累计用户"},
                ],
            },
            "valueFormat": "percent", "layout": "full", "palette": {"kind": "categorical", "name": "blue"},
            "settings": {"orientation": "horizontal", "sort": "descending", "showValues": True},
            "surface": {"surface": "explorer", "viewMode": "both"},
        },
    ]

    tables = [
        {
            "id": "channel-comparison-table", "title": "功能渠道累计与近期对比",
            "subtitle": "累计口径为主；近30天环比只用于识别近期变化。", "showDescription": True,
            "dataset": "channel_comparison", "defaultSort": {"field": "lifetime_orders", "direction": "desc"},
            "density": "spacious", "sourceId": "orders-source", "layout": "full",
            "columns": [
                {"field": "project", "label": "项目", "type": "text"}, {"field": "channel", "label": "功能渠道", "type": "text"},
                {"field": "lifetime_orders", "label": "累计订单", "format": "number"},
                {"field": "lifetime_share", "label": "累计占比", "format": "percent"},
                {"field": "lifetime_active_users", "label": "累计用户", "format": "number"},
                {"field": "current_30d_orders", "label": "近30天订单", "format": "number"},
                {"field": "recent_delta", "label": "近30天环比", "format": "percent", "movement": True},
                {"field": "lifetime_amount", "label": "累计流水", "format": "currency"},
            ],
        },
        {
            "id": "payment-table", "title": "上线以来支付方式结构", "subtitle": "支付方式来自订单业务字段。",
            "showDescription": True, "dataset": "lifetime_payment_mix", "defaultSort": {"field": "orders", "direction": "desc"},
            "density": "spacious", "sourceId": "orders-source", "layout": "full",
            "columns": [
                {"field": "project", "label": "项目", "type": "text"}, {"field": "payment", "label": "支付方式", "type": "text"},
                {"field": "orders", "label": "累计订单", "format": "number"}, {"field": "order_share", "label": "累计占比", "format": "percent"},
                {"field": "active_users", "label": "累计用户", "format": "number"}, {"field": "amount", "label": "累计流水", "format": "currency"},
            ],
        },
        {
            "id": "concentration-table", "title": "上线以来使用强度与场景集中度",
            "subtitle": "高频定义为项目上线以来累计21单及以上。", "showDescription": True,
            "dataset": "concentration", "defaultSort": {"field": "high_frequency_order_share", "direction": "desc"},
            "density": "spacious", "sourceId": "orders-source", "layout": "full",
            "columns": [
                {"field": "project", "label": "项目", "type": "text"},
                {"field": "high_frequency_user_share", "label": "高频用户占比", "format": "percent"},
                {"field": "high_frequency_order_share", "label": "高频用户订单贡献", "format": "percent"},
                {"field": "top3_restaurant_share", "label": "Top3餐厅订单占比", "format": "percent"},
                {"field": "weekend_order_share", "label": "周末订单占比", "format": "percent"},
            ],
        },
        {
            "id": "restaurants-table", "title": "上线以来各项目 Top 5 使用场景",
            "subtitle": "按累计有效订单排序；餐厅名称来自项目主数据。", "showDescription": True,
            "dataset": "top_restaurants", "defaultSort": {"field": "orders", "direction": "desc"},
            "density": "dense", "sourceId": "orders-source", "layout": "full",
            "columns": [
                {"field": "project", "label": "项目", "type": "text"}, {"field": "rank", "label": "项目内排名", "format": "number"},
                {"field": "restaurant", "label": "餐厅/场景", "type": "text"}, {"field": "orders", "label": "累计订单", "format": "number"},
                {"field": "order_share", "label": "项目内占比", "format": "percent"},
                {"field": "active_users", "label": "累计用户", "format": "number"}, {"field": "amount", "label": "累计流水", "format": "currency"},
            ],
        },
        {
            "id": "feature-signals-table", "title": "订单之外的累计功能使用证据",
            "subtitle": "上线以来有记录的业务行为；近30天只用于观察变化。", "showDescription": True,
            "dataset": "feature_signals", "defaultSort": {"field": "lifetime_records", "direction": "desc"},
            "density": "dense", "sourceId": "feature-source", "layout": "full",
            "columns": [
                {"field": "project", "label": "项目", "type": "text"}, {"field": "feature", "label": "功能", "type": "text"},
                {"field": "evidence_type", "label": "证据类型", "type": "text"},
                {"field": "lifetime_records", "label": "累计记录", "format": "number"},
                {"field": "lifetime_distinct_actors", "label": "累计去重主体", "format": "number"},
                {"field": "current_30d_records", "label": "近30天记录", "format": "number"},
                {"field": "recent_delta", "label": "近30天环比", "format": "percent", "movement": True},
            ],
        },
        {
            "id": "zero-use-table", "title": "上线以来未见业务记录的能力",
            "subtitle": "0 仅表示指定业务表无记录；没有页面点击流，不能证明页面从未打开。", "showDescription": True,
            "dataset": "zero_use_features", "defaultSort": {"field": "project", "direction": "asc"},
            "density": "dense", "sourceId": "feature-source", "layout": "full",
            "columns": [
                {"field": "project", "label": "项目", "type": "text"}, {"field": "feature", "label": "能力", "type": "text"},
                {"field": "evidence_type", "label": "证据类型", "type": "text"},
                {"field": "lifetime_records", "label": "累计记录", "format": "number"},
                {"field": "current_30d_records", "label": "近30天记录", "format": "number"},
            ],
        },
        {
            "id": "management-table", "title": "上线以来后台管理操作",
            "subtitle": "报表导出与赛迪安全审计聚合；不展示操作者身份。", "showDescription": True,
            "dataset": "management_activity", "defaultSort": {"field": "events", "direction": "desc"},
            "density": "dense", "sourceId": "feature-source", "layout": "full",
            "columns": [
                {"field": "project_name", "label": "项目", "type": "text"}, {"field": "category", "label": "类别", "type": "text"},
                {"field": "activity", "label": "操作", "type": "text"}, {"field": "result", "label": "结果", "type": "text"},
                {"field": "events", "label": "累计记录", "format": "number"},
                {"field": "distinct_operators", "label": "累计去重操作者", "format": "number"},
            ],
        },
    ]

    executive = f"""## Executive Summary

- **首都机场：上线以来是高规模消费机项目。** 从 2026-01-20 至 2026-08-03 共 {fmt_int(airport_life['orders'])} 单、{fmt_int(airport_life['active_users'])} 位用户；消费机占 {fmt_pct(airport_channels['消费机']['order_share'])}，外部订单接入占 {fmt_pct(airport_channels['外部订单接入']['order_share'])}。
- **赛迪：上线以来是“消费机＋闸机”双核心。** 从 2026-01-18 至 2026-08-03 共 {fmt_int(saidi_life['orders'])} 单、{fmt_int(saidi_life['active_users'])} 位用户；消费机占 {fmt_pct(saidi_channels['消费机']['order_share'])}，闸机占 {fmt_pct(saidi_channels['闸机']['order_share'])}，线上订餐仅占 {fmt_pct(saidi_channels['线上订餐']['order_share'])}。
- **后台最明确的长期刚需仍是运营管理。** 机场累计报表导出 {fmt_int(airport_export['lifetime_records'])} 次，但只有 {fmt_int(airport_export['lifetime_distinct_actors'])} 位操作者；赛迪累计实体卡、审计、限额控制和取餐流程都有真实记录。
- **近期变化只作补充。** 机场近30天订单环比 {pct_change(recent['airport']['orders'], previous['airport']['orders']):+.2%}；赛迪订单环比 {pct_change(recent['saidi']['orders'], previous['saidi']['orders']):+.2%}、活跃用户环比 {pct_change(recent['saidi']['active_users'], previous['saidi']['active_users']):+.2%}。赛迪近30天限额拦截 {fmt_int(saidi_limit['current_30d_records'])} 次、前30天 {fmt_int(saidi_limit['previous_30d_records'])} 次，仍需排查。"""

    blocks = [
        {"id": "title", "type": "markdown", "body": f"# {TITLE}"},
        {"id": "executive-summary", "type": "markdown", "body": executive},
        {"id": "headline-strip", "type": "metric-strip", "cardIds": [
            "airport-orders", "airport-users", "airport-amount", "saidi-orders", "saidi-users", "saidi-amount",
        ]},
        {
            "id": "scope", "type": "markdown", "sourceId": "orders-source",
            "body": """## 这版如何定义“上线以来”

生产库没有统一可核验的合同上线日期字段，因此以**首个有效订单日**作为“数据可观察上线起点”：赛迪为 2026-01-18，首都机场为 2026-01-20，统一统计至 2026-08-03。这个日期表示业务数据开始出现，不等同于合同签署、部署、验收或正式运营日期；若后续拿到项目正式上线证明，应替换起点并重新跑数。""",
        },
        {
            "id": "channel-finding", "type": "markdown", "sourceId": "orders-source",
            "body": f"""## 上线以来，真正高频的是交易入口

**首都机场**累计消费机 {fmt_int(airport_channels['消费机']['orders'])} 单、占 {fmt_pct(airport_channels['消费机']['order_share'])}；外部订单接入 {fmt_int(airport_channels['外部订单接入']['orders'])} 单、占 {fmt_pct(airport_channels['外部订单接入']['order_share'])}。产品与运维优先级应围绕消费机稳定性、外部订单一致性、退款和对账展开。

**赛迪物业**累计消费机 {fmt_int(saidi_channels['消费机']['orders'])} 单、占 {fmt_pct(saidi_channels['消费机']['order_share'])}；闸机 {fmt_int(saidi_channels['闸机']['orders'])} 单、占 {fmt_pct(saidi_channels['闸机']['order_share'])}；线上订餐 {fmt_int(saidi_channels['线上订餐']['orders'])} 单、仅占 {fmt_pct(saidi_channels['线上订餐']['order_share'])}。长期结构证明“消费机＋闸机”是双核心，线上订餐仍是补充入口。""",
        },
        {"id": "channel-chart-block", "type": "chart", "chartId": "channel-share"},
        {"id": "channel-table-block", "type": "table", "tableId": "channel-comparison-table"},
        {
            "id": "airport-trend-finding", "type": "markdown", "sourceId": "orders-source",
            "body": f"""## 首都机场：高频用户和分散场景构成规模基础

上线以来累计人均 {airport_life['orders_per_user']:.2f} 单；累计21单以上用户占 {fmt_pct(airport_conc['high_frequency_user_share'])}，贡献 {fmt_pct(airport_conc['high_frequency_order_share'])} 的订单。Top3 餐厅只占 {fmt_pct(airport_conc['top3_restaurant_share'])}，业务分布在多个餐厅与设备场景；周末占 {fmt_pct(airport_conc['weekend_order_share'])}，保障不能只按普通工作日食堂设计。""",
        },
        {"id": "airport-monthly-block", "type": "chart", "chartId": "airport-monthly"},
        {
            "id": "saidi-trend-finding", "type": "markdown", "sourceId": "orders-source",
            "body": f"""## 赛迪物业：工作日核心点位高度集中

上线以来累计人均 {saidi_life['orders_per_user']:.2f} 单；累计21单以上用户占 {fmt_pct(saidi_conc['high_frequency_user_share'])}，贡献 {fmt_pct(saidi_conc['high_frequency_order_share'])} 的订单。Top3 餐厅贡献 {fmt_pct(saidi_conc['top3_restaurant_share'])}，周末仅占 {fmt_pct(saidi_conc['weekend_order_share'])}，适合围绕少数工作日高密度点位完成产品试点和问题闭环。""",
        },
        {"id": "saidi-monthly-block", "type": "chart", "chartId": "saidi-monthly"},
        {
            "id": "meal-finding", "type": "markdown", "sourceId": "orders-source",
            "body": """## 长期餐段与支付结构决定保障重点

机场午餐占 49.27%、早餐 33.28%、晚餐 17.45%，三餐均有稳定规模；支付以刷卡 55.09%、消费码 34.91%、线上 10.00% 为主。赛迪午餐占 62.92%、早餐 28.15%，支付高度集中在消费码 84.43%。机场需要多餐段、多支付链路保障；赛迪应重点守住工作日午餐和消费码链路。""",
        },
        {"id": "meal-chart-block", "type": "chart", "chartId": "meal-share"},
        {"id": "payment-table-block", "type": "table", "tableId": "payment-table"},
        {"id": "concentration-table-block", "type": "table", "tableId": "concentration-table"},
        {"id": "restaurants-table-block", "type": "table", "tableId": "restaurants-table"},
        {
            "id": "secondary-finding", "type": "markdown", "sourceId": "feature-source",
            "body": f"""## 订单之外，长期真实使用集中在报表、退款、卡片和规则控制

机场上线以来累计报表导出 {fmt_int(airport_export['lifetime_records'])} 次，其中消费订单导出 1,177 次；全部导出记录仅涉及 1 位操作者。另有退款申请 1,473 条、实体卡 11,016 条。后台管理需求明确，但操作人集中度需要确认是否符合岗位与权限设计。

赛迪上线以来累计实体卡 2,180 条、安全审计 1,347 条、消费限额拦截 {fmt_int(saidi_limit['lifetime_records'])} 次、取餐流程 {fmt_int(saidi_pickup['lifetime_records'])} 条。取餐记录全部停留在 `prepare_status=10`（备餐中）；这是累计范围内的流程闭环问题，不只是最近30天现象。""",
        },
        {"id": "feature-signals-block", "type": "table", "tableId": "feature-signals-table"},
        {"id": "management-table-block", "type": "table", "tableId": "management-table"},
        {
            "id": "zero-use-finding", "type": "markdown", "sourceId": "feature-source",
            "body": """## 已部署不等于已使用：营养健康与 AI 上线以来仍未形成可见闭环

在本次核查的业务表中，两个项目上线以来均未见 AI 营养对话、餐单预约、签到打卡或线上充值记录；赛迪的健康测量、健康数据、称重设备、采购、账单缴费、取餐柜和售货机也为 0。赛迪历史仅有 7 条人脸记录，但订单中没有刷脸支付。

**解释边界：** 这只能说明指定业务表没有记录。生产库没有统一页面点击流，因此不能用 0 证明页面从未打开，也不能直接判断功能无价值。""",
        },
        {"id": "zero-use-table-block", "type": "table", "tableId": "zero-use-table"},
        {
            "id": "recommendations", "type": "markdown",
            "body": """## 下一步建议

1. **机场 P0：** 对消费机与外部订单接入建立按餐厅/设备的成功率、延迟、离线单、重复单、退款率与对账监控；将高频报表改为自动订阅或定时生成，降低单人操作依赖。
2. **赛迪 P0：** 以消费机＋闸机为双核心，统一监控成功率、重复消费/通行、订单一致性和午餐高峰容量；线上订餐先验证目标用户与复购，再决定投入。
3. **赛迪一周内核查：** 复盘累计 210 次限额拦截的规则分布，重点抽查近30天 95 次激增；核对 257 条取餐记录全部“备餐中”是业务设计、历史数据还是状态回写缺陷。
4. **补统一埋点：** PC、小程序、H5 和设备统一记录 page_view、feature_action、success、fail，才能回答页面访问排行、点击后失败和功能漏斗。
5. **以后所有项目统一口径：** 主看“上线以来累计使用”，再辅以近30天活跃度、环比和异常变化；不要用近期窗口替代项目全生命周期结论。""",
        },
        {
            "id": "caveats", "type": "markdown", "sourceId": "quality-source",
            "body": """## 口径与限制

- 主统计期：首都机场 2026-01-20 至 2026-08-03；赛迪 2026-01-18 至 2026-08-03。起点均为首个有效订单日。
- 近期辅助窗口：近30天 2026-07-05 至 2026-08-03；前30天 2026-06-05 至 2026-07-04；时区 Asia/Shanghai。
- 核心订单按 `meal_date` 统计，必须已支付、已完成、未删除、用户 ID 有效；2026-08-04 当日未纳入。
- 月度图中首月和2026年8月均为不完整月，不能与整月直接比较。
- 订单流水不等于收入、回款、合同额或毛利；未来用餐订单可能是正常预约，不自动判为异常。
- 本报告能回答“哪些功能产生了数据库业务记录”，不能完整回答“哪些页面被打开最多”。""",
        },
    ]

    artifact = {
        "surface": "report",
        "manifest": {
            "version": 1, "surface": "report", "title": TITLE,
            "description": "基于两个线上生产库的只读聚合，按首个有效订单日至2026-08-03识别累计高频功能，并以近30天作为趋势补充。",
            "generatedAt": GENERATED_AT, "cards": cards, "charts": charts, "tables": tables,
            "sources": sources, "blocks": blocks,
        },
        "snapshot": {
            "version": 1, "generatedAt": GENERATED_AT, "status": "ready",
            "datasets": {
                "headline_metrics": headline_metrics,
                "channel_comparison": channel_comparison,
                "lifetime_channel_share": lifetime_channel_share,
                "lifetime_payment_mix": lifetime_payment_mix,
                "lifetime_meal_mix": lifetime_meal_mix,
                "monthly_airport": monthly_datasets["airport"],
                "monthly_saidi": monthly_datasets["saidi"],
                "concentration": concentration,
                "top_restaurants": top_restaurants,
                "feature_signals": feature_signals,
                "zero_use_features": zero_use_features,
                "management_activity": lifetime_management,
                "data_quality": quality,
            },
        },
        "sources": sources,
    }
    ARTIFACT.write_text(json.dumps(artifact, ensure_ascii=False, indent=2), encoding="utf-8")
    print(ARTIFACT)


if __name__ == "__main__":
    main()
