event_play_service.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. package service
  2. import (
  3. "context"
  4. "net/http"
  5. "strings"
  6. "cmr-backend/internal/apperr"
  7. "cmr-backend/internal/store/postgres"
  8. )
  9. type EventPlayService struct {
  10. store *postgres.Store
  11. }
  12. type EventPlayInput struct {
  13. EventPublicID string
  14. UserID string
  15. }
  16. type EventPlayResult struct {
  17. Event struct {
  18. ID string `json:"id"`
  19. Slug string `json:"slug"`
  20. DisplayName string `json:"displayName"`
  21. Summary *string `json:"summary,omitempty"`
  22. Status string `json:"status"`
  23. } `json:"event"`
  24. Release *struct {
  25. ID string `json:"id"`
  26. ConfigLabel string `json:"configLabel"`
  27. ManifestURL string `json:"manifestUrl"`
  28. ManifestChecksumSha256 *string `json:"manifestChecksumSha256,omitempty"`
  29. RouteCode *string `json:"routeCode,omitempty"`
  30. } `json:"release,omitempty"`
  31. ResolvedRelease *ResolvedReleaseView `json:"resolvedRelease,omitempty"`
  32. Runtime *RuntimeSummaryView `json:"runtime,omitempty"`
  33. Preview *MapPreviewView `json:"preview,omitempty"`
  34. CurrentPresentation *PresentationSummaryView `json:"currentPresentation,omitempty"`
  35. CurrentContentBundle *ContentBundleSummaryView `json:"currentContentBundle,omitempty"`
  36. Play struct {
  37. AssignmentMode *string `json:"assignmentMode,omitempty"`
  38. CourseVariants []CourseVariantView `json:"courseVariants,omitempty"`
  39. CanLaunch bool `json:"canLaunch"`
  40. PrimaryAction string `json:"primaryAction"`
  41. Reason string `json:"reason"`
  42. LaunchSource string `json:"launchSource,omitempty"`
  43. OngoingSession *EntrySessionSummary `json:"ongoingSession,omitempty"`
  44. RecentSession *EntrySessionSummary `json:"recentSession,omitempty"`
  45. } `json:"play"`
  46. }
  47. func NewEventPlayService(store *postgres.Store) *EventPlayService {
  48. return &EventPlayService{store: store}
  49. }
  50. func (s *EventPlayService) GetEventPlay(ctx context.Context, input EventPlayInput) (*EventPlayResult, error) {
  51. input.EventPublicID = strings.TrimSpace(input.EventPublicID)
  52. input.UserID = strings.TrimSpace(input.UserID)
  53. if input.EventPublicID == "" {
  54. return nil, apperr.New(http.StatusBadRequest, "invalid_params", "event id is required")
  55. }
  56. if input.UserID == "" {
  57. return nil, apperr.New(http.StatusUnauthorized, "unauthorized", "user is required")
  58. }
  59. event, err := s.store.GetEventByPublicID(ctx, input.EventPublicID)
  60. if err != nil {
  61. return nil, err
  62. }
  63. if event == nil {
  64. return nil, apperr.New(http.StatusNotFound, "event_not_found", "event not found")
  65. }
  66. sessions, err := s.store.ListSessionsByUserAndEvent(ctx, input.UserID, event.ID, 10)
  67. if err != nil {
  68. return nil, err
  69. }
  70. result := &EventPlayResult{}
  71. result.Event.ID = event.PublicID
  72. result.Event.Slug = event.Slug
  73. result.Event.DisplayName = event.DisplayName
  74. result.Event.Summary = event.Summary
  75. result.Event.Status = event.Status
  76. variantPlan := resolveVariantPlan(event.ReleasePayloadJSON)
  77. result.Play.AssignmentMode = variantPlan.AssignmentMode
  78. if len(variantPlan.CourseVariants) > 0 {
  79. result.Play.CourseVariants = variantPlan.CourseVariants
  80. }
  81. if event.CurrentReleasePubID != nil && event.ConfigLabel != nil && event.ManifestURL != nil {
  82. result.Release = &struct {
  83. ID string `json:"id"`
  84. ConfigLabel string `json:"configLabel"`
  85. ManifestURL string `json:"manifestUrl"`
  86. ManifestChecksumSha256 *string `json:"manifestChecksumSha256,omitempty"`
  87. RouteCode *string `json:"routeCode,omitempty"`
  88. }{
  89. ID: *event.CurrentReleasePubID,
  90. ConfigLabel: *event.ConfigLabel,
  91. ManifestURL: *event.ManifestURL,
  92. ManifestChecksumSha256: event.ManifestChecksum,
  93. RouteCode: event.RouteCode,
  94. }
  95. }
  96. result.ResolvedRelease = buildResolvedReleaseFromEvent(event, LaunchSourceEventCurrentRelease)
  97. result.Runtime = buildRuntimeSummaryFromEvent(event)
  98. if preview, err := buildPreviewFromPayload(event.ReleasePayloadJSON); err != nil {
  99. return nil, err
  100. } else {
  101. result.Preview = preview
  102. }
  103. result.CurrentPresentation = buildPresentationSummaryFromEvent(event)
  104. if enrichedPresentation, err := loadPresentationSummaryByPublicID(ctx, s.store, event.PresentationID); err != nil {
  105. return nil, err
  106. } else if enrichedPresentation != nil {
  107. result.CurrentPresentation = enrichedPresentation
  108. }
  109. result.CurrentContentBundle = buildContentBundleSummaryFromEvent(event)
  110. if enrichedBundle, err := loadContentBundleSummaryByPublicID(ctx, s.store, event.ContentBundleID); err != nil {
  111. return nil, err
  112. } else if enrichedBundle != nil {
  113. result.CurrentContentBundle = enrichedBundle
  114. }
  115. if len(sessions) > 0 {
  116. recent := buildEntrySessionSummary(&sessions[0])
  117. result.Play.RecentSession = &recent
  118. }
  119. for i := range sessions {
  120. if isSessionOngoingStatus(sessions[i].Status) {
  121. ongoing := buildEntrySessionSummary(&sessions[i])
  122. result.Play.OngoingSession = &ongoing
  123. break
  124. }
  125. }
  126. canLaunch, launchReason := evaluateEventLaunchReadiness(event)
  127. result.Play.CanLaunch = canLaunch
  128. if canLaunch {
  129. result.Play.LaunchSource = LaunchSourceEventCurrentRelease
  130. }
  131. switch {
  132. case result.Play.OngoingSession != nil:
  133. result.Play.PrimaryAction = "continue"
  134. result.Play.Reason = "user has an ongoing session for this event"
  135. case canLaunch:
  136. result.Play.PrimaryAction = "start"
  137. result.Play.Reason = launchReason
  138. case result.Play.RecentSession != nil:
  139. result.Play.PrimaryAction = "review_last_result"
  140. result.Play.Reason = launchReason + ", but user has previous session history"
  141. default:
  142. result.Play.PrimaryAction = "unavailable"
  143. result.Play.Reason = launchReason
  144. }
  145. return result, nil
  146. }