package service import ( "crypto/rand" "encoding/hex" "errors" "strings" "time" "seeyon-filesystem/config" "seeyon-filesystem/dto" "seeyon-filesystem/model" "seeyon-filesystem/repository" "seeyon-filesystem/storage" "seeyon-filesystem/utils" ) // ShareService 分享业务服务 type ShareService struct { shareRepo *repository.ShareRepository fileRepo *repository.FileRepository } // NewShareService 创建分享服务实例 func NewShareService() *ShareService { return &ShareService{ shareRepo: repository.NewShareRepository(), fileRepo: repository.NewFileRepository(), } } // Create 创建分享链接 func (s *ShareService) Create(req *dto.ShareCreateRequest, ownerID uint64) (*dto.ShareVO, error) { // 验证文件存在 file, err := s.fileRepo.FindByID(req.FileID) if err != nil { return nil, errors.New("文件不存在") } if file.OwnerID != ownerID { return nil, errors.New("无权分享此文件") } // 生成分享码 shareCode, err := generateShareCode() if err != nil { return nil, errors.New("生成分享码失败") } share := &model.Share{ FileID: req.FileID, OwnerID: ownerID, ShareCode: shareCode, AllowPreview: 1, AllowDownload: 1, Status: 1, } // 设置密码 if req.Password != "" { hashed, _ := utils.HashPassword(req.Password) share.Password = hashed } // 设置过期时间 if req.ExpireHours > 0 { expireAt := time.Now().Add(time.Duration(req.ExpireHours) * time.Hour) share.ExpireAt = &expireAt } share.MaxDownload = req.MaxDownload // 设置权限 share.AllowPreview = int8(req.AllowPreview) share.AllowDownload = int8(req.AllowDownload) // 如果都是0(未传), 默认允许 if share.AllowPreview == 0 && share.AllowDownload == 0 { share.AllowPreview = 1 share.AllowDownload = 1 } if err := s.shareRepo.Create(share); err != nil { return nil, errors.New("创建分享失败") } return s.toShareVO(share), nil } // GetByCode 通过分享码获取分享信息(公开访问) func (s *ShareService) GetByCode(code string) (*dto.ShareFileVO, *model.Share, error) { share, err := s.shareRepo.FindByShareCode(code) if err != nil { return nil, nil, errors.New("分享链接不存在或已失效") } // 检查过期 if share.ExpireAt != nil && share.ExpireAt.Before(time.Now()) { return nil, nil, errors.New("分享链接已过期") } // 检查下载次数 if share.MaxDownload > 0 && share.DownloadCount >= share.MaxDownload { return nil, nil, errors.New("下载次数已达上限") } // 获取文件信息 file, err := s.fileRepo.FindByID(share.FileID) if err != nil { return nil, nil, errors.New("文件不存在") } // 生成预览URL previewURL := "" if share.AllowPreview == 1 && file.StorageKey != "" { engine, err := storage.GetEngineByPolicyID(file.StoragePolicyID) if err == nil { if localEngine, ok := engine.(*storage.LocalEngine); ok { previewURL = localEngine.GetURL(file.StorageKey) } else if minioEngine, ok := engine.(*storage.MinIOEngine); ok { previewURL = minioEngine.GetPreviewURL(file.StorageKey) } } } fileVO := &dto.ShareFileVO{ Name: file.Name, Extension: file.Extension, Size: file.Size, MimeType: file.MimeType, PreviewURL: previewURL, AllowPreview: share.AllowPreview == 1, AllowDownload: share.AllowDownload == 1, CreatedAt: file.CreatedAt, } return fileVO, share, nil } // VerifyPassword 验证分享密码 func (s *ShareService) VerifyPassword(shareID uint64, password string) bool { share, err := s.shareRepo.FindByID(shareID) if err != nil { return false } return utils.CheckPassword(password, share.Password) } // ListByOwner 查询用户的分享列表 func (s *ShareService) ListByOwner(ownerID uint64) ([]dto.ShareVO, error) { shares, err := s.shareRepo.ListByOwner(ownerID) if err != nil { return nil, err } vos := make([]dto.ShareVO, 0, len(shares)) for i := range shares { vos = append(vos, *s.toShareVO(&shares[i])) } return vos, nil } // Cancel 取消分享 func (s *ShareService) Cancel(id uint64, ownerID uint64) error { share, err := s.shareRepo.FindByID(id) if err != nil { return errors.New("分享不存在") } if share.OwnerID != ownerID { return errors.New("无权操作此分享") } return s.shareRepo.Cancel(id) } // toShareVO 转换为视图对象 func (s *ShareService) toShareVO(share *model.Share) *dto.ShareVO { // 生成完整分享链接 publicURL := config.AppConfig.Server.PublicURL if publicURL == "" { publicURL = "http://localhost:8080" } publicURL = strings.TrimRight(publicURL, "/") return &dto.ShareVO{ ID: share.ID, ShareCode: share.ShareCode, ShareURL: publicURL + "/s/" + share.ShareCode, HasPassword: share.Password != "", ExpireAt: share.ExpireAt, MaxDownload: share.MaxDownload, DownloadCount: share.DownloadCount, AllowPreview: share.AllowPreview, AllowDownload: share.AllowDownload, CreatedAt: share.CreatedAt, } } // generateShareCode 生成8字节随机分享码(16位十六进制字符串) func generateShareCode() (string, error) { bytes := make([]byte, 8) if _, err := rand.Read(bytes); err != nil { return "", err } return hex.EncodeToString(bytes), nil }