orderBy('work_date', 'desc') ->orderBy('id', 'desc'); if (!empty($params['search'])) { $search = $params['search']; $query->where(function ($q) use ($search) { $q->where('site_name', 'like', "%{$search}%") ->orWhere('description', 'like', "%{$search}%"); }); } if (!empty($params['date_from'])) { $query->where('work_date', '>=', $params['date_from']); } if (!empty($params['date_to'])) { $query->where('work_date', '<=', $params['date_to']); } $perPage = (int) ($params['per_page'] ?? 12); return $query->paginate($perPage); } public function create(array $data): ConstructionSitePhoto { return ConstructionSitePhoto::create([ 'tenant_id' => session('selected_tenant_id'), 'user_id' => Auth::id(), 'site_name' => $data['site_name'], 'work_date' => $data['work_date'], 'description' => $data['description'] ?? null, ]); } public function uploadPhoto(ConstructionSitePhoto $photo, $file, string $type): bool { if (!in_array($type, ['before', 'during', 'after'])) { return false; } $extension = $file->getClientOriginalExtension(); $timestamp = now()->format('Ymd_His'); $objectName = "construction-site-photos/{$photo->tenant_id}/{$photo->id}/{$timestamp}_{$type}.{$extension}"; // 기존 사진이 있으면 GCS에서 삭제 $oldPath = $photo->{$type . '_photo_path'}; if ($oldPath) { $this->googleCloudService->deleteFromStorage($oldPath); } // 임시 파일로 저장 후 GCS 업로드 $tempPath = $file->getRealPath(); $result = $this->googleCloudService->uploadToStorage($tempPath, $objectName); if (!$result) { Log::error('ConstructionSitePhoto: GCS 업로드 실패', [ 'photo_id' => $photo->id, 'type' => $type, ]); return false; } $photo->update([ $type . '_photo_path' => $objectName, $type . '_photo_gcs_uri' => $result['uri'], $type . '_photo_size' => $result['size'], ]); AiTokenHelper::saveGcsStorageUsage('공사현장사진대지-GCS저장', $result['size']); return true; } public function update(ConstructionSitePhoto $photo, array $data): ConstructionSitePhoto { $photo->update([ 'site_name' => $data['site_name'], 'work_date' => $data['work_date'], 'description' => $data['description'] ?? null, ]); return $photo->fresh(); } public function delete(ConstructionSitePhoto $photo): bool { // GCS에서 모든 사진 삭제 foreach (['before', 'during', 'after'] as $type) { $path = $photo->{$type . '_photo_path'}; if ($path) { $this->googleCloudService->deleteFromStorage($path); } } return $photo->delete(); } public function deletePhotoByType(ConstructionSitePhoto $photo, string $type): bool { if (!in_array($type, ['before', 'during', 'after'])) { return false; } $path = $photo->{$type . '_photo_path'}; if ($path) { $this->googleCloudService->deleteFromStorage($path); } $photo->update([ $type . '_photo_path' => null, $type . '_photo_gcs_uri' => null, $type . '_photo_size' => null, ]); return true; } }