'boolean', 'uploaded_at' => 'datetime', ]; // Versions are immutable - disable updating protected static function booted() { static::updating(function ($version) { // Only allow updating is_current flag $dirty = $version->getDirty(); if (count($dirty) > 1 || !isset($dirty['is_current'])) { throw new \Exception('Document versions are immutable and cannot be modified'); } }); } // ==================== Relationships ==================== /** * Get the document this version belongs to */ public function document() { return $this->belongsTo(Document::class); } /** * Get the user who uploaded this version */ public function uploadedBy() { return $this->belongsTo(User::class, 'uploaded_by_user_id'); } // ==================== File Methods ==================== /** * Get the full file path */ public function getFullPath(): string { return storage_path('app/' . $this->file_path); } /** * Check if file exists */ public function fileExists(): bool { return Storage::exists($this->file_path); } /** * Get file download URL */ public function getDownloadUrl(): string { return route('admin.documents.download-version', [ 'document' => $this->document_id, 'version' => $this->id, ]); } /** * Verify file integrity */ public function verifyIntegrity(): bool { if (!$this->fileExists()) { return false; } $currentHash = hash_file('sha256', $this->getFullPath()); return $currentHash === $this->file_hash; } // ==================== Helper Methods ==================== /** * Get human-readable file size */ public function getFileSizeHuman(): string { $bytes = $this->file_size; $units = ['B', 'KB', 'MB', 'GB']; for ($i = 0; $bytes > 1024; $i++) { $bytes /= 1024; } return round($bytes, 2) . ' ' . $units[$i]; } /** * Get file extension */ public function getFileExtension(): string { return pathinfo($this->original_filename, PATHINFO_EXTENSION); } /** * Get file icon based on mime type */ public function getFileIcon(): string { return match(true) { str_contains($this->mime_type, 'pdf') => '📄', str_contains($this->mime_type, 'word') || str_contains($this->mime_type, 'document') => '📝', str_contains($this->mime_type, 'sheet') || str_contains($this->mime_type, 'excel') => '📊', str_contains($this->mime_type, 'image') => '🖼️', str_contains($this->mime_type, 'zip') || str_contains($this->mime_type, 'compressed') => '🗜️', default => '📎', }; } /** * Check if version is current */ public function isCurrent(): bool { return $this->is_current; } /** * Get version badge class for UI */ public function getBadgeClass(): string { return $this->is_current ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'; } /** * Get version badge text */ public function getBadgeText(): string { return $this->is_current ? '當前版本' : '歷史版本'; } }