Initial backend repository commit.

Set up project files and add .gitignore to exclude local build/runtime artifacts.

Made-with: Cursor
This commit is contained in:
2026-03-09 21:28:58 +08:00
commit 4d8f2ec997
102 changed files with 25022 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
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)
}