feat: 采集管线基础设施全面接线
后端: - bzzoiro events 采集成功后写入 RawEvent(Bronze 层) - 采集失败写入 IngestFailure(死信队列) - 写入 DataLineage(ETL 血缘追踪) - stats 回填成功后写入 RawEvent + DataLineage - 新增 DataQualityScheduler(每小时自动质量检查) - 新增 /api/v1/admin/data-quality 端点 - 新增 /api/v1/admin/ingest-failures 端点(失败记录 + 重试) - 新增 /api/v1/admin/llm/ping 端点(LLM 连通性测试) - lifespan 启动 quality_scheduler - Match 表补充 CHECK 约束(完赛必须有比分、状态枚举、半场≤全场) - Prediction FK 加 ondelete="CASCADE" 与迁移对齐 - PredictRequest mode 加 pattern 校验 前端: - 新增「数据管线」管理页(/admin/data-pipeline) - 采集失败记录列表(状态/重试次数/错误类型 + 重试按钮) - 数据质量检查结果(通过/未通过/严重度) - 手动触发质量检查按钮 - 预测历史:比赛信息内嵌(日期/队名/主客徽标/赛果自动填充) - 导航统一为 React Router Link - AdminStats 类型扩展(matches/stats/standings 真实计数) Co-Authored-By: new-provider/LongCat-2.0 <<EMAIL>>
This commit is contained in:
co-authored by
new-provider/LongCat-2.0 <
parent
ae89d0f04f
commit
6a4994097a
+20
-2
@@ -94,7 +94,7 @@ class Match(Base):
|
||||
foreign_keys=[away_team_id], back_populates="away_matches", lazy="selectin"
|
||||
)
|
||||
stats: Mapped["MatchStats | None"] = relationship(
|
||||
back_populates="match", cascade="all, delete-orphan", lazy="selectin"
|
||||
back_populates="match", cascade="all, delete-orphan", lazy="select"
|
||||
)
|
||||
predictions: Mapped[list["Prediction"]] = relationship(back_populates="match", cascade="all, delete-orphan")
|
||||
|
||||
@@ -111,6 +111,24 @@ class Match(Base):
|
||||
"match_date_date",
|
||||
unique=True,
|
||||
),
|
||||
# DB-5: 数据库级约束 — 已完赛比赛必须有比分
|
||||
CheckConstraint(
|
||||
"match_status <> 'finished' OR (home_goals IS NOT NULL AND away_goals IS NOT NULL)",
|
||||
name="ck_matches_finished_has_score",
|
||||
),
|
||||
CheckConstraint(
|
||||
"match_status IN ('finished', 'scheduled', 'in_play', 'paused', 'postponed', 'cancelled', 'suspended')",
|
||||
name="ck_matches_status_enum",
|
||||
),
|
||||
# 半场进球 ≤ 全场进球
|
||||
CheckConstraint(
|
||||
"home_ht_goals IS NULL OR home_goals IS NULL OR home_ht_goals <= home_goals",
|
||||
name="ck_matches_home_ht_le_full",
|
||||
),
|
||||
CheckConstraint(
|
||||
"away_ht_goals IS NULL OR away_goals IS NULL OR away_ht_goals <= away_goals",
|
||||
name="ck_matches_away_ht_le_full",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -196,7 +214,7 @@ class Prediction(Base):
|
||||
__tablename__ = "predictions"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
match_id: Mapped[int] = mapped_column(ForeignKey("matches.id"), nullable=False)
|
||||
match_id: Mapped[int] = mapped_column(ForeignKey("matches.id", ondelete="CASCADE"), nullable=False)
|
||||
provider: Mapped[str] = mapped_column(String(30), nullable=False)
|
||||
model: Mapped[str] = mapped_column(String(80), nullable=False)
|
||||
prompt_version: Mapped[str] = mapped_column(String(20), nullable=False, default="v1")
|
||||
|
||||
Reference in New Issue
Block a user