77 门诊挂号-》预约签到
This commit is contained in:
@@ -16,6 +16,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -123,6 +124,7 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
if (query == null) {
|
if (query == null) {
|
||||||
query = new com.openhis.appointmentmanage.dto.TicketQueryDTO();
|
query = new com.openhis.appointmentmanage.dto.TicketQueryDTO();
|
||||||
}
|
}
|
||||||
|
normalizeQueryStatus(query);
|
||||||
|
|
||||||
// 2. 构造 MyBatis 的分页对象 (传入前端给的当前页和每页条数)
|
// 2. 构造 MyBatis 的分页对象 (传入前端给的当前页和每页条数)
|
||||||
com.baomidou.mybatisplus.extension.plugins.pagination.Page<com.openhis.appointmentmanage.domain.TicketSlotDTO> pageParam = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(
|
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.setDepartment(raw.getDepartmentName()); // 注意:以前这里传成了ID,导致前端出Bug,现在修复成了真正的科室名
|
||||||
dto.setFee(raw.getFee());
|
dto.setFee(raw.getFee());
|
||||||
dto.setPatientName(raw.getPatientName());
|
dto.setPatientName(raw.getPatientName());
|
||||||
dto.setPatientId(raw.getPatientId() != null ? String.valueOf(raw.getPatientId()) : null);
|
dto.setPatientId(raw.getMedicalCard());
|
||||||
dto.setPhone(raw.getPhone());
|
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) {
|
if (raw.getRegType() != null && raw.getRegType() == 1) {
|
||||||
dto.setTicketType("expert");
|
dto.setTicketType("expert");
|
||||||
} else {
|
} else {
|
||||||
dto.setTicketType("general");
|
dto.setTicketType("general");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拼接就诊时间
|
|
||||||
if (raw.getScheduleDate() != null && raw.getExpectTime() != null) {
|
if (raw.getScheduleDate() != null && raw.getExpectTime() != null) {
|
||||||
dto.setDateTime(raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
|
dto.setDateTime(raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
|
||||||
try {
|
try {
|
||||||
dto.setAppointmentDate(
|
String timeStr = raw.getAppointmentTime() != null ? raw.getAppointmentTime() : (raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
|
||||||
new java.text.SimpleDateFormat("yyyy-MM-dd").parse(raw.getScheduleDate().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) {
|
} catch (Exception e) {
|
||||||
dto.setAppointmentDate(new java.util.Date());
|
dto.setAppointmentDate(new java.util.Date());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 精准状态翻译!把底层的1和2,翻译回前端能懂的中文
|
|
||||||
if (Boolean.TRUE.equals(raw.getIsStopped())) {
|
if (Boolean.TRUE.equals(raw.getIsStopped())) {
|
||||||
dto.setStatus("已停诊");
|
dto.setStatus("已停诊");
|
||||||
} else {
|
} else {
|
||||||
Integer slotStatus = raw.getSlotStatus();
|
Integer slotStatus = raw.getSlotStatus();
|
||||||
if (slotStatus != null) {
|
if (slotStatus != null) {
|
||||||
if (SlotStatus.BOOKED.equals(slotStatus)) {
|
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
|
||||||
dto.setStatus(AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus()) ? "已取号" : "已预约");
|
dto.setStatus("已取号");
|
||||||
} else if (SlotStatus.STOPPED.equals(slotStatus)) {
|
} 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("已停诊");
|
dto.setStatus("已停诊");
|
||||||
|
} else if (SlotStatus.LOCKED.equals(slotStatus)) {
|
||||||
|
dto.setStatus("已锁定");
|
||||||
} else {
|
} else {
|
||||||
dto.setStatus("未预约");
|
dto.setStatus("未预约");
|
||||||
}
|
}
|
||||||
@@ -198,6 +224,62 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
return R.ok(result);
|
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
|
@Override
|
||||||
public R<?> listDoctorAvailability(com.openhis.appointmentmanage.dto.TicketQueryDTO query) {
|
public R<?> listDoctorAvailability(com.openhis.appointmentmanage.dto.TicketQueryDTO query) {
|
||||||
if (query == null) {
|
if (query == null) {
|
||||||
@@ -242,7 +324,7 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
dto.setDepartment(raw.getDepartmentName());
|
dto.setDepartment(raw.getDepartmentName());
|
||||||
dto.setFee(raw.getFee());
|
dto.setFee(raw.getFee());
|
||||||
dto.setPatientName(raw.getPatientName());
|
dto.setPatientName(raw.getPatientName());
|
||||||
dto.setPatientId(raw.getPatientId() != null ? String.valueOf(raw.getPatientId()) : null);
|
dto.setPatientId(raw.getMedicalCard());
|
||||||
dto.setPhone(raw.getPhone());
|
dto.setPhone(raw.getPhone());
|
||||||
|
|
||||||
// --- 号源类型处理 (普通/专家) ---
|
// --- 号源类型处理 (普通/专家) ---
|
||||||
@@ -258,9 +340,13 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
if (raw.getScheduleDate() != null && raw.getExpectTime() != null) {
|
if (raw.getScheduleDate() != null && raw.getExpectTime() != null) {
|
||||||
dto.setDateTime(raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
|
dto.setDateTime(raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
|
||||||
try {
|
try {
|
||||||
dto.setAppointmentDate(
|
String timeStr = raw.getAppointmentTime() != null ? raw.getAppointmentTime() : (raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
|
||||||
new java.text.SimpleDateFormat("yyyy-MM-dd").parse(raw.getScheduleDate().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) {
|
} catch (Exception e) {
|
||||||
|
log.error("时间解析失败", e);
|
||||||
dto.setAppointmentDate(new java.util.Date());
|
dto.setAppointmentDate(new java.util.Date());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,10 +359,22 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
// 第二关:看独立的细分槽位状态 (0: 可用, 1: 已预约, 2: 已取消...)
|
// 第二关:看独立的细分槽位状态 (0: 可用, 1: 已预约, 2: 已取消...)
|
||||||
Integer slotStatus = raw.getSlotStatus();
|
Integer slotStatus = raw.getSlotStatus();
|
||||||
if (slotStatus != null) {
|
if (slotStatus != null) {
|
||||||
if (SlotStatus.BOOKED.equals(slotStatus)) {
|
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
|
||||||
dto.setStatus(AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus()) ? "已取号" : "已预约");
|
dto.setStatus("已取号");
|
||||||
} else if (SlotStatus.STOPPED.equals(slotStatus)) {
|
} else if (SlotStatus.BOOKED.equals(slotStatus)) {
|
||||||
dto.setStatus("已停诊"); // 视业务可改回已取消
|
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 {
|
} else {
|
||||||
dto.setStatus("未预约");
|
dto.setStatus("未预约");
|
||||||
}
|
}
|
||||||
@@ -355,14 +453,11 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
if (patient != null) {
|
if (patient != null) {
|
||||||
Integer genderEnum = patient.getGenderEnum();
|
Integer genderEnum = patient.getGenderEnum();
|
||||||
if (genderEnum != null) {
|
if (genderEnum != null) {
|
||||||
switch (genderEnum) {
|
if (Integer.valueOf(1).equals(genderEnum)) {
|
||||||
case 1:
|
|
||||||
dto.setGender("男");
|
dto.setGender("男");
|
||||||
break;
|
} else if (Integer.valueOf(2).equals(genderEnum)) {
|
||||||
case 2:
|
|
||||||
dto.setGender("女");
|
dto.setGender("女");
|
||||||
break;
|
} else {
|
||||||
default:
|
|
||||||
dto.setGender("未知");
|
dto.setGender("未知");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,4 +99,15 @@ public class TicketDto {
|
|||||||
*/
|
*/
|
||||||
@JsonSerialize(using = ToStringSerializer.class)
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
private Long doctorId;
|
private Long doctorId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 真实患者ID(数据库主键,区别于 patientId 存的就诊卡号)
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long realPatientId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 身份证号
|
||||||
|
*/
|
||||||
|
private String idCard;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ import com.openhis.common.enums.ybenums.YbPayment;
|
|||||||
import com.openhis.common.utils.EnumUtils;
|
import com.openhis.common.utils.EnumUtils;
|
||||||
import com.openhis.common.utils.HisPageUtils;
|
import com.openhis.common.utils.HisPageUtils;
|
||||||
import com.openhis.common.utils.HisQueryUtils;
|
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.PaymentReconciliation;
|
||||||
import com.openhis.financial.domain.RefundLog;
|
import com.openhis.financial.domain.RefundLog;
|
||||||
import com.openhis.financial.service.IRefundLogService;
|
import com.openhis.financial.service.IRefundLogService;
|
||||||
@@ -48,6 +52,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -97,6 +102,15 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
@Resource
|
@Resource
|
||||||
IRefundLogService iRefundLogService;
|
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);
|
recordRefundLog(cancelRegPaymentDto, byId, result, paymentRecon);
|
||||||
|
|
||||||
@@ -399,6 +418,74 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
return R.ok("已取消挂号");
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 补打挂号
|
* 补打挂号
|
||||||
* 补打挂号不需要修改数据库,只需要返回成功即可,前端已有所有需要的数据用于打印
|
* 补打挂号不需要修改数据库,只需要返回成功即可,前端已有所有需要的数据用于打印
|
||||||
|
|||||||
@@ -769,15 +769,21 @@ public class CommonConstants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 号源槽位状态 (adm_schedule_slot.slot_status)
|
* 号源槽位状态 (adm_schedule_slot.status)
|
||||||
*/
|
*/
|
||||||
public interface SlotStatus {
|
public interface SlotStatus {
|
||||||
/** 可用 / 待预约 */
|
/** 可用 / 待预约 */
|
||||||
Integer AVAILABLE = 0;
|
Integer AVAILABLE = 0;
|
||||||
/** 已预约 */
|
/** 已预约 */
|
||||||
Integer BOOKED = 1;
|
Integer BOOKED = 1;
|
||||||
/** 已停诊 / 已失效 */
|
/** 已取消 / 已停诊 */
|
||||||
Integer STOPPED = 2;
|
Integer CANCELLED = 2;
|
||||||
|
/** 已锁定 */
|
||||||
|
Integer LOCKED = 3;
|
||||||
|
/** 已签到 / 已取号 */
|
||||||
|
Integer CHECKED_IN = 4;
|
||||||
|
/** 已退号 */
|
||||||
|
Integer RETURNED = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -790,6 +796,8 @@ public class CommonConstants {
|
|||||||
Integer CHECKED_IN = 2;
|
Integer CHECKED_IN = 2;
|
||||||
/** 已取消 */
|
/** 已取消 */
|
||||||
Integer CANCELLED = 3;
|
Integer CANCELLED = 3;
|
||||||
|
/** 已退号 */
|
||||||
|
Integer RETURNED = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import lombok.EqualsAndHashCode;
|
|||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 号源池明细Entity
|
* 号源池明细Entity
|
||||||
*
|
*
|
||||||
@@ -29,7 +31,7 @@ public class ScheduleSlot extends HisBaseEntity {
|
|||||||
/** 序号 */
|
/** 序号 */
|
||||||
private Integer seqNo;
|
private Integer seqNo;
|
||||||
|
|
||||||
/** 序号状态: 0-可用,1-已预约,2-已取消,3-已过期等 */
|
/** 序号状态: 0-可用,1-已预约,2-已取消/已停诊,3-已锁定,4-已签到,5-已退号 */
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
/** 预约订单ID */
|
/** 预约订单ID */
|
||||||
@@ -37,4 +39,7 @@ public class ScheduleSlot extends HisBaseEntity {
|
|||||||
|
|
||||||
/** 预计叫号时间 */
|
/** 预计叫号时间 */
|
||||||
private LocalTime expectTime;
|
private LocalTime expectTime;
|
||||||
|
|
||||||
|
/** 签到时间 */
|
||||||
|
private Date checkInTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,13 @@ public class TicketSlotDTO {
|
|||||||
private Long patientId;
|
private Long patientId;
|
||||||
private String phone;
|
private String phone;
|
||||||
private Integer orderStatus;
|
private Integer orderStatus;
|
||||||
|
private Long orderId;
|
||||||
|
private String orderNo;
|
||||||
|
private String patientGender;
|
||||||
|
private Integer genderEnum;
|
||||||
|
private String idCard;
|
||||||
|
private String encounterId;
|
||||||
|
private String appointmentTime;
|
||||||
|
|
||||||
// 底层逻辑判断专属字段
|
// 底层逻辑判断专属字段
|
||||||
private Integer slotStatus;
|
private Integer slotStatus;
|
||||||
|
|||||||
@@ -37,4 +37,21 @@ public interface SchedulePoolMapper extends BaseMapper<SchedulePool> {
|
|||||||
AND p.delete_flag = '0'
|
AND p.delete_flag = '0'
|
||||||
""")
|
""")
|
||||||
int refreshPoolStats(@Param("poolId") Long poolId);
|
int refreshPoolStats(@Param("poolId") Long poolId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签到时更新号源池统计:锁定数-1,已预约数+1
|
||||||
|
*
|
||||||
|
* @param poolId 号源池ID
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Update("""
|
||||||
|
UPDATE adm_schedule_pool
|
||||||
|
SET locked_num = locked_num - 1,
|
||||||
|
booked_num = booked_num + 1,
|
||||||
|
update_time = NOW()
|
||||||
|
WHERE id = #{poolId}
|
||||||
|
AND locked_num > 0
|
||||||
|
AND delete_flag = '0'
|
||||||
|
""")
|
||||||
|
int updatePoolStatsOnCheckIn(@Param("poolId") Integer poolId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.openhis.appointmentmanage.domain.ScheduleSlot;
|
|||||||
import com.openhis.appointmentmanage.domain.TicketSlotDTO;
|
import com.openhis.appointmentmanage.domain.TicketSlotDTO;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.openhis.appointmentmanage.dto.TicketQueryDTO;
|
import com.openhis.appointmentmanage.dto.TicketQueryDTO;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
@@ -30,6 +31,16 @@ public interface ScheduleSlotMapper extends BaseMapper<ScheduleSlot> {
|
|||||||
*/
|
*/
|
||||||
int updateSlotStatus(@Param("slotId") Long slotId, @Param("status") Integer status);
|
int updateSlotStatus(@Param("slotId") Long slotId, @Param("status") Integer status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新槽位状态并记录签到时间
|
||||||
|
*
|
||||||
|
* @param slotId 槽位ID
|
||||||
|
* @param status 状态
|
||||||
|
* @param checkInTime 签到时间
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int updateSlotStatusAndCheckInTime(@Param("slotId") Long slotId, @Param("status") Integer status, @Param("checkInTime") Date checkInTime);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据槽位ID查询所属号源池ID。
|
* 根据槽位ID查询所属号源池ID。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import lombok.experimental.Accessors;
|
|||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
public class Order extends HisBaseEntity {
|
public class Order extends HisBaseEntity {
|
||||||
|
|
||||||
@TableId(type = IdType.ASSIGN_ID)
|
@TableId(type = IdType.AUTO)
|
||||||
@JsonSerialize(using = ToStringSerializer.class)
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
|
|||||||
@@ -32,4 +32,14 @@ public interface OrderMapper extends BaseMapper<Order> {
|
|||||||
int updateOrderStatusById(Long id, Integer status);
|
int updateOrderStatusById(Long id, Integer status);
|
||||||
|
|
||||||
int updateOrderCancelInfoById(Long id, Date cancelTime, String cancelReason);
|
int updateOrderCancelInfoById(Long id, Date cancelTime, String cancelReason);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新订单支付状态
|
||||||
|
*
|
||||||
|
* @param orderId 订单ID
|
||||||
|
* @param payStatus 支付状态:0-未支付,1-已支付
|
||||||
|
* @param payTime 支付时间
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int updatePayStatus(@Param("orderId") Long orderId, @Param("payStatus") Integer payStatus, @Param("payTime") Date payTime);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
Order order = new Order();
|
Order order = new Order();
|
||||||
|
order.setId(null); // 显式置空,确保触发数据库自增,避免 MP 预分配雪花 ID 的干扰
|
||||||
String orderNo = assignSeqUtil.getSeq(AssignSeqEnum.ORDER_NUM.getPrefix(), 18);
|
String orderNo = assignSeqUtil.getSeq(AssignSeqEnum.ORDER_NUM.getPrefix(), 18);
|
||||||
order.setOrderNo(orderNo);
|
order.setOrderNo(orderNo);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|||||||
import com.openhis.appointmentmanage.domain.AppointmentConfig;
|
import com.openhis.appointmentmanage.domain.AppointmentConfig;
|
||||||
import com.openhis.appointmentmanage.service.IAppointmentConfigService;
|
import com.openhis.appointmentmanage.service.IAppointmentConfigService;
|
||||||
import com.openhis.appointmentmanage.domain.TicketSlotDTO;
|
import com.openhis.appointmentmanage.domain.TicketSlotDTO;
|
||||||
|
import com.openhis.appointmentmanage.domain.ScheduleSlot;
|
||||||
import com.openhis.appointmentmanage.mapper.SchedulePoolMapper;
|
import com.openhis.appointmentmanage.mapper.SchedulePoolMapper;
|
||||||
import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper;
|
import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper;
|
||||||
import com.openhis.clinical.domain.Order;
|
import com.openhis.clinical.domain.Order;
|
||||||
@@ -52,6 +53,9 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
@Resource
|
@Resource
|
||||||
private SchedulePoolMapper schedulePoolMapper;
|
private SchedulePoolMapper schedulePoolMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private com.openhis.clinical.mapper.OrderMapper orderMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private IAppointmentConfigService appointmentConfigService;
|
private IAppointmentConfigService appointmentConfigService;
|
||||||
|
|
||||||
@@ -277,7 +281,7 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取号
|
* 取号(签到)
|
||||||
*
|
*
|
||||||
* @param slotId 槽位ID
|
* @param slotId 槽位ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
@@ -290,7 +294,24 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
throw new RuntimeException("当前号源没有可取号的预约订单");
|
throw new RuntimeException("当前号源没有可取号的预约订单");
|
||||||
}
|
}
|
||||||
Order latestOrder = orders.get(0);
|
Order latestOrder = orders.get(0);
|
||||||
return orderService.updateOrderStatusById(latestOrder.getId(), AppointmentOrderStatus.CHECKED_IN);
|
|
||||||
|
// 1. 更新订单状态为已取号,并更新支付状态和支付时间
|
||||||
|
orderService.updateOrderStatusById(latestOrder.getId(), AppointmentOrderStatus.CHECKED_IN);
|
||||||
|
// 更新支付状态为已支付,记录支付时间
|
||||||
|
orderMapper.updatePayStatus(latestOrder.getId(), 1, new Date());
|
||||||
|
|
||||||
|
// 2. 查询号源槽位信息
|
||||||
|
ScheduleSlot slot = scheduleSlotMapper.selectById(slotId);
|
||||||
|
|
||||||
|
// 3. 更新号源槽位状态为已签到,记录签到时间
|
||||||
|
scheduleSlotMapper.updateSlotStatusAndCheckInTime(slotId, SlotStatus.CHECKED_IN, new Date());
|
||||||
|
|
||||||
|
// 4. 更新号源池统计:锁定数-1,已预约数+1
|
||||||
|
if (slot != null && slot.getPoolId() != null) {
|
||||||
|
schedulePoolMapper.updatePoolStatsOnCheckIn(slot.getPoolId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -312,7 +333,7 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
orderService.cancelAppointmentOrder(order.getId(), "医生停诊");
|
orderService.cancelAppointmentOrder(order.getId(), "医生停诊");
|
||||||
}
|
}
|
||||||
|
|
||||||
int updated = scheduleSlotMapper.updateSlotStatus(slotId, SlotStatus.STOPPED);
|
int updated = scheduleSlotMapper.updateSlotStatus(slotId, SlotStatus.CANCELLED);
|
||||||
if (updated > 0) {
|
if (updated > 0) {
|
||||||
refreshPoolStatsBySlotId(slotId);
|
refreshPoolStatsBySlotId(slotId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,41 @@
|
|||||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="com.openhis.appointmentmanage.mapper.ScheduleSlotMapper">
|
<mapper namespace="com.openhis.appointmentmanage.mapper.ScheduleSlotMapper">
|
||||||
|
|
||||||
|
<!-- 统一状态值(兼容数字/英文字符串存储),输出 Integer,避免 resultType 映射 NumberFormatException -->
|
||||||
|
<sql id="slotStatusNormExpr">
|
||||||
|
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 ('3', 'locked') THEN 3
|
||||||
|
WHEN LOWER(CONCAT('', s.status)) IN ('4', 'checked', 'checked_in', 'checkin') THEN 4
|
||||||
|
WHEN LOWER(CONCAT('', s.status)) IN ('5', 'returned') THEN 5
|
||||||
|
ELSE NULL
|
||||||
|
END
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<sql id="orderStatusNormExpr">
|
||||||
|
CASE
|
||||||
|
WHEN LOWER(CONCAT('', o.status)) IN ('1', 'booked') THEN 1
|
||||||
|
WHEN LOWER(CONCAT('', o.status)) IN ('2', 'checked', 'checked_in', 'checkin') THEN 2
|
||||||
|
WHEN LOWER(CONCAT('', o.status)) IN ('3', 'cancelled', 'canceled') THEN 3
|
||||||
|
WHEN LOWER(CONCAT('', o.status)) IN ('4', 'returned') THEN 4
|
||||||
|
ELSE NULL
|
||||||
|
END
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<sql id="poolStatusNormExpr">
|
||||||
|
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 ('3', 'locked') THEN 3
|
||||||
|
WHEN LOWER(CONCAT('', p.status)) IN ('4', 'checked', 'checked_in', 'checkin') THEN 4
|
||||||
|
WHEN LOWER(CONCAT('', p.status)) IN ('5', 'returned') THEN 5
|
||||||
|
ELSE NULL
|
||||||
|
END
|
||||||
|
</sql>
|
||||||
|
|
||||||
<!-- 注意这里的 resultType 指向了您刚建好的 DTO 实体类 -->
|
<!-- 注意这里的 resultType 指向了您刚建好的 DTO 实体类 -->
|
||||||
<select id="selectAllTicketSlots" resultType="com.openhis.appointmentmanage.domain.TicketSlotDTO">
|
<select id="selectAllTicketSlots" resultType="com.openhis.appointmentmanage.domain.TicketSlotDTO">
|
||||||
SELECT
|
SELECT
|
||||||
@@ -16,12 +51,12 @@
|
|||||||
o.patient_name AS patientName,
|
o.patient_name AS patientName,
|
||||||
o.medical_card AS medicalCard,
|
o.medical_card AS medicalCard,
|
||||||
o.phone AS phone,
|
o.phone AS phone,
|
||||||
o.status AS orderStatus,
|
<include refid="orderStatusNormExpr" /> AS orderStatus,
|
||||||
s.status AS slotStatus,
|
<include refid="slotStatusNormExpr" /> AS slotStatus,
|
||||||
s.expect_time AS expectTime,
|
s.expect_time AS expectTime,
|
||||||
p.schedule_date AS scheduleDate,
|
p.schedule_date AS scheduleDate,
|
||||||
d.reg_type AS regType,
|
d.reg_type AS regType,
|
||||||
p.status AS poolStatus,
|
<include refid="poolStatusNormExpr" /> AS poolStatus,
|
||||||
p.stop_reason AS stopReason,
|
p.stop_reason AS stopReason,
|
||||||
d.is_stopped AS isStopped
|
d.is_stopped AS isStopped
|
||||||
FROM
|
FROM
|
||||||
@@ -39,7 +74,7 @@
|
|||||||
FROM
|
FROM
|
||||||
order_main
|
order_main
|
||||||
WHERE
|
WHERE
|
||||||
status IN (1, 2)
|
LOWER(CONCAT('', status)) IN ('1', '2', '4', 'booked', 'checked', 'checked_in', 'checkin', 'returned')
|
||||||
ORDER BY
|
ORDER BY
|
||||||
slot_id,
|
slot_id,
|
||||||
create_time DESC
|
create_time DESC
|
||||||
@@ -66,12 +101,18 @@
|
|||||||
o.patient_name AS patientName,
|
o.patient_name AS patientName,
|
||||||
o.medical_card AS medicalCard,
|
o.medical_card AS medicalCard,
|
||||||
o.phone AS phone,
|
o.phone AS phone,
|
||||||
o.status AS orderStatus,
|
o.id AS orderId,
|
||||||
s.status AS slotStatus,
|
o.order_no AS orderNo,
|
||||||
|
COALESCE(CAST(o.gender AS VARCHAR), CAST(pinfo.gender_enum AS VARCHAR)) AS patientGender,
|
||||||
|
pinfo.gender_enum AS genderEnum,
|
||||||
|
pinfo.id_card AS idCard,
|
||||||
|
o.appointment_time AS appointmentTime,
|
||||||
|
<include refid="orderStatusNormExpr" /> AS orderStatus,
|
||||||
|
<include refid="slotStatusNormExpr" /> AS slotStatus,
|
||||||
s.expect_time AS expectTime,
|
s.expect_time AS expectTime,
|
||||||
p.schedule_date AS scheduleDate,
|
p.schedule_date AS scheduleDate,
|
||||||
d.reg_type AS regType,
|
d.reg_type AS regType,
|
||||||
p.status AS poolStatus,
|
<include refid="poolStatusNormExpr" /> AS poolStatus,
|
||||||
p.stop_reason AS stopReason,
|
p.stop_reason AS stopReason,
|
||||||
d.is_stopped AS isStopped
|
d.is_stopped AS isStopped
|
||||||
FROM
|
FROM
|
||||||
@@ -87,15 +128,20 @@
|
|||||||
patient_name,
|
patient_name,
|
||||||
medical_card,
|
medical_card,
|
||||||
phone,
|
phone,
|
||||||
|
id,
|
||||||
|
order_no,
|
||||||
|
gender,
|
||||||
|
appointment_time,
|
||||||
status
|
status
|
||||||
FROM
|
FROM
|
||||||
order_main
|
order_main
|
||||||
WHERE
|
WHERE
|
||||||
status IN (1, 2)
|
LOWER(CONCAT('', status)) IN ('1', '2', '4', 'booked', 'checked', 'checked_in', 'checkin', 'returned')
|
||||||
ORDER BY
|
ORDER BY
|
||||||
slot_id,
|
slot_id,
|
||||||
create_time DESC
|
create_time DESC
|
||||||
) o ON o.slot_id = s.id
|
) o ON o.slot_id = s.id
|
||||||
|
LEFT JOIN adm_patient pinfo ON o.patient_id = pinfo.id
|
||||||
WHERE
|
WHERE
|
||||||
s.id = #{id}
|
s.id = #{id}
|
||||||
</select>
|
</select>
|
||||||
@@ -115,7 +161,7 @@
|
|||||||
UPDATE adm_schedule_slot
|
UPDATE adm_schedule_slot
|
||||||
SET
|
SET
|
||||||
status = #{status},
|
status = #{status},
|
||||||
<if test="status == 0">
|
<if test="status != null and '0'.equals(status.toString())">
|
||||||
order_id = NULL,
|
order_id = NULL,
|
||||||
</if>
|
</if>
|
||||||
update_time = now()
|
update_time = now()
|
||||||
@@ -124,10 +170,24 @@
|
|||||||
AND delete_flag = '0'
|
AND delete_flag = '0'
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<update id="updateSlotStatusAndCheckInTime">
|
||||||
|
UPDATE adm_schedule_slot
|
||||||
|
SET
|
||||||
|
status = #{status},
|
||||||
|
check_in_time = #{checkInTime},
|
||||||
|
update_time = NOW()
|
||||||
|
WHERE
|
||||||
|
id = #{slotId}
|
||||||
|
AND delete_flag = '0'
|
||||||
|
</update>
|
||||||
|
|
||||||
<select id="selectPoolIdBySlotId" resultType="java.lang.Long">
|
<select id="selectPoolIdBySlotId" resultType="java.lang.Long">
|
||||||
SELECT pool_id
|
SELECT
|
||||||
FROM adm_schedule_slot
|
pool_id
|
||||||
WHERE id = #{slotId}
|
FROM
|
||||||
|
adm_schedule_slot
|
||||||
|
WHERE
|
||||||
|
id = #{slotId}
|
||||||
AND delete_flag = '0'
|
AND delete_flag = '0'
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@@ -155,12 +215,18 @@
|
|||||||
o.patient_name AS patientName,
|
o.patient_name AS patientName,
|
||||||
o.medical_card AS medicalCard,
|
o.medical_card AS medicalCard,
|
||||||
o.phone AS phone,
|
o.phone AS phone,
|
||||||
o.status AS orderStatus,
|
o.id AS orderId,
|
||||||
s.status AS slotStatus,
|
o.order_no AS orderNo,
|
||||||
|
COALESCE(CAST(o.gender AS VARCHAR), CAST(pinfo.gender_enum AS VARCHAR)) AS patientGender,
|
||||||
|
pinfo.gender_enum AS genderEnum,
|
||||||
|
pinfo.id_card AS idCard,
|
||||||
|
o.appointment_time AS appointmentTime,
|
||||||
|
<include refid="orderStatusNormExpr" /> AS orderStatus,
|
||||||
|
<include refid="slotStatusNormExpr" /> AS slotStatus,
|
||||||
s.expect_time AS expectTime,
|
s.expect_time AS expectTime,
|
||||||
p.schedule_date AS scheduleDate,
|
p.schedule_date AS scheduleDate,
|
||||||
d.reg_type AS regType,
|
d.reg_type AS regType,
|
||||||
p.status AS poolStatus,
|
<include refid="poolStatusNormExpr" /> AS poolStatus,
|
||||||
p.stop_reason AS stopReason,
|
p.stop_reason AS stopReason,
|
||||||
d.is_stopped AS isStopped
|
d.is_stopped AS isStopped
|
||||||
FROM
|
FROM
|
||||||
@@ -176,15 +242,20 @@
|
|||||||
patient_name,
|
patient_name,
|
||||||
medical_card,
|
medical_card,
|
||||||
phone,
|
phone,
|
||||||
|
id,
|
||||||
|
order_no,
|
||||||
|
gender,
|
||||||
|
appointment_time,
|
||||||
status
|
status
|
||||||
FROM
|
FROM
|
||||||
order_main
|
order_main
|
||||||
WHERE
|
WHERE
|
||||||
status IN (1, 2)
|
LOWER(CONCAT('', status)) IN ('1', '2', '4', 'booked', 'checked', 'checked_in', 'checkin', 'returned')
|
||||||
ORDER BY
|
ORDER BY
|
||||||
slot_id,
|
slot_id,
|
||||||
create_time DESC
|
create_time DESC
|
||||||
) o ON o.slot_id = s.id
|
) o ON o.slot_id = s.id
|
||||||
|
LEFT JOIN adm_patient pinfo ON o.patient_id = pinfo.id
|
||||||
<where>
|
<where>
|
||||||
p.delete_flag = '0'
|
p.delete_flag = '0'
|
||||||
AND s.delete_flag = '0' <!-- 1. 按日期查 -->
|
AND s.delete_flag = '0' <!-- 1. 按日期查 -->
|
||||||
@@ -225,35 +296,49 @@
|
|||||||
<!-- 5. 核心:解答您疑问的 4 种业务状态的复合查询! -->
|
<!-- 5. 核心:解答您疑问的 4 种业务状态的复合查询! -->
|
||||||
<if test="query.status != null and query.status != '' and query.status != 'all'">
|
<if test="query.status != null and query.status != '' and query.status != 'all'">
|
||||||
<choose>
|
<choose>
|
||||||
<when test="query.status == 'unbooked'">
|
<when test="'unbooked'.equals(query.status) or '未预约'.equals(query.status)">
|
||||||
AND s.status = 0
|
AND <include refid="slotStatusNormExpr" /> = 0
|
||||||
AND (
|
AND (
|
||||||
d.is_stopped IS NULL
|
d.is_stopped IS NULL
|
||||||
OR d.is_stopped = FALSE
|
OR d.is_stopped = FALSE
|
||||||
)
|
)
|
||||||
</when>
|
</when>
|
||||||
<when test="query.status == 'booked'">
|
<when test="'booked'.equals(query.status) or '已预约'.equals(query.status)">
|
||||||
AND s.status = 1
|
AND <include refid="slotStatusNormExpr" /> = 1
|
||||||
AND o.status = 1
|
AND <include refid="orderStatusNormExpr" /> = 1
|
||||||
AND (
|
AND (
|
||||||
d.is_stopped IS NULL
|
d.is_stopped IS NULL
|
||||||
OR d.is_stopped = FALSE
|
OR d.is_stopped = FALSE
|
||||||
)
|
)
|
||||||
</when>
|
</when>
|
||||||
<when test="query.status == 'checked'">
|
<when test="'checked'.equals(query.status) or '已取号'.equals(query.status)">
|
||||||
AND s.status = 1
|
AND (
|
||||||
AND o.status = 2
|
<include refid="slotStatusNormExpr" /> = 4
|
||||||
|
OR (
|
||||||
|
<include refid="slotStatusNormExpr" /> = 1
|
||||||
|
AND <include refid="orderStatusNormExpr" /> = 2
|
||||||
|
)
|
||||||
|
)
|
||||||
AND (
|
AND (
|
||||||
d.is_stopped IS NULL
|
d.is_stopped IS NULL
|
||||||
OR d.is_stopped = FALSE
|
OR d.is_stopped = FALSE
|
||||||
)
|
)
|
||||||
</when>
|
</when>
|
||||||
<when test="query.status == 'cancelled'">
|
<when test="'cancelled'.equals(query.status) or '已停诊'.equals(query.status) or '已取消'.equals(query.status)">
|
||||||
AND (
|
AND (
|
||||||
s.status = 2
|
<include refid="slotStatusNormExpr" /> = 2
|
||||||
OR d.is_stopped = TRUE
|
OR d.is_stopped = TRUE
|
||||||
)
|
)
|
||||||
</when>
|
</when>
|
||||||
|
<when test="'returned'.equals(query.status) or '已退号'.equals(query.status)">
|
||||||
|
AND (
|
||||||
|
<include refid="slotStatusNormExpr" /> = 5
|
||||||
|
OR <include refid="orderStatusNormExpr" /> = 4
|
||||||
|
)
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
AND 1 = 2
|
||||||
|
</otherwise>
|
||||||
</choose>
|
</choose>
|
||||||
</if>
|
</if>
|
||||||
</where>
|
</where>
|
||||||
@@ -266,9 +351,22 @@
|
|||||||
SELECT
|
SELECT
|
||||||
p.doctor_id AS doctorId,
|
p.doctor_id AS doctorId,
|
||||||
p.doctor_name AS doctorName,
|
p.doctor_name AS doctorName,
|
||||||
COALESCE(SUM(GREATEST(COALESCE(p.total_quota, 0) - COALESCE(p.booked_num, 0) - COALESCE(p.locked_num, 0), 0)), 0) AS available,
|
COALESCE(
|
||||||
|
SUM(
|
||||||
|
GREATEST(
|
||||||
|
COALESCE(p.total_quota, 0) - COALESCE(p.booked_num, 0) - COALESCE(p.locked_num, 0),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS available,
|
||||||
CASE
|
CASE
|
||||||
WHEN MAX(CASE WHEN d.reg_type = 1 THEN 1 ELSE 0 END) = 1 THEN 'expert'
|
WHEN MAX(
|
||||||
|
CASE
|
||||||
|
WHEN d.reg_type = 1 THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) = 1 THEN 'expert'
|
||||||
ELSE 'general'
|
ELSE 'general'
|
||||||
END AS ticketType
|
END AS ticketType
|
||||||
FROM
|
FROM
|
||||||
@@ -290,7 +388,10 @@
|
|||||||
AND d.reg_type = 1
|
AND d.reg_type = 1
|
||||||
</when>
|
</when>
|
||||||
<when test="query.type == 'general'">
|
<when test="query.type == 'general'">
|
||||||
AND (d.reg_type != 1 OR d.reg_type IS NULL)
|
AND (
|
||||||
|
d.reg_type != 1
|
||||||
|
OR d.reg_type IS NULL
|
||||||
|
)
|
||||||
</when>
|
</when>
|
||||||
</choose>
|
</choose>
|
||||||
</if>
|
</if>
|
||||||
|
|||||||
@@ -127,7 +127,7 @@
|
|||||||
select * from order_main where slot_id = #{slotId} and status = 1
|
select * from order_main where slot_id = #{slotId} and status = 1
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<insert id="insertOrder" parameterType="com.openhis.clinical.domain.Order">
|
<insert id="insertOrder" parameterType="com.openhis.clinical.domain.Order" useGeneratedKeys="true" keyProperty="id">
|
||||||
insert into order_main
|
insert into order_main
|
||||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||||
<if test="orderNo != null and orderNo != ''">order_no,</if>
|
<if test="orderNo != null and orderNo != ''">order_no,</if>
|
||||||
@@ -225,6 +225,12 @@
|
|||||||
update order_main set status = 3, cancel_time = #{cancelTime}, cancel_reason = #{cancelReason} where id = #{id}
|
update order_main set status = 3, cancel_time = #{cancelTime}, cancel_reason = #{cancelReason} where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<update id="updatePayStatus">
|
||||||
|
update order_main
|
||||||
|
set pay_status = #{payStatus}, pay_time = #{payTime}, update_time = NOW()
|
||||||
|
where id = #{orderId}
|
||||||
|
</update>
|
||||||
|
|
||||||
<delete id="deleteOrderById">
|
<delete id="deleteOrderById">
|
||||||
delete from order_main where id = #{id}
|
delete from order_main where id = #{id}
|
||||||
</delete>
|
</delete>
|
||||||
|
|||||||
@@ -162,3 +162,61 @@ export const STATUS = {
|
|||||||
NORMAL: '0', // 正常/启用
|
NORMAL: '0', // 正常/启用
|
||||||
DISABLE: '1' // 停用
|
DISABLE: '1' // 停用
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 号源槽位状态(与后端 CommonConstants.SlotStatus 保持一致)
|
||||||
|
* adm_schedule_slot.status 字段
|
||||||
|
*/
|
||||||
|
export const SlotStatus = {
|
||||||
|
/** 可用 / 待预约 */
|
||||||
|
AVAILABLE: 0,
|
||||||
|
/** 已预约 */
|
||||||
|
BOOKED: 1,
|
||||||
|
/** 已取消 / 已停诊 */
|
||||||
|
CANCELLED: 2,
|
||||||
|
/** 已锁定 */
|
||||||
|
LOCKED: 3,
|
||||||
|
/** 已签到 / 已取号 */
|
||||||
|
CHECKED_IN: 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 号源槽位状态说明信息
|
||||||
|
*/
|
||||||
|
export const SlotStatusDescriptions = {
|
||||||
|
0: '未预约',
|
||||||
|
1: '已预约',
|
||||||
|
2: '已停诊',
|
||||||
|
3: '已锁定',
|
||||||
|
4: '已取号',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 号源槽位状态对应的CSS类名
|
||||||
|
*/
|
||||||
|
export const SlotStatusClassMap = {
|
||||||
|
'未预约': 'status-unbooked',
|
||||||
|
'已预约': 'status-booked',
|
||||||
|
'已取号': 'status-checked',
|
||||||
|
'已停诊': 'status-cancelled',
|
||||||
|
'已取消': 'status-cancelled',
|
||||||
|
'已锁定': 'status-locked',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取号源槽位状态的说明
|
||||||
|
* @param {number} value - 状态值
|
||||||
|
* @returns {string} - 说明信息
|
||||||
|
*/
|
||||||
|
export function getSlotStatusDescription(value) {
|
||||||
|
return SlotStatusDescriptions[value] || '未知状态';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取号源槽位状态对应的CSS类名
|
||||||
|
* @param {string} status - 状态说明
|
||||||
|
* @returns {string} - CSS类名
|
||||||
|
*/
|
||||||
|
export function getSlotStatusClass(status) {
|
||||||
|
return SlotStatusClassMap[status] || 'status-unbooked';
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
<option value="booked">已预约</option>
|
<option value="booked">已预约</option>
|
||||||
<option value="checked">已取号</option>
|
<option value="checked">已取号</option>
|
||||||
<option value="cancelled">已停诊</option>
|
<option value="cancelled">已停诊</option>
|
||||||
|
<option value="returned">已退号</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div id="patientSearch" class="patient-search">
|
<div id="patientSearch" class="patient-search">
|
||||||
@@ -254,6 +255,7 @@ const STATUS_CLASS_MAP = {
|
|||||||
'未预约': 'status-unbooked',
|
'未预约': 'status-unbooked',
|
||||||
'已预约': 'status-booked',
|
'已预约': 'status-booked',
|
||||||
'已取号': 'status-checked',
|
'已取号': 'status-checked',
|
||||||
|
'已退号': 'status-returned',
|
||||||
'已停诊': 'status-cancelled',
|
'已停诊': 'status-cancelled',
|
||||||
'已取消': 'status-cancelled'
|
'已取消': 'status-cancelled'
|
||||||
};
|
};
|
||||||
@@ -703,10 +705,36 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const records = payload.list || payload.records || [];
|
const records = payload.list || payload.records || [];
|
||||||
|
const filteredRecords = this.applyStatusFilter(records);
|
||||||
const total = Number(payload.total);
|
const total = Number(payload.total);
|
||||||
this.tickets = [...records];
|
this.tickets = [...filteredRecords];
|
||||||
this.allTickets = [...records];
|
this.allTickets = [...filteredRecords];
|
||||||
|
// 当按状态筛选时,优先使用前端过滤后的数量,避免后端状态未生效导致“显示全部”
|
||||||
|
if (this.selectedStatus && this.selectedStatus !== 'all') {
|
||||||
|
this.totalTickets = this.tickets.length;
|
||||||
|
} else {
|
||||||
this.totalTickets = Number.isFinite(total) ? total : this.tickets.length;
|
this.totalTickets = Number.isFinite(total) ? total : this.tickets.length;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
applyStatusFilter(records = []) {
|
||||||
|
if (!Array.isArray(records) || records.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (!this.selectedStatus || this.selectedStatus === 'all') {
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
const statusMap = {
|
||||||
|
unbooked: ['未预约'],
|
||||||
|
booked: ['已预约'],
|
||||||
|
checked: ['已取号'],
|
||||||
|
cancelled: ['已停诊', '已取消'],
|
||||||
|
returned: ['已退号']
|
||||||
|
};
|
||||||
|
const matchedStatusList = statusMap[this.selectedStatus] || [];
|
||||||
|
if (matchedStatusList.length === 0) {
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
return records.filter(item => matchedStatusList.includes(item?.status));
|
||||||
},
|
},
|
||||||
updateDoctorsListFromApi(doctorResponse) {
|
updateDoctorsListFromApi(doctorResponse) {
|
||||||
let doctorList = [];
|
let doctorList = [];
|
||||||
@@ -1376,6 +1404,11 @@ export default {
|
|||||||
color: #52c41a;
|
color: #52c41a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-returned {
|
||||||
|
background-color: #fff7e6;
|
||||||
|
color: #d46b08;
|
||||||
|
}
|
||||||
|
|
||||||
.status-cancelled {
|
.status-cancelled {
|
||||||
background-color: #fff1f0;
|
background-color: #fff1f0;
|
||||||
color: #ff4d4f;
|
color: #ff4d4f;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<div style="display: flex; align-items: center; width: 100%">
|
<div style="display: flex; align-items: center; width: 100%">
|
||||||
<span style="font-size: 16px; font-weight: bold; margin-right: 20px;">门诊挂号</span>
|
<span style="font-size: 16px; font-weight: bold; margin-right: 20px;">门诊挂号</span>
|
||||||
<div style="flex: 1; display: flex; justify-content: center; align-items: center;">
|
<div style="flex: 1; display: flex; justify-content: center; align-items: center;">
|
||||||
|
<el-button type="success" icon="Check" @click="handleCheckIn" size="small">预约签到</el-button>
|
||||||
<el-button type="primary" icon="Document" @click="goToPatientRecord" size="small">档案</el-button>
|
<el-button type="primary" icon="Document" @click="goToPatientRecord" size="small">档案</el-button>
|
||||||
<el-button type="primary" icon="Plus" @click="handleAddPatient" size="small">新建</el-button>
|
<el-button type="primary" icon="Plus" @click="handleAddPatient" size="small">新建</el-button>
|
||||||
<el-button type="primary" plain icon="Search" @click="handleSearch" size="small">查询</el-button>
|
<el-button type="primary" plain icon="Search" @click="handleSearch" size="small">查询</el-button>
|
||||||
@@ -15,7 +16,7 @@
|
|||||||
<el-button type="primary" plain @click="handleReadCard('03')" size="small">医保卡</el-button>
|
<el-button type="primary" plain @click="handleReadCard('03')" size="small">医保卡</el-button>
|
||||||
<el-button type="warning" plain icon="CircleClose" @click="handleClear" size="small">清空</el-button>
|
<el-button type="warning" plain icon="CircleClose" @click="handleClear" size="small">清空</el-button>
|
||||||
<el-button type="primary" icon="Plus" @click="handleAdd" size="small">保存挂号</el-button>
|
<el-button type="primary" icon="Plus" @click="handleAdd" size="small">保存挂号</el-button>
|
||||||
<el-button type="success" icon="Printer" @click="handleReprint" size="small">补打挂号</el-button>
|
<el-button type="info" icon="Printer" @click="handleReprint" size="small">补打挂号</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -615,6 +616,74 @@
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 预约签到患者选择弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="showCheckInPatientModal"
|
||||||
|
title="请选择预约的患者"
|
||||||
|
width="1200px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<div style="margin-bottom: 20px; display: flex; gap: 10px;">
|
||||||
|
<el-input
|
||||||
|
v-model="checkInSearchKey"
|
||||||
|
placeholder="输入患者姓名回车查询"
|
||||||
|
style="width: 400px"
|
||||||
|
@keyup.enter="loadCheckInPatientList"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" @click="loadCheckInPatientList">查询</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
v-loading="checkInLoading"
|
||||||
|
:data="checkInPatientList"
|
||||||
|
border
|
||||||
|
style="width: 100%"
|
||||||
|
@row-click="selectRow"
|
||||||
|
highlight-current-row
|
||||||
|
>
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
|
<el-table-column prop="patientId" label="就诊卡号" width="120" align="center" />
|
||||||
|
<el-table-column prop="patientName" label="姓名" width="120" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span style="color: #ff4d4f">{{ scope.row.patientName }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="gender" label="性别" width="80" align="center" />
|
||||||
|
<el-table-column label="证件类型" width="150" align="center">
|
||||||
|
<template #default>居民身份证</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="idCard" label="证件号码" width="200" align="center" />
|
||||||
|
<el-table-column prop="phone" label="手机号码" width="150" align="center" />
|
||||||
|
<el-table-column label="号源类型" width="100" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="scope.row.ticketType === 'expert' ? 'danger' : 'success'">
|
||||||
|
{{ scope.row.ticketType === 'expert' ? '专家号' : '普通号' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="fee" label="预约金额" width="100" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span style="font-weight: bold; color: #f5222d">¥{{ scope.row.fee }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="dateTime" label="就诊时间" width="180" align="center" />
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div style="margin-top: 20px; display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="checkInPage"
|
||||||
|
v-model:page-size="checkInLimit"
|
||||||
|
:total="checkInTotal"
|
||||||
|
layout="prev, pager, next"
|
||||||
|
@current-change="loadCheckInPatientList"
|
||||||
|
/>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="showCheckInPatientModal = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="confirmCheckIn" :disabled="!selectedCheckInPatient">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -632,6 +701,7 @@ import {
|
|||||||
returnRegister,
|
returnRegister,
|
||||||
updatePatientPhone,
|
updatePatientPhone,
|
||||||
} from './components/outpatientregistration';
|
} from './components/outpatientregistration';
|
||||||
|
import { listTicket, checkInTicket } from '@/api/appoinmentmanage/ticket';
|
||||||
import { invokeYbPlugin5000, invokeYbPlugin5001 } from '@/api/public';
|
import { invokeYbPlugin5000, invokeYbPlugin5001 } from '@/api/public';
|
||||||
import patientInfoDialog from './components/patientInfoDialog';
|
import patientInfoDialog from './components/patientInfoDialog';
|
||||||
import PatientAddDialog from './components/patientAddDialog';
|
import PatientAddDialog from './components/patientAddDialog';
|
||||||
@@ -644,7 +714,7 @@ import {handleColor} from '@/utils/his';
|
|||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
import {formatDateStr} from '@/utils/index';
|
import {formatDateStr} from '@/utils/index';
|
||||||
import {isValidCNPhoneNumber} from '../../../utils/validate';
|
import {isValidCNPhoneNumber} from '../../../utils/validate';
|
||||||
import {ElMessage} from 'element-plus';
|
import {ElMessage, ElMessageBox} from 'element-plus';
|
||||||
import {hiprint} from 'vue-plugin-hiprint';
|
import {hiprint} from 'vue-plugin-hiprint';
|
||||||
import outpatientRegistrationTemplate from '@/components/Print/OutpatientRegistration.json';
|
import outpatientRegistrationTemplate from '@/components/Print/OutpatientRegistration.json';
|
||||||
|
|
||||||
@@ -687,14 +757,25 @@ const ybTypeRef = ref(null);
|
|||||||
const openDialog = ref(false);
|
const openDialog = ref(false);
|
||||||
const openRefundDialog = ref(false);
|
const openRefundDialog = ref(false);
|
||||||
const openReprintDialog = ref(false);
|
const openReprintDialog = ref(false);
|
||||||
|
|
||||||
|
// 预约签到相关变量
|
||||||
|
const showCheckInPatientModal = ref(false);
|
||||||
|
const checkInPatientList = ref([]);
|
||||||
|
const selectedCheckInPatient = ref(null);
|
||||||
const totalAmount = ref(0);
|
const totalAmount = ref(0);
|
||||||
const chargeItemIdList = ref([]);
|
const chargeItemIdList = ref([]);
|
||||||
const chrgBchnoList = ref([]);
|
const chrgBchnoList = ref([]);
|
||||||
const paymentId = ref('');
|
const paymentId = ref('');
|
||||||
const loadingText = ref('');
|
const loadingText = ref('');
|
||||||
|
const checkInSearchKey = ref('');
|
||||||
|
const checkInPage = ref(1);
|
||||||
|
const checkInLimit = ref(10);
|
||||||
|
const checkInTotal = ref(0);
|
||||||
|
const checkInLoading = ref(false);
|
||||||
const registerInfo = ref({}); // 原挂号记录信息
|
const registerInfo = ref({}); // 原挂号记录信息
|
||||||
const queryType = ref('all'); // 查询类型:all-全部, normal-正常挂号, returned-退号记录
|
const queryType = ref('all'); // 查询类型:all-全部, normal-正常挂号, returned-退号记录
|
||||||
const guardianAgeConfig = ref(''); // 监护人规定年龄配置
|
const guardianAgeConfig = ref(''); // 监护人规定年龄配置
|
||||||
|
const currentSlotId = ref(null); // 当前预约签到的号源ID
|
||||||
|
|
||||||
// 使用 ref 定义查询所得用户信息数据
|
// 使用 ref 定义查询所得用户信息数据
|
||||||
const patientInfoList = ref(undefined);
|
const patientInfoList = ref(undefined);
|
||||||
@@ -1584,6 +1665,189 @@ function handleReprint() {
|
|||||||
openReprintDialog.value = true;
|
openReprintDialog.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 预约签到 - 打开患者选择弹窗 */
|
||||||
|
function handleCheckIn() {
|
||||||
|
// 打开患者选择弹窗,显示已预约但未签到的患者列表
|
||||||
|
showCheckInPatientModal.value = true;
|
||||||
|
// 加载已预约未签到的患者列表
|
||||||
|
loadCheckInPatientList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 加载预约签到患者列表 */
|
||||||
|
function loadCheckInPatientList() {
|
||||||
|
checkInLoading.value = true;
|
||||||
|
const today = formatDateStr(new Date(), 'YYYY-MM-DD');
|
||||||
|
listTicket({
|
||||||
|
date: today,
|
||||||
|
status: 'booked',
|
||||||
|
name: checkInSearchKey.value, // 支持姓名等模糊查询,后端需适配
|
||||||
|
page: checkInPage.value,
|
||||||
|
limit: checkInLimit.value
|
||||||
|
}).then(res => {
|
||||||
|
const data = res.data?.list || res.list || res.data || [];
|
||||||
|
const total = res.data?.total || res.total || data.length;
|
||||||
|
|
||||||
|
checkInPatientList.value = data.map(item => ({
|
||||||
|
...item,
|
||||||
|
appointmentDate: item.scheduleDate + ' ' + (item.expectTime || '')
|
||||||
|
}));
|
||||||
|
checkInTotal.value = total;
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('加载预约导出失败:', err);
|
||||||
|
ElMessage.error('获取预约列表失败');
|
||||||
|
}).finally(() => {
|
||||||
|
checkInLoading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 弹窗行点击处理 */
|
||||||
|
function selectRow(row) {
|
||||||
|
selectedCheckInPatient.value = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 确认签到(一键签到:直接构建挂号参数 → 预结算 → 弹收费窗口) */
|
||||||
|
async function confirmCheckIn() {
|
||||||
|
if (!selectedCheckInPatient.value) {
|
||||||
|
ElMessage.warning('请先选择患者');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const patient = selectedCheckInPatient.value;
|
||||||
|
// 每次开始新的签到流程先清理残留 slotId,避免历史脏值串单
|
||||||
|
currentSlotId.value = null;
|
||||||
|
|
||||||
|
// 弹出确认提示
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
`确认为患者【${patient.patientName}】办理签到挂号?\n` +
|
||||||
|
`科室:${patient.department || '-'}\n` +
|
||||||
|
`医生:${patient.doctor || '-'}\n` +
|
||||||
|
`费用:¥${patient.fee || '0.00'}`,
|
||||||
|
'签到确认',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确认签到',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'info',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
// 用户点了取消
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showCheckInPatientModal.value = false;
|
||||||
|
|
||||||
|
readCardLoading.value = true;
|
||||||
|
loadingText.value = '正在处理签到挂号...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 用科室ID加载该科室的挂号类型列表,获取 serviceTypeId 和 definitionId
|
||||||
|
const healthcareRes = await getHealthcareMetadata({ organizationId: patient.departmentId });
|
||||||
|
const healthcareRecords = healthcareRes.data?.records || [];
|
||||||
|
|
||||||
|
if (healthcareRecords.length === 0) {
|
||||||
|
ElMessage.error('该科室未配置挂号类型,无法自动签到');
|
||||||
|
readCardLoading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 按号源类型(专家/普通)模糊匹配挂号类型
|
||||||
|
const matchTypeName = (patient.ticketType === 'expert') ? '专家' : '普通';
|
||||||
|
const matchedService = healthcareRecords.find(h => h.name && h.name.includes(matchTypeName));
|
||||||
|
|
||||||
|
if (!matchedService) {
|
||||||
|
// 匹配不到就取第一个作为兜底
|
||||||
|
ElMessage.warning('未精确匹配到挂号类型,已使用默认类型');
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = matchedService || healthcareRecords[0];
|
||||||
|
const realPatientId = patient.realPatientId; // 后端新增的真实患者数据库ID
|
||||||
|
|
||||||
|
if (!realPatientId) {
|
||||||
|
ElMessage.error('患者ID缺失,请联系管理员检查预约数据');
|
||||||
|
readCardLoading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 构建挂号参数(与 transformFormData 结构一致)
|
||||||
|
const registrationParam = {
|
||||||
|
encounterFormData: {
|
||||||
|
patientId: realPatientId,
|
||||||
|
priorityEnum: 3, // 默认优先级
|
||||||
|
serviceTypeId: service.id,
|
||||||
|
organizationId: patient.departmentId,
|
||||||
|
},
|
||||||
|
encounterLocationFormData: {
|
||||||
|
locationId: null,
|
||||||
|
},
|
||||||
|
encounterParticipantFormData: {
|
||||||
|
practitionerId: patient.doctorId,
|
||||||
|
},
|
||||||
|
accountFormData: {
|
||||||
|
patientId: realPatientId,
|
||||||
|
typeCode: 1, // 个人现金账户
|
||||||
|
contractNo: '0000', // 默认自费
|
||||||
|
},
|
||||||
|
chargeItemFormData: {
|
||||||
|
patientId: realPatientId,
|
||||||
|
definitionId: service.definitionId,
|
||||||
|
serviceId: service.id,
|
||||||
|
totalPrice: parseFloat(patient.fee) || ((service.price || 0) + (service.activityPrice || 0)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 4. 设置 patientInfo(ChargeDialog 需要展示)
|
||||||
|
patientInfo.value = {
|
||||||
|
patientId: realPatientId,
|
||||||
|
patientName: patient.patientName,
|
||||||
|
genderEnum_enumText: patient.gender || '-',
|
||||||
|
age: '',
|
||||||
|
contractName: '自费',
|
||||||
|
idCard: patient.idCard,
|
||||||
|
phone: patient.phone,
|
||||||
|
categoryEnum: '门诊',
|
||||||
|
organizationName: patient.department || '',
|
||||||
|
practitionerName: patient.doctor || '',
|
||||||
|
healthcareName: service.name || '',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 同步设置 form 的 contractNo,ChargeDialog 的 feeType 会读取它
|
||||||
|
form.value.contractNo = '0000';
|
||||||
|
|
||||||
|
// 5. 调用预结算接口(reg-pre-pay)
|
||||||
|
const res = await addOutpatientRegistration(registrationParam);
|
||||||
|
|
||||||
|
if (res.code == 200) {
|
||||||
|
// 仅在预结算成功后记录待签到的号源,避免失败路径残留脏数据
|
||||||
|
currentSlotId.value = patient.slot_id;
|
||||||
|
|
||||||
|
// 6. 设置收费弹窗所需的数据
|
||||||
|
chrgBchno.value = res.data.chrgBchno;
|
||||||
|
registerBusNo.value = res.data.busNo;
|
||||||
|
totalAmount.value = res.data.psnCashPay;
|
||||||
|
patientInfo.value.encounterId = res.data.encounterId || '';
|
||||||
|
patientInfo.value.busNo = res.data.busNo || '';
|
||||||
|
transformedData.value = registrationParam;
|
||||||
|
chargeItemIdList.value = [];
|
||||||
|
|
||||||
|
// 7. 打开收费弹窗
|
||||||
|
openDialog.value = true;
|
||||||
|
|
||||||
|
// 打印挂号单
|
||||||
|
printRegistrationByHiprint(res.data);
|
||||||
|
} else {
|
||||||
|
currentSlotId.value = null;
|
||||||
|
ElMessage.error(res.msg || '预结算失败');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
currentSlotId.value = null;
|
||||||
|
console.error('预约签到失败:', err);
|
||||||
|
ElMessage.error('签到处理失败: ' + (err.message || '未知错误'));
|
||||||
|
} finally {
|
||||||
|
readCardLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 点击患者列表给表单赋值
|
* 点击患者列表给表单赋值
|
||||||
*/
|
*/
|
||||||
@@ -1656,20 +1920,29 @@ function handleClose(value) {
|
|||||||
proxy.$modal.msgSuccess('操作成功');
|
proxy.$modal.msgSuccess('操作成功');
|
||||||
// 更新患者手机号
|
// 更新患者手机号
|
||||||
updatePhone();
|
updatePhone();
|
||||||
// getList();
|
|
||||||
// reset();
|
// 先取出并清空,避免接口失败/取消等路径导致 slotId 残留污染下一单
|
||||||
// addOutpatientRegistration(transformedData.value).then((response) => {
|
const pendingSlotId = currentSlotId.value;
|
||||||
// reset();
|
currentSlotId.value = null;
|
||||||
// proxy.$modal.msgSuccess('新增成功');
|
|
||||||
// getList();
|
// 如果是预约签到的挂号,执行签到状态更新
|
||||||
// });
|
if (pendingSlotId) {
|
||||||
|
checkInTicket(pendingSlotId).then(() => {
|
||||||
|
console.log('预约状态已更新为已取号');
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('更新预约状态失败:', err);
|
||||||
|
ElMessage.error('预约状态更新失败,请手动签到');
|
||||||
|
});
|
||||||
|
}
|
||||||
} else if (value == 'cancel') {
|
} else if (value == 'cancel') {
|
||||||
|
currentSlotId.value = null;
|
||||||
// cancelRegister(patientInfo.value.encounterId).then((res) => {
|
// cancelRegister(patientInfo.value.encounterId).then((res) => {
|
||||||
// if (res.code == 200) {
|
// if (res.code == 200) {
|
||||||
// getList();
|
// getList();
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
} else {
|
} else {
|
||||||
|
currentSlotId.value = null;
|
||||||
openRefundDialog.value = false;
|
openRefundDialog.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user