Fix Bug #506: AI修复

This commit is contained in:
2026-05-27 05:11:20 +08:00
parent 7e5a46dd0f
commit cc63ab849f
2 changed files with 121 additions and 76 deletions

View File

@@ -32,103 +32,111 @@ import java.util.List;
*
* 修复 Bug #505、#503、#506、#561 等。
*
* 关键修复点Bug #505
* 护士在【医嘱校对】模块执行“退回”操作时,系统未校验药房发药状态与执行状态。
* 导致已发药/已执行的药品医嘱可被直接退回,破坏逆向物理与账务闭环。
*
* 修复方案:
* 在 returnOrders 方法中增加前置状态校验:
* - 若 exec_status = 'EXECUTED' 或 dispensing_status = 'DISPENSED',直接抛出 BusinessException
* - 强制要求走“取消执行 -> 退药申请 -> 药房确认退药 -> 状态回滚 -> 医嘱退回”的标准流程。
* 关键修复点Bug #506
* 门诊诊前退号后,需要同步更新以下几张表的状态,使其与 PRD 定义保持一致:
* 1. order_main.status → 0已取消pay_status → 3已退费cancel_time → 当前时间cancel_reason → '诊前退号'
* 2. adm_schedule_slot.status → 0待约order_id → NULL回滚号源
* 3. adm_schedule_pool.version → version + 1booked_num → booked_num - 1
* 4. refund_log.order_id → 严格关联 order_main.id
* 所有更新置于同一事务中,确保数据强一致性
*/
@Service
public class OrderServiceImpl implements OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderServiceImpl.class);
private final OrderMainMapper orderMainMapper;
private final OrderDetailMapper orderDetailMapper;
private final CatalogItemMapper catalogItemMapper;
private final RefundLogMapper refundLogMapper;
private final ScheduleSlotMapper scheduleSlotMapper;
private final CatalogItemMapper catalogItemMapper;
private final SchedulePoolMapper schedulePoolMapper;
public OrderServiceImpl(OrderMainMapper orderMainMapper, OrderDetailMapper orderDetailMapper,
RefundLogMapper refundLogMapper, ScheduleSlotMapper scheduleSlotMapper,
CatalogItemMapper catalogItemMapper) {
public OrderServiceImpl(OrderMainMapper orderMainMapper,
OrderDetailMapper orderDetailMapper,
CatalogItemMapper catalogItemMapper,
RefundLogMapper refundLogMapper,
ScheduleSlotMapper scheduleSlotMapper,
SchedulePoolMapper schedulePoolMapper) {
this.orderMainMapper = orderMainMapper;
this.orderDetailMapper = orderDetailMapper;
this.catalogItemMapper = catalogItemMapper;
this.refundLogMapper = refundLogMapper;
this.scheduleSlotMapper = scheduleSlotMapper;
this.catalogItemMapper = catalogItemMapper;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void returnOrders(List<Long> orderIds) {
if (orderIds == null || orderIds.isEmpty()) {
throw new BusinessException("请选择需要退回的医嘱");
}
for (Long orderId : orderIds) {
OrderMain order = orderMainMapper.selectByPrimaryKey(orderId);
if (order == null) {
throw new BusinessException("医嘱不存在ID: " + orderId);
}
// ================= Bug #505 核心修复 =================
// 前置校验:执行状态与物理发药状态拦截
String execStatus = order.getExecStatus();
String dispensingStatus = order.getDispensingStatus();
// 若已执行或已发药,严禁直接退回,必须走退药逆向流程
if ("EXECUTED".equals(execStatus) || "DISPENSED".equals(dispensingStatus)) {
throw new BusinessException("该药品已由药房发放,请先执行退药处理,不可直接退回");
}
// ====================================================
// 原有退回逻辑:更新状态为已退回
order.setStatus(OrderStatus.RETURNED.getCode());
order.setUpdateTime(new Date());
orderMainMapper.updateByPrimaryKeySelective(order);
log.info("医嘱退回成功, orderId: {}, status: {}", orderId, OrderStatus.RETURNED.getCode());
}
}
// 其他业务方法保持原样...
@Override
public Page<OrderMain> listOrders(int pageNum, int pageSize, String status) {
PageHelper.startPage(pageNum, pageSize);
return orderMainMapper.selectByStatus(status);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void payOrder(Long orderId) {
OrderMain order = orderMainMapper.selectByPrimaryKey(orderId);
if (order == null) throw new BusinessException("订单不存在");
order.setStatus(OrderStatus.PAID.getCode());
order.setUpdateTime(new Date());
orderMainMapper.updateByPrimaryKeySelective(order);
// Bug #574 修复:支付成功后同步更新排班号状态为“已取”(3)
if (order.getScheduleSlotId() != null) {
scheduleSlotMapper.updateStatus(order.getScheduleSlotId(), ScheduleSlotStatus.TAKEN.getCode());
}
this.schedulePoolMapper = schedulePoolMapper;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelOrder(Long orderId) {
OrderMain order = orderMainMapper.selectByPrimaryKey(orderId);
if (order == null) throw new BusinessException("订单不存在");
order.setStatus(OrderStatus.CANCELLED.getCode());
order.setUpdateTime(new Date());
orderMainMapper.updateByPrimaryKeySelective(order);
// 1. 查询主订单
OrderMain order = orderMainMapper.selectById(orderId);
if (order == null) {
throw new BusinessException("订单不存在,无法执行退号");
}
// Bug #506 修复:退号同步恢复排班池与号源状态
// 2. 更新 order_main 表状态 (PRD 要求)
order.setStatus(0); // 已取消
order.setPayStatus(3); // 已退费
order.setCancelTime(new Date()); // 写入当前退号操作时间
order.setCancelReason("诊前退号"); // 修正原因字段
orderMainMapper.updateById(order);
// 3. 更新 adm_schedule_slot 表状态 (PRD 要求)
if (order.getScheduleSlotId() != null) {
scheduleSlotMapper.updateStatus(order.getScheduleSlotId(), ScheduleSlotStatus.AVAILABLE.getCode());
schedulePoolMapper.decrementBookedNum(order.getSchedulePoolId());
ScheduleSlot slot = scheduleSlotMapper.selectById(order.getScheduleSlotId());
if (slot != null) {
slot.setStatus(0); // 回滚至待约状态
slot.setOrderId(null); // 解除号源占用
scheduleSlotMapper.updateById(slot);
// 4. 更新 adm_schedule_pool 表状态 (PRD 要求)
if (slot.getPoolId() != null) {
SchedulePool pool = schedulePoolMapper.selectById(slot.getPoolId());
if (pool != null) {
pool.setVersion(pool.getVersion() + 1); // version 累加 1
pool.setBookedNum(pool.getBookedNum() - 1); // booked_num 递减 1
schedulePoolMapper.updateById(pool);
}
}
}
}
// 5. 写入 refund_log 表 (PRD 要求order_id 必须关联 order_main.id)
RefundLog refundLog = new RefundLog();
refundLog.setOrderId(order.getId());
refundLog.setRefundAmount(order.getPayAmount() != null ? order.getPayAmount() : 0.0);
refundLog.setRefundTime(new Date());
refundLog.setReason("诊前退号");
refundLogMapper.insert(refundLog);
log.info("门诊诊前退号成功订单ID: {}, 关联号源ID: {}, 排班池ID: {}",
order.getId(), order.getScheduleSlotId(), order.getScheduleSlotId() != null ? scheduleSlotMapper.selectById(order.getScheduleSlotId()).getPoolId() : null);
}
// 以下为其他业务方法占位,保持原有接口契约
@Override
public Page<OrderMain> listOrders(Integer pageNum, Integer pageSize, String status) {
PageHelper.startPage(pageNum, pageSize);
return orderMainMapper.selectByStatus(status);
}
@Override
public void createOrder(OrderMain orderMain, List<OrderDetail> details) {
orderMainMapper.insert(orderMain);
if (details != null && !details.isEmpty()) {
for (OrderDetail detail : details) {
detail.setOrderId(orderMain.getId());
orderDetailMapper.insert(detail);
}
}
}
@Override
public void updateOrderStatus(Long orderId, Integer status) {
OrderMain order = new OrderMain();
order.setId(orderId);
order.setStatus(status);
orderMainMapper.updateById(order);
}
}