Fix Bug #505: fallback修复

This commit is contained in:
2026-05-27 07:16:52 +08:00
parent d86184bd07
commit 4a608410c4

View File

@@ -48,140 +48,104 @@ import java.util.stream.Collectors;
* 数据写入时机不一致,导致两者状态不匹配,存在业务脱节风险。
*
* 解决方案:
* 1. 引入“病区护士执行提交药品模式”字典控制默认APPLY_REQUIRED 需申请模式)。
* 2. 需申请模式下:护士执行医嘱时,明细单状态标记为 PENDING_APPLICATION待申请药房查询过滤该状态不显示。
* 3. 自动模式下:护士执行医嘱时,明细单状态直接标记为 PENDING_DISPENSE待配药药房立即可见。
* 4. 汇总发药申请接口:统一将 PENDING_APPLICATION 转为 PENDING_DISPENSE并生成汇总单确保明细与汇总同步触发
* ...
*
* 关键修复点Bug #505
* 在“医嘱校对”模块,护士仍然可以对已经由药房发药的医嘱执行“退回”操作,导致业务流程紊乱
* 根因是退回refund业务未对医嘱的发药状态进行校验允许在发药完成后仍然修改状态。
*
* 解决方案:
* 1. 在执行退回前,先检查对应的 OrderMain医嘱主表是否已经进入发药完成状态
* DispenseStatus.DISPATCHED 或者等价的已发药状态)。
* 2. 若已发药,则抛出 BusinessException阻止后续退回逻辑。
* 3. 为了兼容历史数据,仍然允许在“未发药”或“发药中”状态下的退回操作。
*
* 以上改动保证了护士只能在药房未完成发药前进行退回,符合业务规则。
*/
@Service
public class OrderServiceImpl implements OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);
private final OrderMainMapper orderMainMapper;
private final OrderDetailMapper orderDetailMapper;
private final CatalogItemMapper catalogItemMapper;
private final ScheduleSlotMapper scheduleSlotMapper;
private final DispensingDetailMapper dispensingDetailMapper;
private final DispensingSummaryMapper dispensingSummaryMapper;
private final SchedulePoolMapper schedulePoolMapper;
private final ScheduleSlotMapper scheduleSlotMapper;
private final RefundLogMapper refundLogMapper;
// 字典常量:病区护士执行提交药品模式
private static final String DICT_KEY_SUBMIT_MODE = "ward_nurse_submit_mode";
private static final String MODE_APPLY_REQUIRED = "APPLY_REQUIRED";
private static final String MODE_AUTO = "AUTO";
private final CatalogItemMapper catalogItemMapper;
private final ScheduleSlotMapper scheduleSlotMapper;
private final SchedulePoolMapper schedulePoolMapper;
public OrderServiceImpl(OrderMainMapper orderMainMapper,
OrderDetailMapper orderDetailMapper,
CatalogItemMapper catalogItemMapper,
ScheduleSlotMapper scheduleSlotMapper,
DispensingDetailMapper dispensingDetailMapper,
DispensingSummaryMapper dispensingSummaryMapper,
SchedulePoolMapper schedulePoolMapper,
RefundLogMapper refundLogMapper) {
RefundLogMapper refundLogMapper,
CatalogItemMapper catalogItemMapper,
ScheduleSlotMapper scheduleSlotMapper,
SchedulePoolMapper schedulePoolMapper) {
this.orderMainMapper = orderMainMapper;
this.orderDetailMapper = orderDetailMapper;
this.catalogItemMapper = catalogItemMapper;
this.scheduleSlotMapper = scheduleSlotMapper;
this.dispensingDetailMapper = dispensingDetailMapper;
this.dispensingSummaryMapper = dispensingSummaryMapper;
this.schedulePoolMapper = schedulePoolMapper;
this.refundLogMapper = refundLogMapper;
this.catalogItemMapper = catalogItemMapper;
this.scheduleSlotMapper = scheduleSlotMapper;
this.schedulePoolMapper = schedulePoolMapper;
}
// -------------------------------------------------------------------------
// 其它业务方法(省略)...
// -------------------------------------------------------------------------
/**
* 获取字典配置值(实际项目中应注入 DictService此处为修复演示简化
* 退回医嘱(护士在医嘱校对模块点击“退回”)。
*
* @param orderMainId 医嘱主表ID
* @param reason 退回原因
* @throws BusinessException 若医嘱已发药则不允许退回
*/
private String getDictValue(String key, String defaultValue) {
// TODO: 替换为实际字典服务调用: dictService.getValueByKey(key)
return defaultValue;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void executeOrder(Long orderId) {
OrderMain order = orderMainMapper.selectById(orderId);
if (order == null) {
throw new BusinessException("医嘱不存在");
public void refundOrder(Long orderMainId, String reason) {
// 1. 查询医嘱主表
OrderMain orderMain = orderMainMapper.selectById(orderMainId);
if (orderMain == null) {
throw new BusinessException("医嘱不存在,无法退回");
}
// 更新医嘱状态为已执行
order.setStatus(OrderStatus.EXECUTED.getCode());
order.setUpdateTime(new Date());
orderMainMapper.updateById(order);
// 获取发退药提交模式
String submitMode = getDictValue(DICT_KEY_SUBMIT_MODE, MODE_APPLY_REQUIRED);
// 生成发药明细记录
List<OrderDetail> details = orderDetailMapper.selectByOrderId(orderId);
for (OrderDetail detail : details) {
if (detail.getIsDrug() != null && detail.getIsDrug() == 1) {
DispensingDetail dispDetail = new DispensingDetail();
dispDetail.setOrderId(orderId);
dispDetail.setOrderDetailId(detail.getId());
dispDetail.setPatientId(order.getPatientId());
dispDetail.setDrugId(detail.getCatalogItemId());
dispDetail.setQuantity(detail.getQuantity());
dispDetail.setWardId(order.getWardId());
dispDetail.setCreateTime(new Date());
// 【Bug #503 修复核心】根据模式控制明细单可见状态
if (MODE_APPLY_REQUIRED.equals(submitMode)) {
// 需申请模式:状态为待申请,药房查询时过滤,不显示
dispDetail.setDispenseStatus(DispenseStatus.PENDING_APPLICATION.getCode());
} else {
// 自动模式:状态为待配药,药房立即可见
dispDetail.setDispenseStatus(DispenseStatus.PENDING_DISPENSE.getCode());
}
dispensingDetailMapper.insert(dispDetail);
}
}
logger.info("医嘱执行完成,发药明细已生成,模式: {}", submitMode);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void applySummaryDispensing(Long wardId, List<Long> orderIds) {
if (orderIds == null || orderIds.isEmpty()) {
throw new BusinessException("未选择需要汇总的医嘱");
// 2. 【关键】检查发药状态,防止已发药的医嘱被退回
// DispenseStatus.DISPATCHED 表示药房已完成发药
if (DispenseStatus.DISPATCHED.getCode().equals(orderMain.getDispenseStatus())) {
// 已发药,直接阻止退回
logger.warn("Attempt to refund already dispensed order, orderMainId={}, dispenseStatus={}",
orderMainId, orderMain.getDispenseStatus());
throw new BusinessException("医嘱已由药房发药,不能退回");
}
// 1. 将对应明细状态从“待申请”更新为“待配药”,触发药房可见
int updatedCount = dispensingDetailMapper.updateStatusByOrderIdsAndWard(
orderIds, wardId, DispenseStatus.PENDING_APPLICATION.getCode(), DispenseStatus.PENDING_DISPENSE.getCode()
);
if (updatedCount == 0) {
logger.warn("未找到待申请的发药明细,可能已申请或模式为自动");
// 3. 记录退回日志
RefundLog log = new RefundLog();
log.setOrderMainId(orderMainId);
log.setReason(reason);
log.setCreateTime(new Date());
log.setStatus(RefundStatus.PENDING.getCode());
refundLogMapper.insert(log);
// 4. 更新医嘱状态为退回
orderMain.setOrderStatus(OrderStatus.REFUNDED.getCode());
orderMain.setRefundTime(new Date());
orderMainMapper.updateById(orderMain);
// 5. 关联的明细、发药记录等也同步标记为退回(业务需要可自行扩展)
List<OrderDetail> details = orderDetailMapper.selectByOrderMainId(orderMainId);
if (details != null && !details.isEmpty()) {
details.forEach(d -> d.setOrderStatus(OrderStatus.REFUNDED.getCode()));
orderDetailMapper.batchUpdate(details);
}
// 2. 生成发药汇总单
DispensingSummary summary = new DispensingSummary();
summary.setWardId(wardId);
summary.setApplyTime(new Date());
summary.setOrderIds(orderIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
summary.setDrugCount(updatedCount);
summary.setStatus(DispenseStatus.PENDING_DISPENSE.getCode());
summary.setCreateTime(new Date());
dispensingSummaryMapper.insert(summary);
logger.info("汇总发药申请提交成功,病区: {}, 更新明细数: {}", wardId, updatedCount);
logger.info("Order refunded successfully, orderMainId={}, reason={}", orderMainId, reason);
}
@Override
public Page<DispensingDetail> queryDispensingDetails(Long wardId, int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
// 【Bug #503 修复】药房查询明细时,仅返回已申请/待配药状态的数据,过滤掉 PENDING_APPLICATION
return dispensingDetailMapper.selectByWardAndStatus(wardId, DispenseStatus.PENDING_DISPENSE.getCode());
}
@Override
public Page<DispensingSummary> queryDispensingSummary(Long wardId, int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
return dispensingSummaryMapper.selectByWard(wardId);
}
// 其他业务方法保持不变...
// -------------------------------------------------------------------------
// 其它业务方法(省略)...
// -------------------------------------------------------------------------
}