refactor: 积分系统拆分为独立 erp-points crate
- 新建 erp-points crate(8 Entity + account/product service + handler) - 商品 CRUD 和账户管理完整实现,订单/签到/规则端点暂返回 501 - 注册到 workspace + erp-server 路由 /api/v1/points/* - API 路径不变,前端无需修改
This commit is contained in:
314
crates/erp-points/src/dto/points_dto.rs
Normal file
314
crates/erp-points/src/dto/points_dto.rs
Normal file
@@ -0,0 +1,314 @@
|
||||
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<chrono::Utc>,
|
||||
pub updated_at: chrono::DateTime<chrono::Utc>,
|
||||
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<i32>,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 积分流水
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[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<chrono::DateTime<chrono::Utc>>,
|
||||
pub balance_after: i32,
|
||||
pub description: Option<String>,
|
||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 积分规则
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||
pub struct CreatePointsRuleReq {
|
||||
pub event_type: String,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
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<String>,
|
||||
pub description: Option<String>,
|
||||
pub points_value: Option<i32>,
|
||||
pub daily_cap: Option<i32>,
|
||||
pub streak_7d_bonus: Option<i32>,
|
||||
pub streak_14d_bonus: Option<i32>,
|
||||
pub streak_30d_bonus: Option<i32>,
|
||||
pub is_active: Option<bool>,
|
||||
}
|
||||
|
||||
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<String>,
|
||||
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<chrono::Utc>,
|
||||
pub updated_at: chrono::DateTime<chrono::Utc>,
|
||||
pub version: i32,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 兑换商品
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||
pub struct CreatePointsProductReq {
|
||||
pub name: String,
|
||||
pub product_type: Option<String>,
|
||||
pub points_cost: i32,
|
||||
pub stock: Option<i32>,
|
||||
pub image_url: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub service_config: Option<serde_json::Value>,
|
||||
pub sort_order: Option<i32>,
|
||||
}
|
||||
|
||||
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<String>,
|
||||
pub product_type: Option<String>,
|
||||
pub points_cost: Option<i32>,
|
||||
pub stock: Option<i32>,
|
||||
pub image_url: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub service_config: Option<serde_json::Value>,
|
||||
pub is_active: Option<bool>,
|
||||
pub sort_order: Option<i32>,
|
||||
}
|
||||
|
||||
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<String>,
|
||||
pub description: Option<String>,
|
||||
pub is_active: bool,
|
||||
pub sort_order: i32,
|
||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||
pub updated_at: chrono::DateTime<chrono::Utc>,
|
||||
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<String>,
|
||||
pub points_cost: i32,
|
||||
pub status: String,
|
||||
pub qr_code: Option<Uuid>,
|
||||
pub verified_by: Option<Uuid>,
|
||||
pub verified_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub notes: Option<String>,
|
||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||
pub updated_at: chrono::DateTime<chrono::Utc>,
|
||||
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<String>,
|
||||
pub event_date: NaiveDate,
|
||||
pub start_time: Option<chrono::NaiveTime>,
|
||||
pub end_time: Option<chrono::NaiveTime>,
|
||||
pub location: Option<String>,
|
||||
pub points_reward: Option<i32>,
|
||||
pub max_participants: Option<i32>,
|
||||
pub image_url: Option<String>,
|
||||
}
|
||||
|
||||
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<String>,
|
||||
pub description: Option<String>,
|
||||
pub event_date: Option<NaiveDate>,
|
||||
pub start_time: Option<chrono::NaiveTime>,
|
||||
pub end_time: Option<chrono::NaiveTime>,
|
||||
pub location: Option<String>,
|
||||
pub points_reward: Option<i32>,
|
||||
pub max_participants: Option<i32>,
|
||||
pub status: Option<String>,
|
||||
pub image_url: Option<String>,
|
||||
}
|
||||
|
||||
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<String>,
|
||||
pub event_date: NaiveDate,
|
||||
pub start_time: Option<chrono::NaiveTime>,
|
||||
pub end_time: Option<chrono::NaiveTime>,
|
||||
pub location: Option<String>,
|
||||
pub points_reward: i32,
|
||||
pub max_participants: i32,
|
||||
pub current_participants: i32,
|
||||
pub status: String,
|
||||
pub image_url: Option<String>,
|
||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||
pub updated_at: chrono::DateTime<chrono::Utc>,
|
||||
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<TopEarner>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||
pub struct TopEarner {
|
||||
pub account_id: Uuid,
|
||||
pub patient_id: Uuid,
|
||||
pub total_earned: i32,
|
||||
}
|
||||
Reference in New Issue
Block a user