- 移动本地接口定义到 interfaces.go 和 admin/interfaces.go - favorite_controller.go → favorite_item_controller.go - space_controller.go → space_loaders.go - settings_controller.go → settings_api_audit.go - admin_energy_controller.go → admin_energy_api_controller.go - admin_post_controller.go → admin_post_action_controller.go - comment_repo.go → comment_mention_repo.go - post_repo.go → post_stats_repo.go
109 lines
3.8 KiB
Go
109 lines
3.8 KiB
Go
package repository
|
||
|
||
import (
|
||
"time"
|
||
|
||
"metazone.cc/metalab/internal/model"
|
||
|
||
"gorm.io/gorm"
|
||
"gorm.io/gorm/clause"
|
||
)
|
||
|
||
// CountUserPosts 统计某用户的已发布文章数
|
||
func (r *PostRepo) CountUserPosts(userID uint) (int64, error) {
|
||
var count int64
|
||
err := r.db.Model(&model.Post{}).
|
||
Where("user_id = ? AND status = ? AND deleted_at IS NULL", userID, model.PostStatusApproved).
|
||
Count(&count).Error
|
||
return count, err
|
||
}
|
||
|
||
// GetUserStats 获取用户的文章统计数据(总获赞/总收藏/总阅读)
|
||
func (r *PostRepo) GetUserStats(userID uint) (likes, favorites, reads int64, err error) {
|
||
type stats struct {
|
||
TotalLikes int64
|
||
TotalFavorites int64
|
||
TotalReads int64
|
||
}
|
||
var s stats
|
||
err = r.db.Model(&model.Post{}).
|
||
Select("COALESCE(SUM(likes_count), 0) AS total_likes, COALESCE(SUM(favorites_count), 0) AS total_favorites, COALESCE(SUM(views_count), 0) AS total_reads").
|
||
Where("user_id = ? AND status = ? AND deleted_at IS NULL", userID, model.PostStatusApproved).
|
||
Scan(&s).Error
|
||
return s.TotalLikes, s.TotalFavorites, s.TotalReads, err
|
||
}
|
||
|
||
// overviewRow GetOverviewByUserID 单次 GROUP BY 查询结果行
|
||
type overviewRow struct {
|
||
TotalPosts int64
|
||
DraftCount int64
|
||
PendingCount int64
|
||
ApprovedCount int64
|
||
RejectedCount int64
|
||
}
|
||
|
||
// GetOverviewByUserID 用单次 GROUP BY 查询聚合用户各状态文章数
|
||
func (r *PostRepo) GetOverviewByUserID(userID uint) (totalPosts, draftCount, pendingCount, approvedCount, rejectedCount int64, err error) {
|
||
var row overviewRow
|
||
err = r.db.Model(&model.Post{}).
|
||
Select(`
|
||
COUNT(*) AS total_posts,
|
||
COALESCE(SUM(CASE WHEN status = 'draft' THEN 1 ELSE 0 END), 0) AS draft_count,
|
||
COALESCE(SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END), 0) AS pending_count,
|
||
COALESCE(SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END), 0) AS approved_count,
|
||
COALESCE(SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END), 0) AS rejected_count
|
||
`).
|
||
Where("user_id = ? AND deleted_at IS NULL", userID).
|
||
Scan(&row).Error
|
||
return row.TotalPosts, row.DraftCount, row.PendingCount, row.ApprovedCount, row.RejectedCount, err
|
||
}
|
||
|
||
// readCooldown 两次阅读记录最小间隔
|
||
const readCooldown = 2 * time.Second
|
||
|
||
// RecordRead 记录已登录用户阅读文章(防刷量:取去重+冷却)
|
||
// 返回 true 表示首次阅读(新记录),false 表示重复或冷却中
|
||
func (r *PostRepo) RecordRead(userID, postID uint) (bool, error) {
|
||
// 冷却检查:同用户两次阅读之间至少间隔 2 秒
|
||
var lastRead model.PostReadLog
|
||
if err := r.db.Where("user_id = ?", userID).
|
||
Order("read_at DESC").First(&lastRead).Error; err == nil {
|
||
if time.Since(lastRead.ReadAt) < readCooldown {
|
||
return false, nil
|
||
}
|
||
}
|
||
|
||
result := r.db.Clauses(clause.OnConflict{DoNothing: true}).
|
||
Create(&model.PostReadLog{UserID: userID, PostID: postID})
|
||
if result.Error != nil {
|
||
return false, result.Error
|
||
}
|
||
return result.RowsAffected > 0, nil
|
||
}
|
||
|
||
// RecordGuestRead 记录访客阅读文章(基于 Cookie visitor_id 去重+冷却)
|
||
// 返回 true 表示首次阅读(新记录),false 表示重复或冷却中
|
||
func (r *PostRepo) RecordGuestRead(visitorID string, postID uint) (bool, error) {
|
||
// 冷却检查:同访客两次阅读之间至少间隔 2 秒
|
||
var lastRead model.PostGuestReadLog
|
||
if err := r.db.Where("visitor_id = ?", visitorID).
|
||
Order("read_at DESC").First(&lastRead).Error; err == nil {
|
||
if time.Since(lastRead.ReadAt) < readCooldown {
|
||
return false, nil
|
||
}
|
||
}
|
||
|
||
result := r.db.Clauses(clause.OnConflict{DoNothing: true}).
|
||
Create(&model.PostGuestReadLog{VisitorID: visitorID, PostID: postID})
|
||
if result.Error != nil {
|
||
return false, result.Error
|
||
}
|
||
return result.RowsAffected > 0, nil
|
||
}
|
||
|
||
// IncrementViewsCount 阅读数 +1
|
||
func (r *PostRepo) IncrementViewsCount(postID uint) error {
|
||
return r.db.Model(&model.Post{}).Where("id = ?", postID).
|
||
Update("views_count", gorm.Expr("views_count + 1")).Error
|
||
}
|