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} }