from __future__ import annotations from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi import Query from fastapi.middleware.cors import CORSMiddleware from app.models import ( Account, ClsNewsDocument, DailyInputDocument, DailyInputUpsertPayload, ReportDocument, ReportListItem, ) from app.services.domain import ( get_accounts, get_cls_news, list_reports, load_daily_input, load_report, normalize_daily_input, normalize_date, refresh_cls_news, save_daily_input, save_report, seed_demo_content, generate_report, ) from app.services.storage import init_database @asynccontextmanager async def lifespan(_app: FastAPI): init_database() seed_demo_content() yield app = FastAPI( title="WeChat Finance Daily", version="0.1.0", lifespan=lifespan, ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/api/health") def health() -> dict[str, str]: return {"status": "ok"} @app.get("/api/accounts", response_model=list[Account]) def accounts() -> list[Account]: return get_accounts() @app.get("/api/daily-inputs/{date_str}", response_model=DailyInputDocument) def get_daily_inputs(date_str: str) -> DailyInputDocument: return load_daily_input(normalize_date(date_str)) @app.put("/api/daily-inputs/{date_str}", response_model=DailyInputDocument) def put_daily_inputs(date_str: str, payload: DailyInputUpsertPayload) -> DailyInputDocument: document = normalize_daily_input(normalize_date(date_str), payload) return save_daily_input(document) @app.post("/api/reports/{date_str}/generate", response_model=ReportDocument) def generate_daily_report(date_str: str) -> ReportDocument: normalized_date = normalize_date(date_str) input_document = load_daily_input(normalized_date) report = generate_report(normalized_date, input_document) return save_report(report) @app.get("/api/reports", response_model=list[ReportListItem]) def get_report_list() -> list[ReportListItem]: return list_reports() @app.get("/api/opinions/{date_str}", response_model=ReportDocument) def get_opinion_report(date_str: str) -> ReportDocument: normalized_date = normalize_date(date_str) existing = load_report(normalized_date) if existing is not None: return existing report = generate_report(normalized_date, load_daily_input(normalized_date)) return save_report(report) @app.get("/api/cls-news", response_model=ClsNewsDocument) def get_cls_news_payload(date: str | None = Query(default=None)) -> ClsNewsDocument: return get_cls_news(date) @app.post("/api/cls-news/refresh", response_model=ClsNewsDocument) def refresh_cls_news_payload(date: str | None = Query(default=None)) -> ClsNewsDocument: return refresh_cls_news(date)