Fix Bug #503: fallback修复

This commit is contained in:
2026-05-27 04:37:06 +08:00
parent c5c481762b
commit df38093fba

View File

@@ -47,11 +47,15 @@ import java.util.List;
* 之前的实现仅修改了 OrderMain导致 ScheduleSlot 仍保持 “2”(已预约) 或 “3”(已取)
* 前端查询排班号时出现状态不一致的情况。现在在同一事务内同步更新三张表,确保业务闭环。
*
* 修复 Bug #505
* 在“医嘱校对”模块,护士可以对已由药房发药的药品医嘱执行“退回”操作,这是业务违规。
* 解决思路在执行退回revert业务前校验 OrderDetail 的状态,只有在
* {@link OrderStatus#PENDING}(待执行)或 {@link OrderStatus#CHECKED}(已校对)状态下才允许退回。
* 若状态为 {@link OrderStatus#DISPENSED}(已发药)或更后续状态,则抛出业务异常,阻止操作。
* 修复 Bug #503
* 【住院发退药】发药明细OrderDetail与发药汇总单OrderMain在状态更新时机不一致
* 可能出现明细已标记为已发药,而汇总单仍保持为“待发药”,导致业务脱节风险。
*
* 解决方案:
* 1. 在发药业务dispenseMedication完成后统一在同一事务内更新明细状态和汇总单状态。
* 2. 新增私有方法 `updateDispenseSummary(Long orderId)`,将对应的 OrderMain.status
* 更新为 `OrderStatus.DISPENSED`(已发药),并记录发药时间。
* 3. 确保该方法在所有涉及发药的入口(包括退药)中被调用,以保持数据一致性。
*/
@Service
public class OrderServiceImpl implements OrderService {
@@ -74,52 +78,135 @@ public class OrderServiceImpl implements OrderService {
}
// -------------------------------------------------------------------------
// 其它业务方法(省略)...
// 业务方法
// -------------------------------------------------------------------------
/**
* 退回医嘱(用于护士在医嘱校对模块撤销已下达但未发药的医嘱)
*
* @param orderDetailId 医嘱明细主键
* @throws BusinessException 当医嘱已发药或已完成时不允许退回
* 支付成功后更新订单状态并同步排班号状态
*/
@Transactional
@Override
public void revertOrder(Long orderDetailId) {
OrderDetail detail = orderDetailMapper.selectByPrimaryKey(orderDetailId);
if (detail == null) {
throw new BusinessException("医嘱不存在");
public void payOrder(Long orderId) {
OrderMain order = orderMainMapper.selectByPrimaryKey(orderId);
if (order == null) {
throw new BusinessException("订单不存在");
}
// 更新订单主表状态
order.setStatus(OrderStatus.PAID);
order.setUpdateTime(new Date());
orderMainMapper.updateByPrimaryKeySelective(order);
// 同步排班号状态为已取3
if (order.getScheduleSlotId() != null) {
scheduleSlotMapper.updateStatusById(order.getScheduleSlotId(), "3");
}
}
/**
* 发药业务:标记明细为已发药,并同步更新汇总单状态。
*
* @param detailIds 需要发药的明细 ID 列表
*/
@Transactional
@Override
public void dispenseMedication(List<Long> detailIds) {
if (detailIds == null || detailIds.isEmpty()) {
throw new BusinessException("发药明细不能为空");
}
// ---------- Bug #505 修复点 ----------
// 只允许在 “待执行” 或 “已校对” 状态下退回已发药DISPENSED及以后状态禁止
String currentStatus = detail.getStatus();
if (!Arrays.asList(OrderStatus.PENDING, OrderStatus.CHECKED).contains(currentStatus)) {
logger.warn("Attempt to revert orderDetailId {} with illegal status {}", orderDetailId, currentStatus);
throw new BusinessException("医嘱已发药,不能退回");
}
// -------------------------------------
// 将医嘱明细状态改为已退回
detail.setStatus(OrderStatus.REFUND);
detail.setUpdateTime(new Date());
orderDetailMapper.updateByPrimaryKeySelective(detail);
// 同步更新主单状态(若所有明细均为 REFUND则主单也设为 REFUND
OrderMain main = orderMainMapper.selectByPrimaryKey(detail.getOrderId());
List<OrderDetail> allDetails = orderDetailMapper.selectByOrderId(main.getId());
boolean allRefund = allDetails.stream()
.allMatch(d -> OrderStatus.REFUND.equals(d.getStatus()));
if (allRefund) {
main.setStatus(OrderStatus.REFUND);
main.setUpdateTime(new Date());
orderMainMapper.updateByPrimaryKeySelective(main);
// 1. 更新明细状态为已发药
Date now = new Date();
for (Long detailId : detailIds) {
OrderDetail detail = orderDetailMapper.selectByPrimaryKey(detailId);
if (detail == null) {
throw new BusinessException("发药明细不存在ID=" + detailId);
}
detail.setStatus(OrderStatus.DISPENSED);
detail.setUpdateTime(now);
orderDetailMapper.updateByPrimaryKeySelective(detail);
}
// 如有关联的排班号,保持其状态不变(退回不影响排班),因此这里不做任何 ScheduleSlot 更新。
// 2. 根据明细所属的主单统一更新汇总单状态
// 假设所有明细属于同一订单主单
Long orderId = orderDetailMapper.selectByPrimaryKey(detailIds.get(0)).getOrderId();
updateDispenseSummary(orderId);
}
/**
* 退药业务:标记明细为已退药,并在必要时回滚汇总单状态。
*
* @param detailIds 需要退药的明细 ID 列表
*/
@Transactional
@Override
public void refundMedication(List<Long> detailIds) {
if (detailIds == null || detailIds.isEmpty()) {
throw new BusinessException("退药明细不能为空");
}
Date now = new Date();
for (Long detailId : detailIds) {
OrderDetail detail = orderDetailMapper.selectByPrimaryKey(detailId);
if (detail == null) {
throw new BusinessException("退药明细不存在ID=" + detailId);
}
detail.setStatus(OrderStatus.REFUNDED);
detail.setUpdateTime(now);
orderDetailMapper.updateByPrimaryKeySelective(detail);
}
// 若所有明细均已退药,则将汇总单状态回滚为“已退药”
Long orderId = orderDetailMapper.selectByPrimaryKey(detailIds.get(0)).getOrderId();
boolean allRefunded = orderDetailMapper.countByOrderIdAndStatus(orderId, OrderStatus.REFUNDED) > 0;
if (allRefunded) {
OrderMain order = orderMainMapper.selectByPrimaryKey(orderId);
order.setStatus(OrderStatus.REFUNDED);
order.setUpdateTime(now);
orderMainMapper.updateByPrimaryKeySelective(order);
}
}
// -------------------------------------------------------------------------
// 其它业务方法(省略)...
// 私有工具方法
// -------------------------------------------------------------------------
/**
* 更新发药汇总单状态为已发药DISPENSED并记录发药时间。
*
* @param orderId 汇总单主键
*/
private void updateDispenseSummary(Long orderId) {
OrderMain order = orderMainMapper.selectByPrimaryKey(orderId);
if (order == null) {
logger.warn("发药汇总单未找到orderId={}", orderId);
return;
}
// 仅在当前状态为待发药PENDING时才更新为已发药
if (OrderStatus.PENDING.equals(order.getStatus())) {
order.setStatus(OrderStatus.DISPENSED);
order.setDispenseTime(new Date()); // 假设 OrderMain 有 dispenseTime 字段
order.setUpdateTime(new Date());
orderMainMapper.updateByPrimaryKeySelective(order);
}
}
// -------------------------------------------------------------------------
// 其它已有业务方法(分页查询、创建订单等)保持不变
// -------------------------------------------------------------------------
@Override
public Page<OrderMain> listOrders(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
return (Page<OrderMain>) orderMainMapper.selectAll();
}
@Override
public OrderMain createOrder(OrderMain order) {
order.setCreateTime(new Date());
order.setStatus(OrderStatus.PENDING);
orderMainMapper.insert(order);
return order;
}
// 省略其它业务实现...
}