- Add EventPublisher interface in biz layer for domain event publishing - Wire EventBusPublisher (Watermill EventBus adapter) into FileUsecase, FolderUsecase, ShareUsecase - Publish events after UploadFile, DeleteFile, CreateFolder, DeleteFolder, CreateShare - Implement CQRSHandler with logging event handlers for all 6 event types - Register event handlers via CQRSBus.RegisterHandlers using Watermill EventProcessor - Store subscriber and wmLogger in CQRSBus for EventProcessor wiring - Expose SqlDB() on Data struct for Watermill SQL pub/sub - Start Watermill router in goroutine alongside Kratos app with graceful close - Use appContext wrapper struct to pass CQRSBus through Wire DI graph
121 lines
4.7 KiB
Go
121 lines
4.7 KiB
Go
package biz
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"time"
|
|
|
|
"rag/file-system/internal/data"
|
|
"rag/file-system/internal/watermark"
|
|
|
|
"github.com/go-kratos/kratos/v2/log"
|
|
)
|
|
|
|
// FileRepo defines the interface for S3 storage operations.
|
|
type FileRepo interface {
|
|
UploadFile(ctx context.Context, bucket, key string, fileData io.Reader) error
|
|
DownloadFile(ctx context.Context, bucket, key string) (io.ReadCloser, error)
|
|
ListBuckets(ctx context.Context) ([]string, error)
|
|
CreateBucket(ctx context.Context, name string) error
|
|
DeleteBucket(ctx context.Context, name string) error
|
|
ListObjectsV2(ctx context.Context, bucket, prefix string, maxKeys int32, token *string) (*data.ListFilesResult, error)
|
|
GeneratePresignedURL(ctx context.Context, bucket, key string, expiry time.Duration) (string, error)
|
|
GetFileContent(ctx context.Context, bucket, key string) (string, error)
|
|
DeleteFile(ctx context.Context, bucket, key string) error
|
|
CreateMultipartUpload(ctx context.Context, bucket, key string) (string, error)
|
|
UploadPart(ctx context.Context, bucket, key, uploadID string, partNumber int32, fileData io.Reader) (string, error)
|
|
CompleteMultipartUpload(ctx context.Context, bucket, key, uploadID string, parts []data.Part) (string, error)
|
|
AbortMultipartUpload(ctx context.Context, bucket, key, uploadID string) error
|
|
}
|
|
|
|
// FileUsecase wraps FileRepo and provides file-level business operations.
|
|
type FileUsecase struct {
|
|
repo FileRepo
|
|
eventPub EventPublisher
|
|
log *log.Helper
|
|
}
|
|
|
|
// NewFileUsecase creates a new FileUsecase.
|
|
func NewFileUsecase(repo FileRepo, eventPub EventPublisher, logger log.Logger) *FileUsecase {
|
|
return &FileUsecase{
|
|
repo: repo,
|
|
eventPub: eventPub,
|
|
log: log.NewHelper(logger),
|
|
}
|
|
}
|
|
|
|
// UploadFile uploads data to the specified bucket and key.
|
|
func (uc *FileUsecase) UploadFile(ctx context.Context, bucket, key string, fileData io.Reader) error {
|
|
if err := uc.repo.UploadFile(ctx, bucket, key, fileData); err != nil {
|
|
return err
|
|
}
|
|
// Publish domain event on success.
|
|
if err := uc.eventPub.Publish(ctx, &watermark.FileUploadedEvent{
|
|
BucketName: bucket,
|
|
ObjectKey: key,
|
|
}); err != nil {
|
|
uc.log.Errorf("failed to publish FileUploadedEvent: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DownloadFile downloads an object from S3.
|
|
func (uc *FileUsecase) DownloadFile(ctx context.Context, bucket, key string) (io.ReadCloser, error) {
|
|
return uc.repo.DownloadFile(ctx, bucket, key)
|
|
}
|
|
|
|
// GetFileContent retrieves text content for preview.
|
|
func (uc *FileUsecase) GetFileContent(ctx context.Context, bucket, key string) (string, error) {
|
|
return uc.repo.GetFileContent(ctx, bucket, key)
|
|
}
|
|
|
|
// DeleteFile removes a file from S3.
|
|
func (uc *FileUsecase) DeleteFile(ctx context.Context, bucket, key string) error {
|
|
if err := uc.repo.DeleteFile(ctx, bucket, key); err != nil {
|
|
return err
|
|
}
|
|
// Publish domain event on success.
|
|
if err := uc.eventPub.Publish(ctx, &watermark.FileDeletedEvent{
|
|
BucketName: bucket,
|
|
ObjectKey: key,
|
|
}); err != nil {
|
|
uc.log.Errorf("failed to publish FileDeletedEvent: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ListObjectsV2 lists files with pagination support.
|
|
func (uc *FileUsecase) ListObjectsV2(ctx context.Context, bucket, prefix string, maxKeys int32, token *string) (*data.ListFilesResult, error) {
|
|
return uc.repo.ListObjectsV2(ctx, bucket, prefix, maxKeys, token)
|
|
}
|
|
|
|
// GeneratePresignedURL generates a presigned URL with custom expiry.
|
|
func (uc *FileUsecase) GeneratePresignedURL(ctx context.Context, bucket, key string, expiry time.Duration) (string, error) {
|
|
return uc.repo.GeneratePresignedURL(ctx, bucket, key, expiry)
|
|
}
|
|
|
|
// GetPreviewURL generates a presigned URL with 24h expiry for file preview.
|
|
func (uc *FileUsecase) GetPreviewURL(ctx context.Context, bucket, key string) (string, error) {
|
|
return uc.repo.GeneratePresignedURL(ctx, bucket, key, 24*time.Hour)
|
|
}
|
|
|
|
// CreateMultipartUpload initializes a multipart upload session.
|
|
func (uc *FileUsecase) CreateMultipartUpload(ctx context.Context, bucket, key string) (string, error) {
|
|
return uc.repo.CreateMultipartUpload(ctx, bucket, key)
|
|
}
|
|
|
|
// UploadPart uploads a single part of a multipart upload.
|
|
func (uc *FileUsecase) UploadPart(ctx context.Context, bucket, key, uploadID string, partNumber int32, fileData io.Reader) (string, error) {
|
|
return uc.repo.UploadPart(ctx, bucket, key, uploadID, partNumber, fileData)
|
|
}
|
|
|
|
// CompleteMultipartUpload assembles all parts to complete the upload.
|
|
func (uc *FileUsecase) CompleteMultipartUpload(ctx context.Context, bucket, key, uploadID string, parts []data.Part) (string, error) {
|
|
return uc.repo.CompleteMultipartUpload(ctx, bucket, key, uploadID, parts)
|
|
}
|
|
|
|
// AbortMultipartUpload cancels an in-progress multipart upload.
|
|
func (uc *FileUsecase) AbortMultipartUpload(ctx context.Context, bucket, key, uploadID string) error {
|
|
return uc.repo.AbortMultipartUpload(ctx, bucket, key, uploadID)
|
|
}
|