file($inputName); if (!$file || !$file->isValid()) { throw new \RuntimeException('上传文件无效'); } $extension = strtolower($file->getUploadExtension() ?: 'jpg'); $filename = sprintf('ticket_%s.%s', uniqid(), $extension); $relativeDir = 'uploads/tickets/' . date('Ymd'); $relativePath = $relativeDir . '/' . $filename; $this->storage()->putUploadedFile($file, $relativePath); $fileUrl = $this->storage()->publicUrl($request, $relativePath); return [ 'file_id' => md5($relativePath), 'file_url' => $fileUrl, 'thumbnail_url' => $fileUrl, 'name' => $file->getUploadName(), ]; } public function delete(string $fileUrl): void { $relativePath = $this->storage()->storagePath($fileUrl); if (!str_starts_with($relativePath, 'uploads/tickets/')) { return; } $this->storage()->delete($relativePath); } public function normalize(mixed $attachments, ?Request $request = null, bool $forStorage = false): array { if (is_string($attachments) && $attachments !== '') { $decoded = json_decode($attachments, true); $attachments = is_array($decoded) ? $decoded : []; } if (!is_array($attachments)) { return []; } $list = []; foreach ($attachments as $item) { if (!is_array($item)) { continue; } $fileUrl = trim((string)($item['file_url'] ?? '')); if ($fileUrl === '') { continue; } $storedFileUrl = $this->storage()->storagePath($fileUrl); $storedThumbnailUrl = $this->storage()->storagePath(trim((string)($item['thumbnail_url'] ?? $fileUrl))); $list[] = [ 'file_id' => trim((string)($item['file_id'] ?? md5($storedFileUrl))), 'file_url' => $forStorage ? '/' . $storedFileUrl : ($request ? $this->storage()->normalizeUrl($fileUrl, $request) : $fileUrl), 'thumbnail_url' => $forStorage ? '/' . $storedThumbnailUrl : ($request ? $this->storage()->normalizeUrl(trim((string)($item['thumbnail_url'] ?? $fileUrl)), $request) : trim((string)($item['thumbnail_url'] ?? $fileUrl))), 'name' => trim((string)($item['name'] ?? '')), ]; } return $list; } private function storage(): FileStorageService { return new FileStorageService(); } }