Files
Scoreboard/docs/报名名单导入规范.md
T
AskaEth 5ae49b001c feat: 参赛单位「彻底删除」= 假删除(界面隐藏,数据与榜单全保留)
与运动员的真物理删除是**两套相反口径**,有意如此(两个按钮同名,代码与
文档里都做了显著区分):

- 运动员 `?purge=1`:真删人员+报名+成绩+流水并重算积分(上一版 `9d4ee09`)
- 参赛单位 `?purge=1`:只置 `teams.deleted_at` + `active = 0`,让单位从列表里
  消失;报名、成绩、积分流水、人工调整、纪录、名下运动员全部原样,
  **不触发重算、积分榜不变**(单位下挂着几十号人,真删代价太大)

实现:
- 迁移 0018 `ALTER TABLE teams ADD COLUMN deleted_at TEXT`
- `listTeams` 默认排除已隐藏单位;`?includeDeleted=1` 仅 admin;
  `GET /api/teams/{id}` 对已隐藏单位:admin 200 / 非 admin 404
- 审计 `team.purge`(detail 标明是隐藏);成功走 `writeVoid`(无统计对象)
- 报名导入按 `team_code` 命中已隐藏单位时复活(清 deleted_at、置 active=1)
- 故意不过滤的地方都加了注释:榜单补单位名、写路径与审计、导入按 code 查、
  赛事包导出、大屏快照、按 team_id JOIN 取历史单位名——过滤会让历史成绩的
  单位名变空白或让包的引用悬挂

管理端「参赛单位」行加「彻底删除」按钮 + 危险确认框(如实写明"从列表移除、
数据不受影响、积分榜不会变化"),失败原文展示、404 不降级。

顺带修一处界面文案与行为不符:单位页原写「停用后新报名不再可选该单位」,
但报名选择器其实仍会列出已停用单位供手动勾选(只有「全选」跳过它们),
已按真实行为改写(两处)。

测试:服务端 12 包全绿(新增 team_purge_test.go 与 0018 迁移用例);
前端 172 例全过(新增 team-purge-api.test.ts 6 例)。docs/ 与 devdocs/ 同步。
2026-09-10 23:48:28 +08:00

198 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 报名名单导入规范
> 这份文件说明**报名名单应该做成什么样子、每一列怎么填**,用于把各单位报上来的名单一次性导入积分榜系统。
> 给负责收表、填表的同志看即可,不需要懂系统内部。
>
> 配套模板可在系统里直接下载:**基础数据 → 报名导入 → 下载 CSV 模板**。
---
## 一、先看懂三件事
1. **用什么软件做表**:Excel 或 WPS 都行,做完存成 `.xlsx` 或 `.csv` 都可以(推荐 `.csv`,系统兼容性最好,不用管编码)。
2. **表格长什么样**:**第一行是表头**(固定的 11 个英文列名),**从第二行开始每人一行**。不要合并单元格,不要在表头上方再加标题行、说明行。
3. **编码不能自己编**:表里的「小项编码」「单位编码」「组别名称」必须是系统里已经配好的,**照抄系统里的写法**。系统里没有的编码,这一行会被跳过并报错。
---
## 二、表头(第一行的列名)
一共 11 列。**列名请直接用下面这套英文名,不要翻译成中文**;列的**先后顺序可以随便调**,多出来的列系统会忽略。
| 列名 | 是否必须 | 填什么 | 示例 |
|---|---|---|---|
| `event_code` | **必须** | 小项编码,抄系统里配好的 | `M100` |
| `category` | **必须** | 组别**名称**(中文,要跟系统里一字不差) | `男子30岁以下` |
| `team_code` | **必须** | 参赛单位编码,抄系统里配好的 | `F1` |
| `athlete_name` | **必须** | 运动员姓名。**团体项目这一列留空** | `张伟` |
| `id_card` | 可选 | 证件号(身份证号) | `110101199001011234` |
| `gender` | 可选 | 性别,只写「男」或「女」 | `男` |
| `age` | 可选 | 年龄,0–120 的整数 | `26` |
| `bib` | 可选 | 号码布 | `101` |
| `seed` | 可选 | 道次 / 种子,随便写或留空 | `种子1` |
| `status` | 可选 | 报名状态:4 个英文词(大小写都行)或等价的中文别名(见下) | `registered` |
| `member_names` | 可选 | 团体项目的队员名单,多人用 `/` 隔开 | `张三/李四/王五` |
> 「必须」指的是**这一列的表头要有**。至于每格里能不能留空,看下面第三节逐列的说明。
**表头大小写不敏感**:写 `Event_Code` 或 `event_code` 都认。
---
## 三、逐列怎么填(重点)
### `event_code` 小项编码 —— 必须填
在系统「基础数据 → 大项 / 小项」里给每个小项配的**编码**,比如 `M100`(男子100米)、`BB`(多人抓杆蛇形跑)。
填了系统里没有的编码,这一行会被跳过,提示「小项 XXX 不存在」。
### `category` 组别名称 —— 必须填
填**组别的名字**,不是编码。比如「男子30岁以下」「男子31岁以上」「女子组」「基层组」「领导组」。
- 必须和系统里该小项的组别名称**完全一致**(含标点),否则提示「组别 XXX 不存在」。
- 这一格**留空**也可以:系统会自动放到该小项排序最靠前的那个组别。一个单位一个项目只报一个组时,留空能省事;报了多个组别的项目,必须写明。
### `team_code` 单位编码 —— 必须填
在系统「基础数据 → 参赛单位」里给每个大单位配的**编码**,比如 `F1`、`F2`。系统里没有的会提示「单位 XXX 不存在」。
被「彻底删除」的单位,导入时若再次用到它的编码,它会**重新出现(复活)**,这一行照常导入——单位编码是唯一的,系统按编码复用原来那条记录,它以前的报名、成绩与积分都还在。
### `athlete_name` 运动员姓名
- **个人项目**:填姓名,必填。
- **团体项目**:**这一列留空**,表示"这是某个单位的一条团体报名",队员写在 `member_names` 里。
- 姓名为空的行,系统只建一条单位报名,不会去建运动员档案。
### `id_card` 证件号 / `bib` 号码布 —— 二选一,至少填一个
只要有姓名,**证件号与号码布至少要填一个**,否则无法区分同名同姓的两个人,这一行会被跳过,提示「填写了 athlete_name 就必须填 id_card 或 bib」。
- 优先认证件号;没有证件号就认号码布(比如没有身份证的、外聘人员)。
- **同一份表里,同一个证件号或同一个号码布只能出现一次**,重复的会被跳过并报错。
- 号码布会同时写进人员档案和报名记录。
### `gender` 性别
只认「男」「女」两个字。留空没问题;写「男性」「女 」等其它写法会被**当成没填**(不报错,但也不记录)。
### `age` 年龄
0–120 的整数,可以留空。填了「二十六」「26岁」这种会被跳过,提示「age 需要是 0-120 的整数」。
### `seed` 道次 / 种子
自由填写,可以留空。系统只做记录,不参与算分、不影响名次。
### `status` 报名状态 —— 英文词或中文别名都认
**大小写不敏感**(`registered`、`Registered`、`DNS` 都认),也可以直接写中文:
| 英文写法 | 等价的中文别名 | 含义 |
|---|---|---|
| `registered` | `正常` / `正常参赛` / `参赛` | 正常参赛 |
| `scratched` | `退赛` / `弃赛` / `请假` | 退赛 / 弃赛 |
| `dq` | `取消资格` / `犯规` | 取消资格 |
| `dns` | `未出场` / `未到` / `缺席` | 未出场 |
留空 = `registered`(正常参赛),拿不准就留空,特殊情况再填。
真正**认不出**的写法(例如「已报到」「未参赛」):这一行会被跳过,并给出中文行级提示,其余行照常导入。
> 第 12 行:status「已报到」无法识别,只能填 registered / scratched / dq / dns(或留空)
### `member_names` 队员名单
团体项目用这一列写队员,多人之间用 `/`、`,` 或 `,` 分开:
```
张三/李四/王五
张三,李四,王五
```
- 团体项目**可以只报单位、不写队员**(这一列留空即可),队员名单以后还能补。
- 系统界面里会把这条名单显示成「张三、李四、王五」。
---
## 四、三个完整例子
### 例 1:个人项目(七单位 × 若干人)
表头那一行照抄,下面每人一行。假设 `M100` 是按年龄分组的男子100米,`F1`/`F2` 是两个单位:
| event_code | category | team_code | athlete_name | id_card | gender | age | bib | seed | status | member_names |
|---|---|---|---|---|---|---|---|---|---|---|
| M100 | 男子30岁以下 | F1 | 张伟 | 110101199001011234 | 男 | 26 | 101 | | registered | |
| M100 | 男子31岁以上 | F1 | 李强 | 110101198505054321 | 男 | 41 | 102 | | registered | |
| M100 | 女子组 | F2 | 王芳 | | 女 | | 202 | | registered | |
| W100 | 女子组 | F2 | 王芳 | 110101199203031234 | 女 | 34 | 202 | | registered | |
要点:**同一个人报多个项目,就写多行**(如上面的王芳报了两项);每行的 `event_code` 不同。
### 例 2:团体项目(七单位 × 两组)
`BB` 是「多人抓杆蛇形跑」,七家单位各出基层组和领导组两支队伍:
| event_code | category | team_code | athlete_name | id_card | gender | age | bib | seed | status | member_names |
|---|---|---|---|---|---|---|---|---|---|---|
| BB | 基层组 | F1 | | | | | | | registered | 张三/李四/王五/赵六 |
| BB | 基层组 | F2 | | | | | | | registered | 钱七/孙八 |
| BB | 领导组 | F1 | | | | | | | registered | 周九 |
| BB | 领导组 | F3 | | | | | | | registered | |
要点:`athlete_name` 一列**全部留空**;不参赛的单位**不写行**(不写就是没报名);最后一行是不写队员的纯单位报名,也是允许的。
### 例 3:「个人 + 团体」项目
这类项目(比如团体引体向上)**按个人报名填**:每人一行、填姓名和单位。系统会自己算两份成绩——个人的名次分记在个人身上,团体分按各队成员的**平均成绩**排名后算给单位。**不需要**再单独写团体行。
---
## 五、导入前请先检查(自检清单)
- [ ] 第一行是那 11 个英文列名,没有被翻译、没有被合并单元格
- [ ] 表头上方没有多余的标题行或空行
- [ ] `event_code`、`team_code` 都是从系统里抄的编码,没有空格
- [ ] `category` 与系统里的组别名一字不差
- [ ] 个人行都有姓名,且都有证件号或号码布
- [ ] 一份表里没有重复的证件号、没有重复的号码布
- [ ] `status` 只用那 4 个英文词(大小写都行)或第三节列的中文别名,不写别的词;拿不准就留空
- [ ] 全表没有因为"怕麻烦"把某些列删掉——**表头 8 个必须列一个都不能少**(哪怕整列是空的)
---
## 六、常见报错怎么办
| 提示 | 原因 | 怎么办 |
|---|---|---|
| 第 N 行:event_code / team_code 必填 | 这两格至少有一格是空的 | 每行都必须有小项编码和单位编码 |
| 报名表缺少列 xxx | 表头少了这一列,或者列名被写成中文 / 有错别字 / 上面多了标题行 | 对照第二节的表头逐字核对;最稳的是重新下载模板,把数据粘进去 |
| 中文变成乱码 | 以前会这样(Excel 另存的 CSV 编码问题),**现在已修好** | 直接传原文件即可,不用转码 |
| 第 N 行:小项 XXX 不存在 | `event_code` 写错,或该小项还没在系统里配 | 到「基础数据 → 大项 / 小项」核对编码;没配的先配 |
| 第 N 行:单位 XX 不存在 | `team_code` 写错,或该单位还没配 | 到「基础数据 → 参赛单位」核对 |
| 第 N 行:组别 XXX 不存在 | 组别名和系统里不一致,或该小项还没有这个组别 | 到「基础数据 → 组别」核对名称(注意别多字少字) |
| 第 N 行:小项 XXX 没有组别 | 该小项一个组别都没配 | 先在「组别」里配好,再导入 |
| 第 N 行:填写了 athlete_name 就必须填 id_card 或 bib | 个人行缺两项唯一标识 | 补证件号或号码布;确实都没有的,请先在「运动员」里登记 |
| 第 N 行:证件号 / 号码布 … 在报名表内重复出现 | 同一份表里同一个人出现了两次 | 删掉重复行(同一个人报多个项目时,`event_code` 必须不同) |
| 第 N 行:status「xxx」无法识别 | `status` 填了系统不认的词(4 个标准词以外的英文,或第三节没列的中文) | 改成 `registered` / `scratched` / `dq` / `dns`(大小写都行)或第三节列的中文别名,也可以留空(= 正常参赛);改完重新上传 |
| 提示「冲突」,人员没有写入 | 这个人在系统里已有记录,但姓名 / 性别 / 年龄 / 单位 / 号码布跟表里**对不上** | 在导入结果里逐条选「保留已有数据」或「使用新数据」,选完再提交 |
| 文件传不上去 | 超过 16MB | 拆分上传,或改用 xlsx |
| 只导入了第一张表 | xlsx 只读第一个工作表 | 把要导入的数据放在第一个工作表里 |
---
## 七、导入以后
1. 系统会显示**新增 / 更新 / 跳过**的数量:
- **新增**:这次新建的报名;
- **更新**:这个人本来就有这个项目的报名,用表里的信息覆盖了(**不会产生重复报名**);
- **跳过**:没能导入的行,会带着**行号**列出来,改好那几行重新上传即可。
2. **重复上传同一份表是安全的**,不会把名单变成两份。改完再传一遍就行。
- ⚠️ 但要注意「更新」是**整条覆盖**:表里空着的格会把系统里原来的值清掉(队员名单、道次、号码布都会被清空),`status` 空着会被重置成「正常参赛」。如果之前手工调整过某些人的状态,**重传前请在表里把 `status` 写清楚**。
3. 名单导入后,到「成绩录入」里录成绩、发布,积分榜就会自动更新。
4. **被「彻底删除」的人,导入时会以新人员建档**:管理员彻底删除某个人时,他的档案连同他名下的报名与成绩会一起从库里删掉;之后表里再出现同一个证件号或号码布,系统匹配不到旧档案,会把他当作**新人员**重新建档——名单里多出一条「以前删过的人」通常就是这么来的。新档案是全新的,他以前的报名、成绩与积分不会回来,需要重新录。
5. **被「彻底删除」的单位则相反,导入时会「复活」**:管理员彻底删除某个单位,只是把它从单位列表里移除,它的报名与成绩一条都没删;表里再出现这个 `team_code`,系统按编码复用原来那条单位记录,清掉删除标记并让它重新出现在列表里,**原有的报名、成绩与积分都还在**,也不会新建第二个同名单位。
---
## 八、给填写人的最短提示(一页版)
> 1. 用系统里下载的模板,**第一行不要动**;
> 2. **一个人一行**;一个人报几项就写几行;
> 3. `event_code`(小项)、`team_code`(单位)、`category`(组别)照抄系统里配好的,不自己编;
> 4. 每人要有**证件号或号码布**,至少一个;
> 5. 团体项目:姓名列留空,队员写进 `member_names`,多人用 `/` 隔开;
> 6. 拿不准的列**留空**,不要写「无」「/」「—」这类符号;
> 7. 表里**不要合并单元格**、不要加标题行、不要插图;
> 8. 填完先对着第五节的清单自查一遍,再发给收表人。