appointmentService = $appointmentService; $this->planService = $planService; $this->blockService = $blockService; } /** * 创建预约 * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function create(Request $request) { $user = $request->attributes->get('payload'); if (!$user) { return Rs::error('用户信息获取失败'); } // 获取用户ID $userId = $user['user_id'] ?? null; if(empty($userId)){ return Rs::error('用户信息获取失败'); } $applicationIds = $request->input('application_ids'); $channelId = $request->input('channel_id'); $planId = $request->input('plan_id'); // 验证参数 if (empty($applicationIds) || !is_array($applicationIds)) { return Rs::error('请提供医嘱ID数组'); } if (empty($channelId)) { return Rs::error('请提供渠道ID'); } if (empty($planId)) { return Rs::error('请提供号源ID'); } $result = $this->appointmentService->createAppointment( $userId, $applicationIds, (int)$channelId, (int)$planId ); if ($result['success']) { return Rs::success($result['data'] ?? null, $result['message']); } else { $data = $result['data'] ?? []; $data['conflicts'] = $result['conflicts'] ?? []; return Rs::error($result['message'], 400, $data); } } /** * 取消预约 * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function cancel(Request $request) { $user = $request->attributes->get('payload'); if (!$user) { return Rs::error('用户信息获取失败'); } // 获取用户ID $userId = $user['user_id'] ?? null; if(empty($userId)){ return Rs::error('用户信息获取失败'); } $appointmentId = $request->input('appointment_id'); $cancelReason = $request->input('cancel_reason'); // 验证参数 if (empty($appointmentId)) { return Rs::error('请提供预约ID'); } $result = $this->appointmentService->cancelAppointment($userId,(int)$appointmentId, $cancelReason); if ($result['success']) { return Rs::success(null, $result['message']); } else { return Rs::error($result['message']); } } /** * 改约 * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function reschedule(Request $request) { $user = $request->attributes->get('payload'); if (!$user) { return Rs::error('用户信息获取失败'); } // 获取用户ID $userId = $user['user_id'] ?? null; if(empty($userId)){ return Rs::error('用户信息获取失败'); } $appointmentId = $request->input('appointment_id'); $cancelReason = $request->input('cancel_reason'); $newPlanId = $request->input('plan_id'); // 验证参数 if (empty($appointmentId)) { return Rs::error('请提供原预约ID'); } if (empty($newPlanId)) { return Rs::error('请提供新号源ID'); } if (empty($cancelReason)) { return Rs::error('请提供改约原因'); } $result = $this->appointmentService->rescheduleAppointment( $userId, (int)$appointmentId, $cancelReason, (int)$newPlanId ); if ($result['success']) { return Rs::success($result['data'] ?? null, $result['message']); } else { $data = $result['data'] ?? []; $data['conflicts'] = $result['conflicts'] ?? []; return Rs::error($result['message'], 400, $data); } } /** * 自动预约接口 * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function autoCreate(Request $request) { $user = $request->attributes->get('payload'); if (!$user) { return Rs::error('用户信息获取失败'); } $userId = $user['user_id'] ?? null; if (empty($userId)) { return Rs::error('用户信息获取失败'); } // 验证参数 $data = $request->validate([ 'channel_id' => 'required|integer|min:1', 'patient_info' => 'required|array', 'patient_info.*.patient_id' => 'required|integer', 'patient_info.*.his_order_ids' => 'required|array', 'patient_info.*.his_order_ids.*' => 'string', 'date_range' => 'nullable|array', 'date_range.*' => 'date' ]); $channelId = $data['channel_id']; $patientInfo = $data['patient_info']; $dateRange = $data['date_range'] ?? null; // 确定日期范围 if (empty($dateRange)) { $startDate = date('Y-m-d'); $days = 15; } else { $startDate = $dateRange[0]; $endDate = $dateRange[1]; $days = (strtotime($endDate) - strtotime($startDate)) / 86400 + 1; if ($days < 1) { return Rs::error('日期范围无效'); } } // 初始化结果 $results = []; $totalApplications = 0; $totalSuccessful = 0; $totalFailed = 0; // 按患者遍历 foreach ($patientInfo as $patientData) { $patientId = $patientData['patient_id']; $hisOrderIds = $patientData['his_order_ids']; // 查询该患者的医嘱(患者信息包含在 exam_application 表中) $applications = DB::table('exam_application') ->where('patient_id', $patientId) ->whereIn('his_order_id', $hisOrderIds) ->where('status', 0) // 申请中状态 ->get(); // 黑名单检测 $patientIdCardNo = $applications->first()->patient_id_card_no ?? null; if ($patientIdCardNo) { $blockCheck = $this->blockService->checkAppointmentPermission($channelId, $patientIdCardNo); if (!$blockCheck['status']) { // 黑名单检测失败,记录失败 $patientResult = [ 'patient_id' => $patientId, 'patient_name' => $applications->first()->patient_name ?? '未知', 'appointments' => [], 'failed_applications' => [], 'summary' => [ 'total_count' => count($hisOrderIds), 'success_count' => 0, 'failed_count' => count($hisOrderIds) ] ]; foreach ($hisOrderIds as $hisOrderId) { $patientResult['failed_applications'][] = [ 'his_order_id' => $hisOrderId, 'error_reason' => $blockCheck['msg'] ]; } $totalFailed += count($hisOrderIds); $results[] = $patientResult; continue; } } // 从医嘱中获取患者姓名 $patientName = $applications->first()->patient_name ?? '未知'; $examApplicationIds = $applications->pluck('id')->toArray(); $totalApplications += count($hisOrderIds); // 初始化该患者的预约结果 $patientResult = [ 'patient_id' => $patientId, 'patient_name' => $patientName, 'appointments' => [], 'failed_applications' => [], 'summary' => [ 'total_count' => count($hisOrderIds), 'success_count' => 0, 'failed_count' => 0 ] ]; // 检查医嘱是否存在 if (empty($examApplicationIds)) { foreach ($hisOrderIds as $hisOrderId) { $patientResult['failed_applications'][] = [ 'his_order_id' => $hisOrderId, 'error_reason' => '医嘱不存在或状态不符合预约条件' ]; } $patientResult['summary']['failed_count'] = count($hisOrderIds); $totalFailed += count($hisOrderIds); $results[] = $patientResult; continue; } // 每个患者的预约放在一个事务里 try { DB::transaction(function () use ( $userId, $channelId, $startDate, $days, $applications, $examApplicationIds, $hisOrderIds, &$patientResult, &$totalSuccessful, &$totalFailed ) { // 尝试为所有医嘱查找共同资源的最早号源 $commonSlot = $this->planService->findEarliestAvailableSlot( $examApplicationIds, $channelId, $startDate, $days ); if ($commonSlot) { // 有共同资源且配额充足,统一预约 $result = $this->appointmentService->createAppointment( $userId, $examApplicationIds, $channelId, $commonSlot['plan_id'] ); if ($result['success']) { // 获取预约记录 $appointmentIds = $result['data']['appointment_ids'] ?? []; $appointments = DB::table('exam_appointment') ->whereIn('exam_appointment.id', $appointmentIds) ->leftJoin('exam_application', 'exam_appointment.exam_application_id', '=', 'exam_application.id') ->leftJoin('department_resource', 'exam_appointment.department_resource_id', '=', 'department_resource.id') ->select( 'exam_appointment.id as appointment_id', 'exam_application.his_order_id', 'exam_application.examination_item_name', 'department_resource.name as resource_name', 'exam_appointment.appointment_time' ) ->get(); foreach ($appointments as $appointment) { $patientResult['appointments'][] = [ 'appointment_id' => $appointment->appointment_id, 'his_order_id' => $appointment->his_order_id, 'examination_item_name' => $appointment->examination_item_name, 'resource_name' => $appointment->resource_name, 'appointment_time' => $appointment->appointment_time, 'time_slot_id' => $commonSlot['plan_id'] ]; } $patientResult['summary']['success_count'] = count($appointmentIds); $totalSuccessful += count($appointmentIds); } else { // 预约失败,所有医嘱都记录为失败 foreach ($applications as $app) { $patientResult['failed_applications'][] = [ 'his_order_id' => $app->his_order_id, 'examination_item_name' => $app->examination_item_name, 'error_reason' => $result['message'] ]; } $patientResult['summary']['failed_count'] = count($applications); $totalFailed += count($applications); } } else { // 没有共同资源或配额不足,分别预约每个医嘱 foreach ($applications as $app) { $singleSlot = $this->planService->findEarliestAvailableSlot( [$app->id], $channelId, $startDate, $days ); if ($singleSlot) { $result = $this->appointmentService->createAppointment( $userId, [$app->id], $channelId, $singleSlot['plan_id'] ); if ($result['success']) { $appointmentId = $result['data']['appointment_ids'][0] ?? null; if ($appointmentId) { $appointment = DB::table('exam_appointment') ->where('exam_appointment.id', $appointmentId) ->leftJoin('exam_application', 'exam_appointment.exam_application_id', '=', 'exam_application.id') ->leftJoin('department_resource', 'exam_appointment.department_resource_id', '=', 'department_resource.id') ->select( 'exam_appointment.id as appointment_id', 'exam_application.his_order_id', 'exam_application.examination_item_name', 'department_resource.name as resource_name', 'exam_appointment.appointment_time' ) ->first(); if ($appointment) { $patientResult['appointments'][] = [ 'appointment_id' => $appointment->appointment_id, 'his_order_id' => $appointment->his_order_id, 'examination_item_name' => $appointment->examination_item_name, 'resource_name' => $appointment->resource_name, 'appointment_time' => $appointment->appointment_time, 'time_slot_id' => $singleSlot['plan_id'] ]; } } $patientResult['summary']['success_count']++; $totalSuccessful++; } else { $patientResult['failed_applications'][] = [ 'his_order_id' => $app->his_order_id, 'examination_item_name' => $app->examination_item_name, 'error_reason' => $result['message'] ]; $patientResult['summary']['failed_count']++; $totalFailed++; } } else { $patientResult['failed_applications'][] = [ 'his_order_id' => $app->his_order_id, 'examination_item_name' => $app->examination_item_name, 'error_reason' => '指定日期范围内无可用号源' ]; $patientResult['summary']['failed_count']++; $totalFailed++; } } } }); } catch (\Exception $e) { // 事务失败,记录错误 foreach ($applications as $app) { $patientResult['failed_applications'][] = [ 'his_order_id' => $app->his_order_id, 'examination_item_name' => $app->examination_item_name, 'error_reason' => '预约失败:' . $e->getMessage() ]; } $patientResult['summary']['failed_count'] = count($applications); $totalFailed += count($applications); } $results[] = $patientResult; } // 返回结果 $allSuccess = $totalFailed === 0; return Rs::success([ 'results' => $results, 'total_summary' => [ 'total_applications' => $totalApplications, 'successful_appointments' => $totalSuccessful, 'failed_applications' => $totalFailed, 'patients_count' => count($patientInfo) ] ], $allSuccess ? '自动预约成功' : '部分医嘱预约成功'); } /** * 预约预览接口(模拟预约,不实际创建) * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function preview(Request $request) { $user = $request->attributes->get('payload'); if (!$user) { return Rs::error('用户信息获取失败'); } // 验证参数 $data = $request->validate([ 'channel_id' => 'required|integer|min:1', 'patient_info' => 'required|array', 'patient_info.*.patient_id' => 'required|integer', 'patient_info.*.his_order_ids' => 'required|array', 'patient_info.*.his_order_ids.*' => 'string', 'date_range' => 'nullable|array', 'date_range.*' => 'date' ]); $channelId = $data['channel_id']; $patientInfo = $data['patient_info']; $dateRange = $data['date_range'] ?? null; // 确定日期范围 if (empty($dateRange)) { $startDate = date('Y-m-d'); $days = 15; } else { $startDate = $dateRange[0]; $endDate = $dateRange[1]; $days = (strtotime($endDate) - strtotime($startDate)) / 86400 + 1; if ($days < 1) { return Rs::error('日期范围无效'); } } // 初始化结果 $results = []; $totalApplications = 0; $totalSuccessful = 0; $totalFailed = 0; // 按患者遍历 foreach ($patientInfo as $patientData) { $patientId = $patientData['patient_id']; $hisOrderIds = $patientData['his_order_ids']; // 查询该患者的医嘱 $applications = DB::table('exam_application') ->where('patient_id', $patientId) ->whereIn('his_order_id', $hisOrderIds) ->where('status', 0) ->get(); // 黑名单检测 $patientIdCardNo = $applications->first()->patient_id_card_no ?? null; if ($patientIdCardNo) { $blockCheck = $this->blockService->checkAppointmentPermission($channelId, $patientIdCardNo); if (!$blockCheck['status']) { // 黑名单检测失败,记录失败 $patientResult = [ 'patient_id' => $patientId, 'patient_name' => $applications->first()->patient_name ?? '未知', 'appointments' => [], 'failed_applications' => [], 'summary' => [ 'total_count' => count($hisOrderIds), 'success_count' => 0, 'failed_count' => count($hisOrderIds) ] ]; foreach ($hisOrderIds as $hisOrderId) { $patientResult['failed_applications'][] = [ 'his_order_id' => $hisOrderId, 'error_reason' => $blockCheck['msg'] ]; } $totalFailed += count($hisOrderIds); $results[] = $patientResult; continue; } } // 从医嘱中获取患者姓名 $patientName = $applications->first()->patient_name ?? '未知'; $examApplicationIds = $applications->pluck('id')->toArray(); $totalApplications += count($hisOrderIds); // 初始化该患者的预览结果 $patientResult = [ 'patient_id' => $patientId, 'patient_name' => $patientName, 'appointments' => [], 'failed_applications' => [], 'summary' => [ 'total_count' => count($hisOrderIds), 'success_count' => 0, 'failed_count' => 0 ] ]; // 检查医嘱是否存在 if (empty($examApplicationIds)) { foreach ($hisOrderIds as $hisOrderId) { $patientResult['failed_applications'][] = [ 'his_order_id' => $hisOrderId, 'error_reason' => '医嘱不存在或状态不符合预约条件' ]; } $patientResult['summary']['failed_count'] = count($hisOrderIds); $totalFailed += count($hisOrderIds); $results[] = $patientResult; continue; } // 尝试为所有医嘱查找共同资源的最早号源(不创建预约) $commonSlot = $this->planService->findEarliestAvailableSlot( $examApplicationIds, $channelId, $startDate, $days ); if ($commonSlot) { // 有共同资源且配额充足,生成统一预览 // 查询相关信息用于预览 $planInfo = DB::table('plan') ->leftJoin('plan_batch', 'plan.batch_id', '=', 'plan_batch.id') ->where('plan.id', $commonSlot['plan_id']) ->select('plan.start_time', 'plan.end_time', 'plan_batch.plan_date') ->first(); $appointmentTime = null; if ($planInfo && $planInfo->plan_date) { $startTime = $planInfo->start_time ? substr($planInfo->start_time, 0, 5) : ''; $endTime = $planInfo->end_time ? substr($planInfo->end_time, 0, 5) : ''; $timeRange = ($startTime && $endTime) ? " {$startTime}-{$endTime}" : ''; $appointmentTime = $planInfo->plan_date . $timeRange; } $resource = DB::table('department_resource') ->where('id', $commonSlot['resource_id']) ->first(); $resourceName = $resource->name ?? '未知'; foreach ($applications as $app) { $patientResult['appointments'][] = [ 'appointment_id' => null, // 预览时不生成预约ID 'exam_application_id' => $app->id, 'his_order_id' => $app->his_order_id, 'examination_item_name' => $app->examination_item_name, 'resource_name' => $resourceName, 'appointment_time' => $appointmentTime, 'time_slot_id' => $commonSlot['plan_id'] ]; } $patientResult['summary']['success_count'] = count($applications); $totalSuccessful += count($applications); } else { // 没有共同资源或配额不足,分别查找每个医嘱的号源 foreach ($applications as $app) { $singleSlot = $this->planService->findEarliestAvailableSlot( [$app->id], $channelId, $startDate, $days ); if ($singleSlot) { // 查询相关信息用于预览 $planInfo = DB::table('plan') ->leftJoin('plan_batch', 'plan.batch_id', '=', 'plan_batch.id') ->where('plan.id', $singleSlot['plan_id']) ->select('plan.start_time', 'plan.end_time', 'plan_batch.plan_date') ->first(); $appointmentTime = null; if ($planInfo && $planInfo->plan_date) { $startTime = $planInfo->start_time ? substr($planInfo->start_time, 0, 5) : ''; $endTime = $planInfo->end_time ? substr($planInfo->end_time, 0, 5) : ''; $timeRange = ($startTime && $endTime) ? " {$startTime}-{$endTime}" : ''; $appointmentTime = $planInfo->plan_date . $timeRange; } $resource = DB::table('department_resource') ->where('id', $singleSlot['resource_id']) ->first(); $patientResult['appointments'][] = [ 'appointment_id' => null, 'exam_application_id' => $app->id, 'his_order_id' => $app->his_order_id, 'examination_item_name' => $app->examination_item_name, 'resource_name' => $resource->name ?? '未知', 'appointment_time' => $appointmentTime, 'time_slot_id' => $singleSlot['plan_id'] ]; $patientResult['summary']['success_count']++; $totalSuccessful++; } else { $patientResult['failed_applications'][] = [ 'his_order_id' => $app->his_order_id, 'examination_item_name' => $app->examination_item_name, 'error_reason' => '指定日期范围内无可用号源' ]; $patientResult['summary']['failed_count']++; $totalFailed++; } } } $results[] = $patientResult; } // 返回预览结果 return Rs::success([ 'results' => $results, 'total_summary' => [ 'total_applications' => $totalApplications, 'successful_appointments' => $totalSuccessful, 'failed_applications' => $totalFailed, 'patients_count' => count($patientInfo) ] ], '预约预览生成成功'); } }