Files
gochat/backend/internal/repository/user_repo.go
T
rogee aeddedf2a3 Reorganize repo: backend/, deploy/, docs/ layout + AGENTS.md
Restructure the monorepo into clear top-level directories:
- backend/: Go module root (cmd, internal, pkg, configs, migrations,
  docs/swagger, scripts, tests, go.mod, Makefile, .air.toml)
- deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd
- docs/: project documentation + reports/ (moved from repo root)
- AGENTS.md: new AI coding-agent guide at repo root

Update all references to the new layout:
- Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root)
- docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile,
  env_file ../../.env, volume mounts ../../backend:/app
- deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile
- CI: working-directory: backend for go commands, file deploy/docker/Dockerfile,
  coverage path backend/coverage.out, health_check backend/scripts/
- backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../
- README: architecture tree, quickstart, config paths updated

Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh,
gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to
preserve history. Build, vet, SQLite tests, and docker compose config verified.
2026-07-07 14:44:12 +08:00

142 lines
4.3 KiB
Go

package repository
import (
"context"
"gorm.io/gorm"
"github.com/gochat/gochat/internal/model"
)
// UserRepo implements GORM repository for User.
// Reference: Chatwoot app/models/user.rb
type UserRepo struct {
db *gorm.DB
}
// NewUserRepo creates a new User repository.
func NewUserRepo(db *gorm.DB) *UserRepo {
return &UserRepo{db: db}
}
// FindByID retrieves a user by primary key.
func (r *UserRepo) FindByID(ctx context.Context, id uint) (*model.User, error) {
var user model.User
err := r.db.WithContext(ctx).First(&user, id).Error
if err != nil {
return nil, err
}
return &user, nil
}
// FindByIDs retrieves multiple users by their primary keys.
func (r *UserRepo) FindByIDs(ctx context.Context, ids []uint) ([]model.User, error) {
if len(ids) == 0 {
return []model.User{}, nil
}
var users []model.User
err := r.db.WithContext(ctx).Where("id IN ?", ids).Find(&users).Error
return users, err
}
// FindByEmail retrieves a user by email address.
func (r *UserRepo) FindByEmail(ctx context.Context, email string) (*model.User, error) {
var user model.User
err := r.db.WithContext(ctx).Where("email = ?", email).First(&user).Error
if err != nil {
return nil, err
}
return &user, nil
}
// Create inserts a new user.
func (r *UserRepo) Create(ctx context.Context, user *model.User) error {
return r.db.WithContext(ctx).Create(user).Error
}
// Update modifies an existing user.
func (r *UserRepo) Update(ctx context.Context, user *model.User) error {
return r.db.WithContext(ctx).Save(user).Error
}
// Delete soft-deletes a user.
func (r *UserRepo) Delete(ctx context.Context, id uint) error {
return r.db.WithContext(ctx).Delete(&model.User{}, id).Error
}
// List retrieves all users with pagination.
func (r *UserRepo) List(ctx context.Context, offset, limit int) ([]model.User, int64, error) {
var users []model.User
var total int64
if err := r.db.WithContext(ctx).Model(&model.User{}).Count(&total).Error; err != nil {
return nil, 0, err
}
err := r.db.WithContext(ctx).Offset(offset).Limit(limit).Order("id ASC").
Find(&users).Error
return users, total, err
}
// Search searches users by name or email.
func (r *UserRepo) Search(ctx context.Context, query string, offset, limit int) ([]model.User, int64, error) {
var users []model.User
var total int64
likeQuery := "%" + query + "%"
condition := r.db.WithContext(ctx).Model(&model.User{}).
Where("name ILIKE ? OR email ILIKE ?", likeQuery, likeQuery)
if err := condition.Count(&total).Error; err != nil {
return nil, 0, err
}
err := condition.Offset(offset).Limit(limit).Order("id ASC").
Find(&users).Error
return users, total, err
}
// UpdateAvailability updates user availability status.
// Accepts "online"/"available" → true, anything else → false.
func (r *UserRepo) UpdateAvailability(ctx context.Context, id uint, availability string) error {
isAvailable := availability == "online" || availability == "available"
return r.db.WithContext(ctx).Model(&model.User{}).Where("id = ?", id).
Update("available", isAvailable).Error
}
// UpdateLastSignIn updates sign-in tracking fields.
func (r *UserRepo) UpdateLastSignIn(ctx context.Context, id uint, ip string) error {
return r.db.WithContext(ctx).Model(&model.User{}).Where("id = ?", id).
Updates(map[string]interface{}{
"last_sign_in_ip": ip,
"current_sign_in_ip": ip,
"sign_in_count": gorm.Expr("sign_in_count + 1"),
}).Error
}
// FindByAccount retrieves users belonging to an account (delegated to AccountRepo).
func (r *UserRepo) FindByAccount(ctx context.Context, accountID uint, offset, limit int) ([]model.User, int64, error) {
var users []model.User
var total int64
countDB := r.db.WithContext(ctx).Model(&model.User{}).
Joins("JOIN account_users ON account_users.user_id = users.id").
Where("account_users.account_id = ?", accountID)
if err := countDB.Count(&total).Error; err != nil {
return nil, 0, err
}
err := r.db.WithContext(ctx).
Joins("JOIN account_users ON account_users.user_id = users.id").
Where("account_users.account_id = ?", accountID).
Offset(offset).Limit(limit).Order("id ASC").
Find(&users).Error
return users, total, err
}
// Count returns total number of users.
func (r *UserRepo) Count(ctx context.Context) (int64, error) {
var total int64
err := r.db.WithContext(ctx).Model(&model.User{}).Count(&total).Error
return total, err
}