initial commit
This commit is contained in:
238
internal/application/landing/service.go
Normal file
238
internal/application/landing/service.go
Normal file
@@ -0,0 +1,238 @@
|
||||
package landing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
"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"
|
||||
"base/pkg/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultAssetsPerCategory = 6
|
||||
defaultSpecialistsLimit = 6
|
||||
defaultRolesLimit = 20
|
||||
landingCacheKey = "landing:page"
|
||||
landingCacheTTL = 5 * time.Minute
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
GetLanding(ctx context.Context) (*dto.Landing, error)
|
||||
}
|
||||
|
||||
type service struct {
|
||||
logger zerolog.Logger
|
||||
cache cache.Cache[dto.Landing]
|
||||
categoryRepo domainAsset.CategoryRepository
|
||||
assetRepo domainAsset.AssetRepository
|
||||
profileRepo domainProfile.Repository
|
||||
roleRepo domainProfile.RoleRepository
|
||||
}
|
||||
|
||||
type Param struct {
|
||||
Logger zerolog.Logger
|
||||
Cache cache.Cache[dto.Landing]
|
||||
CategoryRepo domainAsset.CategoryRepository
|
||||
AssetRepo domainAsset.AssetRepository
|
||||
ProfileRepo domainProfile.Repository
|
||||
RoleRepo domainProfile.RoleRepository
|
||||
|
||||
fx.In
|
||||
}
|
||||
|
||||
func New(param Param) Service {
|
||||
return &service{
|
||||
logger: param.Logger,
|
||||
cache: param.Cache,
|
||||
categoryRepo: param.CategoryRepo,
|
||||
assetRepo: param.AssetRepo,
|
||||
profileRepo: param.ProfileRepo,
|
||||
roleRepo: param.RoleRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *service) GetLanding(ctx context.Context) (*dto.Landing, error) {
|
||||
result, err := s.cache.WithCache(ctx, landingCacheKey, s.fetchLanding, landingCacheTTL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (s *service) fetchLanding(ctx context.Context) (dto.Landing, error) {
|
||||
data := &dto.LandingPageData{
|
||||
Categories: []dto.CategoryDTO{},
|
||||
SpecialistRoles: []dto.ProfileRole{},
|
||||
Assets: []dto.LandingAssetData{},
|
||||
Specialists: []dto.Specialist{},
|
||||
Blogs: []dto.Blog{},
|
||||
}
|
||||
|
||||
g, gCtx := errgroup.WithContext(ctx)
|
||||
|
||||
g.Go(func() error {
|
||||
categories, err := s.categoryRepo.FindAll(gCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data.Categories = make([]dto.CategoryDTO, len(categories))
|
||||
for i, c := range categories {
|
||||
data.Categories[i] = dto.CategoryDTO{
|
||||
ID: c.ID,
|
||||
Name: c.Name,
|
||||
Icon: c.Icon,
|
||||
Color: c.Color,
|
||||
CardType: c.CardType,
|
||||
Featured: c.Featured,
|
||||
Description: c.Description,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
domainRoles, err := s.roleRepo.List(gCtx, defaultRolesLimit, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data.SpecialistRoles = make([]dto.ProfileRole, len(domainRoles))
|
||||
for i, r := range domainRoles {
|
||||
data.SpecialistRoles[i] = dto.ProfileRole{
|
||||
Id: r.ID.String(),
|
||||
Title: r.Title,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
profiles, _, err := s.profileRepo.FindAll(
|
||||
gCtx,
|
||||
domainProfile.Filter{
|
||||
Page: 1,
|
||||
PageSize: defaultSpecialistsLimit,
|
||||
SortedBy: "created_at",
|
||||
Ascending: false,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data.Specialists = make([]dto.Specialist, len(profiles))
|
||||
for i, p := range profiles {
|
||||
data.Specialists[i] = dto.Specialist{
|
||||
Id: p.ID.String(),
|
||||
Handle: p.Handle,
|
||||
Avatar: p.About.ProfilePicture,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
categories, err := s.categoryRepo.FindAll(gCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
assetsByCat := make([]dto.LandingAssetData, len(categories))
|
||||
mu := &sync.Mutex{}
|
||||
eg, egCtx := errgroup.WithContext(gCtx)
|
||||
|
||||
for index, category := range categories {
|
||||
i, cat := index, category
|
||||
|
||||
eg.Go(func() error {
|
||||
assets, findLatestAssetErr := s.assetRepo.FindLatestByCategory(egCtx, cat.ID, defaultAssetsPerCategory)
|
||||
if findLatestAssetErr != nil {
|
||||
return findLatestAssetErr
|
||||
}
|
||||
|
||||
assetResp := make([]dto.AssetResponse, len(assets))
|
||||
for j, a := range assets {
|
||||
assetResp[j] = *s.toAssetResponse(a)
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
|
||||
assetsByCat[i] = dto.LandingAssetData{
|
||||
AssetCategory: dto.AssetCategory{
|
||||
Id: cat.ID.String(),
|
||||
Title: cat.Name,
|
||||
Icon: cat.Icon,
|
||||
},
|
||||
Assets: assetResp,
|
||||
}
|
||||
|
||||
mu.Unlock()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err = eg.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
data.Assets = assetsByCat
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return dto.Landing{}, err
|
||||
}
|
||||
|
||||
return dto.Landing{
|
||||
Message: "Landing page fetched successfully",
|
||||
Data: *data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *service) toAssetResponse(a *domainAsset.Asset) *dto.AssetResponse {
|
||||
coverImage := ""
|
||||
for _, art := range a.AssetArtifacts {
|
||||
if strings.Contains(strings.ToLower(art.Type), "image") {
|
||||
coverImage = art.DownloadURL
|
||||
break
|
||||
}
|
||||
}
|
||||
resp := &dto.AssetResponse{
|
||||
ID: a.ID,
|
||||
ProfileID: a.ProfileID,
|
||||
AssetCategoryID: a.AssetCategoryID,
|
||||
Title: a.Title,
|
||||
Description: a.Description,
|
||||
Link: a.Link,
|
||||
CoverImage: coverImage,
|
||||
Status: int(a.Status),
|
||||
CreatedAt: formatTime(a.CreatedAt),
|
||||
UpdatedAt: formatTime(a.UpdatedAt),
|
||||
}
|
||||
if a.AssetCategory.ID != uuid.Nil {
|
||||
resp.Category = 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,
|
||||
}
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func formatTime(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return t.Format(time.RFC3339)
|
||||
}
|
||||
Reference in New Issue
Block a user