package users import ( "context" "errors" "time" "github.com/google/uuid" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) type User struct { ID uuid.UUID `json:"id"` Email string `json:"email,omitempty"` Phone string `json:"phone,omitempty"` TelegramID *int64 `json:"telegram_id,omitempty"` Name string `json:"name"` Birthdate *time.Time `json:"birthdate,omitempty"` Gender string `json:"gender"` City string `json:"city"` Bio string `json:"bio"` PhotoURL string `json:"photo_url,omitempty"` IsVerified bool `json:"is_verified"` IsBlocked bool `json:"is_blocked"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` LastSeenAt *time.Time `json:"last_seen_at,omitempty"` } type Repo struct { pool *pgxpool.Pool } func NewRepo(pool *pgxpool.Pool) *Repo { return &Repo{pool: pool} } func (r *Repo) CreateWithPassword(ctx context.Context, u *User, passwordHash string) error { return r.pool.QueryRow(ctx, ` INSERT INTO users (email, phone, password_hash, name, birthdate, gender, city) VALUES (NULLIF($1, ''), NULLIF($2, ''), $3, $4, $5, NULLIF($6, ''), NULLIF($7, '')) RETURNING id, created_at, updated_at`, u.Email, u.Phone, passwordHash, u.Name, u.Birthdate, u.Gender, u.City, ).Scan(&u.ID, &u.CreatedAt, &u.UpdatedAt) } func (r *Repo) UpdateRaw(ctx context.Context, sql string, args ...interface{}) (int64, error) { tag, err := r.pool.Exec(ctx, sql, args...) if err != nil { return 0, err } return tag.RowsAffected(), nil } // CreateWithTelegramID — создать пользователя с привязкой к Telegram func (r *Repo) CreateWithTelegramID(ctx context.Context, u *User, tgID int64) error { return r.pool.QueryRow(ctx, ` INSERT INTO users (id, telegram_id, name, password_hash) VALUES ($1, $2, $3, '') ON CONFLICT (telegram_id) DO UPDATE SET name = EXCLUDED.name, updated_at = NOW() RETURNING id, created_at, updated_at`, u.ID, tgID, u.Name, ).Scan(&u.ID, &u.CreatedAt, &u.UpdatedAt) } func (r *Repo) GetByTelegramID(ctx context.Context, tgID int64) (*User, error) { u := &User{} var email, phone, gender, city, bio, photo *string var birth *time.Time var telegramID *int64 err := r.pool.QueryRow(ctx, ` SELECT id, email, phone, telegram_id, name, birthdate, gender, city, bio, photo_url, is_verified, is_blocked, created_at, updated_at, last_seen_at FROM users WHERE telegram_id=$1`, tgID, ).Scan(&u.ID, &email, &phone, &telegramID, &u.Name, &birth, &gender, &city, &bio, &photo, &u.IsVerified, &u.IsBlocked, &u.CreatedAt, &u.UpdatedAt, &u.LastSeenAt) if errors.Is(err, pgx.ErrNoRows) { return nil, nil } if err != nil { return nil, err } if email != nil { u.Email = *email } if phone != nil { u.Phone = *phone } if telegramID != nil { u.TelegramID = telegramID } if gender != nil { u.Gender = *gender } if city != nil { u.City = *city } if bio != nil { u.Bio = *bio } if photo != nil { u.PhotoURL = *photo } if birth != nil { u.Birthdate = birth } return u, nil } func (r *Repo) TouchLastSeen(ctx context.Context, id uuid.UUID) error { _, err := r.pool.Exec(ctx, `UPDATE users SET last_seen_at=NOW() WHERE id=$1`, id) return err } func (r *Repo) GetByID(ctx context.Context, id uuid.UUID) (*User, error) { u := &User{} var email, phone, gender, city, bio, photo *string var birth *time.Time err := r.pool.QueryRow(ctx, ` SELECT id, email, phone, name, birthdate, gender, city, bio, photo_url, is_verified, is_blocked, created_at, updated_at, last_seen_at FROM users WHERE id=$1`, id, ).Scan(&u.ID, &email, &phone, &u.Name, &birth, &gender, &city, &bio, &photo, &u.IsVerified, &u.IsBlocked, &u.CreatedAt, &u.UpdatedAt, &u.LastSeenAt) if errors.Is(err, pgx.ErrNoRows) { return nil, nil } if err != nil { return nil, err } if email != nil { u.Email = *email } if phone != nil { u.Phone = *phone } if gender != nil { u.Gender = *gender } if city != nil { u.City = *city } if bio != nil { u.Bio = *bio } if photo != nil { u.PhotoURL = *photo } if birth != nil { u.Birthdate = birth } return u, nil } func (r *Repo) GetByEmail(ctx context.Context, email string) (*User, string, error) { u := &User{} var hash string var em *string var birth *time.Time var gender, city, bio, photo *string err := r.pool.QueryRow(ctx, ` SELECT id, email, password_hash, name, birthdate, gender, city, bio, photo_url, is_verified, is_blocked, created_at, updated_at, last_seen_at FROM users WHERE email=$1`, email, ).Scan(&u.ID, &em, &hash, &u.Name, &birth, &gender, &city, &bio, &photo, &u.IsVerified, &u.IsBlocked, &u.CreatedAt, &u.UpdatedAt, &u.LastSeenAt) if errors.Is(err, pgx.ErrNoRows) { return nil, "", nil } if err != nil { return nil, "", err } if em != nil { u.Email = *em } if gender != nil { u.Gender = *gender } if city != nil { u.City = *city } if bio != nil { u.Bio = *bio } if photo != nil { u.PhotoURL = *photo } if birth != nil { u.Birthdate = birth } return u, hash, nil }