init push
This commit is contained in:
226
internal/repository/mysql/user_repo.go
Normal file
226
internal/repository/mysql/user_repo.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
|
||||
"wucher/internal/domain/auth"
|
||||
"wucher/internal/domain/user"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
)
|
||||
|
||||
type UserRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewUserRepository(db *gorm.DB) *UserRepository {
|
||||
return &UserRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *UserRepository) Create(ctx context.Context, u *user.User) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if actor := actorUserIDFromContext(ctx); len(actor) > 0 {
|
||||
u.CreatedBy = actor
|
||||
u.UpdatedBy = actor
|
||||
}
|
||||
if err := tx.Create(u).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return replaceUserRolesTx(ctx, tx, u.ID, u.RoleID, u.RoleIDs)
|
||||
})
|
||||
}
|
||||
|
||||
func (r *UserRepository) Update(ctx context.Context, u *user.User) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if actor := actorUserIDFromContext(ctx); len(actor) > 0 {
|
||||
u.UpdatedBy = actor
|
||||
}
|
||||
// Persist only user columns and avoid relation side-effects from preloaded structs.
|
||||
if err := tx.Omit(clause.Associations).Save(u).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// Keep legacy and current image columns in sync when image patch is requested.
|
||||
if u.ProfileAttachmentPatchSet {
|
||||
updates := map[string]any{
|
||||
"profile_attachment_id": nil,
|
||||
"image_attachment_id": nil,
|
||||
}
|
||||
if len(u.ProfileAttachmentID) == 16 {
|
||||
updates["profile_attachment_id"] = append([]byte(nil), u.ProfileAttachmentID...)
|
||||
updates["image_attachment_id"] = append([]byte(nil), u.ProfileAttachmentID...)
|
||||
}
|
||||
if err := tx.Model(&user.User{}).Where("id = ?", u.ID).Updates(updates).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if u.RoleIDs == nil {
|
||||
return nil
|
||||
}
|
||||
return replaceUserRolesTx(ctx, tx, u.ID, u.RoleID, u.RoleIDs)
|
||||
})
|
||||
}
|
||||
|
||||
func (r *UserRepository) Delete(ctx context.Context, id []byte) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Model(&user.User{}).
|
||||
Where("id = ?", id).
|
||||
Updates(map[string]any{
|
||||
"image_attachment_id": nil,
|
||||
"profile_attachment_id": nil,
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if idStr, err := uuidv7.BytesToString(id); err == nil && idStr != "" {
|
||||
if err := tx.Exec("DELETE FROM attachments WHERE ref_type = ? AND ref_id = ?", "user_profile", idStr).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tx.Where("user_id = ?", id).Delete(&auth.UserRole{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Delete(&user.User{}, "id = ?", id).Error
|
||||
})
|
||||
}
|
||||
|
||||
func (r *UserRepository) GetByID(ctx context.Context, id []byte) (*user.User, error) {
|
||||
var u user.User
|
||||
err := r.db.WithContext(ctx).
|
||||
Model(&user.User{}).
|
||||
Select("users.*, roles.name AS role_name, roles.description AS role_description").
|
||||
Joins("LEFT JOIN roles ON roles.id = users.role_id").
|
||||
Preload("ProfileAttachment").
|
||||
Preload("ProfileAttachment.File").
|
||||
Where("users.id = ?", id).
|
||||
First(&u).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := attachUserDomainRoles(ctx, r.db, &u); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
func (r *UserRepository) GetByEmail(ctx context.Context, email string) (*user.User, error) {
|
||||
var u user.User
|
||||
err := r.db.WithContext(ctx).
|
||||
Model(&user.User{}).
|
||||
Select("users.*, roles.name AS role_name, roles.description AS role_description").
|
||||
Joins("LEFT JOIN roles ON roles.id = users.role_id").
|
||||
Preload("ProfileAttachment").
|
||||
Preload("ProfileAttachment.File").
|
||||
Where("users.email = ?", email).
|
||||
First(&u).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := attachUserDomainRoles(ctx, r.db, &u); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
func (r *UserRepository) GetByUsername(ctx context.Context, username string) (*user.User, error) {
|
||||
var u user.User
|
||||
err := r.db.WithContext(ctx).
|
||||
Model(&user.User{}).
|
||||
Select("users.*, roles.name AS role_name, roles.description AS role_description").
|
||||
Joins("LEFT JOIN roles ON roles.id = users.role_id").
|
||||
Preload("ProfileAttachment").
|
||||
Preload("ProfileAttachment.File").
|
||||
Where("users.username = ?", username).
|
||||
First(&u).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := attachUserDomainRoles(ctx, r.db, &u); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
func (r *UserRepository) GetLastNameByID(ctx context.Context, id []byte) (string, error) {
|
||||
var u user.User
|
||||
err := r.db.WithContext(ctx).
|
||||
Table("users").
|
||||
Select("last_name").
|
||||
Where("id = ?", id).
|
||||
First(&u).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return "", nil
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.LastName, nil
|
||||
}
|
||||
|
||||
// GetShortNameByID returns the user's profile short name (pilot or technician
|
||||
// 3-letter code, e.g. "GAN"), or an empty string when the user has no profile
|
||||
// short name set. Callers apply their own fallback (e.g. initials).
|
||||
func (r *UserRepository) GetShortNameByID(ctx context.Context, id []byte) (string, error) {
|
||||
var out struct {
|
||||
ShortName string `gorm:"column:short_name"`
|
||||
}
|
||||
err := r.db.WithContext(ctx).
|
||||
Table("users u").
|
||||
Select("COALESCE(NULLIF(pp.short_name, ''), NULLIF(tp.short_name, ''), '') AS short_name").
|
||||
Joins("LEFT JOIN pilot_profiles pp ON pp.user_id = u.id").
|
||||
Joins("LEFT JOIN technician_profiles tp ON tp.user_id = u.id").
|
||||
Where("u.id = ?", id).
|
||||
Take(&out).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return "", nil
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out.ShortName, nil
|
||||
}
|
||||
|
||||
func (r *UserRepository) List(ctx context.Context, filter string, sort string, limit, offset int) ([]user.User, int64, error) {
|
||||
var users []user.User
|
||||
var total int64
|
||||
|
||||
base := r.db.WithContext(ctx).
|
||||
Model(&user.User{}).
|
||||
Joins("LEFT JOIN roles ON roles.id = users.role_id")
|
||||
if filter != "" {
|
||||
like := "%" + filter + "%"
|
||||
base = base.Where("users.email LIKE ? OR users.username LIKE ? OR users.first_name LIKE ? OR users.last_name LIKE ?", like, like, like, like)
|
||||
}
|
||||
query := base.Select("users.*, roles.name AS role_name, roles.description AS role_description")
|
||||
query = query.Preload("ProfileAttachment").Preload("ProfileAttachment.File")
|
||||
if sort != "" {
|
||||
query = query.Order(sort)
|
||||
}
|
||||
if err := base.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit).Offset(offset)
|
||||
}
|
||||
if err := query.Find(&users).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
ptrs := make([]*user.User, 0, len(users))
|
||||
for i := range users {
|
||||
ptrs = append(ptrs, &users[i])
|
||||
}
|
||||
if err := attachUserDomainRoles(ctx, r.db, ptrs...); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return users, total, nil
|
||||
}
|
||||
Reference in New Issue
Block a user