package business import ( "context" "strconv" "time" ) type DashboardWindow struct{ Begin, End, PreviousBegin, PreviousEnd time.Time } type DashboardMetrics struct { Register, Active, Effective, ActiveChannels, NewChannels int64 LockedChannels, TerminatedChannels int64 Pay string } type DashboardData struct { Current, Previous DashboardMetrics Available bool EventsAvailable bool } type DashboardRepo interface { Overview(context.Context, AnalyticsScope, []string, DashboardWindow) (*DashboardData, error) } type DashboardOverview struct { Cards map[string]*WorkbenchCard Begin, End string Settings OtherSettings Available bool } type DashboardUsecase struct { repo DashboardRepo workbench *WorkbenchUsecase effective *EffectivenessUsecase now func() time.Time } func NewDashboardUsecase(repo DashboardRepo, workbench *WorkbenchUsecase, effective *EffectivenessUsecase) *DashboardUsecase { return &DashboardUsecase{repo: repo, workbench: workbench, effective: effective, now: time.Now} } func (uc *DashboardUsecase) Overview(ctx context.Context, sources []string, period string, begin, end *time.Time) (*DashboardOverview, error) { scope, err := uc.workbench.scope(ctx, sources) if err != nil { return nil, err } today := workbenchWindow(uc.now()).Today b, e := today, today switch period { case "", "today": case "yesterday": b, e = today.AddDate(0, 0, -1), today.AddDate(0, 0, -1) case "last7": b = today.AddDate(0, 0, -6) case "last30": b = today.AddDate(0, 0, -29) case "custom": if begin == nil || end == nil { return nil, ErrPerformanceInput } b, e = *begin, *end default: return nil, ErrPerformanceInput } if e.Before(b) || e.After(today) { return nil, ErrPerformanceInput } days := int(e.Sub(b).Hours()/24) + 1 window := DashboardWindow{Begin: b, End: e, PreviousBegin: b.AddDate(0, 0, -days), PreviousEnd: b.AddDate(0, 0, -1)} if err := uc.effective.Ensure(ctx); err != nil { return nil, err } rule, err := uc.effective.Rule(ctx) if err != nil { return nil, err } data, err := uc.repo.Overview(ctx, scope, sources, window) if err != nil { return nil, err } r := &DashboardOverview{Cards: map[string]*WorkbenchCard{}, Begin: b.Format("2006-01-02"), End: e.Format("2006-01-02"), Settings: rule, Available: data.Available} num := func(v int64) string { return strconv.FormatInt(v, 10) } for _, card := range []struct{ key, current, previous string }{{"todayRegister", num(data.Current.Register), num(data.Previous.Register)}, {"todayActive", num(data.Current.Active), num(data.Previous.Active)}, {"todayPay", data.Current.Pay, data.Previous.Pay}, {"effectivePromote", num(data.Current.Effective), num(data.Previous.Effective)}, {"activePromote", num(data.Current.ActiveChannels), num(data.Previous.ActiveChannels)}, {"newPromote", num(data.Current.NewChannels), num(data.Previous.NewChannels)}} { value, err := workbenchCard(card.current, card.previous, "较上一同期", data.Available) if err != nil { return nil, err } r.Cards[card.key] = value } // The imported SDK channel table has only current state; historical event // counts must stay unavailable until a timestamped event source is connected. for _, card := range []struct { key string current, previous int64 }{{"lockedPromote", data.Current.LockedChannels, data.Previous.LockedChannels}, {"terminatedPromote", data.Current.TerminatedChannels, data.Previous.TerminatedChannels}} { label := "较上一同期" if !data.EventsAvailable { label = "事件数据未接入" } value, err := workbenchCard(num(card.current), num(card.previous), label, data.Available && data.EventsAvailable) if err != nil { return nil, err } r.Cards[card.key] = value } return r, nil }