// 杜康好客 · V2.1 数据模型 // 约束说明见 doc/数据库设计2-杜康好客.md generator client { provider = "prisma-client-js" } datasource db { provider = "mysql" url = env("DATABASE_URL") } // ─── 枚举 ───────────────────────────────────────────── enum ClientApp { USER_MINI USER_H5 PARTNER_MINI PARTNER_H5 HQ_MINI SHOP_H5 } enum AccountStatus { ACTIVE DISABLED } enum HqAdminRole { SUPER_ADMIN OPS FINANCE CUSTOMER_SERVICE } enum PartnerStaffRole { PARTNER INTERNAL PROMOTER } enum CityStatus { PENDING ACTIVE PAUSED } enum AromaType { QINGXIANG JIANGXIANG NONGXIANG } enum ProductStatus { DRAFT ON_SALE OFF_SALE } enum StoreStatus { OPEN PAUSED CLOSED } enum StoreAuditType { NEW UPDATE } enum StoreAuditStatus { PENDING APPROVED REJECTED } enum DeliveryType { LOCAL CROSS_CITY } enum OrderType { NORMAL RESHIPMENT } enum OrderStatus { PENDING_PAY PENDING_SHIP OUT_WAREHOUSE SHIPPING PENDING_RECEIVE COMPLETED CANCELLED REFUNDING REFUNDED } enum PaymentStatus { PENDING SUCCESS FAILED CLOSED } enum RefundStatus { PENDING APPROVED REJECTED PROCESSING SUCCESS FAILED } enum InterceptStatus { PENDING INTERCEPTING SUCCESS FAILED REDELIVERING COMPLETED } enum BenefitCouponStatus { ACTIVE USED_UP VOID } enum BenefitLedgerType { GRANT REDEEM REFUND_VOID ADJUST } enum RedeemTokenStatus { ACTIVE USED EXPIRED CANCELLED } enum StorePayoutStatus { PENDING PAID } enum PartnerBillStatus { DRAFT PENDING_CONFIRM CONFIRMED PAID REJECTED } enum WithdrawalStatus { PENDING APPROVED REJECTED PAID } enum PromoCodeStatus { ACTIVE DISABLED } enum AlertType { ORDER_TIMEOUT DELIVERY_DELAY OTHER } enum AlertStatus { OPEN RESOLVED } enum AfterSaleType { REFUND RESHIPMENT } enum AfterSaleStatus { PENDING PROCESSING COMPLETED REJECTED } // ─── 微信应用配置 ───────────────────────────────────── model WxAppConfig { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt clientApp ClientApp @unique @map("client_app") appId String @map("app_id") @db.VarChar(64) appSecret String @map("app_secret") @db.VarChar(128) mchId String? @map("mch_id") @db.VarChar(32) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) @@map("wx_app_configs") } // ─── C端用户(phone 唯一主键,微信字段辅助)────────── model User { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt userNo String @unique @map("user_no") @db.VarChar(20) phone String @unique @db.VarChar(20) wxOpenId String? @map("wx_open_id") @db.VarChar(64) wxUnionId String? @map("wx_union_id") @db.VarChar(64) nickname String? @db.VarChar(64) avatarUrl String? @map("avatar_url") @db.VarChar(512) status Int @default(1) @db.TinyInt createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) addresses UserAddress[] cityPref UserCityPreference? orders Order[] benefitCoupons BenefitCoupon[] benefitLedgers BenefitLedger[] redeemRecords RedeemRecord[] promoTouch UserPromoAttribution? eventLogs EventLog[] @@index([wxOpenId]) @@index([wxUnionId]) @@map("users") } model UserAddress { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt userId BigInt @map("user_id") @db.UnsignedBigInt receiverName String @map("receiver_name") @db.VarChar(32) phone String @db.VarChar(20) province String @db.VarChar(32) city String @db.VarChar(32) district String @db.VarChar(32) detail String @db.VarChar(256) latitude Decimal? @db.Decimal(10, 7) longitude Decimal? @db.Decimal(10, 7) isDefault Int @default(0) @map("is_default") @db.TinyInt createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@map("user_addresses") } model UserCityPreference { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt userId BigInt @unique @map("user_id") @db.UnsignedBigInt selectedCityCode String? @map("selected_city_code") @db.VarChar(16) selectedDistrict String? @map("selected_district") @db.VarChar(32) locateCityCode String? @map("locate_city_code") @db.VarChar(16) locateDistrict String? @map("locate_district") @db.VarChar(32) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("user_city_preferences") } // ─── B端账号(三表分离)────────────────────────────── model StoreAccount { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt storeId BigInt @unique @map("store_id") @db.UnsignedBigInt phone String @unique @db.VarChar(20) name String @db.VarChar(64) wxOpenId String? @map("wx_open_id") @db.VarChar(64) wxUnionId String? @map("wx_union_id") @db.VarChar(64) status AccountStatus @default(ACTIVE) lastLoginAt DateTime? @map("last_login_at") @db.DateTime(3) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) store Store @relation(fields: [storeId], references: [id], onDelete: Cascade) @@index([wxOpenId]) @@map("store_accounts") } model PartnerAccount { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt partnerId BigInt @map("partner_id") @db.UnsignedBigInt phone String @unique @db.VarChar(20) name String @db.VarChar(64) wxOpenId String? @map("wx_open_id") @db.VarChar(64) wxUnionId String? @map("wx_union_id") @db.VarChar(64) isPrimary Int @default(0) @map("is_primary") @db.TinyInt parentAccountId BigInt? @map("parent_account_id") @db.UnsignedBigInt staffRole PartnerStaffRole? @map("staff_role") status AccountStatus @default(ACTIVE) lastLoginAt DateTime? @map("last_login_at") @db.DateTime(3) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict) parent PartnerAccount? @relation("PartnerAccountHierarchy", fields: [parentAccountId], references: [id], onDelete: SetNull) children PartnerAccount[] @relation("PartnerAccountHierarchy") @@index([partnerId]) @@index([parentAccountId]) @@index([wxOpenId]) @@map("partner_accounts") } model HqAccount { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt phone String @unique @db.VarChar(20) name String @db.VarChar(64) adminRole HqAdminRole @default(OPS) @map("admin_role") wxOpenId String? @map("wx_open_id") @db.VarChar(64) wxUnionId String? @map("wx_union_id") @db.VarChar(64) status AccountStatus @default(ACTIVE) lastLoginAt DateTime? @map("last_login_at") @db.DateTime(3) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) storeAudits StoreAudit[] operationLogs OperationLog[] afterSales AfterSaleTicket[] refunds Refund[] @@index([wxOpenId]) @@index([adminRole]) @@map("hq_accounts") } // ─── 开城与合伙人 ───────────────────────────────────── model Partner { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt companyName String @map("company_name") @db.VarChar(128) address String @db.VarChar(256) contactPhone String @map("contact_phone") @db.VarChar(20) bankAccountName String? @map("bank_account_name") @db.VarChar(64) bankAccountNo String? @map("bank_account_no") @db.VarChar(32) bankBranch String? @map("bank_branch") @db.VarChar(128) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) cities City[] accounts PartnerAccount[] stores Store[] bills PartnerBill[] withdrawals PartnerWithdrawal[] contracts PartnerContract[] orderCommissions OrderCommission[] @@index([contactPhone]) @@map("partners") } model City { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt code String @unique @db.VarChar(16) name String @db.VarChar(64) province String @db.VarChar(32) status CityStatus @default(PENDING) partnerId BigInt? @map("partner_id") @db.UnsignedBigInt localMinQty Int @default(2) @map("local_min_qty") crossMinQty Int @default(6) @map("cross_min_qty") createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) partner Partner? @relation(fields: [partnerId], references: [id], onDelete: SetNull) commissionRule CityCommissionRule? stores Store[] orders Order[] @@index([partnerId]) @@index([status]) @@map("cities") } model CityCommissionRule { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt cityId BigInt @unique @map("city_id") @db.UnsignedBigInt orderCommissionRate Decimal @map("order_commission_rate") @db.Decimal(5, 4) redeemCommissionRate Decimal @map("redeem_commission_rate") @db.Decimal(5, 4) partnerProfitRate Decimal @default(0.35) @map("partner_profit_rate") @db.Decimal(5, 4) storeSettlementRate Decimal @default(0.60) @map("store_settlement_rate") @db.Decimal(5, 4) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) city City @relation(fields: [cityId], references: [id], onDelete: Cascade) @@map("city_commission_rules") } model PartnerContract { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt partnerId BigInt @map("partner_id") @db.UnsignedBigInt contractNo String @map("contract_no") @db.VarChar(64) fileUrl String @map("file_url") @db.VarChar(512) signedAt DateTime @map("signed_at") @db.DateTime(3) expireAt DateTime? @map("expire_at") @db.DateTime(3) status String @default("ACTIVE") @db.VarChar(16) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict) @@index([partnerId]) @@map("partner_contracts") } // ─── 商品 ───────────────────────────────────────────── model Product { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt skuCode String @unique @map("sku_code") @db.VarChar(32) name String @db.VarChar(128) subtitle String? @db.VarChar(256) aromaType AromaType @map("aroma_type") spec String @db.VarChar(128) price Decimal @db.Decimal(10, 2) benefitAmount Decimal? @map("benefit_amount") @db.Decimal(10, 2) status ProductStatus @default(DRAFT) sortOrder Int @default(0) @map("sort_order") mainImageUrl String @map("main_image_url") @db.VarChar(512) detailContent Json? @map("detail_content") carouselUrls Json? @map("carousel_urls") createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) orderItems OrderItem[] @@index([status, aromaType]) @@map("products") } // ─── 门店 ───────────────────────────────────────────── model StoreCategory { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt code String @unique @db.VarChar(32) name String @db.VarChar(64) sort Int @default(0) stores Store[] @@map("store_categories") } model Store { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt cityId BigInt @map("city_id") @db.UnsignedBigInt partnerId BigInt @map("partner_id") @db.UnsignedBigInt categoryId BigInt? @map("category_id") @db.UnsignedBigInt name String @db.VarChar(128) phone String @db.VarChar(20) province String @db.VarChar(32) cityName String @map("city") @db.VarChar(32) district String @db.VarChar(32) address String @db.VarChar(256) latitude Decimal? @db.Decimal(10, 7) longitude Decimal? @db.Decimal(10, 7) intro String? @db.Text coverUrl String? @map("cover_url") @db.VarChar(512) avgPrice Decimal? @map("avg_price") @db.Decimal(10, 2) rating Decimal? @db.Decimal(3, 2) tags Json? status StoreStatus @default(PAUSED) openTime String? @map("open_time") @db.VarChar(8) closeTime String? @map("close_time") @db.VarChar(8) bankAccountName String? @map("bank_account_name") @db.VarChar(64) bankAccountNo String? @map("bank_account_no") @db.VarChar(32) bankBranch String? @map("bank_branch") @db.VarChar(128) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) cityRef City @relation(fields: [cityId], references: [id], onDelete: Restrict) partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict) category StoreCategory? @relation(fields: [categoryId], references: [id], onDelete: SetNull) account StoreAccount? media StoreMedia[] audits StoreAudit[] redeemRecords RedeemRecord[] storePayouts StorePayout[] ratings StoreRating[] @@index([cityId, status]) @@index([partnerId]) @@map("stores") } model StoreMedia { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt storeId BigInt @map("store_id") @db.UnsignedBigInt mediaType String @map("media_type") @db.VarChar(16) url String @db.VarChar(512) sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) store Store @relation(fields: [storeId], references: [id], onDelete: Cascade) @@index([storeId]) @@map("store_media") } model StoreAudit { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt storeId BigInt @map("store_id") @db.UnsignedBigInt auditType StoreAuditType @map("audit_type") status StoreAuditStatus @default(PENDING) submitData Json? @map("submit_data") rejectReason String? @map("reject_reason") @db.VarChar(512) reviewerId BigInt? @map("reviewer_id") @db.UnsignedBigInt submittedAt DateTime @default(now()) @map("submitted_at") @db.DateTime(3) reviewedAt DateTime? @map("reviewed_at") @db.DateTime(3) store Store @relation(fields: [storeId], references: [id], onDelete: Cascade) reviewer HqAccount? @relation(fields: [reviewerId], references: [id], onDelete: SetNull) @@index([storeId, status]) @@index([reviewerId]) @@map("store_audits") } // ─── 推广码 ─────────────────────────────────────────── model PromoCode { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt code String @unique @db.VarChar(32) name String @db.VarChar(128) status PromoCodeStatus @default(ACTIVE) wxQrcodeUrl String? @map("wx_qrcode_url") @db.VarChar(512) scanCount Int @default(0) @map("scan_count") orderCount Int @default(0) @map("order_count") createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) orders Order[] attributions UserPromoAttribution[] @@map("promo_codes") } model UserPromoAttribution { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt userId BigInt @unique @map("user_id") @db.UnsignedBigInt promoCodeId BigInt @map("promo_code_id") @db.UnsignedBigInt channelName String @map("channel_name") @db.VarChar(128) firstTouchAt DateTime @map("first_touch_at") @db.DateTime(3) user User @relation(fields: [userId], references: [id], onDelete: Cascade) promoCode PromoCode @relation(fields: [promoCodeId], references: [id], onDelete: Restrict) @@index([promoCodeId]) @@map("user_promo_attributions") } // ─── 订单与支付 ───────────────────────────────────── model Order { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt orderNo String @unique @map("order_no") @db.VarChar(32) orderType OrderType @default(NORMAL) @map("order_type") userId BigInt @map("user_id") @db.UnsignedBigInt cityId BigInt @map("city_id") @db.UnsignedBigInt status OrderStatus @default(PENDING_PAY) deliveryType DeliveryType @map("delivery_type") originOrderId BigInt? @map("origin_order_id") @db.UnsignedBigInt promoCodeId BigInt? @map("promo_code_id") @db.UnsignedBigInt channelSource String? @map("channel_source") @db.VarChar(128) receiverName String @map("receiver_name") @db.VarChar(32) receiverPhone String @map("receiver_phone") @db.VarChar(20) receiverAddress String @map("receiver_address") @db.Text receiverProvince String @map("receiver_province") @db.VarChar(32) receiverCity String @map("receiver_city") @db.VarChar(32) receiverDistrict String @map("receiver_district") @db.VarChar(32) clientIp String? @map("client_ip") @db.VarChar(45) ipProvince String? @map("ip_province") @db.VarChar(32) ipCity String? @map("ip_city") @db.VarChar(32) ipDistrict String? @map("ip_district") @db.VarChar(32) gpsProvince String? @map("gps_province") @db.VarChar(32) gpsCity String? @map("gps_city") @db.VarChar(32) gpsDistrict String? @map("gps_district") @db.VarChar(32) gpsLatitude Decimal? @map("gps_latitude") @db.Decimal(10, 7) gpsLongitude Decimal? @map("gps_longitude") @db.Decimal(10, 7) gpsAddress String? @map("gps_address") @db.VarChar(256) productAmount Decimal @map("product_amount") @db.Decimal(10, 2) freightAmount Decimal @default(0) @map("freight_amount") @db.Decimal(10, 2) freightPayType String? @map("freight_pay_type") @db.VarChar(8) payAmount Decimal @map("pay_amount") @db.Decimal(10, 2) benefitAmount Decimal @default(0) @map("benefit_amount") @db.Decimal(10, 2) paidAt DateTime? @map("paid_at") @db.DateTime(3) shippedAt DateTime? @map("shipped_at") @db.DateTime(3) completedAt DateTime? @map("completed_at") @db.DateTime(3) cancelledAt DateTime? @map("cancelled_at") @db.DateTime(3) payExpireAt DateTime? @map("pay_expire_at") @db.DateTime(3) remark String? @db.VarChar(512) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) user User @relation(fields: [userId], references: [id], onDelete: Restrict) city City @relation(fields: [cityId], references: [id], onDelete: Restrict) originOrder Order? @relation("ReshipmentOrder", fields: [originOrderId], references: [id], onDelete: SetNull) reshipments Order[] @relation("ReshipmentOrder") promoCode PromoCode? @relation(fields: [promoCodeId], references: [id], onDelete: SetNull) items OrderItem[] statusLogs OrderStatusLog[] delivery OrderDelivery? payment Payment? refunds Refund[] benefitCoupons BenefitCoupon[] intercepts DeliveryIntercept[] afterSales AfterSaleTicket[] commissions OrderCommission[] @@index([userId, status]) @@index([cityId, createdAt]) @@index([receiverPhone]) @@index([originOrderId]) @@index([ipCity]) @@index([gpsCity]) @@map("orders") } model OrderItem { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt orderId BigInt @map("order_id") @db.UnsignedBigInt productId BigInt @map("product_id") @db.UnsignedBigInt productName String @map("product_name") @db.VarChar(128) productSpec String @map("product_spec") @db.VarChar(128) productImage String @map("product_image") @db.VarChar(512) unitPrice Decimal @map("unit_price") @db.Decimal(10, 2) quantity Int subtotal Decimal @db.Decimal(10, 2) order Order @relation(fields: [orderId], references: [id], onDelete: Cascade) product Product @relation(fields: [productId], references: [id], onDelete: Restrict) @@index([orderId]) @@map("order_items") } model OrderStatusLog { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt orderId BigInt @map("order_id") @db.UnsignedBigInt fromStatus String? @map("from_status") @db.VarChar(32) toStatus String @map("to_status") @db.VarChar(32) operator String? @db.VarChar(64) remark String? @db.VarChar(256) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) order Order @relation(fields: [orderId], references: [id], onDelete: Cascade) @@index([orderId]) @@map("order_status_logs") } model OrderDelivery { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt orderId BigInt @unique @map("order_id") @db.UnsignedBigInt provider String @db.VarChar(16) providerOrderNo String? @map("provider_order_no") @db.VarChar(64) trackingNo String? @map("tracking_no") @db.VarChar(64) outWarehouseAt DateTime? @map("out_warehouse_at") @db.DateTime(3) shippingAt DateTime? @map("shipping_at") @db.DateTime(3) deliveredAt DateTime? @map("delivered_at") @db.DateTime(3) rawPayload Json? @map("raw_payload") updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) order Order @relation(fields: [orderId], references: [id], onDelete: Cascade) @@map("order_deliveries") } model Payment { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt orderId BigInt @unique @map("order_id") @db.UnsignedBigInt paymentNo String @unique @map("payment_no") @db.VarChar(32) amount Decimal @db.Decimal(10, 2) status PaymentStatus @default(PENDING) wxTransactionId String? @unique @map("wx_transaction_id") @db.VarChar(64) wxPrepayId String? @map("wx_prepay_id") @db.VarChar(64) paidAt DateTime? @map("paid_at") @db.DateTime(3) notifyPayload Json? @map("notify_payload") createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) order Order @relation(fields: [orderId], references: [id], onDelete: Cascade) @@map("payments") } model Refund { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt orderId BigInt @map("order_id") @db.UnsignedBigInt refundNo String @unique @map("refund_no") @db.VarChar(32) amount Decimal @db.Decimal(10, 2) reason String? @db.VarChar(512) status RefundStatus @default(PENDING) wxRefundId String? @map("wx_refund_id") @db.VarChar(64) benefitAdjust Decimal? @map("benefit_adjust") @db.Decimal(10, 2) operatorId BigInt? @map("operator_id") @db.UnsignedBigInt approvedAt DateTime? @map("approved_at") @db.DateTime(3) refundedAt DateTime? @map("refunded_at") @db.DateTime(3) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) order Order @relation(fields: [orderId], references: [id], onDelete: Restrict) operator HqAccount? @relation(fields: [operatorId], references: [id], onDelete: SetNull) @@index([orderId]) @@index([operatorId]) @@map("refunds") } model DeliveryIntercept { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt orderId BigInt @map("order_id") @db.UnsignedBigInt status InterceptStatus @default(PENDING) oldAddress Json @map("old_address") newAddress Json @map("new_address") partnerId BigInt? @map("partner_id") @db.UnsignedBigInt interceptAt DateTime? @map("intercept_at") @db.DateTime(3) redeliveryOrderId BigInt? @map("redelivery_order_id") @db.UnsignedBigInt remark String? @db.VarChar(512) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) order Order @relation(fields: [orderId], references: [id], onDelete: Restrict) @@index([orderId, status]) @@map("delivery_intercepts") } // ─── 好客权益 ───────────────────────────────────────── model BenefitCoupon { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt couponNo String @unique @map("coupon_no") @db.VarChar(32) userId BigInt @map("user_id") @db.UnsignedBigInt orderId BigInt @map("order_id") @db.UnsignedBigInt totalAmount Decimal @map("total_amount") @db.Decimal(10, 2) usedAmount Decimal @default(0) @map("used_amount") @db.Decimal(10, 2) balance Decimal @db.Decimal(10, 2) status BenefitCouponStatus @default(ACTIVE) sourceProduct String @map("source_product") @db.VarChar(128) version Int @default(0) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) user User @relation(fields: [userId], references: [id], onDelete: Restrict) order Order @relation(fields: [orderId], references: [id], onDelete: Restrict) ledgers BenefitLedger[] redeemRecords RedeemRecord[] redeemTokens RedeemToken[] @@index([userId, status]) @@index([orderId]) @@map("benefit_coupons") } model BenefitLedger { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt userId BigInt @map("user_id") @db.UnsignedBigInt couponId BigInt @map("coupon_id") @db.UnsignedBigInt type BenefitLedgerType amount Decimal @db.Decimal(10, 2) balanceAfter Decimal @map("balance_after") @db.Decimal(10, 2) refType String? @map("ref_type") @db.VarChar(32) refId BigInt? @map("ref_id") @db.UnsignedBigInt remark String? @db.VarChar(256) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) user User @relation(fields: [userId], references: [id], onDelete: Restrict) coupon BenefitCoupon @relation(fields: [couponId], references: [id], onDelete: Restrict) @@index([userId, createdAt]) @@index([couponId]) @@map("benefit_ledgers") } // ─── 核销 ───────────────────────────────────────────── model RedeemToken { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt token String @unique @db.VarChar(64) userId BigInt @map("user_id") @db.UnsignedBigInt couponId BigInt @map("coupon_id") @db.UnsignedBigInt storeId BigInt? @map("store_id") @db.UnsignedBigInt amount Decimal @db.Decimal(10, 2) status RedeemTokenStatus @default(ACTIVE) expireAt DateTime @map("expire_at") @db.DateTime(3) usedAt DateTime? @map("used_at") @db.DateTime(3) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) coupon BenefitCoupon @relation(fields: [couponId], references: [id], onDelete: Restrict) @@index([couponId]) @@map("redeem_tokens") } model RedeemRecord { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt redeemNo String @unique @map("redeem_no") @db.VarChar(32) userId BigInt @map("user_id") @db.UnsignedBigInt couponId BigInt @map("coupon_id") @db.UnsignedBigInt storeId BigInt @map("store_id") @db.UnsignedBigInt amount Decimal @db.Decimal(10, 2) settleAmount Decimal @map("settle_amount") @db.Decimal(10, 2) tokenId BigInt? @map("token_id") @db.UnsignedBigInt createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) user User @relation(fields: [userId], references: [id], onDelete: Restrict) coupon BenefitCoupon @relation(fields: [couponId], references: [id], onDelete: Restrict) store Store @relation(fields: [storeId], references: [id], onDelete: Restrict) payout StorePayout? rating StoreRating? commissions OrderCommission[] @@index([storeId, createdAt]) @@index([userId]) @@map("redeem_records") } model StoreRating { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt redeemRecordId BigInt @unique @map("redeem_record_id") @db.UnsignedBigInt storeId BigInt @map("store_id") @db.UnsignedBigInt serviceScore Int @map("service_score") @db.TinyInt envScore Int @map("env_score") @db.TinyInt createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) redeemRecord RedeemRecord @relation(fields: [redeemRecordId], references: [id], onDelete: Cascade) store Store @relation(fields: [storeId], references: [id], onDelete: Restrict) @@map("store_ratings") } // ─── 结算 ───────────────────────────────────────────── model StorePayout { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt redeemRecordId BigInt @unique @map("redeem_record_id") @db.UnsignedBigInt storeId BigInt @map("store_id") @db.UnsignedBigInt redeemAmount Decimal @map("redeem_amount") @db.Decimal(10, 2) payoutAmount Decimal @map("payout_amount") @db.Decimal(10, 2) settlementRate Decimal @map("settlement_rate") @db.Decimal(5, 4) status StorePayoutStatus @default(PENDING) expectedPayAt DateTime @map("expected_pay_at") @db.DateTime(3) paidAt DateTime? @map("paid_at") @db.DateTime(3) batchNo String? @map("batch_no") @db.VarChar(32) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) redeemRecord RedeemRecord @relation(fields: [redeemRecordId], references: [id], onDelete: Restrict) store Store @relation(fields: [storeId], references: [id], onDelete: Restrict) @@index([storeId, status]) @@index([expectedPayAt]) @@map("store_payouts") } model OrderCommission { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt orderId BigInt? @map("order_id") @db.UnsignedBigInt redeemId BigInt? @map("redeem_id") @db.UnsignedBigInt partnerId BigInt @map("partner_id") @db.UnsignedBigInt commissionType String @map("commission_type") @db.VarChar(16) baseAmount Decimal @map("base_amount") @db.Decimal(10, 2) rate Decimal @db.Decimal(5, 4) amount Decimal @db.Decimal(10, 2) billId BigInt? @map("bill_id") @db.UnsignedBigInt createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) order Order? @relation(fields: [orderId], references: [id], onDelete: SetNull) redeem RedeemRecord? @relation(fields: [redeemId], references: [id], onDelete: SetNull) partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict) bill PartnerBill? @relation(fields: [billId], references: [id], onDelete: SetNull) @@index([partnerId]) @@index([billId]) @@map("order_commissions") } model PartnerBill { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt billNo String @unique @map("bill_no") @db.VarChar(32) partnerId BigInt @map("partner_id") @db.UnsignedBigInt periodStart DateTime @map("period_start") @db.DateTime(3) periodEnd DateTime @map("period_end") @db.DateTime(3) orderCommission Decimal @map("order_commission") @db.Decimal(10, 2) redeemCommission Decimal @map("redeem_commission") @db.Decimal(10, 2) totalAmount Decimal @map("total_amount") @db.Decimal(10, 2) status PartnerBillStatus @default(DRAFT) confirmedAt DateTime? @map("confirmed_at") @db.DateTime(3) paidAt DateTime? @map("paid_at") @db.DateTime(3) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict) commissions OrderCommission[] @@index([partnerId, status]) @@map("partner_bills") } model PartnerWithdrawal { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt partnerId BigInt @map("partner_id") @db.UnsignedBigInt amount Decimal @db.Decimal(10, 2) status WithdrawalStatus @default(PENDING) remark String? @db.VarChar(256) appliedAt DateTime @default(now()) @map("applied_at") @db.DateTime(3) processedAt DateTime? @map("processed_at") @db.DateTime(3) partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict) @@index([partnerId]) @@map("partner_withdrawals") } // ─── 埋点 ───────────────────────────────────────────── model EventLog { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt userId BigInt? @map("user_id") @db.UnsignedBigInt eventName String @map("event_name") @db.VarChar(64) params Json? clientApp String? @map("client_app") @db.VarChar(32) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) user User? @relation(fields: [userId], references: [id], onDelete: SetNull) @@index([eventName, createdAt]) @@index([userId]) @@map("event_logs") } // ─── 运营与售后 ───────────────────────────────────── model AfterSaleTicket { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt ticketNo String @unique @map("ticket_no") @db.VarChar(32) type AfterSaleType status AfterSaleStatus @default(PENDING) orderId BigInt @map("order_id") @db.UnsignedBigInt reshipmentOrderId BigInt? @map("reshipment_order_id") @db.UnsignedBigInt refundId BigInt? @map("refund_id") @db.UnsignedBigInt reason String? @db.VarChar(512) operatorId BigInt? @map("operator_id") @db.UnsignedBigInt createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) completedAt DateTime? @map("completed_at") @db.DateTime(3) order Order @relation(fields: [orderId], references: [id], onDelete: Restrict) operator HqAccount? @relation(fields: [operatorId], references: [id], onDelete: SetNull) @@index([orderId]) @@index([status, type]) @@index([operatorId]) @@map("after_sale_tickets") } model Alert { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt alertType AlertType @map("alert_type") status AlertStatus @default(OPEN) refType String @map("ref_type") @db.VarChar(32) refId BigInt @map("ref_id") @db.UnsignedBigInt title String @db.VarChar(128) content String @db.Text resolvedAt DateTime? @map("resolved_at") @db.DateTime(3) createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) @@index([status, alertType]) @@map("alerts") } model OperationLog { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt hqAccountId BigInt @map("hq_account_id") @db.UnsignedBigInt action String @db.VarChar(64) refType String? @map("ref_type") @db.VarChar(32) refId BigInt? @map("ref_id") @db.UnsignedBigInt detail Json? createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) hqAccount HqAccount @relation(fields: [hqAccountId], references: [id], onDelete: Restrict) @@index([hqAccountId, createdAt]) @@map("operation_logs") } model SmsLog { id BigInt @id @default(autoincrement()) @db.UnsignedBigInt phone String @db.VarChar(20) template String @db.VarChar(64) content String @db.Text status String @db.VarChar(16) refType String? @map("ref_type") @db.VarChar(32) refId BigInt? @map("ref_id") @db.UnsignedBigInt createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) @@index([phone, createdAt]) @@map("sms_logs") }