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>
220 lines
4.5 KiB
Go
220 lines
4.5 KiB
Go
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
|
|
}
|