- 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)
228 lines
6.1 KiB
Go
228 lines
6.1 KiB
Go
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
|
||
}
|