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