feat: 实现关注系统 + 通知侧边栏分类 TAB + 点赞聚合通知
- 新增关注系统:UserFollow 模型、FollowService(toggle/status/列表隐私控制) - User 新增 FollowersCount/FollowingCount/FollowListPublic/NotifyPrefs 字段 - Space 页面增加四态关注按钮(关注/已关注/回关/已互粉)+ 粉丝/关注数链接 - 新增 /space/:uid/followers 和 /space/:uid/following 列表页 - 新增 NotifyFollow/NotifyLikeAggregated 通知类型 - ReactionService 点赞时写入 daily_like_summary,访问时生成聚合通知 - FollowService 关注时触发 NotifyFollow 通知 - 消息中心侧边栏升级为分类 TAB(全部/系统通知/@艾特/点赞/关注) - NotificationService 新增 ListByCategory 按分类分页查询
This commit is contained in:
112
internal/repository/follow_repo.go
Normal file
112
internal/repository/follow_repo.go
Normal file
@ -0,0 +1,112 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"metazone.cc/metalab/internal/model"
|
||||
"metazone.cc/metalab/internal/service"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// FollowRepo 关注数据访问
|
||||
type FollowRepo struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewFollowRepo 构造函数
|
||||
func NewFollowRepo(db *gorm.DB) *FollowRepo {
|
||||
return &FollowRepo{db: db}
|
||||
}
|
||||
|
||||
// WithTx 基于事务连接创建 FollowRepo
|
||||
func (r *FollowRepo) WithTx(tx *gorm.DB) service.FollowStore {
|
||||
return &FollowRepo{db: tx}
|
||||
}
|
||||
|
||||
// Create 创建关注
|
||||
func (r *FollowRepo) Create(follow *model.UserFollow) error {
|
||||
return r.db.Create(follow).Error
|
||||
}
|
||||
|
||||
// Delete 取消关注
|
||||
func (r *FollowRepo) Delete(followerID, followeeID uint) error {
|
||||
return r.db.Where("follower_id = ? AND followee_id = ?", followerID, followeeID).
|
||||
Delete(&model.UserFollow{}).Error
|
||||
}
|
||||
|
||||
// Exists 检查是否已关注
|
||||
func (r *FollowRepo) Exists(followerID, followeeID uint) (bool, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.UserFollow{}).
|
||||
Where("follower_id = ? AND followee_id = ?", followerID, followeeID).
|
||||
Count(&count).Error
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
// CountFollowers 粉丝数
|
||||
func (r *FollowRepo) CountFollowers(userID uint) (int64, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.UserFollow{}).Where("followee_id = ?", userID).Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
// CountFollowing 关注数
|
||||
func (r *FollowRepo) CountFollowing(userID uint) (int64, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.UserFollow{}).Where("follower_id = ?", userID).Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
// ListFollowers 粉丝列表(分页,JOIN users 获取用户名)
|
||||
func (r *FollowRepo) ListFollowers(userID uint, offset, limit int) ([]model.UserFollow, int64, error) {
|
||||
var total int64
|
||||
if err := r.db.Model(&model.UserFollow{}).Where("followee_id = ?", userID).Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var items []model.UserFollow
|
||||
err := r.db.Table("user_follows").
|
||||
Select("user_follows.*, u.username AS follower_username").
|
||||
Joins("INNER JOIN users u ON u.id = user_follows.follower_id").
|
||||
Where("user_follows.followee_id = ?", userID).
|
||||
Order("user_follows.created_at DESC").
|
||||
Offset(offset).Limit(limit).
|
||||
Scan(&items).Error
|
||||
return items, total, err
|
||||
}
|
||||
|
||||
// ListFollowing 关注列表(分页,JOIN users 获取用户名)
|
||||
func (r *FollowRepo) ListFollowing(userID uint, offset, limit int) ([]model.UserFollow, int64, error) {
|
||||
var total int64
|
||||
if err := r.db.Model(&model.UserFollow{}).Where("follower_id = ?", userID).Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var items []model.UserFollow
|
||||
err := r.db.Table("user_follows").
|
||||
Select("user_follows.*, u.username AS followee_username").
|
||||
Joins("INNER JOIN users u ON u.id = user_follows.followee_id").
|
||||
Where("user_follows.follower_id = ?", userID).
|
||||
Order("user_follows.created_at DESC").
|
||||
Offset(offset).Limit(limit).
|
||||
Scan(&items).Error
|
||||
return items, total, err
|
||||
}
|
||||
|
||||
// IncrFollowersCount 原子增减粉丝数
|
||||
func (r *FollowRepo) IncrFollowersCount(userID uint, delta int) error {
|
||||
return r.db.Model(&model.User{}).
|
||||
Where("id = ?", userID).
|
||||
UpdateColumn("followers_count", gorm.Expr("followers_count + ?", delta)).Error
|
||||
}
|
||||
|
||||
// IncrFollowingCount 原子增减关注数
|
||||
func (r *FollowRepo) IncrFollowingCount(userID uint, delta int) error {
|
||||
return r.db.Model(&model.User{}).
|
||||
Where("id = ?", userID).
|
||||
UpdateColumn("following_count", gorm.Expr("following_count + ?", delta)).Error
|
||||
}
|
||||
|
||||
// GetFollowListPublic 查询用户关注列表公开性
|
||||
func (r *FollowRepo) GetFollowListPublic(userID uint) (bool, error) {
|
||||
var user model.User
|
||||
err := r.db.Select("follow_list_public").First(&user, userID).Error
|
||||
return user.FollowListPublic, err
|
||||
}
|
||||
@ -60,3 +60,48 @@ func (r *NotificationRepo) MarkAllRead(userID uint) error {
|
||||
Where("user_id = ? AND is_read = false", userID).
|
||||
Update("is_read", true).Error
|
||||
}
|
||||
|
||||
// ListByUserAndType 按类型分页查询用户消息
|
||||
func (r *NotificationRepo) ListByUserAndType(userID uint, notifyType string, offset, limit int) ([]model.Notification, error) {
|
||||
var list []model.Notification
|
||||
err := r.db.Where("user_id = ? AND notify_type = ?", userID, notifyType).
|
||||
Order("created_at DESC").
|
||||
Offset(offset).Limit(limit).
|
||||
Find(&list).Error
|
||||
return list, err
|
||||
}
|
||||
|
||||
// CountByUserAndType 按类型统计用户消息数
|
||||
func (r *NotificationRepo) CountByUserAndType(userID uint, notifyType string) (int64, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.Notification{}).Where("user_id = ? AND notify_type = ?", userID, notifyType).Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
// CountUnreadByType 按类型统计用户未读消息数
|
||||
func (r *NotificationRepo) CountUnreadByType(userID uint, notifyType string) (int64, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.Notification{}).
|
||||
Where("user_id = ? AND is_read = false AND notify_type = ?", userID, notifyType).
|
||||
Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetUnnotifiedDailyLikeSummaries 获取未通知的每日点赞汇总
|
||||
func (r *NotificationRepo) GetUnnotifiedDailyLikeSummaries(userID uint) ([]model.DailyLikeSummary, error) {
|
||||
var summaries []model.DailyLikeSummary
|
||||
err := r.db.Where("author_uid = ? AND notified = ?", userID, false).Find(&summaries).Error
|
||||
return summaries, err
|
||||
}
|
||||
|
||||
// MarkDailyLikeSummaryNotified 标记汇总已通知
|
||||
func (r *NotificationRepo) MarkDailyLikeSummaryNotified(id uint) error {
|
||||
return r.db.Model(&model.DailyLikeSummary{}).Where("id = ?", id).Update("notified", true).Error
|
||||
}
|
||||
|
||||
// GetUsernameByID 根据用户ID获取用户名
|
||||
func (r *NotificationRepo) GetUsernameByID(userID uint) (string, error) {
|
||||
var user model.User
|
||||
err := r.db.Select("username").First(&user, userID).Error
|
||||
return user.Username, err
|
||||
}
|
||||
|
||||
@ -75,3 +75,47 @@ func (r *ReactionRepo) IncrPostDislikes(postID uint, delta int) error {
|
||||
Where("id = ?", postID).
|
||||
UpdateColumn("dislikes_count", gorm.Expr("dislikes_count + ?", delta)).Error
|
||||
}
|
||||
|
||||
// GetPostAuthorID 查询文章作者ID
|
||||
func (r *ReactionRepo) GetPostAuthorID(postID uint) (uint, error) {
|
||||
var post model.Post
|
||||
err := r.db.Select("user_id").First(&post, postID).Error
|
||||
return post.UserID, err
|
||||
}
|
||||
|
||||
// UpsertDailyLikeSummary 追加或创建每日点赞汇总
|
||||
func (r *ReactionRepo) UpsertDailyLikeSummary(authorUID uint, date string, likerUID string) error {
|
||||
var existing model.DailyLikeSummary
|
||||
err := r.db.Where("author_uid = ? AND date = ?", authorUID, date).First(&existing).Error
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return r.db.Create(&model.DailyLikeSummary{
|
||||
AuthorUID: authorUID,
|
||||
Date: date,
|
||||
LikerUIDs: likerUID,
|
||||
Notified: false,
|
||||
}).Error
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 追加 likerUID
|
||||
newUids := existing.LikerUIDs
|
||||
if newUids == "" {
|
||||
newUids = likerUID
|
||||
} else {
|
||||
newUids = newUids + "," + likerUID
|
||||
}
|
||||
return r.db.Model(&existing).Update("liker_uids", newUids).Error
|
||||
}
|
||||
|
||||
// GetUnnotifiedSummaries 查询未通知的每日点赞汇总
|
||||
func (r *ReactionRepo) GetUnnotifiedSummaries(userID uint) ([]model.DailyLikeSummary, error) {
|
||||
var summaries []model.DailyLikeSummary
|
||||
err := r.db.Where("author_uid = ? AND notified = ?", userID, false).Find(&summaries).Error
|
||||
return summaries, err
|
||||
}
|
||||
|
||||
// MarkSummaryNotified 标记汇总已通知
|
||||
func (r *ReactionRepo) MarkSummaryNotified(id uint) error {
|
||||
return r.db.Model(&model.DailyLikeSummary{}).Where("id = ?", id).Update("notified", true).Error
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user