Browse Source

文件夹上传

moell 10 months ago
parent
commit
8579d6c84c

+ 8 - 0
app/Exceptions/ValidationException.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace App\Exceptions;
+
+class ValidationException extends \Exception
+{
+
+}

+ 16 - 0
app/Http/Controllers/API/FileController.php

@@ -5,11 +5,13 @@ namespace App\Http\Controllers\API;
 use App\Http\Controllers\Controller;
 use App\Http\Requests\API\File\DownloadZipRequest;
 use App\Http\Requests\API\File\FileUploadRequest;
+use App\Http\Requests\API\File\KeepDirectoryUploadRequest;
 use App\Http\Resources\API\FileByObjectResource;
 use App\Http\Resources\API\FileDownloadResource;
 use App\Http\Resources\API\FileUploadSuccessResource;
 use App\Models\Enums\FileObjectType;
 use App\Models\File;
+use App\Services\File\Upload\KeepDirectoryUploadService;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\Storage;
@@ -167,6 +169,20 @@ class FileController extends Controller
 
     }
 
+
+    /**
+     * 文件夹上传
+     *
+     * @param KeepDirectoryUploadRequest $request
+     * @return \Illuminate\Http\Response
+     */
+    public function keepDirectoryUpload(KeepDirectoryUploadRequest $request)
+    {
+        (new KeepDirectoryUploadService($request))->upload();
+
+        return $this->created();
+    }
+
     public function byObject(string $objectType, string $objectId)
     {
         $fileObjectType = FileObjectType::from($objectType);

+ 43 - 0
app/Http/Requests/API/File/KeepDirectoryUploadRequest.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace App\Http\Requests\API\File;
+
+use App\Models\Enums\FileObjectType;
+use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Validation\Rules\Enum;
+use Illuminate\Validation\Rules\File;
+
+class KeepDirectoryUploadRequest extends FormRequest
+{
+    /**
+     * Determine if the user is authorized to make this request.
+     */
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
+     */
+    public function rules(): array
+    {
+        $rules =  [
+            "files" => "required|array",
+            "folders" => "required|array",
+            "object_type" => [
+                "required",
+                new Enum(FileObjectType::class),
+            ],
+            "object_id" => "required|min:0",
+            "names" => "array",
+            "folder_id" => "min:0"
+        ];
+
+        $rules['files.*'][] = File::types(['txt', 'jpeg', 'png', 'gif', 'pdf', 'xls', 'xlsx', 'zip', 'wps', 'docx', 'doc'])->max("2gb");
+
+        return $rules;
+    }
+}

+ 3 - 0
app/Models/Enums/FileObjectType.php

@@ -4,6 +4,7 @@ namespace App\Models\Enums;
 
 use App\Models\Action;
 use App\Models\Asset;
+use App\Models\Container;
 use App\Models\Plan;
 use App\Models\Project;
 use App\Models\Requirement;
@@ -34,6 +35,7 @@ enum FileObjectType: string
             self::REQUIREMENT => Requirement::query(),
             self::ACTION => Action::query(),
             self::PLAN => Plan::query(),
+            self::CONTAINER => Container::query(),
         };
     }
 
@@ -46,6 +48,7 @@ enum FileObjectType: string
             self::REQUIREMENT => Requirement::query(),
             self::ACTION => Action::query(),
             self::PLAN => Plan::query(),
+            self::CONTAINER => Container::query()->allowed($id),
         };
     }
 }

+ 96 - 0
app/Services/File/Upload/FilesUploadTrait.php

@@ -0,0 +1,96 @@
+<?php
+
+namespace App\Services\File\Upload;
+
+use App\Http\Resources\API\FileUploadSuccessResource;
+use App\Models\Enums\FileObjectType;
+use App\Models\File;
+use Illuminate\Http\Request;
+use Illuminate\Http\UploadedFile;
+use Illuminate\Support\Facades\Auth;
+
+trait FilesUploadTrait
+{
+    protected function checkRequestData(Request $request): int
+    {
+        $fileObjectType = FileObjectType::from($request->object_type);
+
+        $fileObjectType->modelBuilderAllowed($request->object_id)->findOrFail($request->object_id);
+
+        $filesSize = 0;
+        foreach ($request->file("files") as $file) {
+            throw_validation_if(! $file->isValid(), "File upload failed.");
+
+            $filesSize += $file->getSize();
+        }
+
+        throw_validation_if(
+            $filesSize + Auth::user()->company->used_storage_capacity > Auth::user()->company->storage_size,
+            "Storage capacity is insufficient, please contact the administrator."
+        );
+
+        return $filesSize;
+    }
+
+    protected function uploadFile(Request $request, UploadedFile $file): array
+    {
+        $pathname = $file->storeAs(
+            sprintf("c%s/%s/%s", Auth::user()->company_id, $request->get("object_type"), date("Ymd")),
+            sprintf("%s.%s", md5(uniqid()), $file->extension())
+        );
+
+        throw_validation_if(! $pathname, "File upload failed.");
+
+        return [
+            'pathname' => $pathname,
+            'title' => $file->getClientOriginalName(),
+            'size' => $file->getSize(),
+            'extension' => $file->extension(),
+            'object_type' => $request->object_type,
+            'object_id' => $request->object_id,
+            'created_by' => Auth::id(),
+            'company_id' => Auth::user()->company_id,
+            'source' => $request->get("source", 1),
+        ];
+    }
+
+    public function storeFiles(array $items)
+    {
+        $uploadedFiles = [];
+        foreach ($items as $item) {
+            if ($item['object_id'] && $item['source'] == 1) {
+                $version = File::query()
+                    ->where('object_type', $item['object_type'])
+                    ->where('object_id', $item['object_id'])
+                    ->where("title", $item['title'])
+                    ->where("folder_id", $item['folder_id'] ?? 0)
+                    ->count();
+                $item['version'] = $version + 1;
+                $item['is_latest_version'] = 1;
+
+                File::query()
+                    ->where('object_type', $item['object_type'])
+                    ->where('object_id', $item['object_id'])
+                    ->where("title", $item['title'])
+                    ->where("folder_id", $item['folder_id'] ?? 0)
+                    ->where("is_latest_version", 1)
+                    ->update([
+                        'is_latest_version' => 0
+                    ]);
+            }
+
+            $file = File::query()->create($item);
+
+            $uploadedFiles[] = new FileUploadSuccessResource($file);
+        }
+
+        return $uploadedFiles;
+    }
+
+    protected function updateUsedStorageCapacity(int $filesSize)
+    {
+        $company = Auth::user()->company;
+        $company->increment("used_storage_capacity", $filesSize);
+        $company->save();
+    }
+}

+ 107 - 0
app/Services/File/Upload/KeepDirectoryUploadService.php

@@ -0,0 +1,107 @@
+<?php
+
+namespace App\Services\File\Upload;
+
+use App\Models\Folder;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+
+class KeepDirectoryUploadService
+{
+    use FilesUploadTrait;
+
+    protected array $objectWhere = [];
+
+    protected ?Folder $parentFolder = null;
+
+    protected int $filesSize = 0;
+
+    public function __construct(protected Request $request)
+    {
+        $this->checkFormData();
+
+        $this->objectWhere = [
+            'object_id' => $this->request->get("object_id"),
+            'object_type' => $this->request->get("object_type"),
+        ];
+
+
+        $this->parentFolder = $this->request->get("folder_id")
+            ? Folder::query()->where($this->objectWhere)->findOrFail($this->request->get("folder_id"))
+            : null;
+    }
+
+    public function upload(): array
+    {
+        $folderRelations = $this->buildFolder();
+
+        $items = [];
+        $fileNames = $this->request->get("file_names", []);
+        foreach ($this->request->file("files") as $index => $file) {
+
+            $item = $this->uploadFile($this->request, $file);
+            $item['folder_id'] = $folderRelations[$index] ?? 0;
+            $item['title'] = $fileNames[$index] ?? $item['title'];
+
+            $items[] = $item;
+        }
+
+        $uploadedFiles = $this->storeFiles($items);
+
+        $this->updateUsedStorageCapacity($this->filesSize);
+
+        return $uploadedFiles;
+    }
+
+    protected function buildFolder(): array
+    {
+        $folderRelations = [];
+
+        foreach ($this->request->folders as $index => $folder) {
+            $parentFolder = $this->parentFolder;
+
+            $folderPathArr = array_filter(explode('/', $folder));
+
+            if (! $folderPathArr) {
+                $folderRelations[$index] = $parentFolder?->id ?? 0;
+            }
+
+            foreach ($folderPathArr as $folderName) {
+                $parentId = $parentFolder?->id ?? 0;
+
+                $folder = Folder::query()
+                    ->where($this->objectWhere)
+                    ->where("name", $folderName)
+                    ->where("parent_id", $parentId)
+                    ->first();
+                if (! $folder) {
+                    $folder = Folder::query()->create([
+                        'company_id' => Auth::user()->company_id,
+                        ...$this->objectWhere,
+                        'parent_id' => $parentId,
+                        'name' => $folderName
+                    ]);
+
+                    $folder->path = $parentFolder ? $parentFolder?->path . $folder->id . "," : sprintf(",%s,", $folder->id);
+                    $folder->save();
+                }
+
+                $parentFolder = $folder;
+
+                $folderRelations[$index] = $folder->id;
+            }
+        }
+
+        return $folderRelations;
+    }
+
+    protected function checkFormData()
+    {
+        throw_validation_if(
+            count($this->request->file("files")) != count($this->request->folders),
+            "File and directory do not match"
+        );
+
+        $this->filesSize = $this->checkRequestData($this->request);
+    }
+}

+ 6 - 0
app/helpers.php

@@ -48,6 +48,12 @@ if (!function_exists('text_diff')) {
     }
 }
 
+if (!function_exists('throw_validation_if')) {
+    function throw_validation_if($condition, ...$parameters) {
+        return throw_if($condition, \App\Exceptions\ValidationException::class, ...$parameters);
+    }
+}
+
 
 
 

+ 1 - 0
routes/api.php

@@ -158,6 +158,7 @@ Route::middleware(['auth:sanctum'])->group(function () {
         Route::post("file/download-zip", [API\FileController::class, "downloadZip"])->name("file.download-zip");
         Route::patch("file/{file}/change-name", [API\FileController::class, "changeName"])->name("file.change-name");
         Route::delete("file/{file}/destroy", [API\FileController::class, "destroy"])->name("file.destroy");
+        Route::post("file/keep-directory-upload", [API\FileController::class, "keepDirectoryUpload"])->name("file.keep-directory-upload");
 
         Route::get("notification", [API\NotificationController::class, "index"])->name("notification.index");
         Route::get("notification/unread", [API\NotificationController::class, "unread"])->name("notification.unread");