Files
2026-05-06 00:30:08 +08:00

129 lines
3.7 KiB
Go

package events
import (
"errors"
"fmt"
"strings"
"time"
)
const (
ForumPostLikedEventType = "forum.post.liked"
ForumPostImportedEventType = "forum.post.imported"
ForumRewardEventVersion = "v1"
ForumRewardSourceLike = "forum_like"
ForumRewardSourceImport = "forum_import"
)
// ForumPostRewardPayload 是计划广场作者奖励事件的统一载荷。
//
// 职责边界:
// 1. 只描述“哪个帖子因什么互动触发了作者奖励”,不直接携带最终 Token 数额;
// 2. source 负责表达奖励来源,真正的奖励规则仍由 token-store 自己解析;
// 3. event_id 必须稳定,供 outbox 重试和下游记账幂等共同使用。
type ForumPostRewardPayload struct {
EventID string `json:"event_id"`
PostID uint64 `json:"post_id"`
ImportID uint64 `json:"import_id"`
AuthorUserID uint64 `json:"author_user_id"`
ActorUserID uint64 `json:"actor_user_id"`
RewardReceiverUserID uint64 `json:"reward_receiver_user_id"`
Source string `json:"source"`
OccurredAt time.Time `json:"occurred_at"`
}
func NewForumPostLikedPayload(postID uint64, authorUserID uint64, actorUserID uint64, occurredAt time.Time) ForumPostRewardPayload {
return newForumPostRewardPayload(
ForumPostLikedEventType,
ForumRewardSourceLike,
postID,
0,
authorUserID,
actorUserID,
occurredAt,
)
}
func NewForumPostImportedPayload(postID uint64, importID uint64, authorUserID uint64, actorUserID uint64, occurredAt time.Time) ForumPostRewardPayload {
return newForumPostRewardPayload(
ForumPostImportedEventType,
ForumRewardSourceImport,
postID,
importID,
authorUserID,
actorUserID,
occurredAt,
)
}
func newForumPostRewardPayload(
eventType string,
source string,
postID uint64,
importID uint64,
authorUserID uint64,
actorUserID uint64,
occurredAt time.Time,
) ForumPostRewardPayload {
if occurredAt.IsZero() {
occurredAt = time.Now()
}
return ForumPostRewardPayload{
EventID: ForumRewardEventID(eventType, postID, actorUserID),
PostID: postID,
ImportID: importID,
AuthorUserID: authorUserID,
ActorUserID: actorUserID,
RewardReceiverUserID: authorUserID,
Source: strings.TrimSpace(source),
OccurredAt: occurredAt,
}
}
func ForumRewardEventID(eventType string, postID uint64, actorUserID uint64) string {
return fmt.Sprintf("%s:%d:%d", strings.TrimSpace(eventType), postID, actorUserID)
}
// EventType 根据 source 反推出当前奖励事件类型。
func (p ForumPostRewardPayload) EventType() string {
switch strings.TrimSpace(p.Source) {
case ForumRewardSourceLike:
return ForumPostLikedEventType
case ForumRewardSourceImport:
return ForumPostImportedEventType
default:
return ""
}
}
func (p ForumPostRewardPayload) MessageKey() string {
return strings.TrimSpace(p.EventID)
}
func (p ForumPostRewardPayload) AggregateID() string {
return fmt.Sprintf("post:%d", p.PostID)
}
func (p ForumPostRewardPayload) Validate() error {
if strings.TrimSpace(p.EventID) == "" {
return errors.New("forum reward event_id 不能为空")
}
if strings.TrimSpace(p.EventType()) == "" {
return errors.New("forum reward source 非法")
}
if p.PostID == 0 {
return errors.New("forum reward post_id 不能为空")
}
if p.AuthorUserID == 0 || p.ActorUserID == 0 || p.RewardReceiverUserID == 0 {
return errors.New("forum reward user_id 不能为空")
}
if strings.TrimSpace(p.Source) == ForumRewardSourceImport && p.ImportID == 0 {
return errors.New("forum import reward import_id 不能为空")
}
if p.OccurredAt.IsZero() {
return errors.New("forum reward occurred_at 不能为空")
}
return nil
}