Files
OpenFUT-Core/src/routes/market.rs
T
funman300 e438b58d88
CI / Build, lint & test (push) Failing after 2m10s
Phase 25: division leaderboard, market trade history
- Market: record buy/sell history in market_history table; expose via
  GET /market/trade-history (last 30 events, newest first)
- Division: GET /division/leaderboard returns 10-club table with 9 seeded
  NPC entries + player row, sorted by pts; stable within a season
- rand feature small_rng enabled in Cargo.toml for SmallRng use
- 3 new integration tests (leaderboard count, sort order, empty trade history)
- Core: 96 tests passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 19:13:10 -07:00

103 lines
3.7 KiB
Rust

use axum::{
extract::{Path, Query, State},
Json,
};
use serde::Deserialize;
use serde_json::{json, Value};
use crate::{
app::AppState,
error::AppResult,
models::market::{BuyListingRequest, SellCardRequest},
services::{club as club_svc, market as market_svc, profile as profile_svc},
};
pub async fn get_trade_history(State(state): State<AppState>) -> 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 trades = market_svc::get_trade_history(&state.pool, &club.id).await?;
Ok(Json(json!({ "trades": trades, "total": trades.len() })))
}
#[derive(Deserialize)]
pub struct MarketQuery {
pub min_overall: Option<u8>,
pub position: Option<String>,
}
pub async fn get_market(
State(state): State<AppState>,
Query(query): Query<MarketQuery>,
) -> AppResult<Json<Value>> {
let all = market_svc::get_active_listings(&state.pool, &state.card_db).await?;
let listings: Vec<_> = all
.into_iter()
.filter(|l| {
query
.min_overall
.map(|min| l.card.overall >= min)
.unwrap_or(true)
&& query
.position
.as_ref()
.map(|p| l.card.position.eq_ignore_ascii_case(p))
.unwrap_or(true)
})
.collect();
Ok(Json(
json!({ "listings": listings, "total": listings.len() }),
))
}
pub async fn post_market_buy(
State(state): State<AppState>,
Json(req): Json<BuyListingRequest>,
) -> 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 card = market_svc::buy_listing(&state.pool, &state.card_db, &club.id, &req).await?;
Ok(Json(
json!({ "purchased_card": card, "message": "Card purchased successfully" }),
))
}
pub async fn post_market_sell(
State(state): State<AppState>,
Json(req): Json<SellCardRequest>,
) -> 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 new_balance = market_svc::sell_card(&state.pool, &state.card_db, &club.id, &req).await?;
Ok(Json(
json!({ "new_coin_balance": new_balance, "message": "Card sold to NPC market" }),
))
}
pub async fn post_market_refresh(State(state): State<AppState>) -> AppResult<Json<Value>> {
let count =
market_svc::refresh_npc_listings(&state.pool, &state.card_db, &state.event_defs).await?;
Ok(Json(json!({ "listings_generated": count })))
}
/// Return all active market listings posted by the current player's club.
pub async fn get_my_listings(State(state): State<AppState>) -> 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 listings = market_svc::get_listings_by_seller(&state.pool, &state.card_db, &club.id).await?;
Ok(Json(json!({ "listings": listings, "total": listings.len() })))
}
/// Cancel a player-posted listing and return the card to the collection.
pub async fn delete_market_listing(
State(state): State<AppState>,
Path(listing_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?;
market_svc::cancel_listing(&state.pool, &club.id, &listing_id).await?;
Ok(Json(json!({ "cancelled": listing_id })))
}