This repository has been archived on 2026-06-21. You can view files and clone it, but cannot push or open issues or pull requests.
Files
MetaLab/internal/service/favorite_service.go
Victor_Jay ff9886f08b refactor: 文件行数超标拆解(Service + Controller 9/16 完成)
- Service 层:energy_service → energize/operations/admin/query 四文件
- Service 层:post_service → helpers/audit + 核心 CRUD 保留
- Service 层:favorite_service → items + 核心文件夹操作保留
- Service 层:auth_service → password + 核心注册/登录保留
- Service 层:notification_service → notify + 核心列表/已读保留
- Service 层:audit_service → review + 核心列表/详情保留
- Controller 层:studio_controller → page/write/api/post_api/action 五文件
- Controller 层:post_controller → page/api/upload 三文件
- Controller 层:comment_controller → list/write/action/upload 四文件
- 清理未使用导入(notification_service.go 移除 fmt)
2026-06-02 15:45:54 +08:00

228 lines
6.1 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
import (
"fmt"
"metazone.cc/metalab/internal/common"
"metazone.cc/metalab/internal/model"
"gorm.io/gorm"
)
// FavoriteStore FavoriteService 所需的最小仓储接口ISP
type FavoriteStore interface {
// Folder
CreateFolder(folder *model.Folder) error
FindFolderByID(id uint) (*model.Folder, error)
ListFoldersByUser(userID uint) ([]model.Folder, error)
UpdateFolder(folder *model.Folder) error
DeleteFolder(id uint) error
DeleteItemsByFolder(folderID uint) error
CountByNameAndUser(userID uint, name string, excludeID uint) (int64, error)
HasDefaultFolder(userID uint) (bool, error)
// FolderItem
CreateItem(item *model.FolderItem) error
DeleteItem(folderID, postID uint) error
FindItemByUserAndPost(userID, postID uint) (*model.FolderItem, error)
ListItemsByFolder(folderID uint, offset, limit int) ([]model.FolderItem, int64, error)
CountItemsByFolder(folderID uint) (int64, error)
// Post counter
IncrPostFavoritesCount(postID uint, delta int) error
// Trend
AggregateFavoritesByAuthor(userID uint, since string) ([]TrendPoint, error)
// Tx
WithTx(tx *gorm.DB) FavoriteStore
}
// FavoriteListResult 收藏夹列表 + 收藏内容
type FavoriteListResult struct {
Folders []model.Folder `json:"folders"`
}
// FolderItemsResult 收藏夹内容
type FolderItemsResult struct {
Items []model.FolderItem `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
TotalPages int `json:"total_pages"`
}
// FavoriteStatus 某文章的收藏状态
type FavoriteStatus struct {
Favorited bool `json:"favorited"`
FolderID *uint `json:"folder_id,omitempty"`
FolderName *string `json:"folder_name,omitempty"`
}
// FavoriteService 收藏夹业务逻辑
type FavoriteService struct {
db *gorm.DB
repo FavoriteStore
}
// NewFavoriteService 构造函数
func NewFavoriteService(db *gorm.DB, repo FavoriteStore) *FavoriteService {
return &FavoriteService{db: db, repo: repo}
}
// ensureDefaultFolder 确保用户有默认收藏夹(不存在则创建)
func (s *FavoriteService) ensureDefaultFolder(userID uint) (*model.Folder, error) {
has, err := s.repo.HasDefaultFolder(userID)
if err != nil {
return nil, fmt.Errorf("查询默认收藏夹: %w", err)
}
if has {
folders, err := s.repo.ListFoldersByUser(userID)
if err != nil {
return nil, fmt.Errorf("查询收藏夹列表: %w", err)
}
for _, f := range folders {
if f.IsDefault {
return &f, nil
}
}
}
// 创建默认收藏夹
f := &model.Folder{
UserID: userID,
Name: "默认",
Description: "自动创建的默认收藏夹",
IsPublic: false,
IsDefault: true,
}
if err := s.repo.CreateFolder(f); err != nil {
return nil, fmt.Errorf("创建默认收藏夹: %w", err)
}
return f, nil
}
// ListFolders 获取用户的所有收藏夹
func (s *FavoriteService) ListFolders(userID uint) (*FavoriteListResult, error) {
folders, err := s.repo.ListFoldersByUser(userID)
if err != nil {
return nil, fmt.Errorf("查询收藏夹: %w", err)
}
if folders == nil {
folders = []model.Folder{}
}
return &FavoriteListResult{Folders: folders}, nil
}
// CreateFolder 创建收藏夹
func (s *FavoriteService) CreateFolder(userID uint, name, description string, isPublic bool) (*model.Folder, error) {
if name == "" {
return nil, fmt.Errorf("收藏夹名称不能为空")
}
if len(name) > 50 {
return nil, fmt.Errorf("收藏夹名称不能超过 50 个字符")
}
// 检查名称是否重复
count, err := s.repo.CountByNameAndUser(userID, name, 0)
if err != nil {
return nil, fmt.Errorf("检查名称: %w", err)
}
if count > 0 {
return nil, fmt.Errorf("收藏夹名称已存在")
}
f := &model.Folder{
UserID: userID,
Name: name,
Description: description,
IsPublic: isPublic,
}
if err := s.repo.CreateFolder(f); err != nil {
return nil, fmt.Errorf("创建收藏夹: %w", err)
}
return f, nil
}
// UpdateFolder 修改收藏夹(默认收藏夹不可改名/改描述)
func (s *FavoriteService) UpdateFolder(userID, folderID uint, name, description string, isPublic bool) error {
f, err := s.repo.FindFolderByID(folderID)
if err != nil {
return common.ErrPostNotFound // 复用错误哨兵
}
if f.UserID != userID {
return common.ErrPermissionDenied
}
if f.IsDefault {
// 默认收藏夹只允许修改公开性
f.IsPublic = isPublic
return s.repo.UpdateFolder(f)
}
if name == "" {
return fmt.Errorf("收藏夹名称不能为空")
}
// 检查名称重复
count, err := s.repo.CountByNameAndUser(userID, name, folderID)
if err != nil {
return fmt.Errorf("检查名称: %w", err)
}
if count > 0 {
return fmt.Errorf("收藏夹名称已存在")
}
f.Name = name
f.Description = description
f.IsPublic = isPublic
return s.repo.UpdateFolder(f)
}
// DeleteFolder 删除收藏夹(默认不可删)
func (s *FavoriteService) DeleteFolder(userID, folderID uint) error {
f, err := s.repo.FindFolderByID(folderID)
if err != nil {
return common.ErrPostNotFound
}
if f.UserID != userID {
return common.ErrPermissionDenied
}
if f.IsDefault {
return fmt.Errorf("默认收藏夹不可删除")
}
return s.db.Transaction(func(tx *gorm.DB) error {
txRepo := s.repo.WithTx(tx)
// 先清理 items 的 fans 计数
items, _, _ := txRepo.ListItemsByFolder(folderID, 0, 10000)
for _, item := range items {
if err := txRepo.IncrPostFavoritesCount(item.PostID, -1); err != nil {
return fmt.Errorf("更新文章收藏数: %w", err)
}
}
if err := txRepo.DeleteItemsByFolder(folderID); err != nil {
return fmt.Errorf("删除收藏内容: %w", err)
}
return txRepo.DeleteFolder(folderID)
})
}
// GetPostFavoriteStatus 查询文章收藏状态
func (s *FavoriteService) GetPostFavoriteStatus(userID, postID uint) (*FavoriteStatus, error) {
item, err := s.repo.FindItemByUserAndPost(userID, postID)
if err != nil {
return &FavoriteStatus{Favorited: false}, nil
}
f, err := s.repo.FindFolderByID(item.FolderID)
var folderName *string
if err == nil {
folderName = &f.Name
}
return &FavoriteStatus{
Favorited: true,
FolderID: &item.FolderID,
FolderName: folderName,
}, nil
}