修复住宿:统一住宿生命周期状态与状态变更入口

住宿状态在数据库与业务上只有 0 预分配 / 1 已入住 / 2 已退宿三种,不存在 3 已调房。
调房是业务操作,真实语义为旧记录置退宿、新记录置在住,不应表现为独立状态值。

- 前端住宿页面移除筛选项与两处状态标签中的"已调房"(status=3),补齐历史表格缺失的预分配标签
- 后端通用 PUT /train/accommodation 封死 accommodation_status 写入路径,
  状态变更只能经办理入住、办理退宿、调房等专属业务操作
- 新增住宿状态白名单守卫,非法整数与新增即退宿一律拒绝入库

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
wangzhibiaoandCursor committed 2026-09-22 17:03:43 +08:00
1 parent ecfd38be17
commit aeb76ce35a
3 files changed
+76 -3

No files matched your search

@@ -186,6 +186,12 @@ public class TrainAccommodationServiceImpl implements ITrainAccommodationService
{
accommodation.setAccommodationStatus(TrainAccommodation.STATUS_PREALLOCATED);
}
// FIX-04: 住宿生命周期状态白名单守卫,杜绝非法整数与伪造状态落库
assertStatusInLifecycleWhitelist(accommodation.getAccommodationStatus());
if (TrainAccommodation.STATUS_CHECKED_OUT.equals(accommodation.getAccommodationStatus()))
{
throw new ServiceException("新增住宿安排不能直接置为已退宿状态,退宿请使用专属退宿业务操作");
}
int rows = trainAccommodationMapper.insertTrainAccommodation(accommodation);
if (rows > 0 && accessAuthorizationOrchestrator != null)
{
@@ -223,6 +229,16 @@ public class TrainAccommodationServiceImpl implements ITrainAccommodationService
throw new ServiceException("变更宿舍请使用专属调房换房流程 (transfer),禁止直接修改住宿记录的宿舍ID");
}
// FIX-04: 住宿生命周期状态只能通过办理入住/办理退宿/调房等专属业务操作改变,
// 通用 update 一律不得写 accommodation_status,避免绕过状态机与门禁撤权联动。
if (accommodation.getAccommodationStatus() != null
&& !accommodation.getAccommodationStatus().equals(exist.getAccommodationStatus()))
{
throw new ServiceException("变更住宿状态请使用办理入住、办理退宿或调房等专属业务操作,禁止直接修改住宿记录状态");
}
// 与现状一致时同样不下发该字段,杜绝通用更新路径触碰生命周期状态列
accommodation.setAccommodationStatus(null);
String targetDormId = accommodation.getDormId() != null ? accommodation.getDormId() : exist.getDormId();
String targetBedNo = accommodation.getBedNo() != null ? accommodation.getBedNo() : exist.getBedNo();
@@ -403,6 +419,24 @@ public class TrainAccommodationServiceImpl implements ITrainAccommodationService
return trainAccommodationMapper.deleteTrainAccommodationByAccommodationId(accommodationId);
}
/**
* FIX-04: 住宿生命周期状态白名单校验。
* <p>
* 数据库与业务只承认三个状态:0 预分配、1 已入住、2 已退宿。
* 不存在"已调房"等第四种状态,调房是业务操作而非住宿状态。
*
* @param status 待校验的住宿状态
*/
private void assertStatusInLifecycleWhitelist(Integer status)
{
if (!TrainAccommodation.STATUS_PREALLOCATED.equals(status)
&& !TrainAccommodation.STATUS_CHECKED_IN.equals(status)
&& !TrainAccommodation.STATUS_CHECKED_OUT.equals(status))
{
throw new ServiceException("住宿状态不合法,仅支持预分配、已入住、已退宿三种状态");
}
}
/**
* Model C: 校验床位在开班计划时间窗口内的占用情况 (Fail Closed)
*