field(['id', 'appointment_type', 'status', 'appointment_date', 'appointment_time']) ->select()->toArray(); return self::resolveFromAppointments($appointments, $appointmentId); } /** * $appointments 必须仅包含当前诊单的挂号;未知显式 ID 不可回退到其他挂号。 * * @param list> $appointments * @return array{appointment_id:int,appointment_type:?string,appointment_type_desc:string,can_video_call:bool,can_audio_call:bool,call_disabled_reason:string} */ public static function resolveFromAppointments(array $appointments, int $appointmentId = 0): array { if ($appointmentId !== 0) { foreach ($appointments as $appointment) { if ($appointmentId > 0 && (int) ($appointment['id'] ?? 0) === $appointmentId) { return self::policyForAppointment($appointment); } } throw new RuntimeException('指定挂号不存在或不属于当前诊单'); } $active = array_values(array_filter($appointments, static fn (array $row): bool => (int) ($row['status'] ?? 0) === 1)); if (count($active) === 1) { return self::policyForAppointment($active[0]); } if (count($active) > 1) { return array_replace(self::policyForAppointment(null), [ 'appointment_type_desc' => '待指定挂号', 'call_disabled_reason' => '请指定本次挂号', ]); } $historical = array_values(array_filter($appointments, static fn (array $row): bool => in_array((int) ($row['status'] ?? 0), [3, 4], true))); usort($historical, static function (array $left, array $right): int { return [ (string) ($right['appointment_date'] ?? ''), self::sortableTime($right['appointment_time'] ?? ''), (int) ($right['id'] ?? 0), ] <=> [ (string) ($left['appointment_date'] ?? ''), self::sortableTime($left['appointment_time'] ?? ''), (int) ($left['id'] ?? 0), ]; }); return self::policyForAppointment($historical[0] ?? null); } /** @return array{appointment_id:int,appointment_type:?string,appointment_type_desc:string,can_video_call:bool,can_audio_call:bool,call_disabled_reason:string} */ public static function policyForAppointment(?array $appointment): array { if ($appointment === null) { return [ 'appointment_id' => 0, 'appointment_type' => null, 'appointment_type_desc' => '未挂号', 'can_video_call' => false, 'can_audio_call' => false, 'call_disabled_reason' => '未挂号,不能发起通话', ]; } // 空类型仅对确实存在的历史挂号应用旧视频默认值。 $type = AppointmentTypeEnum::normalizeStored($appointment['appointment_type'] ?? null); $status = (int) ($appointment['status'] ?? 0); $callableStatus = in_array($status, [1, 3, 4], true); $video = $callableStatus && $type === AppointmentTypeEnum::VIDEO; $audio = $callableStatus && in_array($type, [AppointmentTypeEnum::VIDEO, 'phone'], true); $reason = match (true) { $status === 2 => '本次挂号已取消,不能发起通话', !$callableStatus => '本次挂号状态不支持通话', $type === AppointmentTypeEnum::TEXT => '图文问诊不支持音视频通话', $type === 'phone' => '电话问诊仅支持语音通话', !$video && !$audio => '本次挂号问诊方式不支持音视频通话', default => '', }; return [ 'appointment_id' => (int) ($appointment['id'] ?? 0), 'appointment_type' => $type, 'appointment_type_desc' => AppointmentTypeEnum::description($type), 'can_video_call' => $video, 'can_audio_call' => $audio, 'call_disabled_reason' => $reason, ]; } private static function sortableTime($value): string { $time = (string) $value; if (preg_match('/^(\d{1,2}):(\d{2})(?::(\d{2}))?$/', $time, $matches) === 1) { return sprintf('%02d:%02d:%02d', (int) $matches[1], (int) $matches[2], (int) ($matches[3] ?? 0)); } return $time; } }