fix(health): 预约 CAS 从精确匹配改为排班时段范围匹配
预约创建时 CAS 原子操作要求排班 start_time 精确等于预约 start_time, 导致排班 08:00-12:00 只能在 08:00 开始预约,无法选择 09:00 等子时段。 修改为范围匹配:排班 start_time <= 预约 start_time 且 排班 end_time >= 预约 end_time,预约可落在排班时段内任意子时段。 增加 rows_affected > 1 保护:若排班数据存在重叠时段则拒绝并告警。
This commit is contained in:
@@ -154,10 +154,9 @@ pub async fn create_appointment(
|
||||
let txn = state.db.begin().await?;
|
||||
|
||||
// 原子 CAS: 排班名额 +1
|
||||
// 注意: CAS 按 (tenant_id, doctor_id, schedule_date, start_time) 匹配。
|
||||
// 唯一索引在 (tenant_id, doctor_id, schedule_date, period_type) 上。
|
||||
// 这要求同一医生同一天同一 start_time 只有一个排班记录,否则 CAS 可能递增错误的行。
|
||||
// 当前业务逻辑保证: 每个医生每天每个时间段最多一条排班。
|
||||
// CAS 按 (tenant_id, doctor_id, schedule_date) + 时间范围匹配:
|
||||
// 排班的 start_time <= 预约 start_time 且排班的 end_time >= 预约 end_time,
|
||||
// 即预约时段落在排班时段内即可。rows_affected 必须恰好为 1(同一医生同一天不应有时段重叠)。
|
||||
let cas_result = doctor_schedule::Entity::update_many()
|
||||
.col_expr(
|
||||
doctor_schedule::Column::CurrentAppointments,
|
||||
@@ -167,7 +166,8 @@ pub async fn create_appointment(
|
||||
.filter(doctor_schedule::Column::TenantId.eq(tenant_id))
|
||||
.filter(doctor_schedule::Column::DoctorId.eq(doctor_id_val))
|
||||
.filter(doctor_schedule::Column::ScheduleDate.eq(req.appointment_date))
|
||||
.filter(doctor_schedule::Column::StartTime.eq(req.start_time))
|
||||
.filter(doctor_schedule::Column::StartTime.lte(req.start_time))
|
||||
.filter(doctor_schedule::Column::EndTime.gte(req.end_time))
|
||||
.filter(
|
||||
Condition::all()
|
||||
.add(doctor_schedule::Column::DeletedAt.is_null())
|
||||
@@ -183,6 +183,16 @@ pub async fn create_appointment(
|
||||
txn.rollback().await?;
|
||||
return Err(HealthError::ScheduleFull);
|
||||
}
|
||||
if cas_result.rows_affected > 1 {
|
||||
txn.rollback().await?;
|
||||
tracing::error!(
|
||||
doctor_id = %doctor_id_val,
|
||||
date = %req.appointment_date,
|
||||
matched = cas_result.rows_affected,
|
||||
"CAS matched multiple schedules — overlapping schedule data"
|
||||
);
|
||||
return Err(HealthError::Validation("排班数据异常:存在重叠时段,请联系管理员".to_string()));
|
||||
}
|
||||
|
||||
let now = Utc::now();
|
||||
let active = appointment::ActiveModel {
|
||||
@@ -554,7 +564,7 @@ pub async fn send_reminders(
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
event_bus: &erp_core::events::EventBus,
|
||||
) -> crate::error::HealthResult<usize> {
|
||||
use chrono::{Local, NaiveDate};
|
||||
use chrono::Local;
|
||||
use serde_json::json;
|
||||
|
||||
let tomorrow = Local::now().date_naive() + chrono::Duration::days(1);
|
||||
|
||||
Reference in New Issue
Block a user