erp-backend/app/Http/Controllers/FileController.php
2026-04-01 17:07:04 +08:00

535 lines
16 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Http\Requests\FileUploadRequest;
use App\Models\File;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class FileController extends Controller
{
/**
* 文件列表
*/
public function index(Request $request)
{
$query = File::with('user')->orderBy('created_at', 'desc');
// 筛选条件
if ($request->filled('user_id')) {
$query->where('user_id', $request->user_id);
}
if ($request->filled('module')) {
$query->where('module', $request->module);
}
if ($request->filled('purpose')) {
$query->where('purpose', $request->purpose);
}
if ($request->filled('mime_type')) {
$query->where('mime_type', 'like', "%{$request->mime_type}%");
}
if ($request->filled('original_name')) {
$query->where('original_name', 'like', "%{$request->original_name}%");
}
if ($request->filled('status')) {
$query->where('status', $request->status);
}
if ($request->filled('date_range')) {
$dates = explode(',', $request->date_range);
if (count($dates) === 2) {
$query->whereBetween('created_at', [$dates[0], $dates[1]]);
}
}
if ($request->filled('keyword')) {
$keyword = $request->keyword;
$query->where(function ($q) use ($keyword) {
$q->where('original_name', 'like', "%{$keyword}%")
->orWhere('description', 'like', "%{$keyword}%")
->orWhere('module', 'like', "%{$keyword}%")
->orWhere('purpose', 'like', "%{$keyword}%");
});
}
$perPage = $request->input('limit', 20);
$files = $query->paginate($perPage);
// 添加额外信息
$files->getCollection()->transform(function ($file) {
$file->url = $file->getUrl();
$file->size_formatted = $file->size_formatted;
$file->icon = $file->icon;
$file->exists = $file->exists();
return $file;
});
return response()->json([
'code' => 200,
'data' => [
'list' => $files->items(),
'total' => $files->total(),
'current_page' => $files->currentPage(),
'last_page' => $files->lastPage(),
],
'message' => 'success'
]);
}
/**
* 文件详情
*/
public function show(string $id)
{
$file = File::with('user')->find($id);
if (!$file) {
return response()->json([
'code' => 404,
'message' => '文件不存在'
], 404);
}
$file->url = $file->getUrl();
$file->size_formatted = $file->size_formatted;
$file->icon = $file->icon;
$file->exists = $file->exists();
return response()->json([
'code' => 200,
'data' => $file,
'message' => 'success'
]);
}
/**
* 上传文件
*/
public function upload(FileUploadRequest $request)
{
try {
$file = $request->file('file');
$module = $request->input('module', 'default');
$purpose = $request->input('purpose', 'upload');
$description = $request->input('description');
// 生成存储文件名
$originalName = $file->getClientOriginalName();
$extension = $file->getClientOriginalExtension();
$mimeType = $file->getMimeType();
$size = $file->getSize();
$storageName = Str::uuid() . '.' . $extension;
$path = 'uploads/' . date('Y/m/d');
// 存储文件
$disk = config('filesystems.default', 'public');
$filePath = $file->storeAs($path, $storageName, $disk);
// 创建文件记录
$fileRecord = File::create([
'user_id' => auth()->id(),
'original_name' => $originalName,
'storage_name' => $storageName,
'path' => $path,
'url' => Storage::disk($disk)->url($filePath),
'mime_type' => $mimeType,
'size' => $size,
'extension' => $extension,
'disk' => $disk,
'module' => $module,
'purpose' => $purpose,
'description' => $description,
'status' => 'active',
]);
$fileRecord->url = $fileRecord->getUrl();
$fileRecord->size_formatted = $fileRecord->size_formatted;
$fileRecord->icon = $fileRecord->icon;
return response()->json([
'code' => 200,
'data' => $fileRecord,
'message' => '文件上传成功'
]);
} catch (\Exception $e) {
return response()->json([
'code' => 500,
'message' => '文件上传失败: ' . $e->getMessage()
], 500);
}
}
/**
* 批量上传文件
*/
public function batchUpload(Request $request)
{
$request->validate([
'files' => 'required|array|min:1|max:10',
'files.*' => 'file|max:10240', // 10MB per file
'module' => 'nullable|string|max:50',
'purpose' => 'nullable|string|max:50',
]);
$uploadedFiles = [];
$failedFiles = [];
foreach ($request->file('files') as $file) {
try {
$originalName = $file->getClientOriginalName();
$extension = $file->getClientOriginalExtension();
$storageName = Str::uuid() . '.' . $extension;
$path = 'uploads/' . date('Y/m/d');
$disk = config('filesystems.default', 'public');
$filePath = $file->storeAs($path, $storageName, $disk);
$fileRecord = File::create([
'user_id' => auth()->id(),
'original_name' => $originalName,
'storage_name' => $storageName,
'path' => $path,
'url' => Storage::disk($disk)->url($filePath),
'mime_type' => $file->getMimeType(),
'size' => $file->getSize(),
'extension' => $extension,
'disk' => $disk,
'module' => $request->input('module', 'default'),
'purpose' => $request->input('purpose', 'upload'),
'status' => 'active',
]);
$uploadedFiles[] = $fileRecord;
} catch (\Exception $e) {
$failedFiles[] = [
'name' => $originalName ?? '未知文件',
'error' => $e->getMessage(),
];
}
}
return response()->json([
'code' => 200,
'data' => [
'uploaded_count' => count($uploadedFiles),
'failed_count' => count($failedFiles),
'uploaded_files' => $uploadedFiles,
'failed_files' => $failedFiles,
],
'message' => "批量上传完成,成功 {$uploadedCount} 个,失败 {$failedCount}"
]);
}
/**
* 更新文件信息
*/
public function update(Request $request, string $id)
{
$file = File::find($id);
if (!$file) {
return response()->json([
'code' => 404,
'message' => '文件不存在'
], 404);
}
$request->validate([
'original_name' => 'nullable|string|max:255',
'description' => 'nullable|string|max:500',
'module' => 'nullable|string|max:50',
'purpose' => 'nullable|string|max:50',
'status' => 'nullable|string|in:active,inactive,deleted',
]);
try {
$file->update($request->only([
'original_name', 'description', 'module', 'purpose', 'status'
]));
$file->url = $file->getUrl();
$file->size_formatted = $file->size_formatted;
$file->icon = $file->icon;
return response()->json([
'code' => 200,
'data' => $file,
'message' => '文件信息更新成功'
]);
} catch (\Exception $e) {
return response()->json([
'code' => 500,
'message' => '更新失败: ' . $e->getMessage()
], 500);
}
}
/**
* 删除文件
*/
public function destroy(string $id)
{
$file = File::find($id);
if (!$file) {
return response()->json([
'code' => 404,
'message' => '文件不存在'
], 404);
}
try {
// 删除物理文件
$file->deletePhysicalFile();
// 删除数据库记录
$file->delete();
return response()->json([
'code' => 200,
'message' => '文件删除成功'
]);
} catch (\Exception $e) {
return response()->json([
'code' => 500,
'message' => '删除失败: ' . $e->getMessage()
], 500);
}
}
/**
* 批量删除文件
*/
public function batchDelete(Request $request)
{
$request->validate([
'file_ids' => 'required|array|min:1',
'file_ids.*' => 'integer|exists:files,id',
]);
$successCount = 0;
$failedFiles = [];
try {
foreach ($request->file_ids as $fileId) {
$file = File::find($fileId);
if (!$file) {
$failedFiles[] = ['id' => $fileId, 'reason' => '文件不存在'];
continue;
}
try {
$file->deletePhysicalFile();
$file->delete();
$successCount++;
} catch (\Exception $e) {
$failedFiles[] = [
'id' => $fileId,
'name' => $file->original_name,
'reason' => $e->getMessage(),
];
}
}
return response()->json([
'code' => 200,
'data' => [
'success_count' => $successCount,
'failed_files' => $failedFiles,
],
'message' => "批量删除成功,成功 {$successCount}"
]);
} catch (\Exception $e) {
return response()->json([
'code' => 500,
'message' => '批量删除失败: ' . $e->getMessage()
], 500);
}
}
/**
* 下载文件
*/
public function download(string $id)
{
$file = File::find($id);
if (!$file) {
return response()->json([
'code' => 404,
'message' => '文件不存在'
], 404);
}
if (!$file->exists()) {
return response()->json([
'code' => 404,
'message' => '物理文件不存在'
], 404);
}
try {
$path = $file->getFullPathAttribute();
$disk = $file->disk;
return Storage::disk($disk)->download($path, $file->original_name);
} catch (\Exception $e) {
return response()->json([
'code' => 500,
'message' => '文件下载失败: ' . $e->getMessage()
], 500);
}
}
/**
* 预览文件
*/
public function preview(string $id)
{
$file = File::find($id);
if (!$file) {
return response()->json([
'code' => 404,
'message' => '文件不存在'
], 404);
}
if (!$file->exists()) {
return response()->json([
'code' => 404,
'message' => '物理文件不存在'
], 404);
}
// 检查文件类型是否支持预览
$previewableTypes = [
'image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/svg+xml', 'image/webp',
'application/pdf',
'text/plain', 'text/html', 'text/css', 'text/javascript',
'application/json', 'application/xml',
];
if (!in_array($file->mime_type, $previewableTypes)) {
return response()->json([
'code' => 400,
'message' => '该文件类型不支持预览'
], 400);
}
try {
$path = $file->getFullPathAttribute();
$disk = $file->disk;
$content = Storage::disk($disk)->get($path);
return response($content)
->header('Content-Type', $file->mime_type)
->header('Content-Disposition', 'inline; filename="' . $file->original_name . '"');
} catch (\Exception $e) {
return response()->json([
'code' => 500,
'message' => '文件预览失败: ' . $e->getMessage()
], 500);
}
}
/**
* 获取文件统计
*/
public function statistics(Request $request)
{
$userId = $request->input('user_id');
$stats = File::getStatistics($userId);
return response()->json([
'code' => 200,
'data' => $stats,
'message' => 'success'
]);
}
/**
* 获取模块列表
*/
public function getModules()
{
$modules = File::select('module')
->distinct()
->orderBy('module', 'asc')
->pluck('module');
return response()->json([
'code' => 200,
'data' => $modules,
'message' => 'success'
]);
}
/**
* 清理过期文件
*/
public function cleanup(Request $request)
{
$request->validate([
'days' => 'nullable|integer|min:1|max:365',
]);
$days = $request->input('days', 30);
$result = File::cleanupExpired($days);
return response()->json([
'code' => 200,
'data' => $result,
'message' => "清理完成,删除 {$result['deleted_count']} 个文件"
]);
}
/**
* 获取上传配置
*/
public function getUploadConfig()
{
$config = [
'max_size' => 10240, // 10MB in KB
'allowed_types' => [
'image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/svg+xml', 'image/webp',
'application/pdf',
'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'text/plain', 'text/csv',
'application/zip', 'application/x-rar-compressed', 'application/x-7z-compressed',
'application/json', 'application/xml',
],
'allowed_extensions' => [
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp',
'pdf',
'doc', 'docx',
'xls', 'xlsx',
'ppt', 'pptx',
'txt', 'csv',
'zip', 'rar', '7z',
'json', 'xml',
],
'max_files_per_request' => 10,
];
return response()->json([
'code' => 200,
'data' => $config,
'message' => 'success'
]);
}
}