feat: 收藏夹系统 + Studio 趋势图 + 通知偏好设置
- 新增收藏夹系统 (Folder/FolderItem 模型、CRUD API、前端管理页) - 新增文章详情页收藏按钮 (书签图标、ToggleFavorite 一键收藏) - 新增 Studio 趋势图 (赋能值/评论/点赞/收藏 4 线图, 7d/30d/90d) - 新增通知偏好设置页 (评论/回复/点赞/关注 4 类开关) - 后端通知列表支持按偏好过滤 (排除已关闭的通知类型) - 下载 Chart.js 到本地静态资源 (static/js/chart.umd.min.js) - 补充收藏夹卡片、开关滑块、趋势图网格等 CSS 样式
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"regexp"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
@ -309,3 +310,29 @@ func (s *AuthService) UpdateProfile(userID uint, username, bio string) error {
|
||||
}
|
||||
return s.userRepo.Update(user)
|
||||
}
|
||||
|
||||
// GetNotifyPrefs 获取用户通知偏好
|
||||
func (s *AuthService) GetNotifyPrefs(userID uint) (map[string]bool, error) {
|
||||
user, err := s.userRepo.FindByID(userID)
|
||||
if err != nil {
|
||||
return nil, common.ErrUserNotFound
|
||||
}
|
||||
return ParseNotifyPrefs(user.NotifyPrefs), nil
|
||||
}
|
||||
|
||||
// UpdateNotifyPref 更新单个通知偏好项
|
||||
func (s *AuthService) UpdateNotifyPref(userID uint, key string, enabled bool) error {
|
||||
user, err := s.userRepo.FindByID(userID)
|
||||
if err != nil {
|
||||
return common.ErrUserNotFound
|
||||
}
|
||||
|
||||
prefs := ParseNotifyPrefs(user.NotifyPrefs)
|
||||
prefs[key] = enabled
|
||||
data, err := json.Marshal(prefs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user.NotifyPrefs = string(data)
|
||||
return s.userRepo.Update(user)
|
||||
}
|
||||
|
||||
363
internal/service/favorite_service.go
Normal file
363
internal/service/favorite_service.go
Normal file
@ -0,0 +1,363 @@
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
// AddToFolder 将文章加入收藏夹(会先移除该文章在其他收藏夹的收藏)
|
||||
func (s *FavoriteService) AddToFolder(userID, folderID, postID uint) error {
|
||||
// 检查收藏夹归属
|
||||
f, err := s.repo.FindFolderByID(folderID)
|
||||
if err != nil {
|
||||
return common.ErrPostNotFound
|
||||
}
|
||||
if f.UserID != userID {
|
||||
return common.ErrPermissionDenied
|
||||
}
|
||||
|
||||
return s.db.Transaction(func(tx *gorm.DB) error {
|
||||
txRepo := s.repo.WithTx(tx)
|
||||
|
||||
// 如果该文章已在其他收藏夹中,先移除
|
||||
existing, err := txRepo.FindItemByUserAndPost(userID, postID)
|
||||
if err == nil && existing.FolderID != folderID {
|
||||
if err := txRepo.DeleteItem(existing.FolderID, postID); err != nil {
|
||||
return fmt.Errorf("移除旧收藏: %w", err)
|
||||
}
|
||||
// 旧收藏夹没有增加计数,所以不需要减
|
||||
}
|
||||
|
||||
// 检查是否已在目标收藏夹中
|
||||
if err == nil && existing.FolderID == folderID {
|
||||
return fmt.Errorf("文章已在此收藏夹中")
|
||||
}
|
||||
|
||||
item := &model.FolderItem{
|
||||
FolderID: folderID,
|
||||
PostID: postID,
|
||||
UserID: userID,
|
||||
}
|
||||
if err := txRepo.CreateItem(item); err != nil {
|
||||
return fmt.Errorf("添加收藏: %w", err)
|
||||
}
|
||||
|
||||
return txRepo.IncrPostFavoritesCount(postID, 1)
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveFromFolder 从收藏夹移除文章
|
||||
func (s *FavoriteService) RemoveFromFolder(userID, folderID, postID uint) error {
|
||||
f, err := s.repo.FindFolderByID(folderID)
|
||||
if err != nil {
|
||||
return common.ErrPostNotFound
|
||||
}
|
||||
if f.UserID != userID {
|
||||
return common.ErrPermissionDenied
|
||||
}
|
||||
|
||||
return s.db.Transaction(func(tx *gorm.DB) error {
|
||||
txRepo := s.repo.WithTx(tx)
|
||||
if err := txRepo.DeleteItem(folderID, postID); err != nil {
|
||||
return fmt.Errorf("移除收藏: %w", err)
|
||||
}
|
||||
return txRepo.IncrPostFavoritesCount(postID, -1)
|
||||
})
|
||||
}
|
||||
|
||||
// AddFavorite 收藏文章(自动选择或创建默认收藏夹)
|
||||
func (s *FavoriteService) AddFavorite(userID, postID uint, folderID *uint) error {
|
||||
var targetFolderID uint
|
||||
|
||||
if folderID != nil && *folderID > 0 {
|
||||
targetFolderID = *folderID
|
||||
} else {
|
||||
// 使用或创建默认收藏夹
|
||||
f, err := s.ensureDefaultFolder(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetFolderID = f.ID
|
||||
}
|
||||
|
||||
return s.AddToFolder(userID, targetFolderID, postID)
|
||||
}
|
||||
|
||||
// RemoveFavorite 取消收藏文章
|
||||
func (s *FavoriteService) RemoveFavorite(userID, postID uint) error {
|
||||
existing, err := s.repo.FindItemByUserAndPost(userID, postID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("未收藏该文章")
|
||||
}
|
||||
return s.RemoveFromFolder(userID, existing.FolderID, postID)
|
||||
}
|
||||
|
||||
// ListFolderItems 列收藏夹内的文章
|
||||
func (s *FavoriteService) ListFolderItems(userID, folderID uint, page, pageSize int) (*FolderItemsResult, error) {
|
||||
f, err := s.repo.FindFolderByID(folderID)
|
||||
if err != nil {
|
||||
return nil, common.ErrPostNotFound
|
||||
}
|
||||
|
||||
// 权限检查:非本人 + 非公开 → 拒绝
|
||||
if f.UserID != userID && !f.IsPublic {
|
||||
return nil, common.ErrPermissionDenied
|
||||
}
|
||||
|
||||
p := common.Pagination{Page: page, PageSize: pageSize}
|
||||
p.DefaultPagination()
|
||||
|
||||
items, total, err := s.repo.ListItemsByFolder(folderID, p.Offset(), p.PageSize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询收藏内容: %w", err)
|
||||
}
|
||||
if items == nil {
|
||||
items = []model.FolderItem{}
|
||||
}
|
||||
|
||||
return &FolderItemsResult{
|
||||
Items: items,
|
||||
Total: total,
|
||||
Page: p.Page,
|
||||
TotalPages: common.PageCount(total, p.PageSize),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// ToggleFavorite 切换收藏(有则取消,无则加入默认收藏夹)
|
||||
func (s *FavoriteService) ToggleFavorite(userID, postID uint) (bool, error) {
|
||||
existing, err := s.repo.FindItemByUserAndPost(userID, postID)
|
||||
if err == nil {
|
||||
// 已收藏,取消
|
||||
if err := s.RemoveFromFolder(userID, existing.FolderID, postID); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// 未收藏,加入默认
|
||||
if err := s.AddFavorite(userID, postID, nil); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
@ -8,20 +9,45 @@ import (
|
||||
"metazone.cc/metalab/internal/model"
|
||||
)
|
||||
|
||||
// ParseNotifyPrefs 解析用户通知偏好 JSON 字符串
|
||||
func ParseNotifyPrefs(raw string) map[string]bool {
|
||||
prefs := map[string]bool{
|
||||
model.NotifyComment: true,
|
||||
model.NotifyCommentReply: true,
|
||||
model.NotifyLikeAggregated: true,
|
||||
model.NotifyFollow: true,
|
||||
}
|
||||
if raw == "" {
|
||||
return prefs
|
||||
}
|
||||
var parsed map[string]bool
|
||||
if err := json.Unmarshal([]byte(raw), &parsed); err != nil {
|
||||
return prefs
|
||||
}
|
||||
for k, v := range parsed {
|
||||
prefs[k] = v
|
||||
}
|
||||
return prefs
|
||||
}
|
||||
|
||||
// notifRepo 通知服务所需的最小仓储接口(ISP)
|
||||
type notifRepo interface {
|
||||
Create(n *model.Notification) error
|
||||
ListByUser(userID uint, offset, limit int) ([]model.Notification, error)
|
||||
ListByUserAndType(userID uint, notifyType string, offset, limit int) ([]model.Notification, error)
|
||||
ListByUserExcludeTypes(userID uint, excludeTypes []string, offset, limit int) ([]model.Notification, error)
|
||||
CountByUser(userID uint) (int64, error)
|
||||
CountByUserExcludeTypes(userID uint, excludeTypes []string) (int64, error)
|
||||
CountByUserAndType(userID uint, notifyType string) (int64, error)
|
||||
CountUnread(userID uint) (int64, error)
|
||||
CountUnreadExcludeTypes(userID uint, excludeTypes []string) (int64, error)
|
||||
CountUnreadByType(userID uint, notifyType string) (int64, error)
|
||||
MarkRead(id, userID uint) error
|
||||
MarkAllRead(userID uint) error
|
||||
GetUnnotifiedDailyLikeSummaries(userID uint) ([]model.DailyLikeSummary, error)
|
||||
MarkDailyLikeSummaryNotified(id uint) error
|
||||
GetUsernameByID(userID uint) (string, error)
|
||||
GetNotifyPrefs(userID uint) (string, error)
|
||||
}
|
||||
|
||||
// NotificationService 消息/通知业务逻辑
|
||||
@ -56,12 +82,14 @@ func (s *NotificationService) List(userID uint, page, pageSize int) (*model.Noti
|
||||
p := common.Pagination{Page: page, PageSize: pageSize}
|
||||
p.DefaultPagination()
|
||||
|
||||
total, err := s.repo.CountByUser(userID)
|
||||
excludeTypes := s.getDisabledTypes(userID)
|
||||
|
||||
total, err := s.repo.CountByUserExcludeTypes(userID, excludeTypes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items, err := s.repo.ListByUser(userID, p.Offset(), p.PageSize)
|
||||
items, err := s.repo.ListByUserExcludeTypes(userID, excludeTypes, p.Offset(), p.PageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -198,9 +226,31 @@ func sortNotifications(items []model.Notification) {
|
||||
})
|
||||
}
|
||||
|
||||
// CountUnread 统计未读消息数
|
||||
// CountUnread 统计未读消息数(排除用户禁用的通知类型)
|
||||
func (s *NotificationService) CountUnread(userID uint) (int64, error) {
|
||||
return s.repo.CountUnread(userID)
|
||||
excludeTypes := s.getDisabledTypes(userID)
|
||||
return s.repo.CountUnreadExcludeTypes(userID, excludeTypes)
|
||||
}
|
||||
|
||||
// getDisabledTypes 获取用户禁用的通知类型列表
|
||||
func (s *NotificationService) getDisabledTypes(userID uint) []string {
|
||||
raw, err := s.repo.GetNotifyPrefs(userID)
|
||||
if err != nil || raw == "" {
|
||||
return nil
|
||||
}
|
||||
prefs := ParseNotifyPrefs(raw)
|
||||
var disabled []string
|
||||
for _, t := range []string{
|
||||
model.NotifyComment,
|
||||
model.NotifyCommentReply,
|
||||
model.NotifyLikeAggregated,
|
||||
model.NotifyFollow,
|
||||
} {
|
||||
if !prefs[t] {
|
||||
disabled = append(disabled, t)
|
||||
}
|
||||
}
|
||||
return disabled
|
||||
}
|
||||
|
||||
// MarkRead 标记单条消息为已读
|
||||
|
||||
@ -185,3 +185,29 @@ type FollowStore interface {
|
||||
GetFollowListPublic(userID uint) (bool, error)
|
||||
WithTx(tx *gorm.DB) FollowStore
|
||||
}
|
||||
|
||||
// TrendPoint 趋势数据点
|
||||
type TrendPoint struct {
|
||||
Date string `json:"date"`
|
||||
Value int `json:"value"`
|
||||
}
|
||||
|
||||
// EnergyTrendStore 赋能趋势聚合(ISP)
|
||||
type EnergyTrendStore interface {
|
||||
AggregateEnergyByAuthor(userID uint, since string) ([]TrendPoint, error)
|
||||
}
|
||||
|
||||
// CommentTrendStore 评论趋势聚合(ISP)
|
||||
type CommentTrendStore interface {
|
||||
AggregateCommentsByAuthor(userID uint, since string) ([]TrendPoint, error)
|
||||
}
|
||||
|
||||
// LikeTrendStore 点赞趋势聚合(ISP)
|
||||
type LikeTrendStore interface {
|
||||
AggregateLikesByAuthor(userID uint, since string) ([]TrendPoint, error)
|
||||
}
|
||||
|
||||
// FavoriteTrendStore 收藏趋势聚合(ISP)
|
||||
type FavoriteTrendStore interface {
|
||||
AggregateFavoritesByAuthor(userID uint, since string) ([]TrendPoint, error)
|
||||
}
|
||||
|
||||
59
internal/service/trends_service.go
Normal file
59
internal/service/trends_service.go
Normal file
@ -0,0 +1,59 @@
|
||||
package service
|
||||
|
||||
import "fmt"
|
||||
|
||||
// TrendResult 趋势返回结构
|
||||
type TrendResult struct {
|
||||
Energy []TrendPoint `json:"energy"`
|
||||
Comments []TrendPoint `json:"comments"`
|
||||
Likes []TrendPoint `json:"likes"`
|
||||
Favorites []TrendPoint `json:"favorites"`
|
||||
}
|
||||
|
||||
// TrendsService Studio 趋势分析服务
|
||||
type TrendsService struct {
|
||||
energyRepo EnergyTrendStore
|
||||
commentRepo CommentTrendStore
|
||||
likeRepo LikeTrendStore
|
||||
favoriteRepo FavoriteTrendStore
|
||||
}
|
||||
|
||||
// NewTrendsService 构造函数
|
||||
func NewTrendsService(energyRepo EnergyTrendStore, commentRepo CommentTrendStore, likeRepo LikeTrendStore, favoriteRepo FavoriteTrendStore) *TrendsService {
|
||||
return &TrendsService{
|
||||
energyRepo: energyRepo,
|
||||
commentRepo: commentRepo,
|
||||
likeRepo: likeRepo,
|
||||
favoriteRepo: favoriteRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// GetTrends 获取指定时间范围的趋势数据
|
||||
func (s *TrendsService) GetTrends(userID uint, since string) (*TrendResult, error) {
|
||||
energy, err := s.energyRepo.AggregateEnergyByAuthor(userID, since)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询赋能趋势: %w", err)
|
||||
}
|
||||
|
||||
comments, err := s.commentRepo.AggregateCommentsByAuthor(userID, since)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询评论趋势: %w", err)
|
||||
}
|
||||
|
||||
likes, err := s.likeRepo.AggregateLikesByAuthor(userID, since)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询点赞趋势: %w", err)
|
||||
}
|
||||
|
||||
favorites, err := s.favoriteRepo.AggregateFavoritesByAuthor(userID, since)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询收藏趋势: %w", err)
|
||||
}
|
||||
|
||||
return &TrendResult{
|
||||
Energy: energy,
|
||||
Comments: comments,
|
||||
Likes: likes,
|
||||
Favorites: favorites,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user