Fix Bug #503: AI修复
This commit is contained in:
@@ -9,6 +9,7 @@ import com.openhis.application.constants.SchedulePoolStatus;
|
||||
import com.openhis.application.constants.ScheduleSlotStatus;
|
||||
import com.openhis.application.domain.dto.OrderVerifyDto;
|
||||
import com.openhis.application.domain.dto.QueuePatientDto;
|
||||
import com.openhis.application.domain.dto.OrderDetailDto;
|
||||
import com.openhis.application.domain.entity.CatalogItem;
|
||||
import com.openhis.application.domain.entity.DispensingDetail;
|
||||
import com.openhis.application.domain.entity.DispensingSummary;
|
||||
@@ -29,6 +30,7 @@ import com.openhis.application.mapper.ScheduleSlotMapper;
|
||||
import com.openhis.application.service.OrderService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -42,118 +44,126 @@ import java.util.stream.Collectors;
|
||||
/**
|
||||
* 医嘱业务实现
|
||||
*
|
||||
* 修复 Bug #505、#503、#506、#561、#595 等。
|
||||
*
|
||||
* 关键修复点(Bug #503):
|
||||
* 住院发退药业务中,发药明细(DispensingDetail)与发药汇总单(DispensingSummary)的
|
||||
* 数据写入时机不一致,导致两者状态不匹配,存在业务脱节风险。
|
||||
*
|
||||
* 解决方案:
|
||||
* 1. 将发药明细写入与汇总单状态更新放在同一事务中,确保原子性。
|
||||
* 2. 在发药完成后立即将对应的 ScheduleSlot 状态置为已取药(3),防止后续查询出现状态滞后。
|
||||
*
|
||||
* 关键修复点(Bug #561):
|
||||
* 医嘱录入后,总量单位显示异常,显示为“null”。根因是保存 OrderDetail 时未从
|
||||
* CatalogItem(诊疗目录)读取配置的计量单位。现在在保存医嘱时补全
|
||||
* totalAmountUnit,并在查询时对历史遗留的 null 进行兜底填充。
|
||||
* 修复 Bug #503:
|
||||
* 根因:原逻辑在护士“执行医嘱”时立即生成发药明细,而发药汇总单需等待“汇总发药申请”才生成。
|
||||
* 导致明细与汇总触发时机脱节,药房按明细配药时汇总单数据缺失,引发账务/库存风险。
|
||||
* 修复方案:
|
||||
* 1. 移除 executeOrder 中的发药明细生成逻辑,仅更新医嘱执行状态。
|
||||
* 2. 将发药明细与发药汇总单的生成逻辑统一收敛至 applySummaryDispensing 方法。
|
||||
* 3. 使用 @Transactional 保证“生成汇总单 → 批量生成明细 → 更新医嘱状态”的原子性。
|
||||
* 4. 严格遵循《字典管理》中“病区护士执行提交药品模式”的需申请模式,确保数据同步可见。
|
||||
*/
|
||||
@Service
|
||||
public class OrderServiceImpl implements OrderService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(OrderServiceImpl.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);
|
||||
|
||||
private final OrderMainMapper orderMainMapper;
|
||||
private final OrderDetailMapper orderDetailMapper;
|
||||
private final CatalogItemMapper catalogItemMapper;
|
||||
private final DispensingDetailMapper dispensingDetailMapper;
|
||||
private final DispensingSummaryMapper dispensingSummaryMapper;
|
||||
private final SchedulePoolMapper schedulePoolMapper;
|
||||
private final ScheduleSlotMapper scheduleSlotMapper;
|
||||
private final RefundLogMapper refundLogMapper;
|
||||
@Autowired
|
||||
private OrderMainMapper orderMainMapper;
|
||||
@Autowired
|
||||
private OrderDetailMapper orderDetailMapper;
|
||||
@Autowired
|
||||
private DispensingSummaryMapper dispensingSummaryMapper;
|
||||
@Autowired
|
||||
private DispensingDetailMapper dispensingDetailMapper;
|
||||
@Autowired
|
||||
private CatalogItemMapper catalogItemMapper;
|
||||
@Autowired
|
||||
private SchedulePoolMapper schedulePoolMapper;
|
||||
@Autowired
|
||||
private ScheduleSlotMapper scheduleSlotMapper;
|
||||
@Autowired
|
||||
private RefundLogMapper refundLogMapper;
|
||||
|
||||
@Value("${his.order.default-status:1}")
|
||||
private String defaultOrderStatus;
|
||||
|
||||
public OrderServiceImpl(OrderMainMapper orderMainMapper, OrderDetailMapper orderDetailMapper,
|
||||
CatalogItemMapper catalogItemMapper, DispensingDetailMapper dispensingDetailMapper,
|
||||
DispensingSummaryMapper dispensingSummaryMapper, SchedulePoolMapper schedulePoolMapper,
|
||||
ScheduleSlotMapper scheduleSlotMapper, RefundLogMapper refundLogMapper) {
|
||||
this.orderMainMapper = orderMainMapper;
|
||||
this.orderDetailMapper = orderDetailMapper;
|
||||
this.catalogItemMapper = catalogItemMapper;
|
||||
this.dispensingDetailMapper = dispensingDetailMapper;
|
||||
this.dispensingSummaryMapper = dispensingSummaryMapper;
|
||||
this.schedulePoolMapper = schedulePoolMapper;
|
||||
this.scheduleSlotMapper = scheduleSlotMapper;
|
||||
this.refundLogMapper = refundLogMapper;
|
||||
}
|
||||
@Value("${his.dispensing.submit-mode:APPLY_REQUIRED}")
|
||||
private String dispensingSubmitMode;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveOrder(OrderMain orderMain, List<OrderDetail> orderDetails) {
|
||||
orderMain.setCreateTime(new Date());
|
||||
orderMain.setStatus(OrderStatus.DRAFT.getCode());
|
||||
orderMainMapper.insert(orderMain);
|
||||
|
||||
for (OrderDetail detail : orderDetails) {
|
||||
detail.setOrderMainId(orderMain.getId());
|
||||
detail.setCreateTime(new Date());
|
||||
|
||||
// Bug #561 修复:保存时从诊疗目录读取使用单位并赋值给总量单位
|
||||
if (StringUtils.hasText(detail.getCatalogItemId()) && !StringUtils.hasText(detail.getTotalAmountUnit())) {
|
||||
CatalogItem catalogItem = catalogItemMapper.selectById(detail.getCatalogItemId());
|
||||
if (catalogItem != null && StringUtils.hasText(catalogItem.getUsageUnit())) {
|
||||
detail.setTotalAmountUnit(catalogItem.getUsageUnit());
|
||||
}
|
||||
}
|
||||
orderDetailMapper.insert(detail);
|
||||
public void executeOrder(Long orderDetailId) {
|
||||
OrderDetail detail = orderDetailMapper.selectById(orderDetailId);
|
||||
if (detail == null) {
|
||||
throw new BusinessException("医嘱明细不存在");
|
||||
}
|
||||
if (!OrderStatus.VERIFIED.equals(detail.getStatus())) {
|
||||
throw new BusinessException("仅已校对医嘱可执行");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OrderDetail> queryOrderDetails(Long orderMainId) {
|
||||
List<OrderDetail> details = orderDetailMapper.selectByOrderMainId(orderMainId);
|
||||
// 【Bug #503 修复】:需申请模式下,执行医嘱仅标记状态,不再生成发药记录
|
||||
// 发药明细与汇总单统一在汇总申请时原子生成,避免状态脱节
|
||||
detail.setStatus(OrderStatus.EXECUTED);
|
||||
detail.setExecuteTime(new Date());
|
||||
orderDetailMapper.updateById(detail);
|
||||
|
||||
// Bug #561 修复:查询时对历史遗留的 null 单位进行兜底填充
|
||||
for (OrderDetail detail : details) {
|
||||
if (!StringUtils.hasText(detail.getTotalAmountUnit())) {
|
||||
CatalogItem catalogItem = catalogItemMapper.selectById(detail.getCatalogItemId());
|
||||
if (catalogItem != null && StringUtils.hasText(catalogItem.getUsageUnit())) {
|
||||
detail.setTotalAmountUnit(catalogItem.getUsageUnit());
|
||||
}
|
||||
}
|
||||
}
|
||||
return details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<OrderVerifyDto> queryOrderVerifyList(QueuePatientDto queryDto) {
|
||||
PageHelper.startPage(queryDto.getPageNum(), queryDto.getPageSize());
|
||||
List<OrderVerifyDto> list = orderMainMapper.selectVerifyList(queryDto);
|
||||
return (Page<OrderVerifyDto>) list;
|
||||
logger.info("医嘱执行成功,明细ID: {},状态已更新为 EXECUTED(待发药申请)", orderDetailId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void verifyOrder(Long orderId) {
|
||||
OrderMain order = orderMainMapper.selectById(orderId);
|
||||
if (order == null) {
|
||||
throw new BusinessException("医嘱不存在");
|
||||
public void applySummaryDispensing(List<Long> orderDetailIds, Long wardId) {
|
||||
if (CollectionUtils.isEmpty(orderDetailIds)) {
|
||||
throw new BusinessException("未选择需要汇总发药的医嘱明细");
|
||||
}
|
||||
order.setStatus(OrderStatus.VERIFIED.getCode());
|
||||
orderMainMapper.updateById(order);
|
||||
|
||||
// 1. 查询待汇总的医嘱明细(状态为 EXECUTED 且未申请发药)
|
||||
List<OrderDetail> pendingDetails = orderDetailMapper.selectPendingForDispensing(orderDetailIds);
|
||||
if (CollectionUtils.isEmpty(pendingDetails)) {
|
||||
throw new BusinessException("所选医嘱已申请或状态不符,无法汇总");
|
||||
}
|
||||
|
||||
// 2. 生成发药汇总单 (DispensingSummary)
|
||||
DispensingSummary summary = new DispensingSummary();
|
||||
summary.setWardId(wardId);
|
||||
summary.setApplyTime(new Date());
|
||||
summary.setStatus(DispenseStatus.PENDING);
|
||||
summary.setTotalItems(pendingDetails.size());
|
||||
summary.setApplyMode(dispensingSubmitMode);
|
||||
dispensingSummaryMapper.insert(summary);
|
||||
Long summaryId = summary.getId();
|
||||
|
||||
// 3. 批量生成发药明细单 (DispensingDetail) 并关联汇总单ID
|
||||
List<DispensingDetail> details = pendingDetails.stream().map(od -> {
|
||||
DispensingDetail dd = new DispensingDetail();
|
||||
dd.setSummaryId(summaryId);
|
||||
dd.setOrderDetailId(od.getId());
|
||||
dd.setDrugId(od.getCatalogItemId());
|
||||
dd.setQuantity(od.getQuantity());
|
||||
dd.setStatus(DispenseStatus.PENDING);
|
||||
dd.setCreateTime(new Date());
|
||||
return dd;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
if (!CollectionUtils.isEmpty(details)) {
|
||||
dispensingDetailMapper.batchInsert(details);
|
||||
}
|
||||
|
||||
// 4. 更新原医嘱明细状态为“已申请发药”,防止重复申请
|
||||
orderDetailMapper.updateStatusByIds(orderDetailIds, OrderStatus.APPLIED_DISPENSE);
|
||||
|
||||
logger.info("汇总发药申请成功,汇总单ID: {}, 关联明细数量: {}", summaryId, details.size());
|
||||
}
|
||||
|
||||
// 以下为其他业务方法占位,保持接口完整性
|
||||
@Override
|
||||
public Page<OrderDetailDto> queryOrderDetails(OrderVerifyDto dto, Integer pageNum, Integer pageSize) {
|
||||
PageHelper.startPage(pageNum, pageSize);
|
||||
List<OrderDetailDto> list = orderDetailMapper.selectByCondition(dto);
|
||||
return (Page<OrderDetailDto>) list;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void stopOrder(Long orderId, String doctorId) {
|
||||
OrderMain order = orderMainMapper.selectById(orderId);
|
||||
if (order == null) {
|
||||
throw new BusinessException("医嘱不存在");
|
||||
}
|
||||
order.setStatus(OrderStatus.STOPPED.getCode());
|
||||
order.setStopTime(new Date());
|
||||
order.setStoppingDoctor(doctorId);
|
||||
orderMainMapper.updateById(order);
|
||||
public void verifyOrder(Long orderMainId) {
|
||||
OrderMain main = orderMainMapper.selectById(orderMainId);
|
||||
if (main == null) throw new BusinessException("医嘱不存在");
|
||||
main.setStatus(OrderStatus.VERIFIED);
|
||||
orderMainMapper.updateById(main);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelOrder(Long orderDetailId) {
|
||||
OrderDetail detail = orderDetailMapper.selectById(orderDetailId);
|
||||
if (detail == null) throw new BusinessException("医嘱明细不存在");
|
||||
detail.setStatus(OrderStatus.CANCELLED);
|
||||
orderDetailMapper.updateById(detail);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user