fix(import): 导入提示改成「能照着改」的话 + 模板给「姓名」「队员名单」加说明;版本 dev_0.5.9

## 起因(主人原话)
> 我应该如何用表格导入团体报名? 我现在不管怎么填的话,它都提示:
> 「第2行填写了队伍 但是这一行有运动员姓名 按个人报名处理」。但实际上我们程序里,团体报名是可以包含运动员姓名的。

## 核实结论:不是功能 bug,是提示没用
系统用「**`姓名` 列有没有填**」区分个人 / 团体报名(数据模型决定的:一条报名要么属于某个人、要么属于一支队)。
**团体报名的队员要填在「队员名单」列**(写号码布),`姓名` 列留空;队员就是这条报名的成员。
原来那条提示**只说了后果、没说动作**,所以现场只能反复换填法重试。

## 改了什么
1. **两条行级提示改成「后果 + 两步动作」**(保留旧文案里的关键词,既有断言一字未改仍通过):
   - 队伍那条:`第 N 行填了队伍「一队」,但这一行也填了运动员姓名,系统按个人报名处理:队伍只对团体报名生效,这一格没有写进库里。要报团体(一支队)请把姓名列留空、把队员的号码布填到「队员名单」列;这一行确实是个人报名,把「队伍」这一格删掉即可`
   - 队员名单那条:同口径(`…队员只对团体报名生效,这一格只作文字名单保留、没有关联到人。要报团体请把姓名列留空、队员的号码布就填在「队员名单」列…`)
2. **导入模板加说明**:原来只有「单位编码」表头挂了批注;现在给 **`姓名`**(填了就是个人报名、团体请留空)与 **`队员名单`**(团体报名的队员写这里、**写号码布**才会关联到人)两个表头**各加一条批注 + 输入提示**,中英文两套。
3. ⚠️ **模板的表头文字、列序、13 列、工作表命名一个字没动**(动了模板就导不回去,有测试逐字钉着)。

## 文档
`docs/报名名单导入规范.md`(新增「个人报名 vs 团体报名」对照表 + 两条提示原文收进常见报错 + 队员名单分隔符说明)、
`docs/使用手册.md`(在工作台导入那节点明判据)、`docs/升级说明.md`(新增 `dev_0.5.9` 一节)。

## 验证
- **先红后绿**;全量自检两轮**全绿、`EXIT=0`**:服务端 14 包 / 前端 **944 例** 0 fail / Console 68 例 0 fail。

## 版本
`dev_0.5.8` → **`dev_0.5.9`**(**无数据迁移**);安装器素材同步重生成(照例只动 `brand.bmp` 两张)。
This commit is contained in:
AskaEth committed 2026-09-14 23:40:31 +08:00
1 parent c5ce1878fc
commit 55051fd633
12 files changed
+364 -76

No files matched your search

+10 -3
View File
@@ -1445,9 +1445,13 @@ func (s *server) importEntriesWith(ctx context.Context, sheets []importSheet, de
// 队伍只对**纯团体报名**(这一行没填姓名)生效。个人报名按 (组别, 单位, 运动员) 唯一,
// squad 不参与那条判断——填了就写不进库,所以要说一句,不能悄悄丢掉这一格。
// 说清「改哪一格」比说清后果更要紧:现场看到「没写进库里」只会换个填法再试一遍
// (「团体报名到底能不能写姓名」就是这么来的)。
if athleteID != nil && squad != "" {
result.Notices = append(result.Notices, prefix+fmt.Sprintf(
"第 %d 行填了队伍「%s」,但这一行有运动员姓名、按个人报名处理:队伍只对团体报名(不填姓名那几行)生效,这一格没有写进库里",
"第 %d 行填了队伍「%s」,但这一行也填了运动员姓名,系统按个人报名处理:队伍只对团体报名生效,"+
"这一格没有写进库里。要报团体(一支队)请把姓名列留空、把队员的号码布填到「队员名单」列;"+
"这一行确实是个人报名,把「队伍」这一格删掉即可",
line, squad))
squad = ""
}
@@ -1470,10 +1474,13 @@ func (s *server) importEntriesWith(ctx context.Context, sheets []importSheet, de
// 队员只对**纯团体报名**生效:带运动员姓名的行按个人报名处理,那种报名自己就是那个人的
// 身份(见 PUT /api/entries/{id}/athletes 的同一条口径),所以这一格只留文字、不关联。
// 认得出人时才说一句——这一格在个人行上本来就只是备注,空着或只写姓名都不必打扰现场。
// 与上面队伍那条同一口径:给出「团体怎么填 / 个人怎么填」两条动作,不停在「这一格没关联」。
if athleteID != nil && len(link.Members) > 0 {
result.Notices = append(result.Notices, prefix+fmt.Sprintf(
"第 %d 行的队员名单里有 %d 个号码布认得出人,但这一行有运动员姓名、按个人报名处理:"+
"队员只对团体报名(不填姓名那几行)生效,这一格只作文字名单保留,没有关联到人",
"第 %d 行的队员名单里有 %d 个号码布认得出人,但这一行也填了运动员姓名,系统按个人报名处理:"+
"队员只对团体报名生效,这一格只作文字名单保留、没有关联到人。"+
"要报团体请把姓名列留空、队员的号码布就填在「队员名单」列;这一行确实是个人报名,"+
"把「队员名单」这一格删掉即可",
line, len(link.Members)))
link.Members = nil
}