event_play_service.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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. Play struct {
  33. AssignmentMode *string `json:"assignmentMode,omitempty"`
  34. CourseVariants []CourseVariantView `json:"courseVariants,omitempty"`
  35. CanLaunch bool `json:"canLaunch"`
  36. PrimaryAction string `json:"primaryAction"`
  37. Reason string `json:"reason"`
  38. LaunchSource string `json:"launchSource,omitempty"`
  39. OngoingSession *EntrySessionSummary `json:"ongoingSession,omitempty"`
  40. RecentSession *EntrySessionSummary `json:"recentSession,omitempty"`
  41. } `json:"play"`
  42. }
  43. func NewEventPlayService(store *postgres.Store) *EventPlayService {
  44. return &EventPlayService{store: store}
  45. }
  46. func (s *EventPlayService) GetEventPlay(ctx context.Context, input EventPlayInput) (*EventPlayResult, error) {
  47. input.EventPublicID = strings.TrimSpace(input.EventPublicID)
  48. input.UserID = strings.TrimSpace(input.UserID)
  49. if input.EventPublicID == "" {
  50. return nil, apperr.New(http.StatusBadRequest, "invalid_params", "event id is required")
  51. }
  52. if input.UserID == "" {
  53. return nil, apperr.New(http.StatusUnauthorized, "unauthorized", "user is required")
  54. }
  55. event, err := s.store.GetEventByPublicID(ctx, input.EventPublicID)
  56. if err != nil {
  57. return nil, err
  58. }
  59. if event == nil {
  60. return nil, apperr.New(http.StatusNotFound, "event_not_found", "event not found")
  61. }
  62. sessions, err := s.store.ListSessionsByUserAndEvent(ctx, input.UserID, event.ID, 10)
  63. if err != nil {
  64. return nil, err
  65. }
  66. result := &EventPlayResult{}
  67. result.Event.ID = event.PublicID
  68. result.Event.Slug = event.Slug
  69. result.Event.DisplayName = event.DisplayName
  70. result.Event.Summary = event.Summary
  71. result.Event.Status = event.Status
  72. variantPlan := resolveVariantPlan(event.ReleasePayloadJSON)
  73. result.Play.AssignmentMode = variantPlan.AssignmentMode
  74. if len(variantPlan.CourseVariants) > 0 {
  75. result.Play.CourseVariants = variantPlan.CourseVariants
  76. }
  77. if event.CurrentReleasePubID != nil && event.ConfigLabel != nil && event.ManifestURL != nil {
  78. result.Release = &struct {
  79. ID string `json:"id"`
  80. ConfigLabel string `json:"configLabel"`
  81. ManifestURL string `json:"manifestUrl"`
  82. ManifestChecksumSha256 *string `json:"manifestChecksumSha256,omitempty"`
  83. RouteCode *string `json:"routeCode,omitempty"`
  84. }{
  85. ID: *event.CurrentReleasePubID,
  86. ConfigLabel: *event.ConfigLabel,
  87. ManifestURL: *event.ManifestURL,
  88. ManifestChecksumSha256: event.ManifestChecksum,
  89. RouteCode: event.RouteCode,
  90. }
  91. }
  92. result.ResolvedRelease = buildResolvedReleaseFromEvent(event, LaunchSourceEventCurrentRelease)
  93. if len(sessions) > 0 {
  94. recent := buildEntrySessionSummary(&sessions[0])
  95. result.Play.RecentSession = &recent
  96. }
  97. for i := range sessions {
  98. if isSessionOngoingStatus(sessions[i].Status) {
  99. ongoing := buildEntrySessionSummary(&sessions[i])
  100. result.Play.OngoingSession = &ongoing
  101. break
  102. }
  103. }
  104. canLaunch := event.Status == "active" && event.CurrentReleaseID != nil && event.ManifestURL != nil
  105. result.Play.CanLaunch = canLaunch
  106. if canLaunch {
  107. result.Play.LaunchSource = LaunchSourceEventCurrentRelease
  108. }
  109. switch {
  110. case result.Play.OngoingSession != nil:
  111. result.Play.PrimaryAction = "continue"
  112. result.Play.Reason = "user has an ongoing session for this event"
  113. case canLaunch:
  114. result.Play.PrimaryAction = "start"
  115. result.Play.Reason = "event is active and launchable"
  116. case result.Play.RecentSession != nil:
  117. result.Play.PrimaryAction = "review_last_result"
  118. result.Play.Reason = "event is not launchable, but user has previous session history"
  119. default:
  120. result.Play.PrimaryAction = "unavailable"
  121. result.Play.Reason = "event is not launchable"
  122. }
  123. return result, nil
  124. }