兼容第三种房表名单格式

This commit is contained in:
andy
2026-08-04 09:41:16 +08:00
parent 99fb919158
commit d56587a078
18 changed files with 427 additions and 98 deletions

View File

@@ -2,9 +2,9 @@
| 项目 | 内容 |
| --- | --- |
| 文档状态 | 后端 CP1 已实现CP2 已实现旅游日期派生、Adults 系统计算和目标默认值字段收口CP3 第二种来源名单样式已实现 |
| 文档状态 | 后端 CP1 已实现CP2 已实现旅游日期派生、Adults 系统计算和目标默认值字段收口CP3 第二种来源名单样式已实现CP4 第三种单列 `英文名` 来源名单样式已实现 |
| 适用范围 | 从旅行团名单 Excel 解析护照姓名,并生成酒店 / PMS 可导入的 Rooming List Excel |
| 当前目标 | CP1 已支持前端上传来源 Excel、填写分房和目标列默认值后端同步生成目标格式 `.xlsx`CP2 收口为后端从来源 `旅游日期` 派生入住 / 离店日期成人数由系统按分房结果计算目标默认值区域只保留付款方式和国籍CP3 兼容 `英文姓` + `英文名` 第二种名单样式,并由用户补充入住 / 离店日期 |
| 当前目标 | CP1 已支持前端上传来源 Excel、填写分房和目标列默认值后端同步生成目标格式 `.xlsx`CP2 收口为后端从来源 `旅游日期` 派生入住 / 离店日期成人数由系统按分房结果计算目标默认值区域只保留付款方式和国籍CP3 兼容 `英文姓` + `英文名` 第二种名单样式CP4 兼容单列 `英文名` 第三种名单样式;无旅游日期来源样式均由用户补充入住 / 离店日期 |
| 依赖能力 | 登录权限、酒店上下文、Apache POI 或等价 Excel 读写能力 |
## 1. 背景
@@ -34,7 +34,7 @@ CP2 目标调整:
- 后端按分房结果计算目标 `Adults`,不再要求前端手填 `adults`
- 目标默认值区域只保留 `Payment Type``Nationality` 两个字段。
- `Payment Type` 前端默认为 `BTQR`,当前可选择 `BTQR``CA`,后端按同一枚举校验。
- `Nationality` 前端只允许选择 `KR``CHN`,后端按同一枚举校验。
- `Nationality` 前端只允许选择 `KR``CN``TH``MM``RS``TW`,后端按同一枚举校验。
CP3 目标调整:
@@ -45,6 +45,14 @@ CP3 目标调整:
- 第二种样式中的领队也进入房表。例如标题行 `23+1` 应理解为 23 位客人 + 1 位领队,合计 24 位入住人全部参与分房。
- 来源表中的中文名、性别、出生日期、护照号码、出生地、签发地、签发日期、有效期等字段第一版仍不写入目标 Excel也不在错误响应或日志中回显。
CP4 目标调整:
- 兼容第三种来源名单样式:表头包含单列 `英文名`,没有 `护照全名``旅游日期``英文姓`
- 第三种样式的 `英文名` 单元格值形如 `CAI/HAIYUN``HUANG/YUHENG`,后端复用 `/` 或空格拆分规则,第一段写入目标 `Name`,剩余部分写入目标 `First Name`
- 第三种样式没有可靠入住 / 离店日期来源,后端不得从标题中的航班日期猜测酒店入住 / 离店日期;必须使用前端提交的 `arrival` / `departure`
- 第三种样式中的 `领队` 行也进入房表。例如 `19+1` 应理解为 19 位客人 + 1 位领队,合计 20 位入住人全部参与分房。
- 来源表中的中文名、性别、证件号、生日、出生地、签发地、联系方式、小费、住宿说明等字段第一版仍不写入目标 Excel也不在错误响应或日志中回显。
第一阶段不做:
- 不落业务订单。
@@ -70,7 +78,7 @@ CP3 目标调整:
## 4. 来源 Excel 解析规则
基于当前样例来源文件,当前支持类来源名单格式。后端应先在来源 Excel 中识别表头组合,再选择对应解析器。
基于当前样例来源文件,当前支持类来源名单格式。后端应先在来源 Excel 中识别表头组合,再选择对应解析器。
### 4.1 第一种来源样式:护照全名 + 旅游日期
@@ -164,6 +172,33 @@ Sheet1 第 3 行起:名单数据
如果来源文件同时满足第一种和第二种表头组合,后端应优先使用第一种 `护照全名` + `旅游日期`,保持现有已上线行为稳定。
### 4.3 第三种来源样式:单列英文名
第三种样式识别以下表头:
| 来源字段 | 中文说明 | 第一版用途 |
| --- | --- | --- |
| `英文名` | 护照英文姓名,通常为 `姓/名` 格式 | 拆分后写入目标 Excel 的 `Name``First Name` |
已确认样例:
```text
Sheet1 第 1 行:客人名单表,包含航班说明,例如 0803 杭州-曼谷 / 0808 曼谷-杭州
Sheet1 第 2 行:序号 / 姓名 / 性别 / 英文名 / 证件号 / 出生年月日 / ...
Sheet1 第 3 行:领队
Sheet1 第 4 行起:名单数据
```
解析规则:
- 在前若干行内查找 `英文名` 表头;当同一文件不存在 `英文姓` 表头时,按第三种样式处理。
- 从表头下一行开始读取名单行。
- `英文名` 为空的行跳过;如后续需要强校验可单独确认。
- `英文名` 值继续复用第一种样式的姓名拆分规则,支持 `CAI/HAIYUN``CAI HAIYUN`
- 标记为 `领队` 的行也进入名单;实际入住人数以后端读取到的有效 `英文名` 行数为准。
- 不读取也不返回中文名、性别、证件号、生日、出生地、签发地、联系方式、小费、住宿说明。
- 第三种样式没有 `旅游日期`,后端不得从航班行猜测入住 / 离店日期;必须使用前端提交的 `arrival` / `departure`
## 5. 分房生成规则
前端需要传入 `people_per_room`,表示每间房人数。
@@ -205,8 +240,8 @@ people_per_room = 3
| `Name` | 每间房第一位旅客的姓氏 / 第一段姓名 |
| `First Name` | 每间房第一位旅客的名字 / 剩余姓名 |
| `Title` | CP2 固定为空,后续如有稳定默认值再单独确认 |
| `Arrival` | 第一种样式从来源 Excel `旅游日期` 起始日派生;第二种样式使用前端提交的 `arrival` |
| `Departure` | 第一种样式从来源 Excel `旅游日期` 结束日派生;第二种样式使用前端提交的 `departure` |
| `Arrival` | 第一种样式从来源 Excel `旅游日期` 起始日派生;无旅游日期来源样式使用前端提交的 `arrival` |
| `Departure` | 第一种样式从来源 Excel `旅游日期` 结束日派生;无旅游日期来源样式使用前端提交的 `departure` |
| `Room Type` | 前端传入 |
| `Rate Code` | CP2 固定为空,后续如需 Rate Code 选择再单独确认 |
| `Number of Rooms` | 第一版每行固定为 `1`,后续如目标模板变化再调整 |
@@ -215,7 +250,7 @@ people_per_room = 3
| `Payment Type` | 前端选择;当前默认值为 `BTQR`,允许 `BTQR``CA` |
| `VIP` | CP2 固定为空,后续如有稳定默认值再单独确认 |
| `Accompanying Guests` | 同房其他旅客姓名,英文逗号分隔 |
| `Nationality` | 前端选择;当前只允许 `KR``CHN` |
| `Nationality` | 前端选择;当前只允许 `KR``CN``TH``MM``RS``TW` |
| `Email` | CP2 固定为空,后续如有稳定来源再单独确认 |
| `ID Type` | CP2 固定为空,后续如有稳定来源再单独确认 |
| `ID Number` | CP2 固定为空,后续如有稳定来源再单独确认 |
@@ -223,7 +258,7 @@ people_per_room = 3
中文说明:
- CP2 目标默认值区域只保留 `Payment Type``Nationality`,不再展示 `Title``Rate Code``Adults``Children``VIP``Email``ID Type``ID Number`
- `Arrival` / `Departure` 的业务来源按来源样式区分:第一种样式来自来源名单 Excel 的 `旅游日期`第二种样式来自前端用户填写。
- `Arrival` / `Departure` 的业务来源按来源样式区分:第一种样式来自来源名单 Excel 的 `旅游日期`无旅游日期来源样式来自前端用户填写。
- `Adults` 的业务来源是系统分房结果,前端不再手填。
- 如后续需要恢复 Rate Code、Title、Email 或证件字段,需要单独确认字段来源、默认值和安全边界,不能直接把 CP1 自由文本框恢复为生产能力。
@@ -245,12 +280,12 @@ Content-Type: multipart/form-data
| `hotel_id` | string | 否 | 酒店 ID不传时按当前登录用户默认酒店或单酒店上下文解析 |
| `people_per_room` | integer | 是 | 每间房人数,必须大于 0 |
| `room_type` | string | 是 | 目标 Excel 的 `Room Type` |
| `arrival` | date string | 条件必填 | CP3 第二种来源样式必填,格式 `yyyy-MM-dd`;第一种来源样式可不传,后端优先从 `旅游日期` 派生 |
| `departure` | date string | 条件必填 | CP3 第二种来源样式必填,格式 `yyyy-MM-dd`;必须晚于 `arrival` |
| `arrival` | date string | 条件必填 | 无旅游日期来源样式必填,格式 `yyyy-MM-dd`;第一种来源样式可不传,后端优先从 `旅游日期` 派生 |
| `departure` | date string | 条件必填 | 无旅游日期来源样式必填,格式 `yyyy-MM-dd`;必须晚于 `arrival` |
| `payment_type` | string | 否 | CP2 目标 Excel 的 `Payment Type`;为空时后端按 `BTQR` 处理;当前只允许 `BTQR``CA` |
| `nationality` | string | 是 | CP2 目标 Excel 的 `Nationality`;当前只允许 `KR``CHN` |
| `nationality` | string | 是 | CP2 目标 Excel 的 `Nationality`;当前只允许 `KR``CN``TH``MM``RS``TW` |
CP3 后仍不接收以下前端业务字段作为生成依据:
CP3 / CP4 后仍不接收以下前端业务字段作为生成依据:
| 字段 | 处理口径 |
| --- | --- |
@@ -288,9 +323,9 @@ Content-Disposition: attachment; filename="rooming-list-<timestamp>.xlsx"
- multipart 必填字段缺失,返回 `ROOMING_LIST_VALIDATION_FAILED`
- `people_per_room` 数字格式错误,返回 `ROOMING_LIST_VALIDATION_FAILED`
- `payment_type``BTQR` / `CA`,返回 `ROOMING_LIST_VALIDATION_FAILED`
- `nationality``KR` / `CHN`,返回 `ROOMING_LIST_VALIDATION_FAILED`
- 第二种来源样式缺少 `arrival` / `departure`、日期格式错误或 `departure` 不晚于 `arrival`,返回 `ROOMING_LIST_VALIDATION_FAILED`
- 来源文件缺失、非 `.xls` / `.xlsx`、无法读取、既不能识别第一种 `护照全名` + `旅游日期`,也不能识别第二种 `英文姓` + `英文名`,返回 `ROOMING_LIST_SOURCE_FILE_INVALID`
- `nationality``KR` / `CN` / `TH` / `MM` / `RS` / `TW`,返回 `ROOMING_LIST_VALIDATION_FAILED`
- 无旅游日期来源样式缺少 `arrival` / `departure`、日期格式错误或 `departure` 不晚于 `arrival`,返回 `ROOMING_LIST_VALIDATION_FAILED`
- 来源文件缺失、非 `.xls` / `.xlsx`、无法读取、既不能识别第一种 `护照全名` + `旅游日期`,也不能识别第二种 `英文姓` + `英文名` 或第三种单列 `英文名`,返回 `ROOMING_LIST_SOURCE_FILE_INVALID`
- 第一种来源样式缺少 / 无法解析 `旅游日期` 表头,返回 `ROOMING_LIST_SOURCE_FILE_INVALID`
- 同一来源文件出现多个不同旅游日期区间CP2 返回受控错误,不生成 Excel。
- 错误详情只返回字段名或行号,不回显完整名单、证件号或源文件内容。
@@ -324,7 +359,7 @@ RESERVATION_ROOMING_LIST_GENERATE
| `ReservationRoomingListGenerationController` | 接收前端上传请求,做登录、权限和酒店上下文校验 |
| `ReservationRoomingListGenerationService` | 编排来源解析、分房、目标 Excel 渲染和文件响应 |
| `ReservationRoomingListGenerationServiceImpl` | 具体业务实现 |
| `RoomingListSourceExcelParser` | CP1 解析来源 Excel提取护照姓名CP2 已扩展为同时提取并校验统一旅游日期区间CP3 已扩展第二种 `英文姓` + `英文名` 样式,并在该样式下要求用户补日期 |
| `RoomingListSourceExcelParser` | CP1 解析来源 Excel提取护照姓名CP2 已扩展为同时提取并校验统一旅游日期区间CP3 已扩展第二种 `英文姓` + `英文名` 样式CP4 已扩展第三种单列 `英文名` 样式;无旅游日期来源样式要求用户补日期 |
| `RoomingListNameParser` | 拆分 `/` 或空格格式的护照姓名 |
| `RoomingListGroupingService` | 按每房人数分组并生成目标行模型 |
| `RoomingListExcelRenderer` | 生成目标格式 `.xlsx` |
@@ -353,10 +388,10 @@ RESERVATION_ROOMING_LIST_GENERATE
- 上传来源 Excel。
- 填写每间房人数。
- 填写目标 `Room Type`
- 填写 `Arrival` / `Departure`:第一种来源样式可不填,第二种来源样式必填;页面文案需说明“当来源名单没有旅游日期时请填写”。
- 填写 `Arrival` / `Departure`:第一种来源样式可不填,无旅游日期来源样式必填;页面文案需说明“当来源名单没有旅游日期时请填写”。
- 目标默认值区域只保留 `Payment Type``Nationality`
- `Payment Type` 使用下拉选择,默认 `BTQR`,当前可选择 `BTQR``CA`
- `Nationality` 使用下拉,只允许 `KR``CHN`
- `Nationality` 使用下拉,只允许 `KR``CN``TH``MM``RS``TW`
- 展示解析预览:总人数、预计房间数、前几行分房结果。
- 点击生成后下载 `.xlsx`
@@ -364,8 +399,8 @@ RESERVATION_ROOMING_LIST_GENERATE
- 不要在前端解析完整 Excel 作为权威结果;前端预览可以后置为后端 preview 接口。
- 第一版如果没有 preview 接口,可以只展示用户输入和文件名,生成成功后直接下载。
- CP3 页面重新展示并提交可选 `arrival``departure`;后端仅在第二种来源样式下要求这两个字段必填,第一种来源样式仍优先使用来源 Excel `旅游日期`
- 前端不要自行读取 Excel 并推导日期;如果用户上传第二种没有 `旅游日期` 的名单,由用户手工填写 `arrival` / `departure`
- CP3 页面重新展示并提交可选 `arrival``departure`;后端仅在无旅游日期来源样式下要求这两个字段必填,第一种来源样式仍优先使用来源 Excel `旅游日期`
- 前端不要自行读取 Excel 并推导日期;如果用户上传没有 `旅游日期` 的名单,由用户手工填写 `arrival` / `departure`
- `Adults` 由后端按分房结果计算;前端不要让用户覆盖。
- 不要把上传文件内容、客人名单或生成文件内容写进浏览器日志、错误上报、URL 或 localStorage。
@@ -379,7 +414,11 @@ RESERVATION_ROOMING_LIST_GENERATE
- 第二种来源样式下 `英文姓=FENG``英文名=MINNA` 能生成 `Name=FENG``First Name=MINNA`
- 第二种来源样式下领队也进入房表,实际人数以后端读取到的有效名单行数为准。
- 第二种来源样式使用前端提交的 `arrival` / `departure` 写入目标 Excel。
- 第二种来源样式缺少 `arrival` / `departure` 返回受控错误
- 能识别第三种来源样式单列 `英文名` 表头
- 第三种来源样式下 `英文名=CAI/HAIYUN` 能生成 `Name=CAI``First Name=HAIYUN`
- 第三种来源样式下领队也进入房表,实际人数以后端读取到的有效名单行数为准。
- 无旅游日期来源样式使用前端提交的 `arrival` / `departure` 写入目标 Excel。
- 无旅游日期来源样式缺少 `arrival` / `departure` 返回受控错误。
- `LI/CHUNHONG` 能拆成 `Name=LI``First Name=CHUNHONG`
- `LI CHUNHONG` 能拆成 `Name=LI``First Name=CHUNHONG`
- 姓名首尾空格会 trim。
@@ -388,7 +427,7 @@ RESERVATION_ROOMING_LIST_GENERATE
- `Adults` 按每个房间实际人数派生,尾房人数不足时不使用满房人数。
- `Children` 默认为 `0`
- `payment_type` 为空时按 `BTQR` 处理,非 `BTQR` / `CA` 返回受控错误。
- `nationality=KR` / `CHN` 成功,其他值返回受控错误。
- `nationality=KR` / `CN` / `TH` / `MM` / `RS` / `TW` 成功,其他值返回受控错误。
- 来源文件中多个不同旅游日期区间返回受控错误。
- 缺少 `旅游日期` 表头或旅游日期格式无法解析时返回受控错误。
- `Accompanying Guests` 使用英文逗号拼接。