diff --git a/Laravel/app/Http/Controllers/API/Admin/YeWu/PlanListController.php b/Laravel/app/Http/Controllers/API/Admin/YeWu/PlanListController.php index 20feef3..b39a83a 100644 --- a/Laravel/app/Http/Controllers/API/Admin/YeWu/PlanListController.php +++ b/Laravel/app/Http/Controllers/API/Admin/YeWu/PlanListController.php @@ -392,6 +392,7 @@ class PlanListController extends Controller date_default_timezone_set('PRC'); $nowdatetime = date("Y-m-d H:i:s"); $do_userid=request('do_user'); + $is_emergency = request('is_emergency');//是否加急 $planid = request('planid'); $appointment_type = request('appointment_type');//渠道id $mainlistid = request('mainlistid');//主表id @@ -399,7 +400,7 @@ class PlanListController extends Controller if (!isset($do_type)) return \Yz::echoError1('参数:操作类型 不能为空'); $service = new PlanListService(); - return $service->YuYue($planid, $appointment_type, $mainlistid, $do_type,$do_userid); + return $service->YuYue($planid, $appointment_type, $mainlistid, $do_type,$is_emergency,$do_userid); } //自动预约 public function AutoYuYue() diff --git a/Laravel/app/Services/Admin/GroupService.php b/Laravel/app/Services/Admin/GroupService.php index 440e5d1..2917aa0 100644 --- a/Laravel/app/Services/Admin/GroupService.php +++ b/Laravel/app/Services/Admin/GroupService.php @@ -11,6 +11,7 @@ class GroupService $sql=" where status =1"; } $query=DB::select("select * from `group` ".$sql); + $result['special_privileges_list']=config('app.globals.特殊权限'); if(count($query)){ $result['list']=$query; $result['status']='ok'; diff --git a/Laravel/app/Services/Admin/UserService.php b/Laravel/app/Services/Admin/UserService.php index 8a2bd88..9f99a1e 100644 --- a/Laravel/app/Services/Admin/UserService.php +++ b/Laravel/app/Services/Admin/UserService.php @@ -38,6 +38,7 @@ class UserService 'cn_name' => $arr['info']['cname'], 'username' => $arr['info']['uname'], 'status' => $arr['info']['status'], + 'special_privileges' => json_encode($arr['info']['special_privileges'], JSON_UNESCAPED_UNICODE), 'group_locked' => 1, ]; @@ -91,7 +92,7 @@ class UserService return $result; } public function GetDetail($arr){ - $c=DB::table('users')->select(['id','cn_name','username','department_id','department_ids','status','group','img',])->where(['id'=>$arr['id']])->whereIn('status',[0,1])->get(); + $c=DB::table('users')->select(['id','cn_name','username','department_id','department_ids','status','group','img','special_privileges'])->where(['id'=>$arr['id']])->whereIn('status',[0,1])->get(); if(count($c)){ $ids=[]; if(!empty($c[0]->department_ids)){ @@ -114,6 +115,12 @@ class UserService } $c[0]->department_info=$departments_arr; + $privileges = json_decode($c[0]->special_privileges, true); + if ($privileges === null && json_last_error() !== JSON_ERROR_NONE) { + $c[0]->special_privileges = []; + } else { + $c[0]->special_privileges = $privileges; + } $result['info']=$c; $result['status']='ok'; $result['msg']='成功'; diff --git a/Laravel/app/Services/Admin/YeWu/PlanListService.php b/Laravel/app/Services/Admin/YeWu/PlanListService.php index ed9f8ea..1b8f4ec 100644 --- a/Laravel/app/Services/Admin/YeWu/PlanListService.php +++ b/Laravel/app/Services/Admin/YeWu/PlanListService.php @@ -17,6 +17,7 @@ class PlanListService { date_default_timezone_set('PRC'); + $allEmergency = true; $allDevice = [];//所有医嘱检查项目绑定的设备id $commPatientType = [];//所有医嘱共同的病人类型 $zhanweiCount=0;//勾选的检查项目共计占多少名额 @@ -24,6 +25,11 @@ class PlanListService // $info = DB::table('s_list')->where(['reg_num' => $regnum, 'entrust_id' => $entrustid, 'episodeid' => $episodeid, 'is_nullify' => 0])->first(); $info = DB::table('s_list')->where(['entrust_id' => $entrustid, 'is_nullify' => 0])->first(); if (!$info) return \Yz::echoError1('没有找到对应医嘱信息:'.$entrustid); + + if (!isset($info->his_is_emergency) || $info->his_is_emergency != 1) { + $allEmergency = false; + } + $itemInfo = DB::table('s_check_item')->where(['item_name' => $info->entrust, 'status' => 1, "is_del" => 0])->get(); if (count($itemInfo) == 0) return \Yz::echoError1('没有找到可用的检查项目信息'); $itemInfo = $itemInfo[0]; @@ -179,22 +185,34 @@ WHERE 'mainInfo' => $info, 'plan_list' => $pp, 'zhanWeiCount'=>$zhanweiCount, - 'earliestPlan'=>$earliestPlan + 'earliestPlan'=>$earliestPlan, + 'is_emergency' => $allEmergency, ]); } //开始预约占用名额 - public function YuYue($planid, $appointment_type, $mainlistids, $do_type,$do_user=null) + public function YuYue($planid, $appointment_type, $mainlistids, $do_type,$is_emergency=0,$do_user=null) { date_default_timezone_set('PRC'); $nowdatetime = date("Y-m-d H:i:s"); - // $planid = request('planid'); - // $appointment_type = request('appointment_type');//渠道id - // $mainlistid = request('mainlistid');//主表id - // $do_type = request('dotype');//操作类型,1预约,2改约 - // if (!isset($do_type)) return \Yz::echoError1('参数:操作类型 不能为空'); - + if ($is_emergency == 1 && !empty($do_user)) {//如果是加急,校验用户是否有加急权限 + $do_user_info=DB::table('users')->where('id',$do_user)->first(); + if(!$do_user_info){ + return \Yz::echoError1('无效的用户ID,无法进行加急预约'); + }else{ + $special_privileges=[]; + $decoded=json_decode($do_user_info->special_privileges,true); + if(is_array($decoded)){ + $special_privileges=$decoded; + } + if(!in_array('emergency', $special_privileges)){ + return \Yz::echoError1('无权进行加急预约'); + } + } + }else{ + $is_emergency=0; + } $planInfo = DB::table('s_source_roster_detail')->where(['id' => $planid, 'status' => 1, 'is_del' => 0])->first(); if (!$planInfo) return \Yz::echoError1('当前时段不可用'); @@ -334,7 +352,18 @@ WHERE } } - if ($planZongCount < ($planZongUsedCount + $planZongLockedCount + $zhanweiCount)) return \Yz::echoError1('当前预约时间名额不足'); + if ($planZongCount < ($planZongUsedCount + $planZongLockedCount + $zhanweiCount)){ + if($is_emergency!==1){ + return \Yz::echoError1('当前预约时间名额不足'); + }else{ //如果是紧急预约,则把未分配的 强制加到当前预约渠道 + foreach ($roster_detail_counts as $key=> $roster_detail_count) { + if($roster_detail_count->appointment_type_id==$appointment_type){ + $plan_qudao_tempCount[$key]=$weifenpeiCount; + } + } + } + } + //判断某人这些待预约项目里,是否存在互斥 @@ -363,27 +392,37 @@ WHERE try { //更新计划明细表使用数量 $up_plan_count_all_success =true; + $appointment_use_plan_detail_arr=[];//预约各个号源占用号源详情 foreach ($roster_detail_counts as $key => $planCount) { if($plan_qudao_tempCount[$key]==0) continue; - $u = DB::table('s_source_roster_detail_count') - ->where(['id' => $planCount->id]) - ->whereRaw('count >= (used_count + IFNULL(locked_count, 0) + ?)', [$plan_qudao_tempCount[$key]]) - ->increment('used_count',$plan_qudao_tempCount[$key]); + + $query = DB::table('s_source_roster_detail_count')->where(['id' => $planCount->id]); + // 【核心修改】只有非紧急预约时,才检查名额是否充足 + 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]); if(!$u){ $up_plan_count_all_success=false; break; } + $appointment_use_plan_detail_arr[]=[ + "roster_detail_count_id"=>$planCount->id, + "count"=>$plan_qudao_tempCount[$key], + ]; } 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 ($cha->count < $cha->used_count + ($cha->locked_count ?? 0)) { - DB::rollBack(); - return \Yz::echoError1('操作失败1'); + if ($is_emergency!==1) { + if ($cha->count < $cha->used_count + ($cha->locked_count ?? 0)) { + DB::rollBack(); + return \Yz::echoError1('操作失败1'); + } } + } }else{ DB::rollBack(); @@ -405,10 +444,11 @@ WHERE 'xuhao' => $xvhao, 'appointment_type_id' => $appointment_type, 'qudao_appointment_type_id' => $appointment_type, + 'appointment_use_plan_detail'=>$appointment_use_plan_detail_arr ]; $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]; @@ -445,7 +485,52 @@ WHERE // if(count($mainlistids)>1) return \Yz::echoError1('请选择1条医嘱改约,暂不支持批量'); $note = "改约"; //如果是改约,则恢复原来的数量 - $u_old = DB::table('s_source_roster_detail_count')->where(['roster_detail_id' => $oldMainInfo->roster_id, 'appointment_type_id' => $oldMainInfo->appointment_type_id, ['used_count', '>', 0]])->decrement('used_count'); + $useDetailJson = $oldMainInfo->appointment_use_plan_detail; + if (empty($useDetailJson)) { + return \Yz::echoError1('取消失败:缺少占位明细数据,无法精确回退,请联系管理员'); + } + $useDetails = json_decode($useDetailJson, true); + if (!is_array($useDetails)) { + return \Yz::echoError1('取消失败:占位明细数据格式错误'); + } + foreach ($useDetails as $detail) { + $rosterDetailCountId = $detail['roster_detail_count_id'] ?? null; + $countToRelease = (int)($detail['count'] ?? 0); + + if (!$rosterDetailCountId || $countToRelease <= 0) { + continue; + } + $affected = DB::table('s_source_roster_detail_count') + ->where(['id' => $rosterDetailCountId]) + ->lockForUpdate() + ->where('used_count', '>=', $countToRelease) // 防御性编程:确保不会减成负数 + ->decrement('used_count', $countToRelease); + if ($affected == 0) { + // 如果 affected 为 0,可能是记录不存在,或者 used_count 不足 + $checkRecord = DB::table('s_source_roster_detail_count')->where(['id' => $rosterDetailCountId])->first(); + if (!$checkRecord) { + Log::error("改约-号源记录缺失", [ + 'MainListId' => $oldMainInfo->id, + 'count_id' => $rosterDetailCountId + ]); + } + if ($checkRecord->used_count < $countToRelease) { + // 【关键修改】:余额不足,但不阻断取消 + $actualRelease = $checkRecord->used_count; // 最多只能退这么多 + + if ($actualRelease > 0) { + // 把剩余的余额全部退完,强制设为 0 (或者 decrement actualRelease) + DB::table('s_source_roster_detail_count') + ->where(['id' => $rosterDetailCountId]) + ->update(['used_count' => 0]); // 直接置零,防止浮点数或并发问题 + } + + // 记录严重报警日志 + $msg = "数据不一致警告:订单 {$oldMainInfo->id} 试图退还 {$countToRelease} 个名额,但渠道 {$rosterDetailCountId} 仅剩 {$checkRecord->used_count} 个。"; + Log::error($msg); + } + } + } } @@ -557,117 +642,51 @@ WHERE 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个号源 + $useDetailJson = $mainInfo->appointment_use_plan_detail; + if (empty($useDetailJson)) { + return \Yz::echoError1('取消失败:缺少占位明细数据,无法精确回退,请联系管理员'); } - - // 4. 查询排班详情并加锁(确保号源操作安全) - $planInfo = DB::table('s_source_roster_detail') - ->where(['id' => $rosterId]) - ->lockForUpdate() - ->first(); - - if (!$planInfo) { - return \Yz::echoError1('号源排班信息不存在'); + $useDetails = json_decode($useDetailJson, true); + if (!is_array($useDetails)) { + return \Yz::echoError1('取消失败:占位明细数据格式错误'); } + foreach ($useDetails as $detail) { + $rosterDetailCountId = $detail['roster_detail_count_id'] ?? null; + $countToRelease = (int)($detail['count'] ?? 0); - // 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); + if (!$rosterDetailCountId || $countToRelease <= 0) { + continue; } - $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; + $affected = DB::table('s_source_roster_detail_count') + ->where(['id' => $rosterDetailCountId]) + ->lockForUpdate() + ->where('used_count', '>=', $countToRelease) // 防御性编程:确保不会减成负数 + ->decrement('used_count', $countToRelease); + if ($affected == 0) { + // 如果 affected 为 0,可能是记录不存在,或者 used_count 不足 + $checkRecord = DB::table('s_source_roster_detail_count')->where(['id' => $rosterDetailCountId])->first(); + if (!$checkRecord) { + Log::error("取消预约-记录缺失", [ + 'MainListId' => $MainListId, + 'count_id' => $rosterDetailCountId + ]); } + if ($checkRecord->used_count < $countToRelease) { + // 【关键修改】:余额不足,但不阻断取消 + $actualRelease = $checkRecord->used_count; // 最多只能退这么多 + + if ($actualRelease > 0) { + // 把剩余的余额全部退完,强制设为 0 (或者 decrement actualRelease) + DB::table('s_source_roster_detail_count') + ->where(['id' => $rosterDetailCountId]) + ->update(['used_count' => 0]); // 直接置零,防止浮点数或并发问题 + } - $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; + // 记录严重报警日志 + $msg = "数据不一致警告:订单 {$MainListId} 试图退还 {$countToRelease} 个名额,但渠道 {$rosterDetailCountId} 仅剩 {$checkRecord->used_count} 个。"; + Log::error($msg); } } - - // 如果最终仍无法完全释放所需号源数量,说明数据异常(理论上不应发生) - if ($remainingRelease > 0) { - DB::rollBack(); - return \Yz::echoError1('号源释放失败:合并渠道号源不足,无法完成释放'); - } } // 8. 更新主表状态为“已取消”(0) diff --git a/Laravel/config/app.php b/Laravel/config/app.php index dcf2386..8e5e1ab 100644 --- a/Laravel/config/app.php +++ b/Laravel/config/app.php @@ -16,6 +16,12 @@ return [ '2'=>'急诊', '3'=>'体检', ], + '特殊权限'=>[ + [ + 'name'=>'加急预约', + 'key'=>'emergency', + ] + ] ], /* diff --git a/YiJi-admin/src/components/Yewu/YuYue202506.vue b/YiJi-admin/src/components/Yewu/YuYue202506.vue index 6aef90a..9c5f472 100644 --- a/YiJi-admin/src/components/Yewu/YuYue202506.vue +++ b/YiJi-admin/src/components/Yewu/YuYue202506.vue @@ -67,6 +67,7 @@ :class="{'ZhenShiButton zhenshiButton_active': activeZhenShi === item,'ZhenShiButton': activeZhenShi !== item}" @click="zhenshiClick(item)">{{item}}
+