Files
cmr-mini/backend/internal/service/home_service.go

160 lines
4.3 KiB
Go

package service
import (
"context"
"net/http"
"strings"
"time"
"cmr-backend/internal/apperr"
"cmr-backend/internal/store/postgres"
)
type HomeService struct {
store *postgres.Store
}
type ListCardsInput struct {
ChannelCode string
ChannelType string
PlatformAppID string
TenantCode string
Slot string
Limit int
}
type CardResult struct {
ID string `json:"id"`
Type string `json:"type"`
Title string `json:"title"`
Subtitle *string `json:"subtitle,omitempty"`
CoverURL *string `json:"coverUrl,omitempty"`
DisplaySlot string `json:"displaySlot"`
DisplayPriority int `json:"displayPriority"`
Event *struct {
ID string `json:"id"`
DisplayName string `json:"displayName"`
Summary *string `json:"summary,omitempty"`
} `json:"event,omitempty"`
HTMLURL *string `json:"htmlUrl,omitempty"`
}
type HomeResult struct {
Tenant struct {
ID string `json:"id"`
Code string `json:"code"`
Name string `json:"name"`
} `json:"tenant"`
Channel struct {
ID string `json:"id"`
Code string `json:"code"`
Type string `json:"type"`
PlatformAppID *string `json:"platformAppId,omitempty"`
DisplayName string `json:"displayName"`
Status string `json:"status"`
IsDefault bool `json:"isDefault"`
} `json:"channel"`
Cards []CardResult `json:"cards"`
}
func NewHomeService(store *postgres.Store) *HomeService {
return &HomeService{store: store}
}
func (s *HomeService) ListCards(ctx context.Context, input ListCardsInput) ([]CardResult, error) {
entry, err := s.resolveEntry(ctx, input)
if err != nil {
return nil, err
}
cards, err := s.store.ListCardsForEntry(ctx, entry.TenantID, &entry.ID, normalizeSlot(input.Slot), time.Now().UTC(), input.Limit)
if err != nil {
return nil, err
}
return mapCards(cards), nil
}
func (s *HomeService) GetHome(ctx context.Context, input ListCardsInput) (*HomeResult, error) {
entry, err := s.resolveEntry(ctx, input)
if err != nil {
return nil, err
}
cards, err := s.store.ListCardsForEntry(ctx, entry.TenantID, &entry.ID, normalizeSlot(input.Slot), time.Now().UTC(), input.Limit)
if err != nil {
return nil, err
}
result := &HomeResult{
Cards: mapCards(cards),
}
result.Tenant.ID = entry.TenantID
result.Tenant.Code = entry.TenantCode
result.Tenant.Name = entry.TenantName
result.Channel.ID = entry.ID
result.Channel.Code = entry.ChannelCode
result.Channel.Type = entry.ChannelType
result.Channel.PlatformAppID = entry.PlatformAppID
result.Channel.DisplayName = entry.DisplayName
result.Channel.Status = entry.Status
result.Channel.IsDefault = entry.IsDefault
return result, nil
}
func (s *HomeService) resolveEntry(ctx context.Context, input ListCardsInput) (*postgres.EntryChannel, error) {
entry, err := s.store.FindEntryChannel(ctx, postgres.FindEntryChannelParams{
ChannelCode: strings.TrimSpace(input.ChannelCode),
ChannelType: strings.TrimSpace(input.ChannelType),
PlatformAppID: strings.TrimSpace(input.PlatformAppID),
TenantCode: strings.TrimSpace(input.TenantCode),
})
if err != nil {
return nil, err
}
if entry == nil {
return nil, apperr.New(http.StatusNotFound, "entry_channel_not_found", "entry channel not found")
}
return entry, nil
}
func normalizeSlot(slot string) string {
slot = strings.TrimSpace(slot)
if slot == "" {
return "home_primary"
}
return slot
}
func mapCards(cards []postgres.Card) []CardResult {
results := make([]CardResult, 0, len(cards))
for _, card := range cards {
item := CardResult{
ID: card.PublicID,
Type: card.CardType,
Title: card.Title,
Subtitle: card.Subtitle,
CoverURL: card.CoverURL,
DisplaySlot: card.DisplaySlot,
DisplayPriority: card.DisplayPriority,
HTMLURL: card.HTMLURL,
}
if card.EventPublicID != nil || card.EventDisplayName != nil {
item.Event = &struct {
ID string `json:"id"`
DisplayName string `json:"displayName"`
Summary *string `json:"summary,omitempty"`
}{
Summary: card.EventSummary,
}
if card.EventPublicID != nil {
item.Event.ID = *card.EventPublicID
}
if card.EventDisplayName != nil {
item.Event.DisplayName = *card.EventDisplayName
}
}
results = append(results, item)
}
return results
}