From 7e8c8b407a37b4068ded1a14046b97c17dcd39a8 Mon Sep 17 00:00:00 2001 From: yanzai Date: Sun, 27 Jul 2025 22:51:56 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=84=E7=BA=A6=E6=97=B6=E5=8D=A0=E4=BD=8D?= =?UTF-8?q?=EF=BC=8C=E5=8F=96=E6=B6=88=E6=97=B6=E6=A0=B9=E6=8D=AE=E5=8D=A0?= =?UTF-8?q?=E4=BD=8D=E5=8F=96=E6=B6=88=EF=BC=8C=E5=88=A4=E6=96=AD=E5=85=B3?= =?UTF-8?q?=E8=81=94=E5=8F=B7=E6=BA=90=E6=B1=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/Admin/YeWu/PlanListService.php | 267 +++++++++++++----- 1 file changed, 201 insertions(+), 66 deletions(-) diff --git a/Laravel/app/Services/Admin/YeWu/PlanListService.php b/Laravel/app/Services/Admin/YeWu/PlanListService.php index a7451c5..f80f279 100644 --- a/Laravel/app/Services/Admin/YeWu/PlanListService.php +++ b/Laravel/app/Services/Admin/YeWu/PlanListService.php @@ -8,6 +8,7 @@ use DateInterval; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use DateTime; +use Illuminate\Support\Facades\Log; use Tools; class PlanListService @@ -174,36 +175,7 @@ WHERE if (!$planInfo) return \Yz::echoError1('当前时段不可用'); if ($nowdatetime > $planInfo->date . ' ' . $planInfo->end_reservation_time) return \Yz::echoError1('已经超过预约截止时间'); - //查询号源渠道是否有合并 - $appointment_types=[$appointment_type]; - $appointment_type_link = DB::table('s_appointment_type_ratio')->where(['department_id'=>$planInfo->department_id,'appointment_type_id' => $appointment_type])->first(); - if (!empty($appointment_type_link->link)) { - $appointment_types=json_decode($appointment_type_link->link, true); - $appointment_types=array_merge($appointment_types, [$appointment_type]); - } - - $roster_detail_counts = DB::table('s_source_roster_detail_count') - ->where(['roster_detail_id' => $planid]) - ->whereIn('appointment_type_id', $appointment_types) - ->get(); - $planZongCount=0;//合并后的各个渠道总名额 - $planZongUsedCount=0;//合并后的各个渠道已预约的名额 - $plan_qudao_tempCount=[];//拆分各个渠道需要占用的名额 - $weifenpeiCount=count($mainlistids);//未分配的名额 - foreach ($roster_detail_counts as $roster_detail_count) { - $planZongCount+=$roster_detail_count->count; - $planZongUsedCount+=$roster_detail_count->used_count; - $keyongCount=$roster_detail_count->count-$roster_detail_count->used_count; - if($weifenpeiCount-$keyongCount>=0){ - $plan_qudao_tempCount[]=$keyongCount; - $weifenpeiCount-=$keyongCount; - }else{ - $plan_qudao_tempCount[]=$weifenpeiCount; - $weifenpeiCount=0; - } - - } - if ($planZongCount < ($planZongUsedCount + count($mainlistids))) return \Yz::echoError1('当前预约时间名额不足'); + $zhanweiCount=0;//各个检查项目需要占用检查名额的总和 $huchiList=[];//互斥item_id对应关系 $oldMainInfos = [];//临时存储原来的主表信息,用于改约 //遍历多个s_list表id,前端多选,一次预约多个检查项目 @@ -239,9 +211,9 @@ WHERE //判断互斥(暂时根据reg_num判断身份) //查询想要预约的项目 其自身code - $item = DB::table('s_check_item')->where(['item_name' => $mainInfo->entrust, 'status' => 1, 'is_del' => 0])->first(); + $item = DB::table('s_check_item')->where(['item_code' => $mainInfo->entrust_code, 'status' => 1, 'is_del' => 0])->first(); if (!$item) return \Yz::echoError1('此检查项目不可用'); - + $zhanweiCount+=$item->use_seats; //医嘱开具后,预约时间需在设定的等待期之后,单位分钟 $entrust_time = $mainInfo->entrust_date . ' ' . $mainInfo->entrust_time; //医嘱时间 $date = new DateTime($entrust_time); @@ -303,6 +275,41 @@ WHERE } } + + //查询号源渠道是否有合并 + $appointment_types=[$appointment_type]; + $appointment_type_link = DB::table('s_appointment_type_ratio')->where(['department_id'=>$planInfo->department_id,'appointment_type_id' => $appointment_type])->first(); + if (!empty($appointment_type_link->link)) { + $appointment_types=json_decode($appointment_type_link->link, true); + $appointment_types=array_merge($appointment_types, [$appointment_type]); + sort($appointment_types); + } + + $roster_detail_counts = DB::table('s_source_roster_detail_count') + ->where(['roster_detail_id' => $planid]) + ->whereIn('appointment_type_id', $appointment_types) + ->orderBy('appointment_type_id','asc') + ->get(); + $planZongCount=0;//合并后的各个渠道总名额 + $planZongUsedCount=0;//合并后的各个渠道已预约的名额 + $plan_qudao_tempCount=[];//拆分各个渠道需要占用的名额 + $weifenpeiCount=$zhanweiCount;//未分配的名额 + foreach ($roster_detail_counts as $roster_detail_count) { + $planZongCount+=$roster_detail_count->count; + $planZongUsedCount+=$roster_detail_count->used_count; + $keyongCount=$roster_detail_count->count-$roster_detail_count->used_count; + if($weifenpeiCount-$keyongCount>=0){ + $plan_qudao_tempCount[]=$keyongCount; + $weifenpeiCount-=$keyongCount; + }else{ + $plan_qudao_tempCount[]=$weifenpeiCount; + $weifenpeiCount=0; + } + + } + if ($planZongCount < ($planZongUsedCount +$zhanweiCount)) return \Yz::echoError1('当前预约时间名额不足'); + + //判断某人这些待预约项目里,是否存在互斥 $userGroup=[]; foreach ($oldMainInfos as $key1 => $value1) { @@ -372,16 +379,17 @@ WHERE ]; $list_all_success =true; $offset = 0; + 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); + $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') @@ -503,48 +511,175 @@ WHERE date_default_timezone_set('PRC'); $nowdatetime = date("Y-m-d H:i:s"); - $mainInfo = DB::table('s_list')->where(['id' => $MainListId, 'reg_num' => $reg_num])->first(); - if (!$mainInfo) return \Yz::echoError1('医嘱不存在'); - //判断状态 - if ($mainInfo->list_status <> 1) return \Yz::echoError1('该记录无法取消,当前状态:' . $mainInfo->list_status); + // 开启数据库事务 DB::beginTransaction(); try { + // 1. 查询并锁定主预约记录(防并发) + $mainInfo = DB::table('s_list') + ->where(['id' => $MainListId, 'reg_num' => $reg_num]) + ->lockForUpdate() + ->first(); + + if (!$mainInfo) { + return \Yz::echoError1('医嘱不存在'); + } + + // 2. 检查当前状态是否允许取消(仅状态为1可取消) + if ($mainInfo->list_status != 1) { + return \Yz::echoError1('该记录无法取消,当前状态:' . $mainInfo->list_status); + } + + // 3. 获取检查项目信息(判断是否可用) + $item = DB::table('s_check_item') + ->where(['item_code' => $mainInfo->entrust_code, 'status' => 1, 'is_del' => 0]) + ->first(); + + if (!$item) { + return \Yz::echoError1('此检查项目不可用'); + } + + $zhanweiCount = (int)$item->use_seats; // 该预约占用的号源数量 + $rosterId = $mainInfo->roster_id; // 排班详情ID + + if ($zhanweiCount <= 0) { + $zhanweiCount = 1; // 防止配置错误,默认至少占1个号源 + } + + // 4. 查询排班详情并加锁(确保号源操作安全) + $planInfo = DB::table('s_source_roster_detail') + ->where(['id' => $rosterId]) + ->lockForUpdate() + ->first(); + + if (!$planInfo) { + return \Yz::echoError1('号源排班信息不存在'); + } + + // 5. 获取当前预约类型的合并渠道配置 + $appointment_type_link = DB::table('s_appointment_type_ratio') + ->where([ + 'department_id' => $planInfo->department_id, + 'appointment_type_id' => $mainInfo->appointment_type_id + ]) + ->first(); + + // 构建可用的预约类型列表(包含自身 + 合并渠道) + $appointment_types = [$mainInfo->appointment_type_id]; + if (!empty($appointment_type_link->link)) { + $linkTypes = json_decode($appointment_type_link->link, true); + if (is_array($linkTypes)) { + $appointment_types = array_merge($appointment_types, $linkTypes); + } + $appointment_types = array_unique($appointment_types); // 去重 + sort($appointment_types); + } + + // 6. 获取当前渠道已使用的号源数量 + $currentCountRecord = DB::table('s_source_roster_detail_count') + ->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $mainInfo->appointment_type_id]) + ->lockForUpdate() // 加锁 + ->first(); + + if (!$currentCountRecord) { + return \Yz::echoError1('当前渠道号源计数记录不存在'); + } + + $usedCurrent = (int)$currentCountRecord->used_count; + + // 7. 开始释放号源:优先从当前渠道释放,不足则从合并渠道扣除 + $remainingRelease = $zhanweiCount; // 还需释放的数量 + + // 先从当前预约类型渠道释放 + if ($usedCurrent >= $remainingRelease) { + // 当前渠道足够释放 + DB::table('s_source_roster_detail_count') + ->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $mainInfo->appointment_type_id]) + ->decrement('used_count', $remainingRelease); + $remainingRelease = 0; + } else { + // 当前渠道不够,全部释放当前渠道 + $releasedFromCurrent = $usedCurrent; + DB::table('s_source_roster_detail_count') + ->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $mainInfo->appointment_type_id]) + ->update(['used_count' => 0]); + $remainingRelease -= $releasedFromCurrent; + } + + // 如果还有剩余要释放的号源,从合并渠道中扣除 + if ($remainingRelease > 0) { + foreach ($appointment_types as $typeId) { + // 跳过当前已处理的渠道 + if ($typeId == $mainInfo->appointment_type_id) { + continue; + } + + $mergeCountRecord = DB::table('s_source_roster_detail_count') + ->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $typeId]) + ->lockForUpdate() + ->first(); + + if (!$mergeCountRecord || $mergeCountRecord->used_count <= 0) { + continue; + } + + $deduct = min($mergeCountRecord->used_count, $remainingRelease); + DB::table('s_source_roster_detail_count') + ->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $typeId]) + ->decrement('used_count', $deduct); + + $remainingRelease -= $deduct; + + if ($remainingRelease <= 0) { + break; + } + } + + // 如果最终仍无法完全释放所需号源数量,说明数据异常(理论上不应发生) + if ($remainingRelease > 0) { + DB::rollBack(); + return \Yz::echoError1('号源释放失败:合并渠道号源不足,无法完成释放'); + } + } + + // 8. 更新主表状态为“已取消”(0) $u_data = [ - 'list_status' => 0, - 'reservation_date' => null, - 'reservation_time' => null, - 'reservation_sources' => null, - 'services_group' => null, - 'roster_id' => null, - 'xuhao' => null, - 'department_id' => null, - 'appointment_type_id' => null, - 'canel_time' => $nowdatetime, + 'list_status' => 0, + 'reservation_date' => null, + 'reservation_time' => null, + 'reservation_sources' => null, + 'services_group' => null, + 'roster_id' => null, + 'xuhao' => null, + 'department_id' => null, + 'appointment_type_id' => null, + 'canel_time' => $nowdatetime, ]; + $u_mainList = DB::table('s_list')->where(['id' => $MainListId])->update($u_data); + + // 9. 写入操作日志 $i_log = DB::table('s_list_log')->insert([ - 'list_id' => $mainInfo->id, - 'reg_num' => $mainInfo->reg_num, - 'old_status' => $mainInfo->list_status, - 'new_status' => 0, - 'create_user' => null, - 'note' => '取消预约', - 'data' => json_encode($u_data) + 'list_id' => $mainInfo->id, + 'reg_num' => $mainInfo->reg_num, + 'old_status' => $mainInfo->list_status, + 'new_status' => 0, + 'create_user' => null, + 'note' => '取消预约', + 'data' => json_encode($u_data, JSON_UNESCAPED_UNICODE), + 'created_at' => $nowdatetime, ]); - $u_count = DB::table('s_source_roster_detail_count')->where(['roster_detail_id' => $mainInfo->roster_id, 'appointment_type_id' => $mainInfo->appointment_type_id])->decrement('used_count'); - if ($u_mainList && $u_count) { - DB::commit(); - return \Yz::Return(true, '取消成功', []); - } else { - DB::rollBack(); - return \Yz::echoError1('取消失败'); - } + + // 10. 提交事务 + DB::commit(); + + return \Yz::Return(true, '取消成功', []); + } catch (\Exception $e) { DB::rollBack(); - return \Yz::echoError1('取消异常'); + // 记录错误日志(建议使用 Laravel 日志系统) + Log::error("取消预约失败 - ID: {$MainListId}, reg_num: {$reg_num}, 错误: " . $e->getMessage()); + return \Yz::echoError1('取消失败:系统异常,请稍后重试'); } - - } //短信提醒 public function SendMsg($infos,$dotype=1)