feat(export): 新增「导入格式」报名名单导出(汇总 / 分小项),与导入模板逐列一致、可直接重导;版本 dev_0.5.14

主人反馈:导出页「报名名单」卡片导出的表格与导入模板对不上,没法拿回去重导。

核对属实(旧导出是给人看的报表):列序与 importColumns 不同、缺「种子 / 队员名单 / 队伍」
三列、姓名列被拼成「姓名(号码布)」、队名拼在单位名后面、状态列写「已退赛」而
normalizeImportStatus 不认 —— 整体错位 + 状态列报错,重导必炸。

- 新增 GET /api/export/entries-import.xlsx?format=flat|sheets&lang=zh|en&<四个筛选>
  · flat(缺省)= 所有行汇总在一张工作表;表名不含 @(导入侧见到带 @ 的表名就整份按
    分工作表模板解析,汇总表定不出「小项@组别」会被拒)
  · sheets = 按「小项@组别」分工作表,表名走 entrySheetName(与模板、导入解析三处同源,
    31 字符截断与撞名去重都跟着一起走)
  · 权限与筛选参数与 /api/export/entries.xlsx 完全一致(访客可读);空结果回 200 + 只有表头
  · 列序不写常量:按 importColumns 逐列取值,表头走 entryTemplateHeader(lang),
    加列/改序时测试当场红(TestEntryImportRecordCoversImportColumns)
- 取值口径(重导的关键):团体行的「姓名」留空、队员写「队员名单」列(中文逗号分隔;
  以库里 token 为底,再补上 entry_athletes 里后加的队员号码布,只增不减)、
  队伍独立成列、状态写导入器认的四个中文说法(正常参赛/退赛/取消资格/未出场)、
  联队照旧写 A+B、未分批留空、证件号/性别/年龄来自运动员档案(团体行空)
- 新增 internal/export/entries_import.go(读库层,与报表导出同源 SQL、取值口径刻意不同)
  与 internal/httpapi/entries_import_export.go(工作簿组装 + handler)
- 原有报表导出(/api/export/entries.xlsx 与 internal/export/entries.go)一个字没动;
  导出页新增两个按钮与中文/英文表头选择(只影响这两个导入格式)

测试:往返测试把导出件直接喂回报名导入解析器,flat 与 sheets 各断言四条(解析零错误 /
行数与 13 列逐项一致 / 团体行没被变成个人报名 / 两种格式都跑);另做变异验证 —— 状态改回
「已退赛」、给团体行填姓名,守卫当场变红。四套自检 ×2 全绿(finalcheck_0514.log:
前端 996 例 / Console 96 例 / 服务端 16 包,EXIT=0)。

无数据库迁移;已有成绩、名次与榜单不变。
文档:使用手册 §11.1(并订正「不要拿导出件重导」的旧话)、报名名单导入规范 新增第九节、
升级说明 新增 dev_0.5.14、常见问题 新增 Q104。
This commit is contained in:
AskaEth committed 2026-09-17 04:03:52 +08:00
1 parent 19e5630b39
commit ee19b92182
19 files changed
+2135 -34

No files matched your search

+41
View File
@@ -6,6 +6,47 @@
---
## dev_0.5.14
### 一句话
**没有任何数据迁移:不改一行已有数据、不用重配、不用重新导入规则包**,已有的成绩、名次、积分、榜单与**所有导出件一个字不变**。这一版**新增两种导出**:在「导出 → 结果 → 报名名单」卡片里,多了一组**与报名导入模板逐列一致**的导出(原来的报表导出**原样保留、一个字没改**)——以后**导出的报名名单可以直接拿回去重新导入**了。
### ① 新增:「报名名单」卡片里的两种「导入格式」导出
**先说清楚为什么要有它**:原来那张「导出报名名单」是**给人看的报表**(含小项名称、单位名称与备注,姓名列拼了队员、状态写「已退赛」),**导入器不认它**——想「导出 → 改几笔 → 再导回去」这条路以前走不通。现在同一个卡片里加了两个按钮:
| 按钮 | 导出来是什么 | 文件名 |
| --- | --- | --- |
| 导出(汇总·导入格式) | 13 列,所有行**汇总在一张工作表** | `报名名单(导入格式).xlsx` |
| 导出(分小项·导入格式) | 13 列,按「小项名称@组别名称」**分成多张工作表** | `报名名单(导入格式·分小项).xlsx` |
- **13 列与报名导入模板逐列一致**(顺序也是这个):项目编码 / 组别 / 单位编码 / 姓名 / 证件号 / 性别 / 年龄 / 号码布 / 种子 / 批次 / 状态 / 队员名单 / 队伍;**导出的文件可以直接拿去重新导入**(往返已测,见下);
- 卡片上的四个筛选(小项 / 组别 / 单位 / 批次)与原来的报表**完全一样**,都可留空;权限也一样,**访客也能导**;
- 新加的**表头语言**(中文表头 / 英文表头)**只影响这两个导出**,对上面的报表**不生效**;
- **原来的「导出报名名单」一个字没改**,照旧是给人看的报表(含小项名称、单位名称与备注)。
**往返测过**:把这两种导出件**直接喂回导入接口**各跑一遍——**零错误、行数一致、13 列逐项一致、团体行没有被变成个人报名**(队员关联还在)。真实服务端上也手工验过一遍:导出的文件回灌 `POST /api/entries/import` 得到 `created:0, updated:2, errors:[]`。
⚠️ 但它**不是"完全无损"的往返**,有三条限制请一并交代给用的人(详见《报名名单导入规范》第九节):
1. **号码布可能撞冲突面板**:导出件写的是**报名上登记的号码布**,导入时对个人行会拿它跟**运动员档案里的号码布**比;两边都非空且不同,这一行就进「冲突」列表等人工决策(库里两者一致是常态,那时是静默幂等);
2. **联队的份额权重带不回来**:13 列里联队只能写 `A+B`,重导回去是**均分(各 50%)**;原来是 `2:1` 这种非均分的会变均分,要保权重只能在编辑界面改;
3. **「队员名单」只增不减**:导出时会把界面上后来补进这支队的队员一并补上,所以重导后**队员不会少**,但个别名单的顺序 / 写法可能与最初那张表不完全一样。
### ② 升级后 5 分钟核对
1. 到「导出 → 结果」,在**报名名单**卡片点「**导出(汇总·导入格式)**」:打开下载到的 `报名名单(导入格式).xlsx`,确认**第一行就是 13 列表头**(项目编码 / 组别 / 单位编码 / 姓名 / 证件号 / 性别 / 年龄 / 号码布 / 种子 / 批次 / 状态 / 队员名单 / 队伍),并且**团体那一行的「姓名」列是空的**(队员在「队员名单」列);
2. 把刚导出的这个文件**直接**在同一页的「基础数据 → 报名导入」原样上传(**中间不要改任何一格**):应当是**零错误**,提示的「更新 N 条」里名单与上传前**看不出变化**;
3. 想要分小项的,回到卡片点「**导出(分小项·导入格式)**」:得到的文件里**每个「小项名称@组别名称」一张工作表**(没人报名的组合不会出空表),表名与导入模板同一套写法;
4. 其余照旧:**不需要数据迁移、不需要重新导入规则包、不需要重配任何东西**;原来的「导出报名名单」以及其它导出件**一个字不变**。
### ③ 详细说明在哪
《使用手册》§11.1 结果导出(xlsx);《报名名单导入规范》第九节「导出的名单怎么导回去」(含三条限制)。
---
## dev_0.5.13
### 一句话