package service import ( "context" "net/http" "strings" "cmr-backend/internal/apperr" "cmr-backend/internal/store/postgres" ) type EntryHomeService struct { store *postgres.Store } type EntryHomeInput struct { UserID string ChannelCode string ChannelType string PlatformAppID string TenantCode string } type EntryHomeResult struct { User struct { ID string `json:"id"` PublicID string `json:"publicId"` Status string `json:"status"` Nickname *string `json:"nickname,omitempty"` AvatarURL *string `json:"avatarUrl,omitempty"` } `json:"user"` 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"` OngoingSession *EntrySessionSummary `json:"ongoingSession,omitempty"` RecentSession *EntrySessionSummary `json:"recentSession,omitempty"` } type EntrySessionSummary struct { ID string `json:"id"` Status string `json:"status"` EventID string `json:"eventId"` EventName string `json:"eventName"` ReleaseID *string `json:"releaseId,omitempty"` ConfigLabel *string `json:"configLabel,omitempty"` RouteCode *string `json:"routeCode,omitempty"` LaunchedAt string `json:"launchedAt"` StartedAt *string `json:"startedAt,omitempty"` EndedAt *string `json:"endedAt,omitempty"` } func NewEntryHomeService(store *postgres.Store) *EntryHomeService { return &EntryHomeService{store: store} } func (s *EntryHomeService) GetEntryHome(ctx context.Context, input EntryHomeInput) (*EntryHomeResult, error) { input.UserID = strings.TrimSpace(input.UserID) if input.UserID == "" { return nil, apperr.New(http.StatusUnauthorized, "unauthorized", "user is required") } user, err := s.store.GetUserByID(ctx, s.store.Pool(), input.UserID) if err != nil { return nil, err } if user == nil { return nil, apperr.New(http.StatusNotFound, "user_not_found", "user not found") } 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") } cards, err := s.store.ListCardsForEntry(ctx, entry.TenantID, &entry.ID, "home_primary", nowUTC(), 20) if err != nil { return nil, err } sessions, err := s.store.ListSessionsByUserID(ctx, user.ID, 10) if err != nil { return nil, err } result := &EntryHomeResult{ Cards: mapCards(cards), } result.User.ID = user.ID result.User.PublicID = user.PublicID result.User.Status = user.Status result.User.Nickname = user.Nickname result.User.AvatarURL = user.AvatarURL 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 if len(sessions) > 0 { recent := buildEntrySessionSummary(&sessions[0]) result.RecentSession = &recent } for i := range sessions { if isSessionOngoingStatus(sessions[i].Status) { ongoing := buildEntrySessionSummary(&sessions[i]) result.OngoingSession = &ongoing break } } return result, nil } func buildEntrySessionSummary(session *postgres.Session) EntrySessionSummary { summary := EntrySessionSummary{ ID: session.SessionPublicID, Status: session.Status, RouteCode: session.RouteCode, LaunchedAt: session.LaunchedAt.Format(timeRFC3339), } if session.EventPublicID != nil { summary.EventID = *session.EventPublicID } if session.EventDisplayName != nil { summary.EventName = *session.EventDisplayName } summary.ReleaseID = session.ReleasePublicID summary.ConfigLabel = session.ConfigLabel if session.StartedAt != nil { value := session.StartedAt.Format(timeRFC3339) summary.StartedAt = &value } if session.EndedAt != nil { value := session.EndedAt.Format(timeRFC3339) summary.EndedAt = &value } return summary }