diff --git a/.gitignore b/.gitignore index 1be9c88..0e128b0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ package-lock.json .env /ops/shared /ops/*.log +*/__pycache__ diff --git a/docs/crawler-source-policy.md b/docs/crawler-source-policy.md new file mode 100644 index 0000000..8e4a85f --- /dev/null +++ b/docs/crawler-source-policy.md @@ -0,0 +1,26 @@ +# 爬蟲與資料來源政策 + +正式站目標是提供高時效、高可信度的世界盃投注研究資料,但所有來源都必須分級處理,避免把未授權資料當成主資料源。 + +## 來源分級 + +1. `active_api`:有授權 API、可機器讀取、可監控 quota 與錯誤率,例如 The Odds API。 +2. `official_reference`:官方賽程、公告、比分與規則來源,例如 FIFA 官方網站。 +3. `market_reference`:本地市場口徑核對來源,例如台灣運彩官方網站。 +4. `news_reference`:新聞與事件訊號來源,例如 Google 新聞 RSS、NewsAPI、路透社、BBC、ESPN。 +5. `manual_reference`:只能人工核對,不能自動高頻抓取。 + +## 台灣運彩使用原則 + +- `https://www.sportslottery.com.tw/` 可作為台灣市場投注語言、玩法名稱與盤口口徑參考。 +- 未完成條款與 robots 檢查前,不做高頻爬取。 +- 若未來要自動化導入,必須加入節流、快取、錯誤退避、User-Agent 標示與資料來源標記。 +- 台灣運彩資料不得覆蓋授權 odds API,只能作市場口徑交叉檢查與前台繁體中文術語校準。 + +## 首頁推薦資料優先序 + +1. 授權賠率 API 與官方賽程。 +2. 後端量化模型產生的勝率、期望值、模型優勢與倉位建議。 +3. 新聞與事件訊號作風險調整。 +4. 台灣運彩作台灣市場口徑參考。 +5. 若資料源不健康,首頁必須顯示資料延遲或暫停推薦,不得硬推單。 diff --git a/docs/fifa-official-data-seed-2026-06-14.md b/docs/fifa-official-data-seed-2026-06-14.md new file mode 100644 index 0000000..a7d6cd7 --- /dev/null +++ b/docs/fifa-official-data-seed-2026-06-14.md @@ -0,0 +1,29 @@ +# 2026 世界盃官方資料種子與 ingestion 邊界 + +更新時間:2026-06-14 Asia/Taipei + +## 已落地 + +- 新增 `platform/backend/app/analytics/worldcup_seed.py`。 +- 以 upsert 方式建立 16 個 2026 世界盃主辦場館,加上 `待確認場館` fallback。 +- 每筆場館包含:FIFA 賽事期間場館名稱、城市、國家、海拔、時區。 +- production compose 新增 `fifa2026-seed` 一次性服務,`backend` 與 `odds-worker` 會等待 seed 成功後啟動。 +- `/analytics/source-health` 會回報:盤口筆數、賽事數、場館數、高海拔場館數、最新盤口時間、worker 狀態。 + +## 資料來源基準 + +- FIFA 官方 2026 世界盃場館頁:`https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/articles/world-cup-2026-stadiums-fifa-soccer-football-mexico-usa-canada` +- FIFA 官方 2026 世界盃賽程頁:`https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/articles/match-schedule-fixtures-results-teams-stadiums` +- FIFA 官方賽程 PDF:`https://digitalhub.fifa.com/m/1be9ce37eb98fcc5/original/FWC26-Match-Schedule_English.pdf` + +## 邊界 + +- 場館資料可以穩定 seed,因為官方 16 個 host city/stadium 結構已明確。 +- 完整賽程不在本次硬寫,後續應以官方 schedule PDF/API parser 或授權資料源 ingestion 產生,不手動填入不完整賽程。 +- 台灣運彩可作為台灣盤口參考源,但必須先確認 robots、條款與頻率限制,不能無差別高頻爬取。 + +## 下一個 P0 + +- 建立官方賽程 parser,把 104 場完整賽事寫入 `matches`。 +- 將 sportsbook 賽事與官方 schedule 做 team/time/venue reconciliation。 +- 補上場館別模型權重,特別是 Mexico City / Guadalajara 高海拔影響。 diff --git a/docs/gemini-work-audit-2026-06-14.md b/docs/gemini-work-audit-2026-06-14.md new file mode 100644 index 0000000..0deb2cf --- /dev/null +++ b/docs/gemini-work-audit-2026-06-14.md @@ -0,0 +1,67 @@ +# Gemini 工作內容完整盤點與接續推進報告 + +盤點日期:2026-06-14(Asia/Taipei) +正式站目標:https://2026fifa.wooo.work/ + +## 一、使用者目標 + +1. 全站內容與所有溝通都必須使用繁體中文。 +2. 2026 世界盃賽事期間,首頁每天都要能看到專業分析後的投注推薦。 +3. 推薦不能只是報牌,要包含模型勝率、市場隱含機率、期望值、模型優勢、信心分數、倉位建議與資料新鮮度。 +4. 賽程、賠率、新聞、場地、資金流等資訊必須盡可能即時更新。 +5. 正式環境以 `https://2026fifa.wooo.work/` 為準,不以本機頁面完成當作交付。 + +## 二、Gemini 已完成內容 + +### 平台骨架 + +- `platform/web`:Next.js 15 前端,包含首頁、賽事中心、每日作戰室、Kelly、回測、反向盤口、Proof of Yield、球員道具盤、投資組合等頁面。 +- `platform/backend`:FastAPI 後端,包含 `/analytics/*` API、WebSocket、Redis Pub/Sub、PostgreSQL/TimescaleDB 查詢。 +- `platform/alerts`:Redis 掃描與 Telegram 高價值投注警報 worker。 +- `platform/deploy`、`ops`:Docker、K3s、PM2、Nginx、healthcheck 與部署腳本。 + +### 量化與投注功能模組 + +- 期望值、Kelly、去水、Poisson、機器學習、反向盤口、裁判天候、球員道具盤、對沖、投資組合弱點、Proof of Yield。 +- 資料庫 schema 已涵蓋 `matches`、`odds_history`、`smart_money_flow`、`value_bet_recommendations`、affiliate/copy-bet 等延伸表。 + +### 文件與產品方向 + +- `docs/professional-market-architecture.md` 定義 Gemini 路線:資料分層、賠率矩陣、期望值偵測、Poisson、Monte Carlo、CLV。 +- `docs/professional-data-reference.md` 定義官方、賠率、新聞、統計、天氣來源台帳。 + +## 三、主要缺口 + +1. 首頁原本呼叫每日作戰室資料,但缺少 Next API 代理 route,導致高勝率推薦入口可能斷線。 +2. 首頁原本連到 `/schedule`,但沒有實際賽程頁。 +3. 文件提到新聞來源,但前台沒有新聞脈搏或新聞頁。 +4. Daily Card 推薦邏輯過度樣板化,缺少市場隱含機率、模型優勢、信心分數與資料檢查點。 +5. root `README.md` 仍描述舊 Express 緊急版,與目前 `platform/web`、`platform/backend` 主線不一致。 +6. 部分頁面仍是展示骨架或樣本資料,例如回測、深度投注連結、部分 proof-of-yield 流程。 +7. FIFA 官方賽程、The Odds API、生產資料庫、新聞權重與結算回寫還沒有完整閉環。 + +## 四、本次已推進 + +1. 新增每日作戰室 API 代理:`platform/web/app/api/analytics/daily-card/[targetDate]/route.ts`。 +2. 重做首頁為「世界盃即時投研主控台」,直接顯示資料健康、今日推薦、近期賽程與新聞脈搏。 +3. 新增 Google 新聞 RSS 備援 API:`platform/web/app/api/news/route.ts`。 +4. 新增新聞頁:`platform/web/app/news/page.tsx`。 +5. 新增完整賽程頁:`platform/web/app/schedule/page.tsx`。 +6. 強化推薦卡:顯示信心分數、風險類型、市場隱含機率、模型優勢、期望值、建議配置與資料檢查點。 +7. 強化 Daily Card 後端:加入保守勝平負機率、平局機率、正期望值過濾、模型優勢過濾、相關性折減與倉位上限。 +8. 將核心新增畫面內容改為繁體中文。 + +## 五、外部來源核對 + +- FIFA 官方賽程與比分入口:https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/scores-fixtures +- The Odds API V4 文件:https://the-odds-api.com/liveapi/guides/v4/ +- The Odds API sport key 台帳:https://the-odds-api.com/sports-odds-data/sports-apis.html + +## 六、下一步 P0 + +1. 正式站部署後確認首頁是否能看到每日推薦與資料健康。 +2. 把 FIFA 官方賽程正式灌入 `matches`,建立每日校準 job。 +3. 確認 The Odds API production key、`soccer_fifa_world_cup` sport key、regions、markets 與 quota。 +4. 建立 source registry platform 版,直接回傳來源健康、延遲、最後更新時間與錯誤原因。 +5. 把 Proof of Yield 從展示帳本推到真實推薦 id、closing odds、賽果結算與 CLV。 +6. 建立賽事期間高頻刷新:賽前 24 小時、賽前 3 小時、開賽後不同頻率的賠率與新聞刷新策略。 diff --git a/docs/production-page-audit-2026-06-14.md b/docs/production-page-audit-2026-06-14.md new file mode 100644 index 0000000..19108d9 --- /dev/null +++ b/docs/production-page-audit-2026-06-14.md @@ -0,0 +1,77 @@ +# 正式環境頁面盤點與優化紀錄 + +盤點時間:2026-06-14 02:45 Asia/Taipei +正式網址:https://2026fifa.wooo.work/ + +## 一、正式站狀態 + +所有主要頁面 HTTP 狀態皆為 200: + +- `/`:首頁主控台 +- `/daily-card`:每日作戰室 +- `/schedule`:完整賽程表 +- `/news`:即時新聞情報 +- `/matches`:賽事中心 +- `/odds`:市場指數比較 +- `/sharp-money`:聰明錢追蹤 +- `/models`:量化模型庫 +- `/ml-edge`:機器學習優勢 +- `/match-conditions`:裁判/天候模型 +- `/rlm`:反向盤口雷達 +- `/proof-of-yield`:公開收益驗證 +- `/props`:球員道具盤 +- `/kelly`:凱利配置 +- `/backtesting`:策略回測 +- `/deep-bet`:一鍵配置 +- `/portfolio`:個人組合 +- `/paywall`:付費牆 +- `/offline`:離線頁 + +API 狀態: + +- `/api/news`:200,Google 新聞 RSS 備援可用。 +- `/api/analytics/matches`:200,目前只有少量賽事資料。 +- `/api/analytics/daily-card/2026-06-14`:已由 500 修復為 200。 + +## 二、已完成優化 + +1. 正式站改跑平台版 Next.js + FastAPI + TimescaleDB + Redis,不再只依賴舊 Express PM2 腳本。 +2. 首頁改為「世界盃即時投研主控台」,直接顯示資料健康、今日推薦、近期賽程、新聞脈搏與專業模組入口。 +3. 新增 `/schedule` 頁,修復原本入口存在但頁面不存在的問題。 +4. 新增 `/news` 頁與 `/api/news`,以 Google 新聞 RSS 作無金鑰備援。 +5. 修復 `/api/analytics/daily-card/[date]` Next route handler 型別與後端 `generate_daily_card` import 問題。 +6. 移除 Daily Card 的假樣本賽事 fallback,資料不足時不硬推單。 +7. 補充台灣運彩 `https://www.sportslottery.com.tw/` 為台灣市場參考來源,僅作口徑核對,不作未授權高頻抓取。 +8. 前台主要新增內容改為繁體中文,並開始清理英文模型術語。 + +## 三、目前未達標風險 + +1. 每日推薦目前可能顯示 0 組,原因是正式賠率資料與 `odds_history` 還沒有穩定支撐每日 Daily Card。 +2. `/api/analytics/matches` 目前只有少量賽事,且場地仍顯示 Unknown,官方賽程與場地資料未完整灌入。 +3. 部分進階頁面仍是模型展示或手動輸入,不是全自動真資料看板。 +4. Proof of Yield 尚未與真實推薦 id、closing odds、賽果結算完整閉環。 +5. Next.js 15.0.3 build 時提示版本存在安全風險,需升級到修補版本。 +6. 後端 `xgboost` 會拉取大型 NCCL 依賴,導致部署慢且映像偏重。 +7. 多頁仍有 Next `themeColor` metadata warning,已開始調整但需後續完全清理。 + +## 四、下一步 P0 + +1. 建立真實賽事與賠率 ingestion:FIFA 官方賽程 + The Odds API `soccer_fifa_world_cup` + h2h/spreads/totals/btts。 +2. 把 `odds_history` 補齊主勝、平局、客勝與各 bookmaker 最新盤口,讓 Daily Card 有資料可算。 +3. 建立來源健康 API:每個來源最後更新時間、延遲、錯誤、資料筆數。 +4. 首頁推薦只在資料源健康時顯示;資料不足時顯示「暫停推薦」而不是假推薦。 +5. 將台灣運彩納入 `reference_only` crawler 規格,先做條款與頻率限制檢查。 + +## 2026-06-14 03:00 Asia/Taipei 正式環境覆核 + +正式網址:`https://2026fifa.wooo.work/` + +已確認 HTTP 200:首頁、每日作戰室、完整賽程表、即時新聞情報、賽事中心、賠率、聰明錢、模型庫、ML 優勢、場地條件、RLM、公開收益、球員道具盤、凱利、回測、深度下注、投資組合、付費牆、離線頁。 + +API 覆核: + +- `/api/news`:HTTP 200,回傳 12 則新聞項目。 +- `/api/analytics/matches`:HTTP 200,回傳 3 場賽事資料。 +- `/api/analytics/daily-card/2026-06-14`:HTTP 200,但 `matched_matches=0`、`safe_singles=0`、`high_risk_singles=0`。 + +目前判定:正式站已可用,但尚未達到最終商業目標。推薦引擎已避免在缺乏真實盤口與足夠賽事資料時硬產生投注建議;下一個 P0 必須補齊即時賠率、正式賽程、場地、台灣運彩參考盤與排程 ingestion,才會穩定產出每天可檢查的高信心候選。 diff --git a/docs/professional-betting-market-playbook-2026-06-14.md b/docs/professional-betting-market-playbook-2026-06-14.md new file mode 100644 index 0000000..306ceab --- /dev/null +++ b/docs/professional-betting-market-playbook-2026-06-14.md @@ -0,0 +1,32 @@ +# 專業投注玩法與推薦計算規則 + +更新時間:2026-06-14 Asia/Taipei + +## 已支援的推薦玩法 + +| 類型 | 市場 | 勝率模型 | 推薦條件 | +|---|---|---|---| +| 單關 | 勝平負 | Poisson xG score matrix | 勝率、EV、edge、倉位上限均通過 | +| 單關 | 大小球 2.5 | Poisson 總進球分布 | 正 EV 且市場隱含機率偏差明確 | +| 單關 | 雙方進球 BTTS | 雙隊至少一球分布 | 正 EV 且雙方 xG 支撐 | +| 小倉高賠 | 勝平負 / 大小球 / BTTS | 同上 | 更高 EV/edge 門檻,嚴格小倉位 | +| 跨場串關 | 2 串 1 | 單關勝率相乘後乘 0.92 | 兩腿不同賽事,重新計算組合 EV | +| 同場串關 | 同場不同市場 | 單關勝率相乘後乘 0.68 | 高 EV 門檻,小倉位上限 | + +## 不硬推的玩法 + +以下玩法需要完整市場盤口線,不能只靠模型想像: + +- 亞洲讓球:需要 handicap line,例如 -0.25、-0.5、+0.75。 +- 球隊進球數:需要 team total line。 +- 正確比分:需要 correct score odds。 +- 半場/全場:需要 HT/FT market odds。 +- 角球、牌數、球員道具盤:需要對應事件源與市場賠率。 + +## 專業守則 + +- 沒有盤口,不產生投注推薦。 +- 有模型勝率但沒有市場賠率,只能列入觀察,不列入可下注推薦。 +- 串關不得直接把單關勝率相乘,必須套用相關性折減。 +- 同場串關風險最高,預設只允許小倉位。 +- 所有推薦都必須顯示:勝率、目標賠率、市場隱含機率、edge、EV、建議單位與資料檢查。 diff --git a/docs/realtime-scheduler-matrix-2026-06-14.md b/docs/realtime-scheduler-matrix-2026-06-14.md new file mode 100644 index 0000000..1ecbe9c --- /dev/null +++ b/docs/realtime-scheduler-matrix-2026-06-14.md @@ -0,0 +1,33 @@ +# 即時資料排程矩陣 + +更新時間:2026-06-14 Asia/Taipei + +## Production workers + +| Worker | 頻率 | 職責 | 狀態寫入 | +|---|---:|---|---| +| `fifa2026-odds-worker` | 300 秒 | 賠率、比分、比賽狀態、完賽結果同步;The Odds API + ESPN scoreboard 備援 | `ingestion:odds:last_run` | +| `fifa2026-news-worker` | 900 秒 | 世界盃新聞 RSS 快照更新 | `news:worldcup:latest`, `ingestion:news:last_run` | +| `fifa2026-fixtures-worker` | 21600 秒 | 完整賽程 fixtures JSON upsert,可用 `FIXTURES_JSON_URL` 切換官方或授權來源 | `ingestion:fixtures:last_run` | +| `fifa2026-seed` | 部署時一次 | 場館、比分欄位、安全 upsert | Docker completed success | + +## 前台讀取策略 + +- 首頁資料源健康卡讀 `/api/analytics/source-health`。 +- 新聞頁與首頁新聞讀 `/api/news`。 +- `/api/news` 優先讀 backend news snapshot;沒有快照才 fallback Google News RSS。 +- 賽事列表讀 `/api/analytics/matches`,包含繁中隊名、比分、狀態、場館。 +- 每日推薦讀 `/api/analytics/daily-card/{date}`,包含勝平負、大小球、雙方進球、串關/同場候選。 + +## 資料來源策略 + +- `FIXTURES_JSON_URL` 預設為 `https://www.thestatsapi.com/world-cup/data/fixtures.json`。 +- 若取得 FIFA 官方 API 或授權 sports data feed,直接替換 `FIXTURES_JSON_URL`,worker 不需改程式。 +- 台灣運彩 adapter 仍需確認 robots、條款與更新頻率限制後再接入。 + +## 還需要補強 + +- 台灣運彩合法參考盤 adapter。 +- live minute / possession / red card 事件源。 +- Worker failure alert:Telegram/Email 告警。 +- 資料延遲 SLA:首頁顯示下一次更新時間與 stale 警示。 diff --git a/platform/backend/app/__pycache__/main.cpython-311.pyc b/platform/backend/app/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000..19ee0f6 Binary files /dev/null and b/platform/backend/app/__pycache__/main.cpython-311.pyc differ diff --git a/platform/backend/app/analytics/__pycache__/daily_card_generator.cpython-311.pyc b/platform/backend/app/analytics/__pycache__/daily_card_generator.cpython-311.pyc new file mode 100644 index 0000000..5d63511 Binary files /dev/null and b/platform/backend/app/analytics/__pycache__/daily_card_generator.cpython-311.pyc differ diff --git a/platform/web/app/api/analytics/agent-daily-review/[date]/route.ts b/platform/web/app/api/analytics/agent-daily-review/[date]/route.ts new file mode 100644 index 0000000..a897702 --- /dev/null +++ b/platform/web/app/api/analytics/agent-daily-review/[date]/route.ts @@ -0,0 +1,34 @@ +import { NextResponse } from 'next/server'; + +const ANALYTICS_BACKEND = process.env.ANALYTICS_BACKEND_URL || 'http://127.0.0.1:8000'; + +type RouteContext = { + params: Promise<{ date: string }>; +}; + +export async function GET(_request: Request, { params }: RouteContext) { + const { date } = await params; + + if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) { + return NextResponse.json({ message: '日期格式必須為 YYYY-MM-DD' }, { status: 400 }); + } + + try { + const response = await fetch(`${ANALYTICS_BACKEND}/analytics/agent-daily-review/${encodeURIComponent(date)}`, { + 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(), ...data }); + } catch (error) { + const message = error instanceof Error ? error.message : 'NemoTron 反方稽核暫時無法連線'; + return NextResponse.json({ message }, { status: 502 }); + } +} diff --git a/platform/web/app/api/analytics/agent-verification/route.ts b/platform/web/app/api/analytics/agent-verification/route.ts new file mode 100644 index 0000000..8c42646 --- /dev/null +++ b/platform/web/app/api/analytics/agent-verification/route.ts @@ -0,0 +1,24 @@ +import { NextResponse } from 'next/server'; + +const ANALYTICS_BACKEND = process.env.ANALYTICS_BACKEND_URL || 'http://127.0.0.1:8000'; + +export async function GET() { + try { + const response = await fetch(`${ANALYTICS_BACKEND}/analytics/agent-verification`, { + 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(), ...data }); + } catch (error) { + const message = error instanceof Error ? error.message : 'AI 驗證服務暫時無法連線'; + return NextResponse.json({ message }, { status: 502 }); + } +} diff --git a/platform/web/app/api/analytics/daily-card-calendar/route.ts b/platform/web/app/api/analytics/daily-card-calendar/route.ts new file mode 100644 index 0000000..c27620c --- /dev/null +++ b/platform/web/app/api/analytics/daily-card-calendar/route.ts @@ -0,0 +1,43 @@ +import { NextResponse } from 'next/server'; + +const ANALYTICS_BACKEND = process.env.ANALYTICS_BACKEND_URL || 'http://127.0.0.1:8000'; + +export const dynamic = 'force-dynamic'; + +export async function GET(request: Request) { + const url = new URL(request.url); + const startDate = url.searchParams.get('start_date') || '2026-06-11'; + const endDate = url.searchParams.get('end_date'); + + if (!/^\d{4}-\d{2}-\d{2}$/.test(startDate)) { + return NextResponse.json({ message: '開始日期格式必須為 YYYY-MM-DD' }, { status: 400 }); + } + + if (endDate && !/^\d{4}-\d{2}-\d{2}$/.test(endDate)) { + return NextResponse.json({ message: '結束日期格式必須為 YYYY-MM-DD' }, { status: 400 }); + } + + const params = new URLSearchParams({ start_date: startDate }); + if (endDate) params.set('end_date', endDate); + + try { + const response = await fetch(`${ANALYTICS_BACKEND}/analytics/daily-card-calendar?${params.toString()}`, { + 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(data); + } catch (error) { + const message = error instanceof Error ? error.message : '日期推薦摘要服務暫時無法連線'; + return NextResponse.json({ message }, { status: 502 }); + } +} diff --git a/platform/web/app/api/analytics/daily-card-calendar/status/route.ts b/platform/web/app/api/analytics/daily-card-calendar/status/route.ts new file mode 100644 index 0000000..4284045 --- /dev/null +++ b/platform/web/app/api/analytics/daily-card-calendar/status/route.ts @@ -0,0 +1,28 @@ +import { NextResponse } from 'next/server'; + +const ANALYTICS_BACKEND = process.env.ANALYTICS_BACKEND_URL || 'http://127.0.0.1:8000'; + +export const dynamic = 'force-dynamic'; + +export async function GET() { + try { + const response = await fetch(`${ANALYTICS_BACKEND}/analytics/daily-card-calendar/status`, { + 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(data); + } catch (error) { + const message = error instanceof Error ? error.message : '日期推薦摘要快取狀態暫時無法讀取'; + return NextResponse.json({ message }, { status: 502 }); + } +} diff --git a/platform/web/app/api/analytics/gemini-usage/route.ts b/platform/web/app/api/analytics/gemini-usage/route.ts new file mode 100644 index 0000000..e4ea64c --- /dev/null +++ b/platform/web/app/api/analytics/gemini-usage/route.ts @@ -0,0 +1,24 @@ +import { NextResponse } from 'next/server'; + +const ANALYTICS_BACKEND = process.env.ANALYTICS_BACKEND_URL || 'http://127.0.0.1:8000'; + +export async function GET() { + try { + const response = await fetch(`${ANALYTICS_BACKEND}/analytics/gemini-usage`, { + 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(), ...data }); + } catch (error) { + const message = error instanceof Error ? error.message : 'Gemini 用量服務暫時無法連線'; + return NextResponse.json({ message }, { status: 502 }); + } +} diff --git a/platform/web/app/api/analytics/market-coverage/route.ts b/platform/web/app/api/analytics/market-coverage/route.ts new file mode 100644 index 0000000..9027c36 --- /dev/null +++ b/platform/web/app/api/analytics/market-coverage/route.ts @@ -0,0 +1,29 @@ +import { NextResponse } from 'next/server'; + +const ANALYTICS_BACKEND = process.env.ANALYTICS_BACKEND_URL || 'http://127.0.0.1:8000'; + +export async function GET(request: Request) { + const url = new URL(request.url); + const daysAhead = url.searchParams.get('days_ahead') || '2'; + + try { + const response = await fetch(`${ANALYTICS_BACKEND}/analytics/market-coverage?days_ahead=${encodeURIComponent(daysAhead)}`, { + 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(data); + } catch (error) { + const message = error instanceof Error ? error.message : '盤口覆蓋率暫時無法連線'; + return NextResponse.json({ message }, { status: 502 }); + } +} diff --git a/platform/web/app/api/analytics/recommendation-performance/route.ts b/platform/web/app/api/analytics/recommendation-performance/route.ts new file mode 100644 index 0000000..e69de29