632 lines
17 KiB
Go
632 lines
17 KiB
Go
package taskcenter
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"testing"
|
||
"time"
|
||
|
||
tcmodel "bindbox-game/internal/repository/mysql/task_center"
|
||
|
||
"gorm.io/datatypes"
|
||
"gorm.io/driver/sqlite"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// ================================
|
||
// 配置组合生成器
|
||
// ================================
|
||
|
||
// TaskCombination 表示一种任务配置组合
|
||
type TaskCombination struct {
|
||
Name string // 任务名称
|
||
Metric string // 指标类型
|
||
Operator string // 操作符
|
||
Threshold int64 // 阈值
|
||
Window string // 时间窗口
|
||
RewardType string // 奖励类型
|
||
}
|
||
|
||
// GenerateAllCombinations 生成所有有效的任务配置组合
|
||
func GenerateAllCombinations() []TaskCombination {
|
||
metrics := []struct {
|
||
name string
|
||
operators []string
|
||
threshold int64
|
||
}{
|
||
{MetricFirstOrder, []string{OperatorEQ}, 1}, // 首单只用 =
|
||
{MetricOrderCount, []string{OperatorGTE, OperatorEQ}, 3}, // 订单数量支持 >= 和 =
|
||
{MetricOrderAmount, []string{OperatorGTE, OperatorEQ}, 10000}, // 消费金额(单位分 = 100元)
|
||
{MetricInviteCount, []string{OperatorGTE, OperatorEQ}, 2}, // 邀请人数
|
||
}
|
||
windows := []string{WindowDaily, WindowWeekly, WindowLifetime}
|
||
rewards := []string{RewardTypePoints, RewardTypeCoupon, RewardTypeItemCard, RewardTypeTitle, RewardTypeGameTicket}
|
||
|
||
var combinations []TaskCombination
|
||
idx := 0
|
||
for _, m := range metrics {
|
||
for _, op := range m.operators {
|
||
for _, w := range windows {
|
||
for _, r := range rewards {
|
||
idx++
|
||
combinations = append(combinations, TaskCombination{
|
||
Name: fmt.Sprintf("测试任务%d_%s_%s_%s", idx, m.name, w, r),
|
||
Metric: m.name,
|
||
Operator: op,
|
||
Threshold: m.threshold,
|
||
Window: w,
|
||
RewardType: r,
|
||
})
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return combinations
|
||
}
|
||
|
||
// CreateTestDB 创建内存数据库并初始化表结构
|
||
func CreateTestDB(t *testing.T) *gorm.DB {
|
||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||
if err != nil {
|
||
t.Fatalf("创建数据库失败: %v", err)
|
||
}
|
||
|
||
// 创建任务中心相关表
|
||
if err := db.Exec(`CREATE TABLE task_center_tasks (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
name TEXT NOT NULL,
|
||
description TEXT,
|
||
status INTEGER NOT NULL DEFAULT 1,
|
||
start_time DATETIME,
|
||
end_time DATETIME,
|
||
visibility INTEGER NOT NULL DEFAULT 1,
|
||
conditions_schema TEXT,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
);`).Error; err != nil {
|
||
t.Fatalf("创建 task_center_tasks 表失败: %v", err)
|
||
}
|
||
|
||
if err := db.Exec(`CREATE TABLE task_center_task_tiers (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
task_id INTEGER NOT NULL,
|
||
metric TEXT NOT NULL,
|
||
operator TEXT NOT NULL,
|
||
threshold INTEGER NOT NULL,
|
||
window TEXT NOT NULL,
|
||
repeatable INTEGER NOT NULL DEFAULT 1,
|
||
priority INTEGER NOT NULL DEFAULT 0,
|
||
activity_id INTEGER NOT NULL DEFAULT 0,
|
||
extra_params TEXT,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
);`).Error; err != nil {
|
||
t.Fatalf("创建 task_center_task_tiers 表失败: %v", err)
|
||
}
|
||
|
||
if err := db.Exec(`CREATE TABLE task_center_task_rewards (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
task_id INTEGER NOT NULL,
|
||
tier_id INTEGER NOT NULL,
|
||
reward_type TEXT NOT NULL,
|
||
reward_payload TEXT NOT NULL,
|
||
quantity INTEGER NOT NULL DEFAULT 1,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
);`).Error; err != nil {
|
||
t.Fatalf("创建 task_center_task_rewards 表失败: %v", err)
|
||
}
|
||
|
||
if err := db.Exec(`CREATE TABLE task_center_user_progress (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
user_id INTEGER NOT NULL,
|
||
task_id INTEGER NOT NULL,
|
||
activity_id INTEGER NOT NULL DEFAULT 0,
|
||
order_count INTEGER NOT NULL DEFAULT 0,
|
||
order_amount INTEGER NOT NULL DEFAULT 0,
|
||
invite_count INTEGER NOT NULL DEFAULT 0,
|
||
first_order INTEGER NOT NULL DEFAULT 0,
|
||
claimed_tiers TEXT,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
UNIQUE(user_id, task_id, activity_id)
|
||
);`).Error; err != nil {
|
||
t.Fatalf("创建 task_center_user_progress 表失败: %v", err)
|
||
}
|
||
|
||
if err := db.Exec(`CREATE TABLE task_center_event_logs (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
event_id TEXT,
|
||
source_type TEXT,
|
||
source_id INTEGER,
|
||
user_id INTEGER,
|
||
task_id INTEGER,
|
||
tier_id INTEGER,
|
||
idempotency_key TEXT UNIQUE,
|
||
status TEXT,
|
||
result TEXT,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
);`).Error; err != nil {
|
||
t.Fatalf("创建 task_center_event_logs 表失败: %v", err)
|
||
}
|
||
|
||
return db
|
||
}
|
||
|
||
// InsertTaskWithTierAndReward 插入一个完整的任务配置(任务+档位+奖励)
|
||
func InsertTaskWithTierAndReward(t *testing.T, db *gorm.DB, combo TaskCombination) (taskID, tierID int64) {
|
||
// 插入任务
|
||
task := &tcmodel.Task{
|
||
Name: combo.Name,
|
||
Description: fmt.Sprintf("测试 %s + %s + %s + %s", combo.Metric, combo.Operator, combo.Window, combo.RewardType),
|
||
Status: 1,
|
||
Visibility: 1,
|
||
}
|
||
if err := db.Create(task).Error; err != nil {
|
||
t.Fatalf("插入任务失败: %v", err)
|
||
}
|
||
|
||
// 插入档位
|
||
tier := &tcmodel.TaskTier{
|
||
TaskID: task.ID,
|
||
Metric: combo.Metric,
|
||
Operator: combo.Operator,
|
||
Threshold: combo.Threshold,
|
||
Window: combo.Window,
|
||
Priority: 0,
|
||
}
|
||
if err := db.Create(tier).Error; err != nil {
|
||
t.Fatalf("插入档位失败: %v", err)
|
||
}
|
||
|
||
// 生成奖励 payload
|
||
payload := generateRewardPayload(combo.RewardType)
|
||
reward := &tcmodel.TaskReward{
|
||
TaskID: task.ID,
|
||
TierID: tier.ID,
|
||
RewardType: combo.RewardType,
|
||
RewardPayload: datatypes.JSON(payload),
|
||
Quantity: 10, // 默认数量
|
||
}
|
||
if err := db.Create(reward).Error; err != nil {
|
||
t.Fatalf("插入奖励失败: %v", err)
|
||
}
|
||
|
||
return task.ID, tier.ID
|
||
}
|
||
|
||
// generateRewardPayload 根据奖励类型生成对应的 JSON payload
|
||
func generateRewardPayload(rewardType string) string {
|
||
switch rewardType {
|
||
case RewardTypePoints:
|
||
return `{"points": 100}`
|
||
case RewardTypeCoupon:
|
||
return `{"coupon_id": 1, "quantity": 1}`
|
||
case RewardTypeItemCard:
|
||
return `{"card_id": 1, "quantity": 1}`
|
||
case RewardTypeTitle:
|
||
return `{"title_id": 1}`
|
||
case RewardTypeGameTicket:
|
||
return `{"game_code": "minesweeper", "amount": 5}`
|
||
default:
|
||
return `{}`
|
||
}
|
||
}
|
||
|
||
// ================================
|
||
// 单元测试
|
||
// ================================
|
||
|
||
// TestGenerateAllCombinations 测试配置组合生成器
|
||
func TestGenerateAllCombinations(t *testing.T) {
|
||
combos := GenerateAllCombinations()
|
||
|
||
// 预期组合数: (1*1 + 3*2) * 3 * 5 = 7 * 3 * 5 = 105
|
||
expectedCount := 105
|
||
if len(combos) != expectedCount {
|
||
t.Errorf("组合数量不正确: 期望 %d, 实际 %d", expectedCount, len(combos))
|
||
}
|
||
|
||
// 验证每种指标都有覆盖
|
||
metricCounts := make(map[string]int)
|
||
for _, c := range combos {
|
||
metricCounts[c.Metric]++
|
||
}
|
||
|
||
t.Logf("配置组合统计:")
|
||
t.Logf(" 总数: %d", len(combos))
|
||
for metric, count := range metricCounts {
|
||
t.Logf(" %s: %d 种组合", metric, count)
|
||
}
|
||
}
|
||
|
||
// TestInsertAllCombinations 测试将所有配置组合插入数据库
|
||
func TestInsertAllCombinations(t *testing.T) {
|
||
db := CreateTestDB(t)
|
||
combos := GenerateAllCombinations()
|
||
|
||
for _, combo := range combos {
|
||
taskID, tierID := InsertTaskWithTierAndReward(t, db, combo)
|
||
if taskID == 0 || tierID == 0 {
|
||
t.Errorf("插入失败: %s", combo.Name)
|
||
}
|
||
}
|
||
|
||
// 验证数据库记录数
|
||
var taskCount, tierCount, rewardCount int64
|
||
db.Model(&tcmodel.Task{}).Count(&taskCount)
|
||
db.Model(&tcmodel.TaskTier{}).Count(&tierCount)
|
||
db.Model(&tcmodel.TaskReward{}).Count(&rewardCount)
|
||
|
||
if taskCount != int64(len(combos)) {
|
||
t.Errorf("任务数量不正确: 期望 %d, 实际 %d", len(combos), taskCount)
|
||
}
|
||
if tierCount != int64(len(combos)) {
|
||
t.Errorf("档位数量不正确: 期望 %d, 实际 %d", len(combos), tierCount)
|
||
}
|
||
if rewardCount != int64(len(combos)) {
|
||
t.Errorf("奖励数量不正确: 期望 %d, 实际 %d", len(combos), rewardCount)
|
||
}
|
||
|
||
t.Logf("成功插入 %d 个任务配置组合", taskCount)
|
||
}
|
||
|
||
// TestFirstOrderMetric 测试首单指标
|
||
func TestFirstOrderMetric(t *testing.T) {
|
||
db := CreateTestDB(t)
|
||
|
||
combo := TaskCombination{
|
||
Name: "首单测试任务",
|
||
Metric: MetricFirstOrder,
|
||
Operator: OperatorEQ,
|
||
Threshold: 1,
|
||
Window: WindowLifetime,
|
||
RewardType: RewardTypePoints,
|
||
}
|
||
taskID, tierID := InsertTaskWithTierAndReward(t, db, combo)
|
||
|
||
// 模拟用户首单进度
|
||
userID := int64(1001)
|
||
progress := &tcmodel.UserTaskProgress{
|
||
UserID: userID,
|
||
TaskID: taskID,
|
||
OrderCount: 1,
|
||
OrderAmount: 10000,
|
||
FirstOrder: 1, // 首单标记
|
||
ClaimedTiers: datatypes.JSON("[]"),
|
||
}
|
||
if err := db.Create(progress).Error; err != nil {
|
||
t.Fatalf("创建进度失败: %v", err)
|
||
}
|
||
|
||
// 验证进度状态
|
||
var p tcmodel.UserTaskProgress
|
||
db.Where("user_id = ? AND task_id = ?", userID, taskID).First(&p)
|
||
if p.FirstOrder != 1 {
|
||
t.Error("首单标记未正确设置")
|
||
}
|
||
|
||
t.Logf("首单指标测试通过: taskID=%d, tierID=%d", taskID, tierID)
|
||
}
|
||
|
||
// TestOrderCountMetric 测试订单数量指标
|
||
func TestOrderCountMetric(t *testing.T) {
|
||
db := CreateTestDB(t)
|
||
|
||
combo := TaskCombination{
|
||
Name: "订单数量测试任务",
|
||
Metric: MetricOrderCount,
|
||
Operator: OperatorGTE,
|
||
Threshold: 3,
|
||
Window: WindowDaily,
|
||
RewardType: RewardTypeCoupon,
|
||
}
|
||
taskID, _ := InsertTaskWithTierAndReward(t, db, combo)
|
||
|
||
// 模拟用户下单进度
|
||
userID := int64(1002)
|
||
for i := 1; i <= 5; i++ {
|
||
progress := &tcmodel.UserTaskProgress{
|
||
UserID: userID,
|
||
TaskID: taskID,
|
||
OrderCount: int64(i),
|
||
ClaimedTiers: datatypes.JSON("[]"),
|
||
}
|
||
// Upsert 模拟
|
||
db.Where("user_id = ? AND task_id = ?", userID, taskID).Assign(progress).FirstOrCreate(progress)
|
||
}
|
||
|
||
var p tcmodel.UserTaskProgress
|
||
db.Where("user_id = ? AND task_id = ?", userID, taskID).First(&p)
|
||
if p.OrderCount < 3 {
|
||
t.Error("订单数量未达到阈值")
|
||
}
|
||
|
||
t.Logf("订单数量指标测试通过: 当前订单数=%d", p.OrderCount)
|
||
}
|
||
|
||
// TestOrderAmountMetric 测试消费金额指标
|
||
func TestOrderAmountMetric(t *testing.T) {
|
||
db := CreateTestDB(t)
|
||
|
||
combo := TaskCombination{
|
||
Name: "消费金额测试任务",
|
||
Metric: MetricOrderAmount,
|
||
Operator: OperatorGTE,
|
||
Threshold: 10000, // 100元
|
||
Window: WindowWeekly,
|
||
RewardType: RewardTypeItemCard,
|
||
}
|
||
taskID, _ := InsertTaskWithTierAndReward(t, db, combo)
|
||
|
||
// 模拟用户累计消费
|
||
userID := int64(1003)
|
||
amounts := []int64{3000, 4000, 5000} // 累计 120 元
|
||
totalAmount := int64(0)
|
||
for _, amt := range amounts {
|
||
totalAmount += amt
|
||
}
|
||
|
||
progress := &tcmodel.UserTaskProgress{
|
||
UserID: userID,
|
||
TaskID: taskID,
|
||
OrderAmount: totalAmount,
|
||
ClaimedTiers: datatypes.JSON("[]"),
|
||
}
|
||
db.Create(progress)
|
||
|
||
var p tcmodel.UserTaskProgress
|
||
db.Where("user_id = ? AND task_id = ?", userID, taskID).First(&p)
|
||
if p.OrderAmount < 10000 {
|
||
t.Error("消费金额未达到阈值")
|
||
}
|
||
|
||
t.Logf("消费金额指标测试通过: 当前消费=%d分", p.OrderAmount)
|
||
}
|
||
|
||
// TestInviteCountMetric 测试邀请人数指标
|
||
func TestInviteCountMetric(t *testing.T) {
|
||
db := CreateTestDB(t)
|
||
|
||
combo := TaskCombination{
|
||
Name: "邀请人数测试任务",
|
||
Metric: MetricInviteCount,
|
||
Operator: OperatorGTE,
|
||
Threshold: 2,
|
||
Window: WindowLifetime,
|
||
RewardType: RewardTypeTitle,
|
||
}
|
||
taskID, _ := InsertTaskWithTierAndReward(t, db, combo)
|
||
|
||
// 模拟邀请进度
|
||
userID := int64(1004)
|
||
progress := &tcmodel.UserTaskProgress{
|
||
UserID: userID,
|
||
TaskID: taskID,
|
||
InviteCount: 3,
|
||
ClaimedTiers: datatypes.JSON("[]"),
|
||
}
|
||
db.Create(progress)
|
||
|
||
var p tcmodel.UserTaskProgress
|
||
db.Where("user_id = ? AND task_id = ?", userID, taskID).First(&p)
|
||
if p.InviteCount < 2 {
|
||
t.Error("邀请人数未达到阈值")
|
||
}
|
||
|
||
t.Logf("邀请人数指标测试通过: 当前邀请数=%d", p.InviteCount)
|
||
}
|
||
|
||
// TestAllRewardTypes 测试所有奖励类型的 payload 解析
|
||
func TestAllRewardTypes(t *testing.T) {
|
||
rewardTypes := []string{
|
||
RewardTypePoints,
|
||
RewardTypeCoupon,
|
||
RewardTypeItemCard,
|
||
RewardTypeTitle,
|
||
RewardTypeGameTicket,
|
||
}
|
||
|
||
for _, rt := range rewardTypes {
|
||
payload := generateRewardPayload(rt)
|
||
var data map[string]interface{}
|
||
if err := json.Unmarshal([]byte(payload), &data); err != nil {
|
||
t.Errorf("奖励类型 %s 的 payload 解析失败: %v", rt, err)
|
||
continue
|
||
}
|
||
|
||
switch rt {
|
||
case RewardTypePoints:
|
||
if _, ok := data["points"]; !ok {
|
||
t.Errorf("points 类型缺少 points 字段")
|
||
}
|
||
case RewardTypeCoupon:
|
||
if _, ok := data["coupon_id"]; !ok {
|
||
t.Errorf("coupon 类型缺少 coupon_id 字段")
|
||
}
|
||
case RewardTypeItemCard:
|
||
if _, ok := data["card_id"]; !ok {
|
||
t.Errorf("item_card 类型缺少 card_id 字段")
|
||
}
|
||
case RewardTypeTitle:
|
||
if _, ok := data["title_id"]; !ok {
|
||
t.Errorf("title 类型缺少 title_id 字段")
|
||
}
|
||
case RewardTypeGameTicket:
|
||
if _, ok := data["game_code"]; !ok {
|
||
t.Errorf("game_ticket 类型缺少 game_code 字段")
|
||
}
|
||
if _, ok := data["amount"]; !ok {
|
||
t.Errorf("game_ticket 类型缺少 amount 字段")
|
||
}
|
||
}
|
||
|
||
t.Logf("奖励类型 %s 验证通过: %s", rt, payload)
|
||
}
|
||
}
|
||
|
||
// TestTimeWindowDaily 测试每日时间窗口重置逻辑
|
||
func TestTimeWindowDaily(t *testing.T) {
|
||
db := CreateTestDB(t)
|
||
|
||
combo := TaskCombination{
|
||
Name: "每日重置测试任务",
|
||
Metric: MetricOrderCount,
|
||
Operator: OperatorGTE,
|
||
Threshold: 3,
|
||
Window: WindowDaily,
|
||
RewardType: RewardTypePoints,
|
||
}
|
||
taskID, _ := InsertTaskWithTierAndReward(t, db, combo)
|
||
|
||
userID := int64(1005)
|
||
|
||
// 模拟昨天的进度
|
||
yesterday := time.Now().Add(-25 * time.Hour)
|
||
progress := &tcmodel.UserTaskProgress{
|
||
UserID: userID,
|
||
TaskID: taskID,
|
||
OrderCount: 5,
|
||
ClaimedTiers: datatypes.JSON("[]"),
|
||
UpdatedAt: yesterday,
|
||
}
|
||
db.Create(progress)
|
||
|
||
// 检查日期判断逻辑
|
||
now := time.Now()
|
||
y1, m1, d1 := yesterday.Date()
|
||
y2, m2, d2 := now.Date()
|
||
|
||
shouldReset := !(y1 == y2 && m1 == m2 && d1 == d2)
|
||
if !shouldReset {
|
||
t.Error("每日重置逻辑有误:昨天的进度应该需要重置")
|
||
}
|
||
|
||
t.Logf("时间窗口测试通过: 昨天=%v, 今天=%v, 需要重置=%v", yesterday.Format("2006-01-02"), now.Format("2006-01-02"), shouldReset)
|
||
}
|
||
|
||
// TestOperatorComparison 测试操作符比较逻辑
|
||
func TestOperatorComparison(t *testing.T) {
|
||
testCases := []struct {
|
||
name string
|
||
operator string
|
||
threshold int64
|
||
value int64
|
||
expected bool
|
||
}{
|
||
{"GTE-达到阈值", OperatorGTE, 3, 3, true},
|
||
{"GTE-超过阈值", OperatorGTE, 3, 5, true},
|
||
{"GTE-未达阈值", OperatorGTE, 3, 2, false},
|
||
{"EQ-精确匹配", OperatorEQ, 3, 3, true},
|
||
{"EQ-超过不匹配", OperatorEQ, 3, 5, false},
|
||
{"EQ-未达不匹配", OperatorEQ, 3, 2, false},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
var result bool
|
||
switch tc.operator {
|
||
case OperatorGTE:
|
||
result = tc.value >= tc.threshold
|
||
case OperatorEQ:
|
||
result = tc.value == tc.threshold
|
||
}
|
||
|
||
if result != tc.expected {
|
||
t.Errorf("%s: 期望 %v, 实际 %v", tc.name, tc.expected, result)
|
||
} else {
|
||
t.Logf("%s: 通过", tc.name)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestIdempotency 测试幂等性(同一事件不重复处理)
|
||
func TestIdempotency(t *testing.T) {
|
||
db := CreateTestDB(t)
|
||
ctx := context.Background()
|
||
_ = ctx // 用于后续扩展
|
||
|
||
// 插入第一条事件日志
|
||
idk := "1001:1:1:order:100"
|
||
log1 := &tcmodel.TaskEventLog{
|
||
EventID: "evt_001",
|
||
SourceType: "order",
|
||
SourceID: 100,
|
||
UserID: 1001,
|
||
TaskID: 1,
|
||
TierID: 1,
|
||
IdempotencyKey: idk,
|
||
Status: "granted",
|
||
}
|
||
if err := db.Create(log1).Error; err != nil {
|
||
t.Fatalf("创建事件日志失败: %v", err)
|
||
}
|
||
|
||
// 尝试插入重复的幂等键(应该失败)
|
||
log2 := &tcmodel.TaskEventLog{
|
||
EventID: "evt_002",
|
||
SourceType: "order",
|
||
SourceID: 100,
|
||
UserID: 1001,
|
||
TaskID: 1,
|
||
TierID: 1,
|
||
IdempotencyKey: idk, // 相同的幂等键
|
||
Status: "granted",
|
||
}
|
||
err := db.Create(log2).Error
|
||
if err == nil {
|
||
t.Error("幂等性检查失败:重复的幂等键应该被拒绝")
|
||
} else {
|
||
t.Logf("幂等性测试通过: 重复记录被正确拒绝")
|
||
}
|
||
}
|
||
|
||
// TestClaimedTiersTracking 测试已领取档位追踪
|
||
func TestClaimedTiersTracking(t *testing.T) {
|
||
db := CreateTestDB(t)
|
||
|
||
userID := int64(1006)
|
||
taskID := int64(1)
|
||
|
||
// 初始化进度
|
||
progress := &tcmodel.UserTaskProgress{
|
||
UserID: userID,
|
||
TaskID: taskID,
|
||
ClaimedTiers: datatypes.JSON("[]"),
|
||
}
|
||
db.Create(progress)
|
||
|
||
// 模拟领取档位
|
||
claimedTiers := []int64{1, 2, 3}
|
||
b, _ := json.Marshal(claimedTiers)
|
||
db.Model(&tcmodel.UserTaskProgress{}).
|
||
Where("user_id = ? AND task_id = ?", userID, taskID).
|
||
Update("claimed_tiers", datatypes.JSON(b))
|
||
|
||
// 验证领取状态
|
||
var p tcmodel.UserTaskProgress
|
||
db.Where("user_id = ? AND task_id = ?", userID, taskID).First(&p)
|
||
|
||
var claimed []int64
|
||
json.Unmarshal([]byte(p.ClaimedTiers), &claimed)
|
||
|
||
if len(claimed) != 3 {
|
||
t.Errorf("已领取档位数量不正确: 期望 3, 实际 %d", len(claimed))
|
||
}
|
||
|
||
// 检查是否包含特定档位
|
||
tierToCheck := int64(2)
|
||
found := false
|
||
for _, id := range claimed {
|
||
if id == tierToCheck {
|
||
found = true
|
||
break
|
||
}
|
||
}
|
||
if !found {
|
||
t.Errorf("档位 %d 应该已被领取", tierToCheck)
|
||
}
|
||
|
||
t.Logf("已领取档位追踪测试通过: %v", claimed)
|
||
}
|