package handlers import ( "context" "fmt" "time" "rag/file-system/internal/common" "rag/file-system/internal/domain/model" "rag/file-system/internal/domain/repository" ) type GetShareInfoQuery struct { Token string } type DownloadShareQuery struct { Token string Password string } type GetShareInfoHandler struct { ShareRepo repository.ShareRepository FileMetaRepo repository.FileMetaRepository } func NewGetShareInfoHandler(shareRepo repository.ShareRepository, fileMetaRepo repository.FileMetaRepository) *GetShareInfoHandler { return &GetShareInfoHandler{ShareRepo: shareRepo, FileMetaRepo: fileMetaRepo} } func (h *GetShareInfoHandler) Handle(ctx context.Context, q GetShareInfoQuery) (*model.ShareInfo, error) { share, err := h.ShareRepo.GetByToken(ctx, q.Token) if err != nil { return nil, err } if share == nil { return nil, common.NewNotFoundError("分享链接不存在") } if share.ExpiresAt != nil && share.ExpiresAt.Before(time.Now()) { return nil, common.NewBusinessException("分享链接已过期") } if share.MaxDownloads != nil && share.DownloadCount >= *share.MaxDownloads { return nil, common.NewBusinessException("分享链接下载次数已达上限") } info := &model.ShareInfo{ Token: share.Token, ResourceType: share.ResourceType, HasPassword: share.Password != nil, ExpiresAt: share.ExpiresAt, } if share.ResourceType == "file" { file, err := h.FileMetaRepo.GetByID(ctx, share.ResourceID) if err != nil { return nil, err } if file != nil { info.FileName = file.Name info.FileSize = file.Size } } return info, nil } type DownloadShareHandler struct { ShareRepo repository.ShareRepository FileMetaRepo repository.FileMetaRepository S3Repo repository.FileRepository } func NewDownloadShareHandler( shareRepo repository.ShareRepository, fileMetaRepo repository.FileMetaRepository, s3Repo repository.FileRepository, ) *DownloadShareHandler { return &DownloadShareHandler{ ShareRepo: shareRepo, FileMetaRepo: fileMetaRepo, S3Repo: s3Repo, } } func (h *DownloadShareHandler) Handle(ctx context.Context, q DownloadShareQuery) (string, error) { share, err := h.ShareRepo.GetByToken(ctx, q.Token) if err != nil { return "", err } if share == nil { return "", common.NewNotFoundError("分享链接不存在") } if share.ExpiresAt != nil && share.ExpiresAt.Before(time.Now()) { return "", common.NewBusinessException("分享链接已过期") } if share.MaxDownloads != nil && share.DownloadCount >= *share.MaxDownloads { return "", common.NewBusinessException("下载次数已达上限") } if share.Password != nil && *share.Password != "" { if q.Password == "" { return "", common.NewBusinessException("此分享需要密码") } if q.Password != *share.Password { return "", common.NewBusinessException("密码错误") } } if share.ResourceType != "file" { return "", common.NewBusinessException("仅支持文件分享下载") } file, err := h.FileMetaRepo.GetByID(ctx, share.ResourceID) if err != nil { return "", err } if file == nil { return "", common.NewNotFoundError("文件不存在") } presignedURL, err := h.S3Repo.GeneratePresignedURL(ctx, file.S3Bucket, file.S3Key, 5*time.Minute) if err != nil { return "", fmt.Errorf("failed to generate download URL: %w", err) } if err := h.ShareRepo.IncrementDownloadCount(ctx, q.Token); err != nil { common.Logger.Error("failed to increment download count", "token", q.Token, "error", err) } return presignedURL, nil }