77 门诊挂号-》预约签到

This commit is contained in:
HuangXinQuan
2026-04-03 14:42:13 +08:00
parent cb46461ede
commit 1b3d4e3dc0
17 changed files with 821 additions and 77 deletions

View File

@@ -16,6 +16,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -123,6 +124,7 @@ public class TicketAppServiceImpl implements ITicketAppService {
if (query == null) {
query = new com.openhis.appointmentmanage.dto.TicketQueryDTO();
}
normalizeQueryStatus(query);
// 2. 构造 MyBatis 的分页对象 (传入前端给的当前页和每页条数)
com.baomidou.mybatisplus.extension.plugins.pagination.Page<com.openhis.appointmentmanage.domain.TicketSlotDTO> pageParam = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(
@@ -145,37 +147,61 @@ public class TicketAppServiceImpl implements ITicketAppService {
dto.setDepartment(raw.getDepartmentName()); // 注意以前这里传成了ID导致前端出Bug现在修复成了真正的科室名
dto.setFee(raw.getFee());
dto.setPatientName(raw.getPatientName());
dto.setPatientId(raw.getPatientId() != null ? String.valueOf(raw.getPatientId()) : null);
dto.setPatientId(raw.getMedicalCard());
dto.setPhone(raw.getPhone());
dto.setIdCard(raw.getIdCard());
dto.setDoctorId(raw.getDoctorId());
dto.setDepartmentId(raw.getDepartmentId());
dto.setRealPatientId(raw.getPatientId());
// 性别处理:直接读取优先级最高的订单性别字段 (SQL 已处理优先级)
if (raw.getPatientGender() != null) {
String pg = raw.getPatientGender().trim();
dto.setGender("1".equals(pg) ? "" : ("2".equals(pg) ? "" : "未知"));
} else {
dto.setGender("未知");
}
// 号源类型处理 (底层是1前端要的是expert)
if (raw.getRegType() != null && raw.getRegType() == 1) {
dto.setTicketType("expert");
} else {
dto.setTicketType("general");
}
// 拼接就诊时间
if (raw.getScheduleDate() != null && raw.getExpectTime() != null) {
dto.setDateTime(raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
try {
dto.setAppointmentDate(
new java.text.SimpleDateFormat("yyyy-MM-dd").parse(raw.getScheduleDate().toString()));
String timeStr = raw.getAppointmentTime() != null ? raw.getAppointmentTime() : (raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(timeStr.length() > 10 ? "yyyy-MM-dd HH:mm" : "yyyy-MM-dd");
java.util.Date date = sdf.parse(timeStr);
dto.setAppointmentDate(date);
dto.setAppointmentTime(date);
} catch (Exception e) {
dto.setAppointmentDate(new java.util.Date());
}
}
// 精准状态翻译把底层的1和2翻译回前端能懂的中文
if (Boolean.TRUE.equals(raw.getIsStopped())) {
dto.setStatus("已停诊");
} else {
Integer slotStatus = raw.getSlotStatus();
if (slotStatus != null) {
if (SlotStatus.BOOKED.equals(slotStatus)) {
dto.setStatus(AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus()) ? "已取号" : "已预约");
} else if (SlotStatus.STOPPED.equals(slotStatus)) {
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
dto.setStatus("已取号");
} else if (SlotStatus.BOOKED.equals(slotStatus)) {
if (AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus())) {
dto.setStatus("已取号");
} else if (AppointmentOrderStatus.RETURNED.equals(raw.getOrderStatus())) {
dto.setStatus("已退号");
} else {
dto.setStatus("已预约");
}
} else if (SlotStatus.RETURNED.equals(slotStatus)) {
dto.setStatus("已退号");
} else if (SlotStatus.CANCELLED.equals(slotStatus)) {
dto.setStatus("已停诊");
} else if (SlotStatus.LOCKED.equals(slotStatus)) {
dto.setStatus("已锁定");
} else {
dto.setStatus("未预约");
}
@@ -198,6 +224,62 @@ public class TicketAppServiceImpl implements ITicketAppService {
return R.ok(result);
}
/**
* 统一状态入参,避免前端状态值大小写/中文/数字差异导致 SQL 条件失效后回全量数据
*/
private void normalizeQueryStatus(com.openhis.appointmentmanage.dto.TicketQueryDTO query) {
String rawStatus = query.getStatus();
if (rawStatus == null) {
return;
}
String normalized = rawStatus.trim();
if (normalized.isEmpty()) {
query.setStatus(null);
return;
}
String lower = normalized.toLowerCase(Locale.ROOT);
switch (lower) {
case "all":
case "全部":
query.setStatus("all");
break;
case "unbooked":
case "0":
case "未预约":
query.setStatus("unbooked");
break;
case "booked":
case "1":
case "已预约":
query.setStatus("booked");
break;
case "checked":
case "checkin":
case "checkedin":
case "2":
case "已取号":
query.setStatus("checked");
break;
case "cancelled":
case "canceled":
case "3":
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;
}
}
@Override
public R<?> listDoctorAvailability(com.openhis.appointmentmanage.dto.TicketQueryDTO query) {
if (query == null) {
@@ -242,7 +324,7 @@ public class TicketAppServiceImpl implements ITicketAppService {
dto.setDepartment(raw.getDepartmentName());
dto.setFee(raw.getFee());
dto.setPatientName(raw.getPatientName());
dto.setPatientId(raw.getPatientId() != null ? String.valueOf(raw.getPatientId()) : null);
dto.setPatientId(raw.getMedicalCard());
dto.setPhone(raw.getPhone());
// --- 号源类型处理 (普通/专家) ---
@@ -258,9 +340,13 @@ public class TicketAppServiceImpl implements ITicketAppService {
if (raw.getScheduleDate() != null && raw.getExpectTime() != null) {
dto.setDateTime(raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
try {
dto.setAppointmentDate(
new java.text.SimpleDateFormat("yyyy-MM-dd").parse(raw.getScheduleDate().toString()));
String timeStr = raw.getAppointmentTime() != null ? raw.getAppointmentTime() : (raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(timeStr.length() > 10 ? "yyyy-MM-dd HH:mm" : "yyyy-MM-dd");
java.util.Date date = sdf.parse(timeStr);
dto.setAppointmentDate(date);
dto.setAppointmentTime(date);
} catch (Exception e) {
log.error("时间解析失败", e);
dto.setAppointmentDate(new java.util.Date());
}
}
@@ -273,10 +359,22 @@ public class TicketAppServiceImpl implements ITicketAppService {
// 第二关:看独立的细分槽位状态 (0: 可用, 1: 已预约, 2: 已取消...)
Integer slotStatus = raw.getSlotStatus();
if (slotStatus != null) {
if (SlotStatus.BOOKED.equals(slotStatus)) {
dto.setStatus(AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus()) ? "已取号" : "已预约");
} else if (SlotStatus.STOPPED.equals(slotStatus)) {
dto.setStatus("已停诊"); // 视业务可改回已取消
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
dto.setStatus("已取号");
} else if (SlotStatus.BOOKED.equals(slotStatus)) {
if (AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus())) {
dto.setStatus("已取号");
} else if (AppointmentOrderStatus.RETURNED.equals(raw.getOrderStatus())) {
dto.setStatus("已退号");
} else {
dto.setStatus("已预约");
}
} else if (SlotStatus.RETURNED.equals(slotStatus)) {
dto.setStatus("已退号");
} else if (SlotStatus.CANCELLED.equals(slotStatus)) {
dto.setStatus("已停诊");
} else if (SlotStatus.LOCKED.equals(slotStatus)) {
dto.setStatus("已锁定");
} else {
dto.setStatus("未预约");
}
@@ -355,15 +453,12 @@ public class TicketAppServiceImpl implements ITicketAppService {
if (patient != null) {
Integer genderEnum = patient.getGenderEnum();
if (genderEnum != null) {
switch (genderEnum) {
case 1:
dto.setGender("");
break;
case 2:
dto.setGender("");
break;
default:
dto.setGender("未知");
if (Integer.valueOf(1).equals(genderEnum)) {
dto.setGender("");
} else if (Integer.valueOf(2).equals(genderEnum)) {
dto.setGender("");
} else {
dto.setGender("未知");
}
}
}

View File

@@ -99,4 +99,15 @@ public class TicketDto {
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long doctorId;
/**
* 真实患者ID数据库主键区别于 patientId 存的就诊卡号)
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long realPatientId;
/**
* 身份证号
*/
private String idCard;
}

View File

@@ -22,6 +22,10 @@ import com.openhis.common.enums.ybenums.YbPayment;
import com.openhis.common.utils.EnumUtils;
import com.openhis.common.utils.HisPageUtils;
import com.openhis.common.utils.HisQueryUtils;
import com.openhis.appointmentmanage.mapper.SchedulePoolMapper;
import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper;
import com.openhis.clinical.domain.Order;
import com.openhis.clinical.service.IOrderService;
import com.openhis.financial.domain.PaymentReconciliation;
import com.openhis.financial.domain.RefundLog;
import com.openhis.financial.service.IRefundLogService;
@@ -48,6 +52,7 @@ import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
@@ -97,6 +102,15 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
@Resource
IRefundLogService iRefundLogService;
@Resource
IOrderService orderService;
@Resource
ScheduleSlotMapper scheduleSlotMapper;
@Resource
SchedulePoolMapper schedulePoolMapper;
/**
* 门诊挂号 - 查询患者信息
*
@@ -291,6 +305,11 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
}
}
// 如果本次门诊挂号来自预约签到,同步把预约订单与号源槽位状态改为已退号
if (result != null && result.getCode() == 200) {
syncAppointmentReturnStatus(byId, cancelRegPaymentDto.getReason());
}
// 记录退号日志
recordRefundLog(cancelRegPaymentDto, byId, result, paymentRecon);
@@ -399,6 +418,74 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
return R.ok("已取消挂号");
}
/**
* 同步预约号源状态为已退号。
* 说明:
* 1) 门诊退号主流程不依赖该步骤成功与否,因此此方法内部异常仅记录日志,不向上抛出。
* 2) 通过患者、科室、日期以及状态筛选最近一条预约订单,尽量避免误匹配。
*/
private void syncAppointmentReturnStatus(Encounter encounter, String reason) {
if (encounter == null || encounter.getPatientId() == null) {
return;
}
try {
LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<Order>()
.eq(Order::getPatientId, encounter.getPatientId())
.in(Order::getStatus, CommonConstants.AppointmentOrderStatus.BOOKED,
CommonConstants.AppointmentOrderStatus.CHECKED_IN)
.orderByDesc(Order::getUpdateTime)
.orderByDesc(Order::getCreateTime)
.last("LIMIT 1");
if (encounter.getOrganizationId() != null) {
queryWrapper.eq(Order::getDepartmentId, encounter.getOrganizationId());
}
if (encounter.getTenantId() != null) {
queryWrapper.eq(Order::getTenantId, encounter.getTenantId());
}
if (encounter.getCreateTime() != null) {
LocalDate encounterDate = encounter.getCreateTime().toInstant()
.atZone(ZoneId.systemDefault()).toLocalDate();
Date startOfDay = Date.from(encounterDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
Date nextDayStart = Date.from(encounterDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
queryWrapper.ge(Order::getAppointmentDate, startOfDay)
.lt(Order::getAppointmentDate, nextDayStart);
}
Order appointmentOrder = orderService.getOne(queryWrapper, false);
if (appointmentOrder == null) {
return;
}
Date now = new Date();
if (!CommonConstants.AppointmentOrderStatus.RETURNED.equals(appointmentOrder.getStatus())) {
Order updateOrder = new Order();
updateOrder.setId(appointmentOrder.getId());
updateOrder.setStatus(CommonConstants.AppointmentOrderStatus.RETURNED);
updateOrder.setCancelTime(now);
updateOrder.setCancelReason(
StringUtils.isNotEmpty(reason) ? reason : "门诊退号");
updateOrder.setUpdateTime(now);
orderService.updateById(updateOrder);
}
Long slotId = appointmentOrder.getSlotId();
if (slotId == null) {
return;
}
int slotRows = scheduleSlotMapper.updateSlotStatus(slotId, CommonConstants.SlotStatus.RETURNED);
if (slotRows > 0) {
Long poolId = scheduleSlotMapper.selectPoolIdBySlotId(slotId);
if (poolId != null) {
schedulePoolMapper.refreshPoolStats(poolId);
}
}
} catch (Exception e) {
log.warn("同步预约号源已退号状态失败, encounterId={}", encounter.getId(), e);
}
}
/**
* 补打挂号
* 补打挂号不需要修改数据库,只需要返回成功即可,前端已有所有需要的数据用于打印