add user role

This commit is contained in:
Aditya Siregar
2025-09-08 15:21:17 +07:00
parent 2319019eb2
commit d869d83d4b
14 changed files with 162 additions and 56 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ type cacheEntry struct {
expiresAt time.Time
}
func NewCachedUserProcessor(userRepo *repository.UserRepositoryImpl, profileRepo *repository.UserProfileRepository) *CachedUserProcessor {
func NewCachedUserProcessor(userRepo *repository.UserRepositoryImpl, profileRepo *repository.UserProfileRepository, userRoleProc UserRoleProcessor) *CachedUserProcessor {
return &CachedUserProcessor{
userRepo: userRepo,
profileRepo: profileRepo,
+29 -15
View File
@@ -2,6 +2,8 @@ package processor
import (
"context"
"eslogad-be/internal/appcontext"
"time"
"eslogad-be/internal/entities"
"eslogad-be/internal/repository"
@@ -50,40 +52,52 @@ func (p *RecipientProcessorImpl) CreateDefaultRecipients(ctx context.Context, le
}
func (p *RecipientProcessorImpl) CreateRecipients(ctx context.Context, letterID uuid.UUID, departmentIDs []uuid.UUID) ([]entities.LetterIncomingRecipient, error) {
if len(departmentIDs) == 0 {
return []entities.LetterIncomingRecipient{}, nil
}
userCreatorDepartment := appcontext.FromGinContext(ctx).DepartmentID
departmentIDs = append(departmentIDs, userCreatorDepartment)
userMemberships, err := p.userDeptRepo.ListActiveByDepartmentIDs(ctx, departmentIDs)
if err != nil {
return nil, err
}
recipients := p.buildUniqueRecipients(letterID, userMemberships)
recipients := p.buildUniqueRecipients(letterID, userMemberships, userCreatorDepartment)
if len(recipients) > 0 {
if err := p.recipientRepo.CreateBulk(ctx, recipients); err != nil {
return nil, err
}
if err := p.recipientRepo.CreateBulk(ctx, recipients); err != nil {
return nil, err
}
return recipients, nil
}
func (p *RecipientProcessorImpl) buildUniqueRecipients(letterID uuid.UUID, userMemberships []repository.UserDepartmentRow) []entities.LetterIncomingRecipient {
func (p *RecipientProcessorImpl) buildUniqueRecipients(letterID uuid.UUID, userMemberships []repository.UserDepartmentRow, userCreatorDepartment uuid.UUID) []entities.LetterIncomingRecipient {
var recipients []entities.LetterIncomingRecipient
userMap := make(map[string]bool)
now := time.Now()
for _, membership := range userMemberships {
userIDStr := membership.UserID.String()
if !userMap[userIDStr] {
recipients = append(recipients, entities.LetterIncomingRecipient{
LetterID: letterID,
RecipientUserID: &membership.UserID,
RecipientDepartmentID: &membership.DepartmentID,
Status: entities.RecipientStatusNew,
})
userID := membership.UserID
departmentID := membership.DepartmentID
if userCreatorDepartment == membership.DepartmentID {
recipients = append(recipients, entities.LetterIncomingRecipient{
LetterID: letterID,
RecipientUserID: &userID,
RecipientDepartmentID: &departmentID,
Status: entities.RecipientStatusCompleted,
ReadAt: &now,
CompletedAt: &now,
})
} else {
recipients = append(recipients, entities.LetterIncomingRecipient{
LetterID: letterID,
RecipientUserID: &userID,
RecipientDepartmentID: &departmentID,
Status: entities.RecipientStatusNew,
})
}
userMap[userIDStr] = true
}
}
+11 -6
View File
@@ -17,6 +17,7 @@ type UserProcessorImpl struct {
userRepo UserRepository
profileRepo UserProfileRepository
novuProcessor NovuProcessor
userRoleProc UserRoleProcessor
}
type UserProfileRepository interface {
@@ -29,10 +30,12 @@ type UserProfileRepository interface {
func NewUserProcessor(
userRepo UserRepository,
profileRepo UserProfileRepository,
userRoleProc UserRoleProcessor,
) *UserProcessorImpl {
return &UserProcessorImpl{
userRepo: userRepo,
profileRepo: profileRepo,
userRepo: userRepo,
profileRepo: profileRepo,
userRoleProc: userRoleProc,
}
}
@@ -58,7 +61,6 @@ func (p *UserProcessorImpl) CreateUser(ctx context.Context, req *contract.Create
return nil, fmt.Errorf("failed to create user: %w", err)
}
// create default user profile
defaultFullName := userEntity.Name
profile := &entities.UserProfile{
UserID: userEntity.ID,
@@ -70,11 +72,14 @@ func (p *UserProcessorImpl) CreateUser(ctx context.Context, req *contract.Create
}
_ = p.profileRepo.Create(ctx, profile)
// Create Novu subscriber
if req.RoleID != nil {
if err := p.userRoleProc.AssignRoleToUser(ctx, userEntity.ID, *req.RoleID); err != nil {
return nil, fmt.Errorf("failed to assign role to user: %w", err)
}
}
if p.novuProcessor != nil {
if err := p.novuProcessor.CreateSubscriber(ctx, userEntity); err != nil {
// Log error but don't fail user creation
// You might want to add proper logging here
_ = err
}
}
+97
View File
@@ -0,0 +1,97 @@
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)
}
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
}