316 lines
8.4 KiB
Go
316 lines
8.4 KiB
Go
package profile
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/google/uuid"
|
|
"go.uber.org/fx"
|
|
"gorm.io/gorm"
|
|
|
|
domainProfile "base/internal/domain/profile"
|
|
)
|
|
|
|
type profileRepository struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewProfileRepository(lc fx.Lifecycle, db *gorm.DB) domainProfile.Repository {
|
|
lc.Append(
|
|
fx.Hook{
|
|
OnStart: func(ctx context.Context) error {
|
|
return nil
|
|
},
|
|
OnStop: func(ctx context.Context) error {
|
|
return nil
|
|
},
|
|
})
|
|
return &profileRepository{db: db}
|
|
}
|
|
|
|
func (r *profileRepository) Create(ctx context.Context, profile *domainProfile.Profile) error {
|
|
model, err := toProfileModel(profile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Start a transaction
|
|
tx := r.db.WithContext(ctx).Begin()
|
|
if tx.Error != nil {
|
|
return tx.Error
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
// Create profile
|
|
if err := tx.Create(model).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create skills if any
|
|
if len(profile.Skills) > 0 {
|
|
skillModels := toSkillModels(model.ID, profile.Skills)
|
|
if err := tx.Create(&skillModels).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Create social links if any
|
|
if len(profile.Contact.SocialLinks) > 0 {
|
|
socialLinkModels := toSocialLinkModels(model.ID, profile.Contact.SocialLinks)
|
|
if err := tx.Create(&socialLinkModels).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Create achievements if any
|
|
if len(profile.About.Achievements) > 0 {
|
|
achievementModels := toAchievementModels(model.ID, profile.About.Achievements)
|
|
if err := tx.Create(&achievementModels).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := tx.Commit().Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
return copyProfileFromModel(profile, model)
|
|
}
|
|
|
|
func (r *profileRepository) loadRelatedData(ctx context.Context, profileID uuid.UUID) ([]domainProfile.Skill, []domainProfile.SocialLink, []domainProfile.Achievement, error) {
|
|
// Load skills
|
|
var skillModels []SkillModel
|
|
if err := r.db.WithContext(ctx).Where("profile_id = ?", profileID).Find(&skillModels).Error; err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
skills := toSkillDomains(skillModels)
|
|
|
|
// Load social links
|
|
var socialLinkModels []SocialLinkModel
|
|
if err := r.db.WithContext(ctx).Where("profile_id = ?", profileID).Find(&socialLinkModels).Error; err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
socialLinks := toSocialLinkDomains(socialLinkModels)
|
|
|
|
// Load achievements
|
|
var achievementModels []AchievementModel
|
|
if err := r.db.WithContext(ctx).Where("profile_id = ?", profileID).Find(&achievementModels).Error; err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
achievements := toAchievementDomains(achievementModels)
|
|
|
|
return skills, socialLinks, achievements, nil
|
|
}
|
|
|
|
func (r *profileRepository) FindByID(ctx context.Context, id uuid.UUID) (*domainProfile.Profile, error) {
|
|
var model Model
|
|
if err := r.db.WithContext(ctx).Preload("Role").Where("id = ?", id).First(&model).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, errors.New("profile not found")
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
skills, socialLinks, achievements, err := r.loadRelatedData(ctx, model.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return toProfileDomain(&model, skills, socialLinks, achievements)
|
|
}
|
|
|
|
func (r *profileRepository) FindByHandle(ctx context.Context, handle string) (*domainProfile.Profile, error) {
|
|
var model Model
|
|
if err := r.db.WithContext(ctx).Preload("Role").Where("handle = ?", handle).First(&model).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, errors.New("profile not found")
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
skills, socialLinks, achievements, err := r.loadRelatedData(ctx, model.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return toProfileDomain(&model, skills, socialLinks, achievements)
|
|
}
|
|
|
|
func (r *profileRepository) FindByUserID(ctx context.Context, userID uuid.UUID) (*domainProfile.Profile, error) {
|
|
var model Model
|
|
if err := r.db.WithContext(ctx).Preload("Role").Where("user_id = ? AND user_id IS NOT NULL", userID).First(&model).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, errors.New("profile not found")
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
skills, socialLinks, achievements, err := r.loadRelatedData(ctx, model.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return toProfileDomain(&model, skills, socialLinks, achievements)
|
|
}
|
|
|
|
func (r *profileRepository) Update(ctx context.Context, profile *domainProfile.Profile) error {
|
|
model, err := toProfileModel(profile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Start a transaction
|
|
tx := r.db.WithContext(ctx).Begin()
|
|
if tx.Error != nil {
|
|
return tx.Error
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
// Update profile
|
|
if err := tx.Model(&Model{}).Where("id = ?", profile.ID).Updates(model).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Delete existing related data
|
|
if err := tx.Where("profile_id = ?", profile.ID).Delete(&SkillModel{}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Where("profile_id = ?", profile.ID).Delete(&SocialLinkModel{}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Where("profile_id = ?", profile.ID).Delete(&AchievementModel{}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create new skills
|
|
if len(profile.Skills) > 0 {
|
|
skillModels := toSkillModels(profile.ID, profile.Skills)
|
|
if err := tx.Create(&skillModels).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Create new social links
|
|
if len(profile.Contact.SocialLinks) > 0 {
|
|
socialLinkModels := toSocialLinkModels(profile.ID, profile.Contact.SocialLinks)
|
|
if err := tx.Create(&socialLinkModels).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Create new achievements
|
|
if len(profile.About.Achievements) > 0 {
|
|
achievementModels := toAchievementModels(profile.ID, profile.About.Achievements)
|
|
if err := tx.Create(&achievementModels).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return tx.Commit().Error
|
|
}
|
|
|
|
func (r *profileRepository) Delete(ctx context.Context, profile *domainProfile.Profile) error {
|
|
// Start a transaction
|
|
tx := r.db.WithContext(ctx).Begin()
|
|
if tx.Error != nil {
|
|
return tx.Error
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
// Delete related data first
|
|
if err := tx.Where("profile_id = ?", profile.ID).Delete(&SkillModel{}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Where("profile_id = ?", profile.ID).Delete(&SocialLinkModel{}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Where("profile_id = ?", profile.ID).Delete(&AchievementModel{}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Delete profile
|
|
if err := tx.Delete(&Model{}, "id = ?", profile.ID).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
return tx.Commit().Error
|
|
}
|
|
|
|
// buildBaseQuery applies common filters to a query
|
|
func (r *profileRepository) buildBaseQuery(ctx context.Context, filter domainProfile.Filter) *gorm.DB {
|
|
query := r.db.WithContext(ctx).Model(&Model{})
|
|
|
|
if filter.RoleID != uuid.Nil {
|
|
query = query.Where("role_id = ?", filter.RoleID)
|
|
}
|
|
if filter.FirstName != "" {
|
|
query = query.Where("LOWER(first_name) LIKE ?", "%"+filter.FirstName+"%")
|
|
}
|
|
if filter.LastName != "" {
|
|
query = query.Where("LOWER(last_name) LIKE ?", "%"+filter.LastName+"%")
|
|
}
|
|
if filter.Company != "" {
|
|
query = query.Where("LOWER(company) LIKE ?", "%"+filter.Company+"%")
|
|
}
|
|
if filter.SkillName != "" {
|
|
subQuery := r.db.WithContext(ctx).Model(&SkillModel{}).
|
|
Select("DISTINCT profile_id").
|
|
Where("LOWER(skill_name) LIKE ? AND deleted_at IS NULL", "%"+filter.SkillName+"%")
|
|
query = query.Where("profiles.id IN (?)", subQuery)
|
|
}
|
|
|
|
return query
|
|
}
|
|
|
|
func (r *profileRepository) FindAll(ctx context.Context, filter domainProfile.Filter) ([]*domainProfile.Profile, int, error) {
|
|
baseQuery := r.buildBaseQuery(ctx, filter)
|
|
|
|
var total int64
|
|
if err := baseQuery.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
query := baseQuery
|
|
offset := int((filter.Page - 1) * filter.PageSize)
|
|
limit := int(filter.PageSize)
|
|
|
|
if limit > 0 {
|
|
query = query.Limit(limit).Offset(offset)
|
|
}
|
|
|
|
if filter.SortedBy != "" {
|
|
order := "ASC"
|
|
if !filter.Ascending {
|
|
order = "DESC"
|
|
}
|
|
query = query.Order("profiles." + filter.SortedBy + " " + order)
|
|
} else {
|
|
query = query.Order("profiles.created_at DESC")
|
|
}
|
|
|
|
var models []Model
|
|
if err := query.Preload("Role").Find(&models).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
if len(models) == 0 {
|
|
return nil, int(total), nil
|
|
}
|
|
|
|
profiles := make([]*domainProfile.Profile, len(models))
|
|
for i, model := range models {
|
|
skills, socialLinks, achievements, err := r.loadRelatedData(ctx, model.ID)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
profile, err := toProfileDomain(&model, skills, socialLinks, achievements)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
profiles[i] = profile
|
|
}
|
|
|
|
return profiles, int(total), nil
|
|
}
|