Files
Scoreboard/devdocs/04-数据模型.md
T

8.2 KiB

04 · 数据模型

所属:运动会积分榜系统 · 开发文档集 版本:v0.1(讨论稿) · 2026-09-09 上游:README · 相关:02-积分与排名规则、05-接口与实时协议


1. 实体关系概览

settings(赛事配置)

teams(单位) ──┬── athletes(运动员) ──┐
              │                      ├── entries(报名项) ── results(成绩)
sports(大项) ── events(小项) ────────┘         │
                  │                           ├── record_breaks(破纪录)
                  └── scoring_profiles(积分模板)┘
                                              │
score_ledger(积分流水) ── 汇总 ──▶ standings(排名视图/缓存)
point_adjustments(人工调整) ──────────┘

users / sessions / audit_logs / change_log

2. 表结构

SQLite 是动态类型,字段类型按习惯书写,真正的约束靠 CHECK、UNIQUE、外键与索引保证。

settings — 赛事配置

字段 类型 说明
key TEXT PK 如 event.name、display.rotate_seconds
value TEXT JSON 或纯文本
updated_at TEXT ISO8601

关键配置项:赛事名称、副标题、主办方、Logo 路径、主题色、大屏轮播间隔、排名模式、默认积分模板、是否启用审核流。

teams — 参赛单位

字段 类型 约束 说明
id INTEGER PK AUTOINCREMENT
code TEXT UNIQUE NOT NULL 短代码,用于排序与导出
name TEXT NOT NULL 全称
short_name TEXT 大屏显示用
logo_path TEXT 相对 data/uploads/
color TEXT 主题色,用于图表
sort_order INTEGER DEFAULT 0 自定义排序
active INTEGER DEFAULT 1 软删除

athletes — 运动员

字段 类型 约束 说明
id INTEGER PK
name TEXT NOT NULL
gender TEXT CHECK in ('男','女')
team_id INTEGER FK teams
employee_no TEXT 工号/学号,可空
active INTEGER DEFAULT 1

sports — 大项

字段 类型 说明
id INTEGER PK
code / name / short_name TEXT
scoring_profile_id INTEGER FK 该大项默认积分模板
sort_order / active INTEGER

events — 小项

字段 类型 说明
id INTEGER PK
sport_id INTEGER FK
code TEXT UNIQUE
name TEXT 如「男子100米」
gender TEXT 男/女/混合/不限
event_type TEXT individual / team / relay / match(球类)
scoring_profile_id INTEGER FK NULL 覆盖大项模板
multiplier REAL DEFAULT 1 权重,双倍项目填 2
record_eligible INTEGER DEFAULT 1 是否参与纪录榜
status TEXT pending / ongoing / finished / published
scheduled_at TEXT 计划时间
venue TEXT 场地
sort_order / remark

scoring_profiles — 积分模板

字段 类型 说明
id INTEGER PK
code TEXT UNIQUE
name TEXT 如「田径前8名」
tie_mode TEXT skip / average / higher,默认 skip
record_bonus_mode TEXT max / sum,默认 max

scoring_profile_rules — 模板明细

字段 类型 说明
profile_id INTEGER FK 联合主键
rank INTEGER 名次
points REAL 分值

entries — 报名项

字段 类型 说明
id INTEGER PK
event_id INTEGER FK
team_id INTEGER FK
athlete_id INTEGER FK NULL 个人项目
member_names TEXT NULL 团体/接力成员,JSON 数组
bib TEXT 号码/道次
seed TEXT 种子/预赛成绩
status TEXT registered / scratched / dq / dns
remark TEXT
UNIQUE(event_id, team_id, athlete_id) 防重复报名

results — 成绩

字段 类型 说明
id INTEGER PK
event_id INTEGER FK 冗余,便于查询
entry_id INTEGER FK UNIQUE 一条报名项一条成绩
rank INTEGER NULL 名次,可空
result_text TEXT 展示用,如「1:06.33」「6.12米」
result_value REAL NULL 比较用(秒/米/分),用于排序与破纪录判断
unit TEXT s / m / pts / 次
is_tie INTEGER 是否并列
points_override REAL NULL 手工覆盖积分(需理由)
status TEXT ok / dq / dns / dnf / withdrawn
state TEXT draft / published ★
recorded_by / recorded_at
verified_by / verified_at
note TEXT

records — 纪录库

字段 类型 说明
id INTEGER PK
event_id INTEGER FK
level TEXT meet / city / province / national
result_value REAL
result_text TEXT
holder_name / holder_team_id
achieved_at TEXT
is_current INTEGER 是否当前有效纪录
source TEXT history(历届导入)/ current(本届产生)

record_breaks — 破纪录登记

字段 类型 说明
id INTEGER PK
event_id / entry_id
level TEXT
result_value / result_text
bonus_points REAL
state TEXT pending / approved / rejected
approved_by / approved_at

score_ledger — 积分流水(核心)

字段 类型 说明
id INTEGER PK
team_id INTEGER FK 归属单位
athlete_id INTEGER NULL 归属个人(个人榜用)
event_id / entry_id / result_id NULL 溯源
source_type TEXT result / record_bonus / adjustment
source_id INTEGER 对应来源记录 id
points REAL 可正可负
voided INTEGER DEFAULT 0 作废标记
voided_by / voided_at / void_reason
created_by / created_at

point_adjustments — 人工调整

字段 类型 说明
id INTEGER PK
team_id INTEGER FK
delta REAL 正负均可
category TEXT penalty / bonus / correction
reason TEXT NOT NULL 必填
event_id INTEGER NULL 可选关联
voided / created_by / created_at

users / sessions / audit_logs / change_log

表 关键字段
users id, username UNIQUE, password_hash, display_name, role, active, last_login_at
sessions id, user_id, token_hash, expires_at, ip, user_agent
audit_logs id, user_id, action, entity_type, entity_id, before_json, after_json, ip, request_id, created_at
change_log id(version), entity_type, entity_id, action, payload_json, actor_id, created_at

3. 排名如何算(性能与一致性)

数据量:单位 ≤50、小项 ≤100、报名项 ≤2000。每次变更后全量重算耗时可忽略(毫秒级),因此:

  • 不引入复杂增量算法,直接 SELECT ... FROM score_ledger WHERE voided=0 GROUP BY team_id 聚合。
  • 排名结果可物化到 standings_cache 供大屏快速读取;缓存失效时以流水为准重算。
  • 一致性策略:流水是唯一真相,缓存永远可重建。任何「总分不对」的问题,重算一遍即可自愈。

4. 索引与约束

索引

索引 用途
entries(event_id) 按项目取报名项
entries(team_id) 按单位取报名项
results(event_id, state) 大屏取已发布成绩
score_ledger(team_id, voided) 总分聚合
score_ledger(source_type, source_id) 按来源作废流水
change_log(id) 增量补发

约束

  • results.entry_id UNIQUE
  • scoring_profile_rules(profile_id, rank) 联合主键
  • PRAGMA foreign_keys=ON

删除策略

  • 业务数据一律软删除 / 作废,不做物理删除。

5. 迁移与初始化

  • 迁移文件 db/migrations/NNN_描述.sql,按序号执行,记录在 schema_migrations 表。
  • 首次启动:建库 → 跑迁移 → 写入种子数据(默认管理员 + 默认积分模板 + 默认赛事配置)。
  • 默认管理员密码随机生成,打印到控制台,首次登录强制修改。