273 lines
6.7 KiB
Go
273 lines
6.7 KiB
Go
package discovery
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/rs/zerolog"
|
|
"go.uber.org/fx"
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
domainAsset "base/internal/domain/asset"
|
|
domainProfile "base/internal/domain/profile"
|
|
"base/internal/dto"
|
|
)
|
|
|
|
type Service interface {
|
|
GetDiscoveryOverview(ctx context.Context) (*dto.OverviewFetchedResponse, error)
|
|
}
|
|
|
|
type service struct {
|
|
logger zerolog.Logger
|
|
profileRepo domainProfile.Repository
|
|
assetRepo domainAsset.AssetRepository
|
|
categoryRepo domainAsset.CategoryRepository
|
|
}
|
|
|
|
// Param holds dependencies for the discovery overview service.
|
|
type Param struct {
|
|
Logger zerolog.Logger
|
|
ProfileRepo domainProfile.Repository
|
|
AssetRepo domainAsset.AssetRepository
|
|
CategoryRepo domainAsset.CategoryRepository
|
|
|
|
fx.In
|
|
}
|
|
|
|
func New(param Param) Service {
|
|
return &service{
|
|
logger: param.Logger,
|
|
profileRepo: param.ProfileRepo,
|
|
assetRepo: param.AssetRepo,
|
|
categoryRepo: param.CategoryRepo,
|
|
}
|
|
}
|
|
|
|
func (s *service) GetDiscoveryOverview(ctx context.Context) (*dto.OverviewFetchedResponse, error) {
|
|
resp := &struct {
|
|
domainAssets []*domainAsset.Asset
|
|
recentlyJoined []*domainProfile.Profile
|
|
totalProfiles int
|
|
totalAssets int
|
|
}{}
|
|
|
|
g, gCtx := errgroup.WithContext(ctx)
|
|
|
|
g.Go(func() error {
|
|
assets, err := s.assetRepo.FindLatest(gCtx, 6, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp.domainAssets = assets
|
|
return nil
|
|
})
|
|
|
|
g.Go(func() error {
|
|
profiles, total, err := s.profileRepo.FindAll(gCtx, domainProfile.Filter{
|
|
Page: 1,
|
|
PageSize: 6,
|
|
SortedBy: "created_at",
|
|
Ascending: false,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp.recentlyJoined = profiles
|
|
resp.totalProfiles = total
|
|
return nil
|
|
})
|
|
|
|
g.Go(func() error {
|
|
count, err := s.assetRepo.Count(gCtx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp.totalAssets = count
|
|
return nil
|
|
})
|
|
|
|
if err := g.Wait(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
assets := s.toOverviewAssets(ctx, resp.domainAssets)
|
|
flatProfiles := ToFlatProfiles(resp.recentlyJoined)
|
|
|
|
return &dto.OverviewFetchedResponse{
|
|
Message: "Overview fetched successfully",
|
|
Data: dto.OverviewFetchedDataDTO{
|
|
Assets: assets,
|
|
RecentlyJoined: flatProfiles,
|
|
Analytics: dto.AnalyticsDTO{
|
|
TotalAssets: resp.totalAssets,
|
|
TotalProfiles: resp.totalProfiles,
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (s *service) toOverviewAssets(ctx context.Context, assets []*domainAsset.Asset) []dto.OverviewAssetDTO {
|
|
out := make([]dto.OverviewAssetDTO, len(assets))
|
|
for i, a := range assets {
|
|
out[i] = s.toOverviewAsset(ctx, a)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (s *service) toOverviewAsset(ctx context.Context, a *domainAsset.Asset) dto.OverviewAssetDTO {
|
|
price := 0
|
|
coverImage := ""
|
|
for _, art := range a.AssetArtifacts {
|
|
if strings.Contains(strings.ToLower(art.Type), "image") {
|
|
coverImage = art.DownloadURL
|
|
break
|
|
}
|
|
}
|
|
if len(a.AssetArtifacts) > 0 {
|
|
price = a.AssetArtifacts[0].Price
|
|
}
|
|
|
|
cat := (*dto.CategoryDTO)(nil)
|
|
if a.AssetCategory.ID != uuid.Nil {
|
|
cat = &dto.CategoryDTO{
|
|
ID: a.AssetCategory.ID,
|
|
Name: a.AssetCategory.Name,
|
|
Icon: a.AssetCategory.Icon,
|
|
Color: a.AssetCategory.Color,
|
|
CardType: a.AssetCategory.CardType,
|
|
Featured: a.AssetCategory.Featured,
|
|
Description: a.AssetCategory.Description,
|
|
}
|
|
}
|
|
|
|
ownerID := a.ProfileID.String()
|
|
if p, err := s.profileRepo.FindByID(ctx, a.ProfileID); err == nil && p.UserID != nil {
|
|
ownerID = p.UserID.String()
|
|
}
|
|
|
|
return dto.OverviewAssetDTO{
|
|
ID: a.ID.String(),
|
|
Title: a.Title,
|
|
Description: a.Description,
|
|
Content: a.Description,
|
|
AssetCategoryID: a.AssetCategoryID.String(),
|
|
AssetCategory: cat,
|
|
CoverImage: coverImage,
|
|
Link: a.Link,
|
|
OwnerID: ownerID,
|
|
ProfileID: a.ProfileID.String(),
|
|
Profile: nil,
|
|
Price: price,
|
|
Currency: "USD",
|
|
Status: assetStatusToString(a.Status),
|
|
Rating: 0,
|
|
CreatedAt: a.CreatedAt,
|
|
UpdatedAt: a.UpdatedAt,
|
|
}
|
|
}
|
|
|
|
func assetStatusToString(st domainAsset.Status) string {
|
|
switch st {
|
|
case domainAsset.StatusPublished:
|
|
return "published"
|
|
case domainAsset.StatusDisabled:
|
|
return "disabled"
|
|
case domainAsset.StatusPending:
|
|
return "pending"
|
|
case domainAsset.StatusDeleted:
|
|
return "deleted"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func formatTime(t time.Time) string {
|
|
if t.IsZero() {
|
|
return ""
|
|
}
|
|
return t.Format(time.RFC3339)
|
|
}
|
|
|
|
// ToFlatProfiles converts domain profiles to flat DTOs.
|
|
func ToFlatProfiles(profiles []*domainProfile.Profile) []dto.FlatProfileDTO {
|
|
out := make([]dto.FlatProfileDTO, len(profiles))
|
|
for i, p := range profiles {
|
|
out[i] = ToFlatProfile(p)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// ToFlatProfile converts a single profile to flat DTO.
|
|
func ToFlatProfile(p *domainProfile.Profile) dto.FlatProfileDTO {
|
|
roleID := ""
|
|
roleName := ""
|
|
if p.Hero.Role != nil {
|
|
roleID = p.Hero.Role.ID.String()
|
|
if p.Hero.Role.Title != "" {
|
|
roleName = p.Hero.Role.Title
|
|
}
|
|
}
|
|
|
|
achievements := make(map[string]dto.AchievementItemDTO)
|
|
for _, a := range p.About.Achievements {
|
|
key := strings.ToLower(strings.ReplaceAll(a.Title, " ", ""))
|
|
if key == "" {
|
|
key = a.Title
|
|
}
|
|
achievements[key] = dto.AchievementItemDTO{Value: a.Value, Enabled: a.Enabled}
|
|
}
|
|
if len(achievements) == 0 {
|
|
achievements = map[string]dto.AchievementItemDTO{
|
|
"happyClient": {Value: "", Enabled: true},
|
|
"yearExperience": {Value: "", Enabled: true},
|
|
"projectCompeleted": {Value: "", Enabled: true},
|
|
}
|
|
}
|
|
|
|
var socialLinks []dto.SocialLinkDTO
|
|
for _, sl := range p.Contact.SocialLinks {
|
|
socialLinks = append(socialLinks, dto.SocialLinkDTO{LinkType: sl.LinkType, Link: sl.Link})
|
|
}
|
|
|
|
displayName := strings.TrimSpace(p.Hero.FirstName + " " + p.Hero.LastName)
|
|
if displayName == "" {
|
|
displayName = p.Handle
|
|
}
|
|
|
|
status := "published"
|
|
if p.PageSetting.VisibilityLevel != "public" {
|
|
status = "draft"
|
|
}
|
|
|
|
return dto.FlatProfileDTO{
|
|
ID: p.ID.String(),
|
|
ProfileHandle: p.Handle,
|
|
Status: status,
|
|
BackgroundImage: "",
|
|
ProfilePicture: p.About.ProfilePicture,
|
|
FirstName: p.Hero.FirstName,
|
|
LastName: p.Hero.LastName,
|
|
DisplayName: displayName,
|
|
RoleID: roleID,
|
|
Role: dto.RoleDTO{ID: roleID, Name: roleName},
|
|
CurrentCompany: p.Hero.Company,
|
|
ShortDescription: p.Hero.ShortDescription,
|
|
CTAEnabled: p.Hero.CTAEnabled,
|
|
CTAAction: "",
|
|
ResumeLink: p.Hero.ResumeLink,
|
|
About: p.About.About,
|
|
ContactEmail: p.Contact.Email,
|
|
Achievements: achievements,
|
|
ContactPhone: p.Contact.Phone,
|
|
Country: "",
|
|
CustomRoles: "",
|
|
RoleLevel: p.Hero.Role.Level,
|
|
SocialLinks: socialLinks,
|
|
CreatedAt: p.CreatedAt,
|
|
UpdatedAt: p.UpdatedAt,
|
|
HandleUpdatedAt: time.Time{},
|
|
}
|
|
}
|