dukcapil/internal/processor/user_role_processor.go
2025-10-03 13:40:49 +07:00

131 lines
3.3 KiB
Go

package processor
import (
"context"
"time"
"eslogad-be/internal/entities"
"github.com/google/uuid"
"gorm.io/gorm"
)
type UserRoleProcessor interface {
AssignRoleToUser(ctx context.Context, userID, roleID uuid.UUID) error
RemoveRoleFromUser(ctx context.Context, userID, roleID uuid.UUID) error
GetUserRoles(ctx context.Context, userID uuid.UUID) ([]entities.Role, error)
HasRole(ctx context.Context, userID uuid.UUID, roleCode string) (bool, error)
ReplaceUserRole(ctx context.Context, userID, roleID uuid.UUID) error
}
type UserRoleProcessorImpl struct {
db *gorm.DB
}
func NewUserRoleProcessor(db *gorm.DB) *UserRoleProcessorImpl {
return &UserRoleProcessorImpl{
db: db,
}
}
type UserRoleEntry struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()"`
UserID uuid.UUID `gorm:"type:uuid;not null"`
RoleID uuid.UUID `gorm:"type:uuid;not null"`
AssignedAt time.Time `gorm:"default:CURRENT_TIMESTAMP"`
RemovedAt *time.Time
}
func (UserRoleEntry) TableName() string {
return "user_role"
}
func (p *UserRoleProcessorImpl) AssignRoleToUser(ctx context.Context, userID, roleID uuid.UUID) error {
var existingEntry UserRoleEntry
err := p.db.WithContext(ctx).
Where("user_id = ? AND role_id = ? AND removed_at IS NULL", userID, roleID).
First(&existingEntry).Error
if err == nil {
return nil
}
if err != gorm.ErrRecordNotFound {
return err
}
newEntry := UserRoleEntry{
UserID: userID,
RoleID: roleID,
AssignedAt: time.Now(),
}
return p.db.WithContext(ctx).Create(&newEntry).Error
}
func (p *UserRoleProcessorImpl) RemoveRoleFromUser(ctx context.Context, userID, roleID uuid.UUID) error {
now := time.Now()
return p.db.WithContext(ctx).
Model(&UserRoleEntry{}).
Where("user_id = ? AND role_id = ? AND removed_at IS NULL", userID, roleID).
Update("removed_at", now).Error
}
func (p *UserRoleProcessorImpl) GetUserRoles(ctx context.Context, userID uuid.UUID) ([]entities.Role, error) {
var roles []entities.Role
err := p.db.WithContext(ctx).
Table("roles as r").
Select("r.*").
Joins("JOIN user_role ur ON ur.role_id = r.id AND ur.removed_at IS NULL").
Where("ur.user_id = ?", userID).
Find(&roles).Error
return roles, err
}
func (p *UserRoleProcessorImpl) HasRole(ctx context.Context, userID uuid.UUID, roleCode string) (bool, error) {
var count int64
err := p.db.WithContext(ctx).
Table("user_role as ur").
Joins("JOIN roles r ON r.id = ur.role_id").
Where("ur.user_id = ? AND r.code = ? AND ur.removed_at IS NULL", userID, roleCode).
Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}
func (p *UserRoleProcessorImpl) ReplaceUserRole(ctx context.Context, userID uuid.UUID, roleID uuid.UUID) error {
return p.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// Delete old role
if err := tx.Where("user_id = ?", userID).
Delete(&UserRoleEntry{}).Error; err != nil {
return err
}
// Check if new role already exists
var existingEntry UserRoleEntry
err := tx.Where("user_id = ?", userID).
First(&existingEntry).Error
if err == nil {
// Role already assigned
return nil
}
if err != gorm.ErrRecordNotFound {
return err
}
// Assign new role
newEntry := UserRoleEntry{
UserID: userID,
RoleID: roleID,
AssignedAt: time.Now(),
}
return tx.Create(&newEntry).Error
})
}