use chrono::NaiveDate; use erp_core::sanitize::sanitize_option; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; use uuid::Uuid; // --------------------------------------------------------------------------- // 积分账户 // --------------------------------------------------------------------------- #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct PointsAccountResp { pub id: Uuid, pub patient_id: Uuid, pub balance: i32, pub total_earned: i32, pub total_spent: i32, pub total_expired: i32, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, pub version: i32, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct CreateAccountReq { pub patient_id: Uuid, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct CheckinStatusResp { pub checked_in_today: bool, pub consecutive_days: i32, pub next_streak_milestone: Option, } // --------------------------------------------------------------------------- // 积分流水 // --------------------------------------------------------------------------- #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct PointsTransactionResp { pub id: Uuid, pub account_id: Uuid, pub transaction_type: String, pub amount: i32, pub remaining_amount: i32, pub status: String, pub expires_at: Option>, pub balance_after: i32, pub description: Option, pub created_at: chrono::DateTime, } // --------------------------------------------------------------------------- // 积分规则 // --------------------------------------------------------------------------- #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct CreatePointsRuleReq { pub event_type: String, pub name: String, pub description: Option, pub points_value: i32, #[serde(default)] pub daily_cap: i32, #[serde(default)] pub streak_7d_bonus: i32, #[serde(default)] pub streak_14d_bonus: i32, #[serde(default)] pub streak_30d_bonus: i32, } impl CreatePointsRuleReq { pub fn sanitize(&mut self) { self.description = sanitize_option(self.description.take()); } } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct UpdatePointsRuleReq { pub name: Option, pub description: Option, pub points_value: Option, pub daily_cap: Option, pub streak_7d_bonus: Option, pub streak_14d_bonus: Option, pub streak_30d_bonus: Option, pub is_active: Option, } impl UpdatePointsRuleReq { pub fn sanitize(&mut self) { self.description = sanitize_option(self.description.take()); } } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct PointsRuleResp { pub id: Uuid, pub event_type: String, pub name: String, pub description: Option, pub points_value: i32, pub daily_cap: i32, pub streak_7d_bonus: i32, pub streak_14d_bonus: i32, pub streak_30d_bonus: i32, pub is_active: bool, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, pub version: i32, } // --------------------------------------------------------------------------- // 兑换商品 // --------------------------------------------------------------------------- #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct CreatePointsProductReq { pub name: String, pub product_type: Option, pub points_cost: i32, pub stock: Option, pub image_url: Option, pub description: Option, pub service_config: Option, pub sort_order: Option, } impl CreatePointsProductReq { pub fn sanitize(&mut self) { self.description = sanitize_option(self.description.take()); } } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct UpdatePointsProductReq { pub name: Option, pub product_type: Option, pub points_cost: Option, pub stock: Option, pub image_url: Option, pub description: Option, pub service_config: Option, pub is_active: Option, pub sort_order: Option, } impl UpdatePointsProductReq { pub fn sanitize(&mut self) { self.description = sanitize_option(self.description.take()); } } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct PointsProductResp { pub id: Uuid, pub name: String, pub product_type: String, pub points_cost: i32, pub stock: i32, pub image_url: Option, pub description: Option, pub is_active: bool, pub sort_order: i32, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, pub version: i32, } // --------------------------------------------------------------------------- // 兑换订单 // --------------------------------------------------------------------------- #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct CreateOrderReq { pub product_id: Uuid, pub patient_id: Uuid, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct PointsOrderResp { pub id: Uuid, pub patient_id: Uuid, pub product_id: Uuid, pub product_name: Option, pub points_cost: i32, pub status: String, pub qr_code: Option, pub verified_by: Option, pub verified_at: Option>, pub expires_at: Option>, pub notes: Option, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, pub version: i32, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct VerifyOrderReq { pub qr_code: Uuid, } // --------------------------------------------------------------------------- // 线下活动 // --------------------------------------------------------------------------- #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct CreateOfflineEventReq { pub title: String, pub description: Option, pub event_date: NaiveDate, pub start_time: Option, pub end_time: Option, pub location: Option, pub points_reward: Option, pub max_participants: Option, pub image_url: Option, } impl CreateOfflineEventReq { pub fn sanitize(&mut self) { self.description = sanitize_option(self.description.take()); self.location = sanitize_option(self.location.take()); } } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct UpdateOfflineEventReq { pub title: Option, pub description: Option, pub event_date: Option, pub start_time: Option, pub end_time: Option, pub location: Option, pub points_reward: Option, pub max_participants: Option, pub status: Option, pub image_url: Option, } impl UpdateOfflineEventReq { pub fn sanitize(&mut self) { self.description = sanitize_option(self.description.take()); self.location = sanitize_option(self.location.take()); } } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct OfflineEventResp { pub id: Uuid, pub title: String, pub description: Option, pub event_date: NaiveDate, pub start_time: Option, pub end_time: Option, pub location: Option, pub points_reward: i32, pub max_participants: i32, pub current_participants: i32, pub status: String, pub image_url: Option, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, pub version: i32, } // --------------------------------------------------------------------------- // 管理端:带版本号的更新/删除包装 // --------------------------------------------------------------------------- #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct UpdateRuleWithVersion { pub data: UpdatePointsRuleReq, pub version: i32, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct UpdateProductWithVersion { pub data: UpdatePointsProductReq, pub version: i32, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct UpdateOfflineEventWithVersion { pub data: UpdateOfflineEventReq, pub version: i32, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct AdminCheckinReq { pub patient_id: Uuid, } // --------------------------------------------------------------------------- // 积分统计 // --------------------------------------------------------------------------- #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct PointsStatisticsResp { pub total_issued: i64, pub total_spent: i64, pub total_expired: i64, pub active_accounts: i64, pub top_earners: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct TopEarner { pub account_id: Uuid, pub patient_id: Uuid, pub total_earned: i32, }