diff --git a/internal/service/task_center/invite_logic_test.go b/internal/service/task_center/invite_logic_test.go index 5113db8..b0921af 100755 --- a/internal/service/task_center/invite_logic_test.go +++ b/internal/service/task_center/invite_logic_test.go @@ -23,6 +23,7 @@ func TestInviteLogicSymmetry(t *testing.T) { status INTEGER NOT NULL DEFAULT 1, source_type INTEGER NOT NULL DEFAULT 0, total_amount INTEGER NOT NULL DEFAULT 0, + actual_amount INTEGER NOT NULL DEFAULT 0, remark TEXT, deleted_at DATETIME );`) @@ -49,7 +50,7 @@ func TestInviteLogicSymmetry(t *testing.T) { // 只有 101 在活动 77 中下过单并开奖 db.Exec("INSERT INTO activity_issues (id, activity_id) VALUES (1, 77)") db.Exec("INSERT INTO activities (id, price_draw) VALUES (77, 100)") - db.Exec("INSERT INTO orders (id, user_id, status, total_amount, source_type) VALUES (10, 101, 2, 100, 0)") + db.Exec("INSERT INTO orders (id, user_id, status, total_amount, actual_amount, source_type) VALUES (10, 101, 2, 100, 100, 0)") db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (10, 1)") // === 场景 1:全局任务 (ActivityID = 0) === diff --git a/internal/service/task_center/service.go b/internal/service/task_center/service.go index 24b33cf..320981a 100755 --- a/internal/service/task_center/service.go +++ b/internal/service/task_center/service.go @@ -186,11 +186,11 @@ type TaskRewardItem struct { } type orderMetricRow struct { - OrderID int64 - ActivityID int64 - DrawCount int64 - TicketPrice int64 - TotalAmount int64 + OrderID int64 + ActivityID int64 + DrawCount int64 + TicketPrice int64 + ActualAmount int64 } var allowedWindows = map[string]struct{}{ @@ -228,12 +228,12 @@ func tierFingerprint(metric string, threshold int64, activityID int64, window st func (s *service) fetchOrderMetricRows(ctx context.Context, userID int64, activityIDs []int64, start, end *time.Time) ([]orderMetricRow, error) { query := s.repo.GetDbR().WithContext(ctx).Table(model.TableNameOrders). - Select("orders.id AS order_id, activity_issues.activity_id AS activity_id, COUNT(activity_draw_logs.id) AS draw_count, COALESCE(activities.price_draw, 0) AS ticket_price, orders.total_amount"). + Select("orders.id AS order_id, activity_issues.activity_id AS activity_id, COUNT(activity_draw_logs.id) AS draw_count, COALESCE(activities.price_draw, 0) AS ticket_price, orders.actual_amount"). Joins("JOIN activity_draw_logs ON activity_draw_logs.order_id = orders.id"). Joins("JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id"). Joins("LEFT JOIN activities ON activities.id = activity_issues.activity_id"). Where("orders.user_id = ? AND orders.status = 2 AND orders.source_type != 1", userID). - Group("orders.id, activity_issues.activity_id, activities.price_draw, orders.total_amount") + Group("orders.id, activity_issues.activity_id, activities.price_draw, orders.actual_amount") if len(activityIDs) > 0 { query = query.Where("activity_issues.activity_id IN ?", activityIDs) @@ -253,18 +253,7 @@ func (s *service) fetchOrderMetricRows(ctx context.Context, userID int64, activi } func (s *service) calculateEffectiveAmount(row orderMetricRow) int64 { - if row.TicketPrice > 0 && row.DrawCount > 0 { - return row.TicketPrice * row.DrawCount - } - if row.TotalAmount > 0 { - if s.logger != nil && row.TicketPrice == 0 { - s.logger.Warn("task center: missing ticket price snapshot, fallback to order amount", - zap.Int64("order_id", row.OrderID), - zap.Int64("activity_id", row.ActivityID)) - } - return row.TotalAmount - } - return 0 + return row.ActualAmount } func (s *service) aggregateOrderMetrics(rows []orderMetricRow, perActivity bool) (count int64, amount int64) { diff --git a/internal/service/task_center/service_test.go b/internal/service/task_center/service_test.go index 21f4521..cc6d174 100644 --- a/internal/service/task_center/service_test.go +++ b/internal/service/task_center/service_test.go @@ -130,17 +130,17 @@ func TestGetUserProgress_TimeWindow_Integration(t *testing.T) { // 插入三笔订单与邀请,处于不同时间段 o1Time := now.Format(time.DateTime) - db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, created_at) VALUES (101, ?, 2, 0, 100, ?)", userID, o1Time) + db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, actual_amount, created_at) VALUES (101, ?, 2, 0, 100, 100, ?)", userID, o1Time) db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (101, 1)") db.Exec("INSERT INTO user_invites (inviter_id, invitee_id, created_at) VALUES (?, 901, ?)", userID, o1Time) o2Time := now.AddDate(0, -2, 0).Format(time.DateTime) - db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, created_at) VALUES (102, ?, 2, 0, 100, ?)", userID, o2Time) + db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, actual_amount, created_at) VALUES (102, ?, 2, 0, 100, 100, ?)", userID, o2Time) db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (102, 1)") db.Exec("INSERT INTO user_invites (inviter_id, invitee_id, created_at) VALUES (?, 902, ?)", userID, o2Time) o3Time := now.AddDate(-1, 0, 0).Format(time.DateTime) - db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, created_at) VALUES (103, ?, 2, 0, 100, ?)", userID, o3Time) + db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, actual_amount, created_at) VALUES (103, ?, 2, 0, 100, 100, ?)", userID, o3Time) db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (103, 1)") db.Exec("INSERT INTO user_invites (inviter_id, invitee_id, created_at) VALUES (?, 903, ?)", userID, o3Time) @@ -260,7 +260,7 @@ func TestUpsertTaskRewards_AllowsMultipleRewardsSameType(t *testing.T) { } } -func TestGetUserProgress_UsesEffectiveAmount(t *testing.T) { +func TestGetUserProgress_UsesActualAmount(t *testing.T) { repo, err := mysql.NewSQLiteRepoForTest() if err != nil { t.Fatalf("创建 repo 失败: %v", err) @@ -313,13 +313,13 @@ func TestGetUserProgress_UsesEffectiveAmount(t *testing.T) { now := time.Now() inside := now.Format(time.DateTime) - // 次卡订单:total_amount=0,但 price_draw>0, draw_count=2 - db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, created_at) VALUES (401, ?, 2, 0, 0, ?)", userID, inside) + // 次卡订单:actual_amount=0,按纯实付口径不计入消费金额 + db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, actual_amount, created_at) VALUES (401, ?, 2, 0, 0, 0, ?)", userID, inside) db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (401, 301)") db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (401, 301)") - // 现金订单:price_draw=0,需回退 total_amount - db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, created_at) VALUES (402, ?, 2, 0, 1500, ?)", userID, inside) + // 现金订单:按 actual_amount 统计 + db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, actual_amount, created_at) VALUES (402, ?, 2, 0, 1500, 1500, ?)", userID, inside) db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (402, 302)") progress, err := svc.GetUserProgress(context.Background(), userID, task.ID) @@ -327,8 +327,8 @@ func TestGetUserProgress_UsesEffectiveAmount(t *testing.T) { t.Fatalf("获取进度失败: %v", err) } - if progress.OrderAmount != 3500 { - t.Fatalf("订单金额统计错误,期望 3500 实际 %d", progress.OrderAmount) + if progress.OrderAmount != 1500 { + t.Fatalf("订单金额统计错误,期望 1500 实际 %d", progress.OrderAmount) } if progress.OrderCount != 2 { t.Fatalf("订单数量统计错误,期望 2 实际 %d", progress.OrderCount) @@ -337,8 +337,8 @@ func TestGetUserProgress_UsesEffectiveAmount(t *testing.T) { if !ok { t.Fatalf("未找到档位进度") } - if tierProgress.OrderAmount != 2000 { - t.Fatalf("档位金额错误,期望 2000 实际 %d", tierProgress.OrderAmount) + if tierProgress.OrderAmount != 0 { + t.Fatalf("档位金额错误,期望 0 实际 %d", tierProgress.OrderAmount) } if tierProgress.OrderCount != 1 { t.Fatalf("档位订单数错误,期望 1 实际 %d", tierProgress.OrderCount) @@ -388,10 +388,10 @@ func TestTimeWindow_ActivityPeriod(t *testing.T) { inside := start.Add(24 * time.Hour).Format(time.DateTime) outside := end.Add(24 * time.Hour).Format(time.DateTime) - db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, created_at) VALUES (701, ?, 2, 0, 0, ?)", userID, inside) + db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, actual_amount, created_at) VALUES (701, ?, 2, 0, 0, 0, ?)", userID, inside) db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (701, 601)") - db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, created_at) VALUES (702, ?, 2, 0, 0, ?)", userID, outside) + db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, actual_amount, created_at) VALUES (702, ?, 2, 0, 0, 0, ?)", userID, outside) db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (702, 601)") progress, err := svc.GetUserProgress(context.Background(), userID, task.ID) @@ -569,14 +569,14 @@ func TestLifetimeWindow_RespectsTaskStartTime(t *testing.T) { // 插入历史订单(任务开始之前) historicalOrder := taskStart.Add(-10 * 24 * time.Hour).Format(time.DateTime) for i := int64(101); i <= 105; i++ { - db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, created_at) VALUES (?, ?, 2, 0, 100, ?)", i, userID, historicalOrder) + db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, actual_amount, created_at) VALUES (?, ?, 2, 0, 100, 100, ?)", i, userID, historicalOrder) db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (?, 1)", i) } // 插入新订单(任务开始之后) recentOrder := now.Add(-1 * 24 * time.Hour).Format(time.DateTime) for i := int64(201); i <= 202; i++ { - db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, created_at) VALUES (?, ?, 2, 0, 100, ?)", i, userID, recentOrder) + db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, actual_amount, created_at) VALUES (?, ?, 2, 0, 100, 100, ?)", i, userID, recentOrder) db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (?, 1)", i) } @@ -650,12 +650,12 @@ func TestEmptyWindow_RespectsTaskStartTime(t *testing.T) { // 历史订单(任务开始前) oldTime := taskStart.Add(-24 * time.Hour).Format(time.DateTime) - db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, created_at) VALUES (301, ?, 2, 0, 100, ?)", userID, oldTime) + db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, actual_amount, created_at) VALUES (301, ?, 2, 0, 100, 100, ?)", userID, oldTime) db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (301, 1)") // 新订单(任务开始后) newTime := now.Add(-1 * time.Hour).Format(time.DateTime) - db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, created_at) VALUES (302, ?, 2, 0, 100, ?)", userID, newTime) + db.Exec("INSERT INTO orders (id, user_id, status, source_type, total_amount, actual_amount, created_at) VALUES (302, ?, 2, 0, 100, 100, ?)", userID, newTime) db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (302, 1)") progress, err := svc.GetUserProgress(context.Background(), userID, task.ID) diff --git a/internal/service/task_center/task_center_test.go b/internal/service/task_center/task_center_test.go index bd20679..ccf685b 100755 --- a/internal/service/task_center/task_center_test.go +++ b/internal/service/task_center/task_center_test.go @@ -378,12 +378,12 @@ func TestOrderCountMetric(t *testing.T) { t.Logf("订单数量指标测试通过: 当前订单数=%d", p.OrderCount) } -// TestOrderAmountMetric 测试消费金额指标 +// TestOrderAmountMetric 测试实付金额指标 func TestOrderAmountMetric(t *testing.T) { db := CreateTestDB(t) combo := TaskCombination{ - Name: "消费金额测试任务", + Name: "实付金额测试任务", Metric: MetricOrderAmount, Operator: OperatorGTE, Threshold: 10000, // 100元 @@ -411,10 +411,10 @@ func TestOrderAmountMetric(t *testing.T) { var p tcmodel.UserTaskProgress db.Where("user_id = ? AND task_id = ?", userID, taskID).First(&p) if p.OrderAmount < 10000 { - t.Error("消费金额未达到阈值") + t.Error("实付金额未达到阈值") } - t.Logf("消费金额指标测试通过: 当前消费=%d分", p.OrderAmount) + t.Logf("实付金额指标测试通过: 当前实付=%d分", p.OrderAmount) } // TestInviteCountMetric 测试邀请人数指标 @@ -747,25 +747,25 @@ func TestGetUserProgress_ActivityFilter_Integration(t *testing.T) { db.Exec("INSERT INTO activity_issues (id, activity_id) VALUES (20, 200)") // 订单 A: 匹配活动 100 - db.Exec("INSERT INTO orders (id, user_id, status, total_amount, source_type, remark) VALUES (1, ?, 2, 100, 0, ?)", userID, "activity:100|count:1") + db.Exec("INSERT INTO orders (id, user_id, status, total_amount, actual_amount, source_type, remark) VALUES (1, ?, 2, 100, 100, 0, ?)", userID, "activity:100|count:1") db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (1, 10)") // 订单 B: 匹配活动 200 (不应被统计,因为任务关联的是 100) - db.Exec("INSERT INTO orders (id, user_id, status, total_amount, source_type, remark) VALUES (2, ?, 2, 200, 0, ?)", userID, "activity:200|count:1") + db.Exec("INSERT INTO orders (id, user_id, status, total_amount, actual_amount, source_type, remark) VALUES (2, ?, 2, 200, 200, 0, ?)", userID, "activity:200|count:1") db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (2, 20)") // 订单 C: 普通订单 (不应被统计,因为没有关联活动 100) - db.Exec("INSERT INTO orders (id, user_id, status, total_amount, source_type, remark) VALUES (3, ?, 2, 300, 0, ?)", userID, "normal_order") + db.Exec("INSERT INTO orders (id, user_id, status, total_amount, actual_amount, source_type, remark) VALUES (3, ?, 2, 300, 300, 0, ?)", userID, "normal_order") // 订单 D: 匹配活动 100 但未支付 (不应被统计) - db.Exec("INSERT INTO orders (id, user_id, status, total_amount, source_type, remark) VALUES (4, ?, 1, 100, 0, ?)", userID, "activity:100|count:1") + db.Exec("INSERT INTO orders (id, user_id, status, total_amount, actual_amount, source_type, remark) VALUES (4, ?, 1, 100, 100, 0, ?)", userID, "activity:100|count:1") // 3. 插入邀请记录 db.Exec("INSERT INTO user_invites (inviter_id, invitee_id) VALUES (?, 1001)", userID) db.Exec("INSERT INTO user_invites (inviter_id, invitee_id) VALUES (?, 1002)", userID) // 4. 让其中一个被邀请人(1001)在活动 100 中产生有效订单(使其成为"有效邀请”) - db.Exec("INSERT INTO orders (id, user_id, status, total_amount, source_type, remark) VALUES (10, 1001, 2, 50, 0, ?)", "activity:100|count:1") + db.Exec("INSERT INTO orders (id, user_id, status, total_amount, actual_amount, source_type, remark) VALUES (10, 1001, 2, 50, 50, 0, ?)", "activity:100|count:1") db.Exec("INSERT INTO activity_draw_logs (order_id, issue_id) VALUES (10, 10)") // 5. 调用 GetUserProgress