use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use sqlx::{FromRow, Type}; use uuid::Uuid; use validator::Validate; /// Card rarity enum matching Python implementation #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type)] #[sqlx(type_name = "card_rarity", rename_all = "lowercase")] pub enum CardRarity { #[serde(rename = "normal")] Normal, #[serde(rename = "rare")] Rare, #[serde(rename = "super_rare")] SuperRare, #[serde(rename = "kira")] Kira, #[serde(rename = "unique")] Unique, } impl CardRarity { pub fn multiplier(&self) -> f64 { match self { CardRarity::Normal => 1.0, CardRarity::Rare => 1.5, CardRarity::SuperRare => 2.0, CardRarity::Kira => 3.0, CardRarity::Unique => 5.0, } } pub fn as_str(&self) -> &'static str { match self { CardRarity::Normal => "normal", CardRarity::Rare => "rare", CardRarity::SuperRare => "super_rare", CardRarity::Kira => "kira", CardRarity::Unique => "unique", } } } /// Database Models #[derive(Debug, Clone, FromRow, Serialize, Deserialize)] pub struct User { pub id: i32, pub did: String, pub handle: String, pub created_at: DateTime, pub updated_at: DateTime, } #[derive(Debug, Clone, FromRow, Serialize, Deserialize)] pub struct CardMaster { pub id: i32, pub name: String, pub base_cp_min: i32, pub base_cp_max: i32, pub color: String, pub description: String, } #[derive(Debug, Clone, FromRow, Serialize, Deserialize)] pub struct UserCard { pub id: i32, pub user_did: String, pub card_id: i32, pub cp: i32, pub status: CardRarity, pub skill: Option, pub obtained_at: DateTime, pub is_unique: bool, pub unique_id: Option, } #[derive(Debug, Clone, FromRow, Serialize, Deserialize)] pub struct UniqueCardRegistry { pub id: i32, pub unique_id: Uuid, pub card_id: i32, pub owner_did: String, pub obtained_at: DateTime, pub verse_skill_id: Option, } #[derive(Debug, Clone, FromRow, Serialize, Deserialize)] pub struct DrawHistory { pub id: i32, pub user_did: String, pub card_id: i32, pub status: CardRarity, pub cp: i32, pub is_paid: bool, pub drawn_at: DateTime, } #[derive(Debug, Clone, FromRow, Serialize, Deserialize)] pub struct GachaPool { pub id: i32, pub name: String, pub description: String, pub is_active: bool, pub start_at: Option>, pub end_at: Option>, pub pickup_card_ids: Vec, pub rate_up_multiplier: f64, } /// API Request/Response Models #[derive(Debug, Deserialize, Validate)] pub struct LoginRequest { #[validate(length(min = 1))] pub identifier: String, #[validate(length(min = 1))] pub password: String, } #[derive(Debug, Serialize)] pub struct LoginResponse { pub access_token: String, pub token_type: String, pub expires_in: u64, pub user: UserInfo, } #[derive(Debug, Serialize)] pub struct UserInfo { pub did: String, pub handle: String, } #[derive(Debug, Deserialize, Validate)] pub struct CardDrawRequest { pub user_did: String, #[serde(default)] pub is_paid: bool, pub pool_id: Option, } #[derive(Debug, Serialize)] pub struct CardDrawResponse { pub card: UserCardResponse, pub master: CardMasterResponse, pub is_unique: bool, pub animation_type: String, pub draw_history_id: i32, } #[derive(Debug, Serialize)] pub struct UserCardResponse { pub id: i32, pub card_id: i32, pub cp: i32, pub status: CardRarity, pub skill: Option, pub obtained_at: DateTime, pub is_unique: bool, pub unique_id: Option, } #[derive(Debug, Serialize)] pub struct CardMasterResponse { pub id: i32, pub name: String, pub base_cp_min: i32, pub base_cp_max: i32, pub color: String, pub description: String, } #[derive(Debug, Serialize)] pub struct UserCardCollectionResponse { pub user_did: String, pub cards: Vec, pub total_count: i32, pub unique_count: i32, pub rarity_breakdown: RarityBreakdown, } #[derive(Debug, Serialize)] pub struct UserCardWithMaster { pub card: UserCardResponse, pub master: CardMasterResponse, } /// Database query result for JOIN operations #[derive(Debug, Clone, FromRow)] pub struct UserCardWithMasterQuery { // user_cards fields pub id: i32, pub user_did: String, pub card_id: i32, pub cp: i32, pub status: String, pub obtained_at: DateTime, pub is_unique: bool, pub unique_id: Option, // card_master fields pub master_id: i32, pub name: String, pub base_cp_min: i32, pub base_cp_max: i32, pub color: String, pub description: String, } /// Database query result for unique card registry #[derive(Debug, Clone, FromRow)] pub struct UniqueCardQuery { pub card_id: i32, pub card_name: String, pub owner_did: Option, pub owner_handle: Option, pub obtained_at: Option>, } #[derive(Debug, Serialize)] pub struct RarityBreakdown { pub normal: i32, pub rare: i32, pub super_rare: i32, pub kira: i32, pub unique: i32, } #[derive(Debug, Serialize)] pub struct UniqueCardRegistryResponse { pub unique_cards: Vec, pub total_unique_cards: i32, pub available_unique_cards: i32, } #[derive(Debug, Serialize)] pub struct UniqueCardInfo { pub card_id: i32, pub card_name: String, pub owner_did: Option, pub owner_handle: Option, pub obtained_at: Option>, pub is_available: bool, } #[derive(Debug, Serialize)] pub struct GachaStatsResponse { pub probabilities: GachaProbabilities, pub total_draws: i32, pub total_unique_cards: i32, pub available_unique_cards: i32, pub rarity_distribution: RarityBreakdown, } #[derive(Debug, Serialize)] pub struct GachaProbabilities { pub normal: f64, pub rare: f64, pub super_rare: f64, pub kira: f64, pub unique: f64, pub paid_multiplier: f64, } /// External Data Models (from ai.json) #[derive(Debug, Deserialize)] pub struct ExternalCardData { pub ai: AiData, } #[derive(Debug, Deserialize)] pub struct AiData { pub card: CardData, } #[derive(Debug, Deserialize)] pub struct CardData { pub cards: Vec, } #[derive(Debug, Deserialize)] pub struct ExternalCard { pub id: i32, pub name: String, pub cp: CpRange, pub color: String, pub skill: String, pub lang: Option, } #[derive(Debug, Deserialize)] pub struct CpRange { pub min: i32, pub max: i32, } #[derive(Debug, Deserialize)] pub struct LangData { pub ja: Option, } #[derive(Debug, Deserialize)] pub struct JapaneseData { pub name: Option, pub skill: Option, } /// atproto Models #[derive(Debug, Serialize)] pub struct AtprotoCardRecord { #[serde(rename = "$type")] pub record_type: String, #[serde(rename = "cardId")] pub card_id: i32, pub cp: i32, pub status: String, #[serde(rename = "obtainedAt")] pub obtained_at: DateTime, #[serde(rename = "isUnique")] pub is_unique: bool, #[serde(rename = "uniqueId")] pub unique_id: Option, }