Files
SeeyonFileSystem/server/service/share_service.go

204 lines
5.2 KiB
Go
Raw Normal View History

2026-07-03 15:58:29 +08:00
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
}