From 00a0e583a8a0f2bd75228e5896f3c027ac10cda6 Mon Sep 17 00:00:00 2001 From: root <1772105645@qq.com> Date: Fri, 19 Dec 2025 16:40:09 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AD=98=E5=82=A8=E6=A1=B6?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 DeleteBucketHandler 处理存储桶删除请求 - 添加 DELETE /buckets API 端点 - 在前端界面添加删除存储桶按钮功能 - 添加存储桶删除请求验证器 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- cmd/server/main.go | 3 ++ internal/api/endpoints/bucket_endpoint.go | 38 ++++++++++++++++++++ internal/api/handlers/bucket_commands.go | 4 +++ internal/api/handlers/bucket_handlers.go | 16 +++++++++ internal/api/handlers/query_handlers.go | 3 +- internal/api/requests/bucket_requests.go | 4 +++ internal/api/validators/bucket_validators.go | 7 ++++ web/index.html | 38 +++++++++++++++----- 8 files changed, 102 insertions(+), 11 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 25b634a..e22dce3 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -35,6 +35,7 @@ func main() { downloadHandler := handlers.NewDownloadFileHandler(s3Repo) createBucketHandler := handlers.NewCreateBucketHandler(s3Repo) listBucketsHandler := handlers.NewListBucketsHandler(s3Repo) + deleteBucketHandler := handlers.NewDeleteBucketHandler(s3Repo) // New Handlers listFilesHandler := handlers.NewListFilesHandler(s3Repo) previewHandler := handlers.NewGetFilePreviewHandler(s3Repo) @@ -48,6 +49,7 @@ func main() { mediator.Register[handlers.DownloadFileQuery, io.ReadCloser](m, downloadHandler) mediator.Register[handlers.CreateBucketCommand, string](m, createBucketHandler) mediator.Register[handlers.ListBucketsQuery, []string](m, listBucketsHandler) + mediator.Register[handlers.DeleteBucketCommand, string](m, deleteBucketHandler) // New Registrations mediator.Register[handlers.ListFilesQuery, *repository.ListFilesResult](m, listFilesHandler) mediator.Register[handlers.GetFilePreviewQuery, string](m, previewHandler) @@ -100,6 +102,7 @@ func main() { // Bucket operations r.POST("/buckets", bucketEndpoint.CreateBucket) r.GET("/buckets", bucketEndpoint.ListBuckets) + r.DELETE("/buckets", bucketEndpoint.DeleteBucket) // Swagger r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) diff --git a/internal/api/endpoints/bucket_endpoint.go b/internal/api/endpoints/bucket_endpoint.go index 428ed79..09671dd 100644 --- a/internal/api/endpoints/bucket_endpoint.go +++ b/internal/api/endpoints/bucket_endpoint.go @@ -79,3 +79,41 @@ func (e *BucketEndpoint) ListBuckets(c *gin.Context) { } c.JSON(http.StatusOK, gin.H{"buckets": result}) } + +// DeleteBucket godoc +// @Summary 删除存储桶 +// @Description 删除指定的 S3 存储桶(桶必须为空) +// @Tags 存储桶管理 +// @Accept json +// @Produce json +// @Param request body requests.DeleteBucketRequest true "删除存储桶请求参数" +// @Success 200 {object} map[string]string "删除成功消息" +// @Failure 400 {object} map[string]string "参数错误" +// @Failure 500 {object} map[string]string "服务器内部错误" +// @Router /buckets [delete] +func (e *BucketEndpoint) DeleteBucket(c *gin.Context) { + var req requests.DeleteBucketRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if err := e.CreateBucketValidator.ValidateDelete(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + cmd := handlers.DeleteBucketCommand{BucketName: req.BucketName} + + result, err := mediator.Send[handlers.DeleteBucketCommand, string](e.Mediator, c.Request.Context(), cmd) + if err != nil { + if be, ok := err.(*common.BusinessException); ok { + c.JSON(be.Code, gin.H{"error": be.Message}) + } else { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + } + return + } + + c.JSON(http.StatusOK, gin.H{"message": result}) +} diff --git a/internal/api/handlers/bucket_commands.go b/internal/api/handlers/bucket_commands.go index 94df480..05f7408 100644 --- a/internal/api/handlers/bucket_commands.go +++ b/internal/api/handlers/bucket_commands.go @@ -5,3 +5,7 @@ type CreateBucketCommand struct { } type ListBucketsQuery struct{} + +type DeleteBucketCommand struct { + BucketName string +} diff --git a/internal/api/handlers/bucket_handlers.go b/internal/api/handlers/bucket_handlers.go index 04e7f70..a4f7e7d 100644 --- a/internal/api/handlers/bucket_handlers.go +++ b/internal/api/handlers/bucket_handlers.go @@ -32,3 +32,19 @@ func NewListBucketsHandler(repo repository.FileRepository) *ListBucketsHandler { func (h *ListBucketsHandler) Handle(ctx context.Context, query ListBucketsQuery) ([]string, error) { return h.Repo.ListBuckets(ctx) } + +type DeleteBucketHandler struct { + Repo repository.FileRepository +} + +func NewDeleteBucketHandler(repo repository.FileRepository) *DeleteBucketHandler { + return &DeleteBucketHandler{Repo: repo} +} + +func (h *DeleteBucketHandler) Handle(ctx context.Context, cmd DeleteBucketCommand) (string, error) { + err := h.Repo.DeleteBucket(ctx, cmd.BucketName) + if err != nil { + return "", err + } + return "Bucket deleted successfully", nil +} diff --git a/internal/api/handlers/query_handlers.go b/internal/api/handlers/query_handlers.go index b622262..c50549e 100644 --- a/internal/api/handlers/query_handlers.go +++ b/internal/api/handlers/query_handlers.go @@ -3,10 +3,9 @@ package handlers import ( "context" "file-system/internal/common" - "file-sys "file-system/internal/domain/repository" "io" - "file-system/internal/common" + "time" ) // Queries & Commands diff --git a/internal/api/requests/bucket_requests.go b/internal/api/requests/bucket_requests.go index a9fe3c4..781627e 100644 --- a/internal/api/requests/bucket_requests.go +++ b/internal/api/requests/bucket_requests.go @@ -5,3 +5,7 @@ type CreateBucketRequest struct { } type ListBucketsRequest struct{} + +type DeleteBucketRequest struct { + BucketName string `json:"bucket_name"` +} diff --git a/internal/api/validators/bucket_validators.go b/internal/api/validators/bucket_validators.go index bc3fd99..8fec143 100644 --- a/internal/api/validators/bucket_validators.go +++ b/internal/api/validators/bucket_validators.go @@ -17,3 +17,10 @@ func (v *CreateBucketValidator) Validate(req *requests.CreateBucketRequest) erro } return nil } + +func (v *CreateBucketValidator) ValidateDelete(req *requests.DeleteBucketRequest) error { + if req.BucketName == "" { + return common.NewBusinessException("Bucket name cannot be empty") + } + return nil +} diff --git a/web/index.html b/web/index.html index da0db22..c83fc15 100644 --- a/web/index.html +++ b/web/index.html @@ -47,13 +47,16 @@
- - {{ bucket }} - +
暂无存储桶
@@ -496,7 +499,7 @@ window.open(url, '_blank'); }; - // Delete + // Delete File const deleteFile = async (key) => { if (!confirm(`确定要删除文件 "${key}" 吗?此操作不可恢复!`)) return; try { @@ -509,6 +512,23 @@ } }; + // Delete Bucket + const deleteBucket = async (bucketName) => { + if (!confirm(`确定要删除存储桶 "${bucketName}" 吗?\n注意:存储桶必须为空才能删除!`)) return; + try { + await api.delete('/buckets', { + data: { bucket_name: bucketName } + }); + if (currentBucket.value === bucketName) { + currentBucket.value = null; + files.value = []; + } + await loadBuckets(); + } catch (err) { + alert('删除存储桶失败: ' + (err.response?.data?.error || err.message)); + } + }; + // Utils const formatSize = (bytes) => { if (bytes === 0) return '0 B'; @@ -544,7 +564,7 @@ return { buckets, currentBucket, files, loadingFiles, nextToken, pageHistory, filters, showCreateBucketModal, newBucketName, uploads, previewUrl, isPreviewImage, isPreviewVideo, - loadBuckets, createBucket, selectBucket, refreshFiles: () => loadFilesInitial(), + loadBuckets, createBucket, selectBucket, deleteBucket, refreshFiles: () => loadFilesInitial(), nextPage: nextP, prevPage, triggerFileInput, handleFileSelect, previewFile, downloadFile, deleteFile,