From 36827edfeac4d0924d6037b18f47672f30bf0ac9 Mon Sep 17 00:00:00 2001 From: hhs <386998068@qq.com> Date: Sun, 14 Jun 2026 16:55:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=20PostgreSQL=20UserR?= =?UTF-8?q?epository?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/store/user_pg.go | 101 ++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 backend/internal/store/user_pg.go diff --git a/backend/internal/store/user_pg.go b/backend/internal/store/user_pg.go new file mode 100644 index 0000000..fec0631 --- /dev/null +++ b/backend/internal/store/user_pg.go @@ -0,0 +1,101 @@ +package store + +import ( + "context" + "errors" + "time" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" +) + +// PgUserRepository 基于 PostgreSQL 的 UserRepository 实现。 +type PgUserRepository struct { + pool *pgxpool.Pool +} + +// NewPgUserRepository 创建 PgUserRepository。 +func NewPgUserRepository(pool *pgxpool.Pool) *PgUserRepository { + return &PgUserRepository{pool: pool} +} + +func (r *PgUserRepository) Create(ctx context.Context, username, passwordHash string) (string, error) { + var id string + err := r.pool.QueryRow(ctx, + `INSERT INTO users (username, password_hash) VALUES ($1, $2) RETURNING id`, + username, passwordHash, + ).Scan(&id) + if err != nil { + return "", err + } + return id, nil +} + +func (r *PgUserRepository) FindByUsername(ctx context.Context, username string) (*User, error) { + var u User + err := r.pool.QueryRow(ctx, + `SELECT id, username, password_hash, created_at, updated_at FROM users WHERE username = $1`, + username, + ).Scan(&u.ID, &u.Username, &u.PasswordHash, &u.CreatedAt, &u.UpdatedAt) + if errors.Is(err, pgx.ErrNoRows) { + return nil, ErrUserNotFound + } + if err != nil { + return nil, err + } + return &u, nil +} + +func (r *PgUserRepository) FindByID(ctx context.Context, id string) (*User, error) { + var u User + err := r.pool.QueryRow(ctx, + `SELECT id, username, password_hash, created_at, updated_at FROM users WHERE id = $1`, + id, + ).Scan(&u.ID, &u.Username, &u.PasswordHash, &u.CreatedAt, &u.UpdatedAt) + if errors.Is(err, pgx.ErrNoRows) { + return nil, ErrUserNotFound + } + if err != nil { + return nil, err + } + return &u, nil +} + +func (r *PgUserRepository) SaveRefreshToken(ctx context.Context, userID, tokenHash string, expiresAt time.Time) error { + _, err := r.pool.Exec(ctx, + `INSERT INTO refresh_tokens (user_id, token_hash, expires_at) VALUES ($1, $2, $3)`, + userID, tokenHash, expiresAt, + ) + return err +} + +func (r *PgUserRepository) FindRefreshToken(ctx context.Context, tokenHash string) (string, error) { + var userID string + err := r.pool.QueryRow(ctx, + `SELECT user_id FROM refresh_tokens WHERE token_hash = $1 AND expires_at > NOW()`, + tokenHash, + ).Scan(&userID) + if errors.Is(err, pgx.ErrNoRows) { + return "", ErrRefreshTokenNotFound + } + if err != nil { + return "", err + } + return userID, nil +} + +func (r *PgUserRepository) DeleteRefreshToken(ctx context.Context, tokenHash string) error { + _, err := r.pool.Exec(ctx, + `DELETE FROM refresh_tokens WHERE token_hash = $1`, + tokenHash, + ) + return err +} + +func (r *PgUserRepository) DeleteUserRefreshTokens(ctx context.Context, userID string) error { + _, err := r.pool.Exec(ctx, + `DELETE FROM refresh_tokens WHERE user_id = $1`, + userID, + ) + return err +}