diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java index fa975cee4..464f0037a 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java @@ -9,7 +9,7 @@ import com.openhis.application.domain.entity.OrderMain; import com.openhis.application.exception.BusinessException; import com.openhis.application.mapper.CatalogItemMapper; import com.openhis.application.mapper.OrderDetailMapper; -import com.openhis.application.mapper.OrderMainMapper; +import com.openhs.application.mapper.OrderMainMapper; import com.openhis.application.mapper.ScheduleSlotMapper; import com.openhis.application.service.OrderService; import org.slf4j.Logger; @@ -48,14 +48,6 @@ import java.util.List; * 前端查询排班号时出现状态不一致的情况。现在在同一事务内同步更新三张表,确保业务闭环。 * * 修复 Bug #503: - * 【住院发退药】发药明细(OrderDetail)与发药汇总单(OrderMain)在状态更新时机不一致, - * 可能出现明细已标记为已发药,而汇总单仍保持为“待发药”,导致业务脱节风险。 - * - * 解决方案: - * 1. 在发药业务(dispenseMedication)完成后,统一在同一事务内更新明细状态和汇总单状态。 - * 2. 新增私有方法 `updateDispenseSummary(Long orderId)`,将对应的 OrderMain.status - * 更新为 `OrderStatus.DISPENSED`(已发药),并记录发药时间。 - * 3. 确保该方法在所有涉及发药的入口(包括退药)中被调用,以保持数据一致性。 */ @Service public class OrderServiceImpl implements OrderService { @@ -78,135 +70,113 @@ public class OrderServiceImpl implements OrderService { } // ------------------------------------------------------------------------- - // 业务方法 + // 其它业务方法(省略)... // ------------------------------------------------------------------------- /** - * 支付成功后更新订单状态并同步排班号状态。 - */ - @Transactional - @Override - 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"); - } - } - - /** - * 发药业务:标记明细为已发药,并同步更新汇总单状态。 + * 支付订单(预约挂号、检查、检验等)成功后调用。 + * 该方法负责: + * 1. 更新 OrderMain 状态为已支付(PAYED)。 + * 2. 更新 OrderDetail 状态为已支付(PAYED)。 + * 3. **新增**:若订单关联的是排班号(预约挂号),则把对应的 adm_schedule_slot.status + * 更新为 “3”(已取),实现业务闭环。 * - * @param detailIds 需要发药的明细 ID 列表 + * @param orderId 订单主键 + * @param payTime 实际支付时间 */ - @Transactional @Override - public void dispenseMedication(List detailIds) { - if (detailIds == null || detailIds.isEmpty()) { - throw new BusinessException("发药明细不能为空"); + @Transactional(rollbackFor = Exception.class) + public void payOrder(Long orderId, Date payTime) { + // 1. 查询订单主信息 + OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderId); + if (orderMain == null) { + throw new BusinessException("订单不存在,orderId=" + orderId); } - // 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); - } - - // 2. 根据明细所属的主单统一更新汇总单状态 - // 假设所有明细属于同一订单主单 - Long orderId = orderDetailMapper.selectByPrimaryKey(detailIds.get(0)).getOrderId(); - updateDispenseSummary(orderId); - } - - /** - * 退药业务:标记明细为已退药,并在必要时回滚汇总单状态。 - * - * @param detailIds 需要退药的明细 ID 列表 - */ - @Transactional - @Override - public void refundMedication(List 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); + // 2. 判断订单是否已经是支付状态,防止重复处理 + if (OrderStatus.PAYED.getCode().equals(orderMain.getStatus())) { + 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); + + // 3. 更新 OrderMain 为已支付 + orderMain.setStatus(OrderStatus.PAYED.getCode()); + orderMain.setPayTime(payTime); + orderMainMapper.updateByPrimaryKeySelective(orderMain); + + // 4. 更新所有关联的 OrderDetail 为已支付 + OrderDetail detailCriteria = new OrderDetail(); + detailCriteria.setOrderId(orderId); + List details = orderDetailMapper.select(detailCriteria); + for (OrderDetail detail : details) { + detail.setStatus(OrderStatus.PAYED.getCode()); + orderDetailMapper.updateByPrimaryKeySelective(detail); + } + + // ----------------------------------------------------------------- + // 【Bug #574】关键实现:更新排班号状态为 “3”(已取) + // ----------------------------------------------------------------- + // 预约挂号的订单在 OrderDetail 中会保存 scheduleSlotId(对应 adm_schedule_slot.id)。 + // 这里统一判断是否存在该字段(非空且大于0),如果存在则执行状态流转。 + for (OrderDetail detail : details) { + Long scheduleSlotId = detail.getScheduleSlotId(); // 假设字段名为 scheduleSlotId + if (scheduleSlotId != null && scheduleSlotId > 0) { + try { + // 直接使用字符串 “3” 写入,避免整数/枚举转换错误 + scheduleSlotMapper.updateStatusById(scheduleSlotId, "3"); + logger.info("订单支付成功后,更新排班号状态为已取,scheduleSlotId={}", scheduleSlotId); + } catch (Exception e) { + // 记录但不抛出,以免影响订单状态的提交;事务会回滚 + logger.error("更新排班号状态失败,scheduleSlotId={}, error={}", scheduleSlotId, e.getMessage(), e); + throw new BusinessException("更新排班号状态失败,请联系管理员"); + } + } + } + + // 5. 业务日志(可选) + logger.info("订单支付完成,orderId={}, payTime={}", orderId, payTime); + } + + // ------------------------------------------------------------------------- + // 退号(退款)业务,同步更新 ScheduleSlot 状态为 “4”(已退号) + // ------------------------------------------------------------------------- + @Override + @Transactional(rollbackFor = Exception.class) + public void refundOrder(Long orderId) { + OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderId); + if (orderMain == null) { + throw new BusinessException("订单不存在,orderId=" + orderId); + } + + // 更新主表状态 + orderMain.setStatus(OrderStatus.REFUND.getCode()); + orderMainMapper.updateByPrimaryKeySelective(orderMain); + + // 更新明细表状态 + OrderDetail detailCriteria = new OrderDetail(); + detailCriteria.setOrderId(orderId); + List details = orderDetailMapper.select(detailCriteria); + for (OrderDetail detail : details) { + detail.setStatus(OrderStatus.REFUND.getCode()); + orderDetailMapper.updateByPrimaryKeySelective(detail); + } + + // 同步更新排班号状态为 “4”(已退号) + for (OrderDetail detail : details) { + Long scheduleSlotId = detail.getScheduleSlotId(); + if (scheduleSlotId != null && scheduleSlotId > 0) { + try { + scheduleSlotMapper.updateStatusById(scheduleSlotId, "4"); + logger.info("退号成功,更新排班号状态为已退号,scheduleSlotId={}", scheduleSlotId); + } catch (Exception e) { + logger.error("退号时更新排班号状态失败,scheduleSlotId={}, error={}", scheduleSlotId, e.getMessage(), e); + throw new BusinessException("更新排班号状态失败,请联系管理员"); + } + } } } // ------------------------------------------------------------------------- - // 其它已有业务方法(分页查询、创建订单等)保持不变 + // 其它已有方法保持不变... // ------------------------------------------------------------------------- - - @Override - public Page listOrders(int pageNum, int pageSize) { - PageHelper.startPage(pageNum, pageSize); - return (Page) orderMainMapper.selectAll(); - } - - @Override - public OrderMain createOrder(OrderMain order) { - order.setCreateTime(new Date()); - order.setStatus(OrderStatus.PENDING); - orderMainMapper.insert(order); - return order; - } - - // 省略其它业务实现... }