225 lines
5.3 KiB
Go
225 lines
5.3 KiB
Go
package model
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"sync"
|
|
|
|
"spark-server/internal/module/square/entity"
|
|
"spark-server/internal/pkg/errs"
|
|
)
|
|
|
|
type likeKey struct {
|
|
PostID int64
|
|
UserID int64
|
|
}
|
|
|
|
// MemoryRepo is an in-process square store for tests and empty-DSN bring-up.
|
|
type MemoryRepo struct {
|
|
mu sync.Mutex
|
|
posts map[int64]*entity.Post
|
|
audience map[int64][]int64
|
|
likes map[likeKey]*entity.Like
|
|
comments map[int64]*entity.Comment
|
|
notices map[int64]*entity.Notice
|
|
noticeState map[int64]*entity.NoticeState
|
|
}
|
|
|
|
func NewMemoryRepo() *MemoryRepo {
|
|
return &MemoryRepo{
|
|
posts: make(map[int64]*entity.Post),
|
|
audience: make(map[int64][]int64),
|
|
likes: make(map[likeKey]*entity.Like),
|
|
comments: make(map[int64]*entity.Comment),
|
|
notices: make(map[int64]*entity.Notice),
|
|
noticeState: make(map[int64]*entity.NoticeState),
|
|
}
|
|
}
|
|
|
|
func (m *MemoryRepo) GetPost(_ context.Context, id int64) (*entity.Post, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
row, ok := m.posts[id]
|
|
if !ok {
|
|
return nil, errs.ErrNotFound
|
|
}
|
|
return clonePost(row), nil
|
|
}
|
|
|
|
func (m *MemoryRepo) ListByAuthors(_ context.Context, authorIDs []int64, cursor int64, limit int) ([]*entity.Post, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
allow := toSet(authorIDs)
|
|
out := make([]*entity.Post, 0)
|
|
for _, row := range m.posts {
|
|
if row.Status != entity.StatusNormal || !allow[row.UserID] {
|
|
continue
|
|
}
|
|
if cursor > 0 && row.ID >= cursor {
|
|
continue
|
|
}
|
|
out = append(out, clonePost(row))
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].ID > out[j].ID })
|
|
if limit > 0 && len(out) > limit {
|
|
out = out[:limit]
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) ListByAuthor(ctx context.Context, authorID, cursor int64, limit int) ([]*entity.Post, error) {
|
|
return m.ListByAuthors(ctx, []int64{authorID}, cursor, limit)
|
|
}
|
|
|
|
func (m *MemoryRepo) ListAudience(_ context.Context, postIDs []int64) (map[int64][]int64, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
allow := toSet(postIDs)
|
|
out := make(map[int64][]int64)
|
|
for id, peers := range m.audience {
|
|
if !allow[id] {
|
|
continue
|
|
}
|
|
out[id] = append([]int64(nil), peers...)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) ListLikes(_ context.Context, postIDs []int64) (map[int64][]*entity.Like, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
allow := toSet(postIDs)
|
|
out := make(map[int64][]*entity.Like)
|
|
for _, like := range m.likes {
|
|
if !allow[like.PostID] {
|
|
continue
|
|
}
|
|
cp := *like
|
|
out[like.PostID] = append(out[like.PostID], &cp)
|
|
}
|
|
for id := range out {
|
|
sort.Slice(out[id], func(i, j int) bool { return out[id][i].CreatedAtMS < out[id][j].CreatedAtMS })
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) ListComments(_ context.Context, postIDs []int64) (map[int64][]*entity.Comment, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
allow := toSet(postIDs)
|
|
out := make(map[int64][]*entity.Comment)
|
|
for _, c := range m.comments {
|
|
if !allow[c.PostID] {
|
|
continue
|
|
}
|
|
out[c.PostID] = append(out[c.PostID], cloneComment(c))
|
|
}
|
|
for id := range out {
|
|
sort.Slice(out[id], func(i, j int) bool { return out[id][i].ID < out[id][j].ID })
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) GetComment(_ context.Context, id int64) (*entity.Comment, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
row, ok := m.comments[id]
|
|
if !ok {
|
|
return nil, errs.ErrNotFound
|
|
}
|
|
return cloneComment(row), nil
|
|
}
|
|
|
|
func (m *MemoryRepo) ListPublicFeed(_ context.Context, cursor int64, limit int) ([]*entity.Post, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
out := make([]*entity.Post, 0)
|
|
for _, row := range m.posts {
|
|
if row.Status != entity.StatusNormal || row.AuditStatus != entity.AuditApproved {
|
|
continue
|
|
}
|
|
if row.Visibility != entity.VisibilityPublic && row.Visibility != entity.VisibilityExclude {
|
|
continue
|
|
}
|
|
if cursor > 0 && row.ID >= cursor {
|
|
continue
|
|
}
|
|
out = append(out, clonePost(row))
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].ID > out[j].ID })
|
|
if limit > 0 && len(out) > limit {
|
|
out = out[:limit]
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) GetNoticeState(_ context.Context, userID int64) (*entity.NoticeState, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if row, ok := m.noticeState[userID]; ok {
|
|
cp := *row
|
|
return &cp, nil
|
|
}
|
|
return entity.DefaultNoticeState(userID), nil
|
|
}
|
|
|
|
func (m *MemoryRepo) LatestNoticeID(_ context.Context, userID int64) (int64, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
var latest int64
|
|
for _, n := range m.notices {
|
|
if n.ToUserID == userID && n.ID > latest {
|
|
latest = n.ID
|
|
}
|
|
}
|
|
return latest, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) ListNotices(_ context.Context, userID, cursor int64, limit int) ([]*entity.Notice, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
out := make([]*entity.Notice, 0)
|
|
for _, n := range m.notices {
|
|
if n.ToUserID != userID {
|
|
continue
|
|
}
|
|
if cursor > 0 && n.ID >= cursor {
|
|
continue
|
|
}
|
|
cp := *n
|
|
out = append(out, &cp)
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].ID > out[j].ID })
|
|
if limit > 0 && len(out) > limit {
|
|
out = out[:limit]
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func toSet(ids []int64) map[int64]bool {
|
|
out := make(map[int64]bool, len(ids))
|
|
for _, id := range ids {
|
|
out[id] = true
|
|
}
|
|
return out
|
|
}
|
|
|
|
func clonePost(p *entity.Post) *entity.Post {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
cp := *p
|
|
if p.Content != nil {
|
|
cp.Content = append([]byte(nil), p.Content...)
|
|
}
|
|
return &cp
|
|
}
|
|
|
|
func cloneComment(c *entity.Comment) *entity.Comment {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
cp := *c
|
|
return &cp
|
|
}
|