package model import ( "context" "sync" "time" "spark-server/internal/module/user/entity" "spark-server/internal/pkg/errs" ) type MemoryRepo struct { mu sync.Mutex byID map[int64]*entity.User byEmail map[string]int64 bySpark map[string]int64 devices map[deviceKey]*entity.Device logs []*entity.LoginLog deleted map[int64]*entity.Deleted deviceSeq int64 logSeq int64 } type deviceKey struct { UserID int64 DeviceID uint64 } func NewMemoryRepo() *MemoryRepo { return &MemoryRepo{ byID: make(map[int64]*entity.User), byEmail: make(map[string]int64), bySpark: make(map[string]int64), devices: make(map[deviceKey]*entity.Device), logs: make([]*entity.LoginLog, 0), deleted: make(map[int64]*entity.Deleted), } } func (m *MemoryRepo) GetByID(_ context.Context, id int64) (*entity.User, error) { m.mu.Lock() defer m.mu.Unlock() u, ok := m.byID[id] if !ok { return nil, errs.ErrNotFound } cp := *u return &cp, nil } func (m *MemoryRepo) GetByEmail(_ context.Context, email string) (*entity.User, error) { m.mu.Lock() defer m.mu.Unlock() id, ok := m.byEmail[email] if !ok { return nil, errs.ErrNotFound } cp := *m.byID[id] return &cp, nil } func (m *MemoryRepo) GetBySparkID(_ context.Context, sparkID string) (*entity.User, error) { m.mu.Lock() defer m.mu.Unlock() id, ok := m.bySpark[sparkID] if !ok { return nil, errs.ErrNotFound } cp := *m.byID[id] return &cp, nil } func (m *MemoryRepo) GetByQRCode(_ context.Context, qrcode string) (*entity.User, error) { m.mu.Lock() defer m.mu.Unlock() if qrcode == "" { return nil, errs.ErrNotFound } for _, u := range m.byID { if u.QRCode == qrcode { cp := *u return &cp, nil } } return nil, errs.ErrNotFound } func (m *MemoryRepo) CreateWithEmail(_ context.Context, u *entity.User, email string) error { m.mu.Lock() defer m.mu.Unlock() if _, ok := m.byEmail[email]; ok { return errs.ErrDuplicate } if _, ok := m.bySpark[u.SparkID]; ok { return errs.ErrDuplicate } if _, ok := m.byID[u.ID]; ok { return errs.ErrDuplicate } cp := *u m.byID[u.ID] = &cp m.byEmail[email] = u.ID m.bySpark[u.SparkID] = u.ID return nil } func (m *MemoryRepo) UpdateSparkID(_ context.Context, userID int64, sparkID string) error { m.mu.Lock() defer m.mu.Unlock() u, ok := m.byID[userID] if !ok { return errs.ErrNotFound } if other, ok := m.bySpark[sparkID]; ok && other != userID { return errs.ErrDuplicate } delete(m.bySpark, u.SparkID) u.SparkID = sparkID u.UpdatedAt = time.Now() m.bySpark[sparkID] = userID return nil } func (m *MemoryRepo) TouchLogin(_ context.Context, userID int64, at time.Time) error { m.mu.Lock() defer m.mu.Unlock() u, ok := m.byID[userID] if !ok { return errs.ErrNotFound } u.LastLoginAt = at u.LastActiveAt = at return nil } func (m *MemoryRepo) UpsertDevice(_ context.Context, d *entity.Device) error { m.mu.Lock() defer m.mu.Unlock() key := deviceKey{UserID: d.UserID, DeviceID: d.DeviceID} if old, ok := m.devices[key]; ok { d.ID = old.ID d.CreatedAt = old.CreatedAt } else if d.ID == 0 { m.deviceSeq++ d.ID = m.deviceSeq } cp := *d m.devices[key] = &cp return nil } func (m *MemoryRepo) GetDevice(_ context.Context, userID int64, deviceID uint64) (*entity.Device, error) { m.mu.Lock() defer m.mu.Unlock() d, ok := m.devices[deviceKey{UserID: userID, DeviceID: deviceID}] if !ok { return nil, errs.ErrNotFound } cp := *d return &cp, nil } func (m *MemoryRepo) UpdateQRCode(_ context.Context, userID int64, qrcode string) error { m.mu.Lock() defer m.mu.Unlock() u, ok := m.byID[userID] if !ok { return errs.ErrNotFound } u.QRCode = qrcode return nil } func (m *MemoryRepo) UpdateProfile(_ context.Context, u *entity.User) error { if u == nil || u.ID <= 0 { return errs.ErrInvalidArgument } m.mu.Lock() defer m.mu.Unlock() cur, ok := m.byID[u.ID] if !ok { return errs.ErrNotFound } if other, ok := m.bySpark[u.SparkID]; ok && other != u.ID { return errs.ErrDuplicate } delete(m.bySpark, cur.SparkID) cur.SparkID = u.SparkID cur.Nickname = u.Nickname cur.Avatar = u.Avatar cur.Gender = u.Gender cur.Birthday = u.Birthday cur.UpdatedAt = u.UpdatedAt m.bySpark[cur.SparkID] = u.ID return nil } func (m *MemoryRepo) InsertLoginLog(_ context.Context, log *entity.LoginLog) error { if log == nil { return nil } m.mu.Lock() defer m.mu.Unlock() m.logSeq++ cp := *log cp.ID = m.logSeq log.ID = cp.ID m.logs = append(m.logs, &cp) return nil } func (m *MemoryRepo) GetEmail(_ context.Context, userID int64) (string, error) { if userID <= 0 { return "", errs.ErrInvalidArgument } m.mu.Lock() defer m.mu.Unlock() for email, id := range m.byEmail { if id == userID { return email, nil } } return "", errs.ErrNotFound } func (m *MemoryRepo) GetDeleted(_ context.Context, id int64) (*entity.Deleted, error) { m.mu.Lock() defer m.mu.Unlock() row, ok := m.deleted[id] if !ok { return nil, errs.ErrNotFound } cp := *row return &cp, nil } func (m *MemoryRepo) ArchiveAndDelete(_ context.Context, d *entity.Deleted) error { if d == nil || d.ID <= 0 { return errs.ErrInvalidArgument } m.mu.Lock() defer m.mu.Unlock() u, ok := m.byID[d.ID] if !ok { return errs.ErrNotFound } email := d.Email for em, id := range m.byEmail { if id == d.ID { email = em delete(m.byEmail, em) break } } if d.DeletedAt.IsZero() { d.DeletedAt = time.Now() } snap := entity.SnapshotDeleted(u, email, d.DeletedAt) m.deleted[d.ID] = snap delete(m.byID, d.ID) delete(m.bySpark, u.SparkID) for key := range m.devices { if key.UserID == d.ID { delete(m.devices, key) } } kept := m.logs[:0] for _, log := range m.logs { if log != nil && log.UserID != d.ID { kept = append(kept, log) } } m.logs = kept return nil } func (m *MemoryRepo) LoginLogs() []*entity.LoginLog { m.mu.Lock() defer m.mu.Unlock() out := make([]*entity.LoginLog, len(m.logs)) for i, row := range m.logs { cp := *row out[i] = &cp } return out }