spark-server/internal/module/friend/model/memory.go
2026-09-18 17:51:42 +08:00

191 lines
4.3 KiB
Go

package model
import (
"context"
"sort"
"sync"
"time"
"spark-server/internal/module/friend/entity"
"spark-server/internal/pkg/errs"
)
type edgeKey struct {
UserID int64
FriendID int64
}
type requestKey struct {
From int64
To int64
}
// MemoryRepo is an in-process friend store for tests and empty-DSN bring-up.
type MemoryRepo struct {
mu sync.Mutex
relations map[edgeKey]*entity.Relation
requests map[int64]*entity.Request
byPair map[requestKey]int64
reports []*entity.Report
}
func NewMemoryRepo() *MemoryRepo {
return &MemoryRepo{
relations: make(map[edgeKey]*entity.Relation),
requests: make(map[int64]*entity.Request),
byPair: make(map[requestKey]int64),
}
}
func (m *MemoryRepo) GetRelation(_ context.Context, userID, friendID int64) (*entity.Relation, error) {
m.mu.Lock()
defer m.mu.Unlock()
rel, ok := m.relations[edgeKey{UserID: userID, FriendID: friendID}]
if !ok {
return nil, errs.ErrNotFound
}
cp := *rel
return &cp, nil
}
func (m *MemoryRepo) ListFriendIDs(_ context.Context, userID int64) ([]int64, error) {
m.mu.Lock()
defer m.mu.Unlock()
out := make([]int64, 0)
for _, rel := range m.relations {
if rel.UserID == userID && rel.Status == entity.StatusFriend {
out = append(out, rel.FriendID)
}
}
return out, nil
}
func (m *MemoryRepo) ListBlockedPeerIDs(_ context.Context, userID int64) ([]int64, error) {
m.mu.Lock()
defer m.mu.Unlock()
seen := make(map[int64]struct{})
for _, rel := range m.relations {
if !rel.Blocked {
continue
}
var peer int64
switch {
case rel.UserID == userID:
peer = rel.FriendID
case rel.FriendID == userID:
peer = rel.UserID
default:
continue
}
seen[peer] = struct{}{}
}
out := make([]int64, 0, len(seen))
for id := range seen {
out = append(out, id)
}
return out, 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.UserID == userID && rel.Status == entity.StatusFriend {
cp := *rel
out = append(out, &cp)
}
}
sort.Slice(out, func(i, j int) bool {
if out[i].Pinned != out[j].Pinned {
return out[i].Pinned
}
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 entity.RequestStatus, 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 (m *MemoryRepo) rejectPendingLocked(a, b int64, at time.Time) {
for _, req := range m.requests {
if req.Status != entity.RequestPending {
continue
}
pair := (req.FromUser == a && req.ToUser == b) || (req.FromUser == b && req.ToUser == a)
if pair {
req.Status = entity.RequestRejected
req.UpdatedAt = at
}
}
}