Initial commit: OpenFUT Core
Offline Ultimate Team backend — game-independent REST API. - 19 API endpoints: auth, profiles, clubs, cards, packs, squads, objectives, SBCs, match rewards, NPC market, statistics - Axum + SQLite + SQLx with full migrations - Weighted pack generator, SBC validation engine - JSON-driven mod data (cards, packs, objectives, SBCs) - 5 integration tests passing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
use crate::{
|
||||
db::Pool,
|
||||
error::AppResult,
|
||||
models::{
|
||||
match_result::{Match, MatchRewardResult, SubmitMatchRequest},
|
||||
objective::ObjectiveDefinition,
|
||||
},
|
||||
services::{club, objective, profile, statistics},
|
||||
};
|
||||
|
||||
const COINS_WIN: i64 = 400;
|
||||
const COINS_DRAW: i64 = 150;
|
||||
const COINS_LOSS: i64 = 75;
|
||||
const XP_WIN: i64 = 200;
|
||||
const XP_DRAW: i64 = 75;
|
||||
const XP_LOSS: i64 = 30;
|
||||
|
||||
pub async fn process_match(
|
||||
pool: &Pool,
|
||||
profile_id: &str,
|
||||
club_id: &str,
|
||||
req: &SubmitMatchRequest,
|
||||
obj_defs: &[ObjectiveDefinition],
|
||||
) -> AppResult<MatchRewardResult> {
|
||||
let outcome = if req.goals_for > req.goals_against {
|
||||
"win"
|
||||
} else if req.goals_for == req.goals_against {
|
||||
"draw"
|
||||
} else {
|
||||
"loss"
|
||||
};
|
||||
|
||||
let (coins, xp) = match outcome {
|
||||
"win" => (COINS_WIN, XP_WIN),
|
||||
"draw" => (COINS_DRAW, XP_DRAW),
|
||||
_ => (COINS_LOSS, XP_LOSS),
|
||||
};
|
||||
|
||||
let match_record = Match::new(
|
||||
profile_id,
|
||||
&req.squad_id,
|
||||
&req.opponent_name,
|
||||
req.goals_for,
|
||||
req.goals_against,
|
||||
&req.mode,
|
||||
coins,
|
||||
xp,
|
||||
);
|
||||
|
||||
sqlx::query(
|
||||
"INSERT INTO matches (id, profile_id, squad_id, opponent_name, goals_for, goals_against, outcome, coins_awarded, xp_awarded, mode, played_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||
)
|
||||
.bind(&match_record.id)
|
||||
.bind(&match_record.profile_id)
|
||||
.bind(&match_record.squad_id)
|
||||
.bind(&match_record.opponent_name)
|
||||
.bind(match_record.goals_for)
|
||||
.bind(match_record.goals_against)
|
||||
.bind(&match_record.outcome)
|
||||
.bind(match_record.coins_awarded)
|
||||
.bind(match_record.xp_awarded)
|
||||
.bind(&match_record.mode)
|
||||
.bind(&match_record.played_at)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
club::add_coins(pool, club_id, coins).await?;
|
||||
profile::add_xp(pool, profile_id, xp).await?;
|
||||
statistics::record_match(
|
||||
pool,
|
||||
profile_id,
|
||||
outcome,
|
||||
req.goals_for,
|
||||
req.goals_against,
|
||||
coins,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut objectives_updated = Vec::new();
|
||||
|
||||
let mut completed =
|
||||
objective::increment_metric(pool, profile_id, obj_defs, "matchesplayed", 1).await?;
|
||||
objectives_updated.append(&mut completed);
|
||||
|
||||
if outcome == "win" {
|
||||
let mut c =
|
||||
objective::increment_metric(pool, profile_id, obj_defs, "matcheswon", 1).await?;
|
||||
objectives_updated.append(&mut c);
|
||||
}
|
||||
|
||||
let mut c =
|
||||
objective::increment_metric(pool, profile_id, obj_defs, "goalsscored", req.goals_for)
|
||||
.await?;
|
||||
objectives_updated.append(&mut c);
|
||||
|
||||
let mut c =
|
||||
objective::increment_metric(pool, profile_id, obj_defs, "coinsearned", coins).await?;
|
||||
objectives_updated.append(&mut c);
|
||||
|
||||
Ok(MatchRewardResult {
|
||||
match_record,
|
||||
coins_awarded: coins,
|
||||
xp_awarded: xp,
|
||||
objectives_updated,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user