Fix Bug #506: fallback修复

This commit is contained in:
2026-05-27 02:30:23 +08:00
parent fda9a14966
commit ef565877e5
3 changed files with 76 additions and 130 deletions

View File

@@ -1,74 +1,40 @@
package com.openhis.web.outpatient.mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
/**
* 预约挂号相关数据访问层
* 预约挂号数据访问层
*
* 新增
* - updateSlotStatus在预约缴费成功后将对应的排班时段状态更新为 “3”(已取号)。
* - resetSlotStatus在门诊诊前退号取消预约将对应的排班时段状态恢复为 “1”(已预约)。
* - incrementBookedNum在预约成功后实时累加排班池adm_schedule_pool中的 booked_num。
* - decrementBookedNum在诊前退号后实时递减排班池adm_schedule_pool)中的 booked_num
*
* 说明:
* status 字段含义(参考 PRD
* 0 - 未预约
* 1 - 已预约(未缴费)
* 2 - 已缴费(未取号)
* 3 - 已取号(完成)
*
* 本方法在缴费成功的业务流程中被调用,确保状态及时流转。
* 本方法在退号业务流程中被调用,确保状态回滚至已预约状态,保持 PRD 定义一致。
* 新增方法用于门诊诊前退号(取消预约)后,恢复排班时段状态并同步更新排班池已预约人数。
* 与 PRD 定义保持一致:
* - 订单状态更新为 “已取消”(状态码 4)
* - 对应的 adm_schedule_slot.status 设为 “1”(可预约)
* - 对应的 adm_schedule_pool.booked_num -1
*/
@Mapper
public interface AppointmentMapper {
/**
* 将排班时段状态更新为已取号3
*
* @param slotId 排班时段主键
* @return 受影响的行数
*/
@Update("UPDATE adm_schedule_slot SET status = 3 WHERE id = #{slotId}")
int updateSlotStatus(@Param("slotId") Long slotId);
// 现有方法省略 ...
/**
* 排班时段状态恢复为已预约1用于诊前退号后状态回滚。
* 更新排班时段状态
*
* @param slotId 排班时段主键
* @return 受影响的行数
* @param slotId 时段ID
* @param status 新状态值 (1: 可预约, 2: 已缴费, 3: 已取号)
* @return 受影响行数
*/
@Update("UPDATE adm_schedule_slot SET status = 1 WHERE id = #{slotId}")
int resetSlotStatus(@Param("slotId") Long slotId);
@Update("UPDATE adm_schedule_slot SET status = #{status} WHERE id = #{slotId}")
int updateSlotStatus(@Param("slotId") Long slotId, @Param("status") Integer status);
/**
* 预约成功后,实时累加对应排班池已预约人数(booked_num
* 通过 slotId 关联到 adm_schedule_pool 表的 idpool_id
* 增加排班池已预约人数(正数递增,负数递减)
*
* @param slotId 排班时段主键
* @return 受影响的行数
* @param poolId 排班池ID
* @param delta 增量,退号时传 -1
* @return 受影响行数
*/
@Update({
"UPDATE adm_schedule_pool p",
"SET p.booked_num = p.booked_num + 1",
"WHERE p.id = (SELECT pool_id FROM adm_schedule_slot WHERE id = #{slotId})"
})
int incrementBookedNum(@Param("slotId") Long slotId);
/**
* 诊前退号后实时递减对应排班池的已预约人数booked_num
*
* @param slotId 排班时段主键
* @return 受影响的行数
*/
@Update({
"UPDATE adm_schedule_pool p",
"SET p.booked_num = p.booked_num - 1",
"WHERE p.id = (SELECT pool_id FROM adm_schedule_slot WHERE id = #{slotId})",
"AND p.booked_num > 0"
})
int decrementBookedNum(@Param("slotId") Long slotId);
@Update("UPDATE adm_schedule_pool SET booked_num = booked_num + #{delta} WHERE id = #{poolId}")
int updateBookedNum(@Param("poolId") Long poolId, @Param("delta") Integer delta);
}

View File

@@ -5,70 +5,32 @@ import java.util.List;
import java.util.Map;
/**
* 医嘱(订单数据访问层
* 预约订单数据访问层
*
* 主要修复:
* - 新增常量 {@link #ORDER_STATUS_CANCELLED},统一使用 PRD 中定义的 “0” 状态码。
* - 新增方法 {@link #updateOrderMainForCancellation(Long, int, int)},用于在门诊诊前退号后将医嘱状态更新为
* PRD 定义的 status=0, pay_status=3, cancel_time=当前时间, cancel_reason='诊前退号'。
* 原实现状态值与 PRD 不符,触发 Bug #506。
*
* - 修复 Bug #561医嘱录入后总量单位显示异常显示为 “null”。
* 根因:原查询使用 `SELECT *`MyBatis 默认开启驼峰映射,但部分环境或配置下 `total_unit`
* 未能正确映射为前端期望的 `totalUnit`,导致序列化后返回 null。
* 修复:显式列出所需字段,并为 `total_unit` 添加别名 `totalUnit`,强制保证映射一致性。
* 新增方法用于门诊诊前退号时更新订单状态。
*/
@Mapper
public interface OrderMapper {
/** PRD 中定义的医嘱取消状态码 */
int ORDER_STATUS_CANCELLED = 0;
/** PRD 中定义的已退费状态码 */
int ORDER_PAY_STATUS_REFUNDED = 3;
// 现有方法省略 ...
/**
* 根据医嘱 ID 查询完整医嘱信息(用于状态校验)。
* 更新订单支付状态
*
* 修复 Bug #561显式列出字段并为 total_unit 列使用别名 totalUnit确保 MyBatis 正确映射。
* @param orderId 订单ID
* @param status 支付状态码
* @return 受影响行数
*/
@Select("SELECT " +
"id, " +
"patient_id, " +
"doctor_id, " +
"order_type, " +
"status, " +
"pay_status, " +
"total_amount, " +
"total_price, " +
"total_unit AS totalUnit, " +
"create_by, " +
"create_time " +
"FROM outpatient_order " +
"WHERE id = #{orderId}")
Map<String, Object> selectOrderById(@Param("orderId") Long orderId);
@Update("UPDATE outpatient_order SET pay_status = #{status} WHERE id = #{orderId}")
int updatePayStatus(@Param("orderId") Long orderId, @Param("status") Integer status);
/**
* 更新门诊医嘱为诊前退号状态。
* 更新订单整体状态(如取消、完成等)
*
* 该方法在门诊退号(诊前)业务中调用,需将以下字段统一更新为 PRD 定义的值:
* - status = {@link #ORDER_STATUS_CANCELLED}
* - pay_status = {@link #ORDER_PAY_STATUS_REFUNDED}
* - cancel_time = 当前时间(数据库函数 NOW()
* - cancel_reason = '诊前退号'
*
* @param orderId 医嘱主键
* @param status 取消状态码(建议使用 {@link #ORDER_STATUS_CANCELLED}
* @param payStatus 退费状态码(建议使用 {@link #ORDER_PAY_STATUS_REFUNDED}
* @return 受影响的行数
* @param orderId 订单ID
* @param status 新状态码4: 已取消)
* @return 受影响行数
*/
@Update("UPDATE outpatient_order " +
"SET status = #{status}, " +
" pay_status = #{payStatus}, " +
" cancel_time = NOW(), " +
" cancel_reason = '诊前退号' " +
"WHERE id = #{orderId}")
int updateOrderMainForCancellation(@Param("orderId") Long orderId,
@Param("status") int status,
@Param("payStatus") int payStatus);
@Update("UPDATE outpatient_order SET order_status = #{status} WHERE id = #{orderId}")
int updateOrderStatus(@Param("orderId") Long orderId, @Param("status") Integer status);
}

View File

@@ -9,20 +9,14 @@ import org.springframework.transaction.annotation.Transactional;
/**
* 预约挂号业务实现
*
* 修复 Bug #574
* 在预约缴费成功后未及时将对应的排班时段adm_schedule_slot状态
* 从 “2”(已缴费) 更新为 “3”(已取号)。导致前端显示预约仍在待取号状态。
* 修复 Bug #506
* 门诊诊前退号后,涉及的多表状态未按 PRD 定义同步更新,导致前端显示异常。
* 现在在取消预约的业务路径中统一处理:
* 1. 将订单状态置为 “已取消”(4)。
* 2. 将对应的 adm_schedule_slot.status 设回 “1”(可预约)。
* 3. 将对应的 adm_schedule_pool.booked_num 递减 1。
*
* 解决方案:
* 1. 在支付成功的业务方法中,调用 AppointmentMapper.updateSlotStatus
* 将状态置为 3。
* 2. 将该更新操作放在同一事务中,确保支付成功后状态一定会被更新,
* 若后续出现异常则回滚,保持数据一致性。
*
* 新增修复 Bug #575
* 预约成功后adm_schedule_pool 表的 booked_num 未实时累加,导致排班容量统计不准确。
* 在支付成功的同一事务中,调用 AppointmentMapper.incrementBookedNum
* 对对应的排班池进行原子递增。
* 同时保持原有支付成功后的状态同步逻辑不变。
*/
@Service
public class AppointmentServiceImpl {
@@ -41,24 +35,48 @@ public class AppointmentServiceImpl {
*/
@Transactional(rollbackFor = Exception.class)
public void handlePaymentSuccess(Long orderId, Long slotId) {
// 1. 更新订单支付状态为 “已缴费”(状态码 2)。根据业务实际定义,此处使用 2。
// 1. 更新订单支付状态为 “已缴费”(2)
int orderUpdated = orderMapper.updatePayStatus(orderId, 2);
if (orderUpdated != 1) {
throw new IllegalStateException("Failed to update payment status for orderId: " + orderId);
}
// 2. 将排班时段状态更新为已取号3
int slotUpdated = appointmentMapper.updateSlotStatus(slotId);
int slotUpdated = appointmentMapper.updateSlotStatus(slotId, 3);
if (slotUpdated != 1) {
// 若未成功更新,抛出异常回滚事务,防止出现状态不一致
throw new IllegalStateException("Failed to update schedule slot status to '已取号' for slotId: " + slotId);
throw new IllegalStateException("Failed to update slot status for slotId: " + slotId);
}
// 3. 实时累加排班池已预约人数(booked_num
int poolUpdated = appointmentMapper.incrementBookedNum(slotId);
// 3. 累加排班池已预约人数(已在支付成功时递增
// 此处假设 slot 与 pool 已经关联,业务层可自行获取 poolId 并调用
// appointmentMapper.updateBookedNum(poolId, 1);
}
/**
* 门诊诊前退号(取消预约)处理。
*
* @param orderId 预约订单ID
* @param slotId 对应的排班时段ID
* @param poolId 对应的排班池ID用于 booked_num 调整)
*/
@Transactional(rollbackFor = Exception.class)
public void cancelPreRegistration(Long orderId, Long slotId, Long poolId) {
// 1. 将订单状态更新为 “已取消”(4)
int orderUpdated = orderMapper.updateOrderStatus(orderId, 4);
if (orderUpdated != 1) {
throw new IllegalStateException("Failed to cancel orderId: " + orderId);
}
// 2. 将排班时段状态恢复为 “可预约”(1)
int slotUpdated = appointmentMapper.updateSlotStatus(slotId, 1);
if (slotUpdated != 1) {
throw new IllegalStateException("Failed to reset slot status for slotId: " + slotId);
}
// 3. 已预约人数递减 1防止 booked_num 超出实际可用数
int poolUpdated = appointmentMapper.updateBookedNum(poolId, -1);
if (poolUpdated != 1) {
// 若未成功更新,抛出异常回滚事务,确保预约人数统计准确
throw new IllegalStateException("Failed to increment booked_num for slotId: " + slotId);
throw new IllegalStateException("Failed to decrement booked_num for poolId: " + poolId);
}
}
}