package store import ( "context" "fmt" "time" "github.com/google/uuid" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" "github.com/hhs/camtalk/internal/models" ) // UserScenarioRepository 用户自建情景仓储接口。 type UserScenarioRepository interface { Create(ctx context.Context, scenario *models.UserScenario) error FindByID(ctx context.Context, id string) (*models.UserScenario, error) FindByIDAndUserID(ctx context.Context, id, userID string) (*models.UserScenario, error) FindByUserID(ctx context.Context, userID string) ([]*models.UserScenario, error) Update(ctx context.Context, scenario *models.UserScenario) error Delete(ctx context.Context, id string) error CountByUserID(ctx context.Context, userID string) (int, error) } // PostgresUserScenarioRepo PostgreSQL 实现。 type PostgresUserScenarioRepo struct { pool *pgxpool.Pool } // NewPostgresUserScenarioRepo 创建 PostgreSQL 用户情景仓储。 func NewPostgresUserScenarioRepo(pool *pgxpool.Pool) UserScenarioRepository { return &PostgresUserScenarioRepo{pool: pool} } // Create 创建用户情景。 func (r *PostgresUserScenarioRepo) Create(ctx context.Context, scenario *models.UserScenario) error { query := ` INSERT INTO user_scenarios (id, user_id, name, icon, description, prompt, greeting, language, created_at, updated_at) VALUES ($1, $2, $3, $4, NULLIF($5, ''), $6, NULLIF($7, ''), $8, $9, $10) RETURNING id, created_at, updated_at ` now := time.Now() scenario.CreatedAt = now scenario.UpdatedAt = now if scenario.ID == "" { scenario.ID = uuid.New().String() } if scenario.Icon == "" { scenario.Icon = "✨" } if scenario.Language == "" { scenario.Language = "zh-CN" } err := r.pool.QueryRow(ctx, query, scenario.ID, scenario.UserID, scenario.Name, scenario.Icon, scenario.Description, scenario.Prompt, scenario.Greeting, scenario.Language, scenario.CreatedAt, scenario.UpdatedAt, ).Scan(&scenario.ID, &scenario.CreatedAt, &scenario.UpdatedAt) if err != nil { return fmt.Errorf("create user scenario: %w", err) } return nil } // FindByID 根据 ID 查找情景。 func (r *PostgresUserScenarioRepo) FindByID(ctx context.Context, id string) (*models.UserScenario, error) { query := ` SELECT id, user_id, name, icon, description, prompt, greeting, language, created_at, updated_at FROM user_scenarios WHERE id = $1 ` var scenario models.UserScenario err := r.pool.QueryRow(ctx, query, id).Scan( &scenario.ID, &scenario.UserID, &scenario.Name, &scenario.Icon, &scenario.Description, &scenario.Prompt, &scenario.Greeting, &scenario.Language, &scenario.CreatedAt, &scenario.UpdatedAt, ) if err == pgx.ErrNoRows { return nil, fmt.Errorf("user scenario not found: %s", id) } if err != nil { return nil, fmt.Errorf("find user scenario: %w", err) } return &scenario, nil } // FindByIDAndUserID 根据 ID 和用户 ID 查找情景(权限校验)。 func (r *PostgresUserScenarioRepo) FindByIDAndUserID(ctx context.Context, id, userID string) (*models.UserScenario, error) { query := ` SELECT id, user_id, name, icon, description, prompt, greeting, language, created_at, updated_at FROM user_scenarios WHERE id = $1 AND user_id = $2 ` var scenario models.UserScenario err := r.pool.QueryRow(ctx, query, id, userID).Scan( &scenario.ID, &scenario.UserID, &scenario.Name, &scenario.Icon, &scenario.Description, &scenario.Prompt, &scenario.Greeting, &scenario.Language, &scenario.CreatedAt, &scenario.UpdatedAt, ) if err == pgx.ErrNoRows { return nil, fmt.Errorf("user scenario not found or no permission") } if err != nil { return nil, fmt.Errorf("find user scenario: %w", err) } return &scenario, nil } // FindByUserID 查找用户的所有情景。 func (r *PostgresUserScenarioRepo) FindByUserID(ctx context.Context, userID string) ([]*models.UserScenario, error) { query := ` SELECT id, user_id, name, icon, description, prompt, greeting, language, created_at, updated_at FROM user_scenarios WHERE user_id = $1 ORDER BY created_at DESC ` rows, err := r.pool.Query(ctx, query, userID) if err != nil { return nil, fmt.Errorf("find user scenarios: %w", err) } defer rows.Close() var scenarios []*models.UserScenario for rows.Next() { var s models.UserScenario err := rows.Scan( &s.ID, &s.UserID, &s.Name, &s.Icon, &s.Description, &s.Prompt, &s.Greeting, &s.Language, &s.CreatedAt, &s.UpdatedAt, ) if err != nil { return nil, fmt.Errorf("scan user scenario: %w", err) } scenarios = append(scenarios, &s) } if err = rows.Err(); err != nil { return nil, fmt.Errorf("iterate user scenarios: %w", err) } return scenarios, nil } // Update 更新用户情景。 func (r *PostgresUserScenarioRepo) Update(ctx context.Context, scenario *models.UserScenario) error { query := ` UPDATE user_scenarios SET name = $1, icon = $2, description = $3, prompt = $4, greeting = $5, language = $6, updated_at = $7 WHERE id = $8 AND user_id = $9 RETURNING updated_at ` scenario.UpdatedAt = time.Now() err := r.pool.QueryRow(ctx, query, scenario.Name, scenario.Icon, scenario.Description, scenario.Prompt, scenario.Greeting, scenario.Language, scenario.UpdatedAt, scenario.ID, scenario.UserID, ).Scan(&scenario.UpdatedAt) if err == pgx.ErrNoRows { return fmt.Errorf("user scenario not found or no permission") } if err != nil { return fmt.Errorf("update user scenario: %w", err) } return nil } // Delete 删除用户情景。 func (r *PostgresUserScenarioRepo) Delete(ctx context.Context, id string) error { query := `DELETE FROM user_scenarios WHERE id = $1` result, err := r.pool.Exec(ctx, query, id) if err != nil { return fmt.Errorf("delete user scenario: %w", err) } if result.RowsAffected() == 0 { return fmt.Errorf("user scenario not found") } return nil } // CountByUserID 统计用户的情景数量。 func (r *PostgresUserScenarioRepo) CountByUserID(ctx context.Context, userID string) (int, error) { query := `SELECT COUNT(*) FROM user_scenarios WHERE user_id = $1` var count int err := r.pool.QueryRow(ctx, query, userID).Scan(&count) if err != nil { return 0, fmt.Errorf("count user scenarios: %w", err) } return count, nil }