file-system/internal/api/handlers/folder_commands.go
向宁 3a18ca0579 feat: add directory structure and file sharing support
- PostgreSQL metadata overlay layer on top of existing S3 storage
- 3 new tables: folders, files, share_links
- Folder CRUD: create, get with children, tree, rename, delete (cascade)
- File operations: upload to folder, move between folders
- Share links: create with optional password/expiry/download limit, public access
- S3 compensation on PG write failure
- Existing 14 endpoints untouched
2026-05-20 20:26:19 +08:00

107 lines
2.7 KiB
Go

package handlers
import (
"context"
"fmt"
"time"
"rag/file-system/internal/common"
"rag/file-system/internal/domain/model"
"rag/file-system/internal/domain/repository"
"github.com/google/uuid"
)
type CreateFolderCommand struct {
Name string
ParentID *string
OwnerID string
}
type RenameFolderCommand struct {
FolderID string
Name string
OwnerID string
}
type DeleteFolderCommand struct {
FolderID string
OwnerID string
}
type CreateFolderHandler struct {
FolderRepo repository.FolderRepository
}
func NewCreateFolderHandler(folderRepo repository.FolderRepository) *CreateFolderHandler {
return &CreateFolderHandler{FolderRepo: folderRepo}
}
func (h *CreateFolderHandler) Handle(ctx context.Context, cmd CreateFolderCommand) (*model.Folder, error) {
now := time.Now()
folder := &model.Folder{
ID: uuid.New().String(),
ParentID: cmd.ParentID,
Name: cmd.Name,
OwnerID: cmd.OwnerID,
CreatedAt: now,
UpdatedAt: now,
}
if err := h.FolderRepo.Create(ctx, folder); err != nil {
return nil, fmt.Errorf("failed to create folder: %w", err)
}
return folder, nil
}
type RenameFolderHandler struct {
FolderRepo repository.FolderRepository
}
func NewRenameFolderHandler(folderRepo repository.FolderRepository) *RenameFolderHandler {
return &RenameFolderHandler{FolderRepo: folderRepo}
}
func (h *RenameFolderHandler) Handle(ctx context.Context, cmd RenameFolderCommand) (*model.Folder, error) {
folder, err := h.FolderRepo.GetByID(ctx, cmd.FolderID)
if err != nil {
return nil, err
}
if folder == nil || folder.OwnerID != cmd.OwnerID {
return nil, common.NewNotFoundError("目录不存在")
}
folder.Name = cmd.Name
folder.UpdatedAt = time.Now()
if err := h.FolderRepo.Update(ctx, folder); err != nil {
return nil, err
}
return folder, nil
}
type DeleteFolderHandler struct {
FolderRepo repository.FolderRepository
S3Repo repository.FileRepository
}
func NewDeleteFolderHandler(folderRepo repository.FolderRepository, s3Repo repository.FileRepository) *DeleteFolderHandler {
return &DeleteFolderHandler{FolderRepo: folderRepo, S3Repo: s3Repo}
}
func (h *DeleteFolderHandler) Handle(ctx context.Context, cmd DeleteFolderCommand) (string, error) {
files, err := h.FolderRepo.GetDescendantFileS3Keys(ctx, cmd.FolderID, cmd.OwnerID)
if err != nil {
return "", err
}
for _, f := range files {
if delErr := h.S3Repo.DeleteFile(ctx, f.S3Bucket, f.S3Key); delErr != nil {
common.Logger.Error("failed to delete S3 object during folder cleanup",
"s3_key", f.S3Key, "error", delErr)
}
}
if err := h.FolderRepo.Delete(ctx, cmd.FolderID, cmd.OwnerID); err != nil {
return "", err
}
return "目录删除成功", nil
}