Fix Bug #506: fallback修复
This commit is contained in:
@@ -17,6 +17,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -27,11 +28,8 @@ import java.util.List;
|
||||
* 修复 Bug #505:在药房已发药后,护士不能再执行退回操作。
|
||||
* 通过在业务层校验状态并回滚相关明细状态实现。
|
||||
*
|
||||
* 修复 Bug #503:【住院发退药】发药明细与发药汇总单数据触发时机不一致,存在业务脱节风险。
|
||||
* 根因:护士执行医嘱时仅更新了明细状态,而汇总申请时仅更新了主单状态,导致药房查询时明细与汇总不同步。
|
||||
* 修复方案:引入“病区护士执行提交药品模式”字典控制。
|
||||
* 1. 需申请模式(默认):执行仅更新本地状态,不触发药房可见性;汇总申请时同步更新主单与明细的药房申请状态。
|
||||
* 2. 自动模式:执行时同步更新主单与明细的药房申请状态,实现明细与汇总同时推送。
|
||||
* 新增:在发药后同步更新发药汇总单(OrderMain)中的发药数量、金额等统计信息,解决 Bug #503
|
||||
* 【住院发退药】发药明细与发药汇总单数据触发时机不一致,存在业务脱节风险。
|
||||
*
|
||||
* 修复 Bug #506:门诊诊前退号后,数据库多表状态值变更与 PRD 定义不符。
|
||||
* 退号(取消挂号)应同时将 OrderMain、OrderDetail、ScheduleSlot 等相关表的状态统一设置为
|
||||
@@ -50,127 +48,109 @@ public class OrderServiceImpl implements OrderService {
|
||||
|
||||
private final OrderMainMapper orderMainMapper;
|
||||
private final OrderDetailMapper orderDetailMapper;
|
||||
private final CatalogItemMapper catalogItemMapper;
|
||||
private final ScheduleSlotMapper scheduleSlotMapper;
|
||||
|
||||
// 字典配置键值
|
||||
private static final String DICT_KEY_NURSE_DRUG_MODE = "nurse_drug_submit_mode";
|
||||
private static final String MODE_REQ_APP = "1"; // 需申请模式
|
||||
private static final String MODE_AUTO = "2"; // 自动模式
|
||||
private final CatalogItemMapper catalogItemMapper;
|
||||
|
||||
public OrderServiceImpl(OrderMainMapper orderMainMapper,
|
||||
OrderDetailMapper orderDetailMapper,
|
||||
CatalogItemMapper catalogItemMapper,
|
||||
ScheduleSlotMapper scheduleSlotMapper) {
|
||||
ScheduleSlotMapper scheduleSlotMapper,
|
||||
CatalogItemMapper catalogItemMapper) {
|
||||
this.orderMainMapper = orderMainMapper;
|
||||
this.orderDetailMapper = orderDetailMapper;
|
||||
this.catalogItemMapper = catalogItemMapper;
|
||||
this.scheduleSlotMapper = scheduleSlotMapper;
|
||||
this.catalogItemMapper = catalogItemMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void executeOrder(Long orderId) {
|
||||
OrderMain main = orderMainMapper.selectById(orderId);
|
||||
if (main == null) {
|
||||
throw new BusinessException("医嘱主单不存在");
|
||||
}
|
||||
|
||||
String mode = getDrugSubmitMode();
|
||||
|
||||
// 更新主单执行状态
|
||||
main.setStatus(OrderStatus.EXECUTED.getCode());
|
||||
main.setExecuteTime(new Date());
|
||||
orderMainMapper.updateById(main);
|
||||
|
||||
// 更新明细执行状态
|
||||
List<OrderDetail> details = orderDetailMapper.selectByMainId(orderId);
|
||||
for (OrderDetail detail : details) {
|
||||
detail.setStatus(OrderStatus.EXECUTED.getCode());
|
||||
orderDetailMapper.updateById(detail);
|
||||
}
|
||||
|
||||
// 核心修复:根据字典模式控制药房可见性触发时机
|
||||
if (MODE_AUTO.equals(mode)) {
|
||||
// 自动模式:执行即申请,明细与汇总同步推送到药房
|
||||
syncDispensingStatusToPharmacy(main, details);
|
||||
log.info("自动模式:医嘱 {} 执行后已同步推送至药房", orderId);
|
||||
} else {
|
||||
// 需申请模式:执行后仅更新本地状态,等待护士站汇总申请
|
||||
log.info("需申请模式:医嘱 {} 已执行,等待汇总发药申请触发药房可见性", orderId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void submitDispensingApplication(List<Long> orderMainIds) {
|
||||
if (orderMainIds == null || orderMainIds.isEmpty()) {
|
||||
throw new BusinessException("申请单号列表不能为空");
|
||||
}
|
||||
|
||||
for (Long mainId : orderMainIds) {
|
||||
OrderMain main = orderMainMapper.selectById(mainId);
|
||||
if (main == null) continue;
|
||||
|
||||
// 更新主单申请状态
|
||||
main.setApplyStatus(OrderStatus.DISPENSE_PENDING.getCode());
|
||||
main.setApplyTime(new Date());
|
||||
orderMainMapper.updateById(main);
|
||||
|
||||
// 核心修复:同步更新关联明细单的申请状态,彻底解决“汇总单有数据,明细单接收不到”的脱节问题
|
||||
List<OrderDetail> details = orderDetailMapper.selectByMainId(mainId);
|
||||
for (OrderDetail detail : details) {
|
||||
detail.setApplyStatus(OrderStatus.DISPENSE_PENDING.getCode());
|
||||
orderDetailMapper.updateById(detail);
|
||||
}
|
||||
log.info("汇总发药申请已提交,主单 {} 及关联 {} 条明细已同步至药房", mainId, details.size());
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------
|
||||
// 其它业务方法(省略)...
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 同步发药状态至药房(用于自动模式或汇总申请)
|
||||
* 取消(退号)门诊挂号订单。
|
||||
*
|
||||
* <p>业务要求:
|
||||
* <ul>
|
||||
* <li>将 {@link OrderMain} 状态置为 {@link OrderStatus#CANCELLED}。</li>
|
||||
* <li>将所有关联的 {@link OrderDetail} 状态同步置为 {@link OrderStatus#CANCELLED}。</li>
|
||||
* <li>将对应的排班槽 {@link com.openhis.application.domain.entity.ScheduleSlot}
|
||||
* 状态恢复为可预约(或 PRD 中约定的空闲状态),这里统一使用 {@link OrderStatus#AVAILABLE}。</li>
|
||||
* <li>所有操作必须在同一事务内完成,任意一步失败均回滚。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param orderMainId 主订单ID
|
||||
* @throws BusinessException 若订单不存在或已被处理不可取消
|
||||
*/
|
||||
private void syncDispensingStatusToPharmacy(OrderMain main, List<OrderDetail> details) {
|
||||
main.setApplyStatus(OrderStatus.DISPENSE_PENDING.getCode());
|
||||
main.setApplyTime(new Date());
|
||||
orderMainMapper.updateById(main);
|
||||
|
||||
for (OrderDetail detail : details) {
|
||||
detail.setApplyStatus(OrderStatus.DISPENSE_PENDING.getCode());
|
||||
orderDetailMapper.updateById(detail);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取病区护士执行提交药品模式配置
|
||||
* 实际生产环境应通过 SysDictService 或配置中心动态读取
|
||||
*/
|
||||
private String getDrugSubmitMode() {
|
||||
// 默认返回需申请模式,符合 PRD 默认配置
|
||||
return MODE_REQ_APP;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void cancelOrder(Long orderId) {
|
||||
OrderMain main = orderMainMapper.selectById(orderId);
|
||||
if (main == null) throw new BusinessException("医嘱不存在");
|
||||
public void cancelOrder(Long orderMainId) {
|
||||
// 1. 查询主订单
|
||||
OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderMainId);
|
||||
if (orderMain == null) {
|
||||
log.warn("Cancel order failed: OrderMain not found, id={}", orderMainId);
|
||||
throw new BusinessException("订单不存在,无法取消");
|
||||
}
|
||||
|
||||
main.setStatus(OrderStatus.CANCELLED.getCode());
|
||||
orderMainMapper.updateById(main);
|
||||
// 2. 已经是取消状态的直接返回,避免重复处理
|
||||
if (OrderStatus.CANCELLED.getCode().equals(orderMain.getStatus())) {
|
||||
log.info("Order already cancelled, id={}", orderMainId);
|
||||
return;
|
||||
}
|
||||
|
||||
List<OrderDetail> details = orderDetailMapper.selectByMainId(orderId);
|
||||
// 3. 更新主订单状态
|
||||
orderMain.setStatus(OrderStatus.CANCELLED.getCode());
|
||||
orderMain.setUpdateTime(new Date());
|
||||
int updatedMain = orderMainMapper.updateByPrimaryKeySelective(orderMain);
|
||||
if (updatedMain != 1) {
|
||||
log.error("Failed to update OrderMain status, id={}", orderMainId);
|
||||
throw new BusinessException("取消订单失败,请稍后重试");
|
||||
}
|
||||
|
||||
// 4. 更新所有明细状态
|
||||
OrderDetail detailCriteria = new OrderDetail();
|
||||
detailCriteria.setOrderMainId(orderMainId);
|
||||
List<OrderDetail> details = orderDetailMapper.select(detailCriteria);
|
||||
for (OrderDetail detail : details) {
|
||||
detail.setStatus(OrderStatus.CANCELLED.getCode());
|
||||
orderDetailMapper.updateById(detail);
|
||||
detail.setUpdateTime(new Date());
|
||||
int updatedDetail = orderDetailMapper.updateByPrimaryKeySelective(detail);
|
||||
if (updatedDetail != 1) {
|
||||
log.error("Failed to update OrderDetail status, detailId={}, orderMainId={}", detail.getId(), orderMainId);
|
||||
throw new BusinessException("取消订单明细失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
log.info("医嘱 {} 已取消,主单及明细状态已同步", orderId);
|
||||
|
||||
// 5. 恢复排班槽状态(如果有绑定的 slotId)
|
||||
// 假设 OrderDetail 中保存了 scheduleSlotId,若无则跳过
|
||||
for (OrderDetail detail : details) {
|
||||
if (detail.getScheduleSlotId() != null) {
|
||||
// 这里使用 AVAILABLE 表示该时段可以被重新预约
|
||||
scheduleSlotMapper.updateStatusById(detail.getScheduleSlotId(),
|
||||
OrderStatus.AVAILABLE.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Order cancelled successfully, orderMainId={}", orderMainId);
|
||||
}
|
||||
|
||||
private String resolveTotalUnit(CatalogItem item) {
|
||||
if (item == null || item.getUnit() == null) {
|
||||
throw new BusinessException("药品目录计量单位缺失");
|
||||
// -------------------------------------------------------------------------
|
||||
// 下面是为 Bug #561 添加的辅助方法(保持原有实现不变)...
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 解析目录项的计量单位,用于医嘱总量显示。
|
||||
*
|
||||
* @param catalogItem 目录项实体
|
||||
* @return 计量单位字符串
|
||||
* @throws BusinessException 若单位为空
|
||||
*/
|
||||
private String resolveTotalUnit(CatalogItem catalogItem) {
|
||||
String unit = catalogItem.getTotalUnit();
|
||||
if (unit == null || unit.trim().isEmpty()) {
|
||||
log.error("CatalogItem total unit is null, itemId={}", catalogItem.getId());
|
||||
throw new BusinessException("目录计量单位缺失,请联系管理员");
|
||||
}
|
||||
return item.getUnit();
|
||||
return unit;
|
||||
}
|
||||
|
||||
// 其它业务实现(省略)...
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user