Add HTTP /v1 MVC, TCP send path, groups with server-side member-avatar collage, object-storage upload, and friends/favorites around the architecture-v3 layout. Co-authored-by: Cursor <cursoragent@cursor.com>
105 lines
2.3 KiB
Go
105 lines
2.3 KiB
Go
package model
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"sync"
|
|
|
|
"spark-server/internal/module/favorite/entity"
|
|
"spark-server/internal/pkg/errs"
|
|
)
|
|
|
|
type userMsgKey struct {
|
|
UserID int64
|
|
MsgID int64
|
|
}
|
|
|
|
// MemoryRepo is an in-process favorite store for tests and empty-DSN bring-up.
|
|
type MemoryRepo struct {
|
|
mu sync.Mutex
|
|
byID map[int64]*entity.Favorite
|
|
byPair map[userMsgKey]int64
|
|
}
|
|
|
|
func NewMemoryRepo() *MemoryRepo {
|
|
return &MemoryRepo{byID: make(map[int64]*entity.Favorite), byPair: make(map[userMsgKey]int64)}
|
|
}
|
|
|
|
func (m *MemoryRepo) Get(_ context.Context, id int64) (*entity.Favorite, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
row, ok := m.byID[id]
|
|
if !ok {
|
|
return nil, errs.ErrNotFound
|
|
}
|
|
return cloneFav(row), nil
|
|
}
|
|
|
|
func (m *MemoryRepo) GetByUserMsg(_ context.Context, userID, msgID int64) (*entity.Favorite, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
id, ok := m.byPair[userMsgKey{UserID: userID, MsgID: msgID}]
|
|
if !ok {
|
|
return nil, errs.ErrNotFound
|
|
}
|
|
return cloneFav(m.byID[id]), nil
|
|
}
|
|
|
|
func (m *MemoryRepo) Create(_ context.Context, fav *entity.Favorite) error {
|
|
if fav == nil || fav.ID <= 0 || fav.UserID <= 0 {
|
|
return errs.ErrInvalidArgument
|
|
}
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
key := userMsgKey{UserID: fav.UserID, MsgID: fav.MsgID}
|
|
if _, ok := m.byPair[key]; ok {
|
|
return errs.ErrDuplicate
|
|
}
|
|
if _, ok := m.byID[fav.ID]; ok {
|
|
return errs.ErrDuplicate
|
|
}
|
|
m.byID[fav.ID] = cloneFav(fav)
|
|
m.byPair[key] = fav.ID
|
|
return nil
|
|
}
|
|
|
|
func (m *MemoryRepo) ListByUser(_ context.Context, userID int64, limit int) ([]*entity.Favorite, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
limit = entity.ClampLimit(limit)
|
|
out := make([]*entity.Favorite, 0)
|
|
for _, row := range m.byID {
|
|
if row.UserID == userID {
|
|
out = append(out, cloneFav(row))
|
|
}
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].ID > out[j].ID })
|
|
if len(out) > limit {
|
|
out = out[:limit]
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) Delete(_ context.Context, userID, id int64) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
row, ok := m.byID[id]
|
|
if !ok || row.UserID != userID {
|
|
return errs.ErrNotFound
|
|
}
|
|
delete(m.byID, id)
|
|
delete(m.byPair, userMsgKey{UserID: row.UserID, MsgID: row.MsgID})
|
|
return nil
|
|
}
|
|
|
|
func cloneFav(f *entity.Favorite) *entity.Favorite {
|
|
if f == nil {
|
|
return nil
|
|
}
|
|
cp := *f
|
|
if f.Content != nil {
|
|
cp.Content = append([]byte(nil), f.Content...)
|
|
}
|
|
return &cp
|
|
}
|