package logic import ( "context" "time" "spark-server/internal/module/moment/entity" "spark-server/internal/pkg/errs" ) type NoticeView struct { ID int64 `json:"id"` Kind entity.NoticeKind `json:"kind"` From UserCard `json:"from"` PostID int64 `json:"post_id"` CommentID int64 `json:"comment_id,omitempty"` Read bool `json:"read"` CreatedAt int64 `json:"created_at"` } type NoticeList struct { Items []*NoticeView `json:"items"` NextCursor int64 `json:"next_cursor"` } type Unread struct { HasNew bool `json:"has_new"` LatestID int64 `json:"latest_id"` } type ReadInput struct { PostID int64 `json:"post_id" validate:"required,gt=0" label:"field.moment_id"` } type NoticeReadInput struct { Cursor int64 `json:"cursor" validate:"omitempty,gte=0" label:"field.moment_cursor"` } func (s *Service) ListNotices(ctx context.Context, userID, cursor int64, limit int) (*NoticeList, error) { if !s.ready() || userID <= 0 { return nil, errs.ErrUnauthorized } limit = entity.ClampLimit(limit) rows, err := s.repo.ListNotices(ctx, userID, cursor, limit) if err != nil { return nil, err } uids := make([]int64, 0, len(rows)) for _, n := range rows { uids = append(uids, n.FromUserID) } users := s.loadUsers(ctx, uids) remarks := s.loadRemarks(ctx, userID, uids) items := make([]*NoticeView, 0, len(rows)) next := int64(0) for _, n := range rows { items = append(items, &NoticeView{ ID: n.ID, Kind: n.Kind, From: cardOf(users[n.FromUserID], remarks[n.FromUserID]), PostID: n.PostID, CommentID: n.CommentID, Read: n.ReadAtMS > 0, CreatedAt: n.CreatedAtMS, }) next = n.ID } return &NoticeList{Items: items, NextCursor: next}, nil } func (s *Service) ReadNotices(ctx context.Context, userID int64, in *NoticeReadInput) error { if !s.ready() || userID <= 0 { return errs.ErrUnauthorized } cursor := int64(0) if in != nil { cursor = in.Cursor } return s.repo.MarkNoticesRead(ctx, userID, cursor) } func (s *Service) UnreadFeed(ctx context.Context, userID int64) (*Unread, error) { if !s.ready() || userID <= 0 { return nil, errs.ErrUnauthorized } st, err := s.repo.GetSettings(ctx, userID) if err != nil { return nil, err } feed, err := s.Feed(ctx, userID, 0, 1) if err != nil { return nil, err } latest := int64(0) for _, item := range feed.Items { if item != nil && item.User.UID != userID { latest = item.ID break } } return &Unread{HasNew: latest > st.LastReadPostID, LatestID: latest}, nil } func (s *Service) MarkFeedRead(ctx context.Context, userID, postID int64) error { if !s.ready() || userID <= 0 { return errs.ErrUnauthorized } if postID <= 0 { return errs.ErrInvalidArgument } return s.repo.AdvanceRead(ctx, userID, postID) } func (s *Service) notify(ctx context.Context, rows []*entity.Notice) error { out := make([]*entity.Notice, 0, len(rows)) now := time.Now().UnixMilli() for _, n := range rows { if n == nil || n.ToUserID <= 0 || n.ToUserID == n.FromUserID { continue } id, err := s.ids.NextID() if err != nil { return err } n.ID = id n.CreatedAtMS = now out = append(out, n) } return s.repo.CreateNotices(ctx, out) } func (s *Service) newNotice(to, from int64, kind entity.NoticeKind, postID, commentID int64) *entity.Notice { if to <= 0 || to == from { return nil } return &entity.Notice{ToUserID: to, FromUserID: from, Kind: kind, PostID: postID, CommentID: commentID} }