generated from VLADIMIR/template
up
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
// Package game_results_repo reads consistent, private results snapshots.
|
||||
package game_results_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
var ErrNotImported = errors.New("игра не найдена или не импортирована")
|
||||
|
||||
// Snapshot contains only explicitly selected data; account credentials and mail jobs are excluded.
|
||||
type Snapshot struct {
|
||||
ScenarioID int
|
||||
ImportedScenarioID int
|
||||
ImportID string
|
||||
PackageID string
|
||||
SourceServer string
|
||||
SHA256 string
|
||||
OriginalData []byte
|
||||
Manifest []byte
|
||||
Story []byte
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type GameResultsRepo struct{ pool *pgxpool.Pool }
|
||||
|
||||
func NewGameResultsRepo(pool *pgxpool.Pool) *GameResultsRepo { return &GameResultsRepo{pool: pool} }
|
||||
|
||||
// Capture uses one read-only snapshot, including metadata and all gameplay relations.
|
||||
func (r *GameResultsRepo) Capture(ctx context.Context, gameID int) (*Snapshot, error) {
|
||||
tx, err := r.pool.BeginTx(ctx, pgx.TxOptions{IsoLevel: pgx.RepeatableRead, AccessMode: pgx.ReadOnly})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() { _ = tx.Rollback(ctx) }()
|
||||
snapshot := &Snapshot{}
|
||||
err = tx.QueryRow(
|
||||
ctx,
|
||||
`SELECT
|
||||
i.id,
|
||||
i.package_id,
|
||||
i.source_server,
|
||||
i.sha256,
|
||||
i.original_data,
|
||||
i.manifest,
|
||||
s.scenario,
|
||||
g.scenario_id,
|
||||
(i.mappings->>'scenario_id')::integer,
|
||||
jsonb_build_object(
|
||||
'game', jsonb_build_object(
|
||||
'id', g.id, 'status', g.status, 'format', g.format,
|
||||
'started_at', g.started_at AT TIME ZONE 'UTC',
|
||||
'ended_at', g.ended_at AT TIME ZONE 'UTC'
|
||||
),
|
||||
'teams', COALESCE((
|
||||
SELECT
|
||||
jsonb_agg(jsonb_build_object(
|
||||
'id', t.id,
|
||||
'game_id', t.game_id,
|
||||
'name', t.name,
|
||||
'status', t.status,
|
||||
'password', t.password,
|
||||
'creator_id', t.creator_id,
|
||||
'captain_email', t.captain_email,
|
||||
'participants_count', t.participants_count,
|
||||
'participation_status', t.participation_status,
|
||||
'participation_source', t.participation_source,
|
||||
'participation_answered_at', t.participation_answered_at,
|
||||
'registration_delivery_id', t.registration_delivery_id,
|
||||
'created_at', t.created_at AT TIME ZONE 'UTC'
|
||||
) ORDER BY t.id)
|
||||
FROM teams t
|
||||
WHERE t.game_id = g.id
|
||||
), '[]'::jsonb),
|
||||
'tickets', COALESCE((
|
||||
SELECT
|
||||
jsonb_agg(jsonb_build_object(
|
||||
'id', t.id,
|
||||
'game_id', t.game_id,
|
||||
'token_hash', encode(t.token_hash, 'base64'),
|
||||
'status', t.status,
|
||||
'used_at', t.used_at,
|
||||
'used_by', t.used_by,
|
||||
'created_at', t.created_at
|
||||
) ORDER BY t.id)
|
||||
FROM tickets t
|
||||
WHERE t.game_id = g.id
|
||||
), '[]'::jsonb),
|
||||
'actions', COALESCE((
|
||||
SELECT
|
||||
jsonb_agg(jsonb_build_object(
|
||||
'id', a.id,
|
||||
'team_id', a.team_id,
|
||||
'code', COALESCE(a.code, ''),
|
||||
'via_mcp', a.via_mcp,
|
||||
'created_at', a.created_at AT TIME ZONE 'UTC'
|
||||
) ORDER BY a.team_id, a.created_at, a.id)
|
||||
FROM actions a
|
||||
JOIN teams t ON t.id = a.team_id
|
||||
WHERE t.game_id = g.id
|
||||
), '[]'::jsonb),
|
||||
'applications', COALESCE((
|
||||
SELECT
|
||||
jsonb_agg(jsonb_build_object(
|
||||
'id', a.id,
|
||||
'team_id', a.team_id,
|
||||
'name', COALESCE(a.name, ''),
|
||||
'image', COALESCE(a.image, ''),
|
||||
'file_type', COALESCE(a.file_type, ''),
|
||||
'status', a.status,
|
||||
'created_at', a.created_at AT TIME ZONE 'UTC'
|
||||
) ORDER BY a.team_id, a.created_at, a.id)
|
||||
FROM applications a
|
||||
JOIN teams t ON t.id = a.team_id
|
||||
WHERE t.game_id = g.id
|
||||
), '[]'::jsonb),
|
||||
'answers', COALESCE((
|
||||
SELECT
|
||||
jsonb_agg(jsonb_build_object(
|
||||
'team_id', a.team_id,
|
||||
'question_code', a.question_code,
|
||||
'answer', a.answer,
|
||||
'score', a.score,
|
||||
'created_at', a.created_at AT TIME ZONE 'UTC',
|
||||
'updated_at', a.updated_at AT TIME ZONE 'UTC'
|
||||
) ORDER BY a.team_id, a.question_code)
|
||||
FROM team_answers a
|
||||
JOIN teams t ON t.id = a.team_id
|
||||
WHERE t.game_id = g.id
|
||||
), '[]'::jsonb)
|
||||
)
|
||||
FROM games g
|
||||
JOIN scenarios s ON s.id = g.scenario_id
|
||||
JOIN game_imports i ON i.source_game_id = g.id AND i.state = 'ready'
|
||||
WHERE g.id = $1`,
|
||||
gameID,
|
||||
).Scan(&snapshot.ImportID, &snapshot.PackageID, &snapshot.SourceServer,
|
||||
&snapshot.SHA256, &snapshot.OriginalData, &snapshot.Manifest, &snapshot.Story, &snapshot.ScenarioID, &snapshot.ImportedScenarioID, &snapshot.Data)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrNotImported
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return snapshot, nil
|
||||
}
|
||||
Reference in New Issue
Block a user