initial commit
This commit is contained in:
315
internal/repository/postgres/profile/profile.go
Normal file
315
internal/repository/postgres/profile/profile.go
Normal file
@@ -0,0 +1,315 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user