新增校验,防止删除存在有效患者预约的医生排班。
更新 SurgeryDto,为计划手术时间添加 JSON 格式配置。 改进接诊确认逻辑,使医师确认流程更加健壮。 在 OrderMapper 中新增方法,用于统计患者在指定时间段内的有效预约订单数量。 增强 TicketServiceImpl,防止同一患者在相同科室与时间段内重复预约。
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
package com.openhis.web.appointmentmanage.appservice.impl;
|
package com.openhis.web.appointmentmanage.appservice.impl;
|
||||||
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
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.core.domain.R;
|
||||||
import com.core.common.utils.SecurityUtils;
|
import com.core.common.utils.SecurityUtils;
|
||||||
|
import com.openhis.common.constant.CommonConstants;
|
||||||
import com.openhis.appointmentmanage.domain.DoctorSchedule;
|
import com.openhis.appointmentmanage.domain.DoctorSchedule;
|
||||||
import com.openhis.appointmentmanage.domain.DoctorScheduleWithDateDto;
|
import com.openhis.appointmentmanage.domain.DoctorScheduleWithDateDto;
|
||||||
import com.openhis.appointmentmanage.domain.SchedulePool;
|
import com.openhis.appointmentmanage.domain.SchedulePool;
|
||||||
@@ -497,6 +499,15 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
|
|||||||
if (ObjectUtil.isNotEmpty(pools)) {
|
if (ObjectUtil.isNotEmpty(pools)) {
|
||||||
List<Long> poolIds = pools.stream().map(SchedulePool::getId).collect(java.util.stream.Collectors.toList());
|
List<Long> poolIds = pools.stream().map(SchedulePool::getId).collect(java.util.stream.Collectors.toList());
|
||||||
|
|
||||||
|
// 该排班下存在有效患者预约(号源槽:已预约/已锁定/已取号)则禁止删除;已退号、仅可用/已取消槽位不计入
|
||||||
|
long appointmentCount = scheduleSlotService.count(new QueryWrapper<ScheduleSlot>()
|
||||||
|
.in("pool_id", poolIds)
|
||||||
|
.in("status", CommonConstants.SlotStatus.BOOKED, CommonConstants.SlotStatus.LOCKED,
|
||||||
|
CommonConstants.SlotStatus.CHECKED_IN));
|
||||||
|
if (appointmentCount > 0) {
|
||||||
|
return R.fail("该排班已有患者预约,禁止删除!如需取消请先处理患者退预约或使用'停诊'功能。");
|
||||||
|
}
|
||||||
|
|
||||||
// 2. 根据号源池ID找到所有关联的号源槽
|
// 2. 根据号源池ID找到所有关联的号源槽
|
||||||
List<ScheduleSlot> slots = scheduleSlotService.list(
|
List<ScheduleSlot> slots = scheduleSlotService.list(
|
||||||
new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<ScheduleSlot>()
|
new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<ScheduleSlot>()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.openhis.web.clinicalmanage.dto;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.openhis.common.annotation.Dict;
|
import com.openhis.common.annotation.Dict;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
@@ -87,6 +88,7 @@ public class SurgeryDto {
|
|||||||
private String statusEnum_dictText;
|
private String statusEnum_dictText;
|
||||||
|
|
||||||
/** 计划手术时间 */
|
/** 计划手术时间 */
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "GMT+8")
|
||||||
private Date plannedTime;
|
private Date plannedTime;
|
||||||
|
|
||||||
/** 实际开始时间 */
|
/** 实际开始时间 */
|
||||||
|
|||||||
@@ -1365,10 +1365,13 @@ public class ConsultationAppServiceImpl implements IConsultationAppService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. 更新邀请记录(存储会诊意见)
|
// 4. 更新邀请记录(存储会诊意见)
|
||||||
// 格式:科室-医生:意见内容
|
// 格式:科室-会诊确认参加医师:意见内容
|
||||||
|
// 兼容:若前端未填写“会诊确认参加医师”,则回退为当前医生姓名
|
||||||
|
String confirmingPhysicianText =
|
||||||
|
StringUtils.hasText(dto.getConfirmingPhysician()) ? dto.getConfirmingPhysician().trim() : currentPhysicianName;
|
||||||
String formattedOpinion = String.format("%s-%s:%s",
|
String formattedOpinion = String.format("%s-%s:%s",
|
||||||
currentDeptName,
|
currentDeptName,
|
||||||
currentPhysicianName,
|
confirmingPhysicianText,
|
||||||
dto.getConsultationOpinion());
|
dto.getConsultationOpinion());
|
||||||
|
|
||||||
invited.setInvitedStatus(ConsultationStatusEnum.CONFIRMED.getCode()); // 已确认
|
invited.setInvitedStatus(ConsultationStatusEnum.CONFIRMED.getCode()); // 已确认
|
||||||
|
|||||||
@@ -42,4 +42,20 @@ public interface OrderMapper extends BaseMapper<Order> {
|
|||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
int updatePayStatus(@Param("orderId") Long orderId, @Param("payStatus") Integer payStatus, @Param("payTime") Date payTime);
|
int updatePayStatus(@Param("orderId") Long orderId, @Param("payStatus") Integer payStatus, @Param("payTime") Date payTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计同一患者在同一科室、同一时段(上午/下午)内的有效预约订单数量
|
||||||
|
*
|
||||||
|
* @param patientId 患者ID
|
||||||
|
* @param departmentId 科室ID
|
||||||
|
* @param startTime 时段起始时间(含)
|
||||||
|
* @param endTime 时段结束时间(不含)
|
||||||
|
* @param statuses 订单状态集合(如 1=已预约,2=已取号)
|
||||||
|
* @return 数量
|
||||||
|
*/
|
||||||
|
int countPatientDeptOrdersInPeriod(@Param("patientId") Long patientId,
|
||||||
|
@Param("departmentId") Long departmentId,
|
||||||
|
@Param("startTime") Date startTime,
|
||||||
|
@Param("endTime") Date endTime,
|
||||||
|
@Param("statuses") List<Integer> statuses);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import java.time.LocalTime;
|
|||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.temporal.TemporalAdjusters;
|
import java.time.temporal.TemporalAdjusters;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -182,6 +183,28 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
throw new RuntimeException("该排班医生已停诊");
|
throw new RuntimeException("该排班医生已停诊");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2.1 同一患者同一天/同一科室/同一时段(上午/下午)不可重复预约
|
||||||
|
if (dto.getPatientId() != null && slot.getDepartmentId() != null && slot.getScheduleDate() != null && slot.getExpectTime() != null) {
|
||||||
|
boolean isMorning = slot.getExpectTime().isBefore(LocalTime.NOON);
|
||||||
|
LocalDate scheduleDateForCheck = slot.getScheduleDate();
|
||||||
|
LocalDateTime periodStart = isMorning
|
||||||
|
? LocalDateTime.of(scheduleDateForCheck, LocalTime.MIN)
|
||||||
|
: LocalDateTime.of(scheduleDateForCheck, LocalTime.NOON);
|
||||||
|
LocalDateTime periodEnd = isMorning
|
||||||
|
? LocalDateTime.of(scheduleDateForCheck, LocalTime.NOON)
|
||||||
|
: LocalDateTime.of(scheduleDateForCheck.plusDays(1), LocalTime.MIN);
|
||||||
|
|
||||||
|
Date startTime = Date.from(periodStart.atZone(ZoneId.systemDefault()).toInstant());
|
||||||
|
Date endTime = Date.from(periodEnd.atZone(ZoneId.systemDefault()).toInstant());
|
||||||
|
|
||||||
|
// 预约去重以订单为准(order_main),因为预约成功会先落订单;clinical_ticket 不一定在此链路写入
|
||||||
|
List<Integer> effectiveOrderStatuses = Arrays.asList(AppointmentOrderStatus.BOOKED, AppointmentOrderStatus.CHECKED_IN);
|
||||||
|
int exists = orderMapper.countPatientDeptOrdersInPeriod(dto.getPatientId(), slot.getDepartmentId(), startTime, endTime, effectiveOrderStatuses);
|
||||||
|
if (exists > 0) {
|
||||||
|
throw new RuntimeException("该患者已在当前科室该时段存在预约记录,不可重复预约");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 原子抢占:避免并发下同一槽位被重复预约
|
// 原子抢占:避免并发下同一槽位被重复预约
|
||||||
int lockRows = scheduleSlotMapper.lockSlotForBooking(slotId);
|
int lockRows = scheduleSlotMapper.lockSlotForBooking(slotId);
|
||||||
if (lockRows <= 0) {
|
if (lockRows <= 0) {
|
||||||
@@ -264,9 +287,6 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
throw new RuntimeException("当前号源没有可取消的预约订单");
|
throw new RuntimeException("当前号源没有可取消的预约订单");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取订单信息
|
|
||||||
Order latestOrder = orders.get(0);
|
|
||||||
|
|
||||||
// 直接执行取消,不再检查取消限制
|
// 直接执行取消,不再检查取消限制
|
||||||
// 根据需求,取消限制应在预约挂号时检查,而非取消预约时
|
// 根据需求,取消限制应在预约挂号时检查,而非取消预约时
|
||||||
for (Order order : orders) {
|
for (Order order : orders) {
|
||||||
|
|||||||
@@ -217,6 +217,23 @@
|
|||||||
where id = #{id}
|
where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<select id="countPatientDeptOrdersInPeriod" resultType="int">
|
||||||
|
select count(*)
|
||||||
|
from order_main
|
||||||
|
<where>
|
||||||
|
and patient_id = #{patientId}
|
||||||
|
and department_id = #{departmentId}
|
||||||
|
and appointment_time >= #{startTime}
|
||||||
|
and appointment_time < #{endTime}
|
||||||
|
<if test="statuses != null and statuses.size() > 0">
|
||||||
|
and status in
|
||||||
|
<foreach item="s" collection="statuses" open="(" separator="," close=")">
|
||||||
|
#{s}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
</select>
|
||||||
|
|
||||||
<update id="updateOrderStatusById">
|
<update id="updateOrderStatusById">
|
||||||
update order_main set status = #{status} where id = #{id}
|
update order_main set status = #{status} where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|||||||
@@ -352,7 +352,20 @@ const applyRowToForm = (row) => {
|
|||||||
|
|
||||||
if (myOpinion) {
|
if (myOpinion) {
|
||||||
// 如果当前医生已确认,回显其信息
|
// 如果当前医生已确认,回显其信息
|
||||||
|
// 回显“会诊确认参加医师”:优先从 opinion 前缀解析(格式:科室-参加医师:意见)
|
||||||
|
// 兼容旧数据(格式:科室-医生:意见)以及异常格式
|
||||||
|
if (myOpinion.opinion) {
|
||||||
|
const opinionText = myOpinion.opinion
|
||||||
|
const colonIndex = opinionText.indexOf(':')
|
||||||
|
const dashIndex = opinionText.indexOf('-')
|
||||||
|
if (dashIndex >= 0 && colonIndex > dashIndex) {
|
||||||
|
formData.value.confirmingPhysician = opinionText.substring(dashIndex + 1, colonIndex).trim()
|
||||||
|
} else {
|
||||||
formData.value.confirmingPhysician = myOpinion.physicianName || ''
|
formData.value.confirmingPhysician = myOpinion.physicianName || ''
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
formData.value.confirmingPhysician = myOpinion.physicianName || ''
|
||||||
|
}
|
||||||
formData.value.confirmingPhysicianName = myOpinion.physicianName
|
formData.value.confirmingPhysicianName = myOpinion.physicianName
|
||||||
formData.value.confirmingDeptName = myOpinion.deptName
|
formData.value.confirmingDeptName = myOpinion.deptName
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user