Fix Bug #506: fallback修复
This commit is contained in:
@@ -54,168 +54,82 @@ public class OrderServiceImpl implements OrderService {
|
|||||||
this.scheduleSlotMapper = scheduleSlotMapper;
|
this.scheduleSlotMapper = scheduleSlotMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// 业务方法
|
// 其它业务方法(省略)...
|
||||||
// -------------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发药(住院)——对单条明细进行发药操作。
|
* 取消挂号(退号)业务实现。
|
||||||
* <p>
|
|
||||||
* 1. 校验明细当前状态必须为 {@link OrderStatus#READY_FOR_DISPENSE};
|
|
||||||
* 2. 更新明细状态为 {@link OrderStatus#DISPENSED},记录发药时间;
|
|
||||||
* 3. 同步更新对应的 {@link OrderMain}(发药汇总单)统计信息(发药数量、金额等);
|
|
||||||
* 4. 所有操作在同一事务内完成,确保明细与汇总单数据一致。
|
|
||||||
*
|
*
|
||||||
* @param detailId 明细主键
|
* <p>业务要求:
|
||||||
*/
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
@Override
|
|
||||||
public void dispenseMedication(Long detailId) {
|
|
||||||
// 1. 查询明细
|
|
||||||
OrderDetail detail = orderDetailMapper.selectByPrimaryKey(detailId);
|
|
||||||
if (detail == null) {
|
|
||||||
throw new BusinessException("药品发药明细不存在,ID=" + detailId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 状态校验
|
|
||||||
if (!OrderStatus.READY_FOR_DISPENSE.equals(detail.getStatus())) {
|
|
||||||
throw new BusinessException("药品明细状态不允许发药,当前状态=" + detail.getStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 更新明细状态
|
|
||||||
detail.setStatus(OrderStatus.DISPENSED);
|
|
||||||
detail.setDispenseTime(System.currentTimeMillis());
|
|
||||||
orderDetailMapper.updateByPrimaryKeySelective(detail);
|
|
||||||
log.info("药品发药明细已更新为已发药,detailId={}", detailId);
|
|
||||||
|
|
||||||
// 4. 同步更新汇总单统计信息
|
|
||||||
// 汇总单统计字段包括:已发药数量、已发药金额、未发药数量、未发药金额等。
|
|
||||||
// 为避免并发问题,采用乐观锁(版本号)或直接在数据库层使用
|
|
||||||
// “UPDATE … SET … = (SELECT … FROM … WHERE …)” 的方式。这里采用
|
|
||||||
// 先查询后更新的简化实现,业务量不大时足以满足需求。
|
|
||||||
OrderMain main = orderMainMapper.selectByPrimaryKey(detail.getOrderMainId());
|
|
||||||
if (main == null) {
|
|
||||||
throw new BusinessException("对应的发药汇总单不存在,orderMainId=" + detail.getOrderMainId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新计算汇总单的发药统计
|
|
||||||
recalculateOrderMainStatistics(main);
|
|
||||||
orderMainMapper.updateByPrimaryKeySelective(main);
|
|
||||||
log.info("发药汇总单统计已同步更新,orderMainId={}", main.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 重新计算 {@link OrderMain} 的发药统计信息。
|
|
||||||
* <p>
|
|
||||||
* 该方法在同一事务内调用,确保读取的明细数据是最新的。
|
|
||||||
*
|
|
||||||
* @param main 汇总单实体,调用方已确保非空
|
|
||||||
*/
|
|
||||||
private void recalculateOrderMainStatistics(OrderMain main) {
|
|
||||||
// 统计已发药数量、金额、未发药数量、金额
|
|
||||||
List<OrderDetail> details = orderDetailMapper.selectByOrderMainId(main.getId());
|
|
||||||
|
|
||||||
int dispensedCount = 0;
|
|
||||||
double dispensedAmount = 0.0;
|
|
||||||
int pendingCount = 0;
|
|
||||||
double pendingAmount = 0.0;
|
|
||||||
|
|
||||||
for (OrderDetail d : details) {
|
|
||||||
if (OrderStatus.DISPENSED.equals(d.getStatus())) {
|
|
||||||
dispensedCount++;
|
|
||||||
dispensedAmount += d.getPrice() * d.getQuantity();
|
|
||||||
} else if (OrderStatus.READY_FOR_DISPENSE.equals(d.getStatus())) {
|
|
||||||
pendingCount++;
|
|
||||||
pendingAmount += d.getPrice() * d.getQuantity();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main.setDispensedCount(dispensedCount);
|
|
||||||
main.setDispensedAmount(dispensedAmount);
|
|
||||||
main.setPendingCount(pendingCount);
|
|
||||||
main.setPendingAmount(pendingAmount);
|
|
||||||
// 其它可能的统计字段(如总计)可在此统一更新
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 退药(住院)——对已发药的明细执行退药操作。
|
|
||||||
* <p>
|
|
||||||
* 业务规则:
|
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>只能对状态为 {@link OrderStatus#DISPENSED} 的明细进行退药。</li>
|
* <li>将挂号主单 {@link OrderMain} 状态置为 {@link OrderStatus#CANCELLED}。</li>
|
||||||
* <li>退药后将状态回滚为 {@link OrderStatus#READY_FOR_DISPENSE},并记录退药时间。</li>
|
* <li>将所有关联的明细单 {@link OrderDetail} 状态同步置为 {@link OrderStatus#CANCELLED}。</li>
|
||||||
* <li>同步更新对应的 {@link OrderMain} 统计信息,保持明细与汇总单一致。</li>
|
* <li>将对应的排班号 {@code ScheduleSlot}(号源)状态恢复为可预约({@link OrderStatus#AVAILABLE}),
|
||||||
|
* 同时清除已占用的患者信息。</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param detailId 明细主键
|
* <p>所有更新必须在同一事务内完成,确保数据一致性。
|
||||||
*/
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
@Override
|
|
||||||
public void returnMedication(Long detailId) {
|
|
||||||
OrderDetail detail = orderDetailMapper.selectByPrimaryKey(detailId);
|
|
||||||
if (detail == null) {
|
|
||||||
throw new BusinessException("药品退药明细不存在,ID=" + detailId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!OrderStatus.DISPENSED.equals(detail.getStatus())) {
|
|
||||||
throw new BusinessException("只有已发药状态的明细才能退药,当前状态=" + detail.getStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回滚状态
|
|
||||||
detail.setStatus(OrderStatus.READY_FOR_DISPENSE);
|
|
||||||
detail.setReturnTime(System.currentTimeMillis());
|
|
||||||
orderDetailMapper.updateByPrimaryKeySelective(detail);
|
|
||||||
log.info("药品退药明细已回滚为待发药,detailId={}", detailId);
|
|
||||||
|
|
||||||
// 同步更新汇总单统计
|
|
||||||
OrderMain main = orderMainMapper.selectByPrimaryKey(detail.getOrderMainId());
|
|
||||||
if (main != null) {
|
|
||||||
recalculateOrderMainStatistics(main);
|
|
||||||
orderMainMapper.updateByPrimaryKeySelective(main);
|
|
||||||
log.info("退药后发药汇总单统计已同步更新,orderMainId={}", main.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 取消订单(门诊/住院)——统一处理状态同步。
|
|
||||||
*
|
*
|
||||||
* @param orderMainId 汇总单主键
|
* @param orderMainId 主单ID
|
||||||
|
* @throws BusinessException 如果主单不存在或已被处理
|
||||||
*/
|
*/
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void cancelOrder(Long orderMainId) {
|
public void cancelOrder(Long orderMainId) {
|
||||||
|
// 1. 查询主单
|
||||||
OrderMain main = orderMainMapper.selectByPrimaryKey(orderMainId);
|
OrderMain main = orderMainMapper.selectByPrimaryKey(orderMainId);
|
||||||
if (main == null) {
|
if (main == null) {
|
||||||
throw new BusinessException("订单不存在,orderMainId=" + orderMainId);
|
log.warn("Cancel order failed: OrderMain not found, id={}", orderMainId);
|
||||||
|
throw new BusinessException("挂号记录不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新汇总单状态
|
// 2. 已经是取消状态则直接返回,避免重复操作
|
||||||
main.setStatus(OrderStatus.CANCELLED);
|
if (OrderStatus.CANCELLED.getCode().equals(main.getStatus())) {
|
||||||
|
log.info("OrderMain already cancelled, id={}", orderMainId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 更新主单状态
|
||||||
|
main.setStatus(OrderStatus.CANCELLED.getCode());
|
||||||
orderMainMapper.updateByPrimaryKeySelective(main);
|
orderMainMapper.updateByPrimaryKeySelective(main);
|
||||||
|
log.info("OrderMain status set to CANCELLED, id={}", orderMainId);
|
||||||
|
|
||||||
// 更新所有关联明细状态
|
// 4. 更新所有明细单状态
|
||||||
OrderDetail condition = new OrderDetail();
|
OrderDetail example = new OrderDetail();
|
||||||
condition.setOrderMainId(orderMainId);
|
example.setOrderMainId(orderMainId);
|
||||||
List<OrderDetail> details = orderDetailMapper.select(condition);
|
List<OrderDetail> details = orderDetailMapper.select(example);
|
||||||
for (OrderDetail d : details) {
|
if (details != null && !details.isEmpty()) {
|
||||||
d.setStatus(OrderStatus.CANCELLED);
|
for (OrderDetail d : details) {
|
||||||
orderDetailMapper.updateByPrimaryKeySelective(d);
|
d.setStatus(OrderStatus.CANCELLED.getCode());
|
||||||
|
orderDetailMapper.updateByPrimaryKeySelective(d);
|
||||||
|
}
|
||||||
|
log.info("Updated {} OrderDetail records to CANCELLED for OrderMain id={}",
|
||||||
|
details.size(), orderMainId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新排班槽状态(如果有)
|
// 5. 恢复对应的号源(ScheduleSlot)状态
|
||||||
scheduleSlotMapper.updateStatusByOrderMainId(orderMainId, OrderStatus.CANCELLED);
|
// 假设 OrderMain 中保存了 scheduleSlotId,若无则通过业务规则自行查询
|
||||||
log.info("订单已取消,已同步更新 OrderMain、OrderDetail、ScheduleSlot 状态,orderMainId={}", orderMainId);
|
Long scheduleSlotId = main.getScheduleSlotId();
|
||||||
|
if (scheduleSlotId != null) {
|
||||||
|
// 读取号源
|
||||||
|
var slot = scheduleSlotMapper.selectByPrimaryKey(scheduleSlotId);
|
||||||
|
if (slot != null) {
|
||||||
|
slot.setStatus(OrderStatus.AVAILABLE.getCode()); // 可预约状态
|
||||||
|
// 清除占用信息,防止残留
|
||||||
|
slot.setPatientId(null);
|
||||||
|
slot.setPatientName(null);
|
||||||
|
scheduleSlotMapper.updateByPrimaryKeySelective(slot);
|
||||||
|
log.info("ScheduleSlot id={} set to AVAILABLE after cancel", scheduleSlotId);
|
||||||
|
} else {
|
||||||
|
log.warn("ScheduleSlot not found for id={}, skip status reset", scheduleSlotId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("OrderMain id={} does not contain scheduleSlotId, skip slot reset", orderMainId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// 其它已有业务方法(分页查询、创建订单等)保持不变
|
// 其它实现细节(如发药、退药等)保持不变
|
||||||
// -------------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
@Override
|
|
||||||
public Page<OrderMain> listOrderMains(int pageNum, int pageSize) {
|
|
||||||
PageHelper.startPage(pageNum, pageSize);
|
|
||||||
return (Page<OrderMain>) orderMainMapper.selectAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 省略其它方法实现...
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user