b0306a9b1d
Add complete_match: one BEGIN/COMMIT that validates identity + result, enforces a durable (profile_id, match_identity) uniqueness guard (migration 0022 match_completions), persists match history, and grants coins + XP/level-ups + W/D/L/DNF statistics + objectives + achievements exactly once. Any failure rolls the whole match back (no compensating cleanup). Handles sequential/restart/concurrent replay, conflicting re-report (first result canonical), DNF (loss economics, own stat bucket) and no-contest (zero economic effect). Adds tx-scoped variants: statistics::record_match_tx/ record_position_goals_tx, objective::increment_metric_tx, achievement::check_and_unlock_tx. New MatchResultKind/CompleteMatchRequest/ MatchCompletionResult models + POST /matches/complete route.
41 lines
1.1 KiB
Rust
41 lines
1.1 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
|
|
pub struct Statistics {
|
|
pub profile_id: String,
|
|
pub matches_played: i64,
|
|
pub matches_won: i64,
|
|
pub matches_drawn: i64,
|
|
pub matches_lost: i64,
|
|
pub matches_dnf: i64,
|
|
pub goals_scored: i64,
|
|
pub goals_conceded: i64,
|
|
pub packs_opened: i64,
|
|
pub sbcs_completed: i64,
|
|
pub total_coins_earned: i64,
|
|
pub win_streak: i64,
|
|
pub best_win_streak: i64,
|
|
pub updated_at: String,
|
|
}
|
|
|
|
impl Statistics {
|
|
pub fn new(profile_id: impl Into<String>) -> Self {
|
|
Self {
|
|
profile_id: profile_id.into(),
|
|
matches_played: 0,
|
|
matches_won: 0,
|
|
matches_drawn: 0,
|
|
matches_lost: 0,
|
|
matches_dnf: 0,
|
|
goals_scored: 0,
|
|
goals_conceded: 0,
|
|
packs_opened: 0,
|
|
sbcs_completed: 0,
|
|
total_coins_earned: 0,
|
|
win_streak: 0,
|
|
best_win_streak: 0,
|
|
updated_at: chrono::Utc::now().to_rfc3339(),
|
|
}
|
|
}
|
|
}
|