// 微信小程序后端 — Prisma schema // 数据库:PostgreSQL generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ── 用户 ────────────────────────────────────────────── model User { id String @id @default(cuid()) // 微信 openid:登录判定主键。首次登录验证手机号注册,后续登录 openid 在库即放行 openid String @unique // 插件用户标识(当前未用插件,保留可空以兼容未来) openpid String? @unique unionid String? nickname String? avatar String? phone String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt orders Order[] addresses Address[] designLists DesignList[] uploads Upload[] customizations CustomizationTask[] wordCloudJobs WordCloudJob[] @@index([phone]) } // ── 商品分类 ────────────────────────────────────────── model Category { id String @id @default(cuid()) name String parentId String? sort Int @default(0) parent Category? @relation("CategoryTree", fields: [parentId], references: [id]) children Category[] @relation("CategoryTree") products Product[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([parentId]) } // ── 商品 ────────────────────────────────────────────── model Product { id String @id @default(cuid()) name String categoryId String? price Decimal @db.Decimal(10, 2) originalPrice Decimal? @db.Decimal(10, 2) leadTime String @default("3-5个工作日") subtitle String? images String[] iconImg String? description String? story String? scene String? tags String[] @default([]) specs Json @default("[]") tone Int[] @default([]) mask Json sort Int @default(0) status ProductStatus @default(DRAFT) category Category? @relation(fields: [categoryId], references: [id]) orderItems OrderItem[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([categoryId]) @@index([status]) } enum ProductStatus { DRAFT ON_SALE OFF_SHELF } // ── 设计清单 / 定制需求 ─────────────────────────────── model DesignList { id String @id @default(cuid()) userId String title String items Json status DesignListStatus @default(DRAFT) user User @relation(fields: [userId], references: [id]) customizations CustomizationTask[] orders Order[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([userId]) } enum DesignListStatus { DRAFT SUBMITTED PROCESSING DONE } // ── 收货地址 ────────────────────────────────────────── model Address { id String @id @default(cuid()) userId String name String phone String province String city String district String detail String isDefault Boolean @default(false) user User @relation(fields: [userId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([userId]) } // ── 订单 ────────────────────────────────────────────── model Order { id String @id @default(cuid()) orderNo String @unique userId String status OrderStatus @default(PENDING) totalAmount Decimal @db.Decimal(10, 2) addressSnapshot Json // 客户端幂等键;同一用户重复提交时返回已有订单 requestId String? // 待付款订单的服务端截止时间 paymentExpiresAt DateTime? paidAt DateTime? // 关联的设计清单(R4 下单后 WCD 派单据此读取 designData) designListId String? // 客户端幂等键(api-contract-v1 §6:requestId 重复请求返回已创建订单) requestId String? @unique user User @relation(fields: [userId], references: [id]) designList DesignList? @relation(fields: [designListId], references: [id]) items OrderItem[] payment Payment? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([userId]) @@index([status]) @@index([designListId]) @@index([paymentExpiresAt]) @@unique([userId, requestId]) } enum OrderStatus { PENDING PAID PROCESSING SHIPPED COMPLETED CANCELLED PAYMENT_EXPIRED } model OrderItem { id String @id @default(cuid()) orderId String productId String? name String price Decimal @db.Decimal(10, 2) quantity Int order Order @relation(fields: [orderId], references: [id], onDelete: Cascade) product Product? @relation(fields: [productId], references: [id]) @@index([orderId]) } // ── 支付 ────────────────────────────────────────────── model Payment { id String @id @default(cuid()) orderId String @unique transactionId String? provider PaymentProvider @default(WECHAT) status PaymentStatus @default(PENDING) paidAt DateTime? order Order @relation(fields: [orderId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } enum PaymentProvider { WECHAT } enum PaymentStatus { PENDING SUCCESS REFUNDED } // ── 文件上传 ────────────────────────────────────────── model Upload { id String @id @default(cuid()) userId String cosKey String url String mimetype String? size Int? user User @relation(fields: [userId], references: [id]) createdAt DateTime @default(now()) @@index([userId]) } // ── 异步定制任务 ────────────────────────────────────── model CustomizationTask { id String @id @default(cuid()) designListId String? userId String? // R4 下单后 WCD 派单:关联的订单与词云任务 orderId String? wordcloudJobId String? status CustomizationTaskStatus @default(PENDING) resultUrl String? queueJobId String? designList DesignList? @relation(fields: [designListId], references: [id]) user User? @relation(fields: [userId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([designListId]) @@index([orderId]) @@index([status]) } enum CustomizationTaskStatus { PENDING RUNNING SUCCESS FAILED } // ── 词云任务(R4:小程序交互生成 + 下单 WCD 派单共用)──────────────── model WordCloudJob { id String @id @default(cuid()) userId String // wordcloud 侧的外部 job_id(POST /api/jobs 返回);空表示尚未在外部创建 remoteJobId String? status WordCloudJobStatus @default(QUEUED) progress Int @default(0) imageUrl String? error String? user User @relation(fields: [userId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([userId]) } enum WordCloudJobStatus { QUEUED RUNNING SUCCESS FAILED }