Article

2026-06-28-襷の系譜-数据审计与来源策略

个人项目

襷の系譜 数据审计与来源策略

日期:2026-06-28

更新记录:

  • 2026-06-29:补充姓名标准、来源抽样规则和存量重复治理口径。
  • 2026-06-29:补充保护档案上的旧 seed 履历残留治理规则。
  • 2026-06-29:补充按比赛日期反推学籍时间线的规则,禁止使用固定年份常量。

1. 当前判断

V0.1 暂不建设 domain_event

原因:

  • 真实世界事实已经可以由业务表表达,例如 MembershipPersonalBestRaceResult
  • 单独维护事件表会带来重复录入和同步问题。
  • 当前阶段还没有“动态流”“新闻事件”“用户时间线”等明确产品需求。

当前采用:

业务事实:Person / Organization / Membership / PersonalBest / RaceResult / Competition...
数据维护过程:AuditLog

未来如果需要选手生涯时间线,优先从业务表聚合生成,而不是提前维护独立事件表。

2. AuditLog 的职责

AuditLog 只记录数据维护行为,不声明现实世界发生了什么。

它回答:

我们的数据在什么时候、因为什么、被谁、如何修改了?

适合记录:

  • 新增选手
  • 修改选手基础信息
  • 新增或修正 PB
  • 新增比赛成绩
  • 修正出身高中
  • 修改确认状态
  • 合并或拆分实体
  • 替换来源
  • 批量导入导致的变更

不适合记录:

  • 作为产品内容直接展示的“选手动态”
  • 新闻稿或公告正文
  • 与数据表无关的运营日志

3. 审计原因类型

当前枚举:

  • initial_entry:初次录入
  • source_update:来源有新数据
  • source_correction:来源间冲突后按更高优先级来源修正
  • stale_source:旧来源过时
  • manual_error:人工录入错误
  • format_normalization:格式规范化
  • translation_update:翻译更新
  • verification_update:确认状态变化
  • entity_merge:实体合并
  • entity_split:实体拆分
  • import:批量导入
  • system:系统行为

4. 单选手深挖策略

下一阶段不急于拉取大量数据。

建议先选择一个选手做完整样本,例如:

黒田 朝日

目标不是“把一个人做得很满”本身,而是用一个具体选手倒逼数据模型和页面能力:

  • 基础资料是否够用
  • 所属关系是否够用
  • 出身高中是否够用
  • PB 是否能表达来源、日期、比赛、阶段
  • 箱根出场记录是否够用
  • 其他比赛结果是否需要新字段
  • 数据来源冲突如何记录
  • 页面上哪些信息应该展示,哪些只入库

这比直接导入三校完整名单更稳。

5. 数据录入节奏

推荐顺序:

  1. 单选手深挖
  2. 发现字段缺口并调整模型
  3. 形成来源优先级文档
  4. 少量多选手验证
  5. 再设计 CSV/导入脚本
  6. 批量录入三校名单

不要在模型还没有经过真实样本检验时过早建设复杂导入管线。

6. 数据扩展动线与待办策略

数据录入不应长期停留在“从某个选手出发,把这个选手挖到底”的模式。

更适合本站的方式是:

Person-first + Race-frontier。

也就是先从一个高价值选手出发,但每次录入比赛、所属和来源时,都把相邻节点记录为待办项,而不是当场强行补全。

例如从黒田朝日出发:

  1. 录入第102回箱根駅伝 5区成绩。
  2. 同时发现同一 Race 的其他选手,例如工藤慎作。
  3. 不立即补完整个 5 区所有选手,而是创建“第102回箱根駅伝 5区全结果补齐”待办。
  4. 对高价值相邻选手创建“工藤慎作选手页补齐”待办。
  5. 后续按优先级从待办中选择下一个节点。

这个过程类似 BFS,但不是机械遍历,而是受来源可靠性、页面价值和数据缺口驱动。

6.1 待办类型

待办项至少分为四类:

  • 选手补齐:为某个选手建立或补全基础资料、所属履历、PB、主要比赛记录。
  • 比赛补齐:补齐某个 Race、某届赛事或某个区间的结果。
  • 来源校验:确认某个事实的来源优先级,处理多个来源之间的冲突。
  • 数据修正:修正已入库事实,例如 PB 更新、所属变化、录入错误、来源过时。

6.2 待办状态

待办完成后不应物理删除,而应保留状态。

建议状态:

  • pending:待处理
  • in_progress:处理中
  • done:已完成
  • blocked:来源不足、暂时无法确认
  • skipped:已判断暂不处理

保留历史的原因:

  • 能回溯某条数据为什么被优先录入。
  • 能知道哪些节点已经被检查过。
  • 能解释某些数据为什么暂时缺失。
  • 后续做后台、审计、数据导入时可以复用。

6.3 待办优先级

优先级不只看名气,也看数据结构价值。

建议排序因素:

  • 是否来自高可信来源。
  • 是否能补齐一个已展示页面上的明显缺口。
  • 是否能扩展重要关系,例如同区间、同大学、同高中、同实业团。
  • 是否能验证当前模型边界。
  • 是否与首页或选手页用户关注度强相关。

初期可以使用简单优先级:

  • P0:当前页面明显需要,或已入库数据依赖它完成闭环。
  • P1:重要相邻节点,适合下一批补齐。
  • P2:有价值但不影响当前体验。
  • P3:暂存线索。

6.4 首页展示规则

首页可以展示最近待更新的五个项目,用来向用户表达站点正在持续维护。

展示原则:

  • 只展示 pendingin_progress
  • 只展示标记为可公开展示的待办。
  • 优先展示 P0 / P1
  • 文案使用用户能理解的内容,例如“第102回箱根駅伝 5区 全結果”“工藤慎作 選手ページ”。
  • 不展示内部备注、来源冲突细节和低可信线索。

完成后:

  • 首页不再展示该项。
  • 内部状态改为 done
  • 不删除历史记录。

6.5 与 AuditLog 的关系

待办项和审计日志职责不同。

待办项回答:

接下来要补什么?为什么它值得补?

AuditLog 回答:

数据已经如何被修改?修改原因是什么?

因此:

  • 创建待办不一定写 AuditLog。
  • 完成待办时,如果实际修改了业务数据,应写 AuditLog。
  • 如果只是发现来源不足,把待办标记为 blocked 即可,不一定写 AuditLog。
  • 如果因为来源冲突修正已入库数据,应写 AuditLog,并在待办中保留处理结论。

7. 数据确认状态决策

DataStatus 不应只表示“有没有录入”。

Person.status 来说,verified 应表示该选手的 V0.1 核心档案已经整理完成,而不只是“找到了几个可靠来源”。

单个事实可以因为来源可靠而可信,例如某条 PB 或某场比赛结果;但这不等于整个选手档案已经完成。

建议把状态理解为:

  • verified:该实体或事实已经达到当前版本定义的完成标准,可以作为完成资料展示。
  • pending:已有初步数据,但核心字段仍缺来源、来源较弱,或尚未完成复核。 <<<<<<< HEAD =======

8. 保护档案履历治理规则

当某些选手被列入“保护档案”后,后续导入流程不会直接整包替换其 Membership,这是为了避免已经人工整理过的资料被简单覆盖。

但这也会带来一个副作用:

  • 早期 seed-source 的估算履历可能继续留在库里。
  • 后续真实来源导入的新履历会叠加进去。
  • 如果之前还发生过人物合并,旧履历可能一起迁移到保留人物上。

这类情况的处理口径如下:

  1. 页面层不做“看起来去重”的临时掩盖。
  2. 以数据层修复为准,删除被真实来源覆盖的旧 seed-source 履历。
  3. 保留有明确来源支撑的时间线版本。
  4. 删除时写 AuditLog,原因使用 source_correction
  5. 这类修复通过独立维护脚本执行,避免手工逐条改库。

当前已确认的首个样本是:

  • asahi-kuroda
    • 初次清理时误保留了后续导入产生的错误时间线
    • 现已二次回正为:
      • 玉野光南高校:2019-04-01 ~ 2022-03-31
      • 青山学院大学:2022-04-01 ~ 2026-03-31
      • GMOインターネットグループ:2026-04-01 ~ present

这也说明一个重要边界:

  • “保护档案清理”与“学籍时间线回正”是两类不同修复
  • 前者解决重复
  • 后者解决时间线推算错误
  • 两者都需要保留审计轨迹,不能互相替代

9. 学籍时间线反推规则

对于只提供“比赛时年级”的来源,例如箱根 NTV 区间页面:

  1. grade 只能作为比赛时快照。
  2. 高中 / 大学 Membership 的时间线必须基于比赛日期所在学年反推。
  3. 不能使用固定年份常量,例如 2026 - grade

箱根駅伝的特别点:

  • 比赛通常在 1月
  • 但它属于前一年 4月 开始、当年 3月 结束的学年

因此:

  • 2025-01 的箱根比赛,学年结束年应视为 2025
  • 3年 选手的大学时间线应为 2022-04-01 ~ 2026-03-31

后续任何导入脚本若需要从 grade 推学籍,必须优先读取:

  1. Race.startsAt
  2. 若缺失,再读 CompetitionEdition.startsOn
  3. 若仍缺失,再使用届次年份构造保守基准

此外,修复脚本不应默认要求“比赛结果来源 ID”和“履历来源 ID”一致。

原因:

  • 同一事实可能来自不同来源链
  • 比赛结果可能来自官方记录页
  • 履历可能来自 NTV 区间页或早期 seed

修复时应优先按“同一人物 + 同一比赛上下文 + 同一学年推算规则”判断,而不是只按来源 ID 做机械匹配。

4d3e3d7 (Add featured article and sync latest posts)

  • conflicting:多个来源之间存在冲突,且当前无法决策。
  • missing:明确知道该字段/事实需要展示,但目前缺失。

7.1 Person.status 的含义

Person.status 表示“选手 V0.1 核心档案完成状态”。

它不是单个字段的可信度,也不是某条 PB 或某场比赛结果的可信度。

如果一个选手仍有会影响核心档案完整性的待办,应保持 pending。例如:

  • 生年月日缺失
  • 出身地缺失
  • 读音缺失
  • 核心箱根记录只有名次但缺时间
  • PB 缺创造日期、赛事或地点
  • 箱根以外的主要比赛记录明显缺失
  • 来源展示还不能解释页面上的关键事实

但进入 verified 至少需要满足:

  1. 姓名、主要显示名、罗马字或读音已确认。
  2. 当前或最近主要所属已确认。
  3. 高中/大学/实业团等关键所属履历已能解释页面展示。
  4. 生年月日、出身地等核心档案字段没有明显缺口,或页面明确不展示该字段。
  5. 核心 PB 有可靠来源,并尽量具备日期、赛事或地点。
  6. 主要箱根记录至少包含届次、区间、成绩或明确缺失说明、区间名次和来源。
  7. 页面上展示的关键事实均有来源或明确的待确认标记。
  8. 不存在会影响用户理解的重大来源冲突。

7.2 何时保持 pending

以下情况保持 pending

  • 只是从某场比赛结果中创建的选手壳。
  • 只知道参赛时代表组织,不知道真实长期所属。
  • PB 只来自报名页或媒体摘要,尚未确认是否为正式 PB。
  • 生年月日、出身地、大学/高中等核心字段缺失较多。
  • 箱根核心记录缺少时间等关键展示字段。
  • 页面会给用户造成“这个选手已经整理完成”的误解。

例如第102回箱根駅伝 5区补齐时创建的多数选手,初期应保持 pending

工藤慎作这类选手即使已经找到早稲田大学官方 PB 和箱根官方记录,如果生年月日、出身地、过往箱根具体时间等核心信息仍缺失,也应保持 pending,直到 V0.1 核心档案补齐。

7.3 临时代表队与长期所属

Membership 只记录长期或生涯所属,例如高中、大学、实业团、俱乐部。

临时比赛代表组织只写入比赛结果,不写入普通所属履历。

例如:

  • RaceResult.organizationId = 関東学生連合
  • 不创建 Membership(person=髙橋歩夢, organization=関東学生連合)

原因:

  • 関東学生連合是箱根某届赛事的临时参赛队。
  • 它不代表选手的长期所属。
  • 如果写入 Membership,会污染“当前所属”和生涯时间线。

如果暂时不知道选手真实大学,当前所属应为空或待确认,而不是用临时代表队代替。

<<<<<<< HEAD

补充规则:

  • 第100回箱根駅伝不编成 関東学生連合
  • 因此第100回若出现 RaceResult.organizationId = 関東学生連合,应直接视为错误导入,而不是待确认数据。
  • 此类错误优先写入审计日志后删除,不保留为 pending。
  • 東京大学大学院東大大学院 这类学生联合来源页中的所属表记,当前阶段统一归并到 東京大学,用于补齐大学时间线。

4d3e3d7 (Add featured article and sync latest posts)

8. 来源优先级原则

同一事实可能来自多个网站。需要从一开始维护“来源优先级”,否则后续难以解释冲突。

通用原则:

  • 官方来源优先于媒体来源
  • 当前来源优先于旧来源
  • 赛事官方结果优先于新闻报道
  • 大学/队伍官网优先于粉丝站
  • 粉丝站可以作为线索,但关键事实需要二次确认
  • 媒体名鉴可以作为参考,但不复制文章正文和图片

9. 来源类型初步建议

9.1 选手当前所属与队伍名单

优先级:

  1. 大学/队伍官网
  2. 箱根官方报名资料
  3. NTV 箱根页面
  4. 月陸 Online 等媒体名鉴
  5. 粉丝站/数据站

9.2 箱根报名与出场

优先级:

  1. 箱根駅伝官方
  2. 関東学生陸上競技連盟资料
  3. NTV 箱根页面
  4. 媒体报道
  5. 粉丝站/数据站

9.3 PB

优先级:

  1. JAAF / 官方记录系统
  2. 大学/队伍官网
  3. 箱根官方报名资料
  4. NTV 箱根页面
  5. 月陸 Online 等媒体名鉴
  6. 粉丝站/数据站

9.4 普通比赛结果

优先级:

  1. 赛事官方结果
  2. JAAF / 地区陸協 / 学連结果
  3. 大学/队伍官网结果汇总
  4. 媒体报道
  5. 粉丝站/数据站

9.5 出身高中

优先级:

  1. 大学/队伍官网
  2. 箱根官方报名资料
  3. NTV 箱根页面
  4. 高校駅伝官方资料
  5. 媒体名鉴
  6. 粉丝站/数据站

<<<<<<< HEAD

9.6 姓名与罗马字来源规则

姓名相关字段需要单独维护优先级和抽样规则。

统一原则:

  • displayNameJa 以日文原文为主,保存“姓 名”,且统一使用单个半角空格。
  • displayNameRoman 内部统一整理成 GivenName FamilyName
  • 来源页面原文中的罗马字顺序,只作为来源证据,不直接决定 slug。

导入前要求:

  1. 首次访问某类来源时,先抽样 2 到 3 个已知选手。
  2. 判断该来源常用的是 Family Given 还是 Given Family
  3. 抽样结论记录到来源说明或本地 skill。
  4. 若该来源内部混用顺序,则该来源的人名导入默认进入保守模式,优先复用既有实体,不直接新建人物。

当前已知风险:

  • NTV、部分报名页、部分名鉴页,可能出现不同罗马字顺序。
  • 同一选手在不同站点之间出现顺序反转是常见现象。
  • 因此罗马字只能作为辅助匹配,不能当主身份键使用。

9.7 存量重复治理口径

当前库中已发现的重复人物,需要按“历史姓名顺序误判”处理,而不是当成真实不同人物。

治理口径:

  • 先确认保留哪一个主 slug。
  • 迁移关联数据。
  • 写入 AuditLog,原因建议使用 entity_mergeformat_normalization
  • 删除被合并实体前,先确认页面路由、来源映射和结果记录都已转移完成。

4d3e3d7 (Add featured article and sync latest posts)

10. 对黑田朝日样本的建议采集项

先围绕一个选手建立完整样本。

建议采集:

  • 姓名、读音、罗马字
  • 生年月日
  • 出身地
  • 身高体重,如可靠
  • 出身高中
  • 大学、学部、学科、年级
  • 5000m / 10000m / 半马 PB
  • PB 创造日期、比赛名、地点、当时阶段
  • 箱根报名与出场记录
  • 出云駅伝、全日本大学駅伝出场记录
  • 主要赛季成绩
  • 数据来源
  • 来源优先级判断
  • 待确认项

11. 页面与模型的关系

数据采集不必等页面完全完备,但也不应先批量采集。

推荐方式:

用一个选手做深样本,在采集过程中完善模型和页面;不要先拉大量数据。

原因:

  • 真实数据会暴露模型缺口。
  • 页面会暴露哪些字段真正有展示价值。
  • 单选手样本的修正成本低。
  • 批量导入太早会把错误模型固化。

12. 后续动作

短期建议:

  1. 以黒田朝日为样本建立数据采集清单。
  2. 逐条记录来源和优先级。
  3. 建立待办项机制,先支持选手补齐、比赛补齐、来源校验、数据修正。
  4. 首页展示可公开的最近五个待更新项目。
  5. 根据样本补充必要字段。
  6. 页面上新增“数据状态/来源说明”的更清晰展示。
  7. 等模型稳定后再做 CSV 导入管线。