## CI 修复 (P0/P1) P0 — 编译阻塞: - interfaces.go: postUseCase ISP 接口移除不用的 Create/Update/Delete P1 — 必须修复: - ST1000: 为 13 个包添加包注释 (common/config/model/service/...) - ST1005: redis_store.go 全部错误消息改为小写开头 - errcheck (19处): defer Close()→闭包忽略, notifier.Create→_=, r.Run→检查error - errorlint (9处): switch-on-error→errors.Is 链, ==→errors.Is - ST1020/ST1022: 导出符号注释以符号名开头 P2 — 安全评审: - gosec (12处): G203/G301/G304/G306 添加 nolint 注释并附理由 P3 — 清理: - unused: 移除 hasUnicode/energyStore/current/energyAdminUseCase - gofumpt + goimports 全量格式化 (35+ 文件)
166 lines
4.5 KiB
Go
166 lines
4.5 KiB
Go
package service
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"metazone.cc/mce/internal/model"
|
|
)
|
|
|
|
// ReactionType 反应类型
|
|
type ReactionType string
|
|
|
|
const (
|
|
ReactionNone ReactionType = "none"
|
|
ReactionLiked ReactionType = "liked"
|
|
ReactionDisliked ReactionType = "disliked"
|
|
)
|
|
|
|
// ReactionService 赞/踩业务逻辑
|
|
type ReactionService struct {
|
|
repo ReactionStore
|
|
}
|
|
|
|
// NewReactionService 构造函数
|
|
func NewReactionService(repo ReactionStore) *ReactionService {
|
|
return &ReactionService{repo: repo}
|
|
}
|
|
|
|
// ToggleLike 切换点赞(含踩互斥逻辑)
|
|
// 返回:当前是否已点赞
|
|
func (s *ReactionService) ToggleLike(userID, postID uint) (bool, error) {
|
|
var isLiked bool
|
|
|
|
err := s.repo.Transaction(func(txRepo ReactionStore) error {
|
|
// 查当前状态
|
|
_, likeErr := txRepo.FindLike(userID, postID)
|
|
hasLike := likeErr == nil
|
|
|
|
_, dislikeErr := txRepo.FindDislike(userID, postID)
|
|
hasDislike := dislikeErr == nil
|
|
|
|
if hasLike {
|
|
// 已赞 → 取消赞
|
|
if err := txRepo.DeleteLike(userID, postID); err != nil {
|
|
return fmt.Errorf("取消点赞失败: %w", err)
|
|
}
|
|
if err := txRepo.IncrPostLikes(postID, -1); err != nil {
|
|
return fmt.Errorf("更新点赞计数失败: %w", err)
|
|
}
|
|
isLiked = false
|
|
} else {
|
|
// 无赞 → 加赞(若已有踩,先移除踩)
|
|
if hasDislike {
|
|
if err := txRepo.DeleteDislike(userID, postID); err != nil {
|
|
return fmt.Errorf("移除踩记录失败: %w", err)
|
|
}
|
|
if err := txRepo.IncrPostDislikes(postID, -1); err != nil {
|
|
return fmt.Errorf("更新踩计数失败: %w", err)
|
|
}
|
|
}
|
|
|
|
if err := txRepo.CreateLike(&model.PostLike{UserID: userID, PostID: postID}); err != nil {
|
|
return fmt.Errorf("创建点赞记录失败: %w", err)
|
|
}
|
|
if err := txRepo.IncrPostLikes(postID, 1); err != nil {
|
|
return fmt.Errorf("更新点赞计数失败: %w", err)
|
|
}
|
|
isLiked = true
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if err == nil && isLiked {
|
|
// 新赞 → 记录到每日点赞汇总(非关键路径)
|
|
s.recordLikeForAggregation(postID, userID)
|
|
}
|
|
|
|
return isLiked, err
|
|
}
|
|
|
|
// recordLikeForAggregation 记录点赞到每日汇总(非关键路径,失败不阻塞)
|
|
func (s *ReactionService) recordLikeForAggregation(postID uint, likerID uint) {
|
|
authorID, err := s.repo.GetPostAuthorID(postID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
// 不记录自己赞自己
|
|
if authorID == likerID {
|
|
return
|
|
}
|
|
date := time.Now().Format("2006-01-02")
|
|
likerUID := fmt.Sprintf("%d", likerID)
|
|
_ = s.repo.UpsertDailyLikeSummary(authorID, date, likerUID)
|
|
}
|
|
|
|
// ToggleDislike 切换踩(含赞互斥逻辑)
|
|
// 返回:当前是否已踩
|
|
func (s *ReactionService) ToggleDislike(userID, postID uint) (bool, error) {
|
|
var isDisliked bool
|
|
|
|
err := s.repo.Transaction(func(txRepo ReactionStore) error {
|
|
// 查当前状态
|
|
_, likeErr := txRepo.FindLike(userID, postID)
|
|
hasLike := likeErr == nil
|
|
|
|
_, dislikeErr := txRepo.FindDislike(userID, postID)
|
|
hasDislike := dislikeErr == nil
|
|
|
|
if hasDislike {
|
|
// 已踩 → 取消踩
|
|
if err := txRepo.DeleteDislike(userID, postID); err != nil {
|
|
return fmt.Errorf("取消踩失败: %w", err)
|
|
}
|
|
if err := txRepo.IncrPostDislikes(postID, -1); err != nil {
|
|
return fmt.Errorf("更新踩计数失败: %w", err)
|
|
}
|
|
isDisliked = false
|
|
} else {
|
|
// 无踩 → 加踩(若已有赞,先移除赞)
|
|
if hasLike {
|
|
if err := txRepo.DeleteLike(userID, postID); err != nil {
|
|
return fmt.Errorf("移除点赞记录失败: %w", err)
|
|
}
|
|
if err := txRepo.IncrPostLikes(postID, -1); err != nil {
|
|
return fmt.Errorf("更新点赞计数失败: %w", err)
|
|
}
|
|
}
|
|
|
|
if err := txRepo.CreateDislike(&model.PostDislike{UserID: userID, PostID: postID}); err != nil {
|
|
return fmt.Errorf("创建踩记录失败: %w", err)
|
|
}
|
|
if err := txRepo.IncrPostDislikes(postID, 1); err != nil {
|
|
return fmt.Errorf("更新踩计数失败: %w", err)
|
|
}
|
|
isDisliked = true
|
|
}
|
|
return nil
|
|
})
|
|
|
|
return isDisliked, err
|
|
}
|
|
|
|
// GetReaction 查询用户对文章的反应状态
|
|
func (s *ReactionService) GetReaction(userID, postID uint) (ReactionType, error) {
|
|
_, likeErr := s.repo.FindLike(userID, postID)
|
|
if likeErr == nil {
|
|
return ReactionLiked, nil
|
|
}
|
|
|
|
_, dislikeErr := s.repo.FindDislike(userID, postID)
|
|
if dislikeErr == nil {
|
|
return ReactionDisliked, nil
|
|
}
|
|
|
|
return ReactionNone, nil
|
|
}
|
|
|
|
// GetPostLikesCount 查询文章点赞数(公开展示)
|
|
func (s *ReactionService) GetPostLikesCount(postID uint) (int, error) {
|
|
count, err := s.repo.GetPostLikesCount(postID)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("查询文章点赞数: %w", err)
|
|
}
|
|
return count, nil
|
|
}
|