232 预约管理-》门诊预约挂号:打开界面报错且无医生排班预约号源数据
This commit is contained in:
@@ -2,5 +2,5 @@
|
|||||||
"tools": {
|
"tools": {
|
||||||
"approvalMode": "yolo"
|
"approvalMode": "yolo"
|
||||||
},
|
},
|
||||||
"$version": 2
|
"$version": 3
|
||||||
}
|
}
|
||||||
6
.qwen/settings.json.orig
Normal file
6
.qwen/settings.json.orig
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"tools": {
|
||||||
|
"approvalMode": "yolo"
|
||||||
|
},
|
||||||
|
"$version": 2
|
||||||
|
}
|
||||||
@@ -2,7 +2,9 @@ package com.openhis.web.appointmentmanage.appservice;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.openhis.appointmentmanage.dto.TicketQueryDTO;
|
||||||
import com.openhis.web.appointmentmanage.dto.TicketDto;
|
import com.openhis.web.appointmentmanage.dto.TicketDto;
|
||||||
|
import com.openhis.appointmentmanage.dto.TicketQueryDTO;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -14,36 +16,52 @@ import java.util.Map;
|
|||||||
public interface ITicketAppService {
|
public interface ITicketAppService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 预约号源
|
* 分页查询门诊号源列表(真分页)
|
||||||
*
|
*
|
||||||
* @param params 预约参数
|
* @param query 查询参数
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
R<?> bookTicket(Map<String, Object> params);
|
R<?> listTicket(TicketQueryDTO query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询医生余号汇总(基于号源池,不受分页影响)
|
||||||
|
*
|
||||||
|
* @param query 查询参数
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
R<?> listDoctorAvailability(TicketQueryDTO query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预约号源
|
||||||
|
*
|
||||||
|
* @param dto 预约参数
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
R<?> bookTicket(com.openhis.appointmentmanage.domain.AppointmentBookDTO dto);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消预约
|
* 取消预约
|
||||||
*
|
*
|
||||||
* @param ticketId 号源ID
|
* @param slotId 槽位ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
R<?> cancelTicket(Long ticketId);
|
R<?> cancelTicket(Long slotId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取号
|
* 取号
|
||||||
*
|
*
|
||||||
* @param ticketId 号源ID
|
* @param slotId 槽位ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
R<?> checkInTicket(Long ticketId);
|
R<?> checkInTicket(Long slotId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 停诊
|
* 停诊
|
||||||
*
|
*
|
||||||
* @param ticketId 号源ID
|
* @param slotId 槽位ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
R<?> cancelConsultation(Long ticketId);
|
R<?> cancelConsultation(Long slotId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询所有号源(用于测试)
|
* 查询所有号源(用于测试)
|
||||||
|
|||||||
@@ -274,9 +274,9 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
|
|||||||
doctorSchedule.getLimitNumber())
|
doctorSchedule.getLimitNumber())
|
||||||
.set(doctorSchedule.getStopReason() != null, SchedulePool::getStopReason, doctorSchedule.getStopReason())
|
.set(doctorSchedule.getStopReason() != null, SchedulePool::getStopReason, doctorSchedule.getStopReason())
|
||||||
.set(doctorSchedule.getRegType() != null, SchedulePool::getRegType, String.valueOf(doctorSchedule.getRegType()))
|
.set(doctorSchedule.getRegType() != null, SchedulePool::getRegType, String.valueOf(doctorSchedule.getRegType()))
|
||||||
.set(doctorSchedule.getRegisterFee() != null, SchedulePool::getFee, doctorSchedule.getRegisterFee() / 100.0)
|
.set(doctorSchedule.getRegisterFee() != null, SchedulePool::getFee, Double.valueOf(doctorSchedule.getRegisterFee().toString()))
|
||||||
.set(doctorSchedule.getRegisterFee() != null, SchedulePool::getInsurancePrice,
|
.set(doctorSchedule.getRegisterFee() != null, SchedulePool::getInsurancePrice,
|
||||||
doctorSchedule.getRegisterFee() / 100.0)
|
Double.valueOf(doctorSchedule.getRegisterFee().toString()))
|
||||||
.update();
|
.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +306,7 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
|
|||||||
// 不设置available_num,因为它是数据库生成列
|
// 不设置available_num,因为它是数据库生成列
|
||||||
// pool.setAvailableNum(0); // 初始为0,稍后更新
|
// pool.setAvailableNum(0); // 初始为0,稍后更新
|
||||||
pool.setRegType(schedule.getRegisterItem() != null ? schedule.getRegisterItem() : "普通");
|
pool.setRegType(schedule.getRegisterItem() != null ? schedule.getRegisterItem() : "普通");
|
||||||
pool.setFee(schedule.getRegisterFee() != null ? schedule.getRegisterFee() / 100.0 : 0.0); // 假设数据库中以分为单位存储
|
pool.setFee(schedule.getRegisterFee() != null ? Double.valueOf(schedule.getRegisterFee().toString()) : 0.0); // 直接使用原始价格
|
||||||
pool.setInsurancePrice(pool.getFee()); // 医保价格暂时与原价相同
|
pool.setInsurancePrice(pool.getFee()); // 医保价格暂时与原价相同
|
||||||
// 暂时设置support_channel为空字符串,避免JSON类型问题
|
// 暂时设置support_channel为空字符串,避免JSON类型问题
|
||||||
pool.setSupportChannel("");
|
pool.setSupportChannel("");
|
||||||
@@ -359,7 +359,7 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
|
|||||||
// 不设置available_num,因为它是数据库生成列
|
// 不设置available_num,因为它是数据库生成列
|
||||||
// pool.setAvailableNum(0); // 初始为0,稍后更新
|
// pool.setAvailableNum(0); // 初始为0,稍后更新
|
||||||
pool.setRegType(schedule.getRegisterItem() != null ? schedule.getRegisterItem() : "普通");
|
pool.setRegType(schedule.getRegisterItem() != null ? schedule.getRegisterItem() : "普通");
|
||||||
pool.setFee(schedule.getRegisterFee() != null ? schedule.getRegisterFee() / 100.0 : 0.0); // 假设数据库中以分为单位存储
|
pool.setFee(schedule.getRegisterFee() != null ? Double.valueOf(schedule.getRegisterFee().toString()) : 0.0); // 直接使用原始价格
|
||||||
pool.setInsurancePrice(pool.getFee()); // 医保价格暂时与原价相同
|
pool.setInsurancePrice(pool.getFee()); // 医保价格暂时与原价相同
|
||||||
// 暂时设置support_channel为空字符串,避免JSON类型问题
|
// 暂时设置support_channel为空字符串,避免JSON类型问题
|
||||||
pool.setSupportChannel("");
|
pool.setSupportChannel("");
|
||||||
|
|||||||
@@ -4,28 +4,21 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
import com.openhis.administration.domain.Patient;
|
import com.openhis.administration.domain.Patient;
|
||||||
import com.openhis.administration.service.IPatientService;
|
import com.openhis.administration.service.IPatientService;
|
||||||
import com.openhis.appointmentmanage.domain.DoctorSchedule;
|
import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper;
|
||||||
import com.openhis.appointmentmanage.mapper.DoctorScheduleMapper;
|
|
||||||
import com.openhis.appointmentmanage.service.IDoctorScheduleService;
|
|
||||||
import com.openhis.clinical.domain.Order;
|
|
||||||
import com.openhis.clinical.domain.Ticket;
|
import com.openhis.clinical.domain.Ticket;
|
||||||
import com.openhis.clinical.mapper.OrderMapper;
|
|
||||||
import com.openhis.clinical.service.ITicketService;
|
import com.openhis.clinical.service.ITicketService;
|
||||||
import com.openhis.web.appointmentmanage.appservice.IDoctorScheduleAppService;
|
|
||||||
import com.openhis.web.appointmentmanage.appservice.ITicketAppService;
|
import com.openhis.web.appointmentmanage.appservice.ITicketAppService;
|
||||||
import com.openhis.web.appointmentmanage.dto.TicketDto;
|
import com.openhis.web.appointmentmanage.dto.TicketDto;
|
||||||
|
import com.openhis.common.constant.CommonConstants.SlotStatus;
|
||||||
|
import com.openhis.common.constant.CommonConstants.AppointmentOrderStatus;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.time.*;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 号源管理应用服务实现类
|
* 号源管理应用服务实现类
|
||||||
*
|
*
|
||||||
@@ -36,73 +29,41 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ITicketService ticketService;
|
private ITicketService ticketService;
|
||||||
|
@Resource
|
||||||
|
private ScheduleSlotMapper scheduleSlotMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private IPatientService patientService;
|
private IPatientService patientService;
|
||||||
@Resource
|
|
||||||
private IDoctorScheduleAppService doctorScheduleAppService;
|
|
||||||
@Resource
|
|
||||||
private DoctorScheduleMapper doctorScheduleMapper;
|
|
||||||
@Resource
|
|
||||||
private OrderMapper orderMapper;
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TicketAppServiceImpl.class);
|
private static final Logger log = LoggerFactory.getLogger(TicketAppServiceImpl.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 预约号源
|
* 预约号源 (重构版:精准锁定单一槽位)
|
||||||
*
|
*
|
||||||
* @param params 预约参数
|
* @param dto 预约参数
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public R<?> bookTicket(Map<String, Object> params) {
|
public R<?> bookTicket(com.openhis.appointmentmanage.domain.AppointmentBookDTO dto) {
|
||||||
// 1. 获取 ticketId 和 slotId
|
Long slotId = dto.getSlotId();
|
||||||
Long ticketId = null;
|
if (slotId == null) {
|
||||||
Long slotId = null;
|
return R.fail("参数校验失败:缺少排班槽位唯一标识");
|
||||||
if (params.get("ticketId") != null) {
|
|
||||||
ticketId = Long.valueOf(params.get("ticketId").toString());
|
|
||||||
}
|
}
|
||||||
if (params.get("slotId") != null) {
|
|
||||||
slotId = Long.valueOf(params.get("slotId").toString());
|
|
||||||
}
|
|
||||||
// 2. 参数校验
|
|
||||||
if (ticketId == null || slotId == null) {
|
|
||||||
return R.fail("参数错误:ticketId 或 slotId 不能为空");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 3. 执行原有的预约逻辑
|
int result = ticketService.bookTicket(dto);
|
||||||
int result = ticketService.bookTicket(params);
|
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
// 4. 预约成功后,更新排班表状态
|
return R.ok("预约成功!号源已安全锁定。");
|
||||||
DoctorSchedule schedule = new DoctorSchedule();
|
|
||||||
schedule.setId(slotId); // 对应 XML 中的 WHERE id = #{id}
|
|
||||||
schedule.setIsStopped(true); // 设置为已预约
|
|
||||||
schedule.setStopReason("booked"); // 设置停用原因
|
|
||||||
|
|
||||||
// 执行更新
|
|
||||||
int updateCount = doctorScheduleMapper.updateDoctorSchedule(schedule);
|
|
||||||
|
|
||||||
if (updateCount > 0) {
|
|
||||||
return R.ok("预约成功并已更新排班状态");
|
|
||||||
} else {
|
|
||||||
// 如果更新失败,可能需要根据业务逻辑决定是否回滚预约
|
|
||||||
return R.ok("预约成功,但排班状态更新失败");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return R.fail("预约失败");
|
|
||||||
}
|
}
|
||||||
|
return R.fail("预约挂单核发失败");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// e.printStackTrace();
|
log.error("大厅挂号捕获系统异常", e);
|
||||||
log.error(e.getMessage());
|
|
||||||
return R.fail("系统异常:" + e.getMessage());
|
return R.fail("系统异常:" + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消预约
|
* 取消预约 (重构版:精准释放单一槽位)
|
||||||
*
|
*
|
||||||
* @param slotId 医生排班ID
|
* @param slotId 医生槽位排班ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@@ -111,18 +72,8 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
return R.fail("参数错误");
|
return R.fail("参数错误");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ticketService.cancelTicket(slotId);
|
int result = ticketService.cancelTicket(slotId);
|
||||||
DoctorSchedule schedule = new DoctorSchedule();
|
return R.ok(result > 0 ? "取消成功,号源已重新释放回市场" : "取消失败");
|
||||||
schedule.setId(slotId); // 对应 WHERE id = #{id}
|
|
||||||
schedule.setIsStopped(false); // 设置为 false
|
|
||||||
schedule.setStopReason(""); // 将原因清空 (设为空字符串)
|
|
||||||
// 3. 调用自定义更新方法
|
|
||||||
int updateCount = doctorScheduleMapper.updateDoctorSchedule(schedule);
|
|
||||||
if (updateCount > 0) {
|
|
||||||
return R.ok("取消成功");
|
|
||||||
} else {
|
|
||||||
return R.ok("取消成功");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return R.fail(e.getMessage());
|
return R.fail(e.getMessage());
|
||||||
}
|
}
|
||||||
@@ -131,16 +82,16 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
/**
|
/**
|
||||||
* 取号
|
* 取号
|
||||||
*
|
*
|
||||||
* @param ticketId 号源ID
|
* @param slotId 槽位ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public R<?> checkInTicket(Long ticketId) {
|
public R<?> checkInTicket(Long slotId) {
|
||||||
if (ticketId == null) {
|
if (slotId == null) {
|
||||||
return R.fail("参数错误");
|
return R.fail("参数错误");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
int result = ticketService.checkInTicket(ticketId);
|
int result = ticketService.checkInTicket(slotId);
|
||||||
return R.ok(result > 0 ? "取号成功" : "取号失败");
|
return R.ok(result > 0 ? "取号成功" : "取号失败");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return R.fail(e.getMessage());
|
return R.fail(e.getMessage());
|
||||||
@@ -150,16 +101,16 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
/**
|
/**
|
||||||
* 停诊
|
* 停诊
|
||||||
*
|
*
|
||||||
* @param ticketId 号源ID
|
* @param slotId 槽位ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public R<?> cancelConsultation(Long ticketId) {
|
public R<?> cancelConsultation(Long slotId) {
|
||||||
if (ticketId == null) {
|
if (slotId == null) {
|
||||||
return R.fail("参数错误");
|
return R.fail("参数错误");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
int result = ticketService.cancelConsultation(ticketId);
|
int result = ticketService.cancelConsultation(slotId);
|
||||||
return R.ok(result > 0 ? "停诊成功" : "停诊失败");
|
return R.ok(result > 0 ? "停诊成功" : "停诊失败");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return R.fail(e.getMessage());
|
return R.fail(e.getMessage());
|
||||||
@@ -167,92 +118,184 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public R<?> listAllTickets() {
|
public R<?> listTicket(com.openhis.appointmentmanage.dto.TicketQueryDTO query) {
|
||||||
// 1. 从 AppService 获取排班数据
|
// 1. 防空指针处理
|
||||||
R<?> response = doctorScheduleAppService.getDoctorScheduleList();
|
if (query == null) {
|
||||||
// 获取返回的 List 数据 (假设 R.ok 里的数据是 List<DoctorSchedule>)
|
query = new com.openhis.appointmentmanage.dto.TicketQueryDTO();
|
||||||
List<DoctorSchedule> scheduleList = (List<DoctorSchedule>) response.getData();
|
}
|
||||||
|
|
||||||
// 2. 转换数据为 TicketDto
|
// 2. 构造 MyBatis 的分页对象 (传入前端给的当前页和每页条数)
|
||||||
List<TicketDto> tickets = new ArrayList<>();
|
com.baomidou.mybatisplus.extension.plugins.pagination.Page<com.openhis.appointmentmanage.domain.TicketSlotDTO> pageParam = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(
|
||||||
|
query.getPage(), query.getLimit());
|
||||||
|
|
||||||
if (scheduleList != null) {
|
// 3. 调用刚才写的底层动态 SQL 查询!
|
||||||
for (DoctorSchedule schedule : scheduleList) {
|
com.baomidou.mybatisplus.extension.plugins.pagination.Page<com.openhis.appointmentmanage.domain.TicketSlotDTO> rawPage = scheduleSlotMapper
|
||||||
|
.selectTicketSlotsPage(pageParam, query);
|
||||||
|
|
||||||
|
// 4. 将查出来的数据翻译为前端可以直接渲染的结构
|
||||||
|
java.util.List<TicketDto> tickets = new java.util.ArrayList<>();
|
||||||
|
if (rawPage.getRecords() != null) {
|
||||||
|
for (com.openhis.appointmentmanage.domain.TicketSlotDTO raw : rawPage.getRecords()) {
|
||||||
TicketDto dto = new TicketDto();
|
TicketDto dto = new TicketDto();
|
||||||
|
|
||||||
// 基础信息映射
|
// 基础字段映射
|
||||||
dto.setSlot_id(Long.valueOf(schedule.getId())); // Integer 转 Long
|
dto.setSlot_id(raw.getSlotId());
|
||||||
dto.setBusNo(String.valueOf(schedule.getId())); // 生成一个业务编号
|
dto.setBusNo(String.valueOf(raw.getSlotId()));
|
||||||
dto.setDepartment(String.valueOf(schedule.getDeptId())); // 如果有科室名建议关联查询,这里暂填ID
|
dto.setDoctor(raw.getDoctor());
|
||||||
dto.setDoctor(schedule.getDoctor());
|
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.setPhone(raw.getPhone());
|
||||||
|
|
||||||
// 号源类型处理:根据挂号项目判断是普通号还是专家号
|
// 号源类型处理 (底层是1,前端要的是expert)
|
||||||
String registerItem = schedule.getRegisterItem();
|
if (raw.getRegType() != null && raw.getRegType() == 1) {
|
||||||
if (registerItem != null && registerItem.contains("专家")) {
|
|
||||||
dto.setTicketType("expert");
|
dto.setTicketType("expert");
|
||||||
} else {
|
} else {
|
||||||
dto.setTicketType("general");
|
dto.setTicketType("general");
|
||||||
}
|
}
|
||||||
// 时间处理:格式化为日期+时间范围,如 "2025-12-01 08:00-12:00"
|
|
||||||
String currentDate = LocalDate.now().toString(); // 或者从schedule中获取具体日期
|
|
||||||
String timeRange = schedule.getStartTime() + "-" + schedule.getEndTime();
|
|
||||||
dto.setDateTime(currentDate + " " + timeRange);
|
|
||||||
LocalTime nowTime = LocalTime.now();
|
|
||||||
LocalTime endTime = schedule.getEndTime();
|
|
||||||
String stopReason1 = schedule.getStopReason();
|
|
||||||
if ("cancelled".equals(stopReason1)||(endTime != null && nowTime.isAfter(endTime))) {
|
|
||||||
dto.setStatus("已停诊");
|
|
||||||
}else if (Boolean.TRUE.equals(schedule.getIsStopped())) {
|
|
||||||
// 获取原因并处理可能的空值
|
|
||||||
String stopReason = schedule.getStopReason();
|
|
||||||
// 使用 .equals() 比较内容,并将常量放在前面防止空指针
|
|
||||||
if ("booked".equals(stopReason)) {
|
|
||||||
dto.setStatus("已预约");
|
|
||||||
// --- 新增:获取患者信息 ---
|
|
||||||
List<Order> Order = orderMapper.selectOrderBySlotId(Long.valueOf(schedule.getId()));
|
|
||||||
Order latestOrder=Order.get(0);
|
|
||||||
|
|
||||||
if (latestOrder != null) {
|
// 拼接就诊时间
|
||||||
dto.setPatientName(latestOrder.getPatientName());
|
if (raw.getScheduleDate() != null && raw.getExpectTime() != null) {
|
||||||
dto.setPatientId(String.valueOf(latestOrder.getPatientId()));
|
dto.setDateTime(raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
|
||||||
dto.setPhone(latestOrder.getPhone());
|
try {
|
||||||
}
|
dto.setAppointmentDate(
|
||||||
// -----------------------
|
new java.text.SimpleDateFormat("yyyy-MM-dd").parse(raw.getScheduleDate().toString()));
|
||||||
} else if ("checked".equals(stopReason)) {
|
} catch (Exception e) {
|
||||||
dto.setStatus("已取号");
|
dto.setAppointmentDate(new java.util.Date());
|
||||||
} else {
|
|
||||||
// 兜底逻辑:如果 is_stopped 为 true 但没有匹配到原因
|
|
||||||
dto.setStatus("不可预约");
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// is_stopped 为 false 或 null 时
|
|
||||||
dto.setStatus("未预约");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 费用处理 (挂号费 + 诊疗费)
|
// 精准状态翻译!把底层的1和2,翻译回前端能懂的中文
|
||||||
int totalFee = schedule.getRegisterFee() + schedule.getDiagnosisFee();
|
if (Boolean.TRUE.equals(raw.getIsStopped())) {
|
||||||
dto.setFee(String.valueOf(totalFee));
|
dto.setStatus("已停诊");
|
||||||
|
} else {
|
||||||
// 日期处理:LocalDateTime 转 Date
|
Integer slotStatus = raw.getSlotStatus();
|
||||||
if (schedule.getCreateTime() != null) {
|
if (slotStatus != null) {
|
||||||
// 1. 先转成 Instant
|
if (SlotStatus.BOOKED.equals(slotStatus)) {
|
||||||
Instant instant = schedule.getCreateTime().toInstant();
|
dto.setStatus(AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus()) ? "已取号" : "已预约");
|
||||||
// 2. 结合时区转成 ZonedDateTime
|
} else if (SlotStatus.STOPPED.equals(slotStatus)) {
|
||||||
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
|
dto.setStatus("已停诊");
|
||||||
// 3. 再转回 Date (如果 DTO 需要的是 Date)
|
} else {
|
||||||
dto.setAppointmentDate(Date.from(zdt.toInstant()));
|
dto.setStatus("未预约");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dto.setStatus("未预约");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tickets.add(dto);
|
tickets.add(dto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 封装分页响应结构
|
// 5. 按照前端组件需要的【真分页】格式进行包装,并返回
|
||||||
Map<String, Object> result = new HashMap<>();
|
java.util.Map<String, Object> result = new java.util.HashMap<>();
|
||||||
|
result.put("list", tickets);
|
||||||
|
result.put("total", rawPage.getTotal()); // 这个 total 就是底层用 COUNT(*) 算出来的真实总条数!
|
||||||
|
result.put("page", query.getPage());
|
||||||
|
result.put("limit", query.getLimit());
|
||||||
|
|
||||||
|
return R.ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<?> listDoctorAvailability(com.openhis.appointmentmanage.dto.TicketQueryDTO query) {
|
||||||
|
if (query == null) {
|
||||||
|
query = new com.openhis.appointmentmanage.dto.TicketQueryDTO();
|
||||||
|
}
|
||||||
|
|
||||||
|
java.util.List<com.openhis.appointmentmanage.domain.DoctorAvailabilityDTO> rawList = scheduleSlotMapper
|
||||||
|
.selectDoctorAvailabilitySummary(query);
|
||||||
|
java.util.List<java.util.Map<String, Object>> doctors = new java.util.ArrayList<>();
|
||||||
|
if (rawList != null) {
|
||||||
|
for (com.openhis.appointmentmanage.domain.DoctorAvailabilityDTO item : rawList) {
|
||||||
|
java.util.Map<String, Object> row = new java.util.HashMap<>();
|
||||||
|
String doctorName = item.getDoctorName();
|
||||||
|
Long doctorId = item.getDoctorId();
|
||||||
|
row.put("id", doctorId != null ? String.valueOf(doctorId) : doctorName);
|
||||||
|
row.put("name", doctorName);
|
||||||
|
row.put("available", item.getAvailable() == null ? 0 : item.getAvailable());
|
||||||
|
row.put("type", item.getTicketType() == null ? "general" : item.getTicketType());
|
||||||
|
doctors.add(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return R.ok(doctors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<?> listAllTickets() {
|
||||||
|
// 1. 调用最新的 Mapper,直接从数据库抽出我们半成品的 DTO(强类型!)
|
||||||
|
List<com.openhis.appointmentmanage.domain.TicketSlotDTO> rawDtos = scheduleSlotMapper.selectAllTicketSlots();
|
||||||
|
|
||||||
|
// 这是真正要发给前端展示的包裹外卖盒
|
||||||
|
List<TicketDto> tickets = new ArrayList<>();
|
||||||
|
|
||||||
|
if (rawDtos != null) {
|
||||||
|
for (com.openhis.appointmentmanage.domain.TicketSlotDTO raw : rawDtos) {
|
||||||
|
TicketDto dto = new TicketDto();
|
||||||
|
|
||||||
|
// --- 基础字段处理 ---
|
||||||
|
// 注意:这里已经变成了极其舒服的 .getSlotId() 方法调用,告别魔鬼字符串!
|
||||||
|
dto.setSlot_id(raw.getSlotId());
|
||||||
|
dto.setBusNo(String.valueOf(raw.getSlotId())); // 暂时借用真实槽位ID做唯一流水号
|
||||||
|
dto.setDoctor(raw.getDoctor());
|
||||||
|
dto.setDepartment(raw.getDepartmentName());
|
||||||
|
dto.setFee(raw.getFee());
|
||||||
|
dto.setPatientName(raw.getPatientName());
|
||||||
|
dto.setPatientId(raw.getPatientId() != null ? String.valueOf(raw.getPatientId()) : null);
|
||||||
|
dto.setPhone(raw.getPhone());
|
||||||
|
|
||||||
|
// --- 号源类型处理 (普通/专家) ---
|
||||||
|
// 改用底层 adm_doctor_schedule 传来的标准数字字典:0=普通,1=专家
|
||||||
|
if (raw.getRegType() != null && raw.getRegType() == 1) {
|
||||||
|
dto.setTicketType("expert");
|
||||||
|
} else {
|
||||||
|
dto.setTicketType("general");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 就诊时间严谨拼接 ---
|
||||||
|
// 拼接出来给前端展示的,如 "2026-03-20 08:30"
|
||||||
|
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()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
dto.setAppointmentDate(new java.util.Date());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 核心逻辑:精准状态分类 ---
|
||||||
|
// 第一关:底层硬性停诊拦截
|
||||||
|
if (Boolean.TRUE.equals(raw.getIsStopped())) {
|
||||||
|
dto.setStatus("已停诊");
|
||||||
|
} else {
|
||||||
|
// 第二关:看独立的细分槽位状态 (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("已停诊"); // 视业务可改回已取消
|
||||||
|
} else {
|
||||||
|
dto.setStatus("未预约");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dto.setStatus("未预约");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tickets.add(dto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 封装分页响应结构并吐给前端
|
||||||
|
java.util.Map<String, Object> result = new java.util.HashMap<>();
|
||||||
result.put("list", tickets);
|
result.put("list", tickets);
|
||||||
result.put("total", tickets.size());
|
result.put("total", tickets.size());
|
||||||
result.put("page", 1);
|
result.put("page", 1);
|
||||||
result.put("limit", 20);
|
result.put("limit", 20);
|
||||||
|
|
||||||
return R.ok(result);
|
return R.ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ package com.openhis.web.appointmentmanage.controller;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.annotation.Anonymous;
|
import com.core.common.annotation.Anonymous;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.openhis.appointmentmanage.domain.AppointmentBookDTO;
|
||||||
|
import com.openhis.appointmentmanage.dto.TicketQueryDTO;
|
||||||
import com.openhis.web.appointmentmanage.appservice.ITicketAppService;
|
import com.openhis.web.appointmentmanage.appservice.ITicketAppService;
|
||||||
import com.openhis.web.appointmentmanage.dto.TicketDto;
|
import com.openhis.web.appointmentmanage.dto.TicketDto;
|
||||||
|
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@@ -19,11 +23,35 @@ import java.util.Map;
|
|||||||
@RequestMapping("/appointment/ticket")
|
@RequestMapping("/appointment/ticket")
|
||||||
public class TicketController {
|
public class TicketController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询门诊号源列表 (带多条件过滤)
|
||||||
|
*
|
||||||
|
* @param query 查询条件
|
||||||
|
* @return 分页号源列表
|
||||||
|
*/
|
||||||
|
@Anonymous
|
||||||
|
@PostMapping("/list")
|
||||||
|
public R<?> listTicket(@RequestBody @Validated TicketQueryDTO query) {
|
||||||
|
return ticketAppService.listTicket(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询医生余号汇总(基于号源池,不受分页影响)
|
||||||
|
*
|
||||||
|
* @param query 查询条件
|
||||||
|
* @return 医生余号列表
|
||||||
|
*/
|
||||||
|
@Anonymous
|
||||||
|
@PostMapping("/doctorSummary")
|
||||||
|
public R<?> listDoctorAvailability(@RequestBody @Validated TicketQueryDTO query) {
|
||||||
|
return ticketAppService.listDoctorAvailability(query);
|
||||||
|
}
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ITicketAppService ticketAppService;
|
private ITicketAppService ticketAppService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询所有号源(用于测试)
|
* 查询所有号源
|
||||||
*
|
*
|
||||||
* @return 所有号源列表
|
* @return 所有号源列表
|
||||||
*/
|
*/
|
||||||
@@ -36,44 +64,44 @@ public class TicketController {
|
|||||||
/**
|
/**
|
||||||
* 预约号源
|
* 预约号源
|
||||||
*
|
*
|
||||||
* @param params 预约参数
|
* @param dto 预约参数
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@PostMapping("/book")
|
@PostMapping("/book")
|
||||||
public R<?> bookTicket(@RequestBody Map<String, Object> params) {
|
public R<?> bookTicket(@RequestBody @Validated AppointmentBookDTO dto) {
|
||||||
return ticketAppService.bookTicket(params);
|
return ticketAppService.bookTicket(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消预约
|
* 取消预约
|
||||||
*
|
*
|
||||||
* @param ticketId 号源ID
|
* @param slotId 槽位ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@PostMapping("/cancel")
|
@PostMapping("/cancel")
|
||||||
public R<?> cancelTicket(@RequestParam Long ticketId) {
|
public R<?> cancelTicket(@RequestParam Long slotId) {
|
||||||
return ticketAppService.cancelTicket(ticketId);
|
return ticketAppService.cancelTicket(slotId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取号
|
* 取号
|
||||||
*
|
*
|
||||||
* @param ticketId 号源ID
|
* @param slotId 槽位ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@PostMapping("/checkin")
|
@PostMapping("/checkin")
|
||||||
public R<?> checkInTicket(@RequestParam Long ticketId) {
|
public R<?> checkInTicket(@RequestParam Long slotId) {
|
||||||
return ticketAppService.checkInTicket(ticketId);
|
return ticketAppService.checkInTicket(slotId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 停诊
|
* 停诊
|
||||||
*
|
*
|
||||||
* @param ticketId 号源ID
|
* @param slotId 槽位ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@PostMapping("/cancelConsultation")
|
@PostMapping("/cancelConsultation")
|
||||||
public R<?> cancelConsultation(@RequestParam Long ticketId) {
|
public R<?> cancelConsultation(@RequestParam Long slotId) {
|
||||||
return ticketAppService.cancelConsultation(ticketId);
|
return ticketAppService.cancelConsultation(slotId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public class TicketDto {
|
|||||||
private String dateTime;
|
private String dateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 状态 (unbooked:未预约, booked:已预约, checked:已取号, cancelled:已取消, locked:已锁定)
|
* 状态
|
||||||
*/
|
*/
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
|||||||
@@ -768,4 +768,28 @@ public class CommonConstants {
|
|||||||
Integer ACCOUNT_DEVICE_TYPE = 6;
|
Integer ACCOUNT_DEVICE_TYPE = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 号源槽位状态 (adm_schedule_slot.slot_status)
|
||||||
|
*/
|
||||||
|
public interface SlotStatus {
|
||||||
|
/** 可用 / 待预约 */
|
||||||
|
Integer AVAILABLE = 0;
|
||||||
|
/** 已预约 */
|
||||||
|
Integer BOOKED = 1;
|
||||||
|
/** 已停诊 / 已失效 */
|
||||||
|
Integer STOPPED = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预约订单状态 (order_main.status)
|
||||||
|
*/
|
||||||
|
public interface AppointmentOrderStatus {
|
||||||
|
/** 已预约 (待就诊) */
|
||||||
|
Integer BOOKED = 1;
|
||||||
|
/** 已取号 (已就诊) */
|
||||||
|
Integer CHECKED_IN = 2;
|
||||||
|
/** 已取消 */
|
||||||
|
Integer CANCELLED = 3;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.openhis.appointmentmanage.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预约挂号提交表单 (防篡改设计)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AppointmentBookDTO implements Serializable {
|
||||||
|
|
||||||
|
@NotNull(message = "号源槽位ID不能为空")
|
||||||
|
private Long slotId;
|
||||||
|
|
||||||
|
// 兼容前端发来的旧字段,即使发了我们底层也不用,防报错接收
|
||||||
|
private Long ticketId;
|
||||||
|
|
||||||
|
private Long patientId;
|
||||||
|
|
||||||
|
@NotBlank(message = "患者姓名不能为空")
|
||||||
|
private String patientName;
|
||||||
|
|
||||||
|
@NotBlank(message = "就诊卡号不能为空")
|
||||||
|
private String medicalCard;
|
||||||
|
|
||||||
|
@NotBlank(message = "手机号不能为空")
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
private Integer gender;
|
||||||
|
|
||||||
|
// 前端传的 tenant_id,我们为了兼容它带下划线的写法
|
||||||
|
private Integer tenant_id;
|
||||||
|
|
||||||
|
// 前端还会强行发这俩危险字段,我们只管接收堵口子,到了后端全丢弃不用
|
||||||
|
private BigDecimal fee;
|
||||||
|
private String regType;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.openhis.appointmentmanage.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 医生余号汇总DTO(按号源池聚合)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DoctorAvailabilityDTO {
|
||||||
|
private Long doctorId;
|
||||||
|
private String doctorName;
|
||||||
|
private Integer available;
|
||||||
|
private String ticketType;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ public class ScheduleSlot extends HisBaseEntity {
|
|||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
/** 预约订单ID */
|
/** 预约订单ID */
|
||||||
private Integer orderId;
|
private Long orderId;
|
||||||
|
|
||||||
/** 预计叫号时间 */
|
/** 预计叫号时间 */
|
||||||
private LocalTime expectTime;
|
private LocalTime expectTime;
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.openhis.appointmentmanage.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 专门用于承接底层的号源池与具体槽位联查结果 (不对外暴露)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class TicketSlotDTO {
|
||||||
|
// 基础信息
|
||||||
|
private Long slotId;
|
||||||
|
private Long scheduleId;
|
||||||
|
private String doctor;
|
||||||
|
private Long doctorId;
|
||||||
|
private Long departmentId;
|
||||||
|
private String departmentName;
|
||||||
|
private String fee;
|
||||||
|
private String patientName;
|
||||||
|
private String medicalCard;
|
||||||
|
private Long patientId;
|
||||||
|
private String phone;
|
||||||
|
private Integer orderStatus;
|
||||||
|
|
||||||
|
// 底层逻辑判断专属字段
|
||||||
|
private Integer slotStatus;
|
||||||
|
private LocalTime expectTime;
|
||||||
|
private LocalDate scheduleDate;
|
||||||
|
private Integer regType;
|
||||||
|
private Integer poolStatus;
|
||||||
|
private String stopReason;
|
||||||
|
private Boolean isStopped;
|
||||||
|
|
||||||
|
public Boolean getIsStopped() {
|
||||||
|
return isStopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsStopped(Boolean isStopped) {
|
||||||
|
this.isStopped = isStopped;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.openhis.appointmentmanage.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 门诊预约挂号查询条件 DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class TicketQueryDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
// 看诊日期 (例如: 2026-03-25)
|
||||||
|
private String date;
|
||||||
|
|
||||||
|
// 号源状态 (unbooked, booked, checked, cancelled, all)
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
// 号源类型 (general: 普通号, expert: 专家号)
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
// 科室名称 (例如: 内科)
|
||||||
|
private String department;
|
||||||
|
|
||||||
|
// 医生ID
|
||||||
|
private Long doctorId;
|
||||||
|
|
||||||
|
// 患者姓名 (模糊搜索)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
// 就诊卡号 (模糊搜索)
|
||||||
|
private String card;
|
||||||
|
|
||||||
|
// 手机号 (模糊搜索)
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
// 当前页码 (默认第一页)
|
||||||
|
private Integer page = 1;
|
||||||
|
|
||||||
|
// 每页显示条数 (默认查20条)
|
||||||
|
private Integer limit = 20;
|
||||||
|
}
|
||||||
@@ -2,8 +2,39 @@ package com.openhis.appointmentmanage.mapper;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.openhis.appointmentmanage.domain.SchedulePool;
|
import com.openhis.appointmentmanage.domain.SchedulePool;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Update;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface SchedulePoolMapper extends BaseMapper<SchedulePool> {
|
public interface SchedulePoolMapper extends BaseMapper<SchedulePool> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按号源池实时重算统计值,避免并发场景下计数漂移。
|
||||||
|
*
|
||||||
|
* 说明:available_num 在当前项目中可能为数据库生成列,因此这里仅维护
|
||||||
|
* booked_num / locked_num,剩余号由数据库或查询逻辑计算。
|
||||||
|
*/
|
||||||
|
@Update("""
|
||||||
|
UPDATE adm_schedule_pool p
|
||||||
|
SET
|
||||||
|
booked_num = COALESCE((
|
||||||
|
SELECT COUNT(1)
|
||||||
|
FROM adm_schedule_slot s
|
||||||
|
WHERE s.pool_id = p.id
|
||||||
|
AND s.delete_flag = '0'
|
||||||
|
AND s.status = 1
|
||||||
|
), 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
|
||||||
|
), 0),
|
||||||
|
update_time = now()
|
||||||
|
WHERE p.id = #{poolId}
|
||||||
|
AND p.delete_flag = '0'
|
||||||
|
""")
|
||||||
|
int refreshPoolStats(@Param("poolId") Long poolId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,53 @@
|
|||||||
package com.openhis.appointmentmanage.mapper;
|
package com.openhis.appointmentmanage.mapper;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.openhis.appointmentmanage.domain.DoctorAvailabilityDTO;
|
||||||
import com.openhis.appointmentmanage.domain.ScheduleSlot;
|
import com.openhis.appointmentmanage.domain.ScheduleSlot;
|
||||||
|
import com.openhis.appointmentmanage.domain.TicketSlotDTO;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.openhis.appointmentmanage.dto.TicketQueryDTO;
|
||||||
|
import java.util.List;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface ScheduleSlotMapper extends BaseMapper<ScheduleSlot> {
|
public interface ScheduleSlotMapper extends BaseMapper<ScheduleSlot> {
|
||||||
//
|
// 多表查询排班信息展示来预约挂号
|
||||||
|
List<TicketSlotDTO> selectAllTicketSlots();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据槽位ID精确查出完整的聚合信息
|
||||||
|
*/
|
||||||
|
TicketSlotDTO selectTicketSlotById(@Param("id") Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原子抢占槽位:仅当当前状态=0(可用)时,更新为1(已预约)。
|
||||||
|
*/
|
||||||
|
int lockSlotForBooking(@Param("slotId") Long slotId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按主键更新槽位状态。
|
||||||
|
*/
|
||||||
|
int updateSlotStatus(@Param("slotId") Long slotId, @Param("status") Integer status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据槽位ID查询所属号源池ID。
|
||||||
|
*/
|
||||||
|
Long selectPoolIdBySlotId(@Param("slotId") Long slotId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预约成功后,回填对应订单ID到号源槽位。
|
||||||
|
*/
|
||||||
|
int bindOrderToSlot(@Param("slotId") Long slotId, @Param("orderId") Long orderId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带分页和动态条件过滤的真实查询接口
|
||||||
|
*/
|
||||||
|
Page<TicketSlotDTO> selectTicketSlotsPage(Page<TicketSlotDTO> page, @Param("query") TicketQueryDTO query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按号源池聚合医生余号(不受分页影响)。
|
||||||
|
*/
|
||||||
|
List<DoctorAvailabilityDTO> selectDoctorAvailabilitySummary(@Param("query") TicketQueryDTO query);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,10 +74,10 @@ public interface ITicketService extends IService<Ticket> {
|
|||||||
/**
|
/**
|
||||||
* 预约号源
|
* 预约号源
|
||||||
*
|
*
|
||||||
* @param params 预约参数
|
* @param dto 预约参数
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
int bookTicket(Map<String, Object> params);
|
int bookTicket(com.openhis.appointmentmanage.domain.AppointmentBookDTO dto);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消预约
|
* 取消预约
|
||||||
@@ -85,7 +85,7 @@ public interface ITicketService extends IService<Ticket> {
|
|||||||
* @param ticketId 号源ID
|
* @param ticketId 号源ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
int cancelTicket(Long ticketId);
|
int cancelTicket(Long slotId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取号
|
* 取号
|
||||||
@@ -93,7 +93,7 @@ public interface ITicketService extends IService<Ticket> {
|
|||||||
* @param ticketId 号源ID
|
* @param ticketId 号源ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
int checkInTicket(Long ticketId);
|
int checkInTicket(Long slotId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 停诊
|
* 停诊
|
||||||
@@ -101,5 +101,5 @@ public interface ITicketService extends IService<Ticket> {
|
|||||||
* @param ticketId 号源ID
|
* @param ticketId 号源ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
int cancelConsultation(Long ticketId);
|
int cancelConsultation(Long slotId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.core.common.utils.AssignSeqUtil;
|
import com.core.common.utils.AssignSeqUtil;
|
||||||
import com.openhis.clinical.domain.Order;
|
import com.openhis.clinical.domain.Order;
|
||||||
import com.openhis.clinical.domain.Ticket;
|
|
||||||
import com.openhis.clinical.mapper.OrderMapper;
|
import com.openhis.clinical.mapper.OrderMapper;
|
||||||
import com.openhis.clinical.mapper.TicketMapper;
|
|
||||||
import com.openhis.clinical.service.IOrderService;
|
import com.openhis.clinical.service.IOrderService;
|
||||||
|
import com.openhis.common.constant.CommonConstants.AppointmentOrderStatus;
|
||||||
import com.openhis.common.enums.AssignSeqEnum;
|
import com.openhis.common.enums.AssignSeqEnum;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -23,9 +22,6 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
@Resource
|
@Resource
|
||||||
private OrderMapper orderMapper;
|
private OrderMapper orderMapper;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private TicketMapper ticketMapper;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AssignSeqUtil assignSeqUtil;
|
private AssignSeqUtil assignSeqUtil;
|
||||||
|
|
||||||
@@ -86,25 +82,20 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Order createAppointmentOrder(Map<String, Object> params) {
|
public Order createAppointmentOrder(Map<String, Object> params) {
|
||||||
Long slotId = params.get("slotId") != null ? Long.valueOf(params.get("slotId").toString()) : null;
|
Long slotId = (Long) params.get("slotId");
|
||||||
if (slotId == null) {
|
if (slotId == null) {
|
||||||
throw new RuntimeException("号源ID不能为空");
|
throw new RuntimeException("号源ID不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ticket ticket = ticketMapper.selectTicketById(slotId);
|
|
||||||
if (ticket == null) {
|
|
||||||
throw new RuntimeException("号源不存在");
|
|
||||||
}
|
|
||||||
|
|
||||||
Order order = new Order();
|
Order order = new Order();
|
||||||
String orderNo = assignSeqUtil.getSeq(AssignSeqEnum.ORDER_NUM.getPrefix(), 18);
|
String orderNo = assignSeqUtil.getSeq(AssignSeqEnum.ORDER_NUM.getPrefix(), 18);
|
||||||
order.setOrderNo(orderNo);
|
order.setOrderNo(orderNo);
|
||||||
|
|
||||||
Long patientId = params.get("patientId") != null ? Long.valueOf(params.get("patientId").toString()) : null;
|
Long patientId = (Long) params.get("patientId");
|
||||||
String patientName = params.get("patientName") != null ? params.get("patientName").toString() : null;
|
String patientName = (String) params.get("patientName");
|
||||||
String medicalCard = params.get("medicalCard") != null ? params.get("medicalCard").toString() : null;
|
String medicalCard = (String) params.get("medicalCard");
|
||||||
String phone = params.get("phone") != null ? params.get("phone").toString() : null;
|
String phone = (String) params.get("phone");
|
||||||
Integer gender = params.get("gender") != null ? Integer.valueOf(params.get("gender").toString()) : null;
|
Integer gender = (Integer) params.get("gender");
|
||||||
|
|
||||||
order.setPatientId(patientId);
|
order.setPatientId(patientId);
|
||||||
order.setPatientName(patientName);
|
order.setPatientName(patientName);
|
||||||
@@ -113,28 +104,31 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
order.setGender(gender);
|
order.setGender(gender);
|
||||||
|
|
||||||
order.setSlotId(slotId);
|
order.setSlotId(slotId);
|
||||||
order.setDepartmentId(ticket.getDepartmentId());
|
order.setScheduleId((Long) params.get("scheduleId"));
|
||||||
order.setDepartmentName(ticket.getDepartment());
|
order.setDepartmentId((Long) params.get("departmentId"));
|
||||||
order.setDoctorId(ticket.getDoctorId());
|
order.setDepartmentName((String) params.get("departmentName"));
|
||||||
order.setDoctorName(ticket.getDoctor());
|
order.setDoctorId((Long) params.get("doctorId"));
|
||||||
|
order.setDoctorName((String) params.get("doctorName"));
|
||||||
|
|
||||||
String regType = params.get("regType") != null ? params.get("regType").toString() : "普通";
|
String regType = (String) params.get("regType");
|
||||||
order.setRegType(regType);
|
order.setRegType(regType != null ? regType : "普通");
|
||||||
|
|
||||||
BigDecimal fee = params.get("fee") != null ? new BigDecimal(params.get("fee").toString()) : BigDecimal.ZERO;
|
BigDecimal fee = parseFee(params.get("fee"));
|
||||||
order.setFee(fee);
|
order.setFee(fee != null ? fee : BigDecimal.ZERO);
|
||||||
|
|
||||||
Date appointmentDate = new Date();
|
// appointmentDate / appointmentTime 由调用方(TicketServiceImpl)在强类型上下文中
|
||||||
order.setAppointmentDate(appointmentDate);
|
// 提前合并为 java.util.Date,此处直接使用,不再重复做 LocalDate + LocalTime 类型转换。
|
||||||
order.setAppointmentTime(new Date());
|
Date appointmentDateTime = params.get("appointmentDate") instanceof Date
|
||||||
|
? (Date) params.get("appointmentDate")
|
||||||
order.setStatus(1);
|
: new Date(); // 兜底:正常业务不应走到这里
|
||||||
|
order.setAppointmentDate(appointmentDateTime);
|
||||||
|
order.setAppointmentTime(appointmentDateTime);
|
||||||
|
order.setStatus(AppointmentOrderStatus.BOOKED);
|
||||||
order.setPayStatus(0);
|
order.setPayStatus(0);
|
||||||
order.setVersion(0);
|
order.setVersion(0);
|
||||||
|
|
||||||
// 设置租户ID
|
// 设置租户ID
|
||||||
Integer tenantId = params.get("tenant_id") != null ? Integer.valueOf(params.get("tenant_id").toString()) : null;
|
order.setTenantId((Integer) params.get("tenant_id"));
|
||||||
order.setTenantId(tenantId);
|
|
||||||
|
|
||||||
order.setCreateTime(new Date());
|
order.setCreateTime(new Date());
|
||||||
order.setUpdateTime(new Date());
|
order.setUpdateTime(new Date());
|
||||||
@@ -144,16 +138,40 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BigDecimal parseFee(Object feeObj) {
|
||||||
|
if (feeObj == null) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
if (feeObj instanceof BigDecimal) {
|
||||||
|
return (BigDecimal) feeObj;
|
||||||
|
}
|
||||||
|
if (feeObj instanceof Number) {
|
||||||
|
return BigDecimal.valueOf(((Number) feeObj).doubleValue());
|
||||||
|
}
|
||||||
|
if (feeObj instanceof String) {
|
||||||
|
String feeStr = ((String) feeObj).trim();
|
||||||
|
if (feeStr.isEmpty()) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new BigDecimal(feeStr);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new RuntimeException("挂号费格式错误: " + feeStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException("挂号费类型错误: " + feeObj.getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int cancelAppointmentOrder(Long orderId, String cancelReason) {
|
public int cancelAppointmentOrder(Long orderId, String cancelReason) {
|
||||||
Order order = orderMapper.selectOrderById(orderId);
|
Order order = orderMapper.selectOrderById(orderId);
|
||||||
if (order == null) {
|
if (order == null) {
|
||||||
throw new RuntimeException("订单不存在");
|
throw new RuntimeException("订单不存在");
|
||||||
}
|
}
|
||||||
if (order.getStatus() == 3) {
|
if (AppointmentOrderStatus.CANCELLED.equals(order.getStatus())) {
|
||||||
throw new RuntimeException("订单已取消");
|
throw new RuntimeException("订单已取消");
|
||||||
}
|
}
|
||||||
if (order.getStatus() == 2) {
|
if (AppointmentOrderStatus.CHECKED_IN.equals(order.getStatus())) {
|
||||||
throw new RuntimeException("订单已完成,无法取消");
|
throw new RuntimeException("订单已完成,无法取消");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,27 @@ package com.openhis.clinical.service.impl;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.openhis.appointmentmanage.domain.TicketSlotDTO;
|
||||||
|
import com.openhis.appointmentmanage.mapper.SchedulePoolMapper;
|
||||||
|
import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper;
|
||||||
import com.openhis.clinical.domain.Order;
|
import com.openhis.clinical.domain.Order;
|
||||||
import com.openhis.clinical.domain.Ticket;
|
import com.openhis.clinical.domain.Ticket;
|
||||||
import com.openhis.clinical.mapper.TicketMapper;
|
import com.openhis.clinical.mapper.TicketMapper;
|
||||||
import com.openhis.clinical.service.IOrderService;
|
import com.openhis.clinical.service.IOrderService;
|
||||||
import com.openhis.clinical.service.ITicketService;
|
import com.openhis.clinical.service.ITicketService;
|
||||||
|
import com.openhis.common.constant.CommonConstants.AppointmentOrderStatus;
|
||||||
|
import com.openhis.common.constant.CommonConstants.SlotStatus;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -33,6 +43,12 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
@Resource
|
@Resource
|
||||||
private IOrderService orderService;
|
private IOrderService orderService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ScheduleSlotMapper scheduleSlotMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SchedulePoolMapper schedulePoolMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询号源列表
|
* 查询号源列表
|
||||||
*
|
*
|
||||||
@@ -47,7 +63,7 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
/**
|
/**
|
||||||
* 分页查询号源列表
|
* 分页查询号源列表
|
||||||
*
|
*
|
||||||
* @param page 分页参数
|
* @param page 分页参数
|
||||||
* @param ticket 号源信息
|
* @param ticket 号源信息
|
||||||
* @return 号源集合
|
* @return 号源集合
|
||||||
*/
|
*/
|
||||||
@@ -114,154 +130,183 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
/**
|
/**
|
||||||
* 预约号源
|
* 预约号源
|
||||||
*
|
*
|
||||||
* @param params 预约参数
|
* @param dto 预约参数
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public int bookTicket(Map<String, Object> params) {
|
public int bookTicket(com.openhis.appointmentmanage.domain.AppointmentBookDTO dto) {
|
||||||
Long ticketId = Long.valueOf(params.get("ticketId").toString());
|
Long slotId = dto.getSlotId();
|
||||||
Long patientId = params.get("patientId") != null ? Long.valueOf(params.get("patientId").toString()) : null;
|
|
||||||
String patientName = params.get("patientName") != null ? params.get("patientName").toString() : null;
|
|
||||||
String medicalCard = params.get("medicalCard") != null ? params.get("medicalCard").toString() : null;
|
|
||||||
String phone = params.get("phone") != null ? params.get("phone").toString() : null;
|
|
||||||
|
|
||||||
logger.debug("开始预约号源,ticketId: {}, patientId: {}, patientName: {}", ticketId, patientId, patientName);
|
logger.debug("开始执行纯净打单路线,slotId: {}, patientName: {}", slotId, dto.getPatientName());
|
||||||
|
|
||||||
Ticket ticket = ticketMapper.selectTicketById(ticketId);
|
// 1. 直查物理大底座!
|
||||||
if (ticket == null) {
|
TicketSlotDTO slot = scheduleSlotMapper.selectTicketSlotById(slotId);
|
||||||
logger.error("号源不存在,ticketId: {}", ticketId);
|
|
||||||
throw new RuntimeException("号源不存在");
|
if (slot == null) {
|
||||||
|
logger.error("安全拦截:号源底库核对失败,slotId: {}", slotId);
|
||||||
|
throw new RuntimeException("号源数据不存在");
|
||||||
|
}
|
||||||
|
if (slot.getSlotStatus() != null && !SlotStatus.AVAILABLE.equals(slot.getSlotStatus())) {
|
||||||
|
throw new RuntimeException("手慢了!该号源已刚刚被他人抢占");
|
||||||
|
}
|
||||||
|
if (Boolean.TRUE.equals(slot.getIsStopped())) {
|
||||||
|
throw new RuntimeException("该排班医生已停诊");
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("查询到号源信息,id: {}, status: {}, deleteFlag: {}", ticket.getId(), ticket.getStatus(), ticket.getDeleteFlag());
|
// 原子抢占:避免并发下同一槽位被重复预约
|
||||||
|
int lockRows = scheduleSlotMapper.lockSlotForBooking(slotId);
|
||||||
// 详细调试:检查状态字符串的详细信息
|
if (lockRows <= 0) {
|
||||||
String status = ticket.getStatus();
|
throw new RuntimeException("手慢了!该号源已刚刚被他人抢占");
|
||||||
logger.debug("状态字符串详细信息: value='{}', length={}, isNull={}", status, status != null ? status.length() : "null", status == null);
|
|
||||||
if (status != null) {
|
|
||||||
StringBuilder charInfo = new StringBuilder();
|
|
||||||
for (int i = 0; i < status.length(); i++) {
|
|
||||||
charInfo.append(status.charAt(i)).append("(").append((int) status.charAt(i)).append(") ");
|
|
||||||
}
|
|
||||||
logger.debug("状态字符串字符信息: {}", charInfo.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 详细调试:检查每个状态比较的结果
|
// 2. 将 DTO 安全降级转换为 Map 给最底层的建单工厂
|
||||||
boolean isUnbooked = "unbooked".equals(status);
|
// 因为建单方法非常底层,通用性强,为了不影响它,我们在这里安全组装 Map
|
||||||
boolean isLocked = "locked".equals(status);
|
|
||||||
boolean isCancelled = "cancelled".equals(status);
|
|
||||||
boolean isChecked = "checked".equals(status);
|
|
||||||
boolean isBooked = "booked".equals(status);
|
|
||||||
logger.debug("状态比较结果: unbooked={}, locked={}, cancelled={}, checked={}, booked={}",
|
|
||||||
isUnbooked, isLocked, isCancelled, isChecked, isBooked);
|
|
||||||
|
|
||||||
if (!isUnbooked && !isLocked && !isCancelled && !isChecked && !isBooked) {
|
// 在持有强类型 DTO 的地方提前合并日期+时间,转为 java.util.Date 后再传入 Map,
|
||||||
logger.error("号源不可预约,id: {}, status: {}", ticket.getId(), ticket.getStatus());
|
// 避免将 LocalDate/LocalTime 装箱为 Object 跨层传递时可能出现的类型映射失败(如 MyBatis 将
|
||||||
throw new RuntimeException("号源不可预约");
|
// PostgreSQL date 类型映射为 java.sql.Date 导致 slot.getScheduleDate() 返回 null)。
|
||||||
|
LocalDate scheduleDate = slot.getScheduleDate();
|
||||||
|
LocalTime expectTime = slot.getExpectTime();
|
||||||
|
Date appointmentDateTime;
|
||||||
|
if (scheduleDate != null && expectTime != null) {
|
||||||
|
LocalDateTime ldt = LocalDateTime.of(scheduleDate, expectTime);
|
||||||
|
appointmentDateTime = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
|
||||||
|
} else {
|
||||||
|
// 理论上不应走到这里,若走到说明号源池数据异常
|
||||||
|
appointmentDateTime = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
params.put("slotId", ticketId);
|
Map<String, Object> safeParams = new java.util.HashMap<>();
|
||||||
Order order = orderService.createAppointmentOrder(params);
|
safeParams.put("slotId", slotId);
|
||||||
|
safeParams.put("scheduleId", slot.getScheduleId());
|
||||||
|
// 直接传入已合并的 Date 对象,OrderServiceImpl 无需再做类型转换
|
||||||
|
safeParams.put("appointmentDate", appointmentDateTime);
|
||||||
|
safeParams.put("appointmentTime", appointmentDateTime);
|
||||||
|
safeParams.put("patientId", dto.getPatientId());
|
||||||
|
safeParams.put("patientName", dto.getPatientName());
|
||||||
|
safeParams.put("medicalCard", dto.getMedicalCard());
|
||||||
|
safeParams.put("phone", dto.getPhone());
|
||||||
|
safeParams.put("gender", dto.getGender());
|
||||||
|
safeParams.put("tenant_id", dto.getTenant_id());
|
||||||
|
|
||||||
Ticket updateTicket = new Ticket();
|
// 3. 【绝对防御】:强制覆盖!不管前端 DTO 传了什么鬼,全以底层数据库物理表为准!
|
||||||
updateTicket.setId(ticketId);
|
safeParams.put("departmentId", slot.getDepartmentId());
|
||||||
updateTicket.setStatus("booked");
|
safeParams.put("departmentName", slot.getDepartmentName());
|
||||||
updateTicket.setPatientId(patientId);
|
safeParams.put("doctorId", slot.getDoctorId());
|
||||||
updateTicket.setPatientName(patientName);
|
safeParams.put("doctorName", slot.getDoctor());
|
||||||
updateTicket.setMedicalCard(medicalCard);
|
safeParams.put("fee", toBigDecimal(slot.getFee()));
|
||||||
updateTicket.setPhone(phone);
|
safeParams.put("regType", slot.getRegType() != null && slot.getRegType() == 1 ? "专家" : "普通");
|
||||||
updateTicket.setAppointmentDate(new Date());
|
|
||||||
updateTicket.setAppointmentTime(new Date());
|
|
||||||
|
|
||||||
int result = ticketMapper.updateById(updateTicket);
|
// 4. 收银台建单!
|
||||||
logger.debug("预约成功,更新号源状态为booked,result: {}", result);
|
Order order = orderService.createAppointmentOrder(safeParams);
|
||||||
|
if (order == null || order.getId() == null) {
|
||||||
|
throw new RuntimeException("预约订单创建失败");
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
// 5. 回填订单ID到号源槽位,保证号源与订单一一关联
|
||||||
|
int bindRows = scheduleSlotMapper.bindOrderToSlot(slotId, order.getId());
|
||||||
|
if (bindRows <= 0) {
|
||||||
|
throw new RuntimeException("预约成功但号源回填订单失败,请重试");
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshPoolStatsBySlotId(slotId);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消预约
|
* 取消预约
|
||||||
*
|
*
|
||||||
* @param ticketId 号源ID
|
* @param slotId 槽位ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public int cancelTicket(Long ticketId) {
|
public int cancelTicket(Long slotId) {
|
||||||
Ticket ticket = ticketMapper.selectTicketById(ticketId);
|
TicketSlotDTO slot = scheduleSlotMapper.selectTicketSlotById(slotId);
|
||||||
if (ticket == null) {
|
if (slot == null) {
|
||||||
throw new RuntimeException("号源不存在");
|
throw new RuntimeException("号源槽位不存在");
|
||||||
}
|
}
|
||||||
if (!"booked".equals(ticket.getStatus()) && !"locked".equals(ticket.getStatus())) {
|
if (slot.getSlotStatus() == null || !SlotStatus.BOOKED.equals(slot.getSlotStatus())) {
|
||||||
throw new RuntimeException("号源不可取消预约");
|
throw new RuntimeException("号源不可取消预约");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Order> orders = orderService.selectOrderBySlotId(ticketId);
|
List<Order> orders = orderService.selectOrderBySlotId(slotId);
|
||||||
for(Order order:orders){
|
if (orders == null || orders.isEmpty()) {
|
||||||
|
throw new RuntimeException("当前号源没有可取消的预约订单");
|
||||||
|
}
|
||||||
|
for (Order order : orders) {
|
||||||
orderService.cancelAppointmentOrder(order.getId(), "患者取消预约");
|
orderService.cancelAppointmentOrder(order.getId(), "患者取消预约");
|
||||||
}
|
}
|
||||||
|
|
||||||
ticket.setStatus("unbooked");
|
int updated = scheduleSlotMapper.updateSlotStatus(slotId, SlotStatus.AVAILABLE);
|
||||||
ticket.setPatientId(null);
|
if (updated > 0) {
|
||||||
ticket.setPatientName(null);
|
refreshPoolStatsBySlotId(slotId);
|
||||||
ticket.setMedicalCard(null);
|
}
|
||||||
ticket.setPhone(null);
|
return updated;
|
||||||
ticket.setAppointmentDate(null);
|
|
||||||
ticket.setAppointmentTime(null);
|
|
||||||
return ticketMapper.updateTicket(ticket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取号
|
* 取号
|
||||||
*
|
*
|
||||||
* @param ticketId 号源ID
|
* @param slotId 槽位ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int checkInTicket(Long ticketId) {
|
@Transactional(rollbackFor = Exception.class)
|
||||||
// 获取号源信息
|
public int checkInTicket(Long slotId) {
|
||||||
Ticket ticket = ticketMapper.selectTicketById(ticketId);
|
List<Order> orders = orderService.selectOrderBySlotId(slotId);
|
||||||
if (ticket == null) {
|
if (orders == null || orders.isEmpty()) {
|
||||||
throw new RuntimeException("号源不存在");
|
throw new RuntimeException("当前号源没有可取号的预约订单");
|
||||||
}
|
}
|
||||||
if (!"booked".equals(ticket.getStatus()) && !"locked".equals(ticket.getStatus())) {
|
Order latestOrder = orders.get(0);
|
||||||
throw new RuntimeException("号源不可取号");
|
return orderService.updateOrderStatusById(latestOrder.getId(), AppointmentOrderStatus.CHECKED_IN);
|
||||||
}
|
|
||||||
// 更新号源状态为已取号
|
|
||||||
ticket.setStatus("checked");
|
|
||||||
return ticketMapper.updateTicket(ticket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 停诊
|
* 停诊
|
||||||
*
|
*
|
||||||
* @param ticketId 号源ID
|
* @param slotId 槽位ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public int cancelConsultation(Long ticketId) {
|
public int cancelConsultation(Long slotId) {
|
||||||
// 获取号源信息
|
TicketSlotDTO slot = scheduleSlotMapper.selectTicketSlotById(slotId);
|
||||||
Ticket ticket = ticketMapper.selectTicketById(ticketId);
|
if (slot == null) {
|
||||||
if (ticket == null) {
|
throw new RuntimeException("号源槽位不存在");
|
||||||
throw new RuntimeException("号源不存在");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否存在相关订单,如果存在则取消
|
List<Order> orders = orderService.selectOrderBySlotId(slotId);
|
||||||
List<Order> orders = orderService.selectOrderBySlotId(ticketId);
|
for (Order order : orders) {
|
||||||
for(Order order:orders){
|
|
||||||
orderService.cancelAppointmentOrder(order.getId(), "医生停诊");
|
orderService.cancelAppointmentOrder(order.getId(), "医生停诊");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新号源状态为已取消
|
int updated = scheduleSlotMapper.updateSlotStatus(slotId, SlotStatus.STOPPED);
|
||||||
ticket.setStatus("cancelled");
|
if (updated > 0) {
|
||||||
ticket.setPatientId(null);
|
refreshPoolStatsBySlotId(slotId);
|
||||||
ticket.setPatientName(null);
|
}
|
||||||
ticket.setMedicalCard(null);
|
return updated;
|
||||||
ticket.setPhone(null);
|
}
|
||||||
ticket.setAppointmentDate(null);
|
|
||||||
ticket.setAppointmentTime(null);
|
/**
|
||||||
return ticketMapper.updateTicket(ticket);
|
* 根据槽位ID找到号源池并刷新 booked_num / locked_num 统计。
|
||||||
|
*/
|
||||||
|
private void refreshPoolStatsBySlotId(Long slotId) {
|
||||||
|
Long poolId = scheduleSlotMapper.selectPoolIdBySlotId(slotId);
|
||||||
|
if (poolId != null) {
|
||||||
|
schedulePoolMapper.refreshPoolStats(poolId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal toBigDecimal(String fee) {
|
||||||
|
if (fee == null || fee.trim().isEmpty()) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new BigDecimal(fee.trim());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new RuntimeException("挂号费格式错误: " + fee);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,309 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.openhis.appointmentmanage.mapper.ScheduleSlotMapper">
|
||||||
|
|
||||||
|
<!-- 注意这里的 resultType 指向了您刚建好的 DTO 实体类 -->
|
||||||
|
<select id="selectAllTicketSlots" resultType="com.openhis.appointmentmanage.domain.TicketSlotDTO">
|
||||||
|
SELECT
|
||||||
|
s.id AS slotId,
|
||||||
|
p.schedule_id AS scheduleId,
|
||||||
|
p.doctor_name AS doctor,
|
||||||
|
p.dept_id AS departmentId,
|
||||||
|
p.fee AS fee,
|
||||||
|
o.patient_id AS patientId,
|
||||||
|
o.patient_name AS patientName,
|
||||||
|
o.medical_card AS medicalCard,
|
||||||
|
o.phone AS phone,
|
||||||
|
o.status AS orderStatus,
|
||||||
|
s.status AS slotStatus,
|
||||||
|
s.expect_time AS expectTime,
|
||||||
|
p.schedule_date AS scheduleDate,
|
||||||
|
d.reg_type AS regType,
|
||||||
|
p.status AS poolStatus,
|
||||||
|
p.stop_reason AS stopReason,
|
||||||
|
d.is_stopped AS isStopped
|
||||||
|
FROM
|
||||||
|
adm_schedule_slot s
|
||||||
|
INNER JOIN adm_schedule_pool p ON s.pool_id = p.id
|
||||||
|
LEFT JOIN adm_doctor_schedule d ON p.schedule_id = d.id
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT DISTINCT
|
||||||
|
ON (slot_id) slot_id,
|
||||||
|
patient_id,
|
||||||
|
patient_name,
|
||||||
|
medical_card,
|
||||||
|
phone,
|
||||||
|
status
|
||||||
|
FROM
|
||||||
|
order_main
|
||||||
|
WHERE
|
||||||
|
status IN (1, 2)
|
||||||
|
ORDER BY
|
||||||
|
slot_id,
|
||||||
|
create_time DESC
|
||||||
|
) o ON o.slot_id = s.id
|
||||||
|
WHERE
|
||||||
|
p.delete_flag = '0'
|
||||||
|
AND s.delete_flag = '0'
|
||||||
|
ORDER BY
|
||||||
|
p.schedule_date,
|
||||||
|
p.doctor_id,
|
||||||
|
s.expect_time
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectTicketSlotById" resultType="com.openhis.appointmentmanage.domain.TicketSlotDTO">
|
||||||
|
SELECT
|
||||||
|
s.id AS slotId,
|
||||||
|
p.schedule_id AS scheduleId,
|
||||||
|
p.doctor_name AS doctor,
|
||||||
|
p.doctor_id AS doctorId,
|
||||||
|
p.dept_id AS departmentId,
|
||||||
|
org.name AS departmentName,
|
||||||
|
p.fee AS fee,
|
||||||
|
o.patient_id AS patientId,
|
||||||
|
o.patient_name AS patientName,
|
||||||
|
o.medical_card AS medicalCard,
|
||||||
|
o.phone AS phone,
|
||||||
|
o.status AS orderStatus,
|
||||||
|
s.status AS slotStatus,
|
||||||
|
s.expect_time AS expectTime,
|
||||||
|
p.schedule_date AS scheduleDate,
|
||||||
|
d.reg_type AS regType,
|
||||||
|
p.status AS poolStatus,
|
||||||
|
p.stop_reason AS stopReason,
|
||||||
|
d.is_stopped AS isStopped
|
||||||
|
FROM
|
||||||
|
adm_schedule_slot s
|
||||||
|
INNER JOIN adm_schedule_pool p ON s.pool_id = p.id
|
||||||
|
LEFT JOIN adm_doctor_schedule d ON p.schedule_id = d.id
|
||||||
|
LEFT JOIN adm_organization org ON p.dept_id = org.id
|
||||||
|
AND org.delete_flag = '0'
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT DISTINCT
|
||||||
|
ON (slot_id) slot_id,
|
||||||
|
patient_id,
|
||||||
|
patient_name,
|
||||||
|
medical_card,
|
||||||
|
phone,
|
||||||
|
status
|
||||||
|
FROM
|
||||||
|
order_main
|
||||||
|
WHERE
|
||||||
|
status IN (1, 2)
|
||||||
|
ORDER BY
|
||||||
|
slot_id,
|
||||||
|
create_time DESC
|
||||||
|
) o ON o.slot_id = s.id
|
||||||
|
WHERE
|
||||||
|
s.id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<update id="lockSlotForBooking">
|
||||||
|
UPDATE adm_schedule_slot
|
||||||
|
SET
|
||||||
|
status = 1,
|
||||||
|
update_time = now()
|
||||||
|
WHERE
|
||||||
|
id = #{slotId}
|
||||||
|
AND status = 0
|
||||||
|
AND delete_flag = '0'
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<update id="updateSlotStatus">
|
||||||
|
UPDATE adm_schedule_slot
|
||||||
|
SET
|
||||||
|
status = #{status},
|
||||||
|
<if test="status == 0">
|
||||||
|
order_id = NULL,
|
||||||
|
</if>
|
||||||
|
update_time = now()
|
||||||
|
WHERE
|
||||||
|
id = #{slotId}
|
||||||
|
AND delete_flag = '0'
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<select id="selectPoolIdBySlotId" resultType="java.lang.Long">
|
||||||
|
SELECT pool_id
|
||||||
|
FROM adm_schedule_slot
|
||||||
|
WHERE id = #{slotId}
|
||||||
|
AND delete_flag = '0'
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<update id="bindOrderToSlot">
|
||||||
|
UPDATE adm_schedule_slot
|
||||||
|
SET
|
||||||
|
order_id = #{orderId},
|
||||||
|
update_time = now()
|
||||||
|
WHERE
|
||||||
|
id = #{slotId}
|
||||||
|
AND status = 1
|
||||||
|
AND delete_flag = '0'
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<select id="selectTicketSlotsPage" resultType="com.openhis.appointmentmanage.domain.TicketSlotDTO">
|
||||||
|
SELECT
|
||||||
|
s.id AS slotId,
|
||||||
|
p.schedule_id AS scheduleId,
|
||||||
|
p.doctor_name AS doctor,
|
||||||
|
p.doctor_id AS doctorId,
|
||||||
|
p.dept_id AS departmentId,
|
||||||
|
org.name AS departmentName,
|
||||||
|
p.fee AS fee,
|
||||||
|
o.patient_id AS patientId,
|
||||||
|
o.patient_name AS patientName,
|
||||||
|
o.medical_card AS medicalCard,
|
||||||
|
o.phone AS phone,
|
||||||
|
o.status AS orderStatus,
|
||||||
|
s.status AS slotStatus,
|
||||||
|
s.expect_time AS expectTime,
|
||||||
|
p.schedule_date AS scheduleDate,
|
||||||
|
d.reg_type AS regType,
|
||||||
|
p.status AS poolStatus,
|
||||||
|
p.stop_reason AS stopReason,
|
||||||
|
d.is_stopped AS isStopped
|
||||||
|
FROM
|
||||||
|
adm_schedule_slot s
|
||||||
|
INNER JOIN adm_schedule_pool p ON s.pool_id = p.id
|
||||||
|
LEFT JOIN adm_doctor_schedule d ON p.schedule_id = d.id
|
||||||
|
LEFT JOIN adm_organization org ON p.dept_id = org.id
|
||||||
|
AND org.delete_flag = '0'
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT DISTINCT
|
||||||
|
ON (slot_id) slot_id,
|
||||||
|
patient_id,
|
||||||
|
patient_name,
|
||||||
|
medical_card,
|
||||||
|
phone,
|
||||||
|
status
|
||||||
|
FROM
|
||||||
|
order_main
|
||||||
|
WHERE
|
||||||
|
status IN (1, 2)
|
||||||
|
ORDER BY
|
||||||
|
slot_id,
|
||||||
|
create_time DESC
|
||||||
|
) o ON o.slot_id = s.id
|
||||||
|
<where>
|
||||||
|
p.delete_flag = '0'
|
||||||
|
AND s.delete_flag = '0' <!-- 1. 按日期查 -->
|
||||||
|
<if test="query.date != null and query.date != ''">
|
||||||
|
AND p.schedule_date = CAST(#{query.date} AS DATE)
|
||||||
|
</if>
|
||||||
|
<!-- 2. 按科室查 -->
|
||||||
|
<if test="query.department != null and query.department != '' and query.department != 'all'">
|
||||||
|
AND org.name = #{query.department}
|
||||||
|
</if>
|
||||||
|
<if test="query.doctorId != null">
|
||||||
|
AND p.doctor_id = #{query.doctorId}
|
||||||
|
</if>
|
||||||
|
<!-- 3. 按号源类型查 (专家/普通) -->
|
||||||
|
<if test="query.type != null and query.type != ''">
|
||||||
|
<choose>
|
||||||
|
<when test="query.type == 'expert'">
|
||||||
|
AND d.reg_type = 1
|
||||||
|
</when>
|
||||||
|
<when test="query.type == 'general'">
|
||||||
|
AND (
|
||||||
|
d.reg_type != 1
|
||||||
|
OR d.reg_type IS NULL
|
||||||
|
)
|
||||||
|
</when>
|
||||||
|
</choose>
|
||||||
|
</if>
|
||||||
|
<!-- 4. 模糊搜索患者信息 -->
|
||||||
|
<if test="query.name != null and query.name != ''">
|
||||||
|
AND o.patient_name LIKE CONCAT('%', #{query.name}, '%')
|
||||||
|
</if>
|
||||||
|
<if test="query.card != null and query.card != ''">
|
||||||
|
AND o.medical_card LIKE CONCAT('%', #{query.card}, '%')
|
||||||
|
</if>
|
||||||
|
<if test="query.phone != null and query.phone != ''">
|
||||||
|
AND o.phone LIKE CONCAT('%', #{query.phone}, '%')
|
||||||
|
</if>
|
||||||
|
<!-- 5. 核心:解答您疑问的 4 种业务状态的复合查询! -->
|
||||||
|
<if test="query.status != null and query.status != '' and query.status != 'all'">
|
||||||
|
<choose>
|
||||||
|
<when test="query.status == 'unbooked'">
|
||||||
|
AND s.status = 0
|
||||||
|
AND (
|
||||||
|
d.is_stopped IS NULL
|
||||||
|
OR d.is_stopped = FALSE
|
||||||
|
)
|
||||||
|
</when>
|
||||||
|
<when test="query.status == 'booked'">
|
||||||
|
AND s.status = 1
|
||||||
|
AND o.status = 1
|
||||||
|
AND (
|
||||||
|
d.is_stopped IS NULL
|
||||||
|
OR d.is_stopped = FALSE
|
||||||
|
)
|
||||||
|
</when>
|
||||||
|
<when test="query.status == 'checked'">
|
||||||
|
AND s.status = 1
|
||||||
|
AND o.status = 2
|
||||||
|
AND (
|
||||||
|
d.is_stopped IS NULL
|
||||||
|
OR d.is_stopped = FALSE
|
||||||
|
)
|
||||||
|
</when>
|
||||||
|
<when test="query.status == 'cancelled'">
|
||||||
|
AND (
|
||||||
|
s.status = 2
|
||||||
|
OR d.is_stopped = TRUE
|
||||||
|
)
|
||||||
|
</when>
|
||||||
|
</choose>
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
ORDER BY
|
||||||
|
p.schedule_date DESC,
|
||||||
|
s.expect_time ASC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectDoctorAvailabilitySummary" resultType="com.openhis.appointmentmanage.domain.DoctorAvailabilityDTO">
|
||||||
|
SELECT
|
||||||
|
p.doctor_id AS doctorId,
|
||||||
|
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,
|
||||||
|
CASE
|
||||||
|
WHEN MAX(CASE WHEN d.reg_type = 1 THEN 1 ELSE 0 END) = 1 THEN 'expert'
|
||||||
|
ELSE 'general'
|
||||||
|
END AS ticketType
|
||||||
|
FROM
|
||||||
|
adm_schedule_pool p
|
||||||
|
LEFT JOIN adm_doctor_schedule d ON p.schedule_id = d.id
|
||||||
|
LEFT JOIN adm_organization org ON p.dept_id = org.id
|
||||||
|
AND org.delete_flag = '0'
|
||||||
|
<where>
|
||||||
|
p.delete_flag = '0'
|
||||||
|
<if test="query.date != null and query.date != ''">
|
||||||
|
AND p.schedule_date = CAST(#{query.date} AS DATE)
|
||||||
|
</if>
|
||||||
|
<if test="query.department != null and query.department != '' and query.department != 'all'">
|
||||||
|
AND org.name = #{query.department}
|
||||||
|
</if>
|
||||||
|
<if test="query.type != null and query.type != '' and query.type != 'all'">
|
||||||
|
<choose>
|
||||||
|
<when test="query.type == 'expert'">
|
||||||
|
AND d.reg_type = 1
|
||||||
|
</when>
|
||||||
|
<when test="query.type == 'general'">
|
||||||
|
AND (d.reg_type != 1 OR d.reg_type IS NULL)
|
||||||
|
</when>
|
||||||
|
</choose>
|
||||||
|
</if>
|
||||||
|
<if test="query.doctorId != null">
|
||||||
|
AND p.doctor_id = #{query.doctorId}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
GROUP BY
|
||||||
|
p.doctor_id,
|
||||||
|
p.doctor_name
|
||||||
|
ORDER BY
|
||||||
|
p.doctor_name ASC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -9,7 +9,16 @@ export function listDept(query) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询科室详细
|
// 获取挂号科室列表(与排班管理一致的数据源)
|
||||||
|
export function listRegisterOrganizations(query) {
|
||||||
|
return request({
|
||||||
|
url: '/base-data-manage/organization/register-organizations',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询科室详情
|
||||||
export function getDept(deptId) {
|
export function getDept(deptId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/dept/' + deptId,
|
url: '/dept/' + deptId,
|
||||||
|
|||||||
@@ -9,6 +9,15 @@ export function listTicket(query) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询医生余号汇总(基于号源池,不受分页影响)
|
||||||
|
export function listDoctorSummary(query) {
|
||||||
|
return request({
|
||||||
|
url: '/appointment/ticket/doctorSummary',
|
||||||
|
method: 'post',
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 预约号源
|
// 预约号源
|
||||||
export function bookTicket(data) {
|
export function bookTicket(data) {
|
||||||
return request({
|
return request({
|
||||||
@@ -19,29 +28,29 @@ export function bookTicket(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 取消预约
|
// 取消预约
|
||||||
export function cancelTicket(ticketId) {
|
export function cancelTicket(slotId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/appointment/ticket/cancel',
|
url: '/appointment/ticket/cancel',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: { ticketId }
|
params: { slotId }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取号
|
// 取号
|
||||||
export function checkInTicket(ticketId) {
|
export function checkInTicket(slotId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/appointment/ticket/checkin',
|
url: '/appointment/ticket/checkin',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: { ticketId }
|
params: { slotId }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 停诊
|
// 停诊
|
||||||
export function cancelConsultation(ticketId) {
|
export function cancelConsultation(slotId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/appointment/ticket/cancelConsultation',
|
url: '/appointment/ticket/cancelConsultation',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: { ticketId }
|
params: { slotId }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user