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.
118 lines
2.9 KiB
Go
118 lines
2.9 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
// LogEntry represents a single adventure log message.
|
|
type LogEntry struct {
|
|
ID int64 `json:"id"`
|
|
HeroID int64 `json:"heroId"`
|
|
Message string `json:"message"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
// LogStore handles adventure log CRUD operations against PostgreSQL.
|
|
type LogStore struct {
|
|
pool *pgxpool.Pool
|
|
}
|
|
|
|
// NewLogStore creates a new LogStore backed by the given connection pool.
|
|
func NewLogStore(pool *pgxpool.Pool) *LogStore {
|
|
return &LogStore{pool: pool}
|
|
}
|
|
|
|
// Add inserts a new adventure log entry for the given hero.
|
|
func (s *LogStore) Add(ctx context.Context, heroID int64, message string) error {
|
|
_, err := s.pool.Exec(ctx,
|
|
`INSERT INTO adventure_log (hero_id, message) VALUES ($1, $2)`,
|
|
heroID, message,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("add log entry: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetSince returns log entries for a hero created after the given timestamp,
|
|
// ordered oldest-first (chronological). Used to build offline reports from
|
|
// real adventure log entries written by the offline simulator.
|
|
func (s *LogStore) GetSince(ctx context.Context, heroID int64, since time.Time, limit int) ([]LogEntry, error) {
|
|
if limit <= 0 {
|
|
limit = 200
|
|
}
|
|
if limit > 500 {
|
|
limit = 500
|
|
}
|
|
|
|
rows, err := s.pool.Query(ctx, `
|
|
SELECT id, hero_id, message, created_at
|
|
FROM adventure_log
|
|
WHERE hero_id = $1 AND created_at > $2
|
|
ORDER BY created_at ASC
|
|
LIMIT $3
|
|
`, heroID, since, limit)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get log since: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var entries []LogEntry
|
|
for rows.Next() {
|
|
var e LogEntry
|
|
if err := rows.Scan(&e.ID, &e.HeroID, &e.Message, &e.CreatedAt); err != nil {
|
|
return nil, fmt.Errorf("scan log entry: %w", err)
|
|
}
|
|
entries = append(entries, e)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("log since rows: %w", err)
|
|
}
|
|
if entries == nil {
|
|
entries = []LogEntry{}
|
|
}
|
|
return entries, nil
|
|
}
|
|
|
|
// GetRecent returns the most recent log entries for a hero, ordered newest-first.
|
|
func (s *LogStore) GetRecent(ctx context.Context, heroID int64, limit int) ([]LogEntry, error) {
|
|
if limit <= 0 {
|
|
limit = 50
|
|
}
|
|
if limit > 200 {
|
|
limit = 200
|
|
}
|
|
|
|
rows, err := s.pool.Query(ctx, `
|
|
SELECT id, hero_id, message, created_at
|
|
FROM adventure_log
|
|
WHERE hero_id = $1
|
|
ORDER BY created_at DESC
|
|
LIMIT $2
|
|
`, heroID, limit)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get recent log: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var entries []LogEntry
|
|
for rows.Next() {
|
|
var e LogEntry
|
|
if err := rows.Scan(&e.ID, &e.HeroID, &e.Message, &e.CreatedAt); err != nil {
|
|
return nil, fmt.Errorf("scan log entry: %w", err)
|
|
}
|
|
entries = append(entries, e)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("log entries rows: %w", err)
|
|
}
|
|
if entries == nil {
|
|
entries = []LogEntry{}
|
|
}
|
|
return entries, nil
|
|
}
|