204 lines
5.2 KiB
Go
204 lines
5.2 KiB
Go
|
|
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
|
||
|
|
}
|