Fix Bug #574: fallback修复
This commit is contained in:
100
src/services/appointmentService.js
Normal file
100
src/services/appointmentService.js
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* 预约挂号业务服务
|
||||
* 包含预约、签到、缴费等核心流程的实现
|
||||
*/
|
||||
|
||||
const db = require('../models');
|
||||
const { Transaction } = require('sequelize');
|
||||
|
||||
/**
|
||||
* 处理预约缴费成功后的后置业务
|
||||
*
|
||||
* 业务说明:
|
||||
* 1. 缴费成功后,需要把对应的号源槽(adm_schedule_slot)状态从 “2”(已预约) 改为 “3”(已取号);
|
||||
* 2. 同时需要记录实际取号时间,以便后续统计和对账;
|
||||
* 3. 该操作必须在同一个事务中完成,防止出现“缴费成功但号源状态未更新”的不一致情况。
|
||||
*
|
||||
* 之前的实现只在业务层返回了成功信息,忘记了对 adm_schedule_slot 表进行状态更新,
|
||||
* 导致前端在查询号源时仍然显示为 “已预约”,从而出现 Bug #574。
|
||||
*
|
||||
* 下面的实现补足了状态流转的缺失,并确保在异常情况下事务回滚。
|
||||
*
|
||||
* @param {Object} paymentInfo 缴费返回的业务数据,必须包含:
|
||||
* - scheduleSlotId: 对应的号源槽主键
|
||||
* - paymentId: 支付单号(用于日志记录)
|
||||
* @param {Object} userContext 当前操作用户的上下文(如 userId、operatorName 等)
|
||||
* @returns {Promise<Object>} 返回更新后的号源槽信息
|
||||
*/
|
||||
async function handlePaymentSuccess(paymentInfo, userContext) {
|
||||
const { scheduleSlotId, paymentId } = paymentInfo;
|
||||
if (!scheduleSlotId) {
|
||||
throw new Error('scheduleSlotId is required for payment success handling');
|
||||
}
|
||||
|
||||
// 使用事务确保原子性
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
// 1️⃣ 读取当前号源槽,确保它仍然处于“已预约”(status = 2) 状态
|
||||
const slot = await db.adm_schedule_slot.findOne({
|
||||
where: { id: scheduleSlotId },
|
||||
transaction,
|
||||
lock: transaction.LOCK.UPDATE, // 防止并发修改
|
||||
});
|
||||
|
||||
if (!slot) {
|
||||
throw new Error(`Schedule slot not found, id=${scheduleSlotId}`);
|
||||
}
|
||||
|
||||
// 只在状态为“已预约”时才允许流转到“已取号”
|
||||
if (slot.status !== 2) {
|
||||
// 若已经是“已取号”或其他状态,直接返回当前记录,避免重复更新
|
||||
await transaction.commit();
|
||||
return slot;
|
||||
}
|
||||
|
||||
// 2️⃣ 更新号源槽状态为 “3”(已取号) 并记录取号时间
|
||||
await slot.update(
|
||||
{
|
||||
status: 3, // 已取号
|
||||
taken_at: new Date(), // 实际取号时间
|
||||
payment_id: paymentId, // 关联支付单号,便于追溯
|
||||
updated_by: userContext.userId, // 操作人
|
||||
updated_at: new Date(),
|
||||
},
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
// 3️⃣ 如有需要,可在此处写入审计日志(示例)
|
||||
await db.audit_log.create(
|
||||
{
|
||||
action: 'SLOT_STATUS_CHANGED',
|
||||
description: `Schedule slot ${scheduleSlotId} status changed from 2 to 3 after payment ${paymentId}`,
|
||||
operator_id: userContext.userId,
|
||||
operator_name: userContext.operatorName,
|
||||
target_table: 'adm_schedule_slot',
|
||||
target_id: scheduleSlotId,
|
||||
before_status: 2,
|
||||
after_status: 3,
|
||||
created_at: new Date(),
|
||||
},
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
// 提交事务
|
||||
await transaction.commit();
|
||||
|
||||
// 返回最新的号源槽对象
|
||||
return await db.adm_schedule_slot.findByPk(scheduleSlotId);
|
||||
} catch (err) {
|
||||
// 发生异常时回滚事务,确保数据不出现半更新状态
|
||||
await transaction.rollback();
|
||||
// 重新抛出异常,让上层统一处理
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
handlePaymentSuccess,
|
||||
// 其它预约相关的业务方法...
|
||||
};
|
||||
Reference in New Issue
Block a user