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 }