false, 'message' => "缺少必填字段: {$field}"]; } } $validPatientTypes = [1, 2, 3, 4]; if (!in_array((int)$params['patient_type'], $validPatientTypes)) { return ['success' => false, 'message' => 'patient_type 无效,应为 1(门诊) 2(住院) 3(急诊) 4(体检)']; } $validGenders = [0, 1, 2]; if (!in_array((int)$params['patient_gender'], $validGenders)) { return ['success' => false, 'message' => 'patient_gender 无效,应为 0(未知) 1(男) 2(女)']; } return DB::transaction(function () use ($params) { $existing = DB::table('exam_application') ->where('his_request_no', $params['his_request_no']) ->first(); if ($existing) { if ($existing->status == 0) { return [ 'success' => true, 'message' => '申请单已存在(重复推送)', 'data' => ['application_id' => $existing->id, 'his_request_no' => $existing->his_request_no] ]; } return ['success' => false, 'message' => '申请单已存在且状态不为申请中,无法重复入库']; } $insertData = [ 'his_request_no' => $params['his_request_no'], 'his_order_id' => $params['his_order_id'] ?? null, 'patient_id' => (int)$params['patient_id'], 'patient_type' => (int)$params['patient_type'], 'patient_name' => $params['patient_name'], 'patient_id_card_no' => $params['patient_id_card_no'] ?? null, 'patient_phone' => $params['patient_phone'], 'patient_gender' => (int)$params['patient_gender'], 'patient_birthday' => $params['patient_birthday'] ?? null, 'patient_age' => (int)$params['patient_age'], 'inpatient_id' => $params['inpatient_id'] ?? null, 'outpatient_id' => $params['outpatient_id'] ?? null, 'ward_name' => $params['ward_name'] ?? null, 'ward_code' => $params['ward_code'] ?? null, 'bed_name' => $params['bed_name'] ?? null, 'bed_code' => $params['bed_code'] ?? null, 'apply_department_code' => $params['apply_department_code'], 'apply_department_name' => $params['apply_department_name'] ?? null, 'apply_doctor_code' => $params['apply_doctor_code'], 'apply_doctor_name' => $params['apply_doctor_name'], 'examination_item_name' => $params['examination_item_name'] ?? null, 'examination_item_code' => $params['examination_item_code'], 'examination_department_code' => $params['examination_department_code'], 'examination_department_name' => $params['examination_department_name'] ?? null, 'apply_time' => $params['apply_time'], 'payment_status' => isset($params['payment_status']) ? (int)$params['payment_status'] : null, 'clinical_diagnosis' => $params['clinical_diagnosis'] ?? null, 'notes' => $params['notes'] ?? null, 'status' => 0, 'created_at' => now(), 'updated_at' => now(), ]; $id = DB::table('exam_application')->insertGetId($insertData); $this->logOperation($id, 1, 1, $id, 1, 'HIS', null, $insertData, 'HIS系统推送申请单'); return [ 'success' => true, 'message' => '申请单入库成功', 'data' => [ 'application_id' => $id, 'his_request_no' => $params['his_request_no'], ] ]; }); } public function paymentNotify(string $hisRequestNo, int $paymentStatus): array { return DB::transaction(function () use ($hisRequestNo, $paymentStatus) { $application = DB::table('exam_application') ->where('his_request_no', $hisRequestNo) ->lockForUpdate() ->first(); if (!$application) { return ['success' => false, 'message' => '申请单不存在: ' . $hisRequestNo]; } if ($application->status == 4) { return ['success' => false, 'message' => '申请单已作废,无法更新付费状态']; } $oldValues = ['payment_status' => $application->payment_status]; DB::table('exam_application') ->where('id', $application->id) ->update([ 'payment_status' => $paymentStatus, 'updated_at' => now(), ]); $this->logOperation( $application->id, 2, 1, $application->id, 1, 'HIS', $oldValues, ['payment_status' => $paymentStatus], 'HIS付费通知,付费状态变更为: ' . $paymentStatus ); return [ 'success' => true, 'message' => '付费状态更新成功', 'data' => [ 'application_id' => $application->id, 'his_request_no' => $hisRequestNo, 'payment_status' => $paymentStatus, ] ]; }); } public function cancelNotify(string $hisRequestNo, ?string $cancelReason = null): array { return DB::transaction(function () use ($hisRequestNo, $cancelReason) { $application = DB::table('exam_application') ->where('his_request_no', $hisRequestNo) ->lockForUpdate() ->first(); if (!$application) { return ['success' => false, 'message' => '申请单不存在: ' . $hisRequestNo]; } if ($application->status == 4) { return [ 'success' => true, 'message' => '申请单已作废(重复推送)', 'data' => ['application_id' => $application->id, 'his_request_no' => $hisRequestNo] ]; } if ($application->status == 2) { return ['success' => false, 'message' => '申请单已到检,请先取消报到后再作废']; } if ($application->status == 3) { return ['success' => false, 'message' => '申请单已完成检查,无法作废']; } if ($application->status == 1) { $appointments = DB::table('exam_appointment') ->where('exam_application_id', $application->id) ->where('status', 1) ->get(); foreach ($appointments as $appointment) { $this->releaseQuotaForAppointment($appointment); DB::table('exam_appointment') ->where('id', $appointment->id) ->update([ 'status' => 4, 'cancel_time' => now(), 'cancel_reason' => 'HIS系统作废: ' . ($cancelReason ?? ''), 'updated_at' => now(), ]); $this->logOperation( $application->id, 3, 2, $appointment->id, 1, 'HIS', ['status' => 1], ['status' => 4, 'cancel_reason' => 'HIS系统作废'], 'HIS作废通知,取消预约' ); } } $oldValues = ['status' => $application->status]; DB::table('exam_application') ->where('id', $application->id) ->update([ 'status' => 4, 'his_cancel_time' => now(), 'his_cancel_reason' => $cancelReason, 'updated_at' => now(), ]); $this->logOperation( $application->id, 3, 1, $application->id, 1, 'HIS', $oldValues, ['status' => 4, 'his_cancel_time' => now(), 'his_cancel_reason' => $cancelReason], 'HIS作废通知' . ($cancelReason ? ': ' . $cancelReason : '') ); return [ 'success' => true, 'message' => '申请单作废成功', 'data' => [ 'application_id' => $application->id, 'his_request_no' => $hisRequestNo, ] ]; }); } private function releaseQuotaForAppointment($appointment): void { $plan = DB::table('plan') ->where('id', $appointment->time_slot_id) ->where('deleted', 0) ->lockForUpdate() ->first(); if (!$plan) { Log::warning("HIS作废释放配额:号源不存在 plan_id={$appointment->time_slot_id}"); return; } $batch = DB::table('plan_batch') ->where('id', $plan->batch_id) ->where('deleted', 0) ->lockForUpdate() ->first(); if (!$batch) { Log::warning("HIS作废释放配额:批次不存在 batch_id={$plan->batch_id}"); return; } $application = DB::table('exam_application') ->where('id', $appointment->exam_application_id) ->first(); $totalSeats = 1; if ($application && $batch->slot_mode == 1) { $itemSeat = DB::table('exam_item') ->where('code', $application->examination_item_code) ->where('deleted', 0) ->value('use_seats'); $totalSeats = $itemSeat ?? 1; } $channelQuotaUsed = $appointment->channel_quota_used ?? 0; $publicQuotaUsed = $appointment->public_quota_used ?? 0; $enableChannelQuota = ($batch->enable_channel_quota ?? 0) == 1; $channelId = $appointment->channel_id; if ($channelQuotaUsed > 0 && $enableChannelQuota) { $channelQuota = DB::table('plan_batch_channel_quota') ->where('batch_id', $plan->batch_id) ->where('channel_id', $channelId) ->where('status', 1) ->where('deleted', 0) ->lockForUpdate() ->first(); if ($channelQuota) { DB::table('plan_batch_channel_quota') ->where('id', $channelQuota->id) ->where('version', $channelQuota->version) ->update([ 'used_quota' => DB::raw('GREATEST(0, used_quota - ' . $channelQuotaUsed . ')'), 'version' => $channelQuota->version + 1 ]); } } if ($publicQuotaUsed > 0) { $publicQuota = DB::table('plan_batch_channel_quota') ->where('batch_id', $plan->batch_id) ->where('channel_id', -1) ->where('status', 1) ->where('deleted', 0) ->lockForUpdate() ->first(); if ($publicQuota) { DB::table('plan_batch_channel_quota') ->where('id', $publicQuota->id) ->where('version', $publicQuota->version) ->update([ 'used_quota' => DB::raw('GREATEST(0, used_quota - ' . $publicQuotaUsed . ')'), 'version' => $publicQuota->version + 1 ]); } } DB::table('plan_batch') ->where('id', $batch->id) ->where('version', $batch->version ?? 0) ->update([ 'used_quota' => DB::raw('GREATEST(0, used_quota - ' . $totalSeats . ')'), 'version' => ($batch->version ?? 0) + 1 ]); DB::table('plan') ->where('id', $plan->id) ->where('version', $plan->version ?? 0) ->update([ 'used_quota' => DB::raw('GREATEST(0, used_quota - ' . $totalSeats . ')'), 'version' => ($plan->version ?? 0) + 1 ]); } public function queryAppointment(array $params): array { $query = DB::table('exam_appointment') ->leftJoin('exam_application', 'exam_appointment.exam_application_id', '=', 'exam_application.id') ->leftJoin('department_resource', 'exam_appointment.department_resource_id', '=', 'department_resource.id') ->leftJoin('department', 'department_resource.department_id', '=', 'department.id') ->leftJoin('channel', 'exam_appointment.channel_id', '=', 'channel.id') ->select( 'exam_appointment.id as appointment_id', 'exam_appointment.status as appointment_status', 'exam_appointment.appointment_time', 'exam_appointment.check_in_time', 'exam_appointment.finish_time', 'exam_appointment.cancel_time', 'exam_appointment.cancel_reason', 'exam_appointment.created_at as appointment_created_at', 'exam_application.id as application_id', 'exam_application.his_request_no', 'exam_application.his_order_id', 'exam_application.patient_id', 'exam_application.patient_name', 'exam_application.patient_id_card_no', 'exam_application.patient_phone', 'exam_application.patient_gender', 'exam_application.patient_age', 'exam_application.patient_type', 'exam_application.examination_item_code', 'exam_application.examination_item_name', 'exam_application.examination_department_code', 'exam_application.examination_department_name', 'exam_application.apply_department_code', 'exam_application.apply_department_name', 'exam_application.apply_doctor_code', 'exam_application.apply_doctor_name', 'exam_application.clinical_diagnosis', 'exam_application.status as application_status', 'department_resource.id as resource_id', 'department_resource.name as resource_name', 'department_resource.code as resource_code', 'department.name as department_name', 'department.code as department_code', 'channel.name as channel_name' ); if (!empty($params['his_request_no'])) { $query->where('exam_application.his_request_no', $params['his_request_no']); } if (!empty($params['his_order_id'])) { $query->where('exam_application.his_order_id', $params['his_order_id']); } if (!empty($params['patient_id_card_no'])) { $query->where('exam_application.patient_id_card_no', $params['patient_id_card_no']); } if (!empty($params['patient_id'])) { $query->where('exam_application.patient_id', $params['patient_id']); } if (!empty($params['appointment_id'])) { $query->where('exam_appointment.id', $params['appointment_id']); } if (isset($params['appointment_status']) && $params['appointment_status'] !== '') { $query->where('exam_appointment.status', (int)$params['appointment_status']); } if (!empty($params['start_date'])) { $query->where('exam_appointment.appointment_time', '>=', $params['start_date']); } if (!empty($params['end_date'])) { $query->where('exam_appointment.appointment_time', '<=', $params['end_date'] . ' 23:59:59'); } $results = $query->orderByDesc('exam_appointment.id')->limit(200)->get(); $statusMap = [1 => '已预约', 2 => '已到检', 3 => '已完成', 4 => '已取消', 5 => '爽约']; $genderMap = [0 => '未知', 1 => '男', 2 => '女']; $patientTypeMap = [1 => '门诊', 2 => '住院', 3 => '急诊', 4 => '体检']; $list = $results->map(function ($row) use ($statusMap, $genderMap, $patientTypeMap) { return [ 'appointment_id' => $row->appointment_id, 'appointment_status' => $row->appointment_status, 'appointment_status_name' => $statusMap[$row->appointment_status] ?? '未知', 'appointment_time' => $row->appointment_time, 'check_in_time' => $row->check_in_time, 'finish_time' => $row->finish_time, 'cancel_time' => $row->cancel_time, 'cancel_reason' => $row->cancel_reason, 'his_request_no' => $row->his_request_no, 'his_order_id' => $row->his_order_id, 'patient_id' => $row->patient_id, 'patient_name' => $row->patient_name, 'patient_id_card_no' => $row->patient_id_card_no, 'patient_phone' => $row->patient_phone, 'patient_gender' => $row->patient_gender, 'patient_gender_name' => $genderMap[$row->patient_gender] ?? '未知', 'patient_age' => $row->patient_age, 'patient_type' => $row->patient_type, 'patient_type_name' => $patientTypeMap[$row->patient_type] ?? '未知', 'examination_item_code' => $row->examination_item_code, 'examination_item_name' => $row->examination_item_name, 'examination_department_code' => $row->examination_department_code, 'examination_department_name' => $row->examination_department_name, 'apply_department_code' => $row->apply_department_code, 'apply_department_name' => $row->apply_department_name, 'apply_doctor_code' => $row->apply_doctor_code, 'apply_doctor_name' => $row->apply_doctor_name, 'clinical_diagnosis' => $row->clinical_diagnosis, 'resource_id' => $row->resource_id, 'resource_name' => $row->resource_name, 'resource_code' => $row->resource_code, 'department_name' => $row->department_name, 'department_code' => $row->department_code, 'channel_name' => $row->channel_name, ]; }); return [ 'success' => true, 'message' => '查询成功', 'data' => ['total' => $list->count(), 'list' => $list->values()->toArray()] ]; } public function checkIn(string $hisRequestNo): array { return DB::transaction(function () use ($hisRequestNo) { $application = DB::table('exam_application') ->where('his_request_no', $hisRequestNo) ->lockForUpdate() ->first(); if (!$application) { return ['success' => false, 'message' => '申请单不存在: ' . $hisRequestNo]; } if ($application->status == 4) { return ['success' => false, 'message' => '申请单已作废,无法报到']; } $appointment = DB::table('exam_appointment') ->where('exam_application_id', $application->id) ->where('status', 1) ->lockForUpdate() ->first(); if (!$appointment) { return ['success' => false, 'message' => '该申请单没有处于已预约状态的预约记录']; } DB::table('exam_appointment') ->where('id', $appointment->id) ->update([ 'status' => 2, 'check_in_time' => now(), 'updated_at' => now(), ]); DB::table('exam_application') ->where('id', $application->id) ->update([ 'status' => 2, 'updated_at' => now(), ]); $resource = DB::table('department_resource') ->where('id', $appointment->department_resource_id) ->first(); $this->logOperation( $application->id, 2, 2, $appointment->id, 1, 'HIS', ['status' => 1, 'check_in_time' => null], ['status' => 2, 'check_in_time' => now()->toDateTimeString()], '三方系统通知报到' ); return [ 'success' => true, 'message' => '报到成功', 'data' => [ 'appointment_id' => $appointment->id, 'his_request_no' => $hisRequestNo, 'patient_name' => $application->patient_name, 'examination_item_name' => $application->examination_item_name, 'resource_name' => $resource->name ?? null, 'check_in_time' => now()->toDateTimeString(), ] ]; }); } public function cancelCheckIn(string $hisRequestNo, ?string $reason = null): array { return DB::transaction(function () use ($hisRequestNo, $reason) { $application = DB::table('exam_application') ->where('his_request_no', $hisRequestNo) ->lockForUpdate() ->first(); if (!$application) { return ['success' => false, 'message' => '申请单不存在: ' . $hisRequestNo]; } $appointment = DB::table('exam_appointment') ->where('exam_application_id', $application->id) ->where('status', 2) ->lockForUpdate() ->first(); if (!$appointment) { return ['success' => false, 'message' => '该申请单没有处于已到检状态的预约记录']; } DB::table('exam_appointment') ->where('id', $appointment->id) ->update([ 'status' => 1, 'check_in_time' => null, 'updated_at' => now(), ]); DB::table('exam_application') ->where('id', $application->id) ->update([ 'status' => 1, 'updated_at' => now(), ]); $resource = DB::table('department_resource') ->where('id', $appointment->department_resource_id) ->first(); $this->logOperation( $application->id, 2, 2, $appointment->id, 1, 'HIS', ['status' => 2, 'check_in_time' => $appointment->check_in_time], ['status' => 1, 'check_in_time' => null], '三方系统通知取消报到' . ($reason ? ': ' . $reason : '') ); return [ 'success' => true, 'message' => '取消报到成功', 'data' => [ 'appointment_id' => $appointment->id, 'his_request_no' => $hisRequestNo, 'patient_name' => $application->patient_name, 'examination_item_name' => $application->examination_item_name, 'resource_name' => $resource->name ?? null, ] ]; }); } private function logOperation( int $applicationId, int $operationType, int $entityType, int $entityId, int $operatorType, string $operatorId, ?array $oldValues = null, ?array $newValues = null, ?string $operationReason = null ): void { try { DB::table('operation_log')->insert([ 'application_id' => $applicationId, 'operator_type' => $operatorType, 'operator_id' => $operatorId, 'operation_type' => $operationType, 'entity_type' => $entityType, 'entity_id' => $entityId, 'old_values' => $oldValues ? json_encode($oldValues, JSON_UNESCAPED_UNICODE) : null, 'new_values' => $newValues ? json_encode($newValues, JSON_UNESCAPED_UNICODE) : null, 'operation_time' => now(), 'operation_reason' => $operationReason, 'client_ip' => request()->ip(), ]); } catch (\Exception $e) { Log::error('记录操作日志失败:' . $e->getMessage()); } } }