264 lines
8.9 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"context"
"fmt"
"time"
"bindbox-game/configs"
"bindbox-game/internal/pkg/logger"
"bindbox-game/internal/repository/mysql"
"bindbox-game/internal/repository/mysql/dao"
"bindbox-game/internal/repository/mysql/model"
tcmodel "bindbox-game/internal/repository/mysql/task_center"
tasksvc "bindbox-game/internal/service/task_center"
"bindbox-game/internal/service/title"
"bindbox-game/internal/service/user"
"github.com/redis/go-redis/v9"
)
// IntegrationTest 运行集成测试流
func IntegrationTest(repo mysql.Repo) error {
ctx := context.Background()
cfg := configs.Get()
// 1. 初始化日志(自定义)
l, err := logger.NewCustomLogger(dao.Use(repo.GetDbW()))
if err != nil {
return fmt.Errorf("初始化日志失败: %v", err)
}
// 2. 初始化 Redis
rdb := redis.NewClient(&redis.Options{
Addr: cfg.Redis.Addr,
Password: cfg.Redis.Pass,
DB: cfg.Redis.DB,
})
if err := rdb.Ping(ctx).Err(); err != nil {
return fmt.Errorf("连接 Redis 失败: %v", err)
}
// 3. 初始化依赖服务
userSvc := user.New(l, repo)
titleSvc := title.New(l, repo)
taskSvc := tasksvc.New(l, repo, rdb, userSvc, titleSvc)
// 3.5 清理缓存以确保能加载最新配置
if err := rdb.Del(ctx, "task_center:active_tasks").Err(); err != nil {
fmt.Printf("⚠️ 清理缓存失败: %v\n", err)
}
// 4. 选择一个测试用户和任务
// ... (代码逻辑不变)
userID := int64(8888)
// 搜索一个首单任务(满足 lifetime 窗口,奖励为点数)
var task tcmodel.Task
db := repo.GetDbW()
if err := db.Joins("JOIN task_center_task_tiers ON task_center_task_tiers.task_id = task_center_tasks.id").
Joins("JOIN task_center_task_rewards ON task_center_task_rewards.task_id = task_center_tasks.id").
Where("task_center_task_tiers.metric = ? AND task_center_task_tiers.window = ? AND task_center_task_rewards.reward_type = ?", "first_order", "lifetime", "points").
First(&task).Error; err != nil {
return fmt.Errorf("未找到符合条件的集成测试任务: %v", err)
}
fmt.Printf("--- 开始集成测试 ---\n")
fmt.Printf("用户ID: %d, 任务ID: %d (%s)\n", userID, task.ID, task.Name)
// 5. 创建一个模拟订单
orderNo := fmt.Sprintf("TEST_ORDER_%d", time.Now().Unix())
order := &model.Orders{
UserID: userID,
OrderNo: orderNo,
TotalAmount: 100,
ActualAmount: 100,
Status: 2, // 已支付
PaidAt: time.Now(),
}
if err := db.Omit("cancelled_at").Create(order).Error; err != nil {
return fmt.Errorf("创建测试订单失败: %v", err)
}
fmt.Printf("创建测试订单: %s (ID: %d)\n", orderNo, order.ID)
// 6. 触发 OnOrderPaid
fmt.Println("触发 OnOrderPaid 事件...")
if err := taskSvc.OnOrderPaid(ctx, userID, order.ID); err != nil {
return fmt.Errorf("OnOrderPaid 失败: %v", err)
}
// 7. 验证结果
// A. 检查进度是否更新
var progress tcmodel.UserTaskProgress
if err := db.Where("user_id = ? AND task_id = ?", userID, task.ID).First(&progress).Error; err != nil {
fmt.Printf("❌ 进度记录未找到: %v\n", err)
} else {
fmt.Printf("✅ 进度记录已更新: first_order=%d\n", progress.FirstOrder)
}
// B. 检查奖励日志
time.Sleep(1 * time.Second)
var eventLog tcmodel.TaskEventLog
if err := db.Where("user_id = ? AND task_id = ?", userID, task.ID).Order("id desc").First(&eventLog).Error; err != nil {
fmt.Printf("❌ 奖励日志未找到: %v\n", err)
} else {
fmt.Printf("✅ 奖励日志已找到: Status=%s, Result=%s\n", eventLog.Status, eventLog.Result)
if eventLog.Status == "granted" {
fmt.Printf("🎉 集成测试通过!奖励已成功发放。\n")
} else {
fmt.Printf("⚠️ 奖励发放状态异常: %s\n", eventLog.Status)
}
}
return nil
}
// InviteAndTaskIntegrationTest 运行邀请与任务全链路集成测试
func InviteAndTaskIntegrationTest(repo mysql.Repo) error {
ctx := context.Background()
cfg := configs.Get()
db := repo.GetDbW()
// 1. 初始化
l, _ := logger.NewCustomLogger(dao.Use(db))
rdb := redis.NewClient(&redis.Options{Addr: cfg.Redis.Addr, Password: cfg.Redis.Pass, DB: cfg.Redis.DB})
userSvc := user.New(l, repo)
titleSvc := title.New(l, repo)
taskSvc := tasksvc.New(l, repo, rdb, userSvc, titleSvc)
// 2. 准备角色
inviterID := int64(9001)
inviteeID := int64(9002)
_ = ensureUserExists(repo, inviterID, "老司机(邀请者)")
_ = ensureUserExists(repo, inviteeID, "萌新(被邀请者)")
// 3. 建立邀请关系
if err := ensureInviteRelationship(repo, inviterID, inviteeID); err != nil {
return fmt.Errorf("建立邀请关系失败: %v", err)
}
// 4. 清理 Redis 缓存
_ = rdb.Del(ctx, "task_center:active_tasks").Err()
// 5. 查找测试任务
var inviteTask tcmodel.Task
if err := db.Joins("JOIN task_center_task_tiers ON task_center_task_tiers.task_id = task_center_tasks.id").
Where("task_center_task_tiers.metric = ? AND task_center_task_tiers.window = ?", "invite_count", "lifetime").
First(&inviteTask).Error; err != nil {
return fmt.Errorf("未找到邀请任务: %v", err)
}
var firstOrderTask tcmodel.Task
if err := db.Joins("JOIN task_center_task_tiers ON task_center_task_tiers.task_id = task_center_tasks.id").
Where("task_center_task_tiers.metric = ? AND task_center_task_tiers.window = ?", "first_order", "lifetime").
First(&firstOrderTask).Error; err != nil {
return fmt.Errorf("未找到首单任务: %v", err)
}
fmt.Printf("--- 开始邀请全链路测试 ---\n")
fmt.Printf("邀请人: %d, 被邀请人: %d\n", inviterID, inviteeID)
// 6. 模拟邀请成功事件 (触发两次以确保达到默认阈值 2)
fmt.Println("触发 OnInviteSuccess 事件 (第1次)...")
if err := taskSvc.OnInviteSuccess(ctx, inviterID, inviteeID); err != nil {
return fmt.Errorf("OnInviteSuccess 失败: %v", err)
}
fmt.Println("触发 OnInviteSuccess 事件 (第2次, 换个用户ID)...")
if err := taskSvc.OnInviteSuccess(ctx, inviterID, 9999); err != nil {
return fmt.Errorf("OnInviteSuccess 失败: %v", err)
}
// 7. 模拟被邀请者下单
orderNo := fmt.Sprintf("INVITE_ORDER_%d", time.Now().Unix())
order := &model.Orders{
UserID: inviteeID,
OrderNo: orderNo,
TotalAmount: 100,
ActualAmount: 100,
Status: 2, // 已支付
PaidAt: time.Now(),
}
if err := db.Omit("cancelled_at").Create(order).Error; err != nil {
return fmt.Errorf("创建被邀请者订单失败: %v", err)
}
fmt.Printf("被邀请者下单成功: %s (ID: %d)\n", orderNo, order.ID)
fmt.Println("触发 OnOrderPaid 事件 (被邀请者)...")
if err := taskSvc.OnOrderPaid(ctx, inviteeID, order.ID); err != nil {
return fmt.Errorf("OnOrderPaid 失败: %v", err)
}
// 8. 验证
time.Sleep(1 * time.Second)
fmt.Println("\n--- 数据库进度核查 ---")
var allProgress []tcmodel.UserTaskProgress
db.Where("user_id IN (?)", []int64{inviterID, inviteeID}).Find(&allProgress)
if len(allProgress) == 0 {
fmt.Println("⚠️ 数据库中未找到任何进度记录!")
}
for _, p := range allProgress {
userLabel := "邀请人"
if p.UserID == inviteeID {
userLabel = "被邀请人"
}
fmt.Printf("[%s] 用户:%d 任务:%d | Invite=%d, OrderCount=%d, FirstOrder=%d\n",
userLabel, p.UserID, p.TaskID, p.InviteCount, p.OrderCount, p.FirstOrder)
}
fmt.Println("\n--- 奖励发放核查 ---")
var logs []tcmodel.TaskEventLog
db.Where("user_id IN (?) AND status = ?", []int64{inviterID, inviteeID}, "granted").Find(&logs)
fmt.Printf("✅ 累计发放奖励次数: %d\n", len(logs))
for _, l := range logs {
fmt.Printf(" - 用户 %d 触发任务 %d 奖励 | Source:%s\n", l.UserID, l.TaskID, l.SourceType)
}
if len(logs) >= 2 {
fmt.Println("\n🎉 邀请全链路集成测试通过!邀请人和被邀请人都获得了奖励。")
} else {
fmt.Printf("\n⚠ 测试部分完成,奖励次数(%d)少于预期(2)\n", len(logs))
}
return nil
}
// 模拟创建用户的方法(如果不存在)
func ensureUserExists(repo mysql.Repo, userID int64, nickname string) error {
db := repo.GetDbW()
var user model.Users
if err := db.Where("id = ?", userID).First(&user).Error; err != nil {
user = model.Users{
ID: userID,
Nickname: nickname,
Avatar: "http://example.com/a.png",
Status: 1,
InviteCode: fmt.Sprintf("CODE%d", userID),
}
if err := db.Create(&user).Error; err != nil {
return err
}
fmt.Printf("已确保测试用户存在: %d (%s)\n", userID, nickname)
}
return nil
}
// 建立邀请关系
func ensureInviteRelationship(repo mysql.Repo, inviterID, inviteeID int64) error {
db := repo.GetDbW()
var rel model.UserInvites
if err := db.Where("invitee_id = ?", inviteeID).First(&rel).Error; err != nil {
rel = model.UserInvites{
InviterID: inviterID,
InviteeID: inviteeID,
InviteCode: fmt.Sprintf("CODE%d", inviterID),
}
return db.Omit("rewarded_at").Create(&rel).Error
}
// 如果已存在但邀请人不对,修正它
if rel.InviterID != inviterID {
return db.Model(&rel).Update("inviter_id", inviterID).Error
}
return nil
}