57 lines
1.7 KiB
Python
57 lines
1.7 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
import pandas as pd
|
|
from fastapi import APIRouter, HTTPException, Query
|
|
from sqlalchemy import text
|
|
|
|
from ..db import get_engine
|
|
from ..services.schema_discovery import discover_schema
|
|
from ..services.timeseries import normalize_timeseries
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/overview")
|
|
def overview():
|
|
"""
|
|
返回 BI 顶部卡片核心指标(尽量从现有表自动推断)。
|
|
"""
|
|
engine = get_engine()
|
|
schema = discover_schema(engine)
|
|
if not schema.sales_table:
|
|
raise HTTPException(status_code=422, detail="未发现可用销量/订单明细表(需要至少包含 product_id + 时间 + 数量/金额)")
|
|
|
|
q = text(schema.overview_sql)
|
|
with engine.connect() as conn:
|
|
row = conn.execute(q).mappings().first()
|
|
return {"schema": schema.model_dump(), "metrics": dict(row) if row else {}}
|
|
|
|
|
|
@router.get("/sales/timeseries")
|
|
def sales_timeseries(
|
|
product_id: str = Query(..., min_length=1),
|
|
days: int = Query(30, ge=1, le=365),
|
|
):
|
|
engine = get_engine()
|
|
schema = discover_schema(engine)
|
|
if not schema.sales_table:
|
|
raise HTTPException(status_code=422, detail="未发现可用销量/订单明细表")
|
|
|
|
since = datetime.utcnow() - timedelta(days=days)
|
|
q = text(schema.timeseries_sql)
|
|
with engine.connect() as conn:
|
|
df = pd.read_sql(
|
|
q,
|
|
conn,
|
|
params={"product_id": product_id, "since": since},
|
|
)
|
|
if df.empty:
|
|
return {"product_id": product_id, "points": []}
|
|
|
|
points = normalize_timeseries(df, ts_col="ds", value_cols=["units", "gmv"])
|
|
return {"product_id": product_id, "points": points}
|
|
|