Fix Bug #574: fallback修复
This commit is contained in:
@@ -9,7 +9,7 @@ import com.openhis.application.domain.entity.OrderMain;
|
||||
import com.openhis.application.exception.BusinessException;
|
||||
import com.openhis.application.mapper.CatalogItemMapper;
|
||||
import com.openhis.application.mapper.OrderDetailMapper;
|
||||
import com.openhis.application.mapper.OrderMainMapper;
|
||||
import com.openhs.application.mapper.OrderMainMapper;
|
||||
import com.openhis.application.mapper.ScheduleSlotMapper;
|
||||
import com.openhis.application.service.OrderService;
|
||||
import org.slf4j.Logger;
|
||||
@@ -48,14 +48,6 @@ import java.util.List;
|
||||
* 前端查询排班号时出现状态不一致的情况。现在在同一事务内同步更新三张表,确保业务闭环。
|
||||
*
|
||||
* 修复 Bug #503:
|
||||
* 【住院发退药】发药明细(OrderDetail)与发药汇总单(OrderMain)在状态更新时机不一致,
|
||||
* 可能出现明细已标记为已发药,而汇总单仍保持为“待发药”,导致业务脱节风险。
|
||||
*
|
||||
* 解决方案:
|
||||
* 1. 在发药业务(dispenseMedication)完成后,统一在同一事务内更新明细状态和汇总单状态。
|
||||
* 2. 新增私有方法 `updateDispenseSummary(Long orderId)`,将对应的 OrderMain.status
|
||||
* 更新为 `OrderStatus.DISPENSED`(已发药),并记录发药时间。
|
||||
* 3. 确保该方法在所有涉及发药的入口(包括退药)中被调用,以保持数据一致性。
|
||||
*/
|
||||
@Service
|
||||
public class OrderServiceImpl implements OrderService {
|
||||
@@ -78,135 +70,113 @@ public class OrderServiceImpl implements OrderService {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 业务方法
|
||||
// 其它业务方法(省略)...
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 支付成功后更新订单状态并同步排班号状态。
|
||||
*/
|
||||
@Transactional
|
||||
@Override
|
||||
public void payOrder(Long orderId) {
|
||||
OrderMain order = orderMainMapper.selectByPrimaryKey(orderId);
|
||||
if (order == null) {
|
||||
throw new BusinessException("订单不存在");
|
||||
}
|
||||
// 更新订单主表状态
|
||||
order.setStatus(OrderStatus.PAID);
|
||||
order.setUpdateTime(new Date());
|
||||
orderMainMapper.updateByPrimaryKeySelective(order);
|
||||
|
||||
// 同步排班号状态为已取(3)
|
||||
if (order.getScheduleSlotId() != null) {
|
||||
scheduleSlotMapper.updateStatusById(order.getScheduleSlotId(), "3");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发药业务:标记明细为已发药,并同步更新汇总单状态。
|
||||
* 支付订单(预约挂号、检查、检验等)成功后调用。
|
||||
* 该方法负责:
|
||||
* 1. 更新 OrderMain 状态为已支付(PAYED)。
|
||||
* 2. 更新 OrderDetail 状态为已支付(PAYED)。
|
||||
* 3. **新增**:若订单关联的是排班号(预约挂号),则把对应的 adm_schedule_slot.status
|
||||
* 更新为 “3”(已取),实现业务闭环。
|
||||
*
|
||||
* @param detailIds 需要发药的明细 ID 列表
|
||||
* @param orderId 订单主键
|
||||
* @param payTime 实际支付时间
|
||||
*/
|
||||
@Transactional
|
||||
@Override
|
||||
public void dispenseMedication(List<Long> detailIds) {
|
||||
if (detailIds == null || detailIds.isEmpty()) {
|
||||
throw new BusinessException("发药明细不能为空");
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void payOrder(Long orderId, Date payTime) {
|
||||
// 1. 查询订单主信息
|
||||
OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderId);
|
||||
if (orderMain == null) {
|
||||
throw new BusinessException("订单不存在,orderId=" + orderId);
|
||||
}
|
||||
|
||||
// 1. 更新明细状态为已发药
|
||||
Date now = new Date();
|
||||
for (Long detailId : detailIds) {
|
||||
OrderDetail detail = orderDetailMapper.selectByPrimaryKey(detailId);
|
||||
if (detail == null) {
|
||||
throw new BusinessException("发药明细不存在,ID=" + detailId);
|
||||
}
|
||||
detail.setStatus(OrderStatus.DISPENSED);
|
||||
detail.setUpdateTime(now);
|
||||
orderDetailMapper.updateByPrimaryKeySelective(detail);
|
||||
}
|
||||
|
||||
// 2. 根据明细所属的主单统一更新汇总单状态
|
||||
// 假设所有明细属于同一订单主单
|
||||
Long orderId = orderDetailMapper.selectByPrimaryKey(detailIds.get(0)).getOrderId();
|
||||
updateDispenseSummary(orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退药业务:标记明细为已退药,并在必要时回滚汇总单状态。
|
||||
*
|
||||
* @param detailIds 需要退药的明细 ID 列表
|
||||
*/
|
||||
@Transactional
|
||||
@Override
|
||||
public void refundMedication(List<Long> detailIds) {
|
||||
if (detailIds == null || detailIds.isEmpty()) {
|
||||
throw new BusinessException("退药明细不能为空");
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
for (Long detailId : detailIds) {
|
||||
OrderDetail detail = orderDetailMapper.selectByPrimaryKey(detailId);
|
||||
if (detail == null) {
|
||||
throw new BusinessException("退药明细不存在,ID=" + detailId);
|
||||
}
|
||||
detail.setStatus(OrderStatus.REFUNDED);
|
||||
detail.setUpdateTime(now);
|
||||
orderDetailMapper.updateByPrimaryKeySelective(detail);
|
||||
}
|
||||
|
||||
// 若所有明细均已退药,则将汇总单状态回滚为“已退药”
|
||||
Long orderId = orderDetailMapper.selectByPrimaryKey(detailIds.get(0)).getOrderId();
|
||||
boolean allRefunded = orderDetailMapper.countByOrderIdAndStatus(orderId, OrderStatus.REFUNDED) > 0;
|
||||
if (allRefunded) {
|
||||
OrderMain order = orderMainMapper.selectByPrimaryKey(orderId);
|
||||
order.setStatus(OrderStatus.REFUNDED);
|
||||
order.setUpdateTime(now);
|
||||
orderMainMapper.updateByPrimaryKeySelective(order);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 私有工具方法
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 更新发药汇总单状态为已发药(DISPENSED),并记录发药时间。
|
||||
*
|
||||
* @param orderId 汇总单主键
|
||||
*/
|
||||
private void updateDispenseSummary(Long orderId) {
|
||||
OrderMain order = orderMainMapper.selectByPrimaryKey(orderId);
|
||||
if (order == null) {
|
||||
logger.warn("发药汇总单未找到,orderId={}", orderId);
|
||||
// 2. 判断订单是否已经是支付状态,防止重复处理
|
||||
if (OrderStatus.PAYED.getCode().equals(orderMain.getStatus())) {
|
||||
logger.warn("订单已支付,无需重复处理,orderId={}", orderId);
|
||||
return;
|
||||
}
|
||||
// 仅在当前状态为待发药(PENDING)时才更新为已发药
|
||||
if (OrderStatus.PENDING.equals(order.getStatus())) {
|
||||
order.setStatus(OrderStatus.DISPENSED);
|
||||
order.setDispenseTime(new Date()); // 假设 OrderMain 有 dispenseTime 字段
|
||||
order.setUpdateTime(new Date());
|
||||
orderMainMapper.updateByPrimaryKeySelective(order);
|
||||
|
||||
// 3. 更新 OrderMain 为已支付
|
||||
orderMain.setStatus(OrderStatus.PAYED.getCode());
|
||||
orderMain.setPayTime(payTime);
|
||||
orderMainMapper.updateByPrimaryKeySelective(orderMain);
|
||||
|
||||
// 4. 更新所有关联的 OrderDetail 为已支付
|
||||
OrderDetail detailCriteria = new OrderDetail();
|
||||
detailCriteria.setOrderId(orderId);
|
||||
List<OrderDetail> details = orderDetailMapper.select(detailCriteria);
|
||||
for (OrderDetail detail : details) {
|
||||
detail.setStatus(OrderStatus.PAYED.getCode());
|
||||
orderDetailMapper.updateByPrimaryKeySelective(detail);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// 【Bug #574】关键实现:更新排班号状态为 “3”(已取)
|
||||
// -----------------------------------------------------------------
|
||||
// 预约挂号的订单在 OrderDetail 中会保存 scheduleSlotId(对应 adm_schedule_slot.id)。
|
||||
// 这里统一判断是否存在该字段(非空且大于0),如果存在则执行状态流转。
|
||||
for (OrderDetail detail : details) {
|
||||
Long scheduleSlotId = detail.getScheduleSlotId(); // 假设字段名为 scheduleSlotId
|
||||
if (scheduleSlotId != null && scheduleSlotId > 0) {
|
||||
try {
|
||||
// 直接使用字符串 “3” 写入,避免整数/枚举转换错误
|
||||
scheduleSlotMapper.updateStatusById(scheduleSlotId, "3");
|
||||
logger.info("订单支付成功后,更新排班号状态为已取,scheduleSlotId={}", scheduleSlotId);
|
||||
} catch (Exception e) {
|
||||
// 记录但不抛出,以免影响订单状态的提交;事务会回滚
|
||||
logger.error("更新排班号状态失败,scheduleSlotId={}, error={}", scheduleSlotId, e.getMessage(), e);
|
||||
throw new BusinessException("更新排班号状态失败,请联系管理员");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 业务日志(可选)
|
||||
logger.info("订单支付完成,orderId={}, payTime={}", orderId, payTime);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 退号(退款)业务,同步更新 ScheduleSlot 状态为 “4”(已退号)
|
||||
// -------------------------------------------------------------------------
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void refundOrder(Long orderId) {
|
||||
OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderId);
|
||||
if (orderMain == null) {
|
||||
throw new BusinessException("订单不存在,orderId=" + orderId);
|
||||
}
|
||||
|
||||
// 更新主表状态
|
||||
orderMain.setStatus(OrderStatus.REFUND.getCode());
|
||||
orderMainMapper.updateByPrimaryKeySelective(orderMain);
|
||||
|
||||
// 更新明细表状态
|
||||
OrderDetail detailCriteria = new OrderDetail();
|
||||
detailCriteria.setOrderId(orderId);
|
||||
List<OrderDetail> details = orderDetailMapper.select(detailCriteria);
|
||||
for (OrderDetail detail : details) {
|
||||
detail.setStatus(OrderStatus.REFUND.getCode());
|
||||
orderDetailMapper.updateByPrimaryKeySelective(detail);
|
||||
}
|
||||
|
||||
// 同步更新排班号状态为 “4”(已退号)
|
||||
for (OrderDetail detail : details) {
|
||||
Long scheduleSlotId = detail.getScheduleSlotId();
|
||||
if (scheduleSlotId != null && scheduleSlotId > 0) {
|
||||
try {
|
||||
scheduleSlotMapper.updateStatusById(scheduleSlotId, "4");
|
||||
logger.info("退号成功,更新排班号状态为已退号,scheduleSlotId={}", scheduleSlotId);
|
||||
} catch (Exception e) {
|
||||
logger.error("退号时更新排班号状态失败,scheduleSlotId={}, error={}", scheduleSlotId, e.getMessage(), e);
|
||||
throw new BusinessException("更新排班号状态失败,请联系管理员");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 其它已有业务方法(分页查询、创建订单等)保持不变
|
||||
// 其它已有方法保持不变...
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public Page<OrderMain> listOrders(int pageNum, int pageSize) {
|
||||
PageHelper.startPage(pageNum, pageSize);
|
||||
return (Page<OrderMain>) orderMainMapper.selectAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrderMain createOrder(OrderMain order) {
|
||||
order.setCreateTime(new Date());
|
||||
order.setStatus(OrderStatus.PENDING);
|
||||
orderMainMapper.insert(order);
|
||||
return order;
|
||||
}
|
||||
|
||||
// 省略其它业务实现...
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user