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>
154 lines
3.4 KiB
Go
154 lines
3.4 KiB
Go
package model
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
|
|
"spark-server/internal/module/friend/entity"
|
|
"spark-server/internal/pkg/errs"
|
|
)
|
|
|
|
type pairKey struct {
|
|
Small int64
|
|
Big int64
|
|
}
|
|
|
|
type requestKey struct {
|
|
From int64
|
|
To int64
|
|
}
|
|
|
|
type remarkKey struct {
|
|
UserID int64
|
|
FriendID int64
|
|
}
|
|
|
|
type blockKey struct {
|
|
UserID int64
|
|
BlockedID int64
|
|
}
|
|
|
|
// MemoryRepo is an in-process friend store for tests and empty-DSN bring-up.
|
|
type MemoryRepo struct {
|
|
mu sync.Mutex
|
|
relations map[pairKey]*entity.Relation
|
|
requests map[int64]*entity.Request
|
|
byPair map[requestKey]int64
|
|
remarks map[remarkKey]string
|
|
blocks map[blockKey]time.Time
|
|
reports []*entity.Report
|
|
}
|
|
|
|
func NewMemoryRepo() *MemoryRepo {
|
|
return &MemoryRepo{
|
|
relations: make(map[pairKey]*entity.Relation),
|
|
requests: make(map[int64]*entity.Request),
|
|
byPair: make(map[requestKey]int64),
|
|
remarks: make(map[remarkKey]string),
|
|
blocks: make(map[blockKey]time.Time),
|
|
}
|
|
}
|
|
|
|
func (m *MemoryRepo) GetRelation(_ context.Context, a, b int64) (*entity.Relation, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
rel, ok := m.relations[pairOf(a, b)]
|
|
if !ok {
|
|
return nil, errs.ErrNotFound
|
|
}
|
|
cp := *rel
|
|
return &cp, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) ListFriends(_ context.Context, userID int64, limit int) ([]*entity.Relation, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
limit = entity.ClampLimit(limit)
|
|
out := make([]*entity.Relation, 0)
|
|
for _, rel := range m.relations {
|
|
if rel.UserSmall == userID || rel.UserBig == userID {
|
|
cp := *rel
|
|
out = append(out, &cp)
|
|
}
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].CreatedAt.After(out[j].CreatedAt) })
|
|
if len(out) > limit {
|
|
out = out[:limit]
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) GetRequest(_ context.Context, id int64) (*entity.Request, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
req, ok := m.requests[id]
|
|
if !ok {
|
|
return nil, errs.ErrNotFound
|
|
}
|
|
cp := *req
|
|
return &cp, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) GetRequestByPair(_ context.Context, from, to int64) (*entity.Request, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
id, ok := m.byPair[requestKey{From: from, To: to}]
|
|
if !ok {
|
|
return nil, errs.ErrNotFound
|
|
}
|
|
cp := *m.requests[id]
|
|
return &cp, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) UpsertRequest(_ context.Context, req *entity.Request) error {
|
|
if req == nil || req.FromUser <= 0 || req.ToUser <= 0 {
|
|
return errs.ErrInvalidArgument
|
|
}
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
key := requestKey{From: req.FromUser, To: req.ToUser}
|
|
if id, ok := m.byPair[key]; ok {
|
|
cur := m.requests[id]
|
|
cur.Source = req.Source
|
|
cur.Reason = req.Reason
|
|
cur.Remark = req.Remark
|
|
cur.Status = req.Status
|
|
cur.UpdatedAt = req.UpdatedAt
|
|
*req = *cur
|
|
return nil
|
|
}
|
|
cp := *req
|
|
m.requests[req.ID] = &cp
|
|
m.byPair[key] = req.ID
|
|
return nil
|
|
}
|
|
|
|
func (m *MemoryRepo) ListIncoming(_ context.Context, toUserID int64, status, limit int) ([]*entity.Request, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
limit = entity.ClampLimit(limit)
|
|
out := make([]*entity.Request, 0)
|
|
for _, req := range m.requests {
|
|
if req.ToUser != toUserID {
|
|
continue
|
|
}
|
|
if status > 0 && req.Status != status {
|
|
continue
|
|
}
|
|
cp := *req
|
|
out = append(out, &cp)
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].UpdatedAt.After(out[j].UpdatedAt) })
|
|
if len(out) > limit {
|
|
out = out[:limit]
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func pairOf(a, b int64) pairKey {
|
|
small, big := entity.OrderUsers(a, b)
|
|
return pairKey{Small: small, Big: big}
|
|
}
|