package com.openhis.web.inpatient.service.impl; import com.openhis.web.inpatient.mapper.DispenseMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Map; /** * 住院发退药业务实现 * * 修复 Bug #503: * 住院发药时,发药明细(his_inpatient_dispense_detail)与发药汇总单 * (his_inpatient_dispense_summary)的数据写入时机不一致,导致先写入明细后 * 触发汇总单生成的异步任务未能及时感知,出现业务脱节风险。 * * 解决思路: * 1. 将明细写入、汇总单生成、汇总单状态更新全部放在同一个事务中完成; * 2. 在写入明细后立即调用 {@link DispenseMapper#updateSummaryAfterDetail(Long, Integer)} * 通过 SQL 直接在同事务内完成汇总统计,避免异步延迟; * 3. 对外统一返回业务成功/失败结构,保持与其它接口风格一致。 */ @Service public class DispenseServiceImpl { private final DispenseMapper dispenseMapper; public DispenseServiceImpl(DispenseMapper dispenseMapper) { this.dispenseMapper = dispenseMapper; } /** * 发药(包括明细写入与汇总单同步更新)。 * * @param dispenseId 发药单主键 * @param quantity 本次发药数量 * @return 业务结果映射,key 为 code(0 成功,1 失败),msg 为提示信息 */ @Transactional(rollbackFor = Exception.class) public Map dispense(Long dispenseId, Integer quantity) { // 1. 写入发药明细 dispenseMapper.insertDetail(dispenseId, quantity); // 2. 同步更新汇总单统计(在同事务内完成,确保时机一致) dispenseMapper.updateSummaryAfterDetail(dispenseId, quantity); // 3. 返回统一结构 return Map.of("code", 0, "msg", "发药成功"); } /** * 退药(明细与汇总同步回滚)。 * * @param dispenseId 发药单主键 * @param quantity 本次退药数量 * @return 业务结果映射 */ @Transactional(rollbackFor = Exception.class) public Map returnDrug(Long dispenseId, Integer quantity) { // 1. 写入退药明细(负数表示退药) dispenseMapper.insertDetail(dispenseId, -quantity); // 2. 同步更新汇总单统计 dispenseMapper.updateSummaryAfterDetail(dispenseId, -quantity); return Map.of("code", 0, "msg", "退药成功"); } }