mirror of
https://github.com/IS1DI/x0gp.git
synced 2026-07-19 05:47:02 +00:00
Add driver profile stats API, nickname update editing, and security checks
This commit is contained in:
@@ -1046,3 +1046,132 @@ func (f FinishedRace) ToLobbyMeta() lobby.RaceMeta {
|
||||
StartedMs: f.StartedMs,
|
||||
}
|
||||
}
|
||||
|
||||
// DriverStats holds database-calculated stats.
|
||||
type DriverStats struct {
|
||||
TotalRacesStarted int64
|
||||
TotalRacesFinished int64
|
||||
Wins int64
|
||||
Podiums int64
|
||||
BestLapMs int64
|
||||
TotalPlaytimeMs int64
|
||||
}
|
||||
|
||||
// DriverRaceSummary holds a summary of a single finished race.
|
||||
type DriverRaceSummary struct {
|
||||
RaceID string
|
||||
RaceName string
|
||||
FinishedMs int64
|
||||
Position int
|
||||
TotalTimeMs int64
|
||||
BestLapMs int64
|
||||
TrackID string
|
||||
TrackName string
|
||||
}
|
||||
|
||||
// GetDriverStats aggregates statistics for a given driver from finished races.
|
||||
func (s *PgStore) GetDriverStats(ctx context.Context, driverID string) (DriverStats, error) {
|
||||
row := s.pool.QueryRow(ctx, `
|
||||
SELECT
|
||||
COUNT(*)::bigint as total_races_started,
|
||||
COUNT(CASE WHEN rd.position IS NOT NULL OR rd.total_time_ms > 0 THEN 1 END)::bigint as total_races_finished,
|
||||
COUNT(CASE WHEN rd.position = 1 THEN 1 END)::bigint as wins,
|
||||
COUNT(CASE WHEN rd.position >= 1 AND rd.position <= 3 THEN 1 END)::bigint as podiums,
|
||||
COALESCE(MIN(CASE WHEN rd.best_lap_ms > 0 THEN rd.best_lap_ms END), 0)::bigint as best_lap_ms,
|
||||
COALESCE(SUM(rd.total_time_ms), 0)::bigint as total_playtime_ms
|
||||
FROM race_drivers rd
|
||||
JOIN races r ON rd.race_id = r.id
|
||||
WHERE rd.driver_id = $1 AND r.status = 'finished'`, driverID)
|
||||
|
||||
var stats DriverStats
|
||||
err := row.Scan(
|
||||
&stats.TotalRacesStarted,
|
||||
&stats.TotalRacesFinished,
|
||||
&stats.Wins,
|
||||
&stats.Podiums,
|
||||
&stats.BestLapMs,
|
||||
&stats.TotalPlaytimeMs,
|
||||
)
|
||||
if err != nil {
|
||||
return DriverStats{}, fmt.Errorf("query driver stats: %w", err)
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// GetDriverLastRace returns the last completed race for a driver.
|
||||
func (s *PgStore) GetDriverLastRace(ctx context.Context, driverID string) (*DriverRaceSummary, error) {
|
||||
row := s.pool.QueryRow(ctx, `
|
||||
SELECT
|
||||
rd.race_id,
|
||||
r.name,
|
||||
r.finished_ms,
|
||||
COALESCE(rd.position, 0) as position,
|
||||
rd.total_time_ms,
|
||||
rd.best_lap_ms,
|
||||
r.track_id,
|
||||
COALESCE(t.name, '') as track_name
|
||||
FROM race_drivers rd
|
||||
JOIN races r ON rd.race_id = r.id
|
||||
LEFT JOIN tracks t ON r.track_id = t.id
|
||||
WHERE rd.driver_id = $1 AND r.status = 'finished'
|
||||
ORDER BY r.finished_ms DESC, r.id DESC
|
||||
LIMIT 1`, driverID)
|
||||
|
||||
var sum DriverRaceSummary
|
||||
err := row.Scan(
|
||||
&sum.RaceID,
|
||||
&sum.RaceName,
|
||||
&sum.FinishedMs,
|
||||
&sum.Position,
|
||||
&sum.TotalTimeMs,
|
||||
&sum.BestLapMs,
|
||||
&sum.TrackID,
|
||||
&sum.TrackName,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("query driver last race: %w", err)
|
||||
}
|
||||
return &sum, nil
|
||||
}
|
||||
|
||||
// GetDriverBestRace returns the best completed race for a driver (by position, then time).
|
||||
func (s *PgStore) GetDriverBestRace(ctx context.Context, driverID string) (*DriverRaceSummary, error) {
|
||||
row := s.pool.QueryRow(ctx, `
|
||||
SELECT
|
||||
rd.race_id,
|
||||
r.name,
|
||||
r.finished_ms,
|
||||
COALESCE(rd.position, 0) as position,
|
||||
rd.total_time_ms,
|
||||
rd.best_lap_ms,
|
||||
r.track_id,
|
||||
COALESCE(t.name, '') as track_name
|
||||
FROM race_drivers rd
|
||||
JOIN races r ON rd.race_id = r.id
|
||||
LEFT JOIN tracks t ON r.track_id = t.id
|
||||
WHERE rd.driver_id = $1 AND r.status = 'finished' AND rd.position IS NOT NULL AND rd.position > 0
|
||||
ORDER BY rd.position ASC, rd.total_time_ms ASC, r.finished_ms DESC, r.id DESC
|
||||
LIMIT 1`, driverID)
|
||||
|
||||
var sum DriverRaceSummary
|
||||
err := row.Scan(
|
||||
&sum.RaceID,
|
||||
&sum.RaceName,
|
||||
&sum.FinishedMs,
|
||||
&sum.Position,
|
||||
&sum.TotalTimeMs,
|
||||
&sum.BestLapMs,
|
||||
&sum.TrackID,
|
||||
&sum.TrackName,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("query driver best race: %w", err)
|
||||
}
|
||||
return &sum, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user