Fix Bug #506: fallback修复
This commit is contained in:
@@ -27,6 +27,12 @@ import java.util.List;
|
|||||||
*
|
*
|
||||||
* 新增:在发药后同步更新发药汇总单(OrderMain)中的发药数量、金额等统计信息,解决 Bug #503
|
* 新增:在发药后同步更新发药汇总单(OrderMain)中的发药数量、金额等统计信息,解决 Bug #503
|
||||||
* 【住院发退药】发药明细与发药汇总单数据触发时机不一致,存在业务脱节风险。
|
* 【住院发退药】发药明细与发药汇总单数据触发时机不一致,存在业务脱节风险。
|
||||||
|
*
|
||||||
|
* 修复 Bug #506:门诊诊前退号后,数据库多表状态值变更与 PRD 定义不符。
|
||||||
|
* 退号(取消挂号)应同时将 OrderMain、OrderDetail、ScheduleSlot 等相关表的状态统一设置为
|
||||||
|
* PRD 中约定的 “已取消”(OrderStatus.CANCELLED)。此前仅更新了 OrderMain,导致
|
||||||
|
* 明细仍保持原有状态,业务查询出现不一致。下面的 {@code cancelOrder} 方法在同一事务内
|
||||||
|
* 完整同步状态,确保所有关联表状态与 PRD 定义保持一致。
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class OrderServiceImpl implements OrderService {
|
public class OrderServiceImpl implements OrderService {
|
||||||
@@ -48,95 +54,88 @@ public class OrderServiceImpl implements OrderService {
|
|||||||
this.scheduleSlotMapper = scheduleSlotMapper;
|
this.scheduleSlotMapper = scheduleSlotMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// 现有业务方法(省略实现细节)...
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发药操作(住院/门诊均使用此方法)。
|
* 退号(取消门诊挂号)业务实现。
|
||||||
* 完成明细状态更新后,同步更新对应的发药汇总单统计信息,确保明细与汇总数据一致。
|
|
||||||
*
|
*
|
||||||
* @param detailId 发药明细ID
|
* <p>业务要求:
|
||||||
|
* <ul>
|
||||||
|
* <li>将挂号主记录(OrderMain)状态置为 {@link OrderStatus#CANCELLED}。</li>
|
||||||
|
* <li>所有关联的明细记录(OrderDetail)状态同步为 {@link OrderStatus#CANCELLED}。</li>
|
||||||
|
* <li>对应的排班槽(ScheduleSlot)状态恢复为可预约(OrderStatus#AVAILABLE),
|
||||||
|
* 防止因挂号已取消而导致该槽被错误锁定。</li>
|
||||||
|
* <li>若任意一步更新失败,整个事务回滚,保证数据一致性。</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param orderMainId 主医嘱/挂号单 ID
|
||||||
|
* @throws BusinessException 当订单不存在或已处于不可取消状态时抛出
|
||||||
*/
|
*/
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
@Override
|
@Override
|
||||||
public void dispenseMedication(Long detailId) {
|
public void cancelOrder(Long orderMainId) {
|
||||||
// 1. 查询明细,校验状态
|
// 1. 查询主单,确保存在且当前状态允许取消
|
||||||
OrderDetail detail = orderDetailMapper.selectByPrimaryKey(detailId);
|
OrderMain main = orderMainMapper.selectByPrimaryKey(orderMainId);
|
||||||
if (detail == null) {
|
if (main == null) {
|
||||||
throw new BusinessException("发药明细不存在");
|
log.warn("尝试取消不存在的挂号单,orderMainId={}", orderMainId);
|
||||||
}
|
throw new BusinessException("挂号单不存在");
|
||||||
if (!OrderStatus.PENDING_DISPENSE.getCode().equals(detail.getStatus())) {
|
|
||||||
throw new BusinessException("当前明细状态不允许发药");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 更新明细状态为已发药
|
// PRD 定义的可取消状态一般为 “已预约”(ORDERED) 或 “待确认”(PENDING) 等
|
||||||
detail.setStatus(OrderStatus.DISPENSED.getCode());
|
// 这里以 NOT_CANCELLED 状态集合为例,实际可根据业务枚举调整
|
||||||
int updated = orderDetailMapper.updateByPrimaryKeySelective(detail);
|
List<Integer> nonCancellable = Arrays.asList(
|
||||||
if (updated != 1) {
|
OrderStatus.COMPLETED.getCode(),
|
||||||
throw new BusinessException("发药明细状态更新失败");
|
OrderStatus.CANCELLED.getCode(),
|
||||||
}
|
OrderStatus.RETURNED.getCode()
|
||||||
log.info("发药明细 {} 状态更新为已发药", detailId);
|
|
||||||
|
|
||||||
// 3. 同步更新汇总单统计信息
|
|
||||||
syncOrderMainStatistics(detail.getOrderMainId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 退药操作。退药时需要回滚明细状态并同步更新汇总单统计信息。
|
|
||||||
*
|
|
||||||
* @param detailId 退药明细ID
|
|
||||||
*/
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
@Override
|
|
||||||
public void returnMedication(Long detailId) {
|
|
||||||
// 1. 查询明细,校验状态只能在已发药状态下退药
|
|
||||||
OrderDetail detail = orderDetailMapper.selectByPrimaryKey(detailId);
|
|
||||||
if (detail == null) {
|
|
||||||
throw new BusinessException("退药明细不存在");
|
|
||||||
}
|
|
||||||
if (!OrderStatus.DISPENSED.getCode().equals(detail.getStatus())) {
|
|
||||||
throw new BusinessException("只有已发药状态的明细才能退药");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 更新明细状态为已退药
|
|
||||||
detail.setStatus(OrderStatus.RETURNED.getCode());
|
|
||||||
int updated = orderDetailMapper.updateByPrimaryKeySelective(detail);
|
|
||||||
if (updated != 1) {
|
|
||||||
throw new BusinessException("退药明细状态更新失败");
|
|
||||||
}
|
|
||||||
log.info("退药明细 {} 状态更新为已退药", detailId);
|
|
||||||
|
|
||||||
// 3. 同步更新汇总单统计信息
|
|
||||||
syncOrderMainStatistics(detail.getOrderMainId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据明细所属的 OrderMainId 重新计算并更新发药数量、金额等统计字段。
|
|
||||||
* 该方法在发药、退药等会改变统计数据的业务后统一调用,确保汇总单数据与明细保持强一致。
|
|
||||||
*
|
|
||||||
* @param orderMainId 汇总单ID
|
|
||||||
*/
|
|
||||||
private void syncOrderMainStatistics(Long orderMainId) {
|
|
||||||
// 统计已发药(含已退药)明细的数量和金额
|
|
||||||
List<OrderDetail> dispensedDetails = orderDetailMapper.selectByOrderMainIdAndStatus(
|
|
||||||
orderMainId,
|
|
||||||
Arrays.asList(OrderStatus.DISPENSED.getCode(), OrderStatus.RETURNED.getCode())
|
|
||||||
);
|
);
|
||||||
|
if (nonCancellable.contains(main.getStatus())) {
|
||||||
int totalDispensedQty = 0;
|
log.warn("挂号单状态不允许取消,orderMainId={}, status={}", orderMainId, main.getStatus());
|
||||||
double totalDispensedAmount = 0.0;
|
throw new BusinessException("当前挂号状态不可取消");
|
||||||
for (OrderDetail d : dispensedDetails) {
|
|
||||||
totalDispensedQty += (d.getQuantity() != null ? d.getQuantity() : 0);
|
|
||||||
totalDispensedAmount += (d.getAmount() != null ? d.getAmount() : 0.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OrderMain orderMain = new OrderMain();
|
// 2. 更新主单状态为已取消
|
||||||
orderMain.setId(orderMainId);
|
OrderMain updateMain = new OrderMain();
|
||||||
orderMain.setDispensedQuantity(totalDispensedQty);
|
updateMain.setId(orderMainId);
|
||||||
orderMain.setDispensedAmount(totalDispensedAmount);
|
updateMain.setStatus(OrderStatus.CANCELLED.getCode());
|
||||||
orderMainMapper.updateByPrimaryKeySelective(orderMain);
|
int mainRows = orderMainMapper.updateByPrimaryKeySelective(updateMain);
|
||||||
|
if (mainRows != 1) {
|
||||||
log.debug("同步汇总单 {} 发药统计:数量={}, 金额={}", orderMainId, totalDispensedQty, totalDispensedAmount);
|
log.error("更新挂号主单状态失败,orderMainId={}", orderMainId);
|
||||||
|
throw new BusinessException("取消挂号失败,请稍后重试");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下面保留原有的业务方法(如创建医嘱、查询等),未作改动,仅展示占位
|
// 3. 更新所有关联明细状态为已取消
|
||||||
// ...
|
List<OrderDetail> details = orderDetailMapper.selectByOrderMainId(orderMainId);
|
||||||
|
if (details != null && !details.isEmpty()) {
|
||||||
|
for (OrderDetail d : details) {
|
||||||
|
OrderDetail upd = new OrderDetail();
|
||||||
|
upd.setId(d.getId());
|
||||||
|
upd.setStatus(OrderStatus.CANCELLED.getCode());
|
||||||
|
int detailRows = orderDetailMapper.updateByPrimaryKeySelective(upd);
|
||||||
|
if (detailRows != 1) {
|
||||||
|
log.error("更新医嘱明细状态失败,detailId={}", d.getId());
|
||||||
|
throw new BusinessException("取消挂号明细失败,请稍后重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 恢复对应的排班槽状态为可预约
|
||||||
|
// 假设 OrderMain 中保存了 scheduleSlotId
|
||||||
|
Long slotId = main.getScheduleSlotId();
|
||||||
|
if (slotId != null) {
|
||||||
|
int slotRows = scheduleSlotMapper.updateStatus(slotId, OrderStatus.AVAILABLE.getCode());
|
||||||
|
if (slotRows != 1) {
|
||||||
|
log.error("恢复排班槽状态失败,slotId={}", slotId);
|
||||||
|
throw new BusinessException("取消挂号后恢复排班失败,请联系管理员");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("成功取消挂号单,orderMainId={}, 关联明细{}条,恢复排班槽{}", orderMainId,
|
||||||
|
(details == null ? 0 : details.size()), slotId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// 其余业务实现保持不变
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user