From c3de8410ee6a3c8c8bfc93fcff2f86c8d2843f3b Mon Sep 17 00:00:00 2001 From: kent Date: Tue, 30 Dec 2025 22:58:50 +0900 Subject: [PATCH] =?UTF-8?q?feat(API):=20=ED=85=8C=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EB=A1=9C=EA=B3=A0=20=EC=97=85=EB=A1=9C=EB=93=9C=20API=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - POST /api/v1/tenants/logo 엔드포인트 추가 - TenantLogoUploadRequest: 이미지 유효성 검사 (jpeg, png, gif, webp, 5MB) - TenantService.uploadLogo(): 기존 로고 삭제 후 새 로고 저장 - 저장 경로: /storage/tenants/{tenant_id}/logo_{timestamp}.{ext} 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../Controllers/Api/V1/TenantController.php | 8 +++ .../Tenant/TenantLogoUploadRequest.php | 30 ++++++++++ app/Services/TenantService.php | 56 +++++++++++++++++++ lang/ko/message.php | 1 + routes/api.php | 1 + 5 files changed, 96 insertions(+) create mode 100644 app/Http/Requests/Tenant/TenantLogoUploadRequest.php diff --git a/app/Http/Controllers/Api/V1/TenantController.php b/app/Http/Controllers/Api/V1/TenantController.php index 8d48e8c..1843c9a 100644 --- a/app/Http/Controllers/Api/V1/TenantController.php +++ b/app/Http/Controllers/Api/V1/TenantController.php @@ -4,6 +4,7 @@ use App\Helpers\ApiResponse; use App\Http\Controllers\Controller; +use App\Http\Requests\Tenant\TenantLogoUploadRequest; use App\Http\Requests\Tenant\TenantStoreRequest; use App\Http\Requests\Tenant\TenantUpdateRequest; use App\Services\TenantService; @@ -54,4 +55,11 @@ public function restore(Request $request) return $this->service->restoreTenant($request->all()); }, __('message.tenant.restored')); } + + public function uploadLogo(TenantLogoUploadRequest $request) + { + return ApiResponse::handle(function () use ($request) { + return $this->service->uploadLogo($request->file('logo')); + }, __('message.tenant.logo_uploaded')); + } } diff --git a/app/Http/Requests/Tenant/TenantLogoUploadRequest.php b/app/Http/Requests/Tenant/TenantLogoUploadRequest.php new file mode 100644 index 0000000..e7e6c0d --- /dev/null +++ b/app/Http/Requests/Tenant/TenantLogoUploadRequest.php @@ -0,0 +1,30 @@ + 'required|image|mimes:jpeg,png,gif,webp|max:5120', // 5MB + ]; + } + + public function messages(): array + { + return [ + 'logo.required' => __('validation.required', ['attribute' => '로고 이미지']), + 'logo.image' => __('validation.image', ['attribute' => '로고']), + 'logo.mimes' => __('validation.mimes', ['attribute' => '로고', 'values' => 'jpeg, png, gif, webp']), + 'logo.max' => __('validation.max.file', ['attribute' => '로고', 'max' => '5MB']), + ]; + } +} \ No newline at end of file diff --git a/app/Services/TenantService.php b/app/Services/TenantService.php index f5488df..857cef7 100644 --- a/app/Services/TenantService.php +++ b/app/Services/TenantService.php @@ -4,6 +4,8 @@ use App\Models\Members\UserTenant; use App\Models\Tenants\Tenant; +use Illuminate\Http\UploadedFile; +use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Validator; class TenantService @@ -366,4 +368,58 @@ public static function restoreTenant(array $params = []) return 'success'; } + + /** + * 테넌트 로고 업로드 + * + * @param UploadedFile $file 업로드된 로고 이미지 + * @return array{logo_url: string, tenant: Tenant} + */ + public static function uploadLogo(UploadedFile $file): array + { + $tenantId = app('tenant_id'); + + if (! $tenantId) { + throw new \Exception('테넌트 정보를 찾을 수 없습니다.'); + } + + $tenant = Tenant::find($tenantId); + if (! $tenant) { + throw new \Exception('테넌트를 찾을 수 없습니다.'); + } + + // 기존 로고 삭제 + if ($tenant->logo) { + // logo 필드에 저장된 경로가 전체 URL인 경우 처리 + $oldPath = $tenant->logo; + if (str_starts_with($oldPath, '/storage/')) { + $oldPath = str_replace('/storage/', '', $oldPath); + } + if (Storage::disk('public')->exists($oldPath)) { + Storage::disk('public')->delete($oldPath); + } + } + + // 새 로고 저장: /tenants/{tenant_id}/logo_{timestamp}.{ext} + $extension = $file->getClientOriginalExtension(); + $filename = 'logo_'.time().'.'.$extension; + $path = sprintf('tenants/%d/%s', $tenantId, $filename); + + Storage::disk('public')->putFileAs( + dirname($path), + $file, + basename($path) + ); + + // 접근 가능한 URL 생성 + $logoUrl = '/storage/'.$path; + + // DB 업데이트 + $tenant->update(['logo' => $logoUrl]); + + return [ + 'logo_url' => $logoUrl, + 'tenant' => $tenant->fresh(), + ]; + } } diff --git a/lang/ko/message.php b/lang/ko/message.php index 32ed5db..f6b63ce 100644 --- a/lang/ko/message.php +++ b/lang/ko/message.php @@ -170,6 +170,7 @@ 'updated' => '테넌트가 수정되었습니다.', 'deleted' => '테넌트가 삭제되었습니다.', 'restored' => '테넌트가 복구되었습니다.', + 'logo_uploaded' => '회사 로고가 업로드되었습니다.', ], 'tenant_stat_field' => [ diff --git a/routes/api.php b/routes/api.php index f3a301d..2951f2c 100644 --- a/routes/api.php +++ b/routes/api.php @@ -212,6 +212,7 @@ Route::post('/', [TenantController::class, 'store'])->name('v1.tenant.store'); // 테넌트 등록 Route::delete('/', [TenantController::class, 'destroy'])->name('v1.tenant.destroy'); // 테넌트 삭제(탈퇴) Route::put('/restore/{tenant_id}', [TenantController::class, 'restore'])->name('v1.tenant.restore'); // 테넌트 복구 + Route::post('/logo', [TenantController::class, 'uploadLogo'])->name('v1.tenant.upload-logo'); // 로고 업로드 }); // Tenant Statistics Field API