package store import ( "context" "errors" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" "github.com/hhs/camtalk/internal/trace" ) // PgSessionRepository 基于 PostgreSQL 的 SessionRepository 实现。 type PgSessionRepository struct { pool *pgxpool.Pool } // NewPgSessionRepository 创建 PgSessionRepository。 func NewPgSessionRepository(pool *pgxpool.Pool) *PgSessionRepository { return &PgSessionRepository{pool: pool} } func (r *PgSessionRepository) Save(ctx context.Context, s SessionRecord) error { log := trace.FromContext(ctx) _, err := r.pool.Exec(ctx, `INSERT INTO sessions (id, user_id, title, config, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (id) DO UPDATE SET title = EXCLUDED.title, config = EXCLUDED.config, updated_at = EXCLUDED.updated_at`, s.ID, s.UserID, s.Title, s.Config, s.CreatedAt, s.UpdatedAt, ) if err != nil { log.Errorw("save session failed", "session_id", s.ID, "error", err) return err } log.Debugw("session saved", "session_id", s.ID, "user_id", s.UserID) return nil } func (r *PgSessionRepository) FindByID(ctx context.Context, id string) (*SessionRecord, error) { log := trace.FromContext(ctx) var s SessionRecord err := r.pool.QueryRow(ctx, `SELECT id, user_id, title, config, created_at, updated_at FROM sessions WHERE id = $1`, id, ).Scan(&s.ID, &s.UserID, &s.Title, &s.Config, &s.CreatedAt, &s.UpdatedAt) if errors.Is(err, pgx.ErrNoRows) { return nil, ErrSessionNotFound } if err != nil { log.Errorw("find session failed", "session_id", id, "error", err) return nil, err } log.Debugw("session found", "session_id", id) return &s, nil } func (r *PgSessionRepository) FindByUser(ctx context.Context, userID string, page, size int) ([]SessionRecord, int, error) { log := trace.FromContext(ctx) if page <= 0 { page = 1 } if size <= 0 { size = 20 } offset := (page - 1) * size // 查询总数 var total int if err := r.pool.QueryRow(ctx, `SELECT COUNT(*) FROM sessions WHERE user_id = $1`, userID, ).Scan(&total); err != nil { log.Errorw("count user sessions failed", "user_id", userID, "error", err) return nil, 0, err } // 查询列表 rows, err := r.pool.Query(ctx, `SELECT id, user_id, title, config, created_at, updated_at FROM sessions WHERE user_id = $1 ORDER BY updated_at DESC LIMIT $2 OFFSET $3`, userID, size, offset, ) if err != nil { log.Errorw("find user sessions failed", "user_id", userID, "error", err) return nil, 0, err } defer rows.Close() var list []SessionRecord for rows.Next() { var s SessionRecord if err := rows.Scan(&s.ID, &s.UserID, &s.Title, &s.Config, &s.CreatedAt, &s.UpdatedAt); err != nil { log.Errorw("scan session row failed", "user_id", userID, "error", err) return nil, 0, err } list = append(list, s) } if err := rows.Err(); err != nil { log.Errorw("iterate session rows failed", "user_id", userID, "error", err) return nil, 0, err } log.Debugw("user sessions found", "user_id", userID, "count", len(list), "total", total) return list, total, nil } func (r *PgSessionRepository) UpdateTitle(ctx context.Context, id string, title string) error { log := trace.FromContext(ctx) tag, err := r.pool.Exec(ctx, `UPDATE sessions SET title = $2, updated_at = NOW() WHERE id = $1`, id, title, ) if err != nil { log.Errorw("update session title failed", "session_id", id, "error", err) return err } if tag.RowsAffected() == 0 { return ErrSessionNotFound } log.Debugw("session title updated", "session_id", id) return nil } func (r *PgSessionRepository) UpdateConfig(ctx context.Context, id string, configJSON []byte) error { log := trace.FromContext(ctx) tag, err := r.pool.Exec(ctx, `UPDATE sessions SET config = $2, updated_at = NOW() WHERE id = $1`, id, configJSON, ) if err != nil { log.Errorw("update session config failed", "session_id", id, "error", err) return err } if tag.RowsAffected() == 0 { return ErrSessionNotFound } log.Debugw("session config updated", "session_id", id) return nil } func (r *PgSessionRepository) Touch(ctx context.Context, id string) error { log := trace.FromContext(ctx) tag, err := r.pool.Exec(ctx, `UPDATE sessions SET updated_at = NOW() WHERE id = $1`, id, ) if err != nil { log.Errorw("touch session failed", "session_id", id, "error", err) return err } if tag.RowsAffected() == 0 { return ErrSessionNotFound } log.Debugw("session touched", "session_id", id) return nil } func (r *PgSessionRepository) Delete(ctx context.Context, id string) error { log := trace.FromContext(ctx) tag, err := r.pool.Exec(ctx, `DELETE FROM sessions WHERE id = $1`, id, ) if err != nil { log.Errorw("delete session failed", "session_id", id, "error", err) return err } if tag.RowsAffected() == 0 { return ErrSessionNotFound } log.Debugw("session deleted", "session_id", id) return nil }