feat(api): 比赛上下文与联赛列表改公开只读(P1-2/P1-3)
- GET /matches/{id}/context 移除 require_admin:公开站详情页「近况/交锋」
数据来源,匿名 401 会导致前端静默空态;响应结构不变,不触发 LLM
- GET /leagues 改公开只读:公开站联赛筛选动态加载;仅返回
id/code/name/country 四个展示字段,不含配置或密钥信息
- 前端新增 useLeagues hook:优先请求 /api/v1/leagues,失败/空回退
本地五大联赛常量(已知代码保留中文标签,新联赛按 API 名称追加)
- 新增 tests/test_public_readonly_api.py(6 项):匿名 200/404、响应形状、
路由依赖声明检查(matches 路由无 require_admin)+ 判别力守卫
(ingest 路由必须检出 require_admin,防检测器恒真)
This commit is contained in:
@@ -15,13 +15,16 @@ import { fetchMatchDetail, fetchMatchContext } from '../admin/dal'
|
||||
import type { MatchDetailOut, MatchContextOut } from '../admin/types'
|
||||
import { useMatchesList } from './matches/hooks/useMatchesList'
|
||||
import { useMatchPredict } from './matches/hooks/useMatchPredict'
|
||||
import { useLeagues } from './matches/hooks/useLeagues'
|
||||
import { PredictModal } from './matches/components/MatchPredictPanel'
|
||||
import { MatchRow } from './matches/components/MatchDetailSection'
|
||||
import { Spinner, SkeletonRows, Switch, formatDateHeader, groupByDate, withinNext3Days } from './matches/ui'
|
||||
import { LEAGUES, type Match } from './matches/types'
|
||||
import { type Match } from './matches/types'
|
||||
|
||||
export default function Matches() {
|
||||
const [error, setError] = useState<string | null>(null) // 列表与预测共用(拆分前即如此)
|
||||
// P1-3: 联赛列表优先请求 /api/v1/leagues,失败/空回退本地五大联赛常量
|
||||
const leagues = useLeagues()
|
||||
const {
|
||||
league, setLeague,
|
||||
status, setStatus,
|
||||
@@ -58,7 +61,7 @@ export default function Matches() {
|
||||
}, [])
|
||||
const scrollToTop = () => window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
|
||||
const leagueName = LEAGUES.find(l => l.code === league)?.name ?? league
|
||||
const leagueName = leagues.find(l => l.code === league)?.name ?? league
|
||||
|
||||
/** 未开赛默认仅展示未来 3 天;其余状态展示全部。showAllUpcoming=true 时展开全部。 */
|
||||
const isScheduledView = status === 'scheduled'
|
||||
@@ -91,7 +94,7 @@ export default function Matches() {
|
||||
<div className="space-y-5">
|
||||
{/* ── 联赛版面切换 ── */}
|
||||
<nav className="flex items-center gap-6 overflow-x-auto border-b border-ink-900" aria-label="联赛">
|
||||
{LEAGUES.map(l => (
|
||||
{leagues.map(l => (
|
||||
<button
|
||||
key={l.code}
|
||||
onClick={() => setLeague(l.code)}
|
||||
|
||||
Reference in New Issue
Block a user