feat: 实现 PostgreSQL UserRepository
This commit is contained in:
101
backend/internal/store/user_pg.go
Normal file
101
backend/internal/store/user_pg.go
Normal file
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user