feat: 接入计划广场后端主链路

This commit is contained in:
Losita
2026-05-04 20:38:49 +08:00
parent 786c8925a0
commit 46874f0806
22 changed files with 3439 additions and 104 deletions

View File

@@ -0,0 +1,111 @@
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)
}