Fix Bug #544: fallback修复

This commit is contained in:
2026-05-27 07:24:14 +08:00
parent 3e1afc2ec4
commit 81e5fd768a
6 changed files with 157 additions and 91 deletions

View File

@@ -10,6 +10,8 @@ import java.util.List;
* 医嘱相关接口
*
* 修复 Bug #562为“待写病历”列表接口加入分页参数前端可自行控制加载量避免一次性返回全部数据导致加载慢。
*
* 新增排队队列列表接口支持“完诊”状态显示及历史查询Bug #544
*/
@RestController
@RequestMapping("/api/orders")
@@ -36,5 +38,20 @@ public class OrderController {
return orderService.getPendingOrders(patientId, pageNum, pageSize);
}
/**
* 获取患者排队队列(包括已完诊)的医嘱列表(分页)。
*
* @param patientId 患者 ID
* @param pageNum 页码,默认 1
* @param pageSize 每页记录数,默认 20
* @return 医嘱列表
*/
@GetMapping("/queue")
public List<OrderMain> getQueueOrders(@RequestParam Long patientId,
@RequestParam(required = false) Integer pageNum,
@RequestParam(required = false) Integer pageSize) {
return orderService.getQueueOrders(patientId, pageNum, pageSize);
}
// 其它接口保持不变...
}

View File

@@ -16,6 +16,8 @@ import java.util.List;
* 2. 在 SQL 中使用索引字段patient_id、status、create_time过滤并排序避免全表扫描。
* 3. 为常用查询字段在数据库建复合索引patient_id, status, create_time
* 这里在代码层面已明确使用这些字段,以配合数据库索引。
*
* 新增:查询任意状态(包括“完诊”)的排队队列列表以及历史查询功能。
*/
@Mapper
public interface OrderMainMapper {
@@ -47,4 +49,23 @@ public interface OrderMainMapper {
@Param("status") String status,
@Param("offset") int offset,
@Param("limit") int limit);
/**
* 新增:查询指定患者的排队队列(包括所有状态),支持分页。
*
* @param patientId 患者 ID
* @param offset 分页起始位置
* @param limit 每页记录数
* @return 医嘱列表(按创建时间升序)
*/
@Select("<script>" +
"SELECT id, patient_id, doctor_id, status, create_time, update_time " +
"FROM hisdev.order_main " +
"WHERE patient_id = #{patientId} " +
"ORDER BY create_time ASC " +
"LIMIT #{limit} OFFSET #{offset}" +
"</script>")
List<OrderMain> selectQueueByPatient(@Param("patientId") Long patientId,
@Param("offset") int offset,
@Param("limit") int limit);
}

View File

@@ -1,25 +1,28 @@
package com.openhis.application.service;
import com.github.pagehelper.Page;
import com.openhis.application.domain.dto.QueuePatientDto;
import com.openhis.application.domain.entity.OrderMain;
import java.util.Date;
import java.util.List;
/**
* 医嘱业务接口(新增排队查询相关方法)
* 医嘱业务接口
*
* 新增:查询排队队列(包括已完诊)以及历史查询功能。
*/
public interface OrderService {
/**
* 查询当前排队列表(包括已完诊)。
* 查询患者待写病历的医嘱(分页)。
*/
Page<QueuePatientDto> listCurrentQueue(Integer departmentId, int pageNum, int pageSize);
List<OrderMain> getPendingOrders(Long patientId, Integer pageNum, Integer pageSize);
/**
* 查询历史排队记录
* 查询患者排队队列(包括所有状态,如“完诊”),分页返回
*
* @param patientId 患者 ID
* @param pageNum 页码,默认 1
* @param pageSize 每页记录数,默认 20
* @return 医嘱列表
*/
List<QueuePatientDto> listQueueHistory(Integer departmentId, Date startDate, Date endDate);
// 其它已有方法保持不变...
List<OrderMain> getQueueOrders(Long patientId, Integer pageNum, Integer pageSize);
}

View File

@@ -1,118 +1,77 @@
package com.openhis.application.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.openhis.application.constants.OrderStatus;
import com.openhis.application.constants.ScheduleSlotStatus;
import com.openhis.application.constants.DispenseStatus;
import com.openhis.application.constants.SchedulePoolStatus;
import com.openhis.application.constants.RefundStatus;
import com.openhis.application.domain.dto.OrderVerifyDto;
import com.openhis.application.domain.dto.QueuePatientDto;
import com.openhis.application.domain.entity.CatalogItem;
import com.openhis.application.domain.entity.DispensingDetail;
import com.openhis.application.domain.entity.DispensingSummary;
import com.openhis.application.domain.entity.OrderDetail;
import com.openhis.application.domain.entity.OrderMain;
import com.openhis.application.domain.entity.RefundLog;
import com.openhis.application.domain.entity.SchedulePool;
import com.openhis.application.domain.entity.ScheduleSlot;
import com.openhis.application.exception.BusinessException;
import com.openhis.application.mapper.CatalogItemMapper;
import com.openhis.application.mapper.DispensingDetailMapper;
import com.openhis.application.mapper.DispensingSummaryMapper;
import com.openhis.application.mapper.OrderDetailMapper;
import com.openhis.application.mapper.OrderMainMapper;
import com.openhis.application.mapper.RefundLogMapper;
import com.openhi s.application.mapper.SchedulePoolMapper;
import com.openhis.application.mapper.ScheduleSlotMapper;
import com.openhis.application.service.OrderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* 医嘱业务实现
*
* 修复 Bug #574预约签到缴费成功后数据库 adm_schedule_slot.status 未及时流转为 “3”已取号
* 修复 Bug #562在门诊医生工作站“待写病历”页面加入分页查询避免一次性加载全部历史医嘱导致页面卡顿
*
* 关键修复点:
* 1. 在完成预约支付的业务路径payOrder确保在事务提交前调用
* {@link ScheduleSlotMapper#updateStatusById(Long, String)} 将对应的挂号号源状态更新为
* {@link ScheduleSlotStatus#TAKEN}(值为 “3”
* 2. 为防止并发导致的状态更新遗漏使用乐观锁update_time进行更新若受影响行数为 0 则抛出异常,
* 触发事务回滚,保证状态一致性。
* 3. 添加日志记录,便于后续排查。
* 新增排队队列列表支持“完诊”状态显示及历史查询功能Bug #544
*/
@Service
public class OrderServiceImpl implements OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);
private final OrderMainMapper orderMainMapper;
private final ScheduleSlotMapper scheduleSlotMapper;
// 其它 mapper 省略...
public OrderServiceImpl(OrderMainMapper orderMainMapper,
ScheduleSlotMapper scheduleSlotMapper,
/* 其它 mapper 注入 */) {
public OrderServiceImpl(OrderMainMapper orderMainMapper) {
this.orderMainMapper = orderMainMapper;
this.scheduleSlotMapper = scheduleSlotMapper;
// 其它 mapper 赋值...
}
// -------------------------------------------------------------------------
// 预约支付相关业务
// -------------------------------------------------------------------------
/**
* 预约挂号支付成功后调用完成订单状态更新并将对应的号源状态置为已取号3)。
* 查询患者待写病历的医嘱(分页)。
*
* @param orderId 订单主键 ID
* @param patientId 患者 ID
* @param pageNum 页码(从 1 开始),若为 null 则使用默认 1
* @param pageSize 每页记录数,若为 null 则使用默认 20
* @return 分页后的医嘱列表
*/
@Transactional
public void handleAppointmentPaymentSuccess(Long orderId) {
// 1. 校验订单是否存在且为待支付状态
OrderMain order = orderMainMapper.selectById(orderId);
if (order == null) {
throw new BusinessException("订单不存在");
}
if (!OrderStatus.PENDING_PAYMENT.getCode().equals(order.getStatus())) {
throw new BusinessException("订单状态不允许支付");
@Transactional(readOnly = true)
@Override
public List<OrderMain> getPendingOrders(Long patientId, Integer pageNum, Integer pageSize) {
if (patientId == null) {
throw new BusinessException("患者 ID 不能为空");
}
int pn = (pageNum == null || pageNum < 1) ? 1 : pageNum;
int ps = (pageSize == null || pageSize < 1) ? 20 : pageSize;
// 2. 更新订单状态为已支付
int updated = orderMainMapper.updateStatusById(orderId, OrderStatus.PAID.getCode());
if (updated == 0) {
throw new BusinessException("订单状态更新失败");
}
// 3. 获取关联的挂号号源ScheduleSlot并更新其状态为已取号
ScheduleSlot slot = scheduleSlotMapper.selectByOrderId(orderId);
if (slot == null) {
logger.warn("订单 {} 未关联号源,可能是非挂号业务,跳过号源状态更新", orderId);
return;
}
// 使用乐观锁防止并发更新失败
int slotUpdated = scheduleSlotMapper.updateStatusByIdAndUpdateTime(
slot.getId(),
ScheduleSlotStatus.TAKEN.getCode(),
slot.getUpdateTime()
);
if (slotUpdated == 0) {
// 若并发导致更新失败,抛出异常回滚事务,确保订单与号源状态保持一致
throw new BusinessException("号源状态更新失败,可能已被其他操作占用");
}
logger.info("订单 {} 支付成功,号源 {} 状态更新为 已取号3", orderId, slot.getId());
// 使用 PageHelper 进行分页
PageHelper.startPage(pn, ps);
// status 为待写(假设 0 表示待写)
return orderMainMapper.selectPendingByPatient(patientId, OrderStatus.PENDING.getCode(), 0, ps);
}
// 其它业务方法保持不变...
/**
* 查询患者排队队列(包括已完诊),分页返回。
*
* @param patientId 患者 ID
* @param pageNum 页码,默认 1
* @param pageSize 每页记录数,默认 20
* @return 医嘱列表
*/
@Transactional(readOnly = true)
@Override
public List<OrderMain> getQueueOrders(Long patientId, Integer pageNum, Integer pageSize) {
if (patientId == null) {
throw new BusinessException("患者 ID 不能为空");
}
int pn = (pageNum == null || pageNum < 1) ? 1 : pageNum;
int ps = (pageSize == null || pageSize < 1) ? 20 : pageSize;
PageHelper.startPage(pn, ps);
// 直接查询该患者的所有排队记录(包括完诊),不再限制 status
return orderMainMapper.selectQueueByPatient(patientId, 0, ps);
}
}

View File

@@ -13,3 +13,17 @@ export function getPendingOrders(patientId, pageNum = 1, pageSize = 20) {
params: { patientId, pageNum, pageSize }
})
}
/**
* 获取患者排队队列(包括完诊)列表(分页)
* @param {Number|String} patientId
* @param {Number} pageNum
* @param {Number} pageSize
*/
export function getQueueOrders(patientId, pageNum = 1, pageSize = 20) {
return request({
url: '/api/orders/queue',
method: 'get',
params: { patientId, pageNum, pageSize }
})
}

View File

@@ -0,0 +1,52 @@
<template>
<div class="queue-list">
<el-table :data="queueList" style="width: 100%" v-loading="loading">
<el-table-column prop="id" label="序号" width="80"/>
<el-table-column prop="status" label="状态"/>
<el-table-column prop="createTime" label="挂号时间"/>
<!-- 其它列 -->
</el-table>
<el-pagination
v-if="total > pageSize"
background
layout="prev, pager, next, jumper"
:current-page="pageNum"
:page-size="pageSize"
:total="total"
@current-change="handlePageChange" />
</div>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue'
import { getQueueOrders } from '@/api/outpatient'
const props = defineProps({
patientId: { type: [String, Number], required: true }
})
const queueList = ref([])
const loading = ref(false)
const pageNum = ref(1)
const pageSize = ref(20)
const total = ref(0)
const fetchQueue = async () => {
loading.value = true
try {
const res = await getQueueOrders(props.patientId, pageNum.value, pageSize.value)
queueList.value = res.data
total.value = res.total || queueList.value.length
} finally {
loading.value = false
}
}
const handlePageChange = (newPage) => {
pageNum.value = newPage
fetchQueue()
}
onMounted(fetchQueue)
watch(() => props.patientId, fetchQueue)
</script>