home_service.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. package service
  2. import (
  3. "context"
  4. "net/http"
  5. "strings"
  6. "time"
  7. "cmr-backend/internal/apperr"
  8. "cmr-backend/internal/store/postgres"
  9. )
  10. type HomeService struct {
  11. store *postgres.Store
  12. }
  13. type ListCardsInput struct {
  14. ChannelCode string
  15. ChannelType string
  16. PlatformAppID string
  17. TenantCode string
  18. Slot string
  19. Limit int
  20. }
  21. type CardResult struct {
  22. ID string `json:"id"`
  23. Type string `json:"type"`
  24. Title string `json:"title"`
  25. Subtitle *string `json:"subtitle,omitempty"`
  26. CoverURL *string `json:"coverUrl,omitempty"`
  27. DisplaySlot string `json:"displaySlot"`
  28. DisplayPriority int `json:"displayPriority"`
  29. Event *struct {
  30. ID string `json:"id"`
  31. DisplayName string `json:"displayName"`
  32. Summary *string `json:"summary,omitempty"`
  33. } `json:"event,omitempty"`
  34. HTMLURL *string `json:"htmlUrl,omitempty"`
  35. }
  36. type HomeResult struct {
  37. Tenant struct {
  38. ID string `json:"id"`
  39. Code string `json:"code"`
  40. Name string `json:"name"`
  41. } `json:"tenant"`
  42. Channel struct {
  43. ID string `json:"id"`
  44. Code string `json:"code"`
  45. Type string `json:"type"`
  46. PlatformAppID *string `json:"platformAppId,omitempty"`
  47. DisplayName string `json:"displayName"`
  48. Status string `json:"status"`
  49. IsDefault bool `json:"isDefault"`
  50. } `json:"channel"`
  51. Cards []CardResult `json:"cards"`
  52. }
  53. func NewHomeService(store *postgres.Store) *HomeService {
  54. return &HomeService{store: store}
  55. }
  56. func (s *HomeService) ListCards(ctx context.Context, input ListCardsInput) ([]CardResult, error) {
  57. entry, err := s.resolveEntry(ctx, input)
  58. if err != nil {
  59. return nil, err
  60. }
  61. cards, err := s.store.ListCardsForEntry(ctx, entry.TenantID, &entry.ID, normalizeSlot(input.Slot), time.Now().UTC(), input.Limit)
  62. if err != nil {
  63. return nil, err
  64. }
  65. return mapCards(cards), nil
  66. }
  67. func (s *HomeService) GetHome(ctx context.Context, input ListCardsInput) (*HomeResult, error) {
  68. entry, err := s.resolveEntry(ctx, input)
  69. if err != nil {
  70. return nil, err
  71. }
  72. cards, err := s.store.ListCardsForEntry(ctx, entry.TenantID, &entry.ID, normalizeSlot(input.Slot), time.Now().UTC(), input.Limit)
  73. if err != nil {
  74. return nil, err
  75. }
  76. result := &HomeResult{
  77. Cards: mapCards(cards),
  78. }
  79. result.Tenant.ID = entry.TenantID
  80. result.Tenant.Code = entry.TenantCode
  81. result.Tenant.Name = entry.TenantName
  82. result.Channel.ID = entry.ID
  83. result.Channel.Code = entry.ChannelCode
  84. result.Channel.Type = entry.ChannelType
  85. result.Channel.PlatformAppID = entry.PlatformAppID
  86. result.Channel.DisplayName = entry.DisplayName
  87. result.Channel.Status = entry.Status
  88. result.Channel.IsDefault = entry.IsDefault
  89. return result, nil
  90. }
  91. func (s *HomeService) resolveEntry(ctx context.Context, input ListCardsInput) (*postgres.EntryChannel, error) {
  92. entry, err := s.store.FindEntryChannel(ctx, postgres.FindEntryChannelParams{
  93. ChannelCode: strings.TrimSpace(input.ChannelCode),
  94. ChannelType: strings.TrimSpace(input.ChannelType),
  95. PlatformAppID: strings.TrimSpace(input.PlatformAppID),
  96. TenantCode: strings.TrimSpace(input.TenantCode),
  97. })
  98. if err != nil {
  99. return nil, err
  100. }
  101. if entry == nil {
  102. return nil, apperr.New(http.StatusNotFound, "entry_channel_not_found", "entry channel not found")
  103. }
  104. return entry, nil
  105. }
  106. func normalizeSlot(slot string) string {
  107. slot = strings.TrimSpace(slot)
  108. if slot == "" {
  109. return "home_primary"
  110. }
  111. return slot
  112. }
  113. func mapCards(cards []postgres.Card) []CardResult {
  114. results := make([]CardResult, 0, len(cards))
  115. for _, card := range cards {
  116. item := CardResult{
  117. ID: card.PublicID,
  118. Type: card.CardType,
  119. Title: card.Title,
  120. Subtitle: card.Subtitle,
  121. CoverURL: card.CoverURL,
  122. DisplaySlot: card.DisplaySlot,
  123. DisplayPriority: card.DisplayPriority,
  124. HTMLURL: card.HTMLURL,
  125. }
  126. if card.EventPublicID != nil || card.EventDisplayName != nil {
  127. item.Event = &struct {
  128. ID string `json:"id"`
  129. DisplayName string `json:"displayName"`
  130. Summary *string `json:"summary,omitempty"`
  131. }{
  132. Summary: card.EventSummary,
  133. }
  134. if card.EventPublicID != nil {
  135. item.Event.ID = *card.EventPublicID
  136. }
  137. if card.EventDisplayName != nil {
  138. item.Event.DisplayName = *card.EventDisplayName
  139. }
  140. }
  141. results = append(results, item)
  142. }
  143. return results
  144. }