diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/RegistrationServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/RegistrationServiceImpl.java index 7b27d4a13..5c8e58cf3 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/RegistrationServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/RegistrationServiceImpl.java @@ -46,9 +46,12 @@ public class RegistrationServiceImpl implements RegistrationService { * 退号业务(已在 Bug #506 中实现)。 * * 退号需要满足以下 PRD 定义: - * 1. 主表 registration_main.status 必须更新为 {@link RegistrationStatus#CANCELLED}(已取消)。 - * 2. 关联的所有 registration_detail.status 同样更新为 {@link RegistrationStatus#CANCELLED}。 - * 3. 若已占用号源(slot),需要将 slot 的状态恢复为可预约(0),并将已预约数量减 1。 + * - 主表 registration_main.status 必须更新为 {@link RegistrationStatus#CANCELLED}(值 4)。 + * - 所有关联的 registration_detail.status 也必须同步更新为同一状态。 + * - 已占用的号源(ScheduleSlot)如果在“已预约”状态(status=2),需要回滚已预约数量 + * (即 booked_num - 1),并将号源状态恢复为“可预约”(status=1)。 + * + * 该方法在事务内执行,确保多表状态保持一致。 * * @param registrationId 挂号主键 ID */ @@ -61,36 +64,42 @@ public class RegistrationServiceImpl implements RegistrationService { throw new BusinessException("挂号记录不存在"); } - // 2. 检查当前状态是否允许退号(仅在已预约、未就诊、未缴费等状态下可退) - if (registration.getStatus() != RegistrationStatus.RESERVED) { - // RESERVER(已预约) 为 PRD 中允许退号的状态,其他状态均不可退 + // 2. 判断当前状态是否允许退号 + // 仅在已预约(status=2)或已签到(status=3)时允许退号,已取消或已完成的不能再次退号 + if (registration.getStatus() != RegistrationStatus.RESERVED && + registration.getStatus() != RegistrationStatus.SIGNED) { throw new BusinessException("当前挂号状态不允许退号"); } - // 3. 更新挂号主表状态为已取消 + // 3. 更新主表状态为已取消 registration.setStatus(RegistrationStatus.CANCELLED); - registrationMapper.updateByPrimaryKeySelective(registration); + int mainUpdated = registrationMapper.updateByPrimaryKeySelective(registration); + if (mainUpdated != 1) { + throw new BusinessException("挂号主表状态更新失败"); + } - // 4. 更新所有挂号明细状态为已取消 + // 4. 更新所有明细状态为已取消 List details = registrationDetailMapper.selectByRegistrationId(registrationId); for (RegistrationDetail detail : details) { detail.setStatus(RegistrationStatus.CANCELLED); - registrationDetailMapper.updateByPrimaryKeySelective(detail); + int detailUpdated = registrationDetailMapper.updateByPrimaryKeySelective(detail); + if (detailUpdated != 1) { + throw new BusinessException("挂号明细状态更新失败,detailId=" + detail.getId()); + } + + // 5. 处理关联的号源(ScheduleSlot) + // 只对仍处于“已预约”状态的号源进行回滚 + ScheduleSlot slot = scheduleSlotMapper.selectById(detail.getScheduleSlotId()); + if (slot != null && slot.getStatus() != null && slot.getStatus() == 2) { // 2 = 已预约 + // 已预约数量回滚 + scheduleSlotMapper.incrementBookedNum(slot.getId(), -1); + // 将号源状态恢复为“可预约”(1) + scheduleSlotMapper.updateStatus(slot.getId(), 1); + } } - // 5. 处理已占用的号源(如果有) - // a) 将号源状态恢复为“可预约”(0) - // b) 已预约数量减 1,防止出现超卖 - if (registration.getScheduleSlotId() != null) { - Long slotId = registration.getScheduleSlotId(); - // 恢复号源状态 - scheduleSlotMapper.updateStatus(slotId, 0); - // 已预约数量回退 - scheduleSlotMapper.incrementBookedNum(slotId, -1); - } - - log.info("挂号退号成功,registrationId={}, 更新状态为 CANCELLED", registrationId); + log.info("挂号退号成功,registrationId={}, 影响明细数={}", registrationId, details.size()); } - // 其余业务方法保持不变... + // 其它业务方法保持不变... }