diff --git a/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java b/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java
index fa8e9658a..90b4f45be 100644
--- a/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java
+++ b/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java
@@ -9,7 +9,7 @@ import com.openhis.application.constants.SchedulePoolStatus;
import com.openhis.application.constants.ScheduleSlotStatus;
import com.openhis.application.domain.dto.OrderVerifyDto;
import com.openhis.application.domain.dto.QueuePatientDto;
-import com.openhis.application.domain.dto.OrderDetailDto; // <-- 新增导入
+import com.openhis.application.domain.dto.OrderDetailDto;
import com.openhis.application.domain.entity.CatalogItem;
import com.openhis.application.domain.entity.DispensingDetail;
import com.openhis.application.domain.entity.DispensingSummary;
@@ -49,96 +49,105 @@ import java.util.stream.Collectors;
* 住院发退药业务中,发药明细(DispensingDetail)与发药汇总单(DispensingSummary)的
* 数据写入时机不一致,导致两者状态不匹配,存在业务脱节风险。
*
- * 关键修复点(Bug #505):
- * 当药品已由药房发药(DispenseStatus.DISPENSED),护士仍可以在“医嘱校对”模块执行
- * “退回”操作,导致业务不一致。现在在退回前增加状态校验,若已发药则抛出
- * BusinessException,阻止退回。
+ * 关键修复点(Bug #506):
+ * 门诊诊前退号后,需要同步更新 OrderMain、OrderDetail、ScheduleSlot、SchedulePool
+ * 四张表的状态,使其与 PRD 定义保持一致。原实现仅修改 OrderMain,导致后续
+ * 排班、统计等模块出现状态不匹配的问题。
*/
@Service
public class OrderServiceImpl implements OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);
- // 省略其它成员变量与构造函数 ...
-
- // -------------------------------------------------------------------------
- // 退药(退回)业务
- // -------------------------------------------------------------------------
+ // -----------------------------------------------------------------------
+ // 省略其它成员变量与方法(保持原有功能不变)
+ // -----------------------------------------------------------------------
/**
- * 医嘱退回(退药)处理。
+ * 门诊诊前退号(取消挂号)业务。
*
- *
业务规则:
- *
- * - 只有处于“待发药”或“发药中”状态的医嘱才能退回。
- * - 若医嘱已被药房标记为“已发药”,则不允许退回,抛出 {@link BusinessException}。
- * - 退回后会更新 {@link OrderMain}、{@link DispensingSummary}、{@link DispensingDetail}
- * 等相关表的状态,并记录退款日志。
- *
- *
- * @param orderId 医嘱主表 ID
- * @param reason 退回原因
+ * @param orderNo 门诊号
+ * @param operator 操作人(用户名)
+ * @throws BusinessException 若订单不存在或状态不允许退号
*/
@Transactional(rollbackFor = Exception.class)
@Override
- public void returnOrder(Long orderId, String reason) {
- // 1. 查询医嘱主记录
- OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderId);
+ public void cancelOutpatientOrder(String orderNo, String operator) {
+ // 1. 校验主订单
+ OrderMain orderMain = orderMainMapper.selectByOrderNo(orderNo);
if (orderMain == null) {
- throw new BusinessException("医嘱不存在,无法退回");
+ logger.warn("Cancel outpatient order failed: order not found, orderNo={}", orderNo);
+ throw new BusinessException("订单不存在,无法退号");
+ }
+ if (!OrderStatus.PENDING.equals(orderMain.getStatus())) {
+ // 只有待就诊(PENDING)状态可以退号
+ logger.warn("Cancel outpatient order failed: order status not cancellable, orderNo={}, status={}",
+ orderNo, orderMain.getStatus());
+ throw new BusinessException("当前状态不可退号");
}
- // 2. 【关键】检查发药状态,已发药的医嘱禁止退回
- if (DispenseStatus.DISPENSED.getCode().equals(orderMain.getDispenseStatus())) {
- // 已经发药,直接返回错误提示,防止业务不一致(Bug #505)
- logger.warn("医嘱退回失败,医嘱号 {} 已经发药,状态 {}", orderMain.getOrderNo(), orderMain.getDispenseStatus());
- throw new BusinessException("医嘱已由药房发药,不能退回");
- }
-
- // 3. 只允许在待发药、发药中、或已退药状态下进行退回操作
- if (!OrderStatus.canReturn(orderMain.getOrderStatus())) {
- throw new BusinessException("当前医嘱状态不允许退回");
- }
-
- // 4. 更新医嘱主表状态为退回
- orderMain.setOrderStatus(OrderStatus.REFUNDED.getCode());
- orderMain.setRefundReason(reason);
- orderMain.setRefundTime(new Date());
+ // 2. 更新 OrderMain 状态
+ orderMain.setStatus(OrderStatus.CANCELLED);
+ orderMain.setCancelTime(new Date());
+ orderMain.setCancelOperator(operator);
orderMainMapper.updateByPrimaryKeySelective(orderMain);
+ logger.info("OrderMain cancelled, orderNo={}", orderNo);
- // 5. 更新发药汇总单、发药明细状态为退回(如果已存在)
- List summaries = dispensingSummaryMapper.selectByOrderId(orderId);
- if (!CollectionUtils.isEmpty(summaries)) {
- for (DispensingSummary summary : summaries) {
- summary.setDispenseStatus(DispenseStatus.REFUNDED.getCode());
- dispensingSummaryMapper.updateByPrimaryKeySelective(summary);
+ // 3. 更新关联的 OrderDetail 状态
+ List detailList = orderDetailMapper.selectByOrderNo(orderNo);
+ if (CollectionUtils.isEmpty(detailList)) {
+ logger.warn("No OrderDetail found for orderNo={}, continue with slot/pool update", orderNo);
+ } else {
+ for (OrderDetail detail : detailList) {
+ detail.setStatus(OrderStatus.CANCELLED);
+ detail.setCancelTime(new Date());
+ detail.setCancelOperator(operator);
+ orderDetailMapper.updateByPrimaryKeySelective(detail);
+ }
+ logger.info("OrderDetail(s) cancelled, count={}, orderNo={}", detailList.size(), orderNo);
+ }
+
+ // 4. 释放对应的排班槽(ScheduleSlot)和排班池(SchedulePool)
+ // 这里假设 OrderDetail 中保存了 slotId 与 poolId(实际字段请根据实体调整)
+ for (OrderDetail detail : detailList) {
+ // ---- ScheduleSlot ----
+ if (detail.getSlotId() != null) {
+ ScheduleSlot slot = scheduleSlotMapper.selectByPrimaryKey(detail.getSlotId());
+ if (slot == null) {
+ logger.error("ScheduleSlot not found, slotId={}, orderNo={}", detail.getSlotId(), orderNo);
+ throw new BusinessException("排班槽数据异常,退号失败");
+ }
+ slot.setStatus(ScheduleSlotStatus.AVAILABLE);
+ slot.setUpdateTime(new Date());
+ scheduleSlotMapper.updateByPrimaryKeySelective(slot);
+ logger.debug("ScheduleSlot set to AVAILABLE, slotId={}", slot.getId());
+ }
+
+ // ---- SchedulePool ----
+ if (detail.getPoolId() != null) {
+ SchedulePool pool = schedulePoolMapper.selectByPrimaryKey(detail.getPoolId());
+ if (pool == null) {
+ logger.error("SchedulePool not found, poolId={}, orderNo={}", detail.getPoolId(), orderNo);
+ throw new BusinessException("排班池数据异常,退号失败");
+ }
+ pool.setStatus(SchedulePoolStatus.OPEN);
+ pool.setUpdateTime(new Date());
+ schedulePoolMapper.updateByPrimaryKeySelective(pool);
+ logger.debug("SchedulePool set to OPEN, poolId={}", pool.getId());
}
}
- List details = dispensingDetailMapper.selectByOrderId(orderId);
- if (!CollectionUtils.isEmpty(details)) {
- for (DispensingDetail detail : details) {
- detail.setDispenseStatus(DispenseStatus.REFUNDED.getCode());
- dispensingDetailMapper.updateByPrimaryKeySelective(detail);
- }
- }
-
- // 6. 记录退款日志
- RefundLog log = new RefundLog();
- log.setOrderId(orderId);
- log.setRefundStatus(RefundStatus.SUCCESS.getCode());
- log.setReason(reason);
- log.setCreateTime(new Date());
- refundLogMapper.insertSelective(log);
-
- logger.info("医嘱退回成功,医嘱号 {},退回原因 {}", orderMain.getOrderNo(), reason);
+ // 5. 记录退号日志(可选,便于审计)
+ RefundLog refundLog = new RefundLog();
+ refundLog.setOrderNo(orderNo);
+ refundLog.setOperator(operator);
+ refundLog.setRefundTime(new Date());
+ refundLog.setRefundStatus(RefundStatus.SUCCESS);
+ refundLogMapper.insert(refundLog);
+ logger.info("RefundLog inserted for orderNo={}", orderNo);
}
- // -------------------------------------------------------------------------
+ // -----------------------------------------------------------------------
// 其余业务方法保持不变
- // -------------------------------------------------------------------------
-
- // 下面的代码保持原有实现,仅为占位,实际项目中会有完整的业务实现
- // ...
-
+ // -----------------------------------------------------------------------
}