You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
3.7 KiB
Go
138 lines
3.7 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
|
|
_ "github.com/lib/pq"
|
|
_ "github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
// Config holds database configuration
|
|
type Config struct {
|
|
Driver string
|
|
Host string
|
|
Port int
|
|
User string
|
|
Password string
|
|
Database string
|
|
SSLMode string
|
|
}
|
|
|
|
// Client wraps database connection
|
|
type Client struct {
|
|
db *sql.DB
|
|
config Config
|
|
}
|
|
|
|
// NewClient creates a new database client
|
|
func NewClient(config Config) (*Client, error) {
|
|
var dsn string
|
|
|
|
switch config.Driver {
|
|
case "sqlite3":
|
|
dsn = config.Database
|
|
case "postgres":
|
|
dsn = fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
|
|
config.Host, config.Port, config.User, config.Password, config.Database, config.SSLMode)
|
|
default:
|
|
return nil, fmt.Errorf("unsupported driver: %s", config.Driver)
|
|
}
|
|
|
|
db, err := sql.Open(config.Driver, dsn)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open database: %w", err)
|
|
}
|
|
|
|
// Test connection
|
|
if err := db.PingContext(context.Background()); err != nil {
|
|
return nil, fmt.Errorf("failed to ping database: %w", err)
|
|
}
|
|
|
|
return &Client{
|
|
db: db,
|
|
config: config,
|
|
}, nil
|
|
}
|
|
|
|
// Close closes the database connection
|
|
func (c *Client) Close() error {
|
|
if c.db != nil {
|
|
return c.db.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DB returns the underlying sql.DB instance
|
|
func (c *Client) DB() *sql.DB {
|
|
return c.db
|
|
}
|
|
|
|
// Health checks database connection health
|
|
func (c *Client) Health(ctx context.Context) error {
|
|
return c.db.PingContext(ctx)
|
|
}
|
|
|
|
// CreateTables creates the basic table structure
|
|
func (c *Client) CreateTables(ctx context.Context) error {
|
|
queries := []string{
|
|
`CREATE TABLE IF NOT EXISTS questions (
|
|
id TEXT PRIMARY KEY,
|
|
theme TEXT NOT NULL,
|
|
text TEXT NOT NULL,
|
|
answer TEXT NOT NULL,
|
|
hint TEXT,
|
|
difficulty TEXT DEFAULT 'medium' CHECK (difficulty IN ('easy', 'medium', 'hard')),
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
)`,
|
|
`CREATE INDEX IF NOT EXISTS idx_questions_theme ON questions(theme)`,
|
|
`CREATE INDEX IF NOT EXISTS idx_questions_difficulty ON questions(difficulty)`,
|
|
`CREATE INDEX IF NOT EXISTS idx_questions_active ON questions(is_active)`,
|
|
|
|
`CREATE TABLE IF NOT EXISTS game_sessions (
|
|
id TEXT PRIMARY KEY,
|
|
player_name TEXT NOT NULL,
|
|
user_id TEXT,
|
|
total_score INTEGER DEFAULT 0,
|
|
questions_asked INTEGER DEFAULT 0,
|
|
questions_correct INTEGER DEFAULT 0,
|
|
hints_used INTEGER DEFAULT 0,
|
|
start_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
end_time TIMESTAMP,
|
|
status TEXT DEFAULT 'active' CHECK (status IN ('active', 'completed', 'timeout', 'abandoned')),
|
|
current_question_id TEXT,
|
|
current_attempts INTEGER DEFAULT 0,
|
|
session_data TEXT
|
|
)`,
|
|
`CREATE INDEX IF NOT EXISTS idx_sessions_user ON game_sessions(user_id)`,
|
|
`CREATE INDEX IF NOT EXISTS idx_sessions_status ON game_sessions(status)`,
|
|
`CREATE INDEX IF NOT EXISTS idx_sessions_score ON game_sessions(total_score)`,
|
|
|
|
`CREATE TABLE IF NOT EXISTS question_attempts (
|
|
id TEXT PRIMARY KEY,
|
|
session_id TEXT NOT NULL,
|
|
question_id TEXT NOT NULL,
|
|
attempt_number INTEGER NOT NULL,
|
|
submitted_answer TEXT NOT NULL,
|
|
is_correct BOOLEAN NOT NULL,
|
|
used_hint BOOLEAN DEFAULT FALSE,
|
|
points_awarded INTEGER DEFAULT 0,
|
|
submitted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
time_taken_ms INTEGER DEFAULT 0,
|
|
metadata TEXT
|
|
)`,
|
|
`CREATE INDEX IF NOT EXISTS idx_attempts_session ON question_attempts(session_id)`,
|
|
`CREATE INDEX IF NOT EXISTS idx_attempts_question ON question_attempts(question_id)`,
|
|
}
|
|
|
|
for _, query := range queries {
|
|
if _, err := c.db.ExecContext(ctx, query); err != nil {
|
|
return fmt.Errorf("failed to create table: %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
} |