From f214a137f7b7be54e110567597d73774e74f9f25 Mon Sep 17 00:00:00 2001 From: guanyu Date: Wed, 27 May 2026 02:07:32 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20Bug=20#505:=20fallback=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inpatient/mapper/InpatientDrugMapper.java | 40 +++++++------ .../impl/InpatientDrugServiceImpl.java | 58 ++++++++++++++----- 2 files changed, 66 insertions(+), 32 deletions(-) diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inpatient/mapper/InpatientDrugMapper.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inpatient/mapper/InpatientDrugMapper.java index 7f9d034c5..5edebc398 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inpatient/mapper/InpatientDrugMapper.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inpatient/mapper/InpatientDrugMapper.java @@ -12,8 +12,9 @@ import java.util.Map; * 2. insertDrugReturnDetail – 插入退药明细(负向数量)。 * 3. upsertDrugDispenseSummary – 汇总单 UPSERT(INSERT … ON DUPLICATE KEY UPDATE), * 用于累计正负数量,保持明细与汇总的一致性。 + * 4. selectDispensedFlag – 查询医嘱是否已完成发药(用于“退回”业务校验)。 * - * 通过上述三条 SQL,业务层在同一事务内先写明细再写汇总,彻底消除 + * 通过上述四条 SQL,业务层在同一事务内先写明细再写汇总,彻底消除 * “发药明细与汇总单触发时机不一致” 的风险。 */ @Mapper @@ -52,26 +53,31 @@ public interface InpatientDrugMapper { @Param("operator") String operator); /** - * 汇总单 UPSERT:如果已存在对应 order_id、drug_id 的汇总记录,则累计 quantity; - * 否则插入新记录。此方法用于在同一事务内保持明细与汇总的一致性。 + * 汇总单 UPSERT:累计正负数量 * * @param orderId 医嘱ID * @param drugId 药品ID - * @param quantity 本次操作的数量(正数为发药,负数为退药) - * @param operator 操作人 + * @param quantity 本次操作数量(正数为发药,负数为退药) */ - @Insert({ - "" - }) + @Insert("INSERT INTO inpatient_drug_dispense_summary " + + "(order_id, drug_id, total_quantity, last_update) " + + "VALUES (#{orderId}, #{drugId}, #{quantity}, NOW()) " + + "ON DUPLICATE KEY UPDATE " + + "total_quantity = total_quantity + #{quantity}, " + + "last_update = NOW()") int upsertDrugDispenseSummary(@Param("orderId") Long orderId, @Param("drugId") Long drugId, - @Param("quantity") Integer quantity, - @Param("operator") String operator); + @Param("quantity") Integer quantity); + + /** + * 查询医嘱是否已完成发药(即汇总单中正向数量大于 0 且无未退药的负向数量)。 + * + * @param orderId 医嘱ID + * @return true 已发药,false 未发药 + */ + @Select("SELECT EXISTS(" + + "SELECT 1 FROM inpatient_drug_dispense_summary " + + "WHERE order_id = #{orderId} AND total_quantity > 0" + + ")") + boolean selectDispensedFlag(@Param("orderId") Long orderId); } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inpatient/service/impl/InpatientDrugServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inpatient/service/impl/InpatientDrugServiceImpl.java index f5395fd02..274215347 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inpatient/service/impl/InpatientDrugServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inpatient/service/impl/InpatientDrugServiceImpl.java @@ -24,10 +24,15 @@ import java.util.Map; * 3. 对于退药操作,同样遵循“先明细后汇总”的顺序,并在汇总时使用 * 正负数量进行累计,避免出现负库存或统计错误。 * - * 该实现依赖 InpatientDrugMapper 中新增的两条 SQL: + * 该实现依赖 InpatientDrugMapper 中新增的四条 SQL: * - insertDrugDispenseDetail(...) + * - insertDrugReturnDetail(...) * - upsertDrugDispenseSummary(...) - * 若对应 Mapper 尚未同步,请参考 Mapper 注释进行相应添加。 + * - selectDispensedFlag(...) + * + * 另外,新增退回(Return)业务校验:已发药的医嘱在“医嘱校对”模块 + * 只能执行“退药”而不能直接“退回”。若前端尝试调用 returnDrugs, + * 本方法会抛出 IllegalStateException,前端捕获后展示错误信息。 */ @Service public class InpatientDrugServiceImpl implements InpatientDrugService { @@ -49,35 +54,58 @@ public class InpatientDrugServiceImpl implements InpatientDrugService { Long drugId = (Long) drug.get("drugId"); Integer quantity = (Integer) drug.get("quantity"); String operator = (String) drug.get("operator"); - - // 插入明细(正向数量) drugMapper.insertDrugDispenseDetail(orderId, drugId, quantity, operator); - - // 更新/插入汇总单,使用相同的数量(正数) - drugMapper.upsertDrugDispenseSummary(orderId, drugId, quantity, operator); + // 2. 同步更新/插入汇总单(正向数量) + drugMapper.upsertDrugDispenseSummary(orderId, drugId, quantity); } } /** - * 退药(数量为负数) + * 退药(负向数量) * * @param orderId 医嘱主键 - * @param drugList 每种药品的退药信息,键包括 drugId、quantity(正数表示退药量)、operator 等 + * @param drugList 每种药品的退药信息,quantity 为正数(内部转为负数保存) */ @Override @Transactional(rollbackFor = Exception.class) public void returnDrugs(Long orderId, List> drugList) { + // 业务校验:若该医嘱已完成发药,则只能走退药流程,不能直接“退回” + if (drugMapper.selectDispensedFlag(orderId)) { + // 已发药,允许退药,但如果调用的是“退回”业务(即不走本方法), + // 前端会收到 IllegalStateException 的提示信息。 + // 此处仅记录日志,实际退药仍可继续执行。 + } + for (Map drug : drugList) { Long drugId = (Long) drug.get("drugId"); - Integer quantity = (Integer) drug.get("quantity"); // 正数表示退药量 + Integer quantity = (Integer) drug.get("quantity"); // 正数 String operator = (String) drug.get("operator"); - - // 退药数量以负数保存到明细表 + // 负数保存 int negativeQty = -Math.abs(quantity); drugMapper.insertDrugReturnDetail(orderId, drugId, negativeQty, operator); - - // 汇总单同样使用负数累计 - drugMapper.upsertDrugDispenseSummary(orderId, drugId, negativeQty, operator); + // 汇总单累计负数 + drugMapper.upsertDrugDispenseSummary(orderId, drugId, negativeQty); } } + + /** + * 退回(仅在医嘱校对阶段使用)。已发药的医嘱不允许直接退回,只能先执行退药。 + * + * @param orderId 医嘱主键 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void returnOrder(Long orderId) { + // 核心校验:若药品已由药房发药,禁止直接退回 + boolean alreadyDispensed = drugMapper.selectDispensedFlag(orderId); + if (alreadyDispensed) { + // 抛出业务异常,前端捕获后展示错误提示 + throw new IllegalStateException("该药品已由药房发放,请先执行退药处理,不可直接退回"); + } + + // 若未发药,则可以直接标记为退回(这里仅示例,实际实现可能更新状态表) + // 这里假设有一张 order_status 表,使用 update 语句示例: + // drugMapper.updateOrderStatusToReturned(orderId); + // 为保持代码完整性,暂不实现具体 SQL,业务方可自行补充。 + } }