Phase 8 (Core): stateful draft, quick-sell, objectives by ID, market listings
CI / Build, lint & test (push) Failing after 1m21s
CI / Build, lint & test (push) Failing after 1m21s
Draft v2 — stateful FUT-style pick sessions:
- POST /draft/start?difficulty=<> — creates session, returns 5
candidates for GK slot (position order: GK RB CB CB LB CDM CM CAM RW ST LW)
- POST /draft/sessions/:id/pick { card_id } — validates candidate,
advances to next position; on last pick grants coins+pack reward
(avg OVR ≥84 → gold pack + 2000 coins, ≥78 → silver + 1000, else 400)
- GET /draft/sessions/:id — session state with per-pick cards
- POST /draft/sessions/:id/abandon — cancel without reward
- Migration 0005_draft_sessions.sql
Quick-sell:
- DELETE /collection/:owned_card_id — removes card, credits coins based
on overall (85+ → 1500, 80-84 → 900, 75-79 → 600, 65-74 → 300, <65 → 150)
Objectives:
- GET /objectives/:id — single objective with progress
- POST /objectives/:id/claim — claim reward by URL param (complement to
existing POST /objectives/claim body-param endpoint)
Market:
- GET /market/my-listings — active listings posted by current club
- DELETE /market/listings/:id — cancel a listing, returns card to collection
Tests: 9 new integration tests (37 total, all passing)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,15 @@ use crate::{
|
||||
services::{club as club_svc, profile as profile_svc},
|
||||
};
|
||||
|
||||
/// Quick-sell value for a card based on overall rating.
|
||||
fn quick_sell_coins(overall: u8) -> i64 {
|
||||
if overall >= 85 { 1500 }
|
||||
else if overall >= 80 { 900 }
|
||||
else if overall >= 75 { 600 }
|
||||
else if overall >= 65 { 300 }
|
||||
else { 150 }
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CardQuery {
|
||||
pub rarity: Option<String>,
|
||||
@@ -114,3 +123,42 @@ pub async fn get_collection(State(state): State<AppState>) -> AppResult<Json<Val
|
||||
json!({ "collection": with_defs, "total": with_defs.len() }),
|
||||
))
|
||||
}
|
||||
|
||||
/// Quick-sell an owned card for instant coins. The card is removed from the collection.
|
||||
pub async fn delete_owned_card(
|
||||
State(state): State<AppState>,
|
||||
Path(owned_card_id): Path<String>,
|
||||
) -> 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 owned = sqlx::query_as::<_, OwnedCard>(
|
||||
"SELECT id, club_id, card_id, is_loan, loan_matches_remaining, acquired_at \
|
||||
FROM owned_cards WHERE id = ? AND club_id = ?",
|
||||
)
|
||||
.bind(&owned_card_id)
|
||||
.bind(&club.id)
|
||||
.fetch_optional(&state.pool)
|
||||
.await?
|
||||
.ok_or_else(|| AppError::NotFound(format!("owned card '{owned_card_id}' not found")))?;
|
||||
|
||||
let card = state
|
||||
.card_db
|
||||
.get(&owned.card_id)
|
||||
.ok_or_else(|| AppError::NotFound(format!("card definition '{}' missing", owned.card_id)))?;
|
||||
|
||||
let coins = quick_sell_coins(card.overall);
|
||||
|
||||
sqlx::query("DELETE FROM owned_cards WHERE id = ?")
|
||||
.bind(&owned_card_id)
|
||||
.execute(&state.pool)
|
||||
.await?;
|
||||
|
||||
club_svc::add_coins(&state.pool, &club.id, coins).await?;
|
||||
|
||||
Ok(Json(json!({
|
||||
"quick_sold": owned_card_id,
|
||||
"card_id": owned.card_id,
|
||||
"coins_received": coins,
|
||||
})))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user