调整预约号源数量占用

main
岩仔88 4 days ago
parent a5fa190d4c
commit 140714c251

@ -131,6 +131,10 @@ class WorkMainController extends Controller
$list=$list->where(['reservation_department'=>$department->department_name]); $list=$list->where(['reservation_department'=>$department->department_name]);
}else{ }else{
$list=$list->where(['RISRAcceptDeptCode'=>$department->department_number]); $list=$list->where(['RISRAcceptDeptCode'=>$department->department_number]);
$list = $list->where(function ($q) use($department) {
$q->where(['RISRAcceptDeptCode'=>$department->department_number])
->orWhere('RISRAcceptDeptCode', $department->department_number);
});
} }
if ($searchInfo['dateRange']!=null and count($searchInfo['dateRange']) == 2) { if ($searchInfo['dateRange']!=null and count($searchInfo['dateRange']) == 2) {
$list = $list->where(function ($q) use($searchInfo) { $list = $list->where(function ($q) use($searchInfo) {

@ -219,6 +219,7 @@ WHERE
if ($nowdatetime > $planInfo->date . ' ' . $planInfo->end_reservation_time) return \Yz::echoError1('已经超过预约截止时间'); if ($nowdatetime > $planInfo->date . ' ' . $planInfo->end_reservation_time) return \Yz::echoError1('已经超过预约截止时间');
$zhanweiCount=0;//各个检查项目需要占用检查名额的总和 $zhanweiCount=0;//各个检查项目需要占用检查名额的总和
$itemSeatsQueue = []; // 队列:按顺序存储每个项目需要的座位数 [项目ID => 座位数]
$huchiList=[];//互斥item_id对应关系 $huchiList=[];//互斥item_id对应关系
$oldMainInfos = [];//临时存储原来的主表信息,用于改约 $oldMainInfos = [];//临时存储原来的主表信息,用于改约
//遍历多个s_list表id,前端多选,一次预约多个检查项目 //遍历多个s_list表id,前端多选,一次预约多个检查项目
@ -255,6 +256,7 @@ WHERE
//判断互斥暂时根据reg_num判断身份 //判断互斥暂时根据reg_num判断身份
//查询想要预约的项目 其自身code //查询想要预约的项目 其自身code
$item = DB::table('s_check_item')->where(['item_code' => $mainInfo->entrust_code, 'status' => 1, 'is_del' => 0])->first(); $item = DB::table('s_check_item')->where(['item_code' => $mainInfo->entrust_code, 'status' => 1, 'is_del' => 0])->first();
$itemSeatsQueue[$mainlistid]=$item->use_seats;
if (!$item) return \Yz::echoError1('此检查项目不可用'); if (!$item) return \Yz::echoError1('此检查项目不可用');
$zhanweiCount+=$item->use_seats; $zhanweiCount+=$item->use_seats;
//医嘱开具后,预约时间需在设定的等待期之后,单位分钟 //医嘱开具后,预约时间需在设定的等待期之后,单位分钟
@ -319,7 +321,6 @@ WHERE
} }
DB::beginTransaction(); DB::beginTransaction();
try { try {
@ -343,32 +344,131 @@ WHERE
$planZongLockedCount=0;//合并后的各个渠道占位名额 $planZongLockedCount=0;//合并后的各个渠道占位名额
$plan_qudao_tempCount=[];//拆分各个渠道需要占用的名额 $plan_qudao_tempCount=[];//拆分各个渠道需要占用的名额
$weifenpeiCount=$zhanweiCount;//未分配的名额 $weifenpeiCount=$zhanweiCount;//未分配的名额
foreach ($roster_detail_counts as $roster_detail_count) {
$planZongCount+=$roster_detail_count->count;
$planZongUsedCount+=$roster_detail_count->used_count; $itemChannelMap = [];
$planZongLockedCount+=($roster_detail_count->locked_count ?? 0); // 初始化 map
$keyongCount=$roster_detail_count->count - $roster_detail_count->used_count - ($roster_detail_count->locked_count ?? 0); foreach ($mainlistids as $mid) {
if($weifenpeiCount-$keyongCount>=0){ $itemChannelMap[$mid] = [];
$plan_qudao_tempCount[]=$keyongCount;
$weifenpeiCount-=$keyongCount;
}else{
$plan_qudao_tempCount[]=$weifenpeiCount;
$weifenpeiCount=0;
} }
// 指针:当前正在分配哪个项目
reset($itemSeatsQueue);
$currentItemId = key($itemSeatsQueue);
$currentItemRemaining = $currentItemId ? $itemSeatsQueue[$currentItemId] : 0;
$targetChannelKey = -1; // 记录紧急模式下目标渠道的 Key
foreach ($roster_detail_counts as $key => $roster_detail_count) {
$channelId = $roster_detail_count->id;
// 统计总量
$planZongCount += $roster_detail_count->count;
$planZongUsedCount += $roster_detail_count->used_count;
$planZongLockedCount += ($roster_detail_count->locked_count ?? 0);
// 计算物理可用名额
$physicalAvailable = $roster_detail_count->count - $roster_detail_count->used_count - ($roster_detail_count->locked_count ?? 0);
// 确定该渠道本次能贡献的名额 (不能超过剩余需求,也不能超过物理可用)
$contribCount = 0;
if ($physicalAvailable > 0 && $weifenpeiCount > 0) {
$contribCount = min($physicalAvailable, $weifenpeiCount);
} }
if ($planZongCount < ($planZongUsedCount + $planZongLockedCount + $zhanweiCount)){
if($is_emergency!==1){ $plan_qudao_tempCount[$key] = $contribCount;
return \Yz::echoError1('当前预约时间名额不足'); $weifenpeiCount -= $contribCount; // 更新剩余未分配名额
}else{ //如果是紧急预约,则把未分配的 强制加到当前预约渠道
foreach ($roster_detail_counts as $key=> $roster_detail_count) { // 记录目标渠道 Key (用于紧急模式)
if($roster_detail_count->appointment_type_id==$appointment_type){ if ($roster_detail_count->appointment_type_id == $appointment_type) {
$plan_qudao_tempCount[$key]=$weifenpeiCount; $targetChannelKey = $key;
} }
// --- 执行流式分配 (填充 itemChannelMap) ---
if ($contribCount > 0 && $currentItemId !== null) {
$remainingInChannel = $contribCount;
while ($remainingInChannel > 0 && $currentItemId !== null) {
$take = min($currentItemRemaining, $remainingInChannel);
if (!isset($itemChannelMap[$currentItemId][$channelId])) {
$itemChannelMap[$currentItemId][$channelId] = 0;
} }
$itemChannelMap[$currentItemId][$channelId] += $take;
$currentItemRemaining -= $take;
$remainingInChannel -= $take;
// 项目已满,切换下一个
if ($currentItemRemaining <= 0) {
next($itemSeatsQueue);
$nextId = key($itemSeatsQueue);
if ($nextId !== null) {
$currentItemId = $nextId;
$currentItemRemaining = $itemSeatsQueue[$currentItemId];
} else {
$currentItemId = null;
break;
} }
} }
}
}
}
if ($is_emergency == 1 && $weifenpeiCount > 0) {
// 如果还有剩余名额没分出去,且找到了目标渠道
if ($targetChannelKey != -1) {
$targetKey = $targetChannelKey;
$targetChannelId = $roster_detail_counts[$targetKey]->id;
// 【关键】:累加剩余名额到目标渠道
// 注意:这里是 += 而不是 =,因为该渠道在第一阶段可能已经贡献了一部分
$plan_qudao_tempCount[$targetKey] += $weifenpeiCount;
// 【重要】:必须同步更新 itemChannelMap
// 将剩余的 $weifenpeiCount 继续分配给当前还没分满的项目指针
$remainingEmergency = $weifenpeiCount;
while ($remainingEmergency > 0 && $currentItemId !== null) {
$take = min($currentItemRemaining, $remainingEmergency);
if (!isset($itemChannelMap[$currentItemId][$targetChannelId])) {
$itemChannelMap[$currentItemId][$targetChannelId] = 0;
}
$itemChannelMap[$currentItemId][$targetChannelId] += $take;
$currentItemRemaining -= $take;
$remainingEmergency -= $take;
// 项目已满,切换下一个
if ($currentItemRemaining <= 0) {
next($itemSeatsQueue);
$nextId = key($itemSeatsQueue);
if ($nextId !== null) {
$currentItemId = $nextId;
$currentItemRemaining = $itemSeatsQueue[$currentItemId];
} else {
$currentItemId = null;
break;
}
}
}
// 紧急模式下,剩余未分配名额归零(因为都压到目标渠道了)
$weifenpeiCount = 0;
} else {
// 极端情况:紧急模式但没找到对应 appointment_type 的渠道
return \Yz::echoError1('紧急预约失败:未找到匹配的预约渠道配置');
}
}
// --- 3. 最终校验 ---
if ($weifenpeiCount > 0) {
// 如果不是紧急模式,或者紧急模式但没找到渠道导致还有剩余
if ($is_emergency !== 1) {
return \Yz::echoError1('当前预约时间名额不足');
} else {
// 紧急模式下理论上不应走到这里,除非配置错误
return \Yz::echoError1('紧急预约分配异常:无法容纳所有名额');
}
}
//判断某人这些待预约项目里,是否存在互斥 //判断某人这些待预约项目里,是否存在互斥
@ -397,55 +497,60 @@ WHERE
//更新计划明细表使用数量 //更新计划明细表使用数量
$up_plan_count_all_success =true; $up_plan_count_all_success =true;
$appointment_use_plan_detail_arr=[];//预约各个号源占用号源详情
foreach ($roster_detail_counts as $key => $planCount) { foreach ($roster_detail_counts as $key => $planCount) {
if($plan_qudao_tempCount[$key]==0) continue; $countToDeduct = $plan_qudao_tempCount[$key];
$currentVersion = $planCount->version ?? 0; if ($countToDeduct == 0) continue;
$query = DB::table('s_source_roster_detail_count')
->where(['id' => $planCount->id])
->where('version', $currentVersion);
// 【核心修改】只有非紧急预约时,才检查名额是否充足
if ($is_emergency!==1) {
$query->whereRaw('count >= (used_count + IFNULL(locked_count, 0) + ?)', [$plan_qudao_tempCount[$key]]);
}
// $u = $query->increment('used_count', $plan_qudao_tempCount[$key]);
$u = $query->update([
'used_count' => DB::raw("used_count + {$plan_qudao_tempCount[$key]}"),
'version' => DB::raw("version + 1"),
// 如果需要更新更新时间,可以在这里加 'updated_at' => now()
]);
if(!$u){ $query = DB::table('s_source_roster_detail_count')->where(['id' => $planCount->id]);
$up_plan_count_all_success=false;
// 非紧急模式:严格检查库存充足
if ($is_emergency !== 1) {
// 只有当 剩余物理库存 >= 本次扣减 时才执行
// 公式count - used - locked >= countToDeduct => count >= used + locked + countToDeduct
$query->whereRaw('count >= (used_count + IFNULL(locked_count, 0) + ?)', [$countToDeduct]);
}
// 紧急模式:不加 whereRaw 限制,允许超卖 (used_count 可以大于 count)
$affected = $query->increment('used_count', $countToDeduct);
if ($affected == 0) {
if ($is_emergency !== 1) {
// 非紧急模式扣减失败,说明并发导致库存不足
$up_plan_count_all_success = false;
break;
} else {
// 紧急模式扣减失败,通常是因为记录不存在,需检查
$check = DB::table('s_source_roster_detail_count')->find($planCount->id);
if (!$check) {
$up_plan_count_all_success = false;
break; break;
} }
$appointment_use_plan_detail_arr[]=[ // 如果记录存在但 affected=0 (极少见,可能是行锁超时等),在紧急模式下我们尝试强制更新
"roster_detail_count_id"=>$planCount->id, // 这里为了简单,假设 increment 只要记录存在就会成功返回 1 (即使超卖)
"count"=>$plan_qudao_tempCount[$key], // 如果确实返回 0 且记录存在,可能需要重试或报错,视具体 DB 行为而定
]; // 大多数情况下,不加 whereRaw 的 increment 都会成功
} }
if ($up_plan_count_all_success) {
foreach ($roster_detail_counts as $key => $planCount) {
$cha = DB::table('s_source_roster_detail_count')->where(['id' => $planCount->id])->first();
if ($is_emergency!==1) {
if ($cha->count < $cha->used_count + ($cha->locked_count ?? 0)) {
DB::rollBack();
return \Yz::echoError1('操作失败1');
} }
} }
if (!$up_plan_count_all_success && $is_emergency !== 1) {
throw new \Exception('名额不足,扣减失败');
}
$list_all_success = true;
foreach ($mainlistids as $id) {
$details = [];
if (isset($itemChannelMap[$id])) {
foreach ($itemChannelMap[$id] as $cId => $count) {
$details[] = [
'roster_detail_count_id' => $cId,
'count' => $count
];
} }
}else{
DB::rollBack();
return \Yz::echoError1('操作失败');
} }
//排队号 $xuhao = $this->generateQueueNumber($planInfo->id);
$xvhao=0;
$xvhao= $this->generateQueueNumber($planInfo->id);
//更新主表信息
$u_data = [ $u_data = [
'list_status' => 1, 'list_status' => 1,
'reservation_date' => $planInfo->date, 'reservation_date' => $planInfo->date,
@ -454,44 +559,25 @@ WHERE
'services_group' => $planInfo->device_id, 'services_group' => $planInfo->device_id,
'roster_id' => $planInfo->id, 'roster_id' => $planInfo->id,
'department_id' => $planInfo->department_id, 'department_id' => $planInfo->department_id,
'xuhao' => $xvhao, 'xuhao' => $xuhao,
'appointment_type_id' => $appointment_type, 'appointment_type_id' => $appointment_type,
'qudao_appointment_type_id' => $appointment_type, 'qudao_appointment_type_id' => $appointment_type,
'appointment_use_plan_detail'=>$appointment_use_plan_detail_arr, 'appointment_use_plan_detail' => json_encode($details), // 精确明细
'is_emergency'=>$is_emergency 'is_emergency' => $is_emergency
]; ];
$list_all_success =true;
$offset = 0;
$u_mainList=false;
foreach($plan_qudao_tempCount as $key=>$length){
if($length==0) continue;
$u_data['appointment_type_id']= $appointment_types[$key];
// 获取当前批次的记录 ID
$currentBatchIds = array_slice($mainlistids, $offset, $length);//这块逻辑得优化之前是1个项目占位1个现在有可能1个项目占位多个
foreach ($currentBatchIds as $id) {
// 为当前记录生成唯一的排队号
$xuhao = $this->generateQueueNumber($planInfo->id);
// 更新当前记录的数据(包括 xuhao
$u_data['xuhao'] = $xuhao;
$u_mainList = DB::table('s_list')
->where('id', $id)
->update($u_data);
$u_mainList = DB::table('s_list')->where('id', $id)->update($u_data);
if (!$u_mainList) { if (!$u_mainList) {
$list_all_success = false; $list_all_success = false;
break 2; // 如果更新失败,跳出外层循环 break;
} }
} }
$offset += $length;
}
if (!$list_all_success) { if (!$list_all_success) {
DB::rollBack(); throw new \Exception('更新医嘱状态失败');
return \Yz::echoError1('预约失败');
} }
$note = "预约"; $note = "预约";
foreach ($oldMainInfos as $key => $oldMainInfo) { foreach ($oldMainInfos as $key => $oldMainInfo) {

Loading…
Cancel
Save