Files
nl-im-service/internal/service/attachment_service.go
2025-12-03 11:00:47 +08:00

208 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* package service
* 作用:附件管理服务
*/
package service
import (
"errors"
"fmt"
"io"
"mime"
"os"
"path/filepath"
"strings"
"time"
"xk-websocket-v2/internal/model"
"gorm.io/gorm"
)
// AttachmentService 附件服务结构体
type AttachmentService struct {
DB *gorm.DB
}
// AttachmentSvc 全局单例
var AttachmentSvc *AttachmentService
/**
* InitAttachmentService
* 功能:初始化附件服务
*/
func InitAttachmentService(db *gorm.DB) {
AttachmentSvc = &AttachmentService{DB: db}
// 创建上传目录
os.MkdirAll("./uploads/images", os.ModePerm)
os.MkdirAll("./uploads/videos", os.ModePerm)
}
/**
* UploadFile
* 功能:上传文件
* @param userID 上传者ID
* @param fileName 文件名
* @param fileType 文件类型image/video
* @param fileSize 文件大小
* @param fileData 文件数据
* @returns 附件信息和错误
*/
func (s *AttachmentService) UploadFile(userID, fileName, fileType string, fileSize int64, fileData io.Reader) (*model.Attachment, error) {
// 验证文件类型
if fileType != "image" && fileType != "video" {
return nil, errors.New("文件类型必须是image或video")
}
// 验证文件大小
if fileType == "image" && fileSize > model.MaxImageSize {
return nil, fmt.Errorf("图片大小不能超过%dMB", model.MaxImageSize/(1024*1024))
}
if fileType == "video" && fileSize > model.MaxVideoSize {
return nil, fmt.Errorf("视频大小不能超过%dMB", model.MaxVideoSize/(1024*1024))
}
// 验证文件扩展名
ext := strings.ToLower(filepath.Ext(fileName))
allowedExts := s.getAllowedExtensions(fileType)
if !contains(allowedExts, ext) {
return nil, fmt.Errorf("不支持的文件类型,允许的类型: %v", allowedExts)
}
// 生成唯一文件名
timestamp := time.Now().UnixNano()
randomStr := fmt.Sprintf("%d", timestamp%1000000)
newFileName := fmt.Sprintf("%d_%s%s", timestamp, randomStr, ext)
// 确定保存路径
var saveDir string
if fileType == "image" {
saveDir = "./uploads/images"
} else {
saveDir = "./uploads/videos"
}
filePath := filepath.Join(saveDir, newFileName)
// 保存文件
file, err := os.Create(filePath)
if err != nil {
return nil, fmt.Errorf("创建文件失败: %v", err)
}
defer file.Close()
_, err = io.Copy(file, fileData)
if err != nil {
os.Remove(filePath) // 删除失败的文件
return nil, fmt.Errorf("保存文件失败: %v", err)
}
// 生成访问URL
fileURL := fmt.Sprintf("/uploads/%s/%s", fileType+"s", newFileName)
// 获取MIME类型
mimeType := mime.TypeByExtension(ext)
if mimeType == "" {
mimeType = "application/octet-stream"
}
// 创建附件记录
attachment := model.Attachment{
UploaderID: userID,
FileName: fileName,
FileType: fileType,
FileSize: fileSize,
FilePath: filePath,
FileURL: fileURL,
MimeType: mimeType,
}
if err := s.DB.Create(&attachment).Error; err != nil {
os.Remove(filePath) // 删除文件
return nil, fmt.Errorf("保存附件记录失败: %v", err)
}
return &attachment, nil
}
/**
* GetAttachment
* 功能:获取附件信息
*/
func (s *AttachmentService) GetAttachment(attachmentID uint) (*model.Attachment, error) {
var attachment model.Attachment
result := s.DB.First(&attachment, attachmentID)
if result.Error != nil {
return nil, result.Error
}
return &attachment, nil
}
/**
* DeleteAttachment
* 功能:删除附件(验证上传者权限)
*/
func (s *AttachmentService) DeleteAttachment(attachmentID uint, userID string) error {
var attachment model.Attachment
if err := s.DB.First(&attachment, attachmentID).Error; err != nil {
return err
}
// 验证权限
if attachment.UploaderID != userID {
return errors.New("无权删除此附件")
}
// 删除文件
if _, err := os.Stat(attachment.FilePath); err == nil {
os.Remove(attachment.FilePath)
}
// 删除记录
return s.DB.Delete(&attachment).Error
}
/**
* GetUserAttachments
* 功能:获取用户附件列表
*/
func (s *AttachmentService) GetUserAttachments(userID, fileType string, page, pageSize int) ([]model.Attachment, int64, error) {
var attachments []model.Attachment
var total int64
query := s.DB.Where("uploader_id = ?", userID)
if fileType != "" {
query = query.Where("file_type = ?", fileType)
}
// 获取总数
query.Model(&model.Attachment{}).Count(&total)
// 分页查询
offset := (page - 1) * pageSize
result := query.Order("created_at DESC").
Offset(offset).
Limit(pageSize).
Find(&attachments)
return attachments, total, result.Error
}
// 辅助函数:获取允许的文件扩展名
func (s *AttachmentService) getAllowedExtensions(fileType string) []string {
if fileType == "image" {
return []string{".jpg", ".jpeg", ".png", ".gif", ".webp"}
}
return []string{".mp4", ".avi", ".mov", ".wmv", ".flv", ".mkv"}
}
// 辅助函数:检查字符串是否在切片中
func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}