package repository import ( "context" "sort" "sync" "time" "git.yuntuo.hk/k2safe/go-omni/examples/reference/model" ) // MemoryUsers stores data only for this process. Replace it with a database // implementation of service.UserRepository when persistence is required. type MemoryUsers struct { mu sync.RWMutex users map[int64]model.User emails map[string]int64 nextID int64 } func NewMemoryUsers() *MemoryUsers { return &MemoryUsers{ users: make(map[int64]model.User), emails: make(map[string]int64), nextID: 1, } } func (r *MemoryUsers) Create(ctx context.Context, input model.UserInput) (model.User, error) { r.mu.Lock() defer r.mu.Unlock() if err := ctx.Err(); err != nil { return model.User{}, err } if _, exists := r.emails[input.Email]; exists { return model.User{}, model.ErrEmailTaken } now := time.Now().UTC() user := model.User{ ID: r.nextID, Name: input.Name, Email: input.Email, CreatedAt: now, UpdatedAt: now, } r.nextID++ r.users[user.ID] = user r.emails[user.Email] = user.ID return user, nil } func (r *MemoryUsers) Get(ctx context.Context, id int64) (model.User, error) { r.mu.RLock() defer r.mu.RUnlock() if err := ctx.Err(); err != nil { return model.User{}, err } user, exists := r.users[id] if !exists { return model.User{}, model.ErrUserNotFound } return user, nil } func (r *MemoryUsers) List(ctx context.Context, page, pageSize int) (model.UserPage, error) { if page < 1 || pageSize < 1 || pageSize > 100 { return model.UserPage{}, model.ErrInvalidPage } r.mu.RLock() if err := ctx.Err(); err != nil { r.mu.RUnlock() return model.UserPage{}, err } users := make([]model.User, 0, len(r.users)) for _, user := range r.users { users = append(users, user) } r.mu.RUnlock() // A stable order is essential: Go map iteration order changes between calls. sort.Slice(users, func(i, j int) bool { return users[i].ID < users[j].ID }) result := model.UserPage{ Items: []model.User{}, Total: len(users), Page: page, PageSize: pageSize, } // Check the page before multiplying so even a very large page cannot overflow. if len(users) == 0 || page-1 > (len(users)-1)/pageSize { return result, nil } start := (page - 1) * pageSize count := min(pageSize, len(users)-start) result.Items = users[start : start+count] return result, nil } func (r *MemoryUsers) Update(ctx context.Context, id int64, input model.UserInput) (model.User, error) { r.mu.Lock() defer r.mu.Unlock() if err := ctx.Err(); err != nil { return model.User{}, err } user, exists := r.users[id] if !exists { return model.User{}, model.ErrUserNotFound } if owner, exists := r.emails[input.Email]; exists && owner != id { return model.User{}, model.ErrEmailTaken } delete(r.emails, user.Email) user.Name, user.Email = input.Name, input.Email user.UpdatedAt = time.Now().UTC() r.users[id] = user r.emails[user.Email] = id return user, nil } func (r *MemoryUsers) Delete(ctx context.Context, id int64) error { r.mu.Lock() defer r.mu.Unlock() if err := ctx.Err(); err != nil { return err } user, exists := r.users[id] if !exists { return model.ErrUserNotFound } delete(r.users, id) delete(r.emails, user.Email) return nil }