Article

2026-06-29-襷の系譜-数据模型图

个人项目

襷の系譜 数据模型图

更新时间:2026-07-01 CST

说明:这是一版“中文解释版”数据模型文档。内容按当前项目 /Users/xuhuan/workspace_new/project/tasuki-keifu/prisma/schema.prisma 与最新迁移整理,重点补充每张表、主要字段、枚举值的中文含义,方便直接阅读。

这套模型在管什么

这套数据模型主要在管 5 类东西:

  1. 人和组织 Person 记录选手/教练/工作人员,Organization 记录初中、高中、大学、实业团、会社、俱乐部等组织。
  2. 人和组织之间的关系 Membership 记录“某人在某段时间属于某个组织”的履历。
  3. 比赛与成绩 CompetitionCompetitionEditionRace 记录赛事结构,RaceResultPersonalBest 记录个人成绩,TeamCompetitionResultTeamCompetitionLegSnapshot 记录队伍成绩。
  4. 来源与数据治理 SourceSourceReferenceAuditLogImportBatch 用来记录数据从哪来、怎么导入、怎么改过。
  5. 派生内容 PlayerRelationCache 用来缓存“选手关系”这类计算结果。

先看关系图

erDiagram
  Person ||--o{ Membership : "有所属履历"
  Organization ||--o{ Membership : "承载所属履历"
  Source ||--o{ Membership : "提供来源"

  Person ||--o{ PersonalBest : "有 PB"
  Source ||--o{ PersonalBest : "提供来源"

  Competition ||--o{ CompetitionEdition : "有赛事届次"
  CompetitionEdition ||--o{ Race : "有比赛单元"
  Source ||--o{ CompetitionEdition : "提供来源"
  Source ||--o{ Race : "提供来源"

  CompetitionEdition ||--o{ TeamCompetitionResult : "有队伍总成绩"
  Organization ||--o{ TeamCompetitionResult : "队伍成绩归属组织"
  TeamCompetitionResult ||--o{ TeamCompetitionLegSnapshot : "有分区累计快照"
  Source ||--o{ TeamCompetitionResult : "提供来源"
  Source ||--o{ TeamCompetitionLegSnapshot : "提供来源"

  Person ||--o{ RaceResult : "有个人比赛结果"
  Race ||--o{ RaceResult : "对应结果"
  Organization ||--o{ RaceResult : "比赛时代表组织"
  Source ||--o{ RaceResult : "提供来源"

  Person ||--|| PlayerRelationCache : "有关系缓存"

  Person ||--o{ NameVariant : "有人名别名"
  Organization ||--o{ NameVariant : "有组织别名"
  Competition ||--o{ NameVariant : "有赛事别名"
  Source ||--o{ NameVariant : "提供来源"

  Source ||--o{ AuditLog : "支持审计记录"

  Source ||--o{ SourceReference : "外部来源映射"
  Person ||--o{ SourceReference : "映射到人物"
  Organization ||--o{ SourceReference : "映射到组织"
  Competition ||--o{ SourceReference : "映射到赛事系列"
  CompetitionEdition ||--o{ SourceReference : "映射到赛事届次"
  Race ||--o{ SourceReference : "映射到比赛单元"

  Source ||--o{ ImportBatch : "触发导入批次"

表说明

1. Person

用途:人物主表。记录一个人本身比较稳定的信息。

常见场景:

  • 一个选手一条 Person
  • 一个教练一条 Person
  • 一个队伍经理如果要记录,也是一条 Person

主要字段:

字段中文含义
id主键
slug稳定标识,一般用于 URL
displayNameJa日文显示名
displayNameKana日文假名读音
displayNameRoman罗马字名
displayNameZh中文名
displayNameEn英文名
birthDate出生日期
hometown出身地
nationality国籍
heightCm身高,厘米
weightKg体重,公斤
type人物类型
status数据确认状态
notes备注
createdAt创建时间
updatedAt更新时间

补充说明:

  • 旧版本里提到的 registeredPrefecture 现在已经没有了。

2. Organization

用途:组织主表。把学校、大学、实业团、公司、俱乐部等统一成一种实体。

主要字段:

字段中文含义
id主键
slug稳定标识
nameJa日文正式名
nameKana日文读音
nameRoman罗马字名称
nameZh中文名称
nameEn英文名称
shortName简称
type组织类型
location所在地文本
prefecture都道府县
country国家代码,默认 JP
websiteUrl官网
status数据确认状态
notes备注

3. Membership

用途:所属履历表。记录“这个人在这段时间属于这个组织”。

例子:

  • 某人 2021 年到 2024 年就读青山学院大学
  • 某人高中阶段属于佐久长圣高校
  • 某人后来加入某实业团

主要字段:

字段中文含义
personId对应哪一个人
organizationId对应哪一个组织
type关系类型
startDate开始日期
endDate结束日期
startYear开始年份,方便查询
endYear结束年份,方便查询
faculty学部
department学科
cohort第几期/第几代
role在这段组织关系中的身份
status数据确认状态
sourceId来源

补充说明:

  • 旧文档里的 grade 已删除。
  • 当前 role 不是自由填写,而是固定枚举。

4. PersonalBest

用途:个人最好成绩快照表。只保留某人在某个项目下“当前最佳”的记录。

主要字段:

字段中文含义
personId对应人物
discipline比赛项目
mark成绩原文,如 28:12.34
markMillis方便排序和比较的毫秒值
achievedOn达成日期
competitionName赛事名称
venue场地
organizationId当时所属组织 ID,仅作快照记录
stage比赛阶段
isHighSchoolPb是否是高中阶段 PB
isCollegePb是否是大学阶段 PB
status数据确认状态
sourceId来源

补充说明:

  • 当前 Prisma 中 PersonalBest.organizationId 只是字段,没有正式外键关系。

5. Competition

用途:赛事系列表。表示一个赛事品牌或系列。

例子:

  • 箱根駅伝
  • 出雲駅伝
  • 全国高校駅伝
  • 大阪马拉松

主要字段:

字段中文含义
slug稳定标识
nameJa日文名
nameZh中文名
type赛事系列类型
organizer主办方
region地区
websiteUrl官网

6. CompetitionEdition

用途:赛事届次表。表示这个赛事在某一年、某一届的具体实例。

例子:

  • 第 102 回箱根駅伝
  • 2025 大阪马拉松

主要字段:

字段中文含义
competitionId属于哪个赛事系列
slug稳定标识
editionNumber第几届
year年份
officialName官方全名
shortName简称
startsOn开始日期
endsOn结束日期
sourceId来源

7. Race

用途:比赛单元表。表示某届赛事下更细的一场比赛、一个区间、一个组别。

例子:

  • 箱根駅伝 5 区
  • 男子 10000m 第 4 组

主要字段:

字段中文含义
competitionEditionId属于哪个赛事届次
slug稳定标识
name比赛单元名称
discipline比赛项目
leg第几区,駅伝常用
round轮次,如预选、决赛
heat第几组
distanceMeters距离,米
startsAt开始时间
status数据确认状态
sourceId来源

8. RaceResult

用途:个人比赛结果表。记录“某个人在某个 Race 里的成绩”。

主要字段:

字段中文含义
personId对应人物
raceId对应比赛单元
organizationId比赛时代表的组织
isEntry是否报名
isStarter是否实际出场
isRaceDayChange是否为比赛当天变更
mark成绩文本
markMillis成绩毫秒值
rank名次
teamRank所属队伍名次
gradeAtRace比赛时年级快照
status数据确认状态
notes备注,如区間賞、区間新
sourceId来源

9. TeamCompetitionResult

用途:队伍总成绩表。记录一个组织在某一届赛事中的最终队伍成绩。

常见场景:

  • 记录青山学院大学在某届箱根駅伝的总成绩和最终排名

主要字段:

字段中文含义
competitionEditionId对应赛事届次
organizationId对应组织
finalRank最终名次
finalMark最终总成绩文本
finalMarkMillis最终总成绩毫秒值
status数据确认状态
sourceId来源

补充说明:

  • 同一个“赛事届次 + 组织”只能有一条记录。

10. TeamCompetitionLegSnapshot

用途:队伍分区快照表。记录队伍跑完第 N 区之后的累计状态。

主要字段:

字段中文含义
teamCompetitionResultId对应哪条队伍总成绩
leg第几区
cumulativeRank跑完这一段后的累计排名
cumulativeMark跑完这一段后的累计成绩文本
cumulativeMarkMillis累计成绩毫秒值
gapFromLeader与第一名差距文本
gapFromLeaderMillis与第一名差距毫秒值
status数据确认状态
sourceId来源

11. PlayerRelationCache

用途:选手关系缓存表。不是原始事实表,而是把计算出来的关系结果缓存下来,方便页面直接读。

主要字段:

字段中文含义
personId对应哪个人
payload缓存的关系结果 JSON
generatedAt生成时间
sourceHash用于判断缓存是否过期的哈希

补充说明:

  • 一个 Person 最多对应一条关系缓存。

12. Source

用途:数据来源表。记录这条数据来自哪里。

主要字段:

字段中文含义
name来源名称
url来源链接
type来源类型
publishedOn来源发布时间
accessedOn抓取/访问时间
reliability可信度,默认 3
notes备注

13. NameVariant

用途:名称变体表。解决“同一个东西有很多种叫法”的问题。

例子:

  • 同一个人有正式日文名、媒体写法、中文译名
  • 同一个学校有简称和全称

主要字段:

字段中文含义
value名称值本身
language语言
type名称变体类型
isPrimary是否是主用名称
personId如果这条别名属于人,则挂这里
organizationId如果属于组织,则挂这里
competitionId如果属于赛事,则挂这里
sourceId来源

14. AuditLog

用途:审计日志表。记录“数据是怎么被创建、修改、合并、拆分的”。

注意:

  • 它记录的是数据维护行为,不是现实世界发生的比赛事件。

主要字段:

字段中文含义
entityType被操作的实体类型,如 PersonOrganization
entityId被操作实体的 ID
action做了什么动作
fieldName改的是哪个字段
oldValue修改前的值
newValue修改后的值
reasonType变更原因类型
reasonNote原因备注
sourceId依据哪个来源改的
actorType谁改的
actorId操作者 ID
batchId批次号
metadata补充信息 JSON
createdAt记录创建时间

15. SourceReference

用途:外部来源映射表。把外部网站里的一个实体,稳定地映射到系统内部实体。

例子:

  • 某大学官网球员页上的某个选手,对应系统里的哪个 Person
  • 某 PDF 里的一场比赛,对应系统里的哪个 Race

主要字段:

字段中文含义
sourceId来自哪个来源
sourceEntityType外部来源里的实体类型
sourceEntityKey外部来源里的稳定键
sourceUrl外部实体 URL
personId若映射到人物,则填这里
organizationId若映射到组织,则填这里
competitionId若映射到赛事系列,则填这里
competitionEditionId若映射到赛事届次,则填这里
raceId若映射到比赛单元,则填这里
metadata补充信息 JSON

补充说明:

  • 同一个 sourceId + sourceEntityType + sourceEntityKey 只能映射一次。

16. ImportBatch

用途:导入批次表。记录一次正式导入执行的全过程。

主要字段:

字段中文含义
key导入批次唯一键
type导入类型
status导入状态
sourceId来源
summary摘要
payload本次导入的原始载荷
startedAt开始时间
completedAt完成时间
errorMessage错误信息

枚举中文说明

DataStatus

数据确认状态:

中文含义
verified已核实
pending待确认
conflicting有冲突,多个来源不一致
missing数据缺失

PersonType

人物类型:

中文含义
athlete选手
coach教练
staff工作人员/管理人员

MembershipRole

所属关系中的身份:

中文含义
athlete以选手身份属于该组织
coach以教练身份属于该组织
staff以工作人员身份属于该组织

OrganizationType

组织类型:

中文含义
junior_high_school初中
high_school高中
university大学
corporate_team实业团/企业田径队
company公司法人主体
club俱乐部
federation协会/联合会
organizer赛事主办方类组织

MembershipType

人物和组织之间的关系类型:

中文含义
origin出身/来源组织
enrolled入学/在籍
affiliated隶属/效力
graduated毕业于
staff以工作人员身份任职

NameVariantType

名称变体类型:

中文含义
official正式名称
short简称
kana假名写法
romanized罗马字写法
chinese中文译名
english英文名称
former曾用名
media媒体常用写法

SourceType

来源类型:

中文含义
university_official大学官网/大学官方资料
hakone_official箱根駅伝官方来源
ntv日本电视台 NTV 来源
jaaf日本田联来源
rikujokyogi_magazine《月刊陸上競技》等专业媒体
data_site数据网站
fan_site爱好者网站
pdfPDF 文件来源
manual人工录入

EventDiscipline

比赛项目:

中文含义
m1500男子 1500 米
m3000男子 3000 米
m3000sc男子 3000 米障碍
m5000男子 5000 米
m10000男子 10000 米
ten_mile10 英里
half_marathon半程马拉松
marathon马拉松
ekiden_leg駅伝区间

CompetitionType

赛事系列类型:

中文含义
university_ekiden大学駅伝
high_school_ekiden高校駅伝
corporate_ekiden实业团駅伝
mixed_ekiden综合/混合駅伝
track_meet场地赛
road_race公路赛
marathon马拉松

AuditAction

审计动作:

中文含义
create创建
update更新
delete删除
verify核实确认
merge合并实体
split拆分实体

AuditReasonType

审计原因类型:

中文含义
initial_entry初次录入
source_update来源更新导致变更
source_correction来源更正导致变更
stale_source旧来源过时,已替换
manual_error人工修正错误
format_normalization格式标准化
translation_update翻译更新
verification_update核验状态更新
entity_merge实体合并
entity_split实体拆分
import导入过程产生
system系统自动产生

AuditActorType

操作者类型:

中文含义
admin管理员/人工操作
import_script导入脚本
system系统自动处理

ImportBatchStatus

导入批次状态:

中文含义
pending待执行
running执行中
completed已完成
failed失败

当前版本相对旧稿的关键变化

  • Person.registeredPrefecture 已删除
  • Membership.grade 已删除
  • Membership.role 现在是固定枚举,不再是自由文本
  • Competition.type 现在是固定枚举,不再是自由文本
  • 新增 TeamCompetitionResult
  • 新增 TeamCompetitionLegSnapshot
  • 新增 PlayerRelationCache

一句话理解

如果只记核心脉络,可以这么看:

  • Person 是“人”
  • Organization 是“学校/队伍/机构”
  • Membership 是“人在哪个阶段属于哪个组织”
  • Competition -> CompetitionEdition -> Race 是“赛事系列 -> 某一届 -> 某一场/某一区”
  • RaceResult 是“人在比赛中的成绩”
  • TeamCompetitionResult 是“队伍在整届赛事中的总成绩”
  • Source 是“这条数据从哪来”