orderBy('created_at', 'desc'); // Filter by category if ($request->filled('category')) { $query->where('document_category_id', $request->category); } // Filter by access level if ($request->filled('access_level')) { $query->where('access_level', $request->access_level); } // Filter by status if ($request->filled('status')) { $query->where('status', $request->status); } // Search if ($request->filled('search')) { $search = $request->search; $query->where(function($q) use ($search) { $q->where('title', 'like', "%{$search}%") ->orWhere('document_number', 'like', "%{$search}%") ->orWhere('description', 'like', "%{$search}%"); }); } $documents = $query->paginate(20); $categories = DocumentCategory::orderBy('sort_order')->get(); return view('admin.documents.index', compact('documents', 'categories')); } /** * Show the form for creating a new document */ public function create() { $categories = DocumentCategory::orderBy('sort_order')->get(); return view('admin.documents.create', compact('categories')); } /** * Store a newly created document with initial version */ public function store(Request $request) { $validated = $request->validate([ 'document_category_id' => 'required|exists:document_categories,id', 'title' => 'required|string|max:255', 'document_number' => 'nullable|string|max:255|unique:documents,document_number', 'description' => 'nullable|string', 'access_level' => 'required|in:public,members,admin,board', 'file' => 'required|file|max:10240', // 10MB max 'version_notes' => 'nullable|string', ]); // Upload file $file = $request->file('file'); $path = $file->store('documents', 'private'); // Create document $document = Document::create([ 'document_category_id' => $validated['document_category_id'], 'title' => $validated['title'], 'document_number' => $validated['document_number'] ?? null, 'description' => $validated['description'] ?? null, 'access_level' => $validated['access_level'], 'status' => 'active', 'created_by_user_id' => auth()->id(), 'version_count' => 0, ]); // Add first version $document->addVersion( filePath: $path, originalFilename: $file->getClientOriginalName(), mimeType: $file->getMimeType(), fileSize: $file->getSize(), uploadedBy: auth()->user(), versionNotes: $validated['version_notes'] ?? '初始版本' ); // Audit log AuditLog::create([ 'user_id' => auth()->id(), 'action' => 'document.created', 'description' => "建立文件:{$document->title}", 'ip_address' => request()->ip(), ]); return redirect() ->route('admin.documents.show', $document) ->with('status', '文件已成功建立'); } /** * Display the specified document */ public function show(Document $document) { $document->load(['category', 'versions.uploadedBy', 'createdBy', 'lastUpdatedBy', 'accessLogs.user']); $versionHistory = $document->getVersionHistory(); return view('admin.documents.show', compact('document', 'versionHistory')); } /** * Show the form for editing the document metadata */ public function edit(Document $document) { $categories = DocumentCategory::orderBy('sort_order')->get(); return view('admin.documents.edit', compact('document', 'categories')); } /** * Update the document metadata (not the file) */ public function update(Request $request, Document $document) { $validated = $request->validate([ 'document_category_id' => 'required|exists:document_categories,id', 'title' => 'required|string|max:255', 'document_number' => 'nullable|string|max:255|unique:documents,document_number,' . $document->id, 'description' => 'nullable|string', 'access_level' => 'required|in:public,members,admin,board', ]); $document->update([ ...$validated, 'last_updated_by_user_id' => auth()->id(), ]); // Audit log AuditLog::create([ 'user_id' => auth()->id(), 'action' => 'document.updated', 'description' => "更新文件資訊:{$document->title}", 'ip_address' => request()->ip(), ]); return redirect() ->route('admin.documents.show', $document) ->with('status', '文件資訊已成功更新'); } /** * Upload a new version of the document */ public function uploadNewVersion(Request $request, Document $document) { $validated = $request->validate([ 'file' => 'required|file|max:10240', // 10MB max 'version_notes' => 'required|string', ]); // Upload file $file = $request->file('file'); $path = $file->store('documents', 'private'); // Add new version $version = $document->addVersion( filePath: $path, originalFilename: $file->getClientOriginalName(), mimeType: $file->getMimeType(), fileSize: $file->getSize(), uploadedBy: auth()->user(), versionNotes: $validated['version_notes'] ); // Audit log AuditLog::create([ 'user_id' => auth()->id(), 'action' => 'document.version_uploaded', 'description' => "上傳新版本:{$document->title} (版本 {$version->version_number})", 'ip_address' => request()->ip(), ]); return back()->with('status', "新版本 {$version->version_number} 已成功上傳"); } /** * Promote an old version to current */ public function promoteVersion(Document $document, DocumentVersion $version) { if ($version->document_id !== $document->id) { return back()->with('error', '版本不符合'); } $document->promoteVersion($version, auth()->user()); // Audit log AuditLog::create([ 'user_id' => auth()->id(), 'action' => 'document.version_promoted', 'description' => "提升版本為當前版本:{$document->title} (版本 {$version->version_number})", 'ip_address' => request()->ip(), ]); return back()->with('status', "版本 {$version->version_number} 已設為當前版本"); } /** * Download a specific version */ public function downloadVersion(Document $document, DocumentVersion $version) { if ($version->document_id !== $document->id) { abort(404); } if (!$version->fileExists()) { abort(404, '檔案不存在'); } // Log access $document->logAccess('download', auth()->user()); return Storage::disk('private')->download( $version->file_path, $version->original_filename ); } /** * Archive a document */ public function archive(Document $document) { $document->archive(); // Audit log AuditLog::create([ 'user_id' => auth()->id(), 'action' => 'document.archived', 'description' => "封存文件:{$document->title}", 'ip_address' => request()->ip(), ]); return back()->with('status', '文件已封存'); } /** * Restore an archived document */ public function restore(Document $document) { $document->unarchive(); // Audit log AuditLog::create([ 'user_id' => auth()->id(), 'action' => 'document.restored', 'description' => "恢復文件:{$document->title}", 'ip_address' => request()->ip(), ]); return back()->with('status', '文件已恢復'); } /** * Delete a document permanently */ public function destroy(Document $document) { $title = $document->title; // Delete all version files foreach ($document->versions as $version) { if ($version->fileExists()) { Storage::disk('private')->delete($version->file_path); } } $document->delete(); // Audit log AuditLog::create([ 'user_id' => auth()->id(), 'action' => 'document.deleted', 'description' => "刪除文件:{$title}", 'ip_address' => request()->ip(), ]); return redirect() ->route('admin.documents.index') ->with('status', '文件已永久刪除'); } /** * Display document statistics dashboard */ public function statistics() { // Check if statistics feature is enabled $settings = app(\App\Services\SettingsService::class); if (!$settings->isFeatureEnabled('statistics')) { abort(404, '統計功能未啟用'); } // Check user permission if (!auth()->user()->can('view_document_statistics')) { abort(403, '您沒有檢視文件統計的權限'); } $stats = [ 'total_documents' => Document::where('status', 'active')->count(), 'total_versions' => \App\Models\DocumentVersion::count(), 'total_downloads' => Document::sum('download_count'), 'total_views' => Document::sum('view_count'), 'archived_documents' => Document::where('status', 'archived')->count(), ]; // Documents by category $documentsByCategory = DocumentCategory::withCount(['activeDocuments']) ->orderBy('active_documents_count', 'desc') ->get(); // Most viewed documents $mostViewed = Document::with(['category', 'currentVersion']) ->where('status', 'active') ->orderBy('view_count', 'desc') ->limit(10) ->get(); // Most downloaded documents $mostDownloaded = Document::with(['category', 'currentVersion']) ->where('status', 'active') ->orderBy('download_count', 'desc') ->limit(10) ->get(); // Recent activity (last 30 days) $recentActivity = \App\Models\DocumentAccessLog::with(['user', 'document']) ->where('accessed_at', '>=', now()->subDays(30)) ->latest('accessed_at') ->limit(50) ->get(); // Monthly upload trends (last 6 months) $uploadTrends = Document::selectRaw("strftime('%Y-%m', created_at) as month, COUNT(*) as count") ->where('created_at', '>=', now()->subMonths(6)) ->groupBy('month') ->orderBy('month', 'desc') ->get(); // Access level distribution $accessLevelStats = Document::selectRaw('access_level, COUNT(*) as count') ->where('status', 'active') ->groupBy('access_level') ->get(); return view('admin.documents.statistics', compact( 'stats', 'documentsByCategory', 'mostViewed', 'mostDownloaded', 'recentActivity', 'uploadTrends', 'accessLevelStats' )); } }