f70cf4415c
Divergent development line off origin/main (11a811d): a broad refactor across routes/services/models/app + a large integration_test expansion (+1000), plus an untracked game-independent inventory query service and Docker files. Preserved verbatim before moving the canonical Core checkout to the committed migration trunk (66c88fb). Reconciling this refactor with the migration trunk is a separate user decision; nothing here is lost.
100 lines
3.1 KiB
Rust
100 lines
3.1 KiB
Rust
use axum::{
|
|
extract::{Path, Query, State},
|
|
Json,
|
|
};
|
|
use serde::Deserialize;
|
|
use serde_json::{json, Value};
|
|
|
|
use crate::{
|
|
app::AppState,
|
|
error::AppResult,
|
|
services::{club as club_svc, draft as draft_svc, match_service, profile as profile_svc},
|
|
};
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct DraftQuery {
|
|
pub difficulty: Option<String>,
|
|
}
|
|
|
|
/// Legacy endpoint: returns a pre-built random draft squad (no session state).
|
|
pub async fn get_draft_squad(
|
|
State(state): State<AppState>,
|
|
Query(query): Query<DraftQuery>,
|
|
) -> AppResult<Json<Value>> {
|
|
let difficulty = query.difficulty.as_deref().unwrap_or("beginner");
|
|
let generated = match_service::generate_opponent(&state.card_db, difficulty);
|
|
Ok(Json(json!({
|
|
"mode": "draft",
|
|
"difficulty": difficulty,
|
|
"squad_rating": generated["squad_rating"],
|
|
"formation": generated["formation"],
|
|
"cards": generated["cards"],
|
|
})))
|
|
}
|
|
|
|
/// Start a new stateful FUT-style draft session.
|
|
///
|
|
/// Returns a session ID and the 5 candidates for the first position (GK).
|
|
/// The caller picks one candidate, then calls `POST /draft/sessions/:id/pick`
|
|
/// until all 11 slots are filled.
|
|
pub async fn post_draft_start(
|
|
State(state): State<AppState>,
|
|
Query(query): Query<DraftQuery>,
|
|
) -> AppResult<Json<Value>> {
|
|
let profile = profile_svc::get_active_profile(&state.pool).await?;
|
|
let difficulty = query.difficulty.as_deref().unwrap_or("professional");
|
|
let session =
|
|
draft_svc::start_draft(&state.pool, &state.card_db, &profile.id, difficulty).await?;
|
|
Ok(Json(session))
|
|
}
|
|
|
|
/// Get the current state of a draft session.
|
|
pub async fn get_draft_session(
|
|
State(state): State<AppState>,
|
|
Path(session_id): Path<String>,
|
|
) -> AppResult<Json<Value>> {
|
|
let profile = profile_svc::get_active_profile(&state.pool).await?;
|
|
let session =
|
|
draft_svc::get_draft(&state.pool, &state.card_db, &profile.id, &session_id).await?;
|
|
Ok(Json(session))
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct PickRequest {
|
|
pub card_id: String,
|
|
}
|
|
|
|
/// Pick one card from the current candidates list.
|
|
///
|
|
/// Advances the session to the next position.
|
|
/// When the last pick is made the session status changes to "completed"
|
|
/// and rewards (coins + optional pack) are granted automatically.
|
|
pub async fn post_draft_pick(
|
|
State(state): State<AppState>,
|
|
Path(session_id): Path<String>,
|
|
Json(req): Json<PickRequest>,
|
|
) -> AppResult<Json<Value>> {
|
|
let profile = profile_svc::get_active_profile(&state.pool).await?;
|
|
let club = club_svc::get_club_by_profile(&state.pool, &profile.id).await?;
|
|
let session = draft_svc::pick_card(
|
|
&state.pool,
|
|
&state.card_db,
|
|
&club.id,
|
|
&profile.id,
|
|
&session_id,
|
|
&req.card_id,
|
|
)
|
|
.await?;
|
|
Ok(Json(session))
|
|
}
|
|
|
|
/// Abandon an active draft session. No rewards are granted.
|
|
pub async fn post_draft_abandon(
|
|
State(state): State<AppState>,
|
|
Path(session_id): Path<String>,
|
|
) -> AppResult<Json<Value>> {
|
|
let profile = profile_svc::get_active_profile(&state.pool).await?;
|
|
let result = draft_svc::abandon_draft(&state.pool, &profile.id, &session_id).await?;
|
|
Ok(Json(result))
|
|
}
|