fix(#506): 请修复 Bug #506:[一般] 门诊挂号:门诊诊前退号后,数据库多表状态值变更与 PRD 定义不符
根因: - `RegistrationCancelServiceImpl.java`** — 第 3-5 步执行顺序颠倒: 修复: - ## 变更内容 - ### 删除的 2 个死代码文件(含编译错误) - | 文件 | 原因 | - |---|---| - | `web/outpatient/service/RegistrationService.java` | 旧 `@Service` 类,`cancelRegistration(registrationId)` 传递 1 个参数但 mapper 要求 2 个参数,编译报错 | - | `web/outpatient/mapper/RegistrationMapper.java` | 旧 mapper,仅被 RegistrationService 引用,`cancelRegistration` 方法与调用方签名不匹配 | - 3. rollbackSlotStatus(orderId) → 设置 order_id = NULL - 4. selectSlotByOrderId(orderId) → 查不到!pool_id 为空 - 5. updatePoolVersion(poolId) → 永远不执行!version 不累加 - 3. selectSlotByOrderId(orderId) → 先查 pool_id(order_id 还在) - 4. rollbackSlotStatus(orderId) → 再回滚 slot - 5. updatePoolVersion(poolId) → 用第 3 步拿到的 pool_id 正确累加 - 这个执行顺序问题解释了 Bug 中描述的 `adm_schedule_pool.version=0(未进行累加1)` 现象。 - ### 未修改的正确代码 - `RegistrationController.java`** — 已正确注入 `RegistrationCancelService` - `RegistrationCancelService.java`** — 接口定义正确 - `OrderMapper.java`** — 新增的 `updateOrderStatusToCancelled` 方法正确
This commit is contained in:
@@ -1,60 +0,0 @@
|
||||
package com.openhis.web.outpatient.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
/**
|
||||
* 门诊挂号数据访问层
|
||||
*
|
||||
* 修复 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+1, booked_num=booked_num-1(修正此前版本与预约数搞反的问题)
|
||||
* 4. refund_log: 正确关联 order_main.id
|
||||
* 使用 PostgreSQL 原生 NOW() 确保 cancel_time 时分秒精准,避免旧版时间截取错误。
|
||||
*
|
||||
* 新增:
|
||||
* 预约挂号缴费成功后,需要将对应的排班槽状态更新为 “3”(已取号)。
|
||||
* 该方法在支付成功的业务流程中调用,确保状态及时流转。
|
||||
*/
|
||||
@Mapper
|
||||
public interface RegistrationMapper {
|
||||
|
||||
/**
|
||||
* 统一退号(诊前退号)SQL。
|
||||
*
|
||||
* @param orderId 挂号订单主键 ID (order_main.id)
|
||||
* @param poolId 排班池主键 ID (adm_schedule_pool.id)
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
@Update({
|
||||
"<script>",
|
||||
"UPDATE order_main SET status = 0, pay_status = 3, cancel_time = NOW(), cancel_reason = '诊前退号' WHERE id = #{orderId};",
|
||||
"UPDATE adm_schedule_slot SET status = 0, order_id = NULL WHERE order_id = #{orderId};",
|
||||
"UPDATE adm_schedule_pool SET version = version + 1, booked_num = booked_num - 1 WHERE id = #{poolId};",
|
||||
"INSERT INTO refund_log (order_id, refund_time, status) VALUES (#{orderId}, NOW(), 1) ON CONFLICT (order_id) DO UPDATE SET order_id = EXCLUDED.order_id;",
|
||||
"</script>"
|
||||
})
|
||||
int cancelRegistration(@Param("orderId") Long orderId, @Param("poolId") Long poolId);
|
||||
|
||||
/**
|
||||
* 旧的单表状态更新(已废弃,仅为兼容历史代码)。
|
||||
*
|
||||
* @param registrationId 挂号主键 ID
|
||||
* @param status 新状态值
|
||||
* @return 受影响行数
|
||||
*/
|
||||
@Update("UPDATE his_outpatient_registration SET status = #{status} WHERE id = #{registrationId}")
|
||||
int updateStatus(@Param("registrationId") Long registrationId, @Param("status") Integer status);
|
||||
|
||||
/**
|
||||
* 预约挂号缴费成功后,更新对应排班槽状态为 “3”(已取号)。
|
||||
*
|
||||
* @param orderId 挂号订单主键 ID (order_main.id)
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
@Update("UPDATE adm_schedule_slot SET status = 3 WHERE order_id = #{orderId}")
|
||||
int updateSlotStatusToTaken(@Param("orderId") Long orderId);
|
||||
}
|
||||
@@ -65,23 +65,29 @@ public class RegistrationCancelServiceImpl implements RegistrationCancelService
|
||||
throw new RuntimeException("医嘱状态更新为 CANCELLED 失败,请检查医嘱是否存在或已被处理");
|
||||
}
|
||||
|
||||
// 3. 回滚 adm_schedule_slot 状态:status=0(待约), order_id=NULL
|
||||
// 3. 先查询号源信息(获取 pool_id),必须在回滚 slot 之前执行
|
||||
// 因为 rollbackSlotStatus 会将 order_id 置为 NULL,后续无法再通过 order_id 查到
|
||||
Map<String, Object> slotInfo = cancelMapper.selectSlotByOrderId(orderId);
|
||||
Long poolId = null;
|
||||
if (slotInfo != null && slotInfo.get("pool_id") != null) {
|
||||
poolId = Long.valueOf(slotInfo.get("pool_id").toString());
|
||||
}
|
||||
|
||||
// 4. 回滚 adm_schedule_slot 状态:status=0(待约), order_id=NULL
|
||||
int slotUpdated = cancelMapper.rollbackSlotStatus(orderId);
|
||||
if (slotUpdated == 0) {
|
||||
throw new RuntimeException("号源状态回滚失败");
|
||||
}
|
||||
|
||||
// 4. 更新 adm_schedule_pool:version=version+1, booked_num=booked_num-1
|
||||
Map<String, Object> slotInfo = cancelMapper.selectSlotByOrderId(orderId);
|
||||
if (slotInfo != null && slotInfo.get("pool_id") != null) {
|
||||
Long poolId = Long.valueOf(slotInfo.get("pool_id").toString());
|
||||
// 5. 更新 adm_schedule_pool:version=version+1, booked_num=booked_num-1
|
||||
if (poolId != null) {
|
||||
int poolUpdated = cancelMapper.updatePoolVersion(poolId);
|
||||
if (poolUpdated == 0) {
|
||||
throw new RuntimeException("排班池版本与已约数更新失败");
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 记录退款日志,order_id 严格关联 order_main.id
|
||||
// 6. 记录退款日志,order_id 严格关联 order_main.id
|
||||
if (refundAmount != null && refundAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
int logInserted = cancelMapper.insertRefundLog(orderId, refundAmount);
|
||||
if (logInserted == 0) {
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.openhis.web.outpatient.service;
|
||||
|
||||
import com.openhis.web.outpatient.mapper.RegistrationMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 门诊挂号业务服务
|
||||
*
|
||||
* 修复 Bug #506:
|
||||
* 之前的退号实现仅调用 {@link RegistrationMapper#updateRegStatus}
|
||||
* 导致费用表、排队表状态未同步,数据库状态与 PRD 定义不符。
|
||||
*
|
||||
* 现在改为使用 {@link RegistrationMapper#cancelRegistration},并在
|
||||
* 方法上加上 {@code @Transactional},确保在同一事务内完成三表更新。
|
||||
*/
|
||||
@Service
|
||||
public class RegistrationService {
|
||||
|
||||
@Autowired
|
||||
private RegistrationMapper registrationMapper;
|
||||
|
||||
/**
|
||||
* 诊前退号(统一更新挂号、费用、排队三张表的状态)。
|
||||
*
|
||||
* @param registrationId 挂号主键 ID
|
||||
* @return true 表示全部三表均成功更新,false 表示更新失败(受影响行数 < 3)
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean preCancel(Long registrationId) {
|
||||
// 调用统一的多表更新 SQL
|
||||
int affected = registrationMapper.cancelRegistration(registrationId);
|
||||
// 期望受影响行数为 3(每张表各 1 行),否则视为失败
|
||||
return affected == 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* 旧接口保留(兼容旧前端),内部已转为调用统一退号方法。
|
||||
*
|
||||
* @param registrationId 挂号主键 ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean cancelLegacy(Long registrationId) {
|
||||
// 直接使用统一方法,保持业务一致性
|
||||
return preCancel(registrationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询挂号详情(供前端展示)。
|
||||
*/
|
||||
public Map<String, Object> getDetail(Long registrationId) {
|
||||
return registrationMapper.selectRegistrationDetail(registrationId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user