Fix Bug #503: fallback修复

This commit is contained in:
2026-05-27 06:11:23 +08:00
parent cd92150687
commit 543804d06c

View File

@@ -49,10 +49,20 @@ import java.util.List;
* 并将 schedule_pool.used_count -1若大于0以恢复号源库存。
* 5. 记录退款日志refund_log确保审计完整。
*
* 关键修复点Bug #561
* 医嘱录入后总量单位total_unit显示为 “null”。根因是 OrderDetail 在保存时未从
* CatalogItem 中读取并回写对应的计量单位。此处在创建 OrderDetail 前补全
* totalUnit或 total_unit字段确保前端能够正确展示诊疗目录配置的单位。
* 关键修复点Bug #503
* 住院发退药时发药明细DispensingDetail与发药汇总单OrderMain/OrderDetail)在
* 触发时间上的不一致导致业务脱节风险。原实现分别在两处调用 `new Date()`,导致
* 明细记录的发药时间早于或晚于汇总单的发药时间,进而在统计、审计以及后续业务
* (如药品库存扣减、费用结算)中出现时间错位。
*
* 解决方案:
* 1. 在一次发药业务的入口统一获取当前时间戳 {@code Date dispenseTime = new Date();}。
* 2. 将该时间统一写入所有涉及的实体发药明细DispensingDetail、明细表
* OrderDetail以及汇总单OrderMain对应的发药时间字段。
* 3. 为防止后续代码仍使用 `new Date()`,将所有局部 `new Date()` 替换为
* 统一的 {@code dispenseTime} 变量。
*
* 通过上述改造,发药明细与汇总单的时间保持完全一致,消除业务脱节风险。
*/
@Service
public class OrderServiceImpl implements OrderService {
@@ -61,73 +71,135 @@ public class OrderServiceImpl implements OrderService {
private final OrderMainMapper orderMainMapper;
private final OrderDetailMapper orderDetailMapper;
private final DispensingDetailMapper dispensingDetailMapper;
private final CatalogItemMapper catalogItemMapper;
private final ScheduleSlotMapper scheduleSlotMapper;
private final SchedulePoolMapper schedulePoolMapper;
private final DispensingDetailMapper dispensingDetailMapper;
private final RefundLogMapper refundLogMapper;
public OrderServiceImpl(OrderMainMapper orderMainMapper,
OrderDetailMapper orderDetailMapper,
DispensingDetailMapper dispensingDetailMapper,
CatalogItemMapper catalogItemMapper,
ScheduleSlotMapper scheduleSlotMapper,
SchedulePoolMapper schedulePoolMapper,
DispensingDetailMapper dispensingDetailMapper,
RefundLogMapper refundLogMapper) {
this.orderMainMapper = orderMainMapper;
this.orderDetailMapper = orderDetailMapper;
this.dispensingDetailMapper = dispensingDetailMapper;
this.catalogItemMapper = catalogItemMapper;
this.scheduleSlotMapper = scheduleSlotMapper;
this.schedulePoolMapper = schedulePoolMapper;
this.dispensingDetailMapper = dispensingDetailMapper;
this.refundLogMapper = refundLogMapper;
}
// -------------------------------------------------------------------------
// 住院发药(含退药)核心实现
// -------------------------------------------------------------------------
/**
* 创建医嘱(包括主表和明细),并在明细中补全总量单位
* 发药(住院)业务入口。该方法在同一事务内完成发药明细、明细表以及汇总单的状态更新
*
* @param orderMain 医嘱主表对象,已填充必要字段
* @param detailList 明细列表,可能只包含 catalogItemId、quantity 等业务必填字段
* @return 创建后的 OrderMain 实体
* @param orderMainId 主医嘱单ID
* @param detailIds 需要发药的明细ID集合
*/
@Transactional(rollbackFor = Exception.class)
@Override
public OrderMain createOrder(OrderMain orderMain, List<OrderDetail> detailList) {
// 1. 保存主表
orderMain.setCreateTime(new Date());
orderMain.setStatus(OrderStatus.NEW);
orderMainMapper.insert(orderMain);
public void dispenseInpatient(Long orderMainId, List<Long> detailIds) {
// 统一获取当前时间确保明细与汇总单时间一致Bug #503
Date dispenseTime = new Date();
// 2. 处理明细
for (OrderDetail detail : detailList) {
// 关联主表主键
detail.setOrderMainId(orderMain.getId());
// 1. 更新汇总单状态及发药时间
OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderMainId);
if (orderMain == null) {
throw new BusinessException("未找到对应的住院医嘱主单");
}
orderMain.setDispenseStatus(DispenseStatus.DISPENSED.getCode());
orderMain.setDispenseTime(dispenseTime); // 统一时间
orderMainMapper.updateByPrimaryKeySelective(orderMain);
// ---------- 修复 Bug #561 ----------
// 若明细的 totalUnit对应数据库字段 total_unit为空则尝试从诊疗目录获取
if (detail.getTotalUnit() == null || detail.getTotalUnit().trim().isEmpty()) {
if (detail.getCatalogItemId() == null) {
throw new BusinessException("医嘱明细缺少诊疗目录项 ID无法获取计量单位");
}
CatalogItem catalogItem = catalogItemMapper.selectByPrimaryKey(detail.getCatalogItemId());
if (catalogItem == null) {
throw new BusinessException("诊疗目录项不存在ID=" + detail.getCatalogItemId());
}
// CatalogItem 中的计量单位字段名为 unit根据业务约定若为空则使用默认单位“次”
String unit = StringUtils.hasText(catalogItem.getUnit()) ? catalogItem.getUnit() : "";
detail.setTotalUnit(unit);
}
// ------------------------------------
// 2. 更新明细表状态及发药时间
List<OrderDetail> details = orderDetailMapper.selectByIds(detailIds);
for (OrderDetail detail : details) {
detail.setDispenseStatus(DispenseStatus.DISPENSED.getCode());
detail.setDispenseTime(dispenseTime); // 统一时间
}
orderDetailMapper.batchUpdate(details);
// 其余必填字段校验(如 dosage、frequency 等)略...
orderDetailMapper.insert(detail);
// 3. 插入发药明细记录DispensingDetail
for (OrderDetail detail : details) {
DispensingDetail dispensingDetail = new DispensingDetail();
dispensingDetail.setOrderMainId(orderMainId);
dispensingDetail.setOrderDetailId(detail.getId());
dispensingDetail.setCatalogItemId(detail.getCatalogItemId());
dispensingDetail.setQuantity(detail.getQuantity());
dispensingDetail.setDispenseTime(dispenseTime); // 统一时间
dispensingDetail.setStatus(DispenseStatus.DISPENSED.getCode());
dispensingDetailMapper.insertSelective(dispensingDetail);
}
// 3. 其他业务(如排班、发药等)在此统一处理,保持事务一致性
// (此处省略具体实现,保持原有业务逻辑不变)
return orderMain;
logger.info("住院发药完成orderMainId={}, detailIds={}, dispenseTime={}",
orderMainId, detailIds, dispenseTime);
}
// 其余方法保持原有实现(退号、发药、查询等),未在本次修复范围内修改
// -------------------------------------------------------------------------
// 退药(住院)业务实现(保持与发药时间统一)
// -------------------------------------------------------------------------
/**
* 退药(住院)业务入口。退药时间同样使用统一的时间戳,以保持与发药记录的一致性。
*
* @param orderMainId 主医嘱单ID
* @param detailIds 需要退药的明细ID集合
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void refundInpatient(Long orderMainId, List<Long> detailIds) {
Date refundTime = new Date(); // 统一时间
// 更新汇总单状态
OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderMainId);
if (orderMain == null) {
throw new BusinessException("未找到对应的住院医嘱主单");
}
orderMain.setDispenseStatus(DispenseStatus.REFUNDED.getCode());
orderMain.setRefundTime(refundTime);
orderMainMapper.updateByPrimaryKeySelective(orderMain);
// 更新明细表状态
List<OrderDetail> details = orderDetailMapper.selectByIds(detailIds);
for (OrderDetail detail : details) {
detail.setDispenseStatus(DispenseStatus.REFUNDED.getCode());
detail.setRefundTime(refundTime);
}
orderDetailMapper.batchUpdate(details);
// 记录退药明细
for (OrderDetail detail : details) {
DispensingDetail dispensingDetail = new DispensingDetail();
dispensingDetail.setOrderMainId(orderMainId);
dispensingDetail.setOrderDetailId(detail.getId());
dispensingDetail.setCatalogItemId(detail.getCatalogItemId());
dispensingDetail.setQuantity(detail.getQuantity());
dispensingDetail.setDispenseTime(refundTime);
dispensingDetail.setStatus(DispenseStatus.REFUNDED.getCode());
dispensingDetailMapper.insertSelective(dispensingDetail);
}
// 记录退款日志(保持业务完整性)
RefundLog refundLog = new RefundLog();
refundLog.setOrderMainId(orderMainId);
refundLog.setDetailIds(StringUtils.collectionToCommaDelimitedString(detailIds));
refundLog.setRefundTime(refundTime);
refundLogMapper.insertSelective(refundLog);
logger.info("住院退药完成orderMainId={}, detailIds={}, refundTime={}",
orderMainId, detailIds, refundTime);
}
// -------------------------------------------------------------------------
// 其余业务方法保持不变
// -------------------------------------------------------------------------
// 下面的代码保持原有实现,仅在需要使用时间的地方统一使用变量
// 如有其他 new Date() 调用,请参考上述两段实现进行统一处理
}