# 固定赛季页面与成绩录入运行手册

本手册自 S17 正式结算起生效。公开入口是 `/seasons/`；Agent 先读 `/data-guide/` 与本手册，再编辑数据。当前生产域名为 `https://nikke-cn-solo-raid.pages.dev`，沿用仓库生产分支 `site/nikke-cn-solo-raid`。

用户已要求正式归档、临时成绩使用两种通用固定结构；本手册替代旧文档中“`/s16/` 只是 `/raid/?season=S16` 别名”的约定。首页仍保留长期榜，S16 的视觉、双服卡位和档位顺序继续沿用。

## 一、页面契约

| 页面 | 固定路径 | 内容 | 数据来源 |
|---|---|---|---|
| 长期榜 | `/` | 跨期 Rating，保留现有逻辑 | 已结算赛季及审核记录 |
| 赛季目录 | `/seasons/` | 全部赛季、状态、临时记录、录入入口 | `data/season-registry.json` |
| 赛季主页面 | `/s{N}/` | 正式结算；未结算时使用临时结构 | 通用赛季数据结果 |
| 临时成绩目录 | `/s{N}/live/` | 最新临时批次、历史快照链接；结算后仍保留 | 不可变临时快照 |
| 单批快照 | `/s{N}/snapshots/{YYYYMMDD-HHmm}/` | 该时间点的榜单与档位观测 | 对应批次，按北京时间生成 URL |
| 阵容证据 | `/s{N}/lineups/` | 榜首已收录五队，明确图中总伤和取证时间 | `s{N}-champions-*.json` 或历史证据 |
| 单批阵容 | `/s{N}/snapshots/{YYYYMMDD-HHmm}/lineups/` | 当批或此前有效的榜首阵容证据 | 不使用未来批次的阵容 |
| 数据指南 | `/data-guide/` | 两种状态的固定结构、身份、阈值与审核约定 | 本手册及共享代码 |
| 模型与回测 | `/forecast/` | 原冻结预测的真实误差、事后候选模型回放 | 模型与评估 JSON、方法文档 |
| Agent 索引 | `/llms.txt` | 固定目录与录入契约 | 同一份赛季 registry |

上述 HTML 页面都有实体 `index.html`，含独立标题、描述、绝对 canonical、普通 `<a href>` 导航、结构化数据及 sitemap 入口；Agent 索引以独立 `llms.txt` 文本文件提供。成绩、昵称、公会、整数伤害与档位直接在 HTML 中，关闭 JavaScript 也能阅读。JavaScript 增强筛选、统一双档案、公会成员、图片分享及现有截图提交表单；按用户要求不提供 CSV 导出入口。

旧 `/raid/?season=S17` 等 URL 由服务器重定向至 `/s17/`；带 `view=history` 时转至 `/s17/live/`。不要再向新内容写入旧查询参数地址作为主入口。

管理审核通过的内容由服务器使用共享结算函数合并进固定 URL 的 HTML 响应，用户与爬虫读取同一文本。构建产物保留可用基础数据，数据库暂时不可用时不清空已有成绩。

## 二、源文件与生成文件

以下路径均相对 `standalone/nikke-cn-solo-raid/`。

| 文件 | 维护规则 |
|---|---|
| `data/season-registry.json` | 唯一赛季元数据入口：编号、状态、Boss、属性、弱点、时间、档位、近线样本、快照文件列表 |
| `data/raid-snapshots-{批次}.json` | 新增临时批次；每个文件含 `{ "snapshots": [...] }` |
| `data/raid-snapshots-s{N}-final.json` | 正式结算快照；区分 `leaderboard` 和 `threshold` 证据 |
| `data/s{N}-final-thresholds.json` | 最终档位及其取证精度说明，作为评估目标；未确认值保留 `null` |
| `data/s{N}-champions-{批次}.json` | 阵容、五队伤害、角色文本、原图及审核裁剪位置，独立于榜单总伤保存 |
| `data/season-registry.cjs`、`season-snapshot-index.cjs`、各快照 `.cjs` | 编译产物；编辑 JSON 后运行编译，禁止手工维护双份数据 |
| `data/season-archive-seeds.cjs` | 历史赛季公开底稿，统一构建生成，供服务器合并已审核投稿 |
| `dist/data/s{N}-settlement.json` | 当前通用结算结果，不手工编辑 |
| `dist/s{N}/...`、sitemap、llms.txt | 模板生成产物，不逐赛季复制修改 HTML |

新增页面或模块必须先读 [UI 设计系统](UI_DESIGN_SYSTEM.md)、[功能验收矩阵](FEATURE_PARITY.md) 和 [UID 审计](UID_AUDIT.md)。统一视觉以现有 S16/S17 深蓝哑金样式与右侧 Boss 阴影为准。

通用实现文件：

- `scripts/compile-season-data.mjs`：从 registry 收集批次，校验重复 ID，生成后端种子与赛季时序配置。
- `assets/season-settlement-core.js`：同一赛季的身份归并、有效高分选择、名次与来源标记；S16 保留已经核验的历史专用规则。
- `scripts/build-season-data.mjs`：生成结算数据，并更新长期榜和历史底稿的数据输入。
- `scripts/build-season-pages.mjs`：两类固定页面、历史快照、阵容、导航、SEO 及公共文档。
- `scripts/build-site-ui.mjs`：最后统一长期榜外壳、派生账号档案与共享资源；`build-site-ui-secondary.mjs` 适配配队器和兼容旧页面。
- `scripts/lib/site-ui.mjs` 与 `assets/site-ui.css`：唯一共享导航、公告、Boss Hero、赛季卡片及设计变量。
- `scripts/lib/account-identity.mjs`、`rating-core.mjs`：区服＋UID身份与长期评分，浏览器和构建共用。
- `data/account-identities.json`：已核验最新昵称、历史名称、严格限定范围的别名证据。
- `assets/site-interactions.js`：长期/单期共用个人双档案、公会成员、认领/勘误和分享流程。
- `assets/season-page-render.js`：Node 构建、浏览器和 Pages Functions 共享的安全纯文本 HTML 行渲染。
- 仓库根 `functions/_middleware.js` 与 `functions/api/season-settlement.js`：固定页面与审核通过数据的相同合并结果。

## 三、新赛季配置

获得确认的 Boss、属性与排期后，在 `season-registry.json` 中添加一条配置并更新 `currentSeason`。不要在获得公告前猜测 S18 的 Boss、日期或角色池。

```json
{
  "season": 18,
  "status": "provisional",
  "title": "S18 已确认Boss名称",
  "boss": "已确认Boss名称",
  "element": "公告中的Boss属性",
  "weakness": "公告中的弱点属性",
  "startsAt": "带+08:00的实际活动开始时间",
  "challengeStart": "带+08:00的实际挑战开放时间",
  "endsAt": "带+08:00的排他结束时间",
  "revision": "本期唯一版本",
  "thresholds": {"qq": {}, "wx": {}},
  "thresholdObservations": [],
  "rankCutoffs": {"qq": {"200": null}, "wx": {"200": null}},
  "snapshotFiles": ["raid-snapshots.json", "本期真实批次文件名"]
}
```

上面是字段说明，时间和文件名占位值不能直接用于构建。旧赛季配置保持不变；不要为了开放新一期重写历史结果。

UI 同时使用 registry 的 `bossImage` 和 `data/boss-assets.json` 中该季的本地真实素材记录，来源与核验信息不可省略。可选 `displayBossName` 只控制可读简称，不改写原始 `boss`。已有当前五队证据时，`championsSource` 指向 `data/` 内的真实证据文件名；不要在模板里新增赛季判断来接入素材或冠军。具体文件职责见 `UI_DESIGN_SYSTEM.md`。

状态含义：

- `provisional`：赛中或等待正式证据；按临时结构展示，不进入长期结算。
- `settled`：正式结算已核验；按归档结构合并最终榜单、早期有效成绩及已审核更新。

结束时刻采用排他边界。例如游戏在 10 月 21 日 23:59:59 结束，则 `endsAt` 为 10 月 22 日 00:00:00+08:00。时钟到期不能自动把临时快照升格为正式证据。

## 四、临时批次录入

每批双服快照分别有独立 ID 和区服；不要把两服混入同一个快照。至少记录：

```json
{
  "id": "sN:qq:YYYY-MM-DDTHH:mm:top50:v1",
  "season": 18,
  "server": "qq",
  "snapshotAt": "该批次标签时间，包含时区",
  "observation": {
    "version": 1,
    "observedAt": "真实截图开始时间，包含时区",
    "observedUntil": "真实截图结束时间，包含时区",
    "kind": "provisional",
    "sampleType": "leaderboard",
    "timePrecision": "minute",
    "note": "来源和时间标签差异的说明"
  },
  "availableAt": "实际取得证据的时间",
  "reviewedAt": "实际核验时间",
  "contributor": "署名或站长",
  "evidenceCount": 8,
  "evidenceHash": "原始证据校验值",
  "fullTop50": true,
  "rows": [
    {"rank": 1, "percentile": null, "nickname": "原图昵称", "guild": "原图公会", "damage": 12345678901, "playerId": null}
  ]
}
```

伤害必须是正安全整数。名次完整且连续时才标完整 Top50；核对 1～50、伤害递减和截图重复边界。微信榜底 `SOLORAID / 无所属 / 0` 是截图界面占位，不是参赛成绩。

`snapshotAt` 是站长命名的统一批次；`observedAt` / `observedUntil` 是照片反映的实际时刻。两者不一致时保留差异，不能拿上传时间替代截图时刻。榜首换人、同一榜首总伤变化、阵容取证变化分别核对。

已有单快照导入命令可用于附原图的输入：

```sh
node scripts/import-raid-snapshot.mjs /绝对路径/单个快照.json
node scripts/import-raid-snapshot.mjs /绝对路径/单个快照.json --write
```

默认只校验；`--write` 追加到基础快照文件。输入的 `evidenceFiles` 应指向已保存的原图。按批新建文件时使用 `{ "snapshots": [QQ快照, 微信快照] }`，并把文件名加入本期 `snapshotFiles`。两种入口最终进入相同编译链。

新一批使用新 ID；同 ID 不允许覆盖不同伤害。勘误需新增版本、说明撤回或替代关系并保留旧来源，不能把旧快照直接改成新分数。

## 五、正式结算录入

1. 核验正式双服榜单，写入 `raid-snapshots-s{N}-final.json`。`observation.kind` 使用 `final`；实际取证时间不得早于结算边界。
2. 正式 Top50 每服 50 条，保留官方本服名次。若发现同一身份重复，按证据纠正；不得用本站排序虚构全服缺失名次。
3. 将采纳的百分比参考值、Top200（仅直接证据）、Top50 和冠军线填入 registry；证据细节保存在 `s{N}-final-thresholds.json`。
4. 将状态改为 `settled`，填写 `settledAt`、revision、来源说明及最终快照文件列表。
5. 跑统一构建。`/s{N}/` 使用正式归档模板；`/s{N}/live/` 与历史快照保留当时内容。

百分比精度必须准确表达。S17 五个“正好 3.00% / 1.00% / 0.50%”截图的整数伤害是玩家真实分数，站长采纳为结算参考线；游戏百分比只显示两位小数，不能宣称理论分位边界精确到个位。

S17 名为“微信百三.jpg”的截图实际显示 **3.08%**、14,242,638,473。该记录使用 `thresholdObservations`，标为“3.08% 样本 · 非 3% 线”，不能写入微信 `thresholds[3]`，不能用于精确 3% 回测目标。

## 六、历史成绩、审核与身份

选择规则以同区服为边界：已核验 UID 优先；无 UID 使用同昵称、同公会。有效记录保留较高伤害；新审核通过的高分更新当前结果，已有证据保留。较晚审核的低分旧图不会降低成绩。

正式前50提供官方名次。没有结算更新的历史玩家继续显示原成绩，标记“沿用赛中成绩”，不沿用当时的名次。已知最终档位能支持范围时只显示范围，不能把收录顺序当作全服排名。

同名不同公会不能仅靠昵称合并；不同已核验 UID 必须分开。OCR 别名修正必须有证据。匿名昵称不能被假定为跨批同一个人；无法确认的记录保留为匿名证据，计数使用“条已收录成绩”，不能夸称独立玩家人数。

用户通过页面“提交 / 补录”或行末勘误图标提交原图；现有截图识别、人机校验、管理员审核和版本记录继续使用。待审核、拒绝、撤回条目不影响公开榜单。审核通过后，固定页面的服务端文本与手动刷新 API 使用同一合并结果。

## 七、阵容单独核验

阵容图片保留原图和已审核裁剪位置；双服卡片 QQ 在左、微信在右，尺寸对齐，完整五队页按照队伍顺序并列。

当最终总伤高于现有五队图总和，只能写“旧阵容证据”并展示该图对应的旧总伤。S17 微信旧批次五队对应 31,857,708,565；现已补入 `s17-champions-20261022-final.json`，最终五队伤害合计为 34,341,630,854，与正式总伤一致。长期档案由 registry 的 `championsSource` 接入；单期页面通过 `scripts/lib/champion-evidence.mjs` 核对身份，最终优先使用有效的 `championsSource`。同季同服双方 UID 都已知时必须一致，同 UID 更名可关联，不同 UID 拒绝；缺少任一方 UID 时须精确伤害相同且当前或原始昵称对应。历史快照同时过滤批次时间和证据取证时间，只展示截至该时点已存在的阵容，不能倒灌最终证据。

S16、S15 历史阵容沿用已经核验的来源；不得用生成图重绘游戏内容。新赛季使用相同 `s{N}-champions-{批次}.json` 格式即可生成页面。

## 八、构建与必要检查

仓库根运行：

```sh
npm run build
```

编译、结算数据构建、固定页面构建属于同一流水线。需要分段定位时在 `standalone/nikke-cn-solo-raid/` 内使用：

```sh
node scripts/compile-season-data.mjs
node scripts/build-season-data.mjs
node scripts/build-season-pages.mjs
node --test tests/season-settlement.test.mjs tests/season-pages.test.mjs
```

分段命令需要已有基础构建产物。不要同时运行两个完整构建，避免输出互相覆盖。

必要检查包括：双服正式50条及顺序；整数伤害与冠军；同身份有效高分；旧成绩保留与匿名标记；六档顺序及近线样本；首屏和全部记录在 HTML；固定 canonical、内部链接、sitemap；S16/S17、桌面和手机的真实页面；现有配队器、长期榜和提交审核入口。

代码可抓取不代表搜索引擎已经收录；上线核验只确认 URL、状态码和文本响应，不承诺 Google 或百度的实际收录/排名。

## 九、预测模型

方法与命令见 `docs/SEASON_FORECAST_MODEL.md`。保留赛中已发布预测，不用结算数据改写旧答案。最终参考线用于逐条评分；新的候选方法使用当时数据做历史回放，必须明确其在结算后设计，不能冒充事前准确率。

S18 的使用方式是：填注册配置与首批/次批数据，运行通用预测入口，保留固定基线与候选对照。没有足够独立同属性赛季时，属性影响先由实测伤害尺度吸收，不凭空规定“火系倍数”。

## 既有入口与刷新约定

统一赛季模板继续读取 `data/site-announcement.json`，保留公告全文、展开与群号复制；点击公会名称打开当前榜及对应区服内已收录成员。昵称和 UID 进入同一双档案弹窗，保留长期/单期页签、勘误、身份认领、刀伤补录、完整图片分享和证据入口；新成绩投稿保留在榜单工具栏。配队工作台固定为 `/teams/`。这些功能共用原有审核流程，不另建一套投稿接口；入口与实现文件对应关系见 `FEATURE_PARITY.md`。

当前临时主页面 `/sN/` 与 `/sN/live/` 使用通用合并结果，保留以前批次与已审核投稿；历史 `/snapshots/` 只读当批记录。手动刷新遇到 `ready:false` 或 `communityComplete:false` 时保留当前 HTML 成绩，不把不完整结果当同步成功。

默认构建读取 `data/season-forecast-current.json`，仅在当前未结算赛季展示可用预测，并列原基准与新候选。正式结算与历史快照不混入本期最新预测。回放的批次标签与实际信息可用截止时间分列。未知路径由顶层 `404.html` 返回缺页，不使用首页作为成绩页替代。

