From cbad13bddc98c491e3039e5019175d4bd1a5d13d Mon Sep 17 00:00:00 2001 From: wangjian963 <15215920+aprilry@user.noreply.gitee.com> Date: Tue, 19 May 2026 12:12:16 +0800 Subject: [PATCH] =?UTF-8?q?Fix:=20=E9=97=A8=E8=AF=8A=E9=A2=84=E7=BA=A6?= =?UTF-8?q?=E6=8C=82=E5=8F=B7=E2=86=92=E7=AD=BE=E5=88=B0=E2=86=92=E9=80=80?= =?UTF-8?q?=E5=8F=B7=20slot/pool=20=E7=8A=B6=E6=80=81=E6=B5=81=E8=BD=AC?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=E9=9C=80=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 枚举重排: SlotStatus LOCKED=4→2, CANCELLED=2→4,匹配需求编号 - 预约: lockSlotForBooking 写入 LOCKED(2) 替代 BOOKED(1),pool locked_num+1 原子递增 - 签到: LOCKED(2)→BOOKED(1) 替代 CHECKED_IN(3),加前置状态校验 - 退号: 加 BOOKED(1) 前置校验 - 池计数: refreshPoolStats booked_num=COUNT(1), locked_num=COUNT(2) - SQL 状态值全部由 SlotStatus 枚举传入,消除硬编码 - 查询/显示: 加 locked 筛选分支,BOOKED→已取号, LOCKED→已锁定 - 前端常量同步,签到列表查询 book→locked --- .../impl/DoctorScheduleAppServiceImpl.java | 6 +- .../appservice/impl/TicketAppServiceImpl.java | 73 ++++++++++--------- .../OutpatientRegistrationAppServiceImpl.java | 36 +++++---- .../common/constant/CommonConstants.java | 32 -------- .../com/openhis/common/enums/SlotStatus.java | 57 +++++++++++++++ .../mapper/SchedulePoolMapper.java | 15 ++-- .../mapper/ScheduleSlotMapper.java | 19 +++-- .../service/impl/TicketServiceImpl.java | 36 ++++++--- .../administration/ScheduleSlotMapper.xml | 45 +++++++----- openhis-ui-vue3/src/utils/medicalConstants.js | 14 ++-- .../outpatientAppointment/index.vue | 3 + .../charge/outpatientregistration/index.vue | 2 +- 12 files changed, 207 insertions(+), 131 deletions(-) create mode 100644 openhis-server-new/openhis-common/src/main/java/com/openhis/common/enums/SlotStatus.java diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/DoctorScheduleAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/DoctorScheduleAppServiceImpl.java index dbfcee09..da46a87f 100755 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/DoctorScheduleAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/DoctorScheduleAppServiceImpl.java @@ -4,7 +4,7 @@ import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.core.common.core.domain.R; import com.core.common.utils.SecurityUtils; -import com.openhis.common.constant.CommonConstants; +import com.openhis.common.enums.SlotStatus; import com.openhis.appointmentmanage.domain.DoctorSchedule; import com.openhis.appointmentmanage.domain.DoctorScheduleWithDateDto; import com.openhis.appointmentmanage.domain.SchedulePool; @@ -502,8 +502,8 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService { // 该排班下存在有效患者预约(号源槽:已预约/已锁定/已取号)则禁止删除;已退号、仅可用/已取消槽位不计入 long appointmentCount = scheduleSlotService.count(new QueryWrapper() .in("pool_id", poolIds) - .in("status", CommonConstants.SlotStatus.BOOKED, CommonConstants.SlotStatus.LOCKED, - CommonConstants.SlotStatus.CHECKED_IN)); + .in("status", SlotStatus.BOOKED.getValue(), SlotStatus.LOCKED.getValue(), + SlotStatus.CHECKED_IN.getValue())); if (appointmentCount > 0) { return R.fail("该排班已有患者预约,禁止删除!如需取消请先处理患者退预约或使用'停诊'功能。"); } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/TicketAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/TicketAppServiceImpl.java index 3a723a24..27105a9d 100755 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/TicketAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/TicketAppServiceImpl.java @@ -9,7 +9,7 @@ import com.openhis.clinical.domain.Ticket; import com.openhis.clinical.service.ITicketService; import com.openhis.web.appointmentmanage.appservice.ITicketAppService; import com.openhis.web.appointmentmanage.dto.TicketDto; -import com.openhis.common.constant.CommonConstants.SlotStatus; +import com.openhis.common.enums.SlotStatus; import com.openhis.common.enums.OrderStatus; import org.springframework.stereotype.Service; @@ -193,25 +193,24 @@ public class TicketAppServiceImpl implements ITicketAppService { if (Boolean.TRUE.equals(raw.getIsStopped())) { dto.setStatus("已停诊"); } else { - Integer slotStatus = raw.getSlotStatus(); - if (slotStatus != null) { - if (SlotStatus.CHECKED_IN.equals(slotStatus)) { - dto.setStatus("已取号"); - } else if (SlotStatus.BOOKED.equals(slotStatus)) { - // order_main.status: 0=患者取消(已退号) 2=系统取消 其余=已预约 + SlotStatus status = SlotStatus.getByValue(raw.getSlotStatus()); + if (status != null) { + if (status == SlotStatus.LOCKED) { if (OrderStatus.PATIENT_CANCELLED.getValue().equals(raw.getOrderStatus())) { dto.setStatus("已退号"); - } else if (OrderStatus.SYSTEM_CANCELLED.getValue().equals(raw.getOrderStatus())) { - dto.setStatus("系统取消"); } else { - dto.setStatus("已预约"); + dto.setStatus("已锁定"); } - } else if (SlotStatus.RETURNED.equals(slotStatus)) { - dto.setStatus("已退号"); - } else if (SlotStatus.CANCELLED.equals(slotStatus)) { + } else if (status == SlotStatus.BOOKED) { + if (OrderStatus.PATIENT_CANCELLED.getValue().equals(raw.getOrderStatus())) { + dto.setStatus("已退号"); + } else { + dto.setStatus("已取号"); + } + } else if (status == SlotStatus.CANCELLED) { dto.setStatus("已停诊"); - } else if (SlotStatus.LOCKED.equals(slotStatus)) { - dto.setStatus("已锁定"); + } else if (status == SlotStatus.RETURNED) { + dto.setStatus("已退号"); } else { dto.setStatus("未预约"); } @@ -237,6 +236,10 @@ public class TicketAppServiceImpl implements ITicketAppService { /** * 统一状态入参,避免前端状态值大小写/中文/数字差异导致 SQL 条件失效后回全量数据 */ + /** + * 规范前端传入的状态查询参数,映射到 SQL 的 slotStatusNormExpr 值。 + * 数值映射: 0=待约 1=已约(签到后) 2=锁定(预约后) 3=已签到 4=已停诊 5=已退号 + */ private void normalizeQueryStatus(com.openhis.appointmentmanage.dto.TicketQueryDTO query) { String rawStatus = query.getStatus(); if (rawStatus == null) { @@ -263,28 +266,31 @@ public class TicketAppServiceImpl implements ITicketAppService { case "已预约": query.setStatus("booked"); break; + case "locked": + case "2": + case "已锁定": + query.setStatus("locked"); + break; case "checked": case "checkin": case "checkedin": - case "2": + case "3": case "已取号": query.setStatus("checked"); break; case "cancelled": case "canceled": - case "3": + case "4": case "已停诊": case "已取消": query.setStatus("cancelled"); break; case "returned": - case "4": case "5": case "已退号": query.setStatus("returned"); break; default: - // 设置为 impossible 值,配合 mapper 的 otherwise 分支直接返回空 query.setStatus("__invalid__"); break; } @@ -367,26 +373,25 @@ public class TicketAppServiceImpl implements ITicketAppService { if (Boolean.TRUE.equals(raw.getIsStopped())) { dto.setStatus("已停诊"); } else { - // 第二关:看独立的细分槽位状态 (0: 可用, 1: 已预约, 2: 已取消...) - Integer slotStatus = raw.getSlotStatus(); - if (slotStatus != null) { - if (SlotStatus.CHECKED_IN.equals(slotStatus)) { - dto.setStatus("已取号"); - } else if (SlotStatus.BOOKED.equals(slotStatus)) { - // order_main.status: 0=患者取消(已退号) 2=系统取消 其余=已预约 + // 第二关:看独立的细分槽位状态 (0: 可用, 1: 已预约, 2: 已锁定...) + SlotStatus status = SlotStatus.getByValue(raw.getSlotStatus()); + if (status != null) { + if (status == SlotStatus.LOCKED) { if (OrderStatus.PATIENT_CANCELLED.getValue().equals(raw.getOrderStatus())) { dto.setStatus("已退号"); - } else if (OrderStatus.SYSTEM_CANCELLED.getValue().equals(raw.getOrderStatus())) { - dto.setStatus("系统取消"); } else { - dto.setStatus("已预约"); + dto.setStatus("已锁定"); } - } else if (SlotStatus.RETURNED.equals(slotStatus)) { - dto.setStatus("已退号"); - } else if (SlotStatus.CANCELLED.equals(slotStatus)) { + } else if (status == SlotStatus.BOOKED) { + if (OrderStatus.PATIENT_CANCELLED.getValue().equals(raw.getOrderStatus())) { + dto.setStatus("已退号"); + } else { + dto.setStatus("已取号"); + } + } else if (status == SlotStatus.CANCELLED) { dto.setStatus("已停诊"); - } else if (SlotStatus.LOCKED.equals(slotStatus)) { - dto.setStatus("已锁定"); + } else if (status == SlotStatus.RETURNED) { + dto.setStatus("已退号"); } else { dto.setStatus("未预约"); } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/chargemanage/appservice/impl/OutpatientRegistrationAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/chargemanage/appservice/impl/OutpatientRegistrationAppServiceImpl.java index 6a3dd49f..ef6f7bd1 100755 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/chargemanage/appservice/impl/OutpatientRegistrationAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/chargemanage/appservice/impl/OutpatientRegistrationAppServiceImpl.java @@ -18,6 +18,7 @@ import com.openhis.administration.mapper.PatientMapper; import com.openhis.administration.service.*; import com.openhis.common.constant.CommonConstants; import com.openhis.common.constant.PromptMsgConstant; +import com.openhis.common.enums.SlotStatus; import com.openhis.common.enums.*; import com.openhis.common.enums.ybenums.YbPayment; import com.openhis.common.utils.EnumUtils; @@ -643,8 +644,7 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra .set(Order::getStatus, OrderStatus.PATIENT_CANCELLED.getValue()) .set(Order::getPayStatus, PaymentStatus.REFUND_ALL.getValue()) .set(Order::getCancelTime, new Date()) - .set(Order::getCancelReason, - StringUtils.isNotEmpty(reason) ? reason : "诊前退号") + .set(Order::getCancelReason, "诊前退号") .set(Order::getUpdateTime, new Date()) .setSql("version = version + 1") .eq(Order::getId, appointmentOrder.getId()) @@ -660,17 +660,27 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra return appointmentOrder.getId(); } - int slotRows = scheduleSlotMapper.updateSlotStatus(slotId, CommonConstants.SlotStatus.AVAILABLE); - if (slotRows > 0) { - Long poolId = scheduleSlotMapper.selectPoolIdBySlotId(slotId); - if (poolId != null) { - schedulePoolMapper.refreshPoolStats(poolId); - schedulePoolMapper.update(null, - new LambdaUpdateWrapper() - .setSql("version = version + 1") - .set(SchedulePool::getUpdateTime, new Date()) - .eq(SchedulePool::getId, poolId)); - } + // 只有已预约(1)的号源才能退号,对应签到后的 BOOKED 状态 + ScheduleSlot slot = scheduleSlotMapper.selectById(slotId); + if (slot == null || !SlotStatus.BOOKED.getValue().equals(slot.getStatus())) { + log.warn("退号跳过:槽位非已预约状态, slotId={}, status={}", slotId, + slot != null ? slot.getStatus() : null); + return appointmentOrder.getId(); + } + + int slotRows = scheduleSlotMapper.updateSlotStatus(slotId, SlotStatus.AVAILABLE.getValue()); + if (slotRows == 0) { + log.warn("退号时更新槽位状态未影响任何行, slotId={}", slotId); + return appointmentOrder.getId(); + } + + Long poolId = scheduleSlotMapper.selectPoolIdBySlotId(slotId); + if (poolId != null) { + schedulePoolMapper.update(null, + new LambdaUpdateWrapper() + .setSql("booked_num = booked_num - 1, version = version + 1") + .set(SchedulePool::getUpdateTime, new Date()) + .eq(SchedulePool::getId, poolId)); } return appointmentOrder.getId(); } catch (Exception e) { diff --git a/openhis-server-new/openhis-common/src/main/java/com/openhis/common/constant/CommonConstants.java b/openhis-server-new/openhis-common/src/main/java/com/openhis/common/constant/CommonConstants.java index 13e95eb8..36df4ff0 100755 --- a/openhis-server-new/openhis-common/src/main/java/com/openhis/common/constant/CommonConstants.java +++ b/openhis-server-new/openhis-common/src/main/java/com/openhis/common/constant/CommonConstants.java @@ -768,36 +768,4 @@ public class CommonConstants { Integer ACCOUNT_DEVICE_TYPE = 6; } - /** - * 号源槽位状态 (adm_schedule_slot.status) - */ - public interface SlotStatus { - /** 可用 / 待预约 */ - Integer AVAILABLE = 0; - /** 已预约 */ - Integer BOOKED = 1; - /** 已取消 / 已停诊 */ - Integer CANCELLED = 2; - /** 已签到 / 已取号 */ - Integer CHECKED_IN = 3; - /** 已锁定 */ - Integer LOCKED = 4; - /** 已退号 */ - Integer RETURNED = 5; - } - - /** - * 预约订单状态 (order_main.status) - */ - public interface AppointmentOrderStatus { - /** 已预约 (待就诊) */ - Integer BOOKED = 1; - /** 已取号 (已就诊) */ - Integer CHECKED_IN = 2; - /** 已取消 */ - Integer CANCELLED = 3; - /** 已退号 */ - Integer RETURNED = 4; - } - } diff --git a/openhis-server-new/openhis-common/src/main/java/com/openhis/common/enums/SlotStatus.java b/openhis-server-new/openhis-common/src/main/java/com/openhis/common/enums/SlotStatus.java new file mode 100644 index 00000000..912165fa --- /dev/null +++ b/openhis-server-new/openhis-common/src/main/java/com/openhis/common/enums/SlotStatus.java @@ -0,0 +1,57 @@ +package com.openhis.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 号源槽位状态 (adm_schedule_slot.status) + * + *
+ * 状态流转:
+ *   预约     → 0→2 (锁定), locked_num+1
+ *   取消预约 → 2→0 (释放), locked_num-1
+ *   签到     → 2→1 (已约), locked_num-1, booked_num+1
+ *   退号     → 1→0 (释放), booked_num-1
+ *   停诊     → 任意→4 (已取消)
+ * 
+ * + * @author system + */ +@Getter +@AllArgsConstructor +public enum SlotStatus implements HisEnumInterface { + + /** 可用 / 待预约 */ + AVAILABLE(0, "available", "可用"), + + /** 已预约 */ + BOOKED(1, "booked", "已预约"), + + /** 已锁定 (约而不付:预约后锁定号源) */ + LOCKED(2, "locked", "已锁定"), + + /** 已签到 / 已取号 */ + CHECKED_IN(3, "checked_in", "已签到"), + + /** 已取消 / 已停诊 */ + CANCELLED(4, "cancelled", "已取消"), + + /** 已退号 */ + RETURNED(5, "returned", "已退号"); + + private final Integer value; + private final String code; + private final String info; + + public static SlotStatus getByValue(Integer value) { + if (value == null) { + return null; + } + for (SlotStatus val : values()) { + if (val.getValue().equals(value)) { + return val; + } + } + return null; + } +} diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/mapper/SchedulePoolMapper.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/mapper/SchedulePoolMapper.java index ed01dd7b..714395ac 100755 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/mapper/SchedulePoolMapper.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/mapper/SchedulePoolMapper.java @@ -10,10 +10,11 @@ import org.springframework.stereotype.Repository; public interface SchedulePoolMapper extends BaseMapper { /** - * 按号源池实时重算统计值,避免并发场景下计数漂移。 + * 按号源池实时重算统计值。 * - * 说明:available_num 在当前项目中可能为数据库生成列,因此这里仅维护 - * booked_num / locked_num,剩余号由数据库或查询逻辑计算。 + * @param poolId 号源池ID + * @param bookedStatus 已约状态值,由 SlotStatus.BOOKED.getValue() 传入 + * @param lockedStatus 锁定状态值,由 SlotStatus.LOCKED.getValue() 传入 */ @Update(""" UPDATE adm_schedule_pool p @@ -23,20 +24,22 @@ public interface SchedulePoolMapper extends BaseMapper { FROM adm_schedule_slot s WHERE s.pool_id = p.id AND s.delete_flag = '0' - AND s.status = 1 + AND s.status = #{bookedStatus} ), 0), locked_num = COALESCE(( SELECT COUNT(1) FROM adm_schedule_slot s WHERE s.pool_id = p.id AND s.delete_flag = '0' - AND s.status = 3 + AND s.status = #{lockedStatus} ), 0), update_time = now() WHERE p.id = #{poolId} AND p.delete_flag = '0' """) - int refreshPoolStats(@Param("poolId") Long poolId); + int refreshPoolStats(@Param("poolId") Long poolId, + @Param("bookedStatus") Integer bookedStatus, + @Param("lockedStatus") Integer lockedStatus); /** * 签到时更新号源池统计:锁定数-1,已预约数+1 diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/mapper/ScheduleSlotMapper.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/mapper/ScheduleSlotMapper.java index f5910cf3..145af650 100755 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/mapper/ScheduleSlotMapper.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/mapper/ScheduleSlotMapper.java @@ -22,9 +22,12 @@ public interface ScheduleSlotMapper extends BaseMapper { TicketSlotDTO selectTicketSlotById(@Param("id") Long id); /** - * 原子抢占槽位:仅当当前状态=0(可用)时,更新为1(已预约)。 + * 原子抢占槽位:仅当当前状态=0(待约)时,更新为目标锁定状态。 + * + * @param slotId 槽位ID + * @param lockedStatus 锁定状态值,由 SlotStatus.LOCKED.getValue() 传入 */ - int lockSlotForBooking(@Param("slotId") Long slotId); + int lockSlotForBooking(@Param("slotId") Long slotId, @Param("lockedStatus") Integer lockedStatus); /** * 按主键更新槽位状态。 @@ -34,12 +37,16 @@ public interface ScheduleSlotMapper extends BaseMapper { /** * 更新槽位状态并记录签到时间 * - * @param slotId 槽位ID - * @param status 状态 - * @param checkInTime 签到时间 + * @param slotId 槽位ID + * @param status 目标状态,由 SlotStatus.BOOKED.getValue() 传入 + * @param checkInTime 签到时间 + * @param requiredStatus 前置状态,由 SlotStatus.LOCKED.getValue() 传入 * @return 结果 */ - int updateSlotStatusAndCheckInTime(@Param("slotId") Long slotId, @Param("status") Integer status, @Param("checkInTime") Date checkInTime); + int updateSlotStatusAndCheckInTime(@Param("slotId") Long slotId, + @Param("status") Integer status, + @Param("checkInTime") Date checkInTime, + @Param("requiredStatus") Integer requiredStatus); /** * 根据槽位ID查询所属号源池ID。 diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/TicketServiceImpl.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/TicketServiceImpl.java index d7c9861a..1ae75d0f 100755 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/TicketServiceImpl.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/TicketServiceImpl.java @@ -1,10 +1,12 @@ package com.openhis.clinical.service.impl; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.openhis.appointmentmanage.domain.AppointmentConfig; import com.openhis.appointmentmanage.service.IAppointmentConfigService; import com.openhis.appointmentmanage.domain.TicketSlotDTO; +import com.openhis.appointmentmanage.domain.SchedulePool; import com.openhis.appointmentmanage.domain.ScheduleSlot; import com.openhis.appointmentmanage.mapper.SchedulePoolMapper; import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper; @@ -13,7 +15,7 @@ import com.openhis.clinical.domain.Ticket; import com.openhis.clinical.mapper.TicketMapper; import com.openhis.clinical.service.IOrderService; import com.openhis.clinical.service.ITicketService; -import com.openhis.common.constant.CommonConstants.SlotStatus; +import com.openhis.common.enums.SlotStatus; import com.openhis.common.enums.OrderStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -177,7 +179,7 @@ public class TicketServiceImpl extends ServiceImpl impleme logger.error("安全拦截:号源底库核对失败,slotId: {}", slotId); throw new RuntimeException("号源数据不存在"); } - if (slot.getSlotStatus() != null && !SlotStatus.AVAILABLE.equals(slot.getSlotStatus())) { + if (slot.getSlotStatus() != null && SlotStatus.getByValue(slot.getSlotStatus()) != SlotStatus.AVAILABLE) { throw new RuntimeException("手慢了!该号源已刚刚被他人抢占"); } if (Boolean.TRUE.equals(slot.getIsStopped())) { @@ -205,7 +207,7 @@ public class TicketServiceImpl extends ServiceImpl impleme } // 原子抢占:避免并发下同一槽位被重复预约 - int lockRows = scheduleSlotMapper.lockSlotForBooking(slotId); + int lockRows = scheduleSlotMapper.lockSlotForBooking(slotId, SlotStatus.LOCKED.getValue()); if (lockRows <= 0) { throw new RuntimeException("手慢了!该号源已刚刚被他人抢占"); } @@ -260,7 +262,15 @@ public class TicketServiceImpl extends ServiceImpl impleme throw new RuntimeException("预约成功但号源回填订单失败,请重试"); } - refreshPoolStatsBySlotId(slotId); + // 6. 预约成功后 locked_num+1(原子递增替代全量 recount,避免并发计数漂移) + Long poolId = scheduleSlotMapper.selectPoolIdBySlotId(slotId); + if (poolId != null) { + schedulePoolMapper.update(null, + new LambdaUpdateWrapper() + .setSql("locked_num = locked_num + 1, version = version + 1") + .set(SchedulePool::getUpdateTime, new Date()) + .eq(SchedulePool::getId, poolId)); + } return 1; } @@ -277,7 +287,8 @@ public class TicketServiceImpl extends ServiceImpl impleme if (slot == null) { throw new RuntimeException("号源槽位不存在"); } - if (slot.getSlotStatus() == null || !SlotStatus.BOOKED.equals(slot.getSlotStatus())) { + // 只有锁定态(2)的号源可以取消预约 + if (slot.getSlotStatus() == null || SlotStatus.getByValue(slot.getSlotStatus()) != SlotStatus.LOCKED) { throw new RuntimeException("号源不可取消预约"); } @@ -292,7 +303,7 @@ public class TicketServiceImpl extends ServiceImpl impleme orderService.cancelAppointmentOrder(order.getId(), "患者取消预约"); } - int updated = scheduleSlotMapper.updateSlotStatus(slotId, SlotStatus.AVAILABLE); + int updated = scheduleSlotMapper.updateSlotStatus(slotId, SlotStatus.AVAILABLE.getValue()); if (updated > 0) { refreshPoolStatsBySlotId(slotId); } @@ -318,11 +329,14 @@ public class TicketServiceImpl extends ServiceImpl impleme orderService.updateOrderStatusById(latestOrder.getId(), OrderStatus.ACTIVE.getValue()); orderMapper.updatePayStatus(latestOrder.getId(), 1, new Date()); - // 2. 查询号源槽位信息 + // 2. 只有锁定态(2)的号源才能签到,签到时 2→1(LOCKED→BOOKED) ScheduleSlot slot = scheduleSlotMapper.selectById(slotId); + if (slot == null || !SlotStatus.LOCKED.getValue().equals(slot.getStatus())) { + throw new RuntimeException("号源状态异常,无法签到"); + } - // 3. 更新号源槽位状态为已签到,记录签到时间 - scheduleSlotMapper.updateSlotStatusAndCheckInTime(slotId, SlotStatus.CHECKED_IN, new Date()); + // 3. 更新号源槽位状态 2→1(LOCKED→BOOKED,已预约=已签到) + scheduleSlotMapper.updateSlotStatusAndCheckInTime(slotId, SlotStatus.BOOKED.getValue(), new Date(), SlotStatus.LOCKED.getValue()); // 4. 更新号源池统计:锁定数-1,已预约数+1 if (slot != null && slot.getPoolId() != null) { @@ -351,7 +365,7 @@ public class TicketServiceImpl extends ServiceImpl impleme orderService.cancelAppointmentOrder(order.getId(), "医生停诊"); } - int updated = scheduleSlotMapper.updateSlotStatus(slotId, SlotStatus.CANCELLED); + int updated = scheduleSlotMapper.updateSlotStatus(slotId, SlotStatus.CANCELLED.getValue()); if (updated > 0) { refreshPoolStatsBySlotId(slotId); } @@ -364,7 +378,7 @@ public class TicketServiceImpl extends ServiceImpl impleme private void refreshPoolStatsBySlotId(Long slotId) { Long poolId = scheduleSlotMapper.selectPoolIdBySlotId(slotId); if (poolId != null) { - schedulePoolMapper.refreshPoolStats(poolId); + schedulePoolMapper.refreshPoolStats(poolId, SlotStatus.BOOKED.getValue(), SlotStatus.LOCKED.getValue()); } } diff --git a/openhis-server-new/openhis-domain/src/main/resources/mapper/administration/ScheduleSlotMapper.xml b/openhis-server-new/openhis-domain/src/main/resources/mapper/administration/ScheduleSlotMapper.xml index c8a4056f..fd0ca01f 100755 --- a/openhis-server-new/openhis-domain/src/main/resources/mapper/administration/ScheduleSlotMapper.xml +++ b/openhis-server-new/openhis-domain/src/main/resources/mapper/administration/ScheduleSlotMapper.xml @@ -4,14 +4,17 @@ "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - + CASE WHEN LOWER(CONCAT('', s.status)) IN ('0', 'unbooked', 'available') THEN 0 WHEN LOWER(CONCAT('', s.status)) IN ('1', 'booked') THEN 1 - WHEN LOWER(CONCAT('', s.status)) IN ('2', 'cancelled', 'canceled', 'stopped') THEN 2 + WHEN LOWER(CONCAT('', s.status)) IN ('2', 'locked') THEN 2 WHEN LOWER(CONCAT('', s.status)) IN ('3', 'checked', 'checked_in', 'checkin') THEN 3 - WHEN LOWER(CONCAT('', s.status)) IN ('4', 'locked') THEN 4 + WHEN LOWER(CONCAT('', s.status)) IN ('4', 'cancelled', 'canceled', 'stopped') THEN 4 WHEN LOWER(CONCAT('', s.status)) IN ('5', 'returned') THEN 5 ELSE NULL END @@ -31,9 +34,9 @@ CASE WHEN LOWER(CONCAT('', p.status)) IN ('0', 'unbooked', 'available') THEN 0 WHEN LOWER(CONCAT('', p.status)) IN ('1', 'booked') THEN 1 - WHEN LOWER(CONCAT('', p.status)) IN ('2', 'cancelled', 'canceled', 'stopped') THEN 2 + WHEN LOWER(CONCAT('', p.status)) IN ('2', 'locked') THEN 2 WHEN LOWER(CONCAT('', p.status)) IN ('3', 'checked', 'checked_in', 'checkin') THEN 3 - WHEN LOWER(CONCAT('', p.status)) IN ('4', 'locked') THEN 4 + WHEN LOWER(CONCAT('', p.status)) IN ('4', 'cancelled', 'canceled', 'stopped') THEN 4 WHEN LOWER(CONCAT('', p.status)) IN ('5', 'returned') THEN 5 ELSE NULL END @@ -149,10 +152,11 @@ s.id = #{id} + UPDATE adm_schedule_slot SET - status = 1, + status = #{lockedStatus}, update_time = now() WHERE id = #{slotId} @@ -174,6 +178,7 @@ AND delete_flag = '0' + UPDATE adm_schedule_slot SET @@ -182,6 +187,7 @@ update_time = NOW() WHERE id = #{slotId} + AND status = #{requiredStatus} AND delete_flag = '0' @@ -202,7 +208,7 @@ update_time = now() WHERE id = #{slotId} - AND status = 1 + AND status = 2 AND delete_flag = '0' @@ -299,15 +305,16 @@ AND o.phone LIKE CONCAT('%', #{query.phone}, '%') - + AND ( ( = 0 AND (p.schedule_date > CURRENT_DATE OR (p.schedule_date = CURRENT_DATE AND (CAST(p.schedule_date AS TIMESTAMP) + CAST(s.expect_time AS TIME)) >= NOW()))) OR = 1 + OR = 2 OR = 3 OR = 5 OR = 4 ) - + @@ -318,7 +325,15 @@ ) - AND = 1 + AND = 2 + AND = 1 + AND ( + d.is_stopped IS NULL + OR d.is_stopped = FALSE + ) + + + AND = 2 AND = 1 AND ( d.is_stopped IS NULL @@ -326,13 +341,7 @@ ) - AND ( - = 3 - OR ( - = 1 - AND = 2 - ) - ) + AND = 1 AND ( d.is_stopped IS NULL OR d.is_stopped = FALSE @@ -340,7 +349,7 @@ AND ( - = 2 + = 4 OR d.is_stopped = TRUE ) diff --git a/openhis-ui-vue3/src/utils/medicalConstants.js b/openhis-ui-vue3/src/utils/medicalConstants.js index df68bae4..92a5d3c4 100755 --- a/openhis-ui-vue3/src/utils/medicalConstants.js +++ b/openhis-ui-vue3/src/utils/medicalConstants.js @@ -172,12 +172,12 @@ export const SlotStatus = { AVAILABLE: 0, /** 已预约 */ BOOKED: 1, - /** 已取消 / 已停诊 */ - CANCELLED: 2, + /** 已锁定 */ + LOCKED: 2, /** 已签到 / 已取号 */ CHECKED_IN: 3, - /** 已锁定 */ - LOCKED: 4, + /** 已取消 / 已停诊 */ + CANCELLED: 4, }; /** @@ -185,10 +185,10 @@ export const SlotStatus = { */ export const SlotStatusDescriptions = { 0: '未预约', - 1: '已预约', - 2: '已停诊', + 1: '已取号', + 2: '已锁定', 3: '已取号', - 4: '已锁定', + 4: '已停诊', }; /** diff --git a/openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue b/openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue index 336bcfff..4cc41f8b 100755 --- a/openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue +++ b/openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue @@ -34,6 +34,7 @@