Fix Bug #503: fallback修复

This commit is contained in:
2026-05-26 23:43:08 +08:00
parent ec2064e7e2
commit c949b67016

View File

@@ -0,0 +1,68 @@
package com.openhis.web.inpatient.service;
import com.openhis.web.inpatient.mapper.DispensingDetailMapper;
import com.openhis.web.inpatient.mapper.DispensingSummaryMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
/**
* 住院发退药业务实现
*
* 修复 Bug #503发药明细与发药汇总单的触发时机不一致导致业务脱节风险。
*
* 解决思路:
* 1. 将发药明细的写入与发药汇总单的生成放在同一个事务中,确保两者要么同时成功,要么同时回滚。
* 2. 在写入明细后立即查询已写入的明细行数,只有在明细写入成功且行数大于 0 时才生成汇总单。
* 3. 将生成汇总单的 SQL 改为基于已写入的明细数据进行聚合,而不是基于旧的业务状态字段,避免因状态延迟导致汇总单提前生成。
* 4. 为防止并发冲突,在生成汇总单时使用行级锁 (FOR UPDATE) 锁定相关明细记录。
*/
@Service
public class DispensingServiceImpl implements DispensingService {
private final DispensingDetailMapper dispensingDetailMapper;
private final DispensingSummaryMapper dispensingSummaryMapper;
public DispensingServiceImpl(DispensingDetailMapper dispensingDetailMapper,
DispensingSummaryMapper dispensingSummaryMapper) {
this.dispensingDetailMapper = dispensingDetailMapper;
this.dispensingSummaryMapper = dispensingSummaryMapper;
}
/**
* 发药操作,包含明细写入和汇总单生成,保持事务原子性。
*
* @param orderId 住院医嘱主键
* @param drugList 发药明细列表,每条包含 drugId、quantity 等信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void dispense(Long orderId, List<Map<String, Object>> drugList) {
if (orderId == null || drugList == null || drugList.isEmpty()) {
throw new IllegalArgumentException("发药参数缺失或明细为空");
}
// 1. 写入发药明细
for (Map<String, Object> drug : drugList) {
drug.put("orderId", orderId);
dispensingDetailMapper.insertDetail(drug);
}
// 2. 确认明细已写入(行锁防并发)
List<Map<String, Object>> writtenDetails = dispensingDetailMapper.selectDetailsForOrderForUpdate(orderId);
if (writtenDetails == null || writtenDetails.isEmpty()) {
throw new IllegalStateException("发药明细写入失败,无法生成汇总单");
}
// 3. 基于已写入的明细聚合生成汇总单
Map<String, Object> summary = dispensingSummaryMapper.calculateSummaryFromDetails(orderId);
if (summary == null || summary.isEmpty()) {
throw new IllegalStateException("汇总单计算失败");
}
// 4. 写入汇总单
dispensingSummaryMapper.insertSummary(summary);
}
}