package business import ( "context" "kra/internal/biz/business" "testing" "time" ) func TestWorkbenchStorageSummaryAndRankRules(t *testing.T) { db, _ := prepareChannels(t) ctx := context.Background() day := time.Date(2026, 11, 4, 0, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)) for _, sql := range []string{ "ALTER TABLE tab_promote ADD COLUMN last_login_time INTEGER", "INSERT INTO tab_promote(id,source_db,account,promote_level,parent_id,top_promote_id,busier_id,create_time) VALUES(105,'lx','zero-channel',1,0,0,14,1000)", "DELETE FROM tab_sw_report_day", "DELETE FROM tab_sw_report_day_key", "INSERT INTO tab_sw_report_day VALUES('lx',100,1,0,'2026-11-04',10),('lx',101,1,0,'2026-11-04',20),('hw',100,1,0,'2026-11-04',40),('lx',103,1,0,'2026-11-04',-5),('lx',0,1,0,'2026-11-04',5),('lx',999,1,0,'2026-11-04',7),('lx',100,1,1,'2026-11-04',9999),('lx',100,1,0,'2026-11-03',5),('hw',100,1,0,'2026-11-03',5),('lx',100,1,0,'2026-11-01',1),('lx',100,1,0,'2026-10-01',8),('lx',100,1,0,'2026-10-04',12),('lx',100,1,0,'2026-10-05',999)", "INSERT INTO tab_sw_report_day_key VALUES('lx',100,1,1,'2026-11-04',1,'A'),('lx',101,1,1,'2026-11-04',1,'A'),('hw',100,1,1,'2026-11-04',1,'A'),('lx',100,1,1,'2026-11-04',2,'A'),('lx',101,1,1,'2026-11-04',2,'B'),('hw',100,1,1,'2026-11-04',2,'A'),('lx',100,1,0,'2026-11-04',1,'1'),('lx',101,1,0,'2026-11-04',1,'2'),('hw',100,1,0,'2026-11-04',1,'1'),('lx',0,1,1,'2026-11-04',1,'official'),('lx',999,1,1,'2026-11-04',1,'orphan')", } { if err := db.Exec(sql).Error; err != nil { t.Fatal(err) } } if err := db.Exec("UPDATE tab_promote SET create_time=? WHERE source_db='lx' AND id IN(100,101)", day.Unix()).Error; err != nil { t.Fatal(err) } w := business.WorkbenchWindow{Today: day, Yesterday: day.AddDate(0, 0, -1), MonthStart: day.AddDate(0, 0, -3), PreviousMonthStart: day.AddDate(0, -1, -3), PreviousMonthEnd: day.AddDate(0, -1, 0)} r := NewWorkbenchRepo(testData{db}) scope := business.AnalyticsScope{Viewer: channelTestScope(), IncludeSub: true, IDCardDedup: true} sources := []string{"lx", "hw"} out, err := r.Summary(ctx, scope, sources, w) if err != nil || out.Today.Counts.Register != 1 || out.Today.Counts.Active != 2 || portraitTestMoney(out.Today.Pay) != "65.00" || out.Today.ActiveChannels != 3 || out.Today.NewChannels != 2 || portraitTestMoney(out.MonthPay) != "76.00" || portraitTestMoney(out.PreviousMonthPay) != "20.00" { t.Fatalf("工作台统计口径错误: %+v %v", out, err) } scope.IDCardDedup = false out, err = r.Summary(ctx, scope, sources, w) if err != nil || out.Today.Counts.Register != 3 || portraitTestMoney(out.Today.Pay) != "65.00" { t.Fatal("模式0应按平台前缀去重,金额不变") } scope.IncludeSub = false out, err = r.Summary(ctx, scope, sources, w) if err != nil || out.Today.Counts.Register != 2 || out.Today.NewChannels != 1 || out.Today.ActiveChannels != 2 || portraitTestMoney(out.Today.Pay) != "50.00" { t.Fatal("下级偏好未作用到渠道数和金额") } scope = business.AnalyticsScope{Viewer: business.UserScope{All: true}, IncludeSub: true, IDCardDedup: true} f := business.AnalyticsFilter{Sources: sources, Begin: "2026-11-04", End: "2026-11-04"} rows, err := r.Ranks(ctx, scope, f, business.WorkbenchRegister) if err != nil || len(rows.Rows) != 2 { t.Fatalf("注册榜含下级时应排除无渠道实体的官方/孤立记录: %+v %v", rows, err) } for _, row := range rows.Rows { if row.ID != 100 || row.Value != "1" { t.Fatal("注册榜应整棵树去重") } } rows, err = r.Ranks(ctx, scope, f, business.WorkbenchPayLow) if err != nil { t.Fatal(err) } values := map[business.AnalyticsKey]string{} for _, row := range rows.Rows { values[row.AnalyticsKey] = portraitTestMoney(row.Value) } for id, want := range map[int64]string{0: "5.00", 100: "30.00", 103: "-5.00", 105: "0.00", 999: "7.00"} { if values[business.AnalyticsKey{SourceDB: "lx", ID: id}] != want { t.Fatalf("最低榜漏掉零实体或汇总错误: %+v", values) } } scope.Viewer = channelTestScope() rows, err = r.Ranks(ctx, scope, f, business.WorkbenchUnlogin) if err != nil || len(rows.Rows) != 3 { t.Fatal("未登录榜只取可见一级实体") } if err := db.Exec("DROP TABLE tab_sw_report_day").Error; err != nil { t.Fatal(err) } rows, err = r.Ranks(ctx, scope, f, business.WorkbenchRegister) if err != nil || !rows.Available { t.Fatal("注册榜不应依赖金额汇总表") } out, err = r.Summary(ctx, scope, sources, w) if err != nil || out.Available { t.Fatal("六卡缺表应明确未接入") } } func TestWorkbenchSummaryMonthStartAndNullableRegistrationKeys(t *testing.T) { db, _ := prepareChannels(t) for _, statement := range []string{ "DELETE FROM tab_sw_report_day", "DELETE FROM tab_sw_report_day_key", "INSERT INTO tab_sw_report_day VALUES('lx',100,1,0,'2026-10-01',10),('lx',100,1,0,'2026-10-02',999),('lx',100,1,0,'2026-10-31',30),('lx',100,1,0,'2026-11-01',20),('lx',103,1,0,'2026-11-01',-1)", "INSERT INTO tab_sw_report_day_key VALUES('lx',103,1,1,'2026-11-01',1,NULL),('lx',999,1,1,'2026-11-01',1,''),('lx',100,1,1,'2026-10-31',1,'A')", } { if err := db.Exec(statement).Error; err != nil { t.Fatal(err) } } day := time.Date(2026, 11, 1, 0, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)) if err := db.Exec("UPDATE tab_promote SET create_time=CASE WHEN id=100 THEN ? ELSE ? END WHERE source_db='lx' AND id IN(100,101)", day.Unix(), day.AddDate(0, 0, -1).Unix()).Error; err != nil { t.Fatal(err) } window := business.WorkbenchWindow{Today: day, Yesterday: day.AddDate(0, 0, -1), MonthStart: day, PreviousMonthStart: day.AddDate(0, -1, 0), PreviousMonthEnd: day.AddDate(0, -1, 0)} out, err := NewWorkbenchRepo(testData{db}).Summary(context.Background(), business.AnalyticsScope{Viewer: business.UserScope{All: true}, IncludeSub: true, IDCardDedup: true}, []string{"lx"}, window) if err != nil { t.Fatal(err) } if portraitTestMoney(out.Today.Pay) != "19.00" || portraitTestMoney(out.Yesterday.Pay) != "30.00" || portraitTestMoney(out.MonthPay) != "19.00" || portraitTestMoney(out.PreviousMonthPay) != "10.00" { t.Fatalf("month-first comparison mixed yesterday with the previous-month window: %+v", out) } if out.Today.ActiveChannels != 2 || out.Today.Counts.Register != 1 || out.Yesterday.ActiveChannels != 1 || out.Today.NewChannels != 1 || out.Yesterday.NewChannels != 1 { t.Fatalf("daily groups or NULL/empty registration-key behavior changed: %+v", out) } }