package admin import ( "context" "github.com/google/uuid" "github.com/yuxingu/digital-psychology/apps/api/internal/repository" ) // PsychTag is a stable label derived from report types (not NLP). type PsychTag struct { Code string `json:"code"` Label string `json:"label"` } // BehaviorSnapshot is a thin ops read of recent activity. type BehaviorSnapshot struct { Events []repository.BehaviorEventBrief `json:"events"` AskThreadCount int `json:"ask_thread_count"` } // UserInsight is the UserIntelligence admin read model. type UserInsight struct { UserID uuid.UUID `json:"user_id"` ProfilesCount int `json:"profiles_count"` ReportsByType []repository.ReportTypeCount `json:"reports_by_type"` RecentReports []repository.ReportBrief `json:"recent_reports"` Tags []PsychTag `json:"tags"` Behavior BehaviorSnapshot `json:"behavior"` } var reportTypeLabels = map[string]string{ "portrait": "愈心解码", "star": "星座", "rhythm": "节律", "relation": "关系", "synastry": "合盘", "image_card": "意象卡", } // GetUserInsight aggregates read-only ops insight for one user. func (s *Service) GetUserInsight(ctx context.Context, userID uuid.UUID) (*UserInsight, error) { ok, err := s.Repo.UserExists(ctx, userID) if err != nil { return nil, err } if !ok { return nil, ErrUserNotFound } pc, err := s.Repo.CountProfilesForUser(ctx, userID) if err != nil { return nil, err } byType, err := s.Repo.CountReportsByType(ctx, userID) if err != nil { return nil, err } if byType == nil { byType = []repository.ReportTypeCount{} } reports, err := s.Repo.ListReportsForUser(ctx, userID, 10) if err != nil { return nil, err } if reports == nil { reports = []repository.ReportBrief{} } events, err := s.Repo.ListRecentEventsForUser(ctx, userID, 20) if err != nil { return nil, err } if events == nil { events = []repository.BehaviorEventBrief{} } askN, err := s.Repo.CountAskThreadsForUser(ctx, userID) if err != nil { return nil, err } return &UserInsight{ UserID: userID, ProfilesCount: pc, ReportsByType: byType, RecentReports: reports, Tags: tagsFromReportTypes(byType), Behavior: BehaviorSnapshot{ Events: events, AskThreadCount: askN, }, }, nil } func tagsFromReportTypes(counts []repository.ReportTypeCount) []PsychTag { out := make([]PsychTag, 0, len(counts)) for _, c := range counts { if c.Count <= 0 { continue } label := reportTypeLabels[c.Type] if label == "" { label = c.Type } out = append(out, PsychTag{Code: c.Type, Label: label}) } return out }