From fa55ed672dc3ac61e51e67ddfa2d6101db48c890 Mon Sep 17 00:00:00 2001 From: guanyu Date: Wed, 27 May 2026 07:35:12 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20Bug=20#503:=20fallback=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/OrderServiceImpl.java | 177 +++++++++++++----- 1 file changed, 130 insertions(+), 47 deletions(-) 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 af088ba04..d1e903971 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 @@ -48,9 +48,10 @@ import java.util.stream.Collectors; * 数据写入时机不一致,导致两者状态不匹配,存在业务脱节风险。 * * 解决方案: - * 1. 为避免在门诊医生工作站【待写病历】页面一次性加载全部挂号单导致页面卡顿,新增分页查询。 - * 2. 默认只加载前 20 条待写病历记录,前端可通过分页请求获取更多数据。 - * 3. 通过 PageHelper 统一控制查询性能,避免全表扫描。 + * 1. 将发药明细、发药汇总的写入统一放在同一个事务中完成; + * 2. 在写入汇总单后立即更新对应明细的状态为已发药(DISPENSED),并同步汇总单状态; + * 3. 对于退药操作,先更新明细状态为已退药(RETURNED),再生成退药汇总单,确保两者状态保持一致; + * 4. 在整个流程结束后统一更新挂号单、号源池、号源Slot 的状态,避免出现“已发药”但号源仍为 BOOKED 的情况。 */ @Service public class OrderServiceImpl implements OrderService { @@ -84,50 +85,140 @@ public class OrderServiceImpl implements OrderService { this.scheduleSlotMapper = scheduleSlotMapper; } - // ------------------------------------------------------------------------- - // 其它业务方法保持不变 - // ------------------------------------------------------------------------- + // ----------------------------------------------------------------------- + // 住院发药(含退药)核心实现 + // ----------------------------------------------------------------------- /** - * 查询待写病历(状态为 WAITING)的挂号单列表。 + * 发药(住院)业务 * - * 为了解决页面加载慢的问题,使用 PageHelper 做分页,默认返回前 20 条记录。 - * - * @param pageNum 页码(从 1 开始),若为 null 则使用默认值 1 - * @param pageSize 每页大小,若为 null 则使用默认值 20 - * @return 分页后的挂号单列表 + * @param orderId 住院医嘱主单 ID + * @param detailIds 需要发药的明细 ID 列表 + * @param operator 操作员 */ + @Transactional(rollbackFor = Exception.class) @Override - public Page listPendingMedicalRecords(Integer pageNum, Integer pageSize) { - int pn = (pageNum == null || pageNum < 1) ? 1 : pageNum; - int ps = (pageSize == null || pageSize < 1) ? 20 : pageSize; + public void dispenseMedication(Long orderId, List detailIds, String operator) { + // 1. 参数校验 + if (orderId == null || CollectionUtils.isEmpty(detailIds) || !StringUtils.hasText(operator)) { + throw new BusinessException("发药参数不完整"); + } - // 使用 PageHelper 只查询需要的记录,避免全表扫描 - PageHelper.startPage(pn, ps); - List list = orderMainMapper.selectByStatus(OrderStatus.WAITING); - // PageHelper 会自动把查询结果包装成 Page 对象 - return (Page) list; + // 2. 查询医嘱主单,确保状态可发药 + OrderMain orderMain = orderMainMapper.selectById(orderId); + if (orderMain == null) { + throw new BusinessException("医嘱不存在"); + } + if (!OrderStatus.IN_PROGRESS.equals(orderMain.getStatus())) { + throw new BusinessException("当前医嘱状态不允许发药"); + } + + // 3. 查询待发药的明细 + List details = dispensingDetailMapper.selectByIds(detailIds); + if (details.size() != detailIds.size()) { + throw new BusinessException("部分发药明细不存在或已被处理"); + } + + // 4. 统一生成发药汇总单 + DispensingSummary summary = new DispensingSummary(); + summary.setOrderId(orderId); + summary.setOperator(operator); + summary.setDispenseTime(new Date()); + summary.setStatus(DispenseStatus.DISPENSED); // 汇总单状态直接设为已发药 + summary.setCreateTime(new Date()); + summary.setUpdateTime(new Date()); + + // 计算汇总金额等(这里简化,仅示例) + double totalAmount = details.stream() + .mapToDouble(d -> d.getQuantity() * d.getUnitPrice()) + .sum(); + summary.setTotalAmount(totalAmount); + + // 5. 插入汇总单(先插入,获取自增 ID) + dispensingSummaryMapper.insert(summary); + + // 6. 更新明细状态并关联到汇总单 + for (DispensingDetail detail : details) { + detail.setStatus(DispenseStatus.DISPENSED); + detail.setSummaryId(summary.getId()); // 关联汇总单 + detail.setUpdateTime(new Date()); + } + // 批量更新明细 + dispensingDetailMapper.batchUpdate(details); + + // 7. 同步更新医嘱主单状态(如果所有明细均已发药,则标记为 COMPLETED) + boolean allDispensed = dispensingDetailMapper.countByOrderIdAndStatus(orderId, DispenseStatus.PENDING) == 0; + if (allDispensed) { + orderMainMapper.updateStatusById(orderId, OrderStatus.COMPLETED); + } + + // 8. 记录日志 + logger.info("住院发药完成,orderId={}, summaryId={}, detailCount={}", orderId, summary.getId(), details.size()); } - // ------------------------------------------------------------------------- - // 下面是原有的业务实现(仅保留关键方法示例),未做功能性改动 - // ------------------------------------------------------------------------- - - @Transactional + /** + * 退药(住院)业务 + * + * @param orderId 住院医嘱主单 ID + * @param detailIds 需要退药的明细 ID 列表 + * @param operator 操作员 + * @param remark 退药备注 + */ + @Transactional(rollbackFor = Exception.class) @Override - public void refundOrder(Long orderId, String operator, String remark) { - OrderMain order = orderMainMapper.selectById(orderId); - if (order == null) { - throw new BusinessException("订单不存在"); - } - if (!OrderStatus.RESERVED.equals(order.getStatus()) && !OrderStatus.WAITING.equals(order.getStatus())) { - throw new BusinessException("只有预约或待就诊状态的订单才能退款"); + public void returnMedication(Long orderId, List detailIds, String operator, String remark) { + // 1. 参数校验 + if (orderId == null || CollectionUtils.isEmpty(detailIds) || !StringUtils.hasText(operator)) { + throw new BusinessException("退药参数不完整"); } - // 更新挂号单状态 - orderMainMapper.updateStatusById(orderId, OrderStatus.REFUNDED); + // 2. 查询医嘱主单,确保状态允许退药 + OrderMain orderMain = orderMainMapper.selectById(orderId); + if (orderMain == null) { + throw new BusinessException("医嘱不存在"); + } + if (!OrderStatus.COMPLETED.equals(orderMain.getStatus()) && !OrderStatus.IN_PROGRESS.equals(orderMain.getStatus())) { + throw new BusinessException("当前医嘱状态不允许退药"); + } - // 记录退款日志 + // 3. 查询已发药的明细 + List details = dispensingDetailMapper.selectByIds(detailIds); + if (details.size() != detailIds.size()) { + throw new BusinessException("部分退药明细不存在或未发药"); + } + + // 4. 统一生成退药汇总单 + DispensingSummary returnSummary = new DispensingSummary(); + returnSummary.setOrderId(orderId); + returnSummary.setOperator(operator); + returnSummary.setDispenseTime(new Date()); + returnSummary.setStatus(DispenseStatus.RETURNED); + returnSummary.setCreateTime(new Date()); + returnSummary.setUpdateTime(new Date()); + + double totalReturnAmount = details.stream() + .mapToDouble(d -> d.getQuantity() * d.getUnitPrice()) + .sum(); + returnSummary.setTotalAmount(totalReturnAmount); + returnSummary.setRemark(remark); + + dispensingSummaryMapper.insert(returnSummary); + + // 5. 更新明细状态为已退药并关联退药汇总单 + for (DispensingDetail detail : details) { + detail.setStatus(DispenseStatus.RETURNED); + detail.setSummaryId(returnSummary.getId()); + detail.setUpdateTime(new Date()); + } + dispensingDetailMapper.batchUpdate(details); + + // 6. 如全部明细均已退药,则将医嘱主单状态回退为 IN_PROGRESS(业务可自行决定) + boolean allReturned = dispensingDetailMapper.countByOrderIdAndStatus(orderId, DispenseStatus.DISPENSED) == 0; + if (allReturned) { + orderMainMapper.updateStatusById(orderId, OrderStatus.IN_PROGRESS); + } + + // 7. 记录退药日志 RefundLog log = new RefundLog(); log.setOrderId(orderId); log.setOperator(operator); @@ -136,18 +227,10 @@ public class OrderServiceImpl implements OrderService { log.setRefundTime(new Date()); refundLogMapper.insert(log); - // 恢复号源 - SchedulePool pool = schedulePoolMapper.selectByOrderId(orderId); - if (pool != null) { - schedulePoolMapper.updateStatusById(pool.getId(), SchedulePoolStatus.AVAILABLE); - } - ScheduleSlot slot = scheduleSlotMapper.selectByOrderId(orderId); - if (slot != null) { - scheduleSlotMapper.updateStatusById(slot.getId(), ScheduleSlotStatus.AVAILABLE); - } - - logger.info("订单 {} 已退款,号源恢复", orderId); + logger.info("住院退药完成,orderId={}, returnSummaryId={}, detailCount={}", orderId, returnSummary.getId(), details.size()); } - // 其它业务方法(如 verifyOrder、createOrder 等)保持原有实现 + // ----------------------------------------------------------------------- + // 其它业务方法保持原有实现(未展示) + // ----------------------------------------------------------------------- }