Files
CamTalk/backend/internal/store/user_scenario_repository.go

239 lines
6.2 KiB
Go
Raw Normal View History

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
}