179 lines
5.1 KiB
Go
179 lines
5.1 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"veza-backend-api/internal/models"
|
|
)
|
|
|
|
// buildSearchClause returns the SQL fragment for case-insensitive search.
|
|
// SQLite: LIKE (case-insensitive for ASCII), PostgreSQL: ILIKE
|
|
func (s *UserService) buildSearchClause() string {
|
|
if s.db != nil && s.db.Dialector.Name() == "sqlite" {
|
|
return "username LIKE ? OR email LIKE ? OR first_name LIKE ? OR last_name LIKE ?"
|
|
}
|
|
return "username ILIKE ? OR email ILIKE ? OR first_name ILIKE ? OR last_name ILIKE ?"
|
|
}
|
|
|
|
// SearchUsersParams représente les paramètres de recherche d'utilisateurs
|
|
// BE-API-008: Implement user search endpoint
|
|
type SearchUsersParams struct {
|
|
Query string // Recherche par username, email, first_name, last_name
|
|
Page int // Numéro de page (défaut: 1)
|
|
Limit int // Nombre de résultats par page (défaut: 20, max: 100)
|
|
}
|
|
|
|
// SearchUsers recherche des utilisateurs selon les critères fournis
|
|
// BE-API-008: Implement user search endpoint
|
|
func (s *UserService) SearchUsers(ctx context.Context, params SearchUsersParams) ([]*models.User, int64, error) {
|
|
if s.db == nil {
|
|
return nil, 0, errors.New("database connection not available")
|
|
}
|
|
|
|
// Appliquer la pagination
|
|
if params.Limit <= 0 {
|
|
params.Limit = 20
|
|
}
|
|
if params.Limit > 100 {
|
|
params.Limit = 100
|
|
}
|
|
if params.Page < 1 {
|
|
params.Page = 1
|
|
}
|
|
offset := (params.Page - 1) * params.Limit
|
|
|
|
// Construire la requête de recherche
|
|
query := s.db.WithContext(ctx).Model(&models.User{})
|
|
|
|
// Recherche par query (username, email, first_name, last_name)
|
|
if params.Query != "" {
|
|
searchPattern := "%" + params.Query + "%"
|
|
query = query.Where(
|
|
s.buildSearchClause(),
|
|
searchPattern, searchPattern, searchPattern, searchPattern,
|
|
)
|
|
}
|
|
|
|
// Compter le total avant pagination
|
|
var total int64
|
|
if err := query.Count(&total).Error; err != nil {
|
|
return nil, 0, fmt.Errorf("failed to count users: %w", err)
|
|
}
|
|
|
|
// Appliquer pagination et récupérer les résultats
|
|
var users []*models.User
|
|
if err := query.Offset(offset).Limit(params.Limit).Find(&users).Error; err != nil {
|
|
return nil, 0, fmt.Errorf("failed to search users: %w", err)
|
|
}
|
|
|
|
// Exclure les mots de passe des résultats
|
|
for _, user := range users {
|
|
user.PasswordHash = ""
|
|
}
|
|
|
|
return users, total, nil
|
|
}
|
|
|
|
// ListUsersParams représente les paramètres de liste d'utilisateurs
|
|
// BE-API-040: Implement user list endpoint
|
|
type ListUsersParams struct {
|
|
Page int // Numéro de page (défaut: 1)
|
|
Limit int // Nombre de résultats par page (défaut: 20, max: 100)
|
|
Role string // Filtrer par rôle (optionnel)
|
|
IsActive *bool // Filtrer par statut actif (optionnel)
|
|
IsVerified *bool // Filtrer par statut vérifié (optionnel)
|
|
Search string // Recherche par username, email, first_name, last_name (optionnel)
|
|
SortBy string // Trier par champ (created_at, username, email) - défaut: created_at
|
|
SortOrder string // Ordre de tri (asc, desc) - défaut: desc
|
|
}
|
|
|
|
// ListUsers liste les utilisateurs avec pagination et filtrage
|
|
// BE-API-040: Implement user list endpoint
|
|
func (s *UserService) ListUsers(ctx context.Context, params ListUsersParams) ([]*models.User, int64, error) {
|
|
if s.db == nil {
|
|
return nil, 0, errors.New("database connection not available")
|
|
}
|
|
|
|
// Appliquer la pagination
|
|
if params.Limit <= 0 {
|
|
params.Limit = 20
|
|
}
|
|
if params.Limit > 100 {
|
|
params.Limit = 100
|
|
}
|
|
if params.Page < 1 {
|
|
params.Page = 1
|
|
}
|
|
offset := (params.Page - 1) * params.Limit
|
|
|
|
// Construire la requête
|
|
query := s.db.WithContext(ctx).Model(&models.User{})
|
|
|
|
// Appliquer les filtres
|
|
if params.Role != "" {
|
|
query = query.Where("role = ?", params.Role)
|
|
}
|
|
if params.IsActive != nil {
|
|
query = query.Where("is_active = ?", *params.IsActive)
|
|
}
|
|
if params.IsVerified != nil {
|
|
query = query.Where("is_verified = ?", *params.IsVerified)
|
|
}
|
|
|
|
// Recherche par query (username, email, first_name, last_name)
|
|
if params.Search != "" {
|
|
searchPattern := "%" + params.Search + "%"
|
|
query = query.Where(
|
|
s.buildSearchClause(),
|
|
searchPattern, searchPattern, searchPattern, searchPattern,
|
|
)
|
|
}
|
|
|
|
// Compter le total avant pagination
|
|
var total int64
|
|
if err := query.Count(&total).Error; err != nil {
|
|
return nil, 0, fmt.Errorf("failed to count users: %w", err)
|
|
}
|
|
|
|
// Appliquer le tri
|
|
sortBy := params.SortBy
|
|
if sortBy == "" {
|
|
sortBy = "created_at"
|
|
}
|
|
// Valider que sortBy est un champ valide
|
|
validSortFields := map[string]bool{
|
|
"created_at": true,
|
|
"username": true,
|
|
"email": true,
|
|
"last_login_at": true,
|
|
}
|
|
if !validSortFields[sortBy] {
|
|
sortBy = "created_at"
|
|
}
|
|
|
|
sortOrder := params.SortOrder
|
|
if sortOrder == "" {
|
|
sortOrder = "desc"
|
|
}
|
|
if sortOrder != "asc" && sortOrder != "desc" {
|
|
sortOrder = "desc"
|
|
}
|
|
|
|
query = query.Order(fmt.Sprintf("%s %s", sortBy, strings.ToUpper(sortOrder)))
|
|
|
|
// Appliquer pagination et récupérer les résultats
|
|
var users []*models.User
|
|
if err := query.Offset(offset).Limit(params.Limit).Find(&users).Error; err != nil {
|
|
return nil, 0, fmt.Errorf("failed to list users: %w", err)
|
|
}
|
|
|
|
// Exclure les mots de passe des résultats
|
|
for _, user := range users {
|
|
user.PasswordHash = ""
|
|
}
|
|
|
|
return users, total, nil
|
|
}
|