186 lines
9.0 KiB
Go
186 lines
9.0 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"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 TestBusinessAndPlatformPortraitOverHTTP(t *testing.T) {
|
|
open := func(name string) *gorm.DB {
|
|
db, err := gorm.Open(sqlite.Open(filepath.Join(t.TempDir(), name)), &gorm.Config{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pool, _ := db.DB()
|
|
t.Cleanup(func() { _ = pool.Close() })
|
|
return db
|
|
}
|
|
db, platform := open("portrait-main.db"), open("portrait-platform.db")
|
|
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,real_name,level,parent_id,id_path,enable,business_status) VALUES(1,'admin','admin','管理员',0,0,'0',1,0),(7,'owner','owner','商务甲',1,0,'0',1,0),(9,'leaf','leaf','下级商务',3,7,'0,7',1,0),(50,'empty','empty','无绑定商务',1,0,'0',1,0),(99,'other','other','其他商务',1,0,'0',1,0)",
|
|
"INSERT INTO sys_user_bind_platform(user_id,source_db,platform_busier_id,platform_busier_account,status) VALUES(9,'lx',14,'sdk-child','0'),(99,'hw',14,'outside','0')",
|
|
"CREATE TABLE tab_promote(id INTEGER,source_db TEXT,account TEXT,promote_level INTEGER,top_promote_id INTEGER,busier_id INTEGER,create_time INTEGER)",
|
|
"INSERT INTO tab_promote VALUES(100,'lx','主渠道',1,0,14,1000),(101,'lx','子渠道',2,100,14,1000),(100,'hw','其他平台渠道',1,0,14,1000)",
|
|
"CREATE TABLE tab_sw_report_day(source_db TEXT,promote_id INTEGER,game_id INTEGER,dedup_mode INTEGER,stat_date DATE,total_pay DECIMAL(20,2))",
|
|
"CREATE TABLE tab_sw_report_day_key(source_db TEXT,promote_id INTEGER,game_id INTEGER,dedup_mode INTEGER,stat_date DATE,metric INTEGER,identity_key TEXT)",
|
|
"CREATE TABLE tab_user(source_db TEXT,promote_id INTEGER,puid INTEGER,register_time INTEGER)",
|
|
"CREATE TABLE tab_spend(source_db TEXT,promote_id INTEGER,pay_status INTEGER,pay_time INTEGER,pay_amount DECIMAL(20,2))",
|
|
} {
|
|
if err := db.Exec(sql).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
now := time.Now().In(time.FixedZone("CST", 8*3600))
|
|
today := now.Format("2006-01-02")
|
|
previous := now.AddDate(0, 0, -7).Format("2006-01-02")
|
|
for _, row := range []struct {
|
|
source string
|
|
promote, mode int
|
|
date, amount string
|
|
}{{"lx", 100, 0, today, "5.00"}, {"lx", 101, 0, today, "7.00"}, {"lx", 100, 1, today, "1200.00"}, {"lx", 100, 0, previous, "6.00"}, {"hw", 100, 0, today, "999.00"}} {
|
|
if err := db.Exec("INSERT INTO tab_sw_report_day VALUES(?,?,1,?,?,?)", row.source, row.promote, row.mode, row.date, row.amount).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
for _, mode := range []int{0, 1} {
|
|
second := "A"
|
|
if mode == 0 {
|
|
second = "B"
|
|
}
|
|
for _, row := range []struct {
|
|
promote, metric int
|
|
key, date string
|
|
}{{100, 1, "A", today}, {101, 1, second, today}, {100, 2, "A", today}, {101, 2, "B", today}, {100, 3, "A", today}, {100, 1, "P", previous}, {100, 2, "P1", previous}, {100, 2, "P2", previous}, {100, 2, "P3", previous}, {100, 2, "P4", previous}, {100, 3, "P1", previous}} {
|
|
if err := db.Exec("INSERT INTO tab_sw_report_day_key VALUES('lx',?,1,?,?,?,?)", row.promote, mode, row.date, row.metric, row.key).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
if err := db.Exec("INSERT INTO tab_sw_report_day_key VALUES('hw',100,1,1,?,1,'outside')", today).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Unix()
|
|
if err := db.Exec("INSERT INTO tab_user VALUES('lx',100,0,?),('lx',101,0,?),('lx',101,100,?)", start+3600, start+3600, start+3600).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.Exec("INSERT INTO tab_spend VALUES('lx',100,1,?,5),('lx',101,1,?,7),('lx',100,0,?,999)", start+3600, start+3600, start+3600).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, sql := range []string{"CREATE TABLE tab_promote_business(id INTEGER,account TEXT,real_name TEXT,level INTEGER,status INTEGER,commerce1 INTEGER,commerce2 INTEGER,create_time INTEGER,password TEXT)", "INSERT INTO tab_promote_business VALUES(14,'sdk-child','平台商务',3,0,11,12,1000,'not-for-response'),(12,'sdk-parent','平台上级',2,1,11,0,1000,'not-for-response')"} {
|
|
if err := platform.Exec(sql).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
provider := businessHTTPData{db}
|
|
platforms := &channelHTTPDatabases{platform: platform}
|
|
accounts := businessbiz.NewAccountUsecase(businessdata.NewAccountRepo(provider))
|
|
preferences := businessbiz.NewUserPreferenceUsecase(businessdata.NewUserPreferenceRepo(provider))
|
|
h := handler.NewBusinessPortrait(businessservice.NewBusinessPortraitService(businessbiz.NewBusinessPortraitUsecase(businessdata.NewBusinessPortraitRepo(provider, platforms), accounts, preferences)))
|
|
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/business/:userId/portrait", h.ForUser)
|
|
engine.GET("/sw/business/bindPlatform/portrait", h.ForBinding)
|
|
server := httptest.NewServer(engine)
|
|
defer server.Close()
|
|
call := func(user int, path string, expected int) map[string]any {
|
|
t.Helper()
|
|
req, _ := http.NewRequest("GET", server.URL+path, nil)
|
|
req.Header.Set("X-Test-User", strconv.Itoa(user))
|
|
res, err := server.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 body["code"] != float64(expected) {
|
|
t.Fatalf("%s 用户%d响应错误: %+v", path, user, body)
|
|
}
|
|
return body["data"].(map[string]any)
|
|
}
|
|
const profilePath = "/sw/business/7/portrait?sourceDbList=lx,hw&period=last7"
|
|
data := call(7, profilePath, 0)
|
|
basic := data["basic"].(map[string]any)
|
|
metrics := data["metrics"].(map[string]any)
|
|
if basic["userId"] != float64(7) || basic["promoteCount"] != float64(1) || metrics["register"].(map[string]any)["value"] != "1" || metrics["payAmount"].(map[string]any)["value"] != "12.00" || metrics["payRate"].(map[string]any)["value"] != "50.0" || metrics["payRate"].(map[string]any)["changeDiff"] != "25.0" || len(data["registerTrend"].([]any)) != 7 {
|
|
t.Fatalf("商务画像口径或响应错误: %+v", data)
|
|
}
|
|
ranks := data["registerTop5"].([]any)
|
|
if len(ranks) != 1 || ranks[0].(map[string]any)["value"] != "1.00" || ranks[0].(map[string]any)["promoteName"] != "主渠道" {
|
|
t.Fatal("注册排行没有在渠道树内去重")
|
|
}
|
|
data = call(7, "/sw/business/7/portrait?sourceDbList=lx&period=today", 0)
|
|
if len(data["registerTrend"].([]any)) != 24 || data["registerTrend"].([]any)[1].(map[string]any)["value"] != "2" {
|
|
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)
|
|
}
|
|
data = call(7, profilePath, 0)
|
|
if data["metrics"].(map[string]any)["register"].(map[string]any)["value"] != "2" {
|
|
t.Fatal("未读取查看者的去重偏好")
|
|
}
|
|
data = call(1, profilePath, 0)
|
|
if data["metrics"].(map[string]any)["register"].(map[string]any)["value"] != "1" {
|
|
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)
|
|
}
|
|
data = call(7, profilePath, 0)
|
|
if data["metrics"].(map[string]any)["payAmount"].(map[string]any)["value"] != "5.00" {
|
|
t.Fatal("关闭下级仍汇总子渠道金额")
|
|
}
|
|
data = call(9, "/sw/business/bindPlatform/portrait?sourceDb=lx&platformBusierId=14&period=last7", 0)
|
|
basic = data["basic"].(map[string]any)
|
|
if basic["userId"] != float64(14) || basic["userName"] != "sdk-child" || basic["parentUserId"] != float64(12) || basic["parentUserName"] != "sdk-parent" || basic["statusLabel"] != "待审核" {
|
|
t.Fatalf("SDK 基本信息混入 SW 资料: %+v", basic)
|
|
}
|
|
call(99, profilePath, 7)
|
|
call(7, "/sw/business/bindPlatform/portrait?sourceDb=hw&platformBusierId=14", 7)
|
|
call(7, "/sw/business/7/portrait", 7)
|
|
if err := db.Exec("DROP TABLE tab_promote").Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
data = call(7, profilePath, 0)
|
|
if data["available"] != false || data["metrics"] != nil || data["basic"].(map[string]any)["promoteCount"] != nil {
|
|
t.Fatal("缺渠道表时不能伪造零画像")
|
|
}
|
|
data = call(1, "/sw/business/50/portrait?sourceDbList=lx&period=today", 0)
|
|
if data["available"] != true || data["metrics"].(map[string]any)["register"].(map[string]any)["value"] != "0" {
|
|
t.Fatal("无绑定商务仍应返回真实空画像")
|
|
}
|
|
platforms.platform = nil
|
|
call(9, "/sw/business/bindPlatform/portrait?sourceDb=lx&platformBusierId=14", 7)
|
|
}
|