From 50ef2eac1dd0989711cad3c2a4a693bbb2e0d2bb Mon Sep 17 00:00:00 2001 From: ogt Date: Thu, 25 Jun 2026 14:13:57 +0800 Subject: [PATCH] fix: bootstrap daily recommendation snapshots --- platform/backend/app/main.py | 3 ++ .../web/app/api/analytics/daily-card/route.ts | 52 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 platform/web/app/api/analytics/daily-card/route.ts diff --git a/platform/backend/app/main.py b/platform/backend/app/main.py index 0118fd0..f5e6414 100644 --- a/platform/backend/app/main.py +++ b/platform/backend/app/main.py @@ -25,6 +25,7 @@ from .analytics.daily_card_generator import ( recalibrate_daily_card_confidence_payload, update_runtime_market_calibration, ) +from .analytics.worldcup_seed import seed_venues from .analytics.localization import ( localize_city, localize_country, @@ -3670,6 +3671,8 @@ async def relay_redis_events() -> None: @app.on_event('startup') async def on_startup() -> None: + seed_summary = await seed_venues() + logger.info('World Cup seed/schema bootstrap completed: %s', seed_summary) app.state.redis_listener = asyncio.create_task(relay_redis_events()) diff --git a/platform/web/app/api/analytics/daily-card/route.ts b/platform/web/app/api/analytics/daily-card/route.ts new file mode 100644 index 0000000..f2eca17 --- /dev/null +++ b/platform/web/app/api/analytics/daily-card/route.ts @@ -0,0 +1,52 @@ +import { NextResponse } from 'next/server'; + +const ANALYTICS_BACKEND = process.env.ANALYTICS_BACKEND_URL || 'http://127.0.0.1:8000'; + +function getTaipeiDate(): string { + const parts = new Intl.DateTimeFormat('en-US', { + timeZone: 'Asia/Taipei', + year: 'numeric', + month: '2-digit', + day: '2-digit', + }).formatToParts(new Date()); + + const year = parts.find((part) => part.type === 'year')?.value; + const month = parts.find((part) => part.type === 'month')?.value; + const day = parts.find((part) => part.type === 'day')?.value; + + return `${year}-${month}-${day}`; +} + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const targetDate = searchParams.get('date') || getTaipeiDate(); + + if (!/^\d{4}-\d{2}-\d{2}$/.test(targetDate)) { + return NextResponse.json({ message: '日期格式必須為 YYYY-MM-DD' }, { status: 400 }); + } + + try { + const response = await fetch(`${ANALYTICS_BACKEND}/analytics/daily-card/${encodeURIComponent(targetDate)}`, { + method: 'GET', + cache: 'no-store', + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + const message = await response.text(); + return NextResponse.json({ message }, { status: response.status }); + } + + const data = (await response.json()) as Record; + return NextResponse.json({ + generated_at: new Date().toISOString(), + target_date: targetDate, + ...data, + }); + } catch (error) { + const message = error instanceof Error ? error.message : '每日作戰室服務暫時無法連線'; + return NextResponse.json({ message }, { status: 502 }); + } +}