userService = $userService; } /** * 获取医嘱列表 */ public function getApplicationList(Request $request) { $hisRequestNo = $request->input('his_request_no', ''); $patientName = $request->input('patient_name', ''); $patientPhone = $request->input('patient_phone', ''); $applyDoctorName = $request->input('apply_doctor_name', ''); $status = $request->input('status', null); $applyTimeStart = $request->input('apply_time_start', ''); $applyTimeEnd = $request->input('apply_time_end', ''); $page = $request->input('page', 1); $pageSize = $request->input('page_size', 10); // 获取当前用户信息,非管理员按执行科室或开单医生过滤 $sysUser = $this->userService->getUserInfoFromRequest($request); $isAdmin = $this->userService->isAdminFromRequest($request); $deptCode = null; $hisCode = null; if (!$isAdmin) { if (!empty($sysUser->dept_id)) { $dept = DB::table('department')->where('id', $sysUser->dept_id)->where('deleted', 0)->first(); if ($dept && !empty($dept->code)) { $deptCode = $dept->code; } } if (!empty($sysUser->his_code)) { $hisCode = $sysUser->his_code; } } $query = DB::table('exam_application as ea') ->leftJoin('patient_type as pt', 'ea.patient_type', '=', 'pt.mask') ->select([ 'ea.id', 'ea.his_order_id', 'ea.his_request_no', 'ea.patient_id', 'ea.patient_type', 'ea.patient_name', 'ea.patient_id_card_no', 'ea.patient_phone', 'ea.patient_gender', 'ea.patient_age', 'ea.patient_birthday', 'ea.apply_department_code', 'ea.apply_department_name', 'ea.apply_doctor_code', 'ea.apply_doctor_name', 'ea.examination_item_name', 'ea.examination_item_code', 'ea.examination_department_code', 'ea.examination_department_name', 'ea.apply_time', 'ea.payment_status', 'ea.clinical_diagnosis', 'ea.notes', 'ea.status', 'ea.created_at', 'ea.updated_at', 'ea.his_cancel_time', 'ea.his_cancel_reason', 'pt.name as patient_type_name' ]); if (!empty($hisRequestNo)) { $query->where('ea.his_request_no', 'like', '%' . $hisRequestNo . '%'); } if (!empty($patientName)) { $query->where('ea.patient_name', 'like', '%' . $patientName . '%'); } if (!empty($patientPhone)) { $query->where('ea.patient_phone', 'like', '%' . $patientPhone . '%'); } if (!empty($applyDoctorName)) { $query->where('ea.apply_doctor_name', 'like', '%' . $applyDoctorName . '%'); } if ($status !== null) { $query->where('ea.status', $status); } if (!empty($applyTimeStart)) { $query->where('ea.apply_time', '>=', $applyTimeStart); } if (!empty($applyTimeEnd)) { $query->where('ea.apply_time', '<=', $applyTimeEnd . ' 23:59:59'); } // 非管理员:执行科室是自己科室 OR 开单医生是自己 if (!$isAdmin) { if ($deptCode !== null || $hisCode !== null) { $query->where(function ($q) use ($deptCode, $hisCode) { if ($deptCode !== null) { $q->where('ea.examination_department_code', $deptCode); } if ($hisCode !== null) { $q->orWhere('ea.apply_doctor_code', $hisCode); } }); } else { $query->whereRaw('1 = 0'); } } $total = $query->count(); $list = $query->orderBy('ea.apply_time', 'desc') ->orderBy('ea.id', 'desc') ->skip(($page - 1) * $pageSize) ->take($pageSize) ->get(); // 查询已预约的医嘱的预约信息 $appointmentIds = $list->where('status', 1)->pluck('id')->toArray(); $appointments = []; if (!empty($appointmentIds)) { $appointments = DB::table('exam_appointment as ea') ->leftJoin('department_resource as dr', 'ea.department_resource_id', '=', 'dr.id') ->whereIn('ea.exam_application_id', $appointmentIds) ->where('ea.status', 1) // 只获取预约成功的记录 ->select([ 'ea.id', 'ea.exam_application_id', 'ea.time_slot_id', 'ea.channel_id', 'ea.appointment_time', 'ea.department_resource_id', 'dr.cancel_reason_required' ]) ->get() ->keyBy('exam_application_id'); } // 为每个医嘱添加预约信息 foreach ($list as $item) { // 如果医嘱已预约,添加预约信息 if ($item->status === 1 && isset($appointments[$item->id])) { $item->appointment_info = $appointments[$item->id]; } else { $item->appointment_info = null; } } return Rs::success([ 'list' => $list, 'total' => $total, 'page' => $page, 'page_size' => $pageSize, 'total_pages' => ceil($total / $pageSize) ]); } /** * 获取患者预约列表(历史预约记录) */ public function getPatientAppointments(Request $request) { $patientId = $request->input('patient_id', 0); $statusArr = $request->input('status_arr'); if (empty($patientId)) { return Rs::error('患者ID不能为空'); } $list = collect(); // 状态处理逻辑简化 $hasPending = $statusArr === null || (is_array($statusArr) && in_array(0, $statusArr)); $hasScheduled = $statusArr === null || (is_array($statusArr) && array_intersect([1, 2, 3], $statusArr)); // 查询未预约记录 if ($hasPending) { $pending = DB::table('exam_application') ->where('patient_id', $patientId) ->where('status', 0) ->select(['id', 'his_order_id', 'patient_id', 'patient_name', 'patient_id_card_no', 'patient_gender', 'patient_age', 'examination_item_name', 'examination_item_code', 'examination_department_name', 'examination_department_code', 'apply_doctor_name', 'apply_doctor_code', 'apply_department_name', 'apply_time', 'payment_status', 'clinical_diagnosis', 'notes', 'status', 'created_at', 'updated_at']) ->orderBy('apply_time', 'desc') ->orderBy('id', 'desc') ->get() ->map(function ($item) { // 补充预约表字段 return (object) array_merge((array)$item, [ 'time_slot_id' => null, 'channel_id' => null, 'exam_application_id' => $item->id, 'appointment_time' => null, 'check_in_time' => null, 'finish_time' => null, 'cancel_time' => null, 'channel_name' => null, 'channel_short_name' => null ]); }); $list = $list->concat($pending); } // 查询已预约记录 if ($hasScheduled) { $scheduledStatuses = $statusArr === null ? [1, 2, 3] : array_intersect([1, 2, 3], $statusArr); if (!empty($scheduledStatuses)) { $appointments = DB::table('exam_appointment as ea') ->leftJoin('channel as c', 'ea.channel_id', '=', 'c.id') ->leftJoin('exam_application as eapp', 'ea.exam_application_id', '=', 'eapp.id') ->leftJoin('plan as p', 'ea.time_slot_id', '=', 'p.id') ->leftJoin('department_resource as dr', 'ea.department_resource_id', '=', 'dr.id') ->select(['ea.id', 'ea.time_slot_id', 'ea.channel_id', 'ea.exam_application_id', 'ea.patient_id', 'ea.patient_name', 'ea.status', 'ea.appointment_time', 'ea.check_in_time', 'ea.finish_time', 'ea.cancel_time', 'ea.created_at', 'ea.updated_at', 'c.name as channel_name', 'c.short_name as channel_short_name', 'eapp.examination_item_name', 'eapp.examination_item_code', 'eapp.examination_department_name', 'eapp.examination_department_code', 'eapp.apply_doctor_name', 'eapp.apply_doctor_code', 'eapp.apply_department_name', 'eapp.apply_time', 'eapp.payment_status', 'eapp.clinical_diagnosis', 'eapp.notes', 'eapp.patient_id_card_no', 'p.start_time as slot_start_time', 'p.end_time as slot_end_time', 'dr.name as room_name', 'dr.cancel_reason_required']) ->where('ea.patient_id', $patientId) ->whereIn('ea.status', $scheduledStatuses) ->orderBy('ea.id', 'desc') ->get(); $list = $list->concat($appointments); } } // 简化排序逻辑 if (!$list->isEmpty()) { $list = $list->sortByDesc(function ($item) { return $item->updated_at ?? $item->apply_time ?? $item->created_at; })->values(); } return Rs::success($list); } /** * 获取患者详情 */ public function getPatientDetail(Request $request) { $patientId = $request->input('patient_id', 0); if (empty($patientId)) { return Rs::error('患者ID不能为空'); } // 从exam_application表获取患者信息 $patientInfo = DB::table('exam_application') ->where('patient_id', $patientId) ->select([ 'patient_id', 'patient_name', 'patient_gender', 'patient_age', 'patient_phone', 'patient_type', 'patient_birthday' ]) ->first(); if (!$patientInfo) { return Rs::error('未找到患者信息'); } // 获取患者类型名称 $patientTypeName = ''; if ($patientInfo->patient_type == 1) { $patientTypeName = '门诊患者'; } elseif ($patientInfo->patient_type == 2) { $patientTypeName = '住院患者'; } elseif ($patientInfo->patient_type == 3) { $patientTypeName = '急诊患者'; } elseif ($patientInfo->patient_type == 4) { $patientTypeName = '体检患者'; } $patientInfo->patient_type_name = $patientTypeName; return Rs::success($patientInfo); } /** * 根据HIS医嘱ID数组获取医嘱信息 */ public function getApplicationsByHisOrderIds(Request $request) { $hisOrderIds = $request->input('his_order_ids', []); $channelId = $request->input('channel_id', 2); // 默认护士站 if (empty($hisOrderIds) || !is_array($hisOrderIds)) { return Rs::error('医嘱ID列表不能为空'); } $list = DB::table('exam_application as ea') ->leftJoin('patient_type as pt', 'ea.patient_type', '=', 'pt.mask') ->whereIn('ea.his_order_id', $hisOrderIds) ->whereIn('ea.status', [0, 1]) // 只获取申请中和已预约的医嘱 ->select([ 'ea.id', 'ea.his_request_no', 'ea.his_order_id', 'ea.patient_id', 'ea.patient_type', 'ea.patient_name', 'ea.patient_phone', 'ea.patient_gender', 'ea.patient_age', 'ea.apply_department_code', 'ea.apply_department_name', 'ea.apply_doctor_code', 'ea.apply_doctor_name', 'ea.examination_item_name', 'ea.examination_item_code', 'ea.examination_department_code', 'ea.examination_department_name', 'ea.apply_time', 'ea.payment_status', 'ea.status', 'ea.clinical_diagnosis', 'ea.notes', 'ea.created_at', 'ea.updated_at', 'ea.his_cancel_time', 'ea.his_cancel_reason', 'pt.name as patient_type_name' ]) ->orderBy('ea.apply_time', 'desc') ->orderBy('ea.id', 'desc') ->get(); // 获取渠道掩码 $channel = DB::table('channel')->where('id', $channelId)->value('mask'); $channelMask = $channel ?? 2; // 默认护士站掩码 // 查询已预约的医嘱的预约信息 $appointmentIds = $list->where('status', 1)->pluck('id')->toArray(); $appointments = []; if (!empty($appointmentIds)) { $appointments = DB::table('exam_appointment as ea') ->leftJoin('department_resource as dr', 'ea.department_resource_id', '=', 'dr.id') ->whereIn('ea.exam_application_id', $appointmentIds) ->where('ea.status', 1) // 只获取预约成功的记录 ->select([ 'ea.id', 'ea.exam_application_id', 'ea.time_slot_id', 'ea.channel_id', 'ea.appointment_time', 'ea.department_resource_id', 'dr.cancel_reason_required' ]) ->get() ->keyBy('exam_application_id'); } // 为每个医嘱添加项目状态检查和预约信息 foreach ($list as $item) { $item->status_messages = $this->checkItemStatus($item->examination_item_code, $channelMask); // 如果医嘱已预约,添加预约信息 if ($item->status === 1 && isset($appointments[$item->id])) { $item->appointment_info = $appointments[$item->id]; } else { $item->appointment_info = null; } } return Rs::success($list); } /** * 检查项目状态 * @param string $itemCode 项目编码 * @param int $channelMask 渠道掩码 * @return array 状态消息数组 */ private function checkItemStatus($itemCode, $channelMask) { $messages = []; // 1. 检查项目是否存在于数据库 $examItem = DB::table('exam_item') ->where('code', $itemCode) ->where('deleted', 0) ->first(); if (!$examItem) { $messages[] = '项目不存在'; return $messages; } // 2. 检查项目状态是否正常 if ($examItem->status !== 1) { $messages[] = '项目已停用'; } // 3. 检查项目是否需要预约 if ($examItem->need_appointment === 0) { $messages[] = '无需预约'; return $messages; } // 4. 检查项目是否支持当前渠道预约 if ($examItem->enable_channel_mask !== null) { if (($examItem->enable_channel_mask & $channelMask) === 0) { $messages[] = '不支持此渠道预约'; return $messages; } } // 5. 检查项目是否关联了资源 $resourceCount = DB::table('exam_item_resource') ->where('item_id', $examItem->id) ->where('deleted', 0) ->count(); if ($resourceCount === 0) { $messages[] = '未关联资源'; } return $messages; } /** * 获取操作日志 */ public function getOperationLogs(Request $request) { $applicationId = $request->input('application_id', 0); if (empty($applicationId)) { return Rs::error('医嘱ID不能为空'); } $list = DB::table('operation_log') ->where('application_id', $applicationId) ->orderBy('operation_time', 'desc') ->orderBy('id', 'desc') ->get() ->map(function ($item) { // 解析JSON字段 $item->old_values = $item->old_values ? json_decode($item->old_values, true) : null; $item->new_values = $item->new_values ? json_decode($item->new_values, true) : null; // 添加中文描述 $item->operator_type_name = $this->getOperatorTypeName($item->operator_type); $item->operation_type_name = $this->getOperationTypeName($item->operation_type); $item->entity_type_name = $this->getEntityTypeName($item->entity_type); return $item; }); return Rs::success($list); } /** * 获取操作来源名称 */ private function getOperatorTypeName($type) { $typeMap = [ 1 => 'HIS系统', 2 => '预约系统', 3 => '管理员' ]; return $typeMap[$type] ?? '未知'; } /** * 获取操作类型名称 */ private function getOperationTypeName($type) { $typeMap = [ 1 => '创建', 2 => '更新', 3 => '作废', 4 => '取消' ]; return $typeMap[$type] ?? '未知'; } /** * 获取操作实体名称 */ private function getEntityTypeName($type) { $typeMap = [ 1 => '检查申请单', 2 => '预约记录' ]; return $typeMap[$type] ?? '未知'; } /** * 获取病区患者列表 */ public function getWardPatientList(Request $request) { // 获取查询参数 $appointmentStatus = $request->input('appointment_status', null); $bedNumber = $request->input('bed_number', ''); $patientName = $request->input('patient_name', ''); $page = $request->input('page', 1); $pageSize = $request->input('page_size', 20); // 获取当前用户信息 $sysUser = $this->userService->getUserInfoFromRequest($request); if (!$sysUser) { return Rs::error('用户不存在'); } // 判断是否为管理员(从 JWT payload 中直接判断,无需额外查询) $isAdmin = $this->userService->isAdminFromRequest($request); // 构建查询:获取每个患者的最新检查申请记录 $subQuery = DB::table('exam_application as ea') ->select([ 'ea.patient_id', 'ea.id', 'ea.bed_code', 'ea.inpatient_id', 'ea.patient_name', 'ea.patient_gender', 'ea.patient_age', 'ea.ward_code', 'ea.ward_name', 'ea.apply_department_name', 'ea.apply_doctor_name', 'ea.clinical_diagnosis', 'ea.status as appointment_status', 'ea.apply_time' ]) ->whereIn('ea.id', function($query) { $query->selectRaw('MAX(id)') ->from('exam_application') ->groupBy('patient_id'); }); // 非管理员只显示对应病区的患者,未绑定病区则拒绝 if (!$isAdmin) { if (empty($sysUser->ward_code)) { return Rs::error('用户未绑定病区,无法获取病区患者'); } $subQuery->where('ea.ward_code', $sysUser->ward_code); } // 默认只查询住院患者(patient_type = 2) $subQuery->where('ea.patient_type', 2); // 添加筛选条件 if (!empty($bedNumber)) { $subQuery->where('ea.bed_code', $bedNumber); } if (!empty($patientName)) { $subQuery->where('ea.patient_name', 'like', '%' . $patientName . '%'); } if ($appointmentStatus !== null) { $subQuery->where('ea.status', $appointmentStatus); } // 计算总数 $total = $subQuery->count(); // 获取患者列表 $patients = $subQuery ->orderBy('ea.apply_time', 'desc') ->orderBy('ea.id', 'desc') ->skip(($page - 1) * $pageSize) ->take($pageSize) ->get(); // 获取所有患者ID $patientIds = $patients->pluck('patient_id')->toArray(); // 查询每个患者的待预约数量 (status = 0) $pendingCounts = []; if (!empty($patientIds)) { $pendingCounts = DB::table('exam_application') ->whereIn('patient_id', $patientIds) ->where('status', 0) ->selectRaw('patient_id, COUNT(*) as count') ->groupBy('patient_id') ->pluck('count', 'patient_id') ->toArray(); } // 查询每个患者的今日检查数量 $today = date('Y-m-d'); $todayCheckCounts = []; if (!empty($patientIds)) { $todayCheckCounts = DB::table('exam_appointment') ->join('exam_application', 'exam_appointment.exam_application_id', '=', 'exam_application.id') ->whereIn('exam_application.patient_id', $patientIds) ->where('exam_appointment.status', 1) // 预约成功 ->whereDate('exam_appointment.appointment_time', $today) ->selectRaw('exam_application.patient_id, COUNT(*) as count') ->groupBy('exam_application.patient_id') ->pluck('count', 'patient_id') ->toArray(); } // 组装返回数据 $list = $patients->map(function ($patient) use ($pendingCounts, $todayCheckCounts) { return [ 'id' => $patient->patient_id, 'bed_number' => $patient->bed_code ?? '', 'inpatient_no' => $patient->inpatient_id ?? '', 'patient_name' => $patient->patient_name, 'gender' => $patient->patient_gender, 'age' => $patient->patient_age, 'ward_code' => $patient->ward_code, 'ward_name' => $patient->ward_name ?? '', 'department_name' => $patient->apply_department_name ?? '', 'attending_doctor' => $patient->apply_doctor_name ?? '', 'diagnosis' => $patient->clinical_diagnosis ?? '', 'pending_count' => $pendingCounts[$patient->patient_id] ?? 0, 'today_check_count' => $todayCheckCounts[$patient->patient_id] ?? 0, 'appointment_status' => $patient->appointment_status ]; }); return Rs::success([ 'list' => $list, 'total' => $total, 'page' => $page, 'page_size' => $pageSize, 'total_pages' => ceil($total / $pageSize) ]); } /** * 批量获取多个患者的待预约项目 */ public function getBatchPatientExaminations(Request $request) { $patientIds = $request->input('patient_ids', []); if (empty($patientIds) || !is_array($patientIds)) { return Rs::error('患者ID列表不能为空'); } // 限制一次最多查询100个患者 if (count($patientIds) > 100) { return Rs::error('一次最多查询100个患者'); } // 查询所有患者的待预约项目(status = 0) $examinations = DB::table('exam_application') ->whereIn('patient_id', $patientIds) ->where('status', 0) ->select([ 'patient_id', 'patient_name', 'patient_gender', 'patient_age', 'id', 'his_order_id', 'examination_item_name', 'examination_item_code', 'examination_department_name', 'examination_department_code', 'apply_doctor_name', 'apply_doctor_code', 'apply_department_name', 'apply_time', 'payment_status', 'clinical_diagnosis', 'notes' ]) ->orderBy('patient_id') ->orderBy('apply_time', 'desc') ->get(); // 按患者ID分组 $groupedExaminations = []; foreach ($examinations as $exam) { if (!isset($groupedExaminations[$exam->patient_id])) { $groupedExaminations[$exam->patient_id] = []; } $groupedExaminations[$exam->patient_id][] = (array)$exam; } return Rs::success($groupedExaminations); } public function getPatientPendingItems(Request $request) { $patientId = $request->input('patient_id', 0); $channelId = $request->input('channel_id', 2); if (empty($patientId)) { return Rs::error('患者ID不能为空'); } $applications = DB::table('exam_application') ->where('patient_id', $patientId) ->where('status', 0) ->select([ 'id', 'his_order_id', 'patient_id', 'patient_name', 'examination_item_name', 'examination_item_code', 'examination_department_name', 'examination_department_code', 'apply_doctor_name', 'apply_department_name', 'apply_time', 'payment_status', 'status' ]) ->orderBy('apply_time', 'desc') ->get(); if ($applications->isEmpty()) { return Rs::success([ 'items' => [], 'groups' => [], 'extra_his_order_ids' => [] ]); } $itemCodes = $applications->pluck('examination_item_code')->unique()->toArray(); $examItems = DB::table('exam_item') ->whereIn('code', $itemCodes) ->where('deleted', 0) ->select('id', 'code', 'name', 'use_seats') ->get() ->keyBy('code'); $examItemIds = $examItems->pluck('id')->toArray(); $itemResources = DB::table('exam_item_resource') ->whereIn('item_id', $examItemIds) ->where('deleted', 0) ->select('item_id', 'resource_id') ->get(); $resourcesByItem = []; foreach ($itemResources as $ir) { $resourcesByItem[$ir->item_id][] = $ir->resource_id; } $resourcesInfo = []; if (!empty($itemResources)) { $resourceIds = $itemResources->pluck('resource_id')->unique()->toArray(); $resourcesInfo = DB::table('department_resource') ->whereIn('id', $resourceIds) ->pluck('name', 'id') ->toArray(); } $groupMap = []; foreach ($applications as $app) { $examItem = $examItems->get($app->examination_item_code); if (!$examItem) { continue; } $itemId = $examItem->id; $resourceIds = $resourcesByItem[$itemId] ?? []; $groupMap[$app->id] = [ 'application_id' => $app->id, 'his_order_id' => $app->his_order_id, 'item_name' => $app->examination_item_name, 'resource_ids' => $resourceIds, ]; } $assignedGroups = []; $appGroupIndex = []; $groupIndex = 0; foreach ($groupMap as $appId => $info) { if (isset($appGroupIndex[$appId])) { continue; } $groupApps = [$appId]; $groupResources = $info['resource_ids']; foreach ($groupMap as $otherAppId => $otherInfo) { if ($otherAppId === $appId || isset($appGroupIndex[$otherAppId])) { continue; } $intersection = array_intersect($groupResources, $otherInfo['resource_ids']); if (!empty($intersection)) { $groupApps[] = $otherAppId; $groupResources = $intersection; $appGroupIndex[$otherAppId] = $groupIndex; } } $appGroupIndex[$appId] = $groupIndex; $primaryResourceId = !empty($groupResources) ? array_values($groupResources)[0] : null; $assignedGroups[$groupIndex] = [ 'resource_id' => $primaryResourceId, 'resource_name' => $primaryResourceId ? ($resourcesInfo[$primaryResourceId] ?? '未知') : '未知', 'application_ids' => [], 'his_order_ids' => [], 'items' => [] ]; foreach ($groupApps as $gAppId) { $gInfo = $groupMap[$gAppId]; $assignedGroups[$groupIndex]['application_ids'][] = $gAppId; $assignedGroups[$groupIndex]['his_order_ids'][] = $gInfo['his_order_id']; $assignedGroups[$groupIndex]['items'][] = [ 'application_id' => $gAppId, 'his_order_id' => $gInfo['his_order_id'], 'item_name' => $gInfo['item_name'] ]; } $groupIndex++; } $groups = array_values($assignedGroups); $currentHisOrderIds = $request->input('current_his_order_ids', []); $allHisOrderIds = $applications->pluck('his_order_id')->toArray(); $extraHisOrderIds = array_values(array_diff($allHisOrderIds, $currentHisOrderIds)); return Rs::success([ 'items' => $applications, 'groups' => $groups, 'extra_his_order_ids' => $extraHisOrderIds ]); } }