package handlers import ( "context" "fmt" "io" "time" "rag/file-system/internal/common" "rag/file-system/internal/domain/model" "rag/file-system/internal/domain/repository" "github.com/google/uuid" ) type UploadToFolderCommand struct { FolderID string FileName string Data io.Reader S3Bucket string OwnerID string } type MoveFileCommand struct { FileID string TargetFolderID string OwnerID string } type UploadToFolderHandler struct { FileMetaRepo repository.FileMetaRepository FolderRepo repository.FolderRepository S3Repo repository.FileRepository } func NewUploadToFolderHandler( fileMetaRepo repository.FileMetaRepository, folderRepo repository.FolderRepository, s3Repo repository.FileRepository, ) *UploadToFolderHandler { return &UploadToFolderHandler{ FileMetaRepo: fileMetaRepo, FolderRepo: folderRepo, S3Repo: s3Repo, } } func (h *UploadToFolderHandler) Handle(ctx context.Context, cmd UploadToFolderCommand) (*model.FileMeta, 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("目录不存在") } s3Key := uuid.New().String() if err := h.S3Repo.UploadFile(ctx, cmd.S3Bucket, s3Key, cmd.Data); err != nil { return nil, fmt.Errorf("S3 upload failed: %w", err) } now := time.Now() fileMeta := &model.FileMeta{ ID: uuid.New().String(), FolderID: cmd.FolderID, Name: cmd.FileName, S3Key: s3Key, S3Bucket: cmd.S3Bucket, ContentType: "application/octet-stream", OwnerID: cmd.OwnerID, CreatedAt: now, UpdatedAt: now, } if err := h.FileMetaRepo.Create(ctx, fileMeta); err != nil { if delErr := h.S3Repo.DeleteFile(ctx, cmd.S3Bucket, s3Key); delErr != nil { common.Logger.Error("compensation failed: could not delete S3 object after PG write failure", "s3_key", s3Key, "error", delErr) } return nil, fmt.Errorf("failed to save file metadata: %w", err) } return fileMeta, nil } type MoveFileHandler struct { FileMetaRepo repository.FileMetaRepository } func NewMoveFileHandler(fileMetaRepo repository.FileMetaRepository) *MoveFileHandler { return &MoveFileHandler{FileMetaRepo: fileMetaRepo} } func (h *MoveFileHandler) Handle(ctx context.Context, cmd MoveFileCommand) (string, error) { if err := h.FileMetaRepo.Move(ctx, cmd.FileID, cmd.TargetFolderID, cmd.OwnerID); err != nil { return "", err } return "文件移动成功", nil }