spark-server/internal/module/square/model/memory_write.go
DESKTOP-L8OUL9F\gfl0034 715ace7ac6 feat(square): 广场 Bot/RSS 发布与媒体校验
支持广场机器人发帖与媒体类型检查,并补充相关迁移与 i18n。
2026-09-24 23:07:06 +08:00

209 lines
4.7 KiB
Go

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
}