package main import ( "encoding/json" "net/http" "net/http/httptest" "net/url" "path/filepath" "strconv" "testing" "github.com/gin-gonic/gin" "github.com/glebarez/sqlite" "gorm.io/gorm" businessbiz "kra/internal/biz/business" "kra/internal/biz/system" businessdata "kra/internal/data/business" systemdata "kra/internal/data/system" "kra/internal/server/handler" businessservice "kra/internal/service/business" ) func TestServerAnalysisHTTP(t *testing.T) { db, err := gorm.Open(sqlite.Open(filepath.Join(t.TempDir(), "servers.db")), &gorm.Config{}) if err != nil { t.Fatal(err) } pool, _ := db.DB() t.Cleanup(func() { _ = pool.Close() }) for _, step := range append(systemdata.Migrations(), businessdata.Migrations()...) { if err := step.Migrate(db); err != nil { t.Fatal(err) } } for _, sql := range []string{ "INSERT INTO sys_users(id,uuid,username,level,parent_id,id_path,enable) VALUES(1,'root','root',0,0,'0',1),(7,'owner','owner',1,0,'0',1),(9,'leaf','leaf',3,7,'0,7',1),(99,'other','other',1,0,'0',1)", "INSERT INTO sys_user_bind_platform(user_id,source_db,platform_busier_id,status) VALUES(9,'lx',14,'0'),(99,'hw',20,'0')", "CREATE TABLE tab_promote(id INTEGER,source_db TEXT,promote_level INTEGER,busier_id INTEGER)", "INSERT INTO tab_promote VALUES(100,'lx',1,14),(101,'lx',2,14),(100,'hw',1,20)", "CREATE TABLE tab_game(id INTEGER,source_db TEXT,game_name TEXT)", "INSERT INTO tab_game VALUES(1,'lx','游戏'),(1,'hw','其他游戏')", "CREATE TABLE tab_game_server(id INTEGER,source_db TEXT,game_id INTEGER,server_num TEXT,server_name TEXT,start_time INTEGER)", "INSERT INTO tab_game_server VALUES(1,'lx',1,'001','一区',100),(2,'lx',1,'002','二区',200),(3,'hw',1,'001','外部一区',300)", "CREATE TABLE tab_sw_report_server_day(source_db TEXT,game_id INTEGER,game_name TEXT,promote_id INTEGER,server_num TEXT,stat_date DATE,dedup_mode INTEGER,total_pay DECIMAL(20,2))", "INSERT INTO tab_sw_report_server_day VALUES('lx',1,'游戏',100,'001','2026-11-04',0,10),('lx',1,'游戏',101,'001','2026-11-04',0,20),('lx',1,'游戏',100,'002','2026-11-04',0,40),('hw',1,'游戏',100,'001','2026-11-04',0,50),('lx',1,'游戏',100,'001','2026-11-04',1,9999)", "CREATE TABLE tab_sw_report_server_day_key(source_db TEXT,game_id INTEGER,game_name TEXT,promote_id INTEGER,server_num TEXT,stat_date DATE,dedup_mode INTEGER,metric INTEGER,identity_key TEXT)", } { if err := db.Exec(sql).Error; err != nil { t.Fatal(err) } } for _, s := range []struct { source, num string promote int }{{"lx", "001", 100}, {"lx", "001", 101}, {"lx", "002", 100}, {"hw", "001", 100}} { for _, metric := range []int{1, 2, 3} { for _, mode := range []int{0, 1} { identity := "A" if mode == 0 { identity = strconv.Itoa(s.promote) } if err := db.Exec("INSERT INTO tab_sw_report_server_day_key VALUES(?,1,'游戏',?,?,'2026-11-04',?,?,?)", s.source, s.promote, s.num, mode, metric, identity).Error; err != nil { t.Fatal(err) } } } } provider := businessHTTPData{db} uc := businessbiz.NewServerAnalysisUsecase(businessdata.NewServerAnalysisRepo(provider), businessbiz.NewAccountUsecase(businessdata.NewAccountRepo(provider)), businessbiz.NewUserPreferenceUsecase(businessdata.NewUserPreferenceRepo(provider))) h := handler.NewServerAnalysis(businessservice.NewServerAnalysisService(uc)) engine := gin.New() engine.Use(func(c *gin.Context) { id, _ := strconv.ParseUint(c.GetHeader("X-Test-User"), 10, 64) aid := uint(901) if id == 1 { aid = 888 } c.Request = c.Request.WithContext(system.NewActorContext(c.Request.Context(), system.Actor{UserID: uint(id), AuthorityID: aid})) c.Next() }) engine.GET("/sw/serverAnalysis/list", h.List) engine.GET("/sw/serverAnalysis/servers", h.Servers) srv := httptest.NewServer(engine) defer srv.Close() call := func(user int, path, query string, code int) any { t.Helper() req, _ := http.NewRequest("GET", srv.URL+"/sw/serverAnalysis/"+path+"?"+query, nil) req.Header.Set("X-Test-User", strconv.Itoa(user)) res, err := srv.Client().Do(req) if err != nil { t.Fatal(err) } defer res.Body.Close() var body map[string]any if err := json.NewDecoder(res.Body).Decode(&body); err != nil { t.Fatal(err) } if res.StatusCode != 200 || body["code"] != float64(code) { t.Fatalf("接口响应错误: %+v", body) } return body["data"] } q := "gameName=" + url.QueryEscape("游戏") + "&sourceDb=lx,hw&beginTime=2026-11-04&endTime=2026-11-04" p := call(7, "list", q+"&pageSize=1", 0).(map[string]any) sum := p["summary"].(map[string]any) if p["total"] != float64(2) || len(p["rows"].([]any)) != 1 || sum["totalPay"] != "70.00" || sum["activeCount"] != float64(2) || sum["arpu"] != "35.00" { t.Fatalf("分页或全量汇总错误: %+v", p) } p = call(7, "list", q+"&serverNum=001", 0).(map[string]any) row := p["rows"].([]any)[0].(map[string]any) if row["serverNum"] != "001" || row["serverName"] != "一区" || row["openTime"] != float64(100) || row["newRegisterCount"] != float64(1) || row["totalPay"] != "30.00" { t.Fatalf("区服身份、去重或金额错误: %+v", row) } p = call(1, "list", q, 0).(map[string]any) if p["total"] != float64(3) || p["summary"].(map[string]any)["totalPay"] != "120.00" { t.Fatal("跨平台同号区服被合并") } if err := db.Exec("INSERT INTO sys_user_preference(user_id,include_sub_promote,idcard_dedup) VALUES(7,1,0)").Error; err != nil { t.Fatal(err) } p = call(7, "list", q, 0).(map[string]any) if p["summary"].(map[string]any)["activeCount"] != float64(3) || p["summary"].(map[string]any)["totalPay"] != "70.00" { t.Fatal("去重开关改变金额或未改变人数") } if err := db.Exec("UPDATE sys_user_preference SET include_sub_promote=0 WHERE user_id=7").Error; err != nil { t.Fatal(err) } p = call(7, "list", q, 0).(map[string]any) if p["summary"].(map[string]any)["totalPay"] != "50.00" { t.Fatal("下级开关未生效") } options := call(7, "servers", "gameName="+url.QueryEscape("游戏")+"&sourceDb=lx&keyword="+url.QueryEscape("一区"), 0).([]any) if len(options) != 1 || options[0].(map[string]any)["serverNum"] != "001" { t.Fatal("区服选项映射错误") } call(7, "list", "", 7) call(7, "servers", "", 7) call(7, "list", q+"&pageNum=bad", 7) call(7, "list", "gameName=x&beginTime=bad&endTime=2026-11-04", 7) call(7, "list", "gameName=x&beginTime=2026-11-05&endTime=2026-11-04", 7) if err := db.Exec("DROP TABLE tab_sw_report_server_day_key").Error; err != nil { t.Fatal(err) } p = call(7, "list", q, 0).(map[string]any) if p["available"] != false || p["summary"] != nil { t.Fatal("缺表不能返回伪造零") } }