'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', ]; /** * 문서 타입 목록 * - 계약서는 모두의싸인을 통해 별도 처리 */ public const DOCUMENT_TYPES = [ 'resident_copy' => '등본사본', 'bank_account' => '통장사본', ]; /** * 사용자 (영업파트너) */ public function user(): BelongsTo { return $this->belongsTo(User::class); } /** * 업로더 */ public function uploader(): BelongsTo { return $this->belongsTo(User::class, 'uploaded_by'); } /** * 문서 타입 라벨 */ public function getDocumentTypeLabelAttribute(): string { return self::DOCUMENT_TYPES[$this->document_type] ?? $this->document_type; } /** * 파일 크기 포맷팅 */ public function getFormattedSizeAttribute(): string { $bytes = $this->file_size; if ($bytes >= 1073741824) { return number_format($bytes / 1073741824, 2) . ' GB'; } elseif ($bytes >= 1048576) { return number_format($bytes / 1048576, 2) . ' MB'; } elseif ($bytes >= 1024) { return number_format($bytes / 1024, 2) . ' KB'; } return $bytes . ' bytes'; } /** * 파일 확장자 */ public function getExtensionAttribute(): string { return pathinfo($this->original_name, PATHINFO_EXTENSION); } /** * 이미지 여부 */ public function isImage(): bool { return str_starts_with($this->mime_type ?? '', 'image/'); } /** * 스토리지 전체 경로 */ public function getStoragePath(): string { return Storage::disk('tenant')->path($this->file_path); } /** * 파일 존재 여부 */ public function existsInStorage(): bool { return Storage::disk('tenant')->exists($this->file_path); } /** * 파일 URL */ public function getUrl(): ?string { if (!$this->existsInStorage()) { return null; } return Storage::disk('tenant')->url($this->file_path); } /** * 파일 다운로드 응답 */ public function download() { if (!$this->existsInStorage()) { abort(404, '파일을 찾을 수 없습니다.'); } return Storage::disk('tenant')->download($this->file_path, $this->original_name); } }