fix(p2): 按审查清单修 103 条「建议修」+ 奖牌榜改名 + 两个守卫(dev_0.2.0)

## 奖牌榜改名(主人要求)
「团体奖牌榜」→「单位奖牌榜」,界面 / 大屏渲染页 / 导出件(工作表名与
下载文件名 `单位奖牌明细.xlsx`)/ 用户文档 全部统一;`DISPLAY_PAGES` 是
大屏控制页与大屏页的共同来源,改一处两边同时生效。旧名保留为别名,
老链接与老配置(`display.pages`)照常有效。真 Chromium 验证:14 个管理端
路由 + 7 个基础数据分区 0 报错,大屏页标题已是「单位奖牌榜」。

## P2 清单(123 条,103 条标「建议修」)
按 A/B/C/D 分组、以「文件所有权互斥」分 10 个代理并行推进,另有独立复核
代理逐条核对:61 条声称已修 / 55 条证实 / 4 条部分 / 0 条不成立 / 0 垃圾。

主要交付(按风险从高到低):
- 写入侧校验:update 路径补必填与 TrimSpace、模板 rules 重复 rank、
  「小项不存在」的四种回答、`defaultProfileId` 外键、decodeJSON 的 8MB
  上限与 413(A01–A06、D02、D03 批量上限 500)
- 危险操作:一键恢复加互斥(第二个请求当场 409)、恢复超时不再说
  「未执行」、`ConfirmDialog` pending 时锁死 ESC/遮罩/关闭
- 认证:改密与撤会话进同一事务、滑动续期写库失败不再假装成功、
  CSRF Cookie 丢失自动补发、密码长度按字符(原按字节)、
  反代 403 只在可信代理透传原 Host 时采信 `X-Forwarded-Host`(全等比较)、
  过期会话定期清理、登出补审计、登录限流可配
- 数据可见性:导出「已发布未结算」行改为 `名次 — / 积分 — / 状态未结算`、
  单位积分明细成绩列与其它导出件逐字一致、空单位也打合计行、
  基础数据包命中「已彻底删除的单位」整行跳过并报界面路径、
  报名导入换单位给明确提示、勾选报名有冲突不再假报成功
- 导入/导出:赛事包上限 512MB(**它是唯一带字体/图片的包**,字体上限
  20MB)而 multipart 内存档位独立钉在 16MB、工作簿表名尾随空格、
  导出件路由与文件名契约补全
- 前端:一次写请求只生成一个 requestId(幂等缓存此前从未命中)、
  基础数据包补 `X-Request-Id`、示例数据模式禁写扩到撤销发布/存草稿、
  访客 401 不再是裸错误、模板 `{{else if}}` 不再静默吞内容
- Console:按设计退出(切赛事/恢复)不再判成崩溃、旧数据目录读不出来
  会记日志、渲染层日志按上限截断、`service.js` 补 17 例测试

## 测试
服务端用例 610 → 674(13 包全绿,新增老库跨九条迁移升级、panic 形状、
429 落 interceptions、三态对照、avg_mode 分母、会话失效、请求体上限等)
前端 449 → 531(vue-tsc + build 通过);Console 28 → 54(19 个文件)。
另修复一条会让整包挂死 600s 的用例(测试主协程堵在 client.Do,defer
轮不到执行 → 改为可取消 + 探针超时,改坏也 7.5s 快速失败)。

## 两个新守卫(防止再出现「组件 setup 抛异常」这类盲区)
- `web/scripts/check-declaration-order.mjs` 已进测试命令:沿 watch/
  watchEffect 的 const 依赖链查 TDZ(正是「运动员页整块空白」的根因)
- `web/scripts/smoke-admin.mjs`(`pnpm smoke`):真 Chromium 点遍 14 个
  管理端路由 + 7 个基础数据分区 + 大屏页,任何 console 错误即失败

## 文档
- `docs/升级说明.md` 新增 dev_0.2.0 一节(三件升级必知:改名、审计前缀
  与历史记录、密码口径;现场变化;赛事包上限;反代与 CSRF)
- 使用手册 / 常见问题 / 部署指南 / 上线检查清单按实现同步
This commit is contained in:
AskaEth committed 2026-09-11 23:46:59 +08:00
1 parent db94d9c119
commit 6841cf49aa
147 files changed
+8141 -423

No files matched your search

+3 -3
View File
@@ -43,9 +43,9 @@
—— 服务端不会自动加载规则包,必须导入一次
- [ ] 检查计分模板:个人前六名 `10/8/6/3/2/1`、团体 `15/10/8/4/2/1`、最后一名不得分
- [ ] **按赛事规则逐项核对过「奖牌结算开关」与「奖牌通道」**:到「基础数据 → 大项 / 小项」逐个确认要发牌的小项都勾着 **「该项启用奖牌结算」**(不该发牌的项目取消勾选);再到「基础数据 → 计分通道」逐项核对每条通道的 **「发奖牌」** 与(个人名次通道的)**「个人奖牌并入单位金牌榜」** 是否符合本场赛会规则。关掉的通道在「大项 / 小项」页的通道摘要里会标出 `不发奖牌` / `个人奖牌不并入单位榜`(见使用手册 10.2、10.4;查不出原因时按常见问题 Q94 逐条排除)
- [ ] **升级 / 首次启动后确认奖牌榜数字与上一场一致(不是全 0)**:本版新增迁移 0020,迁移只建表不回填,服务端会在下一次启动时自动全量重算补齐名次——「运行日志」里出现 `检测到通道名次为空但已有已发布成绩,正在执行一次性全量重算` 与 `一次性全量重算完成,通道名次与奖牌榜已补齐` 是正常的,不是报错;等它跑完后刷新「积分榜 → 团体奖牌榜」,与升级前的数字(或备份里的导出件)对一遍,**不能是全 0**(见部署指南 3.4、常见问题 Q96)
- [ ] **升级 / 首次启动后确认奖牌榜数字与上一场一致(不是全 0)**:本版新增迁移 0020,迁移只建表不回填,服务端会在下一次启动时自动全量重算补齐名次——「运行日志」里出现 `检测到通道名次为空但已有已发布成绩,正在执行一次性全量重算` 与 `一次性全量重算完成,通道名次与奖牌榜已补齐` 是正常的,不是报错;等它跑完后刷新「积分榜 → 单位奖牌榜」,与升级前的数字(或备份里的导出件)对一遍,**不能是全 0**(见部署指南 3.4、常见问题 Q96)
- [ ] 「只排名不计分」的项目(飞行人员战斗体能)导入后应显示为不计分
- [ ] 「基础数据 → 参赛单位」里各单位的 logo 都已上传(会显示在总积分榜、团体奖牌榜、分项成绩表与个人积分榜上)
- [ ] 「基础数据 → 参赛单位」里各单位的 logo 都已上传(会显示在总积分榜、单位奖牌榜、分项成绩表与个人积分榜上)
- [ ] 同页核对**单位名单齐全、没有误删**:被「彻底删除」的单位不会出现在这个列表里(它的报名、成绩与积分仍在库里,积分榜照常显示它);发现少了就用它的编码导入一次报名表,它会重新出现(详见使用手册 10.1、常见问题 Q61–Q64)
## 5. 大屏
@@ -76,7 +76,7 @@
- [ ] 录入员登录 → 录入一条成绩 → 存草稿 → 发布 → 大屏刷新
- [ ] 撤销发布 → 修改 → 重新发布(历史记录里能看到链路)
- [ ] 导出核对:「结果导出」里「全部」与「全部(单页)」都能正常下载(后者应是一个工作簿、只有一张表);「积分榜」页三个榜单的卡片头各有导出——单位总分榜「导出总明细」得到 `单位积分总明细.xlsx`、团体奖牌榜「导出奖牌明细」得到 `团体奖牌明细.xlsx`、个人积分榜「导出个人明细」得到 `个人积分总明细.xlsx`;展开某单位后还能点「导出该单位明细」得到 `单位积分明细.xlsx`
- [ ] 导出核对:「结果导出」里「全部」与「全部(单页)」都能正常下载(后者应是一个工作簿、只有一张表);「积分榜」页三个榜单的卡片头各有导出——单位总分榜「导出总明细」得到 `单位积分总明细.xlsx`、单位奖牌榜「导出奖牌明细」得到 `单位奖牌明细.xlsx`、个人积分榜「导出个人明细」得到 `个人积分总明细.xlsx`;展开某单位后还能点「导出该单位明细」得到 `单位积分明细.xlsx`
- [ ] 断网 → 大屏仍显示缓存数据 → 恢复后自动补增量
- [ ] 重启服务端 → Console 自动拉起
+26 -21
View File
@@ -175,7 +175,7 @@
**如果管理员关掉了「分批发布时实时更新榜单」**(见 10.5),行为会变成这样:发布后**成绩照常上屏,但名次、积分与奖牌榜都不动**——要等**该项目该组所有「正常参赛」的人都录完成绩**才一次性结算并更新榜单(退赛 / 取消资格 / 未出场的人不算在等待里)。未结算期间:
- 录入页在进度条下方显示一条提示:`延迟结算:本组 X/Y 人已出成绩,名次与榜单等该组全部录完后一起更新`(本页预览的名次仍是实时的),旁边就是「立即结算」按钮;
- 该组在**分项成绩 / 最新成绩里的名次显示 `—`**(成绩照常显示)——这是有意的,免得出现「有金牌但 0 分」这种自相矛盾的画面;
- 该组在**分项成绩 / 最新成绩里的名次显示 `—`**(成绩照常显示)——这是有意的,免得出现「有金牌但 0 分」这种自相矛盾的画面;导出的「分项成绩表」「全部」「全部(单页)」里,这些行的**名次与积分列也是 `—`、状态列写「未结算」**(见 11.1);
- 有人永远录不出成绩时(例如已退赛但没改状态),在录入页点 **「立即结算」**,系统按当前已发布成绩先结算一次榜单和奖牌;之后补录完还会再自动结算。
### 4.7 撤销发布
@@ -289,7 +289,7 @@
## 8. 积分榜
顶部页签:**单位总分榜**、**团体奖牌榜**、**个人积分榜**;右侧搜索框可按单位或运动员过滤。
顶部页签:**单位总分榜**、**单位奖牌榜**、**个人积分榜**;右侧搜索框可按单位或运动员过滤。
### 8.1 单位总分榜
@@ -297,20 +297,20 @@
- 「变化」列显示名次升降(如 `↑2`、`↓1`);
- 点「展开」下钻该单位的积分明细,右侧面板显示合计与每一条得分来源(项目、组别、运动员、成绩、名次、积分),每一分都可溯源。
### 8.2 团体奖牌榜 / 个人积分榜
### 8.2 单位奖牌榜 / 个人积分榜
- 团体奖牌榜显示金、银、铜与合计;
- 单位奖牌榜显示金、银、铜与合计;
- 个人积分榜显示运动员、单位、个人总分与奖牌数;
- 团体奖牌榜那一列的「明细」展开的是**同一个单位积分明细面板**(见 8.1),不是奖牌专属明细;但这份榜单本身有专属的逐枚奖牌导出(见 8.3);
- 单位奖牌榜那一列的「明细」展开的是**同一个单位积分明细面板**(见 8.1),不是奖牌专属明细;但这份榜单本身有专属的逐枚奖牌导出(见 8.3);
- 个人积分榜**没有明细列**(不能点开下钻),但它的逐条得分来源也有专属导出(见 8.3)。
### 8.3 导出明细(xlsx)
- 三个榜单的**卡片头**各有一个导出按钮,按钮跟着当前页签切换;展开某个单位时,右侧明细卡头还有 **导出该单位明细**;
- **导出总明细**(单位总分榜卡片头):导出 `单位积分总明细.xlsx`——一张带「单位」列的总表,外加每个**有数据**的单位一张分表;没有积分的单位不出分表;
- **导出奖牌明细**(团体奖牌榜卡片头):导出 `团体奖牌明细.xlsx`——第一张总表逐枚奖牌列出「单位 / 大项 / 小项 / 组别 / 名次 / 奖牌 / 运动员、队」,`奖牌` 列是 `金牌` / `银牌` / `铜牌`;其后每个**有奖牌**的单位一张分表(列同上、去掉「单位」,末尾一行小计 `金 X / 银 Y / 铜 Z`),没有奖牌的单位不出分表。口径与「团体奖牌榜」一致:同一个报名项在「个人+团体」这类双计通道下只记一枚奖牌,不会重复计数;
- **导出奖牌明细**(单位奖牌榜卡片头):导出 `单位奖牌明细.xlsx`——第一张总表逐枚奖牌列出「单位 / 大项 / 小项 / 组别 / 名次 / 奖牌 / 运动员、队」,`奖牌` 列是 `金牌` / `银牌` / `铜牌`;其后每个**有奖牌**的单位一张分表(列同上、去掉「单位」,末尾一行小计 `金 X / 银 Y / 铜 Z`),没有奖牌的单位不出分表。口径与「单位奖牌榜」一致:同一个报名项在「个人+团体」这类双计通道下只记一枚奖牌,不会重复计数;
- **导出个人明细**(个人积分榜卡片头):导出 `个人积分总明细.xlsx`——第一张总表逐条列出「单位 / 运动员 / 大项 / 小项 / 组别 / 名次 / 成绩 / 得分 / 来源」;其后每个**有得分**的单位一张分表(按单位分,不按人分),没有得分流水的人不出现在表里。`得分` 是记到个人的原始分,**与「个人积分榜」逐个数字一致,且不乘榜单口径里的个人项目得分折算系数**(那个系数只作用于单位总分);
- **导出该单位明细**(右侧单位明细面板头部):先展开某个单位再点,导出 `单位积分明细.xlsx`(列:序号、大项、小项、组别、运动员、名次、成绩、来源、积分、备注,末行是「合计」)。团体奖牌榜展开的也是这个单位积分明细面板,所以想按单位逐条看时同样用它;
- **导出该单位明细**(右侧单位明细面板头部):先展开某个单位再点,导出 `单位积分明细.xlsx`(列:序号、大项、小项、组别、运动员、名次、成绩、来源、积分、备注,末行是「合计」)。单位奖牌榜展开的也是这个单位积分明细面板,所以想按单位逐条看时同样用它;
- 这些导出都是只读下载(访客也能用),不需要写权限;导出过程中按钮会变成「导出中…」并禁用,避免重复下载。
### 8.4 推送明细到大屏
@@ -326,12 +326,14 @@
| 区块 | 操作 |
| --- | --- |
| 切页 | 选择「总积分榜 / 团体奖牌榜 / 个人积分奖牌榜 / 分项成绩 / 实时赛况 / 公告」,点「立即跳转」(**所有大屏一起切**);只想切某一块屏或某台设备,用右侧「在线大屏」卡片里那一行的下拉与「切到此页」 |
| 切页 | 选择「总积分榜 / 单位奖牌榜 / 个人积分奖牌榜 / 分项成绩 / 实时赛况 / 公告」,点「立即跳转」(**所有大屏一起切**);只想切某一块屏或某台设备,用右侧「在线大屏」卡片里那一行的下拉与「切到此页」 |
| 锁定与刷新 | **锁定**:停止自动轮播并停在当前页;**解锁**:恢复轮播;**强制刷新**:让所有大屏重新拉取快照,用于排障 |
| 公告 | 输入公告文字,设置「滚动时长」(5–120 秒,实际是全屏展示的秒数),点「推送公告」;点「清除公告」撤下当前公告 |
页面顶部徽标显示当前是「轮播中」还是「已锁定」。
> 「**单位奖牌榜**」这一页旧称「**团体奖牌榜**」(容易和「团体项目的奖牌」混淆,所以改名),大屏上、切页下拉里现在一律显示「单位奖牌榜」;旧名仍被当作别名接受,老的大屏地址与老配置(`display.pages`)不需要改。
> **权限**:本页的**切页、公告、锁定 / 解锁、强制刷新**(服务端指令)记录员及以上都能用;**开窗 / 收窗 / 重新排布**由服务管理程序在本机执行,不受角色限制(该卡片只在从服务管理窗口打开的面板里可用);只有「实时赛况」卡片(模式与当前项目)属于赛事设置,**仅管理员能保存**,非管理员看到的是整组置灰 + 一行提示条。
**公告**:点「推送公告」后所有大屏先**全屏**展示这条公告,到时间后自动退回原来的页面(没锁定就继续轮播),公告正文留在底栏的「公告」槽位继续显示,直到被新公告替换或点「清除公告」。公告卡片顶部徽标显示「当前公告:有 / 无」,徽标下面那一行就是当前公告正文(没有公告时显示「暂无公告(大屏底栏显示占位)」)。公告会随赛事设置保存:新开的大屏窗口、断线重连之后,会先把当前公告再全屏播一遍(默认 15 秒)后留在底栏;点「清除公告」后底栏回到「暂无公告」占位。
@@ -407,13 +409,13 @@
- 该单位**不再出现在默认的参赛单位列表**里,报名选择(含团体报名、运动员的「所属单位」下拉)里也选不到;
- 它的**报名、成绩、积分流水、人工调整、纪录与名下运动员全部保留**——消失的只是这个单位本身;
- **积分榜照旧显示它**:单位本来有分数的话,总积分榜、团体奖牌榜上仍有它的名字与分数。这是刻意设计(数据没删,分数就不该凭空消失),不是故障;
- **积分榜照旧显示它**:单位本来有分数的话,总积分榜、单位奖牌榜上仍有它的名字与分数。这是刻意设计(数据没删,分数就不该凭空消失),不是故障;
- **再次导入报名表时,如果用到这个单位的编码,它会自动复活**并重新出现在列表里——这里和人员不同:删掉的人再次导入是**以新档案建档**(旧数据回不来),删掉的单位则是**原来那条记录直接恢复**,报名、成绩与积分一直都在(见 10.7);
- 单位名下还有运动员时同样可以删,这些运动员不会被一起删掉,但他们编辑起来会有些不便(见常见问题 Q64)。
> ⚠️ **别和运动员的「彻底删除」混为一谈**:两个页面上的按钮同名,行为完全不同。运动员的「彻底删除」是**真删除**——人员档案连同他名下的报名、成绩与积分流水一起从库里删掉,并重算受影响小项的排名与积分(见 10.6);参赛单位的「彻底删除」是**假删除**——业务数据一条不删,只让单位从列表里消失,积分榜不变。
> 上传的 logo 会显示在**大屏**的总积分榜、团体奖牌榜、分项成绩与个人积分奖牌榜上(个人积分奖牌榜显示在运动员姓名前面);没传 logo 的单位不占位,队名不会因此错位。管理端页面不显示 logo。
> 上传的 logo 会显示在**大屏**的总积分榜、单位奖牌榜、分项成绩与个人积分奖牌榜上(个人积分奖牌榜显示在运动员姓名前面);没传 logo 的单位不占位,队名不会因此错位。管理端页面不显示 logo。
### 10.2 大项 / 小项
@@ -427,7 +429,7 @@
小项表单下方的「选项」里,"是否计分"旁边有一个 **「该项启用奖牌结算」** 勾选框,**默认勾上**。
- **勾上(默认)**:这个小项按名次发奖牌——第 1 名金、第 2 名银、第 3 名铜,和以前一样;
- **取消勾选**:这个小项的**名次与积分照常计算、照常上屏**,但**一块奖牌都不发**——金牌榜(团体奖牌榜)与个人积分榜的奖牌列里都不再出现它。
- **取消勾选**:这个小项的**名次与积分照常计算、照常上屏**,但**一块奖牌都不发**——金牌榜(单位奖牌榜)与个人积分榜的奖牌列里都不再出现它。
什么时候该关掉它?**"算分但不发牌"的项目**——例如赛会规则只记名次分、不设金银铜的项目,或者现场临时决定不发牌、但分数仍要计入单位总分的项目。
@@ -481,7 +483,7 @@
| 开关 | 默认 | 关掉之后 |
| --- | --- | --- |
| **发奖牌** | 打开(该通道的名次发奖牌) | 这条通道算出来的名次**只出积分、不发奖牌**:名次与分数照旧,金牌榜与个人榜里不再为它记牌 |
| **个人奖牌并入单位金牌榜** | 打开 | 只对「名次范围 = 个人名次」的行显示。关掉后这条通道的个人奖牌**仍然进个人积分榜的奖牌列**,但**不计入单位的金牌榜**(团体奖牌榜 / 单位总分榜的金银铜) |
| **个人奖牌并入单位金牌榜** | 打开 | 只对「名次范围 = 个人名次」的行显示。关掉后这条通道的个人奖牌**仍然进个人积分榜的奖牌列**,但**不计入单位的金牌榜**(单位奖牌榜 / 单位总分榜的金银铜) |
怎么用:
@@ -586,7 +588,7 @@
- `未出场` / `未到` / `缺席` → 未出场。
留空即「正常参赛」。真正认不出的写法会给出中文行级错误、该行跳过,例如「第 12 行:status「已报到」无法识别,只能填 registered / scratched / dq / dns(或留空)」;
6. `bib` 列会同时写入人员档案与报名记录;从已有名单勾选生成的报名也会带上人员的证件号与号码布;
7. 把 `.csv` 或 `.xlsx` 拖入虚线框,或点击选择文件(单个文件不超过 16MB;xlsx 只解析第一个工作表);
7. 把 `.csv` 或 `.xlsx` 拖入虚线框,或点击选择文件(单个文件不超过 32MB;xlsx 只解析第一个工作表);
- **CSV 编码不用管**:UTF-8、带 BOM 的 UTF-8(Excel 的「CSV UTF-8」)、GBK / GB18030(中文 Windows 上 Excel「另存为 CSV」的默认编码)、UTF-16 都能直接导入,中文不会乱码,服务端会自动识别;
8. 点「开始导入」,页面显示**新增 / 更新 / 跳过**数量;匹配不到的行会跳过并回显行号,修正后可重新上传。
@@ -623,19 +625,22 @@
| 导出项 | 内容 | 文件名 |
| --- | --- | --- |
| 总表 | 总积分榜 + 团体奖牌榜 + 个人积分奖牌榜,一个工作簿 | `总积分榜.xlsx` |
| 总表 | 总积分榜 + 单位奖牌榜 + 个人积分奖牌榜,一个工作簿 | `总积分榜.xlsx` |
| 全部 | 总表 + 全部分项,多 sheet 工作簿,适合归档打印 | `赛事成绩总表.xlsx` |
| 全部(单页) | 所有单位、所有项目、所有成绩**平铺在一张表**里(一个工作簿只有一张表),适合筛选与二次统计 | `赛事成绩总表(单页).xlsx` |
| 分项成绩表 | 选择一个小项导出成绩与积分 | `分项成绩表.xlsx` |
| 单位积分明细 | 选择一个单位,逐项列出得分来源 | `单位积分明细.xlsx` |
| 报名名单 | **本页下方的「报名名单」卡片**:按小项 / 组别 / 单位 / 批次筛选后导出报名数据(不填 = 全部)。13 列:小项编码、小项名、组别、单位编码、单位名、姓名、证件号、**号码布**、**批次**、性别、年龄、状态、备注;号码布按文本导出,前导零不会丢 | `报名名单.xlsx` |
| 团体奖牌明细 | **在「积分榜 → 团体奖牌榜」页操作**:逐枚奖牌列出单位 / 大项 / 小项 / 组别 / 名次 / 奖牌 / 运动员、队,每个有奖牌的单位一张分表(末尾小计) | `团体奖牌明细.xlsx` |
| 单位奖牌明细 | **在「积分榜 → 单位奖牌榜」页操作**:逐枚奖牌列出单位 / 大项 / 小项 / 组别 / 名次 / 奖牌 / 运动员、队,每个有奖牌的单位一张分表(末尾小计) | `单位奖牌明细.xlsx` |
| 个人积分总明细 | **在「积分榜 → 个人积分榜」页操作**:逐条列出单位 / 运动员 / 大项 / 小项 / 组别 / 名次 / 成绩 / 得分 / 来源,每个有得分的单位一张分表 | `个人积分总明细.xlsx` |
- 「分项成绩表」需先选择小项,「单位积分明细」需先选择单位;「报名名单」的四个筛选(小项 / 组别 / 单位 / 批次)**都可以留空**,留空就是不筛这一维度(全导);
- **录了但还没发布的成绩也会占一行**(「分项成绩表」「全部」「全部(单页)」都是):这类行的成绩列写 **`分数未录入`**、名次与积分列写 `—`、状态列写「草稿」,人不会从表上消失;**完全没有录成绩的报名行**(连草稿都没有)**照样占一行**——名次 / 成绩 / 积分三列都写 `—`(不是空、也不是 `0`)、状态列写「未录入」;
- **三种「还没出结果」的行也照样占一行**(「分项成绩表」「全部」「全部(单页)」都是,逐格口径完全一致):
- **录了但还没发布**:成绩列写 **`分数未录入`**、名次与积分列写 `—`、状态列写「草稿」;
- **完全没有录成绩**(连草稿都没有):名次 / 成绩 / 积分三列都写 `—`(不是空、也不是 `0`)、状态列写「未录入」;
- **成绩已发布、但那一组还没结算**(通常是管理员关掉了「分批发布时实时更新榜单」,见 10.5):**成绩照常打印**(这一条确实跑出来了),名次与积分列写 `—`、状态列写「未结算」——这样一眼能看出是「还没结算」,而不是「算出来就是 0 分」;
- **「全部」与「全部(单页)」的区别**:两者内容同源,覆盖的都是全部小项的全部报名 / 成绩;「全部」把每个小项放成一张分表(多 sheet 工作簿),「全部(单页)」把所有行平铺进同一张表,每行前面补「大项 / 小项 / 组别」三列以便脱离表名也能看懂。单页表的列是:大项、小项、组别、名次、号码、单位、运动员/队、成绩、积分、状态、备注;
- **「团体奖牌明细」与「个人积分总明细」不在本页**,在「积分榜」页对应榜单的卡片头点「导出奖牌明细」「导出个人明细」(见 8.3);个人积分总明细里的「得分」与个人积分榜一致,不受榜单口径折算系数影响;
- **「单位奖牌明细」与「个人积分总明细」不在本页**,在「积分榜」页对应榜单的卡片头点「导出奖牌明细」「导出个人明细」(见 8.3);个人积分总明细里的「得分」与个人积分榜一致,不受榜单口径折算系数影响;
- 导出文件由浏览器直接下载,表头、合并单元格与字体已按打印模板预设。
### 11.2 赛事项目包
@@ -695,7 +700,7 @@
- **表头行可以不在第一行**:从系统导出的包表头在第 4 行(上面是标题与副标题),手工整理的表表头在第 1 行,两种都能识别(识别范围是前 20 行);
- **枚举列要填系统里的英文规范值**(表头是中文,容易踩):类型 `individual` / `team` / `match` / `individual_team`,录入方式 `auto` / `manual`,成绩类型 `time` / `distance` / `count` / `score` / `none`,比较方式 `asc` / `desc` / `none`,小项状态 `pending` / `ongoing` / `finished` / `published`,平均口径 `valid` / `all`;布尔列(启用 / 计分 / 计破纪录)填 **是 / 否**;组别表的「性别」填 **男 / 女**(列在表里但格子留空 = 不改动;新建的组别留空 = 不限);
- **最稳的用法**:先「导出基础数据包」,在导出件上直接改,再导回来——表头与取值天然对得上;手工整理的表格请照导出件的表头写;
- 被「彻底删除」的单位不在导出件里;导入**不会**把它翻出来(已有的隐藏记录只更新字段,仍是隐藏状态)。
- 被「彻底删除」的单位不在导出件里;导入时如果有一行命中它的编码,**这一行整行跳过**(不更新它的任何字段,也不会把它恢复出来),并在导入结果里报一句 `单位 第 N 行:单位 XX 已被彻底删除,本行跳过(导入不会把隐藏的单位翻出来);要放回列表请先恢复该单位`——错误计入「错误 N 条」,其余合法的行照常写入。真要让这个单位回来,先到「基础数据 → 参赛单位」用「恢复」把它放回列表(见 10.1),再导入。
---
@@ -714,7 +719,7 @@
审计日志,只读、不可删除,记录谁在什么时候改了什么。
1. 可按「**动作**」「实体」筛选,点「清除筛选」复位。**动作是子串模糊匹配、由服务端在全库范围执行**(比如填 `entry.`、`import`、`team.purge` 都能筛出一批),**实体筛选与分页作用于返回的最近 200 条窗口**;
1. 可按「**动作**」「实体」筛选,点「清除筛选」复位。**动作是子串模糊匹配、由服务端在全库范围执行**(填 `import` 能筛出**全部导入类动作**——报名名单导入、冲突决策、基础数据包、规则包、赛事包都认这个词;填 `entry` 能筛出成绩录入里的批次与批量操作;填 `team.purge` 能筛出单位删除),**实体筛选与分页作用于返回的最近 200 条窗口**;
2. 列表显示时间、操作人、IP、动作、实体、请求 ID;关键动作会带「关键」标记;
3. 点某行「对比」展开,查看改动前后的 JSON 差异;
4. **动作筛选不受 200 条窗口限制**(服务端按全库匹配后再取回),实体筛选与翻页只在窗口内;
@@ -744,7 +749,7 @@
- 列表显示备份文件名、来源、大小、创建时间;
- 点「立即备份」马上生成一份;
- 服务端默认每 5 分钟自动备份一次,自动、手动、发布前备份都按 `backup.keep`(默认 20 份)滚动保留;⚠️ `deploy\backup.bat` 额外产出的 `*-uploads-*.zip`(上传图片与字体)**不在**这个滚动清理里,要手工清理;
- 每行备份都有**「恢复」**按钮(**仅管理员**):用那份备份覆盖当前库——服务端会先自动留一份「恢复前安全副本」(`pre-restore-…db`,不参与滚动清理),替换文件后优雅重启,页面自动等待刷新。**恢复是覆盖式的**(会丢备份时间点之后的改动),**恢复系统库会连账号与会话一起换掉**,**不会带回上传的图片与字体**;右侧「恢复说明」列出细节与停机手工恢复步骤(详见部署指南第 8 节)。
- 每行备份都有**「恢复」**按钮(**仅管理员**):用那份备份覆盖当前库——服务端会先自动留一份「恢复前安全副本」(`pre-restore-…db`,不参与滚动清理,**每类库只保留最近 5 份**,更旧的会被后续恢复自动清理),替换文件后优雅重启,页面自动等待刷新。**恢复是覆盖式的**(会丢备份时间点之后的改动),**恢复系统库会连账号与会话一起换掉**,**不会带回上传的图片与字体**;右侧「恢复说明」列出细节与停机手工恢复步骤(详见部署指南第 8 节)。
### 14.3 赛事信息
@@ -849,7 +854,7 @@
| 页面 | 内容 |
| --- | --- |
| 总积分榜 | 名次、单位(单位 logo + 单位名)、总分,以及名次升降箭头(约 2 秒后淡出) |
| 团体奖牌榜 | 名次、单位(带单位 logo)、金 / 银 / 铜、合计 |
| 单位奖牌榜 | 名次、单位(带单位 logo)、金 / 银 / 铜、合计 |
| 个人积分奖牌榜 | 名次、姓名(姓名前显示所属单位 logo)、单位、总分、奖牌 |
| 分项成绩 | 最新已完成项目的成绩,单位列名字前显示单位 logo |
| 实时赛况 | 当前正在进行项目的实时排名(见下方说明) |
+56 -1
View File
@@ -6,7 +6,62 @@
---
## dev_0.1.8(当前版本)
## dev_0.2.0(当前版本)
### 一句话
**没有需要迁移的数据**:这一版是 100 多项缺陷与体验修复的合集(含一次改名、几处口径收紧)。升级后榜单数字应当与升级前一致。
### ① 三件升级前必须知道的事
**1)「团体奖牌榜」改名为「单位奖牌榜」**,导出件也跟着改了名:
- 界面上:大屏控制页的切页下拉、大屏奖牌榜页标题、管理端「积分榜」页的页签,都叫 **单位奖牌榜**;
- 导出件:下载文件名由 `团体奖牌明细.xlsx` 改为 **`单位奖牌明细.xlsx`**,Excel 里的工作表名同步改为 **`单位奖牌榜`** / **`单位奖牌明细`**。
→ **如果你把旧文件名写进了对账脚本、打印模板或交接文档,记得改**。
- **旧名仍然可用**:老链接(`?page=团体奖牌榜`)与老配置(`display.pages` 里写 `medals` 或旧名)照常生效,不用改。
**2)审计日志的动作前缀统一了**(`entries.import` → `entry.import`,`entries.import.resolve` → `entry.import.resolve`):
- **升级前落库的历史记录不会跟着改**——它们仍写着旧前缀。所以**查历史时请用子串 `import` 筛**(新旧都能筛到);用 `entry.` 只能筛到升级之后的记录。审计是只追加的,我们不会改写历史。
**3)密码长度改成按「字符」数**(原来是按字节):
- 下限仍是 8 **位**,但现在按字符算:**7 个汉字(原来 21 字节能过)现在会被拒**;`3 个汉字 + 5 个字母`(8 字符)正常通过。
- 上限仍是 72 字节(bcrypt 的边界),文案里会分别写清「位」与「字节」。
### ② 现场会看到的变化
- **导出件里的「未结算」**:开了延迟结算时,「已发布但那一组还没结算」的行,导出的「分项成绩表 / 全部 / 全部(单页)」现在写 **名次 `—` / 积分 `—` / 状态 `未结算`**(成绩照常打印)。原来这三列是「空名次 + 0 分 + 有效」,读起来像「算出来就是 0 分」。
- **基础数据包导入遇到「已彻底删除的单位」**:那一行**整行跳过**并在导入结果里说明原因(不再更新它的字段、也不会把它翻出来);要让单位回来,先到「基础数据 → 参赛单位」勾选「显示已彻底删除的单位」再点「恢复」。
- **报名名单导入换了单位会明确提示**:「第 N 行:某某 换了单位,原单位 A 在本组别的报名已移出,已改在单位 B 加入」——数据行为没变(一直是这样),现在会说出来了。
- **「从已有名单勾选报名」有冲突时不再报「已加入」**:会明确提示「有 N 行没有加入,需要逐条确认」,并在页内列出是哪几行。
- **导出核对更省事**:单位积分明细的「成绩」列与分项成绩表、个人明细**逐字一致**(原来少个单位,如 `11.58` vs `11.58 秒`);空单位的「单位积分明细」也会打印末行「合计」。
- **收不到回执的危险操作不再误导**:一键恢复请求超时/断连时,提示由「恢复未执行」改为「**没收到回执,恢复可能已经生效**」,并单独放宽了超时时间;两个管理员同时点恢复时,第二个会拿到「正在恢复,请稍后」。
### ③ 部署相关的两处
- **赛事包上传上限是 512MB**(名单 CSV 与基础数据包那一档仍是 32MB,赛事包刻意放宽——它要装字体、背景图、各单位 logo;字体单个就允许 20MB)。超限会提示「解析上传失败」。
- **反向代理(nginx 等)**:若以前遇到「登录一定 403」,本版起错误提示会指明原因与修法;正解是让代理透传原 Host(`proxy_set_header Host $host;`),或配置可信代理并把原 Host 透传在 `X-Forwarded-Host` 上。**不要**为了绕过它把校验放宽——那等于开登录 CSRF 的口子。
- 顺带:**丢了 CSRF Cookie 不再等于「能看不能写」**——管理面板会自动补发,症状消失后无需再刷新页面。
### ④ 升级后 5 分钟核对
1. 打开服务管理窗口的运行日志:**这一次升级没有那条「通道名次自愈」记录是正常的**(本版没有新迁移,不需要重算);若看到它,说明你从上上版直接跳过来,等它跑完即可;
2. 刷新积分大屏与管理面板:**单位总分榜、单位奖牌榜、个人积分榜的数字与升级前一致**;
3. 随便挑一个小项导出一次「分项成绩表」,确认表头与列都对;
4. 用《上线检查清单》过一遍。
### ⑤ 详细说明在哪
| 想了解 | 看 |
| --- | --- |
| 榜名/导出文件名的变化 | 本文件 ①;《使用手册》8.2、11.1 |
| 导出里三种「还没出结果」的行 | 《使用手册》11.1;《常见问题》Q90 |
| 基础数据包遇到已删除的单位 | 《使用手册》11.4;《常见问题》Q88 |
| 恢复与安全副本保留份数 | 《部署指南》8.3;《常见问题》Q40 |
| 反代/登录 403 | 《部署指南》7.x 与应用内提示 |
---
## dev_0.1.8
### 一句话
+11 -11
View File
@@ -128,7 +128,7 @@ Console 启动的多屏窗口默认每屏锁定一页。到「大屏控制」点
- 公告保存在赛事设置里(`display.announce_text` / `display.announce_seconds`),所以服务端重启、大屏窗口重开、断线重连之后仍然显示;重开的大屏会先把当前公告全屏播 15 秒,再留在底栏。
**Q26:大屏上的单位图标显示在哪?没显示怎么办?**
配了 logo 的参赛单位会在这几处显示单位图标:总积分榜(单位名左侧)、团体奖牌榜(单位名左侧)、分项成绩(单位列名字左侧)、个人积分奖牌榜(运动员姓名左侧)。图标按行内文字大小缩放(1em),不会撑开行距;没有 logo 的单位不占位,图片临时加载不出来时保留占位、不会出现破图,队名不会跳动。
配了 logo 的参赛单位会在这几处显示单位图标:总积分榜(单位名左侧)、单位奖牌榜(单位名左侧)、分项成绩(单位列名字左侧)、个人积分奖牌榜(运动员姓名左侧)。图标按行内文字大小缩放(1em),不会撑开行距;没有 logo 的单位不占位,图片临时加载不出来时保留占位、不会出现破图,队名不会跳动。
没显示就去「基础数据 → 参赛单位 → 编辑 → Logo 路径」上传或填写该单位的 logo(管理端页面本身不显示 logo,只在大屏上显示)。上传的图片存在该赛事的 `data\event_projs\<项目名>\uploads\` 里,会随赛事项目包一起导出 / 导入。
@@ -194,7 +194,7 @@ Console 启动的多屏窗口默认每屏锁定一页。到「大屏控制」点
会。自动、手动、命令行三种方式都按 `backup.keep`(默认 20 份)滚动保留,`system` 库与赛事库各自计算。⚠️ 但 `deploy\backup.bat` 另外产出的 `*-uploads-*.zip`(上传图片与字体)**不在这个滚动清理里**,不会被自动删,需要手工清理,建议每个项目至少留最新一份。
**Q40:怎么恢复备份?**
**管理端有一键恢复(仅管理员)**:到「系统设置 → 备份」,在列表里找到要退回到的那份(`pre-restore-…` 是系统自动留的「恢复前安全副本」),点该行的**「恢复」**并过危险确认框。服务端会先给当前库拍一份安全副本,再替换库文件并优雅重启,页面自动等待刷新。「恢复系统库」会连账号与会话一起换掉,重启后需要用那个时间点的账号重新登录;**恢复不会带回上传的图片与字体**(它们在 `uploads\` 里,不在 `.db` 内)。恢复是覆盖式的,备份时间点之后的改动都会丢。
**管理端有一键恢复(仅管理员)**:到「系统设置 → 备份」,在列表里找到要退回到的那份(`pre-restore-…` 是系统自动留的「恢复前安全副本」),点该行的**「恢复」**并过危险确认框。服务端会先给当前库拍一份安全副本(**每类库只保留最近 5 份**,更旧的会被后续恢复自动清理),再替换库文件并优雅重启,页面自动等待刷新。「恢复系统库」会连账号与会话一起换掉,重启后需要用那个时间点的账号重新登录;**恢复不会带回上传的图片与字体**(它们在 `uploads\` 里,不在 `.db` 内)。恢复是覆盖式的,备份时间点之后的改动都会丢。
没有 Console 或恢复不了时的兜底是停机手工替换数据库文件:在 Console 里停止服务,到 `<数据目录>\event_projs\<项目名>\` 把目标备份复制为 `event.db`(覆盖原文件),删掉同目录的 `event.db-wal` 与 `event.db-shm`,再启动服务,详见 [部署指南 · 备份与恢复](./部署指南.md#8-备份与恢复)。
@@ -221,7 +221,7 @@ Console 启动的多屏窗口默认每屏锁定一页。到「大屏控制」点
## 导出与导入
**Q43:能导出哪些文件?**
「导出 → 结果导出」页有六类 Excel:**总表**(`总积分榜.xlsx`)、**全部**(`赛事成绩总表.xlsx`,多 sheet)、**全部(单页)**(`赛事成绩总表(单页).xlsx`,单张表平铺)、**分项成绩表**(`分项成绩表.xlsx`)、**单位积分明细**(`单位积分明细.xlsx`),以及可筛选的**报名名单**(`报名名单.xlsx`,见 Q87)。「积分榜」页另有四项:单位总分榜卡片头的**导出总明细**(`单位积分总明细.xlsx`)、团体奖牌榜卡片头的**导出奖牌明细**(`团体奖牌明细.xlsx`)、个人积分榜卡片头的**导出个人明细**(`个人积分总明细.xlsx`),以及展开单位后明细卡头的**导出该单位明细**(`单位积分明细.xlsx`)。另外可导出赛事项目包(zip)、**基础数据包**(xlsx,见 Q88)与规则包(YAML)。
「导出 → 结果导出」页有六类 Excel:**总表**(`总积分榜.xlsx`)、**全部**(`赛事成绩总表.xlsx`,多 sheet)、**全部(单页)**(`赛事成绩总表(单页).xlsx`,单张表平铺)、**分项成绩表**(`分项成绩表.xlsx`)、**单位积分明细**(`单位积分明细.xlsx`),以及可筛选的**报名名单**(`报名名单.xlsx`,见 Q87)。「积分榜」页另有四项:单位总分榜卡片头的**导出总明细**(`单位积分总明细.xlsx`)、单位奖牌榜卡片头的**导出奖牌明细**(`单位奖牌明细.xlsx`)、个人积分榜卡片头的**导出个人明细**(`个人积分总明细.xlsx`),以及展开单位后明细卡头的**导出该单位明细**(`单位积分明细.xlsx`)。另外可导出赛事项目包(zip)、**基础数据包**(xlsx,见 Q88)与规则包(YAML)。
**Q44:导出文件保存在哪?**
浏览器默认下载目录。服务端生成的临时包位于 `data\exports\`,下载完成后自动删除。
@@ -353,7 +353,7 @@ Console 启动的多屏窗口默认每屏锁定一页。到「大屏控制」点
两种常见原因:**管理员恢复了它**,或者**导入把它带回来了**。
- **恢复**:只有**管理员**能在参赛单位页勾选「**显示已彻底删除的单位**」把已删除的单位翻出来(标「已删除」),点该行的「**恢复**」即可原样放回列表;录入员(recorder)界面上看不到这个开关,删错了要**找管理员恢复**。恢复是幂等的,本来就还在时会提示「本就未被彻底删除」;若该编码已被另一个可见单位占用(只能来自手工改库),会拒绝并说明原因。
- **导入带回**:报名表里再次用到这个单位的编码(`team_code`),系统会把它**原样复活**——清掉删除标记、恢复成启用,重新出现在参赛单位列表里。因为单位编码是唯一的,系统按编码找到的就是原来那一行,所以**不会新建第二个同名单位**,原有的报名、成绩与积分也都还在。
- **导入带回**:报名表里再次用到这个单位的编码(`team_code`),系统会把它**原样复活**——清掉删除标记、恢复成启用,重新出现在参赛单位列表里。因为单位编码是唯一的,系统按编码找到的就是原来那一行,所以**不会新建第二个同名单位**,原有的报名、成绩与积分也都还在。(**注意**:这只针对**报名表**导入;用**基础数据包**导入不会复活它,见 Q88。)
这和人员的规则**看着像、结果不同**:删掉的人再次导入是**以全新档案建档**,旧数据不会回来(见 Q58);删掉的单位再次导入是**原来那个单位直接复活**,旧数据一直都在。
@@ -362,7 +362,7 @@ Console 启动的多屏窗口默认每屏锁定一页。到「大屏控制」点
**Q63:为什么积分榜里还能看到已删除的单位?**
这是**刻意设计**,不是 bug。「彻底删除」只是把单位从参赛单位列表里移除的假删除——它的报名、成绩与积分流水**一条都没删**。既然分数还在,积分榜(总积分榜、团体奖牌榜、分项成绩表)就会照旧列出这个单位的名字与分数,否则总分和名次就对不上了。
这是**刻意设计**,不是 bug。「彻底删除」只是把单位从参赛单位列表里移除的假删除——它的报名、成绩与积分流水**一条都没删**。既然分数还在,积分榜(总积分榜、单位奖牌榜、分项成绩表)就会照旧列出这个单位的名字与分数,否则总分和名次就对不上了。
所以「单位列表里没有它」不等于「它的历史成绩不存在」,两处不一致是正常的。如果确实希望它的分数不再影响榜单,那不是「彻底删除单位」能解决的事——它动的只有列表里的条目,不碰任何成绩数据。
@@ -406,17 +406,17 @@ Console 启动的多屏窗口默认每屏锁定一页。到「大屏控制」点
1. **只导一个单位**:到「积分榜」页展开该单位的明细,点右侧明细面板头部的「导出该单位明细」,得到 `单位积分明细.xlsx`(逐条列出每一分的来源,末行是「合计」);
2. **一次导全部单位**:在「积分榜 → 单位总分榜」的**卡片头**点「导出总明细」,得到 `单位积分总明细.xlsx`——一张带「单位」列的总表,外加每个**有数据**的单位一张分表(没有积分的单位不出分表)。
团体奖牌榜那一列的「明细」展开的仍是**同一个单位积分明细面板**,所以想按单位看时也可以在那上面点「导出该单位明细」;但团体奖牌榜本身另有专属的逐枚奖牌导出(卡片头「导出奖牌明细」,见 Q71),个人积分榜也有专属的逐条得分导出(卡片头「导出个人明细」,见 Q70、Q72)。另外「导出 → 结果导出」里的「单位积分明细」也能先选一个单位再导出。这些导出都是只读下载,访客也能用。
单位奖牌榜那一列的「明细」展开的仍是**同一个单位积分明细面板**,所以想按单位看时也可以在那上面点「导出该单位明细」;但单位奖牌榜本身另有专属的逐枚奖牌导出(卡片头「导出奖牌明细」,见 Q71),个人积分榜也有专属的逐条得分导出(卡片头「导出个人明细」,见 Q70、Q72)。另外「导出 → 结果导出」里的「单位积分明细」也能先选一个单位再导出。这些导出都是只读下载,访客也能用。
**Q70:个人积分榜能导出明细吗?**
能。个人积分榜的**卡片头**有「导出个人明细」按钮,导出 `个人积分总明细.xlsx`:第一张总表逐条列出「单位 / 运动员 / 大项 / 小项 / 组别 / 名次 / 成绩 / 得分 / 来源」,其后每个**有得分**的单位一张分表(按单位分,不按人分;没有得分流水的人不出现在表里)。`得分` 是记到个人的原始分,与「个人积分榜」逐个数字一致,**不受榜单口径里的个人项目得分折算系数影响**(那个系数只作用于单位总分),详见 Q72。
**Q71:团体奖牌榜能导出每一块奖牌的来源吗?**
能。到「积分榜 → 团体奖牌榜」,点**卡片头**的「导出奖牌明细」,得到 `团体奖牌明细.xlsx`:
**Q71:单位奖牌榜能导出每一块奖牌的来源吗?**
能。到「积分榜 → 单位奖牌榜」,点**卡片头**的「导出奖牌明细」,得到 `单位奖牌明细.xlsx`:
- 第一张总表逐枚列出「单位 / 大项 / 小项 / 组别 / 名次 / 奖牌 / 运动员、队」,`奖牌` 列是 `金牌` / `银牌` / `铜牌`;
- 其后每个**有奖牌**的单位一张分表,列与总表相同但去掉「单位」,末尾多一行小计 `金 X / 银 Y / 铜 Z`;没有奖牌的单位不出分表;
- 口径与「团体奖牌榜」一致:同一个报名项在「个人+团体」这类双计通道下只记一枚奖牌,导出不会重复计数。
- 口径与「单位奖牌榜」一致:同一个报名项在「个人+团体」这类双计通道下只记一枚奖牌,导出不会重复计数。
**Q72:个人积分明细导出的数字跟个人积分榜对得上吗?会不会受榜单口径系数影响?**
对得上,而且**不受系数影响**。「导出个人明细」(`个人积分总明细.xlsx`)里的 `得分` 是**记到个人的原始分**,与「个人积分榜」逐个数字一致;「基础数据 → 积分模板 → 榜单口径」里的个人项目得分折算系数(键名 `standings.individual_to_team_weight`,默认 1)只在汇总环节把个人分折算进单位总分,**不会改变个人明细里的数字**——比如把系数改成 0.5,个人明细的得分仍是原始分,只有单位总分变小(见 Q18)。
@@ -540,7 +540,7 @@ Console 启动的多屏窗口默认每屏锁定一页。到「大屏控制」点
能。到「导出 → 基础数据」页:
- **导出**:一个工作簿四张工作表(单位 / 大项 / 小项 / 组别,组别表带所属小项编码);
- **导入**(**仅管理员**,只认 `.xlsx`):按编码匹配,**存在就更新、不存在就新建,绝不删除**(表里没有的行不会被删、也不会被停用)。先点「预览差异」看「新增 / 更新 / 无变化 / 错误」与逐行变化,确认后再点「**确认写入**」;**有错的行会跳过、合法的行照常写入**,行级错误是中文并带真实 Excel 行号(如 `单位 第 3 行:单位编码必填`)。
- **导入**(**仅管理员**,只认 `.xlsx`):按编码匹配,**存在就更新、不存在就新建,绝不删除**(表里没有的行不会被删、也不会被停用)。先点「预览差异」看「新增 / 更新 / 无变化 / 错误」与逐行变化,确认后再点「**确认写入**」;**有错的行会跳过、合法的行照常写入**,行级错误是中文并带真实 Excel 行号(如 `单位 第 3 行:单位编码必填`)。已「彻底删除」的单位不在导出件里,命中它的编码那一行会**整行跳过并报错**(不更新它的任何字段、也不会把它恢复出来,想让单位回来先用「参赛单位」页的「恢复」,见 Q62)。
要点:**列不在表里 = 不动该字段**(**布尔列也一样**,缺整列 ≠ 取默认值);**列在表里但格子空**:文本列与年龄上下限清空,权重按 1、布尔列(启用 / 计分等)按「是」,**排序留空保持原值**(**四张表口径一致**,单位 / 大项留空同样不会被改写)。**只有枚举列例外:列在表里但格子空 = 不改动**(小项的类型 / 结果模式 / 结果类型 / 比较 / 状态 / 平均口径、组别的性别),留空就沿用原来的值,只有**新建的行**才用默认值(新建组别时性别留空 = 不限)——这样误清一格不会把已发布小项打回「待录入」、也不会把「团体」改成「个人」。表头行在第 4 行(导出件)或第 1 行(手工表)都能识别;枚举列要填英文规范值、布尔列填「是 / 否」。**最稳的做法是从系统导出、改完再导回**。详见使用手册 11.4。
@@ -550,7 +550,7 @@ Console 启动的多屏窗口默认每屏锁定一页。到「大屏控制」点
⚠️ 但它不是「永不掉线」:超过设定的有效期一直没操作(浏览器关掉、机器关机过夜),再打开仍要重新登录。改设置不会把正在用的会话当场踢下线:已登录会话的到期时间不会在改设置的一刻改变,它会在**下一次操作时按新的有效期续期**(调长立即顺延,例如 12 小时改 72 小时后,正在用的会话下次有请求就顺延成 72 小时;调短也只影响之后的续期)。
**Q90:发布后成绩上屏了,为什么积分榜没变、名次还是「—」?**
多半是管理员把「榜单口径」里的**「分批发布时实时更新榜单」关掉了**(默认是开的),也就是**延迟结算**:分批发布时只把成绩上屏,**积分与名次等该项目该组所有批次都录完才一次性结算**(报名状态不是「正常参赛」的人——退赛 / 取消资格 / 未出场——不算在等待里)。未结算组的**名次显示 `—`**(成绩照常显示)是有意的,免得出现「有金牌但 0 分」这种自相矛盾的画面。
多半是管理员把「榜单口径」里的**「分批发布时实时更新榜单」关掉了**(默认是开的),也就是**延迟结算**:分批发布时只把成绩上屏,**积分与名次等该项目该组所有批次都录完才一次性结算**(报名状态不是「正常参赛」的人——退赛 / 取消资格 / 未出场——不算在等待里)。未结算组的**名次显示 `—`**(成绩照常显示)是有意的,免得出现「有金牌但 0 分」这种自相矛盾的画面。导出的「分项成绩表」「全部」「全部(单页)」对这类行也是**名次与积分列写 `—`、状态列写「未结算」**(成绩照常打印),见使用手册 11.1。
想让榜单立刻跟上:等该组录完会自动结算;确实有人永远录不出成绩时,在**成绩录入页**点 **「立即结算」**,按当前已发布成绩先结算一次。想改回实时结算:管理员到「基础数据 → 积分模板 → 榜单口径」,把「分批发布时实时更新榜单」勾上并点「保存结算方式」(见使用手册 10.5)。
+1 -1
View File
@@ -201,7 +201,7 @@
| 第 N 行:status「xxx」无法识别 | `status` 填了系统不认的词(4 个标准词以外的英文,或第三节没列的中文) | 改成 `registered` / `scratched` / `dq` / `dns`(大小写都行)或第三节列的中文别名,也可以留空(= 正常参赛);改完重新上传 |
| 第 N 行:heat 需要是 1-999 的整数(留空表示未分批) | `heat` 填了非整数、0、负数或超过 999 的值(如 `第1批`、`一组`、`1000`) | 改成 1–999 的整数,或者整格留空(= 未分批);批次只影响录入分组,不参与算分 |
| 提示「冲突」,人员没有写入 | 按证件号(或号码布、「姓名 + 单位」唯一命中)的人在系统里已有记录,但姓名 / 证件号 / 性别 / 年龄 / 单位 / 号码布跟表里**对不上** | 在导入结果里逐条选「保留已有数据」或「使用新数据」,选完再提交 |
| 文件传不上去 | 超过 16MB | 拆分上传,或改用 xlsx |
| 文件传不上去 | 超过 32MB | 拆分上传,或改用 xlsx |
| 只导入了第一张表 | xlsx 只读第一个工作表 | 把要导入的数据放在第一个工作表里 |
---
+9 -6
View File
@@ -111,6 +111,7 @@ deploy\build.bat
- Console 启动时会把这个目录用 `-data` 参数交给服务端,界面上方「运行日志」里能看到 `数据目录:...` 这一行;
- **服务端日志也在数据目录下**:`<数据目录>\logs\` 里分 `operations\`(放行类)与 `interceptions\`(拦截类)两个子目录,按天 + 按大小轮转、自动清理旧文件,可用 `config.yaml` 的 `log:` 段改位置与保留份数(见第 10 节)。它在安装目录之外,覆盖安装不会丢;
- **恢复未完成标记**:数据目录根部的 `pending_restore.json` 只在「一键恢复」**中途异常中断**时留下(正常走完会被清掉),里面记着备份文件名、目标库、恢复前的安全副本与操作员。服务端下次启动会读它并在运行日志里给一条 WARN(含处置建议),**只读不删**——它是现场判断「这台机器上发生过一次没走完的恢复」的唯一线索。平时这个文件不存在,看到它就该按日志提示核对数据库;
- **上传资源随赛事项目走**:赛事会标、大屏背景图、单位 logo,以及「赛事信息 → 字体」里上传的**字体文件**,都在 `<数据目录>\event_projs\<项目名>\uploads\` 里(字体在 `uploads\fonts\`)。它们是文件而不是数据库记录,**不在 `backups\` 的 `.db` 快照里**(`.db` 快照与「一键恢复」都只动数据库,不含这些文件);备份 / 迁移时要连这个目录一起拷(见 8.5、8.6)。构建/开发机上的 `deploy\backup.bat` 会额外把每个项目的 `uploads\` 打成 `*-uploads-<时间戳>.zip`,但那份 zip 不在服务端的 20 份滚动保留里,要手工清理;
- **一次性迁移**:如果新数据目录还是空的,而旧的 `<安装目录>\data` 里有数据,Console 会在启动时把旧目录**整体改名**过去(同盘很快),日志里会写「已把旧数据从 X 迁移到 Y」。任何失败(跨盘、权限不足等)都只记日志,旧数据原地保留,不会被删;
- 如果两个目录里**都有数据**,Console 不做任何动作,只写一条警告日志,由现场人工决定保留哪一份;
@@ -369,7 +370,8 @@ scoreboard.exe passwd -config "%LOCALAPPDATA%\ScoreboardConsole\config.yaml" -da
- 除内置屏外的每块屏各开一个 kiosk 全屏窗口;
- 每块屏**锁定一个页面**,按 `display.pages` 的顺序轮转分配;
- 多屏 kiosk 不响应 F11,也不接收鼠标(`ignoreMouse`),也不显示任何调试标识;
- 可用的页面值:`standings`(总积分榜)、`medals`(团体奖牌榜)、`individual`(个人积分奖牌榜)、`results`(分项成绩)、`live`(实时赛况)、`announcement`(公告);管理端切页用的是同一批页面的规范名 `team-medals` / `athlete-standings` / `event-results`,两种写法大屏都认。把 `live` 加进 `display.pages`,就能把某块屏固定成「实时赛况」(`live` 也认 `live-results` / `实时赛况` / `实时` / `赛况` 这些别名,与其它页一样)。
- 可用的页面值:`standings`(总积分榜)、`medals`(单位奖牌榜)、`individual`(个人积分奖牌榜)、`results`(分项成绩)、`live`(实时赛况)、`announcement`(公告);管理端切页用的是同一批页面的规范名 `team-medals` / `athlete-standings` / `event-results`,两种写法大屏都认。把 `live` 加进 `display.pages`,就能把某块屏固定成「实时赛况」(`live` 也认 `live-results` / `实时赛况` / `实时` / `赛况` 这些别名,与其它页一样)。
- 这一页现在叫 **「单位奖牌榜」**(旧称「团体奖牌榜」容易和「团体项目的奖牌」混淆,已改名);**旧名 `团体奖牌榜` 仍作为别名被接受**,老配置里的 `display.pages`、大屏地址里 `page=团体奖牌榜` 这类老链接都照常生效,不需要改。
### 7.5 在管理端单独控制某块屏
@@ -393,7 +395,7 @@ scoreboard.exe passwd -config "%LOCALAPPDATA%\ScoreboardConsole\config.yaml" -da
### 7.7 大屏画面里的调试标识与单位 logo
- **调试标识**:只有**窗口化且没有全屏**的大屏窗口才会显示一行「实例 … · 屏 …」;**多屏 kiosk 全屏完全不带调试标识**,单屏窗口化窗口按 F11 全屏后它也会连同自绘标题栏一起隐藏。数据版本号角标 `v<n>` 已移除,任何情况下都不再显示(要核对某块屏是否跟上服务端,看管理端仪表盘「实时连接」里的「版本 N」)。要一个完全干净的展示画面,就用多屏 kiosk;
- **单位 logo**:参赛单位的 logo 会显示在总积分榜、团体奖牌榜、分项成绩表与个人积分榜上(个人榜显示在姓名前);没上传 logo 的单位不占位。logo 在「基础数据 → 参赛单位」里上传,随赛事项目包一起导出/导入(见 8.5)。
- **单位 logo**:参赛单位的 logo 会显示在总积分榜、单位奖牌榜、分项成绩表与个人积分榜上(个人榜显示在姓名前);没上传 logo 的单位不占位。logo 在「基础数据 → 参赛单位」里上传,随赛事项目包一起导出/导入(见 8.5)。
### 7.8 大屏窗口的自动行为
@@ -454,7 +456,7 @@ http://<主机IP>:19540/display
**首选:管理端「一键恢复」(仅管理员)。** 到「系统设置 → 备份」,在备份列表里找到要退回到的那一份(列表会显示时间、原因、大小;`pre-restore-…` 是恢复前自动留的安全副本),点该行的**「恢复」**:
1. 先过危险确认框(会写明要覆盖的是系统库还是赛事库、会丢弃备份时间点之后的改动);
2. 服务端先**给当前库拍一份「恢复前安全副本」**(`pre-restore-<库>-<时间戳>.db`,不参与 20 份滚动清理,选错时间点还能用它退回),再替换库文件,然后**优雅退出并由 Console 自动拉起**;
2. 服务端先**给当前库拍一份「恢复前安全副本」**(`pre-restore-<库>-<时间戳>.db`,不参与 20 份滚动清理,选错时间点还能用它退回;**每类库只保留最近 5 份**,更旧的会在后续恢复时被自动清理),再替换库文件,然后**优雅退出并由 Console 自动拉起**;
3. 页面显示「正在重启服务端…」并自动等待刷新(最多 60 秒;超时就打开服务管理窗口确认状态后手动刷新);
4. **恢复是覆盖式的**:备份时间点之后的所有改动都会丢。恢复系统库还会连**账号与会话**一起换掉,重启后需要用那个时间点的账号重新登录;
5. ⚠️ **恢复不会带回上传的图片与字体**(它们在 `uploads\` 里是文件,不在 `.db` 内);需要一起回退时见 8.6。
@@ -496,7 +498,7 @@ http://<主机IP>:19540/display
- `uploads/`:**全部上传资源**——赛事会标、大屏会标、大屏背景图、各参赛单位 logo,以及**上传的字体**(放在 `uploads/fonts/` 下,见使用手册 14.3)。导入时会连文件一起落到新项目目录的 `uploads/`,切换过去后图片直接显示、字体直接可用,不用重新上传;
- `rules.yaml`:当前规则包的一份 YAML 副本,便于单独复用到别的赛事;规则本身存在赛事库里,导入后即刻生效,这份只是副本。
导出界面同一页的「结果导出」标签可以把最终结果导成 xlsx:**总表**(总积分榜 + 团体奖牌榜 + 个人积分奖牌榜,一个工作簿)、**全部**(总表 + 全部分项)、**全部(单页)**(所有成绩平铺在一张表)、**分项成绩表**(按小项)、**单位积分明细**(按单位)各自可单独导出打印,另有可筛选(小项 / 组别 / 单位 / 批次)的**报名名单**;「积分榜」页三个榜单的卡片头还各有明细导出(**导出总明细** / **导出奖牌明细** / **导出个人明细**,见使用手册 8.3)。基础数据(单位 / 大项 / 小项 / 组别)另有单独的基础数据包可导出 / 导入,见 8.7。
导出界面同一页的「结果导出」标签可以把最终结果导成 xlsx:**总表**(总积分榜 + 单位奖牌榜 + 个人积分奖牌榜,一个工作簿)、**全部**(总表 + 全部分项)、**全部(单页)**(所有成绩平铺在一张表)、**分项成绩表**(按小项)、**单位积分明细**(按单位)各自可单独导出打印,另有可筛选(小项 / 组别 / 单位 / 批次)的**报名名单**;「积分榜」页三个榜单的卡片头还各有明细导出(**导出总明细** / **导出奖牌明细** / **导出个人明细**,见使用手册 8.3)。基础数据(单位 / 大项 / 小项 / 组别)另有单独的基础数据包可导出 / 导入,见 8.7。
导入与切换:
@@ -510,6 +512,7 @@ http://<主机IP>:19540/display
- 不能覆盖**正在运行**的赛事项目:对当前赛事的同名包,导入会被直接拒绝(先切到别的赛事再导);
- 勾选「覆盖同名项目」时,旧目录会先改名保留、新项目落盘成功后才删除它;
- 参赛单位 logo、会标、背景图与上传的字体都在 `uploads\` 里,所以**备份 / 迁移时要连 `event_projs\<项目名>\uploads\` 一起拷**(见 8.6);
- **赛事包上传上限是 512MB**(名单 CSV 与基础数据包那一档是 32MB,这里刻意放宽):赛事包是唯一带二进制资源的包——上传字体单个就允许 20MB(且可以传多个),再加上背景图、会标与各单位 logo,「两个字体 + 一张背景图」的正常包很容易超过 32MB,按 32MB 卡会让正常包导不进来。真的超过 512MB 时导入会被拒绝并提示「解析上传失败」,先到「系统设置 → 赛事信息」删掉用不到的图片 / 字体,再重新导出一份包;
- **不要用 `-project` 启动参数来切赛事**:显式传 `-project` 时,服务端会顺手把它写回「上次打开的赛事」记忆文件,于是以后每次启动都优先打开这个项目——在页面里切完、重启后又被拉回去。只有在「刻意把一台机器固定成只开某一个项目」时才这么用:在 Console 的用户配置 `config.json` 里给 `server.args` 加 `"-project","<项目名>"` 后重启 Console,或直接 `scoreboard.exe -config "%LOCALAPPDATA%\ScoreboardConsole\config.yaml" -project <项目名> -data "%LOCALAPPDATA%\ScoreboardConsole\data"` 启动(见 6.2)。
### 8.6 手工备份 / 迁移整个数据目录
@@ -539,7 +542,7 @@ http://<主机IP>:19540/display
- **列不在表里 = 不动该字段**(**布尔列也一样**:缺整列不等于「取默认值」,否则把「启用」列删掉再导回,会把所有停用的单位/大项静默改成启用);**列在表里但格子空**:文本列与年龄上下限清空,权重按 1、布尔列按「是」,**排序留空保持原值**(**四张表口径一致**,单位 / 大项留空同样不会被改写);
- **枚举列例外:列在表里但格子空 = 不改动**(不是「取默认值」)。小项的类型 / 结果模式 / 结果类型 / 比较 / 状态 / 平均口径,以及组别的性别,留空都沿用原值;只有**新建的行**才用默认值(新建组别时性别留空 = 不限)。这是为了防手滑——误清一格不会把已发布小项打回「待录入」、也不会把「团体」改成「个人」;
- 表头行在第 4 行(系统导出件,上面是标题与副标题)或第 1 行(手工整理的表)都能识别;枚举列要填英文规范值(如 `individual` / `time` / `published`),布尔列填「是 / 否」;
- 被「彻底删除」的单位不在导出件里,导入也**不会**把它翻出来(仍是隐藏状态);
- 被「彻底删除」的单位不在导出件里,导入也**不会**把它翻出来:命中它的编码那一行会**整行跳过**(不更新它的任何字段),并在导入结果里报 `单位 第 N 行:单位 XX 已被彻底删除,本行跳过(导入不会把隐藏的单位翻出来);要放回列表请先恢复该单位`,其余合法的行照常写入。要让这个单位回来,先到「基础数据 → 参赛单位」用「恢复」把它放回列表(见使用手册 10.1);
- **这个包只含基础数据**,不含成绩、报名与规则;整场赛事的搬迁仍用赛事项目包(见 8.5)。赛前导出一份留底,现场改乱了可以导回对照。
---
@@ -702,7 +705,7 @@ http://<主机IP>:19540/display
- [ ] **当前赛事正确**:「导出 → 赛事项目包 → 赛事项目列表」里带「当前」徽标的是本次要用的那个项目(刚导入的包要切换过去才算生效,见 8.5)
- [ ] 大屏 HDMI 输出定位正确、分辨率正确、字体正常
- [ ] **显示器菜单可用**:在服务管理窗口打开的「大屏控制」页里能看到每块屏并「创建全屏窗口」,副屏被误关时能重开(见 7.5)
- [ ] **单位 logo 已上传且在榜上显示**(总积分榜 / 团体奖牌榜 / 分项成绩表 / 个人积分榜,见 7.7)
- [ ] **单位 logo 已上传且在榜上显示**(总积分榜 / 单位奖牌榜 / 分项成绩表 / 个人积分榜,见 7.7)
- [ ] **大屏字体已选好并核对**:「系统设置 → 赛事信息 → 字体」里选了标题字体与正文字体,大屏上显示正常;上传的字体在数据目录 `event_projs\<项目名>\uploads\fonts\` 里,导出赛事项目包会一并带走(见 8.5)
- [ ] **公告可用**:推送一条测试公告,全屏到时后底栏仍能看到正文,点「清除公告」后底栏回到「暂无公告」(见 7.6)
- [ ] 管理端各角色账号可登录,权限正确