white-zone-bot/repositories/sqlite/user_repository.go
2025-05-24 09:16:47 +06:00

143 lines
3.8 KiB
Go

package sqlite_repositories
import (
"context"
"database/sql"
"errors"
"fmt"
"strings"
"github.com/theorift/white-zone-bot/database"
"github.com/theorift/white-zone-bot/models"
"github.com/theorift/white-zone-bot/repositories"
)
type SQLiteUserRepository struct {
ctx context.Context
db *sql.DB // Main database connection
executor database.DBTX // Current executor (db or tx)
}
func NewUserRepository(ctx context.Context, db *sql.DB) *SQLiteUserRepository {
return &SQLiteUserRepository{
ctx: ctx,
db: db,
executor: db, // Use db as default executor
}
}
func (r *SQLiteUserRepository) queries() *database.Queries {
return database.New(r.executor)
}
// Core operations
func (r *SQLiteUserRepository) GetUser(id int64) (models.User, error) {
dbUser, err := r.queries().GetUser(r.ctx, id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return models.User{ID: 0}, repositories.ErrRecordNotFound
}
return models.User{ID: 0}, fmt.Errorf("failed to get user: %w", err)
}
return models.User{
ID: dbUser.ID,
Points: dbUser.Points,
ReferrerID: dbUser.ReferrerID.Int64,
}, nil
}
func (r *SQLiteUserRepository) CreateUser(id int64, referrerID int64) error {
params := database.CreateUserParams{
ID: id,
ReferrerID: sql.NullInt64{Int64: referrerID, Valid: referrerID != 0},
}
err := r.queries().CreateUser(r.ctx, params)
if err != nil {
if strings.Contains(err.Error(), "UNIQUE constraint failed") {
return repositories.ErrDuplicateEntry
}
if strings.Contains(err.Error(), "FOREIGN KEY constraint failed") {
return repositories.ErrForeignKeyViolation
}
return fmt.Errorf("failed to create user: %w", err)
}
return nil
}
func (r *SQLiteUserRepository) UpsertReferrer(id int64, referrerID int64) error {
params := database.UpsertReferrerParams{
ID: id,
ReferrerID: sql.NullInt64{Int64: referrerID, Valid: referrerID != 0},
}
err := r.queries().UpsertReferrer(r.ctx, params)
if err != nil {
return fmt.Errorf("failed to upsert referrer: %w", err)
}
return nil
}
// Points operations
func (r *SQLiteUserRepository) AddPointsToUser(id int64, points int64) error {
params := database.AddPointsToUserParams{
ID: id,
Points: points,
}
if err := r.queries().AddPointsToUser(r.ctx, params); err != nil {
return fmt.Errorf("failed to add points: %w", err)
}
return nil
}
func (r *SQLiteUserRepository) ReducePointsFromUser(id int64, points int64) (int64, error) {
params := database.ReducePointsFromUserParams{
ID: id,
Points: points,
}
newPoints, err := r.queries().ReducePointsFromUser(r.ctx, params)
if err != nil {
if strings.Contains(err.Error(), "CHECK constraint failed: points >= 0") {
return 0, repositories.ErrInsufficientUserPoints
}
return 0, fmt.Errorf("failed to reduce points: %w", err)
}
return newPoints, nil
}
// Transaction management
func (r *SQLiteUserRepository) BeginTx() (*sql.Tx, error) {
tx, err := r.db.BeginTx(r.ctx, &sql.TxOptions{
Isolation: sql.LevelSerializable,
})
if err != nil {
return nil, fmt.Errorf("failed to begin transaction: %w", err)
}
return tx, nil
}
func (r *SQLiteUserRepository) WithTx(tx *sql.Tx) repositories.UserRepository {
return &SQLiteUserRepository{
ctx: r.ctx,
db: r.db, // Retain original DB connection
executor: tx, // Use transaction for queries
}
}
func (r *SQLiteUserRepository) Commit(tx *sql.Tx) error {
if err := tx.Commit(); err != nil {
return fmt.Errorf("failed to commit transaction: %w", err)
}
return nil
}
func (r *SQLiteUserRepository) Rollback(tx *sql.Tx) error {
if err := tx.Rollback(); err != nil && !errors.Is(err, sql.ErrTxDone) {
return fmt.Errorf("failed to rollback transaction: %w", err)
}
return nil
}