package profile import ( "context" "github.com/google/uuid" "github.com/rs/zerolog" "github.com/samber/lo" "go.uber.org/fx" "base/internal/domain/profile" "base/internal/dto" ) type Service interface { Create(ctx context.Context, req dto.CreateProfileRequest) (*dto.ProfileResponse, error) Update(ctx context.Context, req dto.UpdateProfileRequest) (*dto.ProfileResponse, error) GetByID(ctx context.Context, id uuid.UUID) (*dto.ProfileResponse, error) GetByHandle(ctx context.Context, handle string) (*dto.ProfileResponse, error) List(ctx context.Context, req dto.ListProfilesRequest) (*dto.ListProfilesResponse, error) Delete(ctx context.Context, id uuid.UUID) error } type service struct { logger zerolog.Logger profileRepo profile.Repository } type Param struct { Logger zerolog.Logger ProfileRepo profile.Repository fx.In } func New(param Param) Service { return &service{ logger: param.Logger, profileRepo: param.ProfileRepo, } } func (s *service) Create(ctx context.Context, req dto.CreateProfileRequest) (*dto.ProfileResponse, error) { domainProfile := &profile.Profile{ ID: uuid.New(), Handle: req.Handle, PageSectionOrder: req.PageSectionOrder, Hero: profile.Hero{ Role: &profile.Role{ ID: lo.FromPtr(req.Hero.RoleID), Level: req.Hero.RoleLevel, }, FirstName: req.Hero.FirstName, LastName: req.Hero.LastName, Company: req.Hero.Company, ShortDescription: req.Hero.ShortDescription, ResumeLink: req.Hero.ResumeLink, CTAEnabled: req.Hero.CTAEnabled, Avatar: req.Hero.Avatar, }, About: profile.About{ ProfilePicture: req.About.ProfilePicture, About: req.About.About, }, Contact: profile.Contact{ Email: req.Contact.Email, Phone: req.Contact.Phone, }, PageSetting: profile.PageSetting{ VisibilityLevel: req.PageSetting.VisibilityLevel, }, } if req.Hero.RoleID != nil { domainProfile.Hero.Role = &profile.Role{ ID: *req.Hero.RoleID, } } for _, skill := range req.Skills { domainProfile.Skills = append(domainProfile.Skills, profile.Skill{ SkillName: skill.SkillName, Level: skill.Level, }) } for _, achievement := range req.About.Achievements { domainProfile.About.Achievements = append(domainProfile.About.Achievements, profile.Achievement{ Title: achievement.Title, Value: achievement.Value, Enabled: achievement.Enabled, }) } for _, socialLink := range req.Contact.SocialLinks { domainProfile.Contact.SocialLinks = append(domainProfile.Contact.SocialLinks, profile.SocialLink{ LinkType: socialLink.LinkType, Link: socialLink.Link, }) } if err := s.profileRepo.Create(ctx, domainProfile); err != nil { return nil, err } return s.toProfileResponse(domainProfile), nil } func (s *service) Update(ctx context.Context, req dto.UpdateProfileRequest) (*dto.ProfileResponse, error) { id, err := uuid.Parse(req.ID) if err != nil { return nil, profile.ErrProfileNotFound } // First, get the existing profile to ensure it exists existingProfile, err := s.profileRepo.FindByID(ctx, id) if err != nil { return nil, profile.ErrProfileNotFound } domainProfile := &profile.Profile{ ID: id, Handle: req.Handle, PageSectionOrder: req.PageSectionOrder, Hero: profile.Hero{ FirstName: req.Hero.FirstName, Role: &profile.Role{ ID: lo.FromPtr(req.Hero.RoleID), Level: req.Hero.RoleLevel, }, LastName: req.Hero.LastName, Company: req.Hero.Company, ShortDescription: req.Hero.ShortDescription, ResumeLink: req.Hero.ResumeLink, CTAEnabled: req.Hero.CTAEnabled, Avatar: req.Hero.Avatar, }, About: profile.About{ ProfilePicture: req.About.ProfilePicture, About: req.About.About, }, Contact: profile.Contact{ Email: req.Contact.Email, Phone: req.Contact.Phone, }, PageSetting: profile.PageSetting{ VisibilityLevel: req.PageSetting.VisibilityLevel, }, } if req.Hero.RoleID != nil { domainProfile.Hero.Role = &profile.Role{ ID: *req.Hero.RoleID, } } else if existingProfile != nil && existingProfile.Hero.Role != nil { domainProfile.Hero.Role = existingProfile.Hero.Role } if req.Hero.RoleLevel == "" && existingProfile != nil { domainProfile.Hero.Role.Level = existingProfile.Hero.Role.Level } for _, skill := range req.Skills { domainProfile.Skills = append(domainProfile.Skills, profile.Skill{ SkillName: skill.SkillName, Level: skill.Level, }) } for _, achievement := range req.About.Achievements { domainProfile.About.Achievements = append(domainProfile.About.Achievements, profile.Achievement{ Title: achievement.Title, Value: achievement.Value, Enabled: achievement.Enabled, }) } for _, socialLink := range req.Contact.SocialLinks { domainProfile.Contact.SocialLinks = append(domainProfile.Contact.SocialLinks, profile.SocialLink{ LinkType: socialLink.LinkType, Link: socialLink.Link, }) } if err := s.profileRepo.Update(ctx, domainProfile); err != nil { return nil, err } return s.toProfileResponse(domainProfile), nil } func (s *service) GetByID(ctx context.Context, id uuid.UUID) (*dto.ProfileResponse, error) { profileData, err := s.profileRepo.FindByID(ctx, id) if err != nil { return nil, profile.ErrProfileNotFound } return s.toProfileResponse(profileData), nil } func (s *service) GetByHandle(ctx context.Context, handle string) (*dto.ProfileResponse, error) { profileData, err := s.profileRepo.FindByHandle(ctx, handle) if err != nil { return nil, profile.ErrProfileNotFound } return s.toProfileResponse(profileData), nil } func (s *service) List(ctx context.Context, req dto.ListProfilesRequest) (*dto.ListProfilesResponse, error) { filter := profile.Filter{ FirstName: req.FirstName, LastName: req.LastName, Company: req.Company, SkillName: req.SkillName, Page: req.Page, PageSize: req.PageSize, SortedBy: req.SortedBy, Ascending: req.Ascending, } if req.Page == 0 { filter.Page = 1 } if req.PageSize == 0 { filter.PageSize = 10 } if req.RoleID != nil { filter.RoleID = *req.RoleID } profiles, total, err := s.profileRepo.FindAll(ctx, filter) if err != nil { return nil, err } response := &dto.ListProfilesResponse{ Profiles: make([]dto.ProfileResponse, len(profiles)), Total: total, Page: filter.Page, PageSize: filter.PageSize, } for i, p := range profiles { response.Profiles[i] = *s.toProfileResponse(p) } return response, nil } func (s *service) Delete(ctx context.Context, id uuid.UUID) error { // Get profile first to ensure it exists profileData, err := s.profileRepo.FindByID(ctx, id) if err != nil { return profile.ErrProfileNotFound } return s.profileRepo.Delete(ctx, profileData) } func (s *service) toProfileResponse(p *profile.Profile) *dto.ProfileResponse { resp := &dto.ProfileResponse{ ID: p.ID, Handle: p.Handle, PageSectionOrder: p.PageSectionOrder, Hero: dto.HeroDTO{ FirstName: p.Hero.FirstName, LastName: p.Hero.LastName, Company: p.Hero.Company, ShortDescription: p.Hero.ShortDescription, ResumeLink: p.Hero.ResumeLink, CTAEnabled: p.Hero.CTAEnabled, Avatar: p.Hero.Avatar, }, About: dto.AboutDTO{ ProfilePicture: p.About.ProfilePicture, About: p.About.About, }, Contact: dto.ContactDTO{ Email: p.Contact.Email, Phone: p.Contact.Phone, }, PageSetting: dto.PageSettingDTO{ VisibilityLevel: p.PageSetting.VisibilityLevel, }, } if p.Hero.Role != nil { resp.Hero.RoleID = &p.Hero.Role.ID } for _, skill := range p.Skills { resp.Skills = append(resp.Skills, dto.SkillDTO{ SkillName: skill.SkillName, Level: skill.Level, }) } for _, achievement := range p.About.Achievements { resp.About.Achievements = append(resp.About.Achievements, dto.AchievementDTO{ Title: achievement.Title, Value: achievement.Value, Enabled: achievement.Enabled, }) } for _, socialLink := range p.Contact.SocialLinks { resp.Contact.SocialLinks = append(resp.Contact.SocialLinks, dto.SocialLinkDTO{ LinkType: socialLink.LinkType, Link: socialLink.Link, }) } return resp }