Set up project files and add .gitignore to exclude local build/runtime artifacts. Made-with: Cursor
140 lines
3.5 KiB
Go
140 lines
3.5 KiB
Go
package service
|
||
|
||
import (
|
||
"carrot_bbs/internal/model"
|
||
"carrot_bbs/internal/repository"
|
||
"errors"
|
||
"net/url"
|
||
"strings"
|
||
)
|
||
|
||
var (
|
||
ErrStickerAlreadyExists = errors.New("sticker already exists")
|
||
ErrInvalidStickerURL = errors.New("invalid sticker url")
|
||
)
|
||
|
||
// StickerService 自定义表情服务接口
|
||
type StickerService interface {
|
||
// 获取用户的所有表情
|
||
GetUserStickers(userID string) ([]model.UserSticker, error)
|
||
// 添加表情
|
||
AddSticker(userID string, url string, width, height int) (*model.UserSticker, error)
|
||
// 删除表情
|
||
DeleteSticker(userID string, stickerID string) error
|
||
// 检查表情是否已存在
|
||
CheckExists(userID string, url string) (bool, error)
|
||
// 重新排序
|
||
ReorderStickers(userID string, orders map[string]int) error
|
||
// 获取用户表情数量
|
||
GetStickerCount(userID string) (int64, error)
|
||
}
|
||
|
||
// stickerService 自定义表情服务实现
|
||
type stickerService struct {
|
||
stickerRepo repository.StickerRepository
|
||
}
|
||
|
||
// NewStickerService 创建自定义表情服务
|
||
func NewStickerService(stickerRepo repository.StickerRepository) StickerService {
|
||
return &stickerService{
|
||
stickerRepo: stickerRepo,
|
||
}
|
||
}
|
||
|
||
// GetUserStickers 获取用户的所有表情
|
||
func (s *stickerService) GetUserStickers(userID string) ([]model.UserSticker, error) {
|
||
stickers, err := s.stickerRepo.GetByUserID(userID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 兼容历史脏数据:过滤本地文件 URI,避免客户端加载 file:// 报错
|
||
filtered := make([]model.UserSticker, 0, len(stickers))
|
||
for _, sticker := range stickers {
|
||
if isValidStickerURL(sticker.URL) {
|
||
filtered = append(filtered, sticker)
|
||
}
|
||
}
|
||
return filtered, nil
|
||
}
|
||
|
||
// AddSticker 添加表情
|
||
func (s *stickerService) AddSticker(userID string, url string, width, height int) (*model.UserSticker, error) {
|
||
if !isValidStickerURL(url) {
|
||
return nil, ErrInvalidStickerURL
|
||
}
|
||
|
||
// 检查是否已存在
|
||
exists, err := s.stickerRepo.Exists(userID, url)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if exists {
|
||
return nil, ErrStickerAlreadyExists
|
||
}
|
||
|
||
// 获取当前数量用于设置排序
|
||
count, err := s.stickerRepo.CountByUserID(userID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
sticker := &model.UserSticker{
|
||
UserID: userID,
|
||
URL: url,
|
||
Width: width,
|
||
Height: height,
|
||
SortOrder: int(count), // 新表情添加到末尾
|
||
}
|
||
|
||
if err := s.stickerRepo.Create(sticker); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return sticker, nil
|
||
}
|
||
|
||
func isValidStickerURL(raw string) bool {
|
||
trimmed := strings.TrimSpace(raw)
|
||
if trimmed == "" {
|
||
return false
|
||
}
|
||
|
||
parsed, err := url.Parse(trimmed)
|
||
if err != nil {
|
||
return false
|
||
}
|
||
|
||
scheme := strings.ToLower(parsed.Scheme)
|
||
return scheme == "http" || scheme == "https"
|
||
}
|
||
|
||
// DeleteSticker 删除表情
|
||
func (s *stickerService) DeleteSticker(userID string, stickerID string) error {
|
||
// 先检查表情是否属于该用户
|
||
sticker, err := s.stickerRepo.GetByID(stickerID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if sticker.UserID != userID {
|
||
return errors.New("sticker not found")
|
||
}
|
||
|
||
return s.stickerRepo.Delete(stickerID)
|
||
}
|
||
|
||
// CheckExists 检查表情是否已存在
|
||
func (s *stickerService) CheckExists(userID string, url string) (bool, error) {
|
||
return s.stickerRepo.Exists(userID, url)
|
||
}
|
||
|
||
// ReorderStickers 重新排序
|
||
func (s *stickerService) ReorderStickers(userID string, orders map[string]int) error {
|
||
return s.stickerRepo.BatchUpdateSortOrder(userID, orders)
|
||
}
|
||
|
||
// GetStickerCount 获取用户表情数量
|
||
func (s *stickerService) GetStickerCount(userID string) (int64, error) {
|
||
return s.stickerRepo.CountByUserID(userID)
|
||
}
|