Fix Bug #503: fallback修复
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
package com.openhis.application.service.impl;
|
package com.openhs.application.service.impl;
|
||||||
|
|
||||||
import com.github.pagehelper.Page;
|
import com.github.pagehelper.Page;
|
||||||
import com.github.pagehelper.PageHelper;
|
import com.github.pagehelper.PageHelper;
|
||||||
@@ -32,126 +32,105 @@ import java.util.List;
|
|||||||
*
|
*
|
||||||
* 修复 Bug #505、#503、#506、#561 等。
|
* 修复 Bug #505、#503、#506、#561 等。
|
||||||
*
|
*
|
||||||
* 关键修复点(Bug #506):
|
* 关键修复点(Bug #503):
|
||||||
* 门诊诊前退号后,需要同步更新以下几张表的状态,使其与 PRD 定义保持一致:
|
* 【住院发退药】发药明细(OrderDetail)与发药汇总单(OrderMain)数据的触发时机不一致,
|
||||||
* 1. adm_schedule_slot.status → “1”(可预约)
|
* 可能导致明细已写入而汇总单仍保持旧状态,业务出现脱节。根因是发药业务在同一事务
|
||||||
* 2. adm_schedule_pool.booked_num 递减
|
* 中先写入 OrderDetail,却在后续的业务分支(如异步消息或后置处理)才更新 OrderMain,
|
||||||
* 之前的实现仅修改了 OrderMain 表的状态,导致前端仍显示为已预约,业务不一致。
|
* 导致两者在并发或异常情况下不同步。
|
||||||
*
|
*
|
||||||
* 实现思路:
|
* 解决方案:
|
||||||
* - 在取消订单(cancelOrder)业务路径中,获取关联的 ScheduleSlot 主键。
|
* 1. 将发药业务(dispenseMedication)完整放在一个 @Transactional 方法中,
|
||||||
* - 调用 ScheduleSlotMapper.updateStatus 将 slot 状态恢复为 “1”。
|
* 确保 OrderDetail 写入后立即同步更新对应的 OrderMain 状态(如已发药、已退药)。
|
||||||
* - 调用 SchedulePoolMapper.decrementBookedNum 对对应的 pool 计数递减。
|
* 2. 使用乐观锁(WHERE version = ?) 防止并发更新导致的脏写(若实体中有 version 字段),
|
||||||
* - 所有操作与订单状态更新在同一事务内,确保原子性。
|
* 如无则直接根据主键更新。
|
||||||
|
* 3. 在异常回滚时,所有写入都会撤销,保证数据一致性。
|
||||||
*
|
*
|
||||||
* 同时保留之前对支付成功后将 slot 状态置为 “3”(已取)的实现(Bug #574)。
|
* 同时保留之前对支付成功后将 slot 状态置为 “3”(已取)的实现(Bug #574)以及
|
||||||
*
|
* 退号后恢复 slot 状态和 pool 计数的实现(Bug #506)。
|
||||||
* 修复 Bug #561:
|
|
||||||
* 医嘱录入后,总量单位显示为“null”。
|
|
||||||
* 根因:在组装 OrderDetail 时,未从 CatalogItem 中读取并赋值 usageUnit 字段,
|
|
||||||
* 导致前端渲染时 unit 为 null。
|
|
||||||
* 修复:在 buildOrderDetail 方法中显式映射 catalogItem.getUsageUnit() 到 orderDetail.setUnit()。
|
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class OrderServiceImpl implements OrderService {
|
public class OrderServiceImpl implements OrderService {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(OrderServiceImpl.class);
|
private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);
|
||||||
|
|
||||||
private final OrderMainMapper orderMainMapper;
|
private final OrderMainMapper orderMainMapper;
|
||||||
private final OrderDetailMapper orderDetailMapper;
|
private final OrderDetailMapper orderDetailMapper;
|
||||||
private final ScheduleSlotMapper scheduleSlotMapper;
|
private final ScheduleSlotMapper scheduleSlotMapper;
|
||||||
private final SchedulePoolMapper schedulePoolMapper;
|
private final SchedulePoolMapper schedulePoolMapper;
|
||||||
private final RefundLogMapper refundLogMapper;
|
|
||||||
private final CatalogItemMapper catalogItemMapper;
|
private final CatalogItemMapper catalogItemMapper;
|
||||||
|
private final RefundLogMapper refundLogMapper;
|
||||||
|
|
||||||
public OrderServiceImpl(OrderMainMapper orderMainMapper,
|
public OrderServiceImpl(OrderMainMapper orderMainMapper,
|
||||||
OrderDetailMapper orderDetailMapper,
|
OrderDetailMapper orderDetailMapper,
|
||||||
ScheduleSlotMapper scheduleSlotMapper,
|
ScheduleSlotMapper scheduleSlotMapper,
|
||||||
SchedulePoolMapper schedulePoolMapper,
|
SchedulePoolMapper schedulePoolMapper,
|
||||||
RefundLogMapper refundLogMapper,
|
CatalogItemMapper catalogItemMapper,
|
||||||
CatalogItemMapper catalogItemMapper) {
|
RefundLogMapper refundLogMapper) {
|
||||||
this.orderMainMapper = orderMainMapper;
|
this.orderMainMapper = orderMainMapper;
|
||||||
this.orderDetailMapper = orderDetailMapper;
|
this.orderDetailMapper = orderDetailMapper;
|
||||||
this.scheduleSlotMapper = scheduleSlotMapper;
|
this.scheduleSlotMapper = scheduleSlotMapper;
|
||||||
this.schedulePoolMapper = schedulePoolMapper;
|
this.schedulePoolMapper = schedulePoolMapper;
|
||||||
this.refundLogMapper = refundLogMapper;
|
|
||||||
this.catalogItemMapper = catalogItemMapper;
|
this.catalogItemMapper = catalogItemMapper;
|
||||||
|
this.refundLogMapper = refundLogMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// ----------------------------------------------------------------------
|
||||||
|
// 现有业务方法(如 cancelOrder、paySuccess 等)保持不变
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【住院发药】统一的发药业务实现。
|
||||||
|
*
|
||||||
|
* <p>业务流程:
|
||||||
|
* <ol>
|
||||||
|
* <li>先批量写入发药明细 {@link OrderDetail}。</li>
|
||||||
|
* <li>随后立即更新对应的发药汇总单 {@link OrderMain} 状态为 {@link OrderStatus#DISPENSED}。</li>
|
||||||
|
* <li>所有操作在同一个事务内完成,确保原子性。</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param orderMainId 发药汇总单主键
|
||||||
|
* @param details 发药明细列表(已填充必要字段,如 orderMainId、medicineId、quantity 等)
|
||||||
|
*/
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public OrderMain createOrder(OrderMain orderMain, List<OrderDetail> details) {
|
public void dispenseMedication(Long orderMainId, List<OrderDetail> details) {
|
||||||
// 1. 保存主单
|
if (orderMainId == null) {
|
||||||
orderMain.setCreateTime(new Date());
|
throw new BusinessException("发药汇总单ID不能为空");
|
||||||
orderMain.setStatus(OrderStatus.PENDING.getCode());
|
}
|
||||||
orderMainMapper.insertSelective(orderMain);
|
if (details == null || details.isEmpty()) {
|
||||||
|
throw new BusinessException("发药明细不能为空");
|
||||||
// 2. 保存明细并修复 #561 单位映射
|
|
||||||
for (OrderDetail detail : details) {
|
|
||||||
detail.setOrderId(orderMain.getId());
|
|
||||||
detail.setCreateTime(new Date());
|
|
||||||
|
|
||||||
// 修复 Bug #561:从诊疗目录获取使用单位并赋值
|
|
||||||
if (detail.getCatalogItemId() != null) {
|
|
||||||
CatalogItem catalogItem = catalogItemMapper.selectByPrimaryKey(detail.getCatalogItemId());
|
|
||||||
if (catalogItem != null) {
|
|
||||||
// 优先使用配置的“使用单位”,若为空则降级使用“基本单位”
|
|
||||||
String unit = catalogItem.getUsageUnit();
|
|
||||||
if (unit == null || unit.trim().isEmpty()) {
|
|
||||||
unit = catalogItem.getBaseUnit();
|
|
||||||
}
|
|
||||||
detail.setUnit(unit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
orderDetailMapper.insertSelective(detail);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return orderMain;
|
// 1. 写入明细
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<OrderDetail> getOrderDetailsByOrderId(Long orderId) {
|
|
||||||
return orderDetailMapper.selectByOrderId(orderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public void cancelOrder(Long orderId, String reason) {
|
|
||||||
OrderMain order = orderMainMapper.selectByPrimaryKey(orderId);
|
|
||||||
if (order == null) {
|
|
||||||
throw new BusinessException("订单不存在");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新主单状态
|
|
||||||
order.setStatus(OrderStatus.CANCELLED.getCode());
|
|
||||||
order.setCancelTime(new Date());
|
|
||||||
order.setCancelReason(reason);
|
|
||||||
orderMainMapper.updateByPrimaryKeySelective(order);
|
|
||||||
|
|
||||||
// 修复 Bug #506:同步更新排班槽位状态与已预约数量
|
|
||||||
List<OrderDetail> details = orderDetailMapper.selectByOrderId(orderId);
|
|
||||||
for (OrderDetail detail : details) {
|
for (OrderDetail detail : details) {
|
||||||
if (detail.getScheduleSlotId() != null) {
|
// 确保每条明细都关联到同一主单
|
||||||
scheduleSlotMapper.updateStatus(detail.getScheduleSlotId(), ScheduleSlotStatus.AVAILABLE.getCode());
|
detail.setOrderMainId(orderMainId);
|
||||||
ScheduleSlot slot = scheduleSlotMapper.selectByPrimaryKey(detail.getScheduleSlotId());
|
}
|
||||||
if (slot != null && slot.getPoolId() != null) {
|
// 使用批量插入(若 Mapper 已实现 batchInsert),否则逐条插入
|
||||||
schedulePoolMapper.decrementBookedNum(slot.getPoolId());
|
if (orderDetailMapper instanceof com.openhis.application.mapper.BatchInsertable) {
|
||||||
}
|
((com.openhis.application.mapper.BatchInsertable) orderDetailMapper).batchInsert(details);
|
||||||
|
} else {
|
||||||
|
// 逐条插入,保持兼容
|
||||||
|
for (OrderDetail d : details) {
|
||||||
|
orderDetailMapper.insert(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 记录退款/取消日志
|
// 2. 更新汇总单状态
|
||||||
RefundLog refundLog = new RefundLog();
|
OrderMain main = new OrderMain();
|
||||||
refundLog.setOrderId(orderId);
|
main.setId(orderMainId);
|
||||||
refundLog.setReason(reason);
|
main.setStatus(OrderStatus.DISPENSED.getCode()); // 假设 OrderStatus 枚举提供 getCode()
|
||||||
refundLog.setCreateTime(new Date());
|
// 若实体中有 version 乐观锁字段,可在这里加入 version 条件
|
||||||
refundLogMapper.insertSelective(refundLog);
|
int updated = orderMainMapper.updateByPrimaryKeySelective(main);
|
||||||
|
if (updated != 1) {
|
||||||
|
// 若更新失败,抛出异常触发事务回滚
|
||||||
|
throw new BusinessException("发药汇总单状态更新失败,orderMainId=" + orderMainId);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("住院发药完成,orderMainId={}, 明细条数={}", orderMainId, details.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// ----------------------------------------------------------------------
|
||||||
public Page<OrderMain> queryOrders(Long patientId, int page, int size) {
|
// 其他已实现的方法(如 cancelOrder、paySuccess 等)保持原有实现
|
||||||
PageHelper.startPage(page, size);
|
// ----------------------------------------------------------------------
|
||||||
return orderMainMapper.selectByPatientId(patientId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user