调整Rooming List导出日期格式
This commit is contained in:
@@ -485,7 +485,7 @@ RESERVATION_ROOMING_LIST_GENERATE
|
||||
- 姓名支持 `LI/CHUNHONG` 和 `LI CHUNHONG` 两类格式;后端会把第一段写入目标 `Name`,剩余部分写入目标 `First Name`。
|
||||
- 前端必须让用户输入 `people_per_room` 和目标 `Room Type`,后端按名单顺序分组,并用 `ceil(total_people / people_per_room)` 生成房间行。
|
||||
- 每组第一位旅客写入 `Name` / `First Name`,同组剩余旅客写入 `Accompanying Guests`,多人用英文逗号分隔。
|
||||
- CP2 起前端不再展示或提交 `Arrival` / `Departure`,后端从来源 Excel 的 `旅游日期` 派生。样例 `2026年5月9日-5月14日` 应生成 `Arrival=2026-05-09`、`Departure=2026-05-14`。
|
||||
- CP2 起前端不再展示或提交 `Arrival` / `Departure`,后端从来源 Excel 的 `旅游日期` 派生。样例 `2026年5月9日-5月14日` 应生成 `Arrival=2026/05/09`、`Departure=2026/05/14`。
|
||||
- CP2 起前端不再展示或提交 `Adults`,后端按每个分房行的实际人数派生;尾房不足满房人数时按尾房实际人数写入。`Children` 第一版固定为 `0`。
|
||||
- CP2 起目标默认值区域只保留 `Payment Type` 和 `Nationality`:`Payment Type` 默认 `BTQR`,当前允许 `BTQR`、`CA`;`Nationality` 只允许 `KR`、`CHN`。
|
||||
- CP2 页面不再展示或提交 `Title`、`Rate Code`、`VIP`、`Email`、`ID Type`、`ID Number`,这些目标列第一版固定为空。
|
||||
|
||||
@@ -1221,7 +1221,7 @@ id_number
|
||||
|
||||
后端派生规则:
|
||||
|
||||
- 从来源 Excel `旅游日期` 列解析入住和离店日期;样例 `2026年5月9日-5月14日` 派生 `Arrival=2026-05-09`、`Departure=2026-05-14`。
|
||||
- 从来源 Excel `旅游日期` 列解析入住和离店日期;样例 `2026年5月9日-5月14日` 派生 `Arrival=2026/05/09`、`Departure=2026/05/14`。
|
||||
- `Adults` 按当前分房行实际人数计算;尾房人数不足时按实际人数写入。
|
||||
- `Children` 固定为 `0`。
|
||||
- `Title`、`Rate Code`、`VIP`、`Email`、`ID Type`、`ID Number` 第一版固定为空。
|
||||
|
||||
@@ -110,15 +110,15 @@ CP2 起后端应识别来源 Excel 的 `旅游日期` 表头,并从有名单
|
||||
- 右侧结束日期可以省略年份;省略年份时继承起始日期年份。
|
||||
- 右侧结束日期可以省略月份;省略月份时继承起始日期月份。
|
||||
- 支持常见区间分隔符,例如 `-`、`-`、`–`、`—`、`至`、`到`。
|
||||
- `Arrival` 使用起始日期,格式化为 `yyyy-MM-dd`。
|
||||
- `Departure` 使用结束日期,格式化为 `yyyy-MM-dd`。
|
||||
- `Arrival` 使用起始日期,格式化为 `yyyy/MM/dd`。
|
||||
- `Departure` 使用结束日期,格式化为 `yyyy/MM/dd`。
|
||||
- 旅游日期属于酒店本地业务日期,不按 UTC 时间点换算。
|
||||
- 同一来源文件中参与生成的名单行必须解析出同一个旅游日期区间;如果出现多个不同区间,CP2 先返回受控错误,不自动混合生成。
|
||||
- 如果缺少 `旅游日期` 表头、旅游日期为空、格式无法解析或结束日期不晚于起始日期,返回 `ROOMING_LIST_SOURCE_FILE_INVALID` 或统一受控业务错误,不应生成半成品 Excel。
|
||||
|
||||
中文说明:
|
||||
|
||||
- 当前样表第 2 行表头中包含 `旅游日期`,第 3 行起数据为 `2026年5月9日-5月14日`,可派生 `Arrival=2026-05-09`、`Departure=2026-05-14`。
|
||||
- 当前样表第 2 行表头中包含 `旅游日期`,第 3 行起数据为 `2026年5月9日-5月14日`,可派生 `Arrival=2026/05/09`、`Departure=2026/05/14`。
|
||||
- CP2 暂不支持同一个文件中多批次、多旅游日期混合分房;如后续出现该真实场景,需要先设计按批次拆文件或按旅游日期分组生成多个 Sheet / 多个文件。
|
||||
|
||||
## 5. 分房生成规则
|
||||
@@ -328,7 +328,7 @@ RESERVATION_ROOMING_LIST_GENERATE
|
||||
后端测试建议覆盖:
|
||||
|
||||
- 能识别 `护照全名` 表头。
|
||||
- 能识别 `旅游日期` 表头,并把 `2026年5月9日-5月14日` 解析为 `Arrival=2026-05-09`、`Departure=2026-05-14`。
|
||||
- 能识别 `旅游日期` 表头,并把 `2026年5月9日-5月14日` 解析为 `Arrival=2026/05/09`、`Departure=2026/05/14`。
|
||||
- `LI/CHUNHONG` 能拆成 `Name=LI`、`First Name=CHUNHONG`。
|
||||
- `LI CHUNHONG` 能拆成 `Name=LI`、`First Name=CHUNHONG`。
|
||||
- 姓名首尾空格会 trim。
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
public class RoomingListExcelRenderer {
|
||||
|
||||
private static final DateTimeFormatter TARGET_DATE_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy/MM/dd");
|
||||
private static final String[] HEADERS = {
|
||||
"Line",
|
||||
"Name",
|
||||
@@ -98,8 +100,8 @@ public class RoomingListExcelRenderer {
|
||||
row.createCell(1).setCellValue(room.primaryGuest().lastName());
|
||||
row.createCell(2).setCellValue(room.primaryGuest().firstName());
|
||||
row.createCell(3).setCellValue("");
|
||||
row.createCell(4).setCellValue(arrival.format(DateTimeFormatter.ISO_LOCAL_DATE));
|
||||
row.createCell(5).setCellValue(departure.format(DateTimeFormatter.ISO_LOCAL_DATE));
|
||||
row.createCell(4).setCellValue(arrival.format(TARGET_DATE_FORMATTER));
|
||||
row.createCell(5).setCellValue(departure.format(TARGET_DATE_FORMATTER));
|
||||
row.createCell(6).setCellValue(defaultString(request.roomType()));
|
||||
row.createCell(7).setCellValue("");
|
||||
row.createCell(8).setCellValue(1);
|
||||
|
||||
@@ -101,9 +101,9 @@ class ReservationRoomingListGenerationControllerTest {
|
||||
.andExpect(result -> assertThat(workbookCell(result.getResponse().getContentAsByteArray(), 1, 3))
|
||||
.isEmpty())
|
||||
.andExpect(result -> assertThat(workbookCell(result.getResponse().getContentAsByteArray(), 1, 4))
|
||||
.isEqualTo("2026-05-09"))
|
||||
.isEqualTo("2026/05/09"))
|
||||
.andExpect(result -> assertThat(workbookCell(result.getResponse().getContentAsByteArray(), 1, 5))
|
||||
.isEqualTo("2026-05-14"))
|
||||
.isEqualTo("2026/05/14"))
|
||||
.andExpect(result -> assertThat(workbookCell(result.getResponse().getContentAsByteArray(), 1, 9))
|
||||
.isEqualTo("2"))
|
||||
.andExpect(result -> assertThat(workbookCell(result.getResponse().getContentAsByteArray(), 1, 10))
|
||||
|
||||
@@ -60,8 +60,8 @@ class ReservationRoomingListGenerationServiceImplTest {
|
||||
assertThat(cellText(firstRoom.getCell(1))).isEqualTo("LI");
|
||||
assertThat(cellText(firstRoom.getCell(2))).isEqualTo("CHUNHONG");
|
||||
assertThat(cellText(firstRoom.getCell(3))).isEmpty();
|
||||
assertThat(cellText(firstRoom.getCell(4))).isEqualTo("2026-05-09");
|
||||
assertThat(cellText(firstRoom.getCell(5))).isEqualTo("2026-05-14");
|
||||
assertThat(cellText(firstRoom.getCell(4))).isEqualTo("2026/05/09");
|
||||
assertThat(cellText(firstRoom.getCell(5))).isEqualTo("2026/05/14");
|
||||
assertThat(cellText(firstRoom.getCell(6))).isEqualTo("UG1");
|
||||
assertThat(cellText(firstRoom.getCell(7))).isEmpty();
|
||||
assertThat(cellText(firstRoom.getCell(8))).isEqualTo("1");
|
||||
@@ -119,8 +119,8 @@ class ReservationRoomingListGenerationServiceImplTest {
|
||||
|
||||
try (XSSFWorkbook workbook = new XSSFWorkbook(new ByteArrayInputStream(generatedFile.content()))) {
|
||||
Row firstRoom = workbook.getSheetAt(0).getRow(1);
|
||||
assertThat(cellText(firstRoom.getCell(4))).isEqualTo("2026-05-09");
|
||||
assertThat(cellText(firstRoom.getCell(5))).isEqualTo("2026-05-14");
|
||||
assertThat(cellText(firstRoom.getCell(4))).isEqualTo("2026/05/09");
|
||||
assertThat(cellText(firstRoom.getCell(5))).isEqualTo("2026/05/14");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user