1009 lines
39 KiB
Plaintext
1009 lines
39 KiB
Plaintext
// 杜康好客 · 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)
|
|
deviceKey String? @unique @map("device_key") @db.VarChar(36)
|
|
phone String? @unique @db.VarChar(20)
|
|
phoneVerifiedAt DateTime? @map("phone_verified_at") @db.DateTime(3)
|
|
mergedIntoUserId BigInt? @map("merged_into_user_id") @db.UnsignedBigInt
|
|
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)
|
|
|
|
mergedInto User? @relation("UserMerge", fields: [mergedIntoUserId], references: [id], onDelete: SetNull)
|
|
mergedFrom User[] @relation("UserMerge")
|
|
addresses UserAddress[]
|
|
cityPref UserCityPreference?
|
|
orders Order[]
|
|
benefitCoupons BenefitCoupon[]
|
|
benefitLedgers BenefitLedger[]
|
|
redeemRecords RedeemRecord[]
|
|
promoTouch UserPromoAttribution?
|
|
eventLogs EventLog[]
|
|
|
|
@@index([wxOpenId])
|
|
@@index([wxUnionId])
|
|
@@index([mergedIntoUserId])
|
|
@@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")
|
|
}
|