fix(database): 用户活跃统计 - 修复 PostgreSQL 冲突更新列歧义
This commit is contained in:
@@ -41,29 +41,29 @@ func (r *Repository) RecordUserActivity(userID string, event string, count int,
|
||||
switch event {
|
||||
case "login":
|
||||
activity.LoginCount = count
|
||||
updates["login_count"] = gorm.Expr("login_count + ?", count)
|
||||
updates["login_count"] = gorm.Expr("user_daily_activities.login_count + ?", count)
|
||||
case "task":
|
||||
activity.TaskCount = count
|
||||
updates["task_count"] = gorm.Expr("task_count + ?", count)
|
||||
updates["task_count"] = gorm.Expr("user_daily_activities.task_count + ?", count)
|
||||
case "agent_message":
|
||||
activity.AgentMessageCount = count
|
||||
updates["agent_message_count"] = gorm.Expr("agent_message_count + ?", count)
|
||||
updates["agent_message_count"] = gorm.Expr("user_daily_activities.agent_message_count + ?", count)
|
||||
case "canvas":
|
||||
activity.CanvasActive = true
|
||||
updates["canvas_active"] = true
|
||||
case "asset":
|
||||
activity.AssetCount = count
|
||||
updates["asset_count"] = gorm.Expr("asset_count + ?", count)
|
||||
updates["asset_count"] = gorm.Expr("user_daily_activities.asset_count + ?", count)
|
||||
case "resource":
|
||||
activity.ResourceCount = count
|
||||
updates["resource_count"] = gorm.Expr("resource_count + ?", count)
|
||||
updates["resource_count"] = gorm.Expr("user_daily_activities.resource_count + ?", count)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
if event != "login" {
|
||||
activity.FirstActiveAt = &now
|
||||
activity.LastActiveAt = &now
|
||||
updates["first_active_at"] = gorm.Expr("COALESCE(first_active_at, ?)", now)
|
||||
updates["first_active_at"] = gorm.Expr("COALESCE(user_daily_activities.first_active_at, ?)", now)
|
||||
updates["last_active_at"] = now
|
||||
}
|
||||
return r.db.Clauses(clause.OnConflict{
|
||||
|
||||
73
backend/internal/repository/analytics_test.go
Normal file
73
backend/internal/repository/analytics_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
type sqlCaptureLogger struct {
|
||||
statements []string
|
||||
}
|
||||
|
||||
func (l *sqlCaptureLogger) LogMode(logger.LogLevel) logger.Interface { return l }
|
||||
func (*sqlCaptureLogger) Info(context.Context, string, ...any) {}
|
||||
func (*sqlCaptureLogger) Warn(context.Context, string, ...any) {}
|
||||
func (*sqlCaptureLogger) Error(context.Context, string, ...any) {}
|
||||
|
||||
func (l *sqlCaptureLogger) Trace(_ context.Context, _ time.Time, query func() (string, int64), _ error) {
|
||||
statement, _ := query()
|
||||
l.statements = append(l.statements, statement)
|
||||
}
|
||||
|
||||
func TestRecordUserActivityQualifiesPostgresConflictColumns(t *testing.T) {
|
||||
capture := &sqlCaptureLogger{}
|
||||
db, err := gorm.Open(postgres.New(postgres.Config{
|
||||
DSN: "host=localhost user=test dbname=test sslmode=disable",
|
||||
PreferSimpleProtocol: true,
|
||||
}), &gorm.Config{
|
||||
DisableAutomaticPing: true,
|
||||
DryRun: true,
|
||||
Logger: capture,
|
||||
SkipDefaultTransaction: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("open dry-run postgres database: %v", err)
|
||||
}
|
||||
|
||||
repo := New(db)
|
||||
cases := []struct {
|
||||
event string
|
||||
column string
|
||||
}{
|
||||
{event: "login", column: "login_count"},
|
||||
{event: "task", column: "task_count"},
|
||||
{event: "agent_message", column: "agent_message_count"},
|
||||
{event: "asset", column: "asset_count"},
|
||||
{event: "resource", column: "resource_count"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.event, func(t *testing.T) {
|
||||
capture.statements = nil
|
||||
if err := repo.RecordUserActivity("user-1", tc.event, 1, time.Unix(1_700_000_000, 0)); err != nil {
|
||||
t.Fatalf("record activity: %v", err)
|
||||
}
|
||||
if len(capture.statements) == 0 {
|
||||
t.Fatal("expected generated SQL")
|
||||
}
|
||||
statement := capture.statements[len(capture.statements)-1]
|
||||
if !strings.Contains(statement, "user_daily_activities."+tc.column) {
|
||||
t.Fatalf("conflict update column is not target-qualified: %s", statement)
|
||||
}
|
||||
if tc.event != "login" && !strings.Contains(statement, "COALESCE(user_daily_activities.first_active_at") {
|
||||
t.Fatalf("first active column is not target-qualified: %s", statement)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user