package model import ( "context" "strings" "time" "spark-server/internal/module/square/entity" "spark-server/internal/pkg/errs" ) func (m *MemoryRepo) CreatePost(_ context.Context, post *entity.Post, audience []int64) error { if post == nil || post.ID <= 0 || post.UserID <= 0 { return errs.ErrInvalidArgument } m.mu.Lock() defer m.mu.Unlock() if _, ok := m.posts[post.ID]; ok { return errs.ErrDuplicate } m.posts[post.ID] = clonePost(post) if len(audience) > 0 { m.audience[post.ID] = append([]int64(nil), audience...) } return nil } func (m *MemoryRepo) CreateBotPost(_ context.Context, post *entity.Post) error { if post == nil || post.ID <= 0 || post.UserID <= 0 { return errs.ErrInvalidArgument } url := strings.TrimSpace(post.SourceURL) if url == "" { return errs.ErrInvalidArgument } post.SourceURL = url if post.Source == 0 { post.Source = entity.SourceBotRSS } m.mu.Lock() defer m.mu.Unlock() if _, ok := m.botLinks[url]; ok { return errs.ErrDuplicate } if _, ok := m.posts[post.ID]; ok { return errs.ErrDuplicate } m.botLinks[url] = post.ID m.posts[post.ID] = clonePost(post) return nil } func (m *MemoryRepo) SoftDeleteExpiredBotPosts(_ context.Context, nowMS int64, limit int) ([]*entity.Post, error) { if limit <= 0 { limit = 100 } if nowMS <= 0 { nowMS = time.Now().UnixMilli() } m.mu.Lock() defer m.mu.Unlock() out := make([]*entity.Post, 0) for _, row := range m.posts { if row.Status != entity.StatusNormal || !row.Source.IsBot() { continue } if !row.Expired(nowMS) { continue } row.Status = entity.StatusDeleted out = append(out, clonePost(row)) if len(out) >= limit { break } } return out, nil } func (m *MemoryRepo) SoftDeletePost(_ context.Context, userID, postID int64) (*entity.Post, error) { m.mu.Lock() defer m.mu.Unlock() row, ok := m.posts[postID] if !ok || row.UserID != userID || row.Status == entity.StatusDeleted { return nil, errs.ErrNotFound } row.Status = entity.StatusDeleted return clonePost(row), nil } func (m *MemoryRepo) AddLike(_ context.Context, like *entity.Like) (bool, error) { if like == nil || like.PostID <= 0 || like.UserID <= 0 { return false, errs.ErrInvalidArgument } m.mu.Lock() defer m.mu.Unlock() post, ok := m.posts[like.PostID] if !ok { return false, errs.ErrNotFound } key := likeKey{PostID: like.PostID, UserID: like.UserID} if _, ok := m.likes[key]; ok { return false, nil } cp := *like if cp.CreatedAtMS == 0 { cp.CreatedAtMS = time.Now().UnixMilli() } m.likes[key] = &cp post.LikeCount++ return true, nil } func (m *MemoryRepo) RemoveLike(_ context.Context, postID, userID int64) error { m.mu.Lock() defer m.mu.Unlock() key := likeKey{PostID: postID, UserID: userID} if _, ok := m.likes[key]; !ok { return errs.ErrNotFound } delete(m.likes, key) if post, ok := m.posts[postID]; ok && post.LikeCount > 0 { post.LikeCount-- } return nil } func (m *MemoryRepo) AddComment(_ context.Context, c *entity.Comment) error { if c == nil || c.ID <= 0 || c.PostID <= 0 || c.UserID <= 0 { return errs.ErrInvalidArgument } m.mu.Lock() defer m.mu.Unlock() post, ok := m.posts[c.PostID] if !ok { return errs.ErrNotFound } if _, ok := m.comments[c.ID]; ok { return errs.ErrDuplicate } m.comments[c.ID] = cloneComment(c) post.CommentCount++ return nil } func (m *MemoryRepo) SoftDeleteComment(_ context.Context, id int64) error { m.mu.Lock() defer m.mu.Unlock() row, ok := m.comments[id] if !ok || row.Status == entity.StatusDeleted { return errs.ErrNotFound } row.Status = entity.StatusDeleted if post, ok := m.posts[row.PostID]; ok && post.CommentCount > 0 { post.CommentCount-- } return nil } func (m *MemoryRepo) CreateNotices(_ context.Context, rows []*entity.Notice) error { m.mu.Lock() defer m.mu.Unlock() for _, n := range rows { if n == nil || n.ID <= 0 { return errs.ErrInvalidArgument } if _, ok := m.notices[n.ID]; ok { return errs.ErrDuplicate } cp := *n m.notices[n.ID] = &cp } return nil } func (m *MemoryRepo) MarkNoticesRead(_ context.Context, userID, cursor int64) error { m.mu.Lock() defer m.mu.Unlock() now := time.Now().UnixMilli() for _, n := range m.notices { if n.ToUserID != userID || n.ReadAtMS > 0 { continue } if cursor > 0 && n.ID > cursor { continue } n.ReadAtMS = now } return nil } func (m *MemoryRepo) AdvanceNoticeRead(_ context.Context, userID, noticeID int64) error { if userID <= 0 || noticeID <= 0 { return errs.ErrInvalidArgument } m.mu.Lock() defer m.mu.Unlock() cur := m.noticeState[userID] if cur == nil { cur = entity.DefaultNoticeState(userID) m.noticeState[userID] = cur } if cur.LastReadNoticeID >= noticeID { return nil } cur.LastReadNoticeID = noticeID cur.UpdatedAtMS = time.Now().UnixMilli() return nil }