Files
smartmate/backend/services/taskclassforum/sv/like.go
2026-05-04 20:38:49 +08:00

112 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package sv
import (
"context"
"fmt"
"time"
"github.com/LoveLosita/smartflow/backend/respond"
forumdao "github.com/LoveLosita/smartflow/backend/services/taskclassforum/dao"
forummodel "github.com/LoveLosita/smartflow/backend/services/taskclassforum/model"
forumcontracts "github.com/LoveLosita/smartflow/backend/shared/contracts/taskclassforum"
)
// LikePost 点赞计划帖子。
//
// 职责边界:
// 1. 负责保证同一用户同一帖子只有一个 active 点赞状态;
// 2. 负责维护帖子 like_count 计数字段;
// 3. 不直接发放 Token只写稳定 event_id后续奖励链路可基于该 ID 幂等消费。
func (s *Service) LikePost(ctx context.Context, actorUserID uint64, postID uint64) (forumcontracts.ForumPostCounters, forumcontracts.ForumPostViewerState, error) {
if err := s.Ready(); err != nil {
return forumcontracts.ForumPostCounters{}, forumcontracts.ForumPostViewerState{}, err
}
if actorUserID == 0 || postID == 0 {
return forumcontracts.ForumPostCounters{}, forumcontracts.ForumPostViewerState{}, respond.MissingParam
}
if err := s.forumDAO.Transaction(ctx, func(txDAO *forumdao.ForumDAO) error {
post, err := txDAO.LockPublishedPost(ctx, postID)
if err != nil {
return normalizeRecordNotFound(err, respond.UserTaskClassNotFound)
}
like, err := txDAO.FindLike(ctx, postID, actorUserID)
if err != nil {
return err
}
if like == nil {
return createActiveLike(ctx, txDAO, post, actorUserID)
}
if like.Status == forummodel.ForumLikeStatusActive {
return nil
}
if err := txDAO.ActivateLike(ctx, like.ID); err != nil {
return err
}
return txDAO.AddPostCounter(ctx, postID, "like_count", 1)
}); err != nil {
return forumcontracts.ForumPostCounters{}, forumcontracts.ForumPostViewerState{}, err
}
return s.postInteractionState(ctx, actorUserID, postID)
}
// UnlikePost 取消计划帖子点赞。
func (s *Service) UnlikePost(ctx context.Context, actorUserID uint64, postID uint64) (forumcontracts.ForumPostCounters, forumcontracts.ForumPostViewerState, error) {
if err := s.Ready(); err != nil {
return forumcontracts.ForumPostCounters{}, forumcontracts.ForumPostViewerState{}, err
}
if actorUserID == 0 || postID == 0 {
return forumcontracts.ForumPostCounters{}, forumcontracts.ForumPostViewerState{}, respond.MissingParam
}
if err := s.forumDAO.Transaction(ctx, func(txDAO *forumdao.ForumDAO) error {
if _, err := txDAO.LockPublishedPost(ctx, postID); err != nil {
return normalizeRecordNotFound(err, respond.UserTaskClassNotFound)
}
like, err := txDAO.FindLike(ctx, postID, actorUserID)
if err != nil {
return err
}
if like == nil || like.Status != forummodel.ForumLikeStatusActive {
return nil
}
if err := txDAO.CancelLike(ctx, like.ID, time.Now()); err != nil {
return err
}
return txDAO.AddPostCounter(ctx, postID, "like_count", -1)
}); err != nil {
return forumcontracts.ForumPostCounters{}, forumcontracts.ForumPostViewerState{}, err
}
return s.postInteractionState(ctx, actorUserID, postID)
}
func createActiveLike(ctx context.Context, txDAO *forumdao.ForumDAO, post *forummodel.ForumPost, actorUserID uint64) error {
like := &forummodel.ForumLike{
PostID: post.ID,
UserID: actorUserID,
AuthorUserID: post.AuthorUserID,
Status: forummodel.ForumLikeStatusActive,
EventID: forumLikeEventID(post.ID, actorUserID),
}
if err := txDAO.CreateLike(ctx, like); err != nil {
return err
}
return txDAO.AddPostCounter(ctx, post.ID, "like_count", 1)
}
func (s *Service) postInteractionState(ctx context.Context, actorUserID uint64, postID uint64) (forumcontracts.ForumPostCounters, forumcontracts.ForumPostViewerState, error) {
post, err := s.forumDAO.FindPublishedPost(ctx, postID)
if err != nil {
return forumcontracts.ForumPostCounters{}, forumcontracts.ForumPostViewerState{}, normalizeRecordNotFound(err, respond.UserTaskClassNotFound)
}
liked, imported, err := s.viewerStateSets(ctx, actorUserID, []uint64{postID})
if err != nil {
return forumcontracts.ForumPostCounters{}, forumcontracts.ForumPostViewerState{}, err
}
return countersFromPost(*post), viewerState(postID, liked, imported), nil
}
func forumLikeEventID(postID uint64, userID uint64) string {
return fmt.Sprintf("forum.post.liked:%d:%d", postID, userID)
}