d97695d414
CI / Build, lint & test (push) Failing after 57s
XP thresholds (500→1200→2000→…→11000→+2500/level) drive automatic level increases. add_xp_with_levelup() replaces bare add_xp() in match processing: for each level gained it grants level×500 coins and milestone packs (bronze@5, silver@10, gold@15, rare_gold@20, gold every 5 after). GET /profile now returns computed level (recalculated from XP so it stays consistent), xp_to_next_level, and xp_for_next_level so the dashboard can render a progress bar without a second call. POST /matches/result response gains level_ups array (empty when no level-up occurred) with new_level, coins_granted, pack_granted per event. Four new tests: profile level fields, level_for_xp boundary checks, level-up event in match result, milestone pack unit test. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
35 lines
1.1 KiB
Rust
35 lines
1.1 KiB
Rust
use crate::{
|
|
app::AppState,
|
|
error::AppResult,
|
|
models::profile::{level_for_xp, XP_THRESHOLDS},
|
|
services::profile as profile_svc,
|
|
};
|
|
use axum::{extract::State, Json};
|
|
use serde_json::{json, Value};
|
|
|
|
pub async fn get_profile(State(state): State<AppState>) -> AppResult<Json<Value>> {
|
|
let profile = profile_svc::get_active_profile(&state.pool).await?;
|
|
let computed_level = level_for_xp(profile.xp);
|
|
|
|
let next_level = computed_level + 1;
|
|
let xp_for_next = if next_level as usize <= XP_THRESHOLDS.len() {
|
|
XP_THRESHOLDS[next_level as usize - 1]
|
|
} else {
|
|
let overflow_base = XP_THRESHOLDS.last().copied().unwrap_or(0);
|
|
let levels_past = next_level - XP_THRESHOLDS.len() as i64;
|
|
overflow_base + levels_past * 2500
|
|
};
|
|
let xp_to_next = (xp_for_next - profile.xp).max(0);
|
|
|
|
Ok(Json(json!({
|
|
"id": profile.id,
|
|
"username": profile.username,
|
|
"level": computed_level,
|
|
"xp": profile.xp,
|
|
"xp_to_next_level": xp_to_next,
|
|
"xp_for_next_level": xp_for_next,
|
|
"created_at": profile.created_at,
|
|
"updated_at": profile.updated_at,
|
|
})))
|
|
}
|