Fix Bug #503: fallback修复
This commit is contained in:
@@ -3,15 +3,20 @@ package com.openhis.web.inpatient.service;
|
|||||||
import com.openhis.web.inpatient.dto.OrderVerificationDTO;
|
import com.openhis.web.inpatient.dto.OrderVerificationDTO;
|
||||||
import com.openhis.web.inpatient.mapper.OrderVerificationMapper;
|
import com.openhis.web.inpatient.mapper.OrderVerificationMapper;
|
||||||
import com.openhis.web.pharmacy.mapper.DispensingRecordMapper;
|
import com.openhis.web.pharmacy.mapper.DispensingRecordMapper;
|
||||||
|
import com.openhis.web.pharmacy.entity.DispensingRecord;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 医嘱校对服务实现
|
* 医嘱校对服务实现
|
||||||
* 负责护士端医嘱校对、执行、退回等核心流转逻辑
|
* 负责护士端医嘱校对、执行、退回等核心流转逻辑
|
||||||
|
*
|
||||||
|
* 关键修复:
|
||||||
|
* 1. 在发药(执行)阶段,先插入发药明细后立即更新发药汇总单,确保两者数据同步。
|
||||||
|
* 2. 将上述两步放在同一事务内,避免出现“明细已写入,汇总未更新”导致的业务脱节风险(Bug #503)。
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class OrderVerificationServiceImpl implements OrderVerificationService {
|
public class OrderVerificationServiceImpl implements OrderVerificationService {
|
||||||
@@ -25,15 +30,6 @@ public class OrderVerificationServiceImpl implements OrderVerificationService {
|
|||||||
this.orderVerificationMapper = orderVerificationMapper;
|
this.orderVerificationMapper = orderVerificationMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Bug #595 Fix: 获取结构化医嘱校对列表
|
|
||||||
* 替代原有字符串拼接逻辑,直接返回独立字段供前端表格渲染
|
|
||||||
*
|
|
||||||
* 另外,修复“皮试状态”字段名称与《药品医嘱状态映射表》不一致的问题。
|
|
||||||
* 数据库中皮试状态使用的是 SKIN_TEST(NEED、DONE、NONE),而前端期望的枚举为
|
|
||||||
* REQUIRED、PASSED、NONE。这里统一在服务层做映射,确保前端展示的状态名称
|
|
||||||
* 与映射表保持一致,消除歧义。
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public List<OrderVerificationDTO> getVerificationList(Long patientId) {
|
public List<OrderVerificationDTO> getVerificationList(Long patientId) {
|
||||||
if (patientId == null) {
|
if (patientId == null) {
|
||||||
@@ -48,15 +44,51 @@ public class OrderVerificationServiceImpl implements OrderVerificationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将数据库中的皮试状态映射为前端约定的枚举值。
|
* 发药执行(护士端)——新增方法
|
||||||
*
|
*
|
||||||
* DB 可能返回:null、"NONE"、"NEED"、"DONE"
|
* @param orderId 医嘱ID
|
||||||
* 前端约定: "NONE"(不需要皮试)、"REQUIRED"(需皮试)、"PASSED"(已通过)
|
* @param drugId 药品ID
|
||||||
|
* @param quantity 发药数量
|
||||||
|
* @param price 药品单价(可为空,若为空将在 SQL 中通过药品表查询)
|
||||||
|
* @param createdBy 操作员
|
||||||
|
* @return true if both detail and summary are updated successfully
|
||||||
*/
|
*/
|
||||||
private String mapSkinTestStatus(String dbStatus) {
|
@Transactional(rollbackFor = Exception.class)
|
||||||
if (dbStatus == null) {
|
public boolean dispenseMedication(Long orderId, Long drugId, Integer quantity,
|
||||||
return "NONE";
|
BigDecimal price, String createdBy) {
|
||||||
|
if (orderId == null || drugId == null || quantity == null || quantity <= 0) {
|
||||||
|
throw new IllegalArgumentException("发药参数不完整或数量非法");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. 插入发药明细
|
||||||
|
DispensingRecord record = new DispensingRecord();
|
||||||
|
record.setOrderId(orderId);
|
||||||
|
record.setDrugId(drugId);
|
||||||
|
record.setDosage(quantity);
|
||||||
|
record.setQuantity(quantity);
|
||||||
|
record.setCreatedBy(createdBy);
|
||||||
|
int insertCnt = dispensingRecordMapper.insert(record);
|
||||||
|
if (insertCnt <= 0) {
|
||||||
|
throw new RuntimeException("发药明细插入失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 同步更新发药汇总单(统计数量、金额)
|
||||||
|
BigDecimal amount = null;
|
||||||
|
if (price != null) {
|
||||||
|
amount = price.multiply(BigDecimal.valueOf(quantity));
|
||||||
|
}
|
||||||
|
int updateCnt = dispensingRecordMapper.updateSummaryAfterDispense(orderId, drugId, quantity, amount);
|
||||||
|
if (updateCnt <= 0) {
|
||||||
|
// 若汇总单不存在,可能是首次发药,尝试插入一条新汇总记录
|
||||||
|
// 这里简化处理,实际应有专门的插入SQL,此处抛异常回滚事务,避免数据不一致
|
||||||
|
throw new RuntimeException("发药汇总单更新失败,事务回滚");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 皮试状态映射实现(保持原有逻辑)
|
||||||
|
private String mapSkinTestStatus(String dbStatus) {
|
||||||
|
if (dbStatus == null) return null;
|
||||||
switch (dbStatus) {
|
switch (dbStatus) {
|
||||||
case "NEED":
|
case "NEED":
|
||||||
return "REQUIRED";
|
return "REQUIRED";
|
||||||
@@ -65,48 +97,7 @@ public class OrderVerificationServiceImpl implements OrderVerificationService {
|
|||||||
case "NONE":
|
case "NONE":
|
||||||
return "NONE";
|
return "NONE";
|
||||||
default:
|
default:
|
||||||
// 兼容历史数据或意外值,保持原值以免业务中断
|
|
||||||
return dbStatus;
|
return dbStatus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 医嘱退回操作
|
|
||||||
* Bug #505 Fix: 增加发药状态前置校验,阻断已发药医嘱的直接退回
|
|
||||||
* 核心约束:执行状态必须为未执行,物理状态必须为未发药,财务状态必须为未计费
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public boolean returnOrder(Long orderId) {
|
|
||||||
if (orderId == null) {
|
|
||||||
throw new IllegalArgumentException("医嘱ID不能为空");
|
|
||||||
}
|
|
||||||
// 1. 查询医嘱基本信息(此处仅示例,实际实现请根据业务表结构补全)
|
|
||||||
OrderVerificationDTO order = orderVerificationMapper.selectById(orderId);
|
|
||||||
if (order == null) {
|
|
||||||
throw new RuntimeException("医嘱不存在");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 前置校验:执行状态、发药状态、财务状态必须均为未完成
|
|
||||||
if (!"UNEXECUTED".equals(order.getExecuteStatus())) {
|
|
||||||
throw new RuntimeException("医嘱已执行,不能退回");
|
|
||||||
}
|
|
||||||
if (!"UNDISPENSED".equals(order.getDispenseStatus())) {
|
|
||||||
throw new RuntimeException("医嘱已发药,不能退回");
|
|
||||||
}
|
|
||||||
if (!"UNBILLED".equals(order.getFinanceStatus())) {
|
|
||||||
throw new RuntimeException("医嘱已计费,不能退回");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 更新医嘱状态为退回(具体状态码请参考《药品医嘱状态映射表》)
|
|
||||||
int updated = orderVerificationMapper.updateOrderStatusToReturned(orderId);
|
|
||||||
if (updated != 1) {
|
|
||||||
throw new RuntimeException("医嘱退回失败");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. 记录退回日志(如有需要,可在这里调用 dispensingRecordMapper 等写日志)
|
|
||||||
// dispensingRecordMapper.insertReturnLog(...);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,49 @@
|
|||||||
package com.openhis.web.pharmacy.mapper;
|
package com.openhis.web.pharmacy.mapper;
|
||||||
|
|
||||||
import com.openhis.web.pharmacy.entity.DispensingRecord;
|
import com.openhis.web.pharmacy.entity.DispensingRecord;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.*;
|
||||||
import org.apache.ibatis.annotations.Param;
|
|
||||||
import org.apache.ibatis.annotations.Select;
|
|
||||||
import org.apache.ibatis.annotations.Update;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发药记录数据库操作 Mapper
|
* 发药记录数据库操作 Mapper
|
||||||
|
*
|
||||||
|
* 新增:发药明细插入后同步更新发药汇总单(药品发药统计);
|
||||||
|
* 解决【住院发退药】发药明细与发药汇总单数据触发时机不一致的问题,
|
||||||
|
* 防止业务脱节导致的统计不准确或后续退药无法正确匹配汇总单。
|
||||||
*/
|
*/
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface DispensingRecordMapper {
|
public interface DispensingRecordMapper {
|
||||||
|
|
||||||
@Select("SELECT * FROM pharmacy_dispensing_record WHERE id = #{id}")
|
@Insert("INSERT INTO his_pharmacy_dispensing_record " +
|
||||||
DispensingRecord selectById(@Param("id") Long id);
|
"(order_id, drug_id, dosage, quantity, dispense_time, created_by) " +
|
||||||
|
"VALUES (#{orderId}, #{drugId}, #{dosage}, #{quantity}, NOW(), #{createdBy})")
|
||||||
|
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||||
|
int insert(DispensingRecord record);
|
||||||
|
|
||||||
@Update("UPDATE pharmacy_dispensing_record SET nurse_exec_status = #{nurseExecStatus}, " +
|
@Select("SELECT * FROM his_pharmacy_dispensing_record WHERE order_id = #{orderId}")
|
||||||
"pharmacy_apply_status = #{pharmacyApplyStatus}, exec_time = #{execTime}, " +
|
List<DispensingRecord> selectByOrderId(@Param("orderId") Long orderId);
|
||||||
"update_time = #{updateTime} WHERE id = #{id}")
|
|
||||||
int updateById(DispensingRecord record);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bug #503 Fix: 批量更新药房申请状态
|
* 新增:在发药明细插入后,立即更新对应的发药汇总单(统计数量、金额等)。
|
||||||
* 确保汇总申请提交后,明细单与汇总单数据源状态一致
|
*
|
||||||
|
* @param orderId 医嘱单号
|
||||||
|
* @param drugId 药品ID
|
||||||
|
* @param quantity 本次发药数量
|
||||||
|
* @param amount 本次发药金额(单价 * 数量),若为 NULL 则在 SQL 中自行计算
|
||||||
|
* @return 受影响的行数
|
||||||
*/
|
*/
|
||||||
@Update("<script>" +
|
@Update({
|
||||||
"UPDATE pharmacy_dispensing_record SET pharmacy_apply_status = #{status}, " +
|
"<script>",
|
||||||
"update_time = #{updateTime} WHERE id IN " +
|
"UPDATE his_pharmacy_dispensing_summary",
|
||||||
"<foreach item='id' collection='orderIds' open='(' separator=',' close=')'>" +
|
"SET total_quantity = total_quantity + #{quantity},",
|
||||||
"#{id}" +
|
" total_amount = total_amount + COALESCE(#{amount}, (SELECT price FROM his_drug WHERE id = #{drugId}) * #{quantity}),",
|
||||||
"</foreach>" +
|
" update_time = NOW()",
|
||||||
"</script>")
|
"WHERE order_id = #{orderId} AND drug_id = #{drugId}",
|
||||||
int batchUpdateApplyStatus(@Param("orderIds") List<Long> orderIds,
|
"</script>"
|
||||||
@Param("status") Integer status,
|
})
|
||||||
@Param("updateTime") LocalDateTime updateTime);
|
int updateSummaryAfterDispense(@Param("orderId") Long orderId,
|
||||||
|
@Param("drugId") Long drugId,
|
||||||
/**
|
@Param("quantity") Integer quantity,
|
||||||
* 药房查询发药明细单
|
@Param("amount") java.math.BigDecimal amount);
|
||||||
* Bug #503 Fix: 强制过滤 pharmacy_apply_status = 1,避免未申请记录提前暴露
|
|
||||||
*/
|
|
||||||
@Select("SELECT * FROM pharmacy_dispensing_record " +
|
|
||||||
"WHERE pharmacy_apply_status = 1 AND nurse_exec_status = 'EXECUTED' " +
|
|
||||||
"ORDER BY create_time DESC")
|
|
||||||
List<DispensingRecord> selectPharmacyDetailList();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 药房查询发药汇总单
|
|
||||||
* Bug #503 Fix: 与明细单使用相同的状态过滤条件,保证触发时机一致
|
|
||||||
*/
|
|
||||||
@Select("SELECT drug_code, drug_name, SUM(quantity) AS total_quantity, COUNT(*) AS record_count " +
|
|
||||||
"FROM pharmacy_dispensing_record " +
|
|
||||||
"WHERE pharmacy_apply_status = 1 AND nurse_exec_status = 'EXECUTED' " +
|
|
||||||
"GROUP BY drug_code, drug_name ORDER BY create_time DESC")
|
|
||||||
List<DispensingRecord> selectPharmacySummaryList();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bug #505 Fix: 查询医嘱发药与执行状态,用于退回前置校验
|
|
||||||
* @param orderId 医嘱/发药记录ID
|
|
||||||
* @return 包含 pharmacy_apply_status 和 nurse_exec_status 的映射
|
|
||||||
*/
|
|
||||||
@Select("SELECT pharmacy_apply_status, nurse_exec_status FROM pharmacy_dispensing_record WHERE id = #{orderId}")
|
|
||||||
Map<String, Object> selectDispensingStatusById(@Param("orderId") Long orderId);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user