feat: backend MVP (auth/rbac, catalog, orders, fulfillment, invoices) + specs + scaffolds

This commit is contained in:
Chengdong Zhang
2026-09-17 12:43:22 +08:00
commit dc9fd31c5e
96 changed files with 17550 additions and 0 deletions
+205
View File
@@ -0,0 +1,205 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::Type;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type)]
#[sqlx(type_name = "user_role", rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum UserRole {
PlatformAdmin,
ShopOwner,
ShopStaff,
Customer,
}
impl UserRole {
pub fn is_shop_role(self) -> bool {
matches!(self, UserRole::ShopOwner | UserRole::ShopStaff)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type)]
#[sqlx(type_name = "shop_status", rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum ShopStatus {
Active,
Suspended,
}
#[derive(Debug, Serialize, sqlx::FromRow)]
pub struct User {
pub id: Uuid,
pub email: String,
#[serde(skip_serializing)]
pub password_hash: String,
pub display_name: String,
pub role: UserRole,
pub shop_id: Option<Uuid>,
pub locale: String,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Serialize, sqlx::FromRow)]
pub struct Shop {
pub id: Uuid,
pub name: serde_json::Value,
pub slug: String,
pub status: ShopStatus,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type)]
#[sqlx(type_name = "product_status", rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum ProductStatus {
Draft,
Published,
Unpublished,
}
#[derive(Debug, Serialize, sqlx::FromRow)]
pub struct Category {
pub id: Uuid,
pub parent_id: Option<Uuid>,
pub name: serde_json::Value,
pub slug: String,
pub position: i32,
}
#[derive(Debug, Clone, Serialize, sqlx::FromRow)]
pub struct Product {
pub id: Uuid,
pub shop_id: Uuid,
pub category_id: Option<Uuid>,
pub slug: String,
pub name: serde_json::Value,
pub description: serde_json::Value,
pub images: serde_json::Value,
pub status: ProductStatus,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, sqlx::FromRow)]
pub struct Sku {
pub id: Uuid,
pub product_id: Uuid,
pub sku_code: String,
pub attributes: serde_json::Value,
pub price_minor: i64,
pub currency: String,
pub stock: i32,
pub active: bool,
}
#[derive(Debug, Serialize, sqlx::FromRow)]
pub struct Currency {
pub code: String,
pub name: serde_json::Value,
pub symbol: String,
pub exponent: i16,
pub is_base: bool,
pub rate_to_base: rust_decimal::Decimal,
pub enabled: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type)]
#[sqlx(type_name = "order_status", rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum OrderStatus {
PendingPayment,
Paid,
Fulfilling,
Shipped,
Completed,
Cancelled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type)]
#[sqlx(type_name = "shipment_status", rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum ShipmentStatus {
Pending,
Shipped,
Delivered,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type)]
#[sqlx(type_name = "invoice_status", rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum InvoiceStatus {
Requested,
Issued,
Cancelled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type)]
#[sqlx(type_name = "invoice_kind", rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum InvoiceKind {
Personal,
Company,
}
#[derive(Debug, Clone, Serialize, sqlx::FromRow)]
pub struct Order {
pub id: Uuid,
pub order_no: String,
pub shop_id: Uuid,
pub user_id: Uuid,
pub status: OrderStatus,
pub currency: String,
pub total_minor: i64,
pub shipping_address: serde_json::Value,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, sqlx::FromRow)]
pub struct OrderItem {
pub id: Uuid,
pub order_id: Uuid,
pub sku_id: Uuid,
pub product_name: serde_json::Value,
pub sku_code: String,
pub image: Option<String>,
pub unit_price_minor: i64,
pub qty: i32,
}
#[derive(Debug, Clone, Serialize, sqlx::FromRow)]
pub struct Shipment {
pub id: Uuid,
pub shipment_no: String,
pub order_id: Uuid,
pub carrier: String,
pub tracking_no: String,
pub status: ShipmentStatus,
pub shipped_at: Option<DateTime<Utc>>,
pub delivered_at: Option<DateTime<Utc>>,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, sqlx::FromRow)]
pub struct ShipmentItem {
pub shipment_id: Uuid,
pub order_item_id: Uuid,
pub qty: i32,
}
#[derive(Debug, Clone, Serialize, sqlx::FromRow)]
pub struct Invoice {
pub id: Uuid,
pub invoice_no: Option<String>,
pub order_id: Uuid,
pub user_id: Uuid,
pub title: String,
pub tax_no: Option<String>,
pub kind: InvoiceKind,
pub amount_minor: i64,
pub currency: String,
pub status: InvoiceStatus,
pub issued_at: Option<DateTime<Utc>>,
pub created_at: DateTime<Utc>,
}