Files
WanderQ-MiniAPP2.0/apps/api/prisma/schema.prisma

397 lines
12 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model AdminUser {
id String @id @default(uuid())
email String @unique
name String
passwordHash String
role String @default("admin")
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
auditLogs AuditLog[]
assignedLeads Lead[] @relation("LeadAssignee")
}
model MediaAsset {
id String @id @default(uuid())
url String @unique
name String?
mimeType String?
sizeBytes Int?
group String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model HeroSlide {
id String @id @default(uuid())
title String
kicker String?
actionLabel String?
image String
targetType String?
targetValue String?
sortOrder Int @default(0)
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Destination {
id String @id @default(uuid())
name String @unique
slug String @unique
region String?
image String?
isHot Boolean @default(false)
sortOrder Int @default(0)
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
aliases DestinationAlias[]
products Product[]
}
model DestinationAlias {
id String @id @default(uuid())
alias String
destinationId String
destination Destination @relation(fields: [destinationId], references: [id], onDelete: Cascade)
@@unique([alias, destinationId])
}
model ThemeCard {
id String @id @default(uuid())
label String
image String
targetType String?
targetValue String?
sortOrder Int @default(0)
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model CtaBanner {
id String @id @default(uuid())
alt String
image String
targetType String
targetValue String?
sortOrder Int @default(0)
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model DestinationPageConfig {
id String @id @default("destination-home")
recommendedProductIds Json
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model SearchPageConfig {
id String @id @default("search-page")
title String
subtitle String
placeholder String
modules Json @default("{}")
popularKeywords Json
groups Json
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model HomeModule {
id String @id
label String
templateType String @default("explore")
content Json?
publishedConfig Json?
sortOrder Int @default(0)
isActive Boolean @default(true)
isSystem Boolean @default(false)
isDeleted Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Product {
id String @id @default(uuid())
sourceId Int? @unique
title String
subtitle String?
destinationId String?
destination Destination? @relation(fields: [destinationId], references: [id], onDelete: SetNull)
priceAmount Int?
priceUnit String @default("起/人")
pricingTiers Json?
tags String[]
coverImage String?
summary String?
durationDays Int?
durationNights Int?
departureDates String[] @default([])
remainingSpots Int?
recommendation String?
keyFacts Json?
contentBlocks Json?
detailSections Json?
status String @default("published")
sortWeight Int @default(0)
publishedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
images ProductImage[]
campaignLinks CampaignProduct[]
leads Lead[]
favorites MiniProgramFavorite[]
histories MiniProgramHistory[]
orders Order[]
}
model ProductImage {
id String @id @default(uuid())
productId String
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
url String
alt String?
sortOrder Int @default(0)
}
model Campaign {
id String @id @default(uuid())
slug String @unique
title String
description String?
coverImage String?
status String @default("published")
startsAt DateTime?
endsAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
products CampaignProduct[]
}
model CampaignProduct {
campaignId String
productId String
sortOrder Int @default(0)
campaign Campaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
@@id([campaignId, productId])
}
model Lead {
id String @id @default(uuid())
requestType String @default("custom")
userId String?
user MiniProgramUser? @relation(fields: [userId], references: [id], onDelete: SetNull)
wecomContactId String?
wecomContact WecomExternalContact? @relation(fields: [wecomContactId], references: [id], onDelete: SetNull)
destination String?
phone String
contactName String?
wechat String?
travelDate DateTime?
peopleCount Int?
adultCount Int?
childCount Int?
roomType String?
plan String?
addOns String[] @default([])
budgetMin Int?
budgetMax Int?
note String?
sourcePage String?
sourceTheme String?
sourceProductId String?
sourceProduct Product? @relation(fields: [sourceProductId], references: [id], onDelete: SetNull)
adminNote String?
processed Boolean @default(false)
status String @default("new")
assignedUserId String?
assignedUser AdminUser? @relation("LeadAssignee", fields: [assignedUserId], references: [id], onDelete: SetNull)
followups LeadFollowup[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId, createdAt])
@@index([phone])
@@index([requestType, status, createdAt])
@@index([wecomContactId])
}
model LeadFollowup {
id String @id @default(uuid())
leadId String
lead Lead @relation(fields: [leadId], references: [id], onDelete: Cascade)
content String
nextAt DateTime?
createdAt DateTime @default(now())
}
model MiniProgramUser {
id String @id @default(uuid())
appId String
openId String
unionId String?
nickname String?
avatarUrl String?
phone String?
phoneAuthorizedAt DateTime?
note String?
source String @default("mini_program")
status String @default("active")
customerId String? @unique
customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
firstSeenAt DateTime @default(now())
lastLoginAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
leads Lead[]
favorites MiniProgramFavorite[]
histories MiniProgramHistory[]
wecomContacts WecomExternalContact[]
@@unique([appId, openId])
@@index([phone])
@@index([status, lastLoginAt])
}
model MiniProgramFavorite {
id String @id @default(uuid())
userId String
productId String
user MiniProgramUser @relation(fields: [userId], references: [id], onDelete: Cascade)
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
@@unique([userId, productId])
@@index([productId])
}
model MiniProgramHistory {
id String @id @default(uuid())
userId String
productId String
viewCount Int @default(1)
lastViewedAt DateTime @default(now())
user MiniProgramUser @relation(fields: [userId], references: [id], onDelete: Cascade)
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
@@unique([userId, productId])
@@index([userId, lastViewedAt])
}
model Customer {
id String @id @default(uuid())
phone String? @unique
name String?
wechat String?
note String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
miniProgramUser MiniProgramUser?
orders Order[]
wecomContacts WecomExternalContact[]
}
model WecomContactWay {
id String @id @default(uuid())
configId String @unique
type Int @default(1)
scene Int @default(1)
style Int @default(1)
remark String?
state String?
wecomUserId String?
enabled Boolean @default(true)
lastSyncedAt DateTime?
lastSyncError String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
externalContacts WecomExternalContact[]
}
model WecomExternalContact {
id String @id @default(uuid())
externalUserId String
wecomUserId String
contactWayId String?
contactWay WecomContactWay? @relation(fields: [contactWayId], references: [id], onDelete: SetNull)
miniProgramUserId String?
miniProgramUser MiniProgramUser? @relation(fields: [miniProgramUserId], references: [id], onDelete: SetNull)
customerId String?
customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
name String?
avatarUrl String?
type Int?
gender Int?
unionId String?
corpName String?
position String?
remark String?
description String?
profile Json?
status String @default("active")
addedAt DateTime?
lastEventAt DateTime @default(now())
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
leads Lead[]
@@unique([externalUserId, wecomUserId])
@@index([status, lastEventAt])
@@index([miniProgramUserId])
@@index([customerId])
@@index([contactWayId])
}
model Order {
id String @id @default(uuid())
customerId String?
customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
productId String?
product Product? @relation(fields: [productId], references: [id], onDelete: SetNull)
status String @default("draft")
travelDate DateTime?
amount Int?
note String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model SiteVersion {
id String @id @default(uuid())
title String
status String @default("draft")
snapshot Json
publishedAt DateTime?
createdAt DateTime @default(now())
}
model AuditLog {
id String @id @default(uuid())
actorId String?
actor AdminUser? @relation(fields: [actorId], references: [id], onDelete: SetNull)
action String
entity String
entityId String?
before Json?
after Json?
createdAt DateTime @default(now())
}