1949 lines
82 KiB
Plaintext
1949 lines
82 KiB
Plaintext
// 杜康好客 · V3.1 数据模型(由 init_v3.sql 生成)
|
||
// 激活方式:确认后替换 schema.prisma,执行 prisma migrate / db push + seed-v31
|
||
// 旧版备份:schema.legacy-v21.prisma
|
||
|
||
generator client {
|
||
provider = "prisma-client-js"
|
||
}
|
||
|
||
datasource db {
|
||
provider = "mysql"
|
||
url = env("DATABASE_URL")
|
||
}
|
||
|
||
// ─── 枚举(与 init_v3.sql COMMENT 对齐)────────────────
|
||
|
||
enum ClientApp {
|
||
USER_MINI
|
||
USER_H5
|
||
PARTNER_MINI
|
||
PARTNER_H5
|
||
HQ_MINI
|
||
HQ_WEB
|
||
SHOP_H5
|
||
}
|
||
|
||
enum ResourceOwnerType {
|
||
PRODUCT
|
||
STORE
|
||
PARTNER
|
||
USER
|
||
ORDER
|
||
PROMO
|
||
HQ
|
||
}
|
||
|
||
enum ResourceBizType {
|
||
COVER
|
||
ENV
|
||
CONTRACT
|
||
CAROUSEL
|
||
DETAIL
|
||
AVATAR
|
||
QRCODE
|
||
SIGN_PHOTO
|
||
VIDEO
|
||
REDEEM_PENDING_PHOTO
|
||
}
|
||
|
||
enum RedeemPendingStatus {
|
||
PENDING
|
||
COMPLETED
|
||
REJECTED
|
||
}
|
||
|
||
/// Redeem channel: SCAN or PHONE
|
||
enum RedeemChannel {
|
||
SCAN
|
||
PHONE
|
||
}
|
||
|
||
enum ResourceMediaType {
|
||
IMAGE
|
||
VIDEO
|
||
FILE
|
||
}
|
||
|
||
enum ResourceStatus {
|
||
ACTIVE
|
||
DELETED
|
||
}
|
||
|
||
enum EventType {
|
||
STORE_AUDIT
|
||
ORDER_STATUS
|
||
BENEFIT_LEDGER
|
||
HQ_OPERATION
|
||
PROMO_TOUCH
|
||
TICKET_COLLAB
|
||
}
|
||
|
||
enum ActorType {
|
||
USER
|
||
STORE
|
||
PARTNER
|
||
HQ
|
||
SYSTEM
|
||
}
|
||
|
||
enum TicketType {
|
||
REFUND
|
||
RESHIPMENT
|
||
ALERT
|
||
DAMAGE_RETURN
|
||
RETURN_REFUND
|
||
PACKAGE_DISPUTE
|
||
}
|
||
|
||
enum SupportTicketType {
|
||
BUG
|
||
SUGGESTION
|
||
OTHER
|
||
}
|
||
|
||
enum SupportTicketStatus {
|
||
PENDING_REVIEW
|
||
REJECTED
|
||
DEVELOPING
|
||
TESTING
|
||
PASSED
|
||
PUBLISHED
|
||
}
|
||
|
||
enum SupportTicketPriority {
|
||
LOW
|
||
NORMAL
|
||
HIGH
|
||
URGENT
|
||
}
|
||
|
||
enum DevPlanTaskType {
|
||
BUG
|
||
REQUIREMENT
|
||
OPTIMIZATION
|
||
}
|
||
|
||
enum DevPlanTaskStatus {
|
||
TODO
|
||
DEVELOPED
|
||
RELEASED
|
||
}
|
||
|
||
enum DevPlanVersionStatus {
|
||
PENDING
|
||
IN_PROGRESS
|
||
TESTING
|
||
RELEASED
|
||
}
|
||
|
||
enum InvoiceTitleType {
|
||
PERSONAL
|
||
ENTERPRISE
|
||
}
|
||
|
||
enum InvoiceKind {
|
||
NORMAL
|
||
SPECIAL
|
||
}
|
||
|
||
enum InvoiceStatus {
|
||
PENDING
|
||
ISSUED
|
||
REJECTED
|
||
}
|
||
|
||
enum AromaType {
|
||
QINGXIANG
|
||
JIANGXIANG
|
||
NONGXIANG
|
||
}
|
||
|
||
enum ProductStatus {
|
||
DRAFT
|
||
ON_SALE
|
||
OFF_SALE
|
||
}
|
||
|
||
enum DetailTemplateStatus {
|
||
ACTIVE
|
||
DISABLED
|
||
}
|
||
|
||
enum PromoCodeStatus {
|
||
ACTIVE
|
||
DISABLED
|
||
}
|
||
|
||
enum PromoCodeScene {
|
||
ONLINE_LINK
|
||
OFFLINE_PICKUP
|
||
PARTNER_CHANNEL
|
||
EVENT
|
||
OTHER
|
||
}
|
||
|
||
enum PromoMetricEventType {
|
||
SCAN
|
||
ATTRIBUTION
|
||
REGISTER
|
||
ORDER
|
||
}
|
||
|
||
enum CityStatus {
|
||
PENDING
|
||
ACTIVE
|
||
PAUSED
|
||
}
|
||
|
||
enum CityPartnerScopeType {
|
||
CITY_WIDE
|
||
DISTRICT
|
||
}
|
||
|
||
enum CityPartnerStatus {
|
||
ACTIVE
|
||
PAUSED
|
||
}
|
||
|
||
enum WarehouseManagerType {
|
||
HQ
|
||
PARTNER
|
||
}
|
||
|
||
enum WarehouseStatus {
|
||
ACTIVE
|
||
PAUSED
|
||
}
|
||
|
||
enum FulfillmentProviderType {
|
||
API
|
||
MANUAL
|
||
}
|
||
|
||
enum FulfillmentProviderStatus {
|
||
ACTIVE
|
||
DISABLED
|
||
}
|
||
|
||
enum WarehouseFulfillmentMode {
|
||
API_AUTO
|
||
MANUAL
|
||
}
|
||
|
||
enum PartnerStaffRole {
|
||
PARTNER
|
||
INTERNAL
|
||
PROMOTER
|
||
}
|
||
|
||
enum StoreStaffRole {
|
||
MANAGER
|
||
CASHIER
|
||
}
|
||
|
||
enum AccountStatus {
|
||
ACTIVE
|
||
DISABLED
|
||
}
|
||
|
||
enum HqAdminRole {
|
||
SUPER_ADMIN
|
||
OPS
|
||
FINANCE
|
||
CUSTOMER_SERVICE
|
||
}
|
||
|
||
enum PartnerBillStatus {
|
||
PENDING_REVIEW
|
||
AWAITING_CONFIRM
|
||
UNPAID
|
||
PAID
|
||
REJECTED
|
||
}
|
||
|
||
// 酒厂/门店对账单打款状态
|
||
enum FinancePayStatus {
|
||
UNPAID
|
||
PAID
|
||
}
|
||
|
||
// 物流承运商结算方式:前期充值,后期挂账月结
|
||
enum LogisticsSettlementMethod {
|
||
PREPAID
|
||
MONTHLY_CREDIT
|
||
}
|
||
|
||
enum LogisticsPrepaidLedgerType {
|
||
RECHARGE
|
||
DEDUCT
|
||
ADJUST
|
||
}
|
||
|
||
enum StoreStatus {
|
||
OPEN
|
||
PAUSED
|
||
CLOSED
|
||
}
|
||
|
||
enum StoreAuditStatus {
|
||
PENDING
|
||
APPROVED
|
||
REJECTED
|
||
}
|
||
|
||
enum StorePackageChangeStatus {
|
||
PENDING
|
||
APPROVED
|
||
REJECTED
|
||
}
|
||
|
||
enum StorePackageSubmitterType {
|
||
PARTNER
|
||
SHOP
|
||
}
|
||
|
||
enum StoreInfoChangeStatus {
|
||
PENDING
|
||
APPROVED
|
||
REJECTED
|
||
}
|
||
|
||
enum StoreInfoChangeSubmitterType {
|
||
PARTNER
|
||
SHOP
|
||
HQ_DIRECT_ADMIN
|
||
}
|
||
|
||
enum UserSourceType {
|
||
ORGANIC
|
||
PROMO_CODE
|
||
SHARE_LINK
|
||
FRIEND_REFERRAL
|
||
OFFLINE_EVENT
|
||
PARTNER_PROXY
|
||
OTHER
|
||
}
|
||
|
||
enum OrderType {
|
||
NORMAL
|
||
RESHIPMENT
|
||
PROXY
|
||
}
|
||
|
||
enum OrderStatus {
|
||
PENDING_PAY
|
||
PENDING_SHIP
|
||
OUT_WAREHOUSE
|
||
SHIPPING
|
||
PENDING_RECEIVE
|
||
COMPLETED
|
||
CANCELLED
|
||
REFUNDING
|
||
REFUNDED
|
||
}
|
||
|
||
enum PayStatus {
|
||
UNPAID
|
||
PAYING
|
||
PAID
|
||
REFUNDING
|
||
REFUNDED
|
||
}
|
||
|
||
enum DeliveryType {
|
||
LOCAL
|
||
CROSS_CITY
|
||
ON_SITE_PICKUP
|
||
}
|
||
|
||
enum FreightPayType {
|
||
FREE
|
||
COD
|
||
}
|
||
|
||
enum DeliveryProvider {
|
||
XFX
|
||
LOGISTICS
|
||
MANUAL
|
||
}
|
||
|
||
enum BenefitCouponStatus {
|
||
ACTIVE
|
||
USED_UP
|
||
VOID
|
||
}
|
||
|
||
enum StorePayoutStatus {
|
||
PENDING
|
||
PAID
|
||
}
|
||
|
||
enum StoreWithdrawStatus {
|
||
PENDING_REVIEW
|
||
REJECTED
|
||
PAID
|
||
}
|
||
|
||
enum ThirdPartyProvider {
|
||
WECHAT_PAY
|
||
WECHAT_REFUND
|
||
WECHAT_AUTH
|
||
WECHAT_MAP
|
||
ALIYUN_OSS
|
||
XFX
|
||
SMS
|
||
LOGISTICS
|
||
}
|
||
|
||
enum ThirdPartyLogStatus {
|
||
PENDING
|
||
SUCCESS
|
||
FAILED
|
||
}
|
||
|
||
enum BenefitLedgerType {
|
||
GRANT
|
||
REDEEM
|
||
REFUND_VOID
|
||
ADJUST
|
||
}
|
||
|
||
// ─── COMMON ───────────────────────────────────────────
|
||
|
||
model SystemVersion {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
gitTag String? @map("git_tag") @db.VarChar(128)
|
||
commitId String @map("commit_id") @db.VarChar(64)
|
||
commitMessage String @map("commit_message") @db.VarChar(512)
|
||
branch String? @db.VarChar(64)
|
||
deployedBy String? @map("deployed_by") @db.VarChar(32)
|
||
deployedAt DateTime @default(now()) @map("deployed_at") @db.DateTime(3)
|
||
|
||
@@index([deployedAt])
|
||
@@map("system_version")
|
||
}
|
||
|
||
model SystemConfig {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
configKey String @unique @map("config_key") @db.VarChar(128)
|
||
value String @db.Text
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
@@map("system_config")
|
||
}
|
||
|
||
/// 企业微信智能机器人(HQ 可创建多实例,长连接)
|
||
model WecomBot {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
name String @db.VarChar(64)
|
||
role String @db.VarChar(32)
|
||
botId String @unique @map("bot_id") @db.VarChar(128)
|
||
secret String @db.VarChar(256)
|
||
avatarUrl String? @map("avatar_url") @db.VarChar(512)
|
||
welcome String? @db.VarChar(1024)
|
||
/// JSON 字符串数组,模块化权限
|
||
permissions String @db.Text
|
||
/// 可执行 support_ticket.review 的企微 userid 白名单
|
||
reviewSuperAdminWecomUserIds String? @map("review_super_admin_wecom_user_ids") @db.Text
|
||
aiEnabled Boolean @default(false) @map("ai_enabled")
|
||
llmConfigId BigInt? @map("llm_config_id") @db.UnsignedBigInt
|
||
knowledgeBaseId BigInt? @map("knowledge_base_id") @db.UnsignedBigInt
|
||
enabled Boolean @default(true)
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
llmConfig LlmApiConfig? @relation(fields: [llmConfigId], references: [id], onDelete: SetNull)
|
||
knowledgeBase KnowledgeBase? @relation(fields: [knowledgeBaseId], references: [id], onDelete: SetNull)
|
||
wecomBotLogs LogWecomBot[]
|
||
|
||
@@index([enabled, sortOrder])
|
||
@@index([llmConfigId])
|
||
@@index([knowledgeBaseId])
|
||
@@map("wecom_bot")
|
||
}
|
||
|
||
/// 企微机器人操作审计
|
||
model LogWecomBot {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
botId BigInt? @map("bot_id") @db.UnsignedBigInt
|
||
botKey String? @map("bot_key") @db.VarChar(64)
|
||
wecomUserId String @map("wecom_user_id") @db.VarChar(64)
|
||
action String @db.VarChar(64)
|
||
permission String? @db.VarChar(64)
|
||
inputSummary String? @map("input_summary") @db.VarChar(512)
|
||
success Boolean @default(true)
|
||
errorMessage String? @map("error_message") @db.VarChar(512)
|
||
latencyMs Int? @map("latency_ms")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
bot WecomBot? @relation(fields: [botId], references: [id], onDelete: SetNull)
|
||
|
||
@@index([botId, createdAt])
|
||
@@index([wecomUserId, createdAt])
|
||
@@index([action, createdAt])
|
||
@@map("log_wecom_bot")
|
||
}
|
||
|
||
/// 企微群机器人 Webhook 消息推送(v3.4.11)
|
||
model WecomMessagePush {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
name String @db.VarChar(64)
|
||
avatarUrl String? @map("avatar_url") @db.VarChar(512)
|
||
webhookUrl String @map("webhook_url") @db.VarChar(512)
|
||
enabled Boolean @default(true)
|
||
mentionWecomUserId String? @map("mention_wecom_user_id") @db.VarChar(64)
|
||
/// JSON 字符串数组,推送条件 key
|
||
pushConditions String @map("push_conditions") @db.Text
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
@@index([enabled, sortOrder])
|
||
@@map("wecom_message_push")
|
||
}
|
||
|
||
/// HQ 语言模型 API 配置(非超管仅可见/可开关自己创建的)
|
||
model LlmApiConfig {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
name String @db.VarChar(64)
|
||
provider String @db.VarChar(32)
|
||
baseUrl String @map("base_url") @db.VarChar(512)
|
||
apiKey String @map("api_key") @db.VarChar(512)
|
||
modelName String @map("model_name") @db.VarChar(128)
|
||
temperature Decimal? @db.Decimal(3, 2)
|
||
maxTokens Int? @map("max_tokens")
|
||
systemPrompt String? @map("system_prompt") @db.Text
|
||
enabled Boolean @default(true)
|
||
createdByHqAccountId BigInt @map("created_by_hq_account_id") @db.UnsignedBigInt
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
wecomBots WecomBot[]
|
||
|
||
@@index([createdByHqAccountId, enabled])
|
||
@@map("llm_api_config")
|
||
}
|
||
|
||
/// HQ 知识库
|
||
model KnowledgeBase {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
name String @db.VarChar(128)
|
||
description String? @db.VarChar(512)
|
||
enabled Boolean @default(true)
|
||
createdByHqAccountId BigInt @map("created_by_hq_account_id") @db.UnsignedBigInt
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
documents KnowledgeDocument[]
|
||
wecomBots WecomBot[]
|
||
|
||
@@index([createdByHqAccountId, enabled])
|
||
@@map("knowledge_base")
|
||
}
|
||
|
||
model KnowledgeDocument {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
knowledgeBaseId BigInt @map("knowledge_base_id") @db.UnsignedBigInt
|
||
title String @db.VarChar(256)
|
||
fileName String? @map("file_name") @db.VarChar(256)
|
||
fileUrl String? @map("file_url") @db.VarChar(1024)
|
||
mimeType String? @map("mime_type") @db.VarChar(128)
|
||
sizeBytes Int? @map("size_bytes")
|
||
contentText String? @map("content_text") @db.LongText
|
||
status String @default("READY") @db.VarChar(16)
|
||
errorMessage String? @map("error_message") @db.VarChar(512)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
knowledgeBase KnowledgeBase @relation(fields: [knowledgeBaseId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([knowledgeBaseId])
|
||
@@map("knowledge_document")
|
||
}
|
||
|
||
model MockSmsCode {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
phone String @db.VarChar(20)
|
||
scene String @db.VarChar(64)
|
||
code String @db.VarChar(8)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
@@index([createdAt])
|
||
@@map("mock_sms_code")
|
||
}
|
||
|
||
model CommonWxAppConfig {
|
||
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("common_wx_app_config")
|
||
}
|
||
|
||
model CommonResource {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
ownerType ResourceOwnerType @map("owner_type")
|
||
ownerId BigInt @map("owner_id") @db.UnsignedBigInt
|
||
bizType ResourceBizType @map("biz_type")
|
||
mediaType ResourceMediaType @default(IMAGE) @map("media_type")
|
||
ossBucket String @map("oss_bucket") @db.VarChar(64)
|
||
ossKey String @map("oss_key") @db.VarChar(256)
|
||
url String @db.VarChar(512)
|
||
fileName String? @map("file_name") @db.VarChar(128)
|
||
fileSize BigInt? @map("file_size") @db.UnsignedBigInt
|
||
mimeType String? @map("mime_type") @db.VarChar(64)
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
status ResourceStatus @default(ACTIVE)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
productCovers CommonProductItem[] @relation("ProductCover")
|
||
promoQrcodes CommonPromoCode[] @relation("PromoQrcode")
|
||
userAvatars User[] @relation("UserAvatar")
|
||
storeCovers Store[] @relation("StoreCover")
|
||
orderImages Order[] @relation("OrderProductImage")
|
||
deliveryPhotos OrderDelivery[] @relation("DeliverySignPhoto")
|
||
redeemPendingPhotos RedeemPendingRecord[] @relation("RedeemPendingPhoto")
|
||
|
||
@@index([ownerType, ownerId, bizType])
|
||
@@index([status])
|
||
@@map("common_resource")
|
||
}
|
||
|
||
model CommonEvent {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
eventType EventType @map("event_type")
|
||
refType String @map("ref_type") @db.VarChar(32)
|
||
refId BigInt @map("ref_id") @db.UnsignedBigInt
|
||
actorType ActorType? @map("actor_type")
|
||
actorId BigInt? @map("actor_id") @db.UnsignedBigInt
|
||
status String? @db.VarChar(32)
|
||
param1 String? @db.VarChar(128)
|
||
param1Desc String? @map("param1_desc") @db.VarChar(64)
|
||
param2 String? @db.VarChar(128)
|
||
param2Desc String? @map("param2_desc") @db.VarChar(64)
|
||
param3 String? @db.VarChar(128)
|
||
param3Desc String? @map("param3_desc") @db.VarChar(64)
|
||
amount1 Decimal? @db.Decimal(10, 2)
|
||
amount2 Decimal? @db.Decimal(10, 2)
|
||
remark String? @db.VarChar(512)
|
||
extraJson Json? @map("extra_json")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
@@index([refType, refId, eventType])
|
||
@@index([eventType, createdAt])
|
||
@@index([actorType, actorId])
|
||
@@map("common_event")
|
||
}
|
||
|
||
model CommonTicket {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
ticketNo String @unique @map("ticket_no") @db.VarChar(32)
|
||
ticketType TicketType @map("ticket_type")
|
||
status String @default("PENDING") @db.VarChar(32)
|
||
refType String @map("ref_type") @db.VarChar(32)
|
||
refId BigInt @map("ref_id") @db.UnsignedBigInt
|
||
operatorType ActorType? @map("operator_type")
|
||
operatorId BigInt? @map("operator_id") @db.UnsignedBigInt
|
||
param1 String? @db.VarChar(128)
|
||
param1Desc String? @map("param1_desc") @db.VarChar(64)
|
||
param2 String? @db.VarChar(128)
|
||
param2Desc String? @map("param2_desc") @db.VarChar(64)
|
||
param3 String? @db.VarChar(128)
|
||
param3Desc String? @map("param3_desc") @db.VarChar(64)
|
||
remark String? @db.VarChar(512)
|
||
extraJson Json? @map("extra_json")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
completedAt DateTime? @map("completed_at") @db.DateTime(3)
|
||
|
||
@@index([refType, refId])
|
||
@@index([ticketType, status])
|
||
@@map("common_ticket")
|
||
}
|
||
|
||
/// HQ 内部技术支持工单(BUG / 建议 / 其他)
|
||
model CommonSupportTicket {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
ticketNo String @unique @map("ticket_no") @db.VarChar(32)
|
||
ticketType SupportTicketType @map("ticket_type")
|
||
status SupportTicketStatus @default(PENDING_REVIEW)
|
||
priority SupportTicketPriority @default(NORMAL) @map("priority")
|
||
title String @db.VarChar(128)
|
||
content String? @db.Text
|
||
rejectReason String? @map("reject_reason") @db.VarChar(512)
|
||
creatorId BigInt @map("creator_id") @db.UnsignedBigInt
|
||
creatorName String @map("creator_name") @db.VarChar(64)
|
||
reviewerId BigInt? @map("reviewer_id") @db.UnsignedBigInt
|
||
reviewerName String? @map("reviewer_name") @db.VarChar(64)
|
||
reviewedAt DateTime? @map("reviewed_at") @db.DateTime(3)
|
||
remark String? @db.VarChar(512)
|
||
attachmentUrls Json? @map("attachment_urls")
|
||
releasedVersionNo String? @map("released_version_no") @db.VarChar(32)
|
||
publishedAt DateTime? @map("published_at") @db.DateTime(3)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
completedAt DateTime? @map("completed_at") @db.DateTime(3)
|
||
|
||
@@index([status, createdAt])
|
||
@@index([ticketType, status])
|
||
@@index([creatorId])
|
||
@@index([releasedVersionNo])
|
||
@@map("common_support_ticket")
|
||
|
||
devPlanTasks DevPlanTask[]
|
||
}
|
||
|
||
/// 开发计划 · 任务
|
||
model DevPlanTask {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
taskNo String @unique @map("task_no") @db.VarChar(32)
|
||
content String @db.Text
|
||
type DevPlanTaskType
|
||
status DevPlanTaskStatus @default(TODO)
|
||
creatorHqAccountId BigInt @map("creator_hq_account_id") @db.UnsignedBigInt
|
||
supportTicketId BigInt? @map("support_ticket_id") @db.UnsignedBigInt
|
||
lastDispatchedAt DateTime? @map("last_dispatched_at") @db.DateTime(3)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
completedAt DateTime? @map("completed_at") @db.DateTime(3)
|
||
|
||
supportTicket CommonSupportTicket? @relation(fields: [supportTicketId], references: [id], onDelete: SetNull)
|
||
versionLinks DevPlanVersionTask[]
|
||
|
||
@@index([status, createdAt])
|
||
@@index([supportTicketId])
|
||
@@map("dev_plan_task")
|
||
}
|
||
|
||
/// 开发计划 · 版本
|
||
model DevPlanVersion {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
versionNo String @unique @map("version_no") @db.VarChar(32)
|
||
content String? @db.Text
|
||
status DevPlanVersionStatus @default(PENDING)
|
||
/// @deprecated 不再使用,保留兼容旧数据
|
||
assigneeHqAccountId BigInt? @map("assignee_hq_account_id") @db.UnsignedBigInt
|
||
devStartedAt DateTime? @map("dev_started_at") @db.DateTime(3)
|
||
devCompletedAt DateTime? @map("dev_completed_at") @db.DateTime(3)
|
||
releasedAt DateTime? @map("released_at") @db.DateTime(3)
|
||
durationMinutes Int? @map("duration_minutes")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
taskLinks DevPlanVersionTask[]
|
||
|
||
@@index([status, createdAt])
|
||
@@map("dev_plan_version")
|
||
}
|
||
|
||
/// 开发计划 · 版本-任务多对多
|
||
model DevPlanVersionTask {
|
||
versionId BigInt @map("version_id") @db.UnsignedBigInt
|
||
taskId BigInt @map("task_id") @db.UnsignedBigInt
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
|
||
version DevPlanVersion @relation(fields: [versionId], references: [id], onDelete: Cascade)
|
||
task DevPlanTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
|
||
|
||
@@id([versionId, taskId])
|
||
@@index([taskId])
|
||
@@map("dev_plan_version_task")
|
||
}
|
||
|
||
/// 开发计划 · 全局设置(单例 id=1)
|
||
model DevPlanSettings {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
reviewAssistantLlmConfigId BigInt? @map("review_assistant_llm_config_id") @db.UnsignedBigInt
|
||
reviewAssistantKnowledgeBaseId BigInt? @map("review_assistant_knowledge_base_id") @db.UnsignedBigInt
|
||
reviewAssistantPrompt String? @map("review_assistant_prompt") @db.Text
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
@@map("dev_plan_settings")
|
||
}
|
||
|
||
/// 开发计划 · 任务派发审计
|
||
model DevPlanTaskDispatch {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
taskIdsJson String @map("task_ids_json") @db.Text
|
||
supplement String? @db.Text
|
||
operatorHqAccountId BigInt @map("operator_hq_account_id") @db.UnsignedBigInt
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
@@index([createdAt])
|
||
@@map("dev_plan_task_dispatch")
|
||
}
|
||
|
||
model CommonProductItem {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
skuCode String @unique @map("sku_code") @db.VarChar(32)
|
||
barcode69 String @unique @map("barcode_69") @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")
|
||
allowOnSitePickup Boolean @default(false) @map("allow_on_site_pickup")
|
||
/// 允许配送到址(同城线上购买)
|
||
allowOnlinePurchase Boolean @default(true) @map("allow_online_purchase")
|
||
/// 允许跨城配送(依附线上购买)
|
||
allowCrossCityDelivery Boolean @default(true) @map("allow_cross_city_delivery")
|
||
/// Online test: only listed phones can see/buy when enabled
|
||
visibilityWhitelistEnabled Boolean @default(false) @map("visibility_whitelist_enabled")
|
||
coverResourceId BigInt? @map("cover_resource_id") @db.UnsignedBigInt
|
||
detailContent Json? @map("detail_content")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
coverResource CommonResource? @relation("ProductCover", fields: [coverResourceId], references: [id], onDelete: SetNull)
|
||
orders Order[]
|
||
visibilityPhones CommonProductVisibilityPhone[]
|
||
|
||
@@index([status, aromaType])
|
||
@@map("common_product_item")
|
||
}
|
||
|
||
/// Product visibility whitelist phones (match by bound phone)
|
||
model CommonProductVisibilityPhone {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
productId BigInt @map("product_id") @db.UnsignedBigInt
|
||
phone String @db.VarChar(20)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
product CommonProductItem @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([productId, phone])
|
||
@@index([phone])
|
||
@@map("common_product_visibility_phone")
|
||
}
|
||
|
||
/// 全局测试白名单手机号(测试账号 + 限测商品/门店可见)
|
||
model CommonTestWhitelistPhone {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
phone String @unique @db.VarChar(20)
|
||
note String? @db.VarChar(256)
|
||
createdByHqId BigInt? @map("created_by_hq_id") @db.UnsignedBigInt
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
createdBy HqAccount? @relation("TestWhitelistCreatedBy", fields: [createdByHqId], references: [id], onDelete: SetNull)
|
||
|
||
@@index([createdAt])
|
||
@@map("common_test_whitelist_phone")
|
||
}
|
||
|
||
model CommonProductDetailTemplate {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
code String @unique @db.VarChar(32)
|
||
name String @db.VarChar(64)
|
||
description String? @db.VarChar(256)
|
||
aromaType AromaType? @map("aroma_type")
|
||
storyTitle String? @map("story_title") @db.VarChar(128)
|
||
storyText String? @map("story_text") @db.Text
|
||
features Json?
|
||
detailImageUrls Json? @map("detail_image_urls")
|
||
suggestedDetailImageCount Int @default(1) @map("suggested_detail_image_count")
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
status DetailTemplateStatus @default(ACTIVE)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
@@index([status, sortOrder])
|
||
@@map("common_product_detail_template")
|
||
}
|
||
|
||
model CommonStoreCategory {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
code String @unique @db.VarChar(32)
|
||
name String @db.VarChar(64)
|
||
sort Int @default(0)
|
||
parentId BigInt? @map("parent_id") @db.UnsignedBigInt
|
||
status String @default("ACTIVE") @db.VarChar(16)
|
||
parent CommonStoreCategory? @relation("StoreCategoryTree", fields: [parentId], references: [id], onDelete: Restrict)
|
||
children CommonStoreCategory[] @relation("StoreCategoryTree")
|
||
stores Store[]
|
||
|
||
@@index([parentId, sort])
|
||
@@index([status])
|
||
@@map("common_store_category")
|
||
}
|
||
|
||
model CommonPromoCode {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
code String @unique @db.VarChar(32)
|
||
name String @db.VarChar(128)
|
||
scene PromoCodeScene @default(ONLINE_LINK)
|
||
qrcodeId String @unique @map("qrcode_id") @db.VarChar(64)
|
||
status PromoCodeStatus @default(ACTIVE)
|
||
ownerUserId BigInt? @map("owner_user_id") @db.UnsignedBigInt
|
||
remark String? @db.VarChar(256)
|
||
qrcodeResourceId BigInt? @map("qrcode_resource_id") @db.UnsignedBigInt
|
||
scanCount Int @default(0) @map("scan_count")
|
||
orderCount Int @default(0) @map("order_count")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
ownerUser User? @relation("PromoOwnerUser", fields: [ownerUserId], references: [id], onDelete: SetNull)
|
||
qrcodeResource CommonResource? @relation("PromoQrcode", fields: [qrcodeResourceId], references: [id], onDelete: SetNull)
|
||
attributions UserPromoAttribution[]
|
||
orders Order[]
|
||
metricEvents LogPromoEvent[]
|
||
|
||
@@index([ownerUserId])
|
||
@@index([scene, status])
|
||
@@map("common_promo_code")
|
||
}
|
||
|
||
model CommonCity {
|
||
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)
|
||
localMinQty Int @default(2) @map("local_min_qty")
|
||
crossMinQty Int @default(6) @map("cross_min_qty")
|
||
maxPartnerCommissionRate Decimal @default(0.05) @map("max_partner_commission_rate") @db.Decimal(5, 4)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
warehouses CityWarehouse[]
|
||
partnerAccounts PartnerAccount[] @relation("PartnerAccountCity")
|
||
stores Store[]
|
||
orders Order[]
|
||
|
||
@@map("common_city")
|
||
}
|
||
|
||
model FulfillmentProvider {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
code String @unique @db.VarChar(32)
|
||
name String @db.VarChar(128)
|
||
type FulfillmentProviderType
|
||
status FulfillmentProviderStatus @default(ACTIVE)
|
||
configJson String? @map("config_json") @db.Text
|
||
capabilitiesJson String? @map("capabilities_json") @db.Text
|
||
bankAccountName String? @map("bank_account_name") @db.VarChar(64)
|
||
bankName String? @map("bank_name") @db.VarChar(64)
|
||
bankBranch String? @map("bank_branch") @db.VarChar(128)
|
||
bankAccountNo String? @map("bank_account_no") @db.VarChar(64)
|
||
settlementMethod LogisticsSettlementMethod @default(PREPAID) @map("settlement_method")
|
||
pricingRulesJson String? @map("pricing_rules_json") @db.Text
|
||
prepaidBalance Decimal @default(0) @map("prepaid_balance") @db.Decimal(12, 2)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
warehouses CityWarehouse[]
|
||
deliveries OrderDelivery[]
|
||
logisticsBills LogisticsBill[]
|
||
prepaidLedgers LogisticsPrepaidLedger[]
|
||
|
||
@@map("common_fulfillment_provider")
|
||
}
|
||
|
||
// 物流对账月账单(按承运商汇总)
|
||
model LogisticsBill {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
billNo String @unique @map("bill_no") @db.VarChar(32)
|
||
fulfillmentProviderId BigInt @map("fulfillment_provider_id") @db.UnsignedBigInt
|
||
periodStart DateTime @map("period_start") @db.DateTime(3)
|
||
periodEnd DateTime @map("period_end") @db.DateTime(3)
|
||
orderCount Int @default(0) @map("order_count")
|
||
bottleCount Int @default(0) @map("bottle_count")
|
||
logisticsAmount Decimal @map("logistics_amount") @db.Decimal(12, 2)
|
||
settlementMethod LogisticsSettlementMethod @map("settlement_method")
|
||
pricingSnapshotJson String? @map("pricing_snapshot_json") @db.Text
|
||
status FinancePayStatus @default(UNPAID)
|
||
paidAt DateTime? @map("paid_at") @db.DateTime(3)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
fulfillmentProvider FulfillmentProvider @relation(fields: [fulfillmentProviderId], references: [id], onDelete: Restrict)
|
||
items LogisticsBillItem[]
|
||
prepaidLedgers LogisticsPrepaidLedger[]
|
||
|
||
@@unique([fulfillmentProviderId, periodStart])
|
||
@@index([status, periodStart])
|
||
@@map("logistics_bill")
|
||
}
|
||
|
||
model LogisticsBillItem {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
logisticsBillId BigInt @map("logistics_bill_id") @db.UnsignedBigInt
|
||
orderId BigInt @map("order_id") @db.UnsignedBigInt
|
||
orderNo String @map("order_no") @db.VarChar(32)
|
||
quantity Int
|
||
logisticsAmount Decimal @map("logistics_amount") @db.Decimal(10, 2)
|
||
shippedAt DateTime @map("shipped_at") @db.DateTime(3)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
logisticsBill LogisticsBill @relation(fields: [logisticsBillId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([logisticsBillId, orderId])
|
||
@@index([logisticsBillId])
|
||
@@map("logistics_bill_item")
|
||
}
|
||
|
||
model LogisticsPrepaidLedger {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
fulfillmentProviderId BigInt @map("fulfillment_provider_id") @db.UnsignedBigInt
|
||
type LogisticsPrepaidLedgerType
|
||
amount Decimal @db.Decimal(12, 2)
|
||
balanceAfter Decimal @map("balance_after") @db.Decimal(12, 2)
|
||
logisticsBillId BigInt? @map("logistics_bill_id") @db.UnsignedBigInt
|
||
remark String? @db.VarChar(256)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
fulfillmentProvider FulfillmentProvider @relation(fields: [fulfillmentProviderId], references: [id], onDelete: Restrict)
|
||
logisticsBill LogisticsBill? @relation(fields: [logisticsBillId], references: [id], onDelete: SetNull)
|
||
|
||
@@index([fulfillmentProviderId, createdAt])
|
||
@@map("logistics_prepaid_ledger")
|
||
}
|
||
|
||
model CityWarehouse {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
cityId BigInt @map("city_id") @db.UnsignedBigInt
|
||
name String @db.VarChar(128)
|
||
address String @db.VarChar(256)
|
||
contactName String @map("contact_name") @db.VarChar(64)
|
||
contactPhone String @map("contact_phone") @db.VarChar(20)
|
||
managerType WarehouseManagerType @map("manager_type")
|
||
partnerAccountId BigInt? @map("partner_account_id") @db.UnsignedBigInt
|
||
status WarehouseStatus @default(ACTIVE)
|
||
fulfillmentMode WarehouseFulfillmentMode @default(MANUAL) @map("fulfillment_mode")
|
||
fulfillmentProviderId BigInt? @map("fulfillment_provider_id") @db.UnsignedBigInt
|
||
manualCarrierLabel String? @map("manual_carrier_label") @db.VarChar(64)
|
||
manualQueryUrlTemplate String? @map("manual_query_url_template") @db.VarChar(512)
|
||
lng Decimal? @db.Decimal(10, 7)
|
||
lat Decimal? @db.Decimal(10, 7)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
city CommonCity @relation(fields: [cityId], references: [id], onDelete: Cascade)
|
||
partnerAccount PartnerAccount? @relation("WarehouseManager", fields: [partnerAccountId], references: [id], onDelete: SetNull)
|
||
managedBy PartnerAccount? @relation("ManagedWarehouse")
|
||
fulfillmentProvider FulfillmentProvider? @relation(fields: [fulfillmentProviderId], references: [id], onDelete: SetNull)
|
||
fulfilledOrders Order[] @relation("OrderFulfillmentWarehouse")
|
||
|
||
@@index([cityId])
|
||
@@index([partnerAccountId])
|
||
@@index([fulfillmentProviderId])
|
||
@@map("common_city_warehouse")
|
||
}
|
||
|
||
// ─── PARTNER ──────────────────────────────────────────
|
||
|
||
model PartnerAccount {
|
||
id BigInt @id @default(autoincrement()) @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)
|
||
wxNickname String? @map("wx_nickname") @db.VarChar(64)
|
||
wxAvatarUrl String? @map("wx_avatar_url") @db.VarChar(512)
|
||
isPrimary Int @default(0) @map("is_primary") @db.TinyInt
|
||
parentAccountId BigInt? @map("parent_account_id") @db.UnsignedBigInt
|
||
staffRole PartnerStaffRole? @map("staff_role")
|
||
permissions Json?
|
||
status AccountStatus @default(ACTIVE)
|
||
lastLoginAt DateTime? @map("last_login_at") @db.DateTime(3)
|
||
cityId BigInt? @map("city_id") @db.UnsignedBigInt
|
||
scopeType CityPartnerScopeType? @map("scope_type")
|
||
districtCodes Json? @map("district_codes")
|
||
orderCommissionRate Decimal? @default(0) @map("order_commission_rate") @db.Decimal(5, 4)
|
||
redeemCommissionRate Decimal? @default(0.03) @map("redeem_commission_rate") @db.Decimal(5, 4)
|
||
bindingStatus CityPartnerStatus? @default(ACTIVE) @map("binding_status")
|
||
companyName String? @map("company_name") @db.VarChar(128)
|
||
address String? @db.VarChar(256)
|
||
contactPhone String? @map("contact_phone") @db.VarChar(20)
|
||
contractNo String? @map("contract_no") @db.VarChar(64)
|
||
contractSignedAt DateTime? @map("contract_signed_at") @db.DateTime(3)
|
||
contractExpireAt DateTime? @map("contract_expire_at") @db.DateTime(3)
|
||
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)
|
||
weeklyStoreTarget Int? @default(20) @map("weekly_store_target")
|
||
managedWarehouseId BigInt? @unique @map("managed_warehouse_id") @db.UnsignedBigInt
|
||
/// 测试合伙人账号
|
||
isTest Boolean @default(false) @map("is_test")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
city CommonCity? @relation("PartnerAccountCity", fields: [cityId], references: [id], onDelete: Restrict)
|
||
managedWarehouse CityWarehouse? @relation("ManagedWarehouse", fields: [managedWarehouseId], references: [id], onDelete: SetNull)
|
||
managedWarehouses CityWarehouse[] @relation("WarehouseManager")
|
||
parent PartnerAccount? @relation("PartnerAccountHierarchy", fields: [parentAccountId], references: [id], onDelete: SetNull)
|
||
children PartnerAccount[] @relation("PartnerAccountHierarchy")
|
||
stores Store[]
|
||
bills PartnerBill[]
|
||
|
||
@@index([cityId, scopeType])
|
||
@@index([cityId, isPrimary])
|
||
@@index([parentAccountId])
|
||
@@index([wxOpenId])
|
||
@@index([contactPhone])
|
||
@@index([isTest])
|
||
@@map("partner_account")
|
||
}
|
||
|
||
model PartnerBill {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
billNo String @unique @map("bill_no") @db.VarChar(32)
|
||
partnerAccountId BigInt @map("partner_account_id") @db.UnsignedBigInt
|
||
periodStart DateTime @map("period_start") @db.DateTime(3)
|
||
periodEnd DateTime @map("period_end") @db.DateTime(3)
|
||
orderCommission Decimal @default(0) @map("order_commission") @db.Decimal(10, 2)
|
||
redeemCommission Decimal @default(0) @map("redeem_commission") @db.Decimal(10, 2)
|
||
totalAmount Decimal @map("total_amount") @db.Decimal(10, 2)
|
||
status PartnerBillStatus @default(PENDING_REVIEW)
|
||
confirmedAt DateTime? @map("confirmed_at") @db.DateTime(3)
|
||
sentAt DateTime? @map("sent_at") @db.DateTime(3)
|
||
paidAt DateTime? @map("paid_at") @db.DateTime(3)
|
||
rejectReason String? @map("reject_reason") @db.VarChar(500)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
partnerAccount PartnerAccount @relation(fields: [partnerAccountId], references: [id], onDelete: Restrict)
|
||
|
||
@@index([partnerAccountId, status])
|
||
@@unique([partnerAccountId, periodStart])
|
||
@@map("partner_bill")
|
||
}
|
||
|
||
// ─── HQ ───────────────────────────────────────────────
|
||
|
||
model HqAccount {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
phone String @unique @db.VarChar(20)
|
||
loginName String? @unique @map("login_name") @db.VarChar(64)
|
||
passwordHash String? @map("password_hash") @db.VarChar(255)
|
||
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)
|
||
|
||
permissions HqAccountPermission[]
|
||
testWhitelistPhones CommonTestWhitelistPhone[] @relation("TestWhitelistCreatedBy")
|
||
|
||
@@map("hq_account")
|
||
}
|
||
|
||
model HqRolePermission {
|
||
adminRole HqAdminRole @map("admin_role")
|
||
permissionKey String @map("permission_key") @db.VarChar(64)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
@@id([adminRole, permissionKey])
|
||
@@map("hq_role_permission")
|
||
}
|
||
|
||
model HqAccountPermission {
|
||
hqAccountId BigInt @map("hq_account_id") @db.UnsignedBigInt
|
||
permissionKey String @map("permission_key") @db.VarChar(64)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
hqAccount HqAccount @relation(fields: [hqAccountId], references: [id], onDelete: Cascade)
|
||
|
||
@@id([hqAccountId, permissionKey])
|
||
@@map("hq_account_permission")
|
||
}
|
||
|
||
// ─── USER ─────────────────────────────────────────────
|
||
|
||
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)
|
||
avatarResourceId BigInt? @map("avatar_resource_id") @db.UnsignedBigInt
|
||
status Int @default(1) @db.TinyInt
|
||
/// 测试账号:命中全局测试白名单手机号
|
||
isTest Boolean @default(false) @map("is_test")
|
||
sourceType UserSourceType @default(ORGANIC) @map("source_type")
|
||
sourceRefId BigInt? @map("source_ref_id") @db.UnsignedBigInt
|
||
sourceLabel String? @map("source_label") @db.VarChar(128)
|
||
referrerUserId BigInt? @map("referrer_user_id") @db.UnsignedBigInt
|
||
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")
|
||
referrer User? @relation("UserReferrer", fields: [referrerUserId], references: [id], onDelete: SetNull)
|
||
referrers User[] @relation("UserReferrer")
|
||
avatar CommonResource? @relation("UserAvatar", fields: [avatarResourceId], references: [id], onDelete: SetNull)
|
||
addresses UserAddress[]
|
||
cityPreference UserCityPreference?
|
||
promoTouch UserPromoAttribution?
|
||
ownedPromoCodes CommonPromoCode[] @relation("PromoOwnerUser")
|
||
orders Order[]
|
||
invoices UserInvoice[]
|
||
invoiceTitles UserInvoiceTitle[]
|
||
benefitCoupons BenefitCoupon[]
|
||
redeemRecords RedeemRecord[]
|
||
redeemPendingRecords RedeemPendingRecord[]
|
||
|
||
@@index([sourceType, sourceRefId])
|
||
@@index([referrerUserId])
|
||
@@index([mergedIntoUserId])
|
||
@@index([wxOpenId])
|
||
@@index([isTest])
|
||
@@map("user_user")
|
||
}
|
||
|
||
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_address")
|
||
}
|
||
|
||
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_preference")
|
||
}
|
||
|
||
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 CommonPromoCode @relation(fields: [promoCodeId], references: [id], onDelete: Restrict)
|
||
|
||
@@index([promoCodeId])
|
||
@@map("user_promo_attribution")
|
||
}
|
||
|
||
// ─── STORE ────────────────────────────────────────────
|
||
|
||
model Store {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
cityId BigInt @map("city_id") @db.UnsignedBigInt
|
||
partnerAccountId BigInt @map("partner_account_id") @db.UnsignedBigInt
|
||
categoryId BigInt? @map("category_id") @db.UnsignedBigInt
|
||
name String @db.VarChar(128)
|
||
/// 门店主账号登录手机号(老板);与 StoreAccount.phone 对齐
|
||
phone String @db.VarChar(20)
|
||
/// 对外联系电话(店长等);C 端拨号展示用;空则回退 phone
|
||
contactPhone String? @map("contact_phone") @db.VarChar(20)
|
||
province String @db.VarChar(32)
|
||
cityName String @map("city_name") @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
|
||
// 好客权益券使用规则(C 端门店详情展示)
|
||
benefitUsageRule String? @map("benefit_usage_rule") @db.Text
|
||
coverResourceId BigInt? @map("cover_resource_id") @db.UnsignedBigInt
|
||
avgPrice Decimal? @map("avg_price") @db.Decimal(10, 2)
|
||
rating Decimal? @db.Decimal(3, 2)
|
||
tags Json?
|
||
status StoreStatus @default(PAUSED)
|
||
auditStatus StoreAuditStatus @default(APPROVED) @map("audit_status")
|
||
rejectReason String? @map("reject_reason") @db.VarChar(512)
|
||
auditedAt DateTime? @map("audited_at") @db.DateTime(3)
|
||
openTime String? @map("open_time") @db.VarChar(8)
|
||
closeTime String? @map("close_time") @db.VarChar(8)
|
||
openTime2 String? @map("open_time_2") @db.VarChar(8)
|
||
closeTime2 String? @map("close_time_2") @db.VarChar(8)
|
||
settlementRate Decimal @default(0.60) @map("settlement_rate") @db.Decimal(5, 4)
|
||
/// Online test: only listed phones can see store on C-end when enabled
|
||
visibilityWhitelistEnabled Boolean @default(false) @map("visibility_whitelist_enabled")
|
||
/// FIN-001:允许未出账手动提现的白名单门店
|
||
withdrawWhitelistEnabled Boolean @default(false) @map("withdraw_whitelist_enabled")
|
||
/// 测试门店:不计结算 / HQ 可手动标记
|
||
isTest Boolean @default(false) @map("is_test")
|
||
/// C 端 / 列表排序:数值越小越靠前
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
cityRef CommonCity @relation(fields: [cityId], references: [id], onDelete: Restrict)
|
||
partnerAccount PartnerAccount @relation(fields: [partnerAccountId], references: [id], onDelete: Restrict)
|
||
category CommonStoreCategory? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
|
||
coverResource CommonResource? @relation("StoreCover", fields: [coverResourceId], references: [id], onDelete: SetNull)
|
||
bindings StoreAccountStore[]
|
||
redeemRecords RedeemRecord[]
|
||
redeemPendingRecords RedeemPendingRecord[]
|
||
ratings StoreRating[]
|
||
payouts StorePayout[]
|
||
storeBills StoreBill[]
|
||
withdrawRequests StoreWithdrawRequest[]
|
||
visibilityPhones StoreVisibilityPhone[]
|
||
packages StorePackage[]
|
||
packageChangeRequests StorePackageChangeRequest[]
|
||
infoChangeRequests StoreInfoChangeRequest[]
|
||
|
||
@@index([cityId, status])
|
||
@@index([partnerAccountId])
|
||
@@index([auditStatus, createdAt])
|
||
@@index([isTest])
|
||
@@index([sortOrder])
|
||
@@map("store_store")
|
||
}
|
||
|
||
/// Store visibility whitelist phones (match by bound user phone)
|
||
model StoreVisibilityPhone {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
storeId BigInt @map("store_id") @db.UnsignedBigInt
|
||
phone String @db.VarChar(20)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([storeId, phone])
|
||
@@index([phone])
|
||
@@map("store_visibility_phone")
|
||
}
|
||
|
||
model StorePackage {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
storeId BigInt @map("store_id") @db.UnsignedBigInt
|
||
name String @db.VarChar(128)
|
||
price Decimal @db.Decimal(10, 2)
|
||
dishes String @db.Text
|
||
usableTime String? @map("usable_time") @db.VarChar(256)
|
||
otherNotes String? @map("other_notes") @db.VarChar(512)
|
||
imageUrl String? @map("image_url") @db.VarChar(512)
|
||
/// 套餐多图 URL 列表(JSON string[]),最多 20 张;imageUrl 同步为首图
|
||
imageUrls Json? @map("image_urls")
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
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([storeId, sortOrder])
|
||
@@map("store_package")
|
||
}
|
||
|
||
model StorePackageChangeRequest {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
storeId BigInt @map("store_id") @db.UnsignedBigInt
|
||
status StorePackageChangeStatus @default(PENDING)
|
||
packagesJson Json @map("packages_json")
|
||
submitterType StorePackageSubmitterType @map("submitter_type")
|
||
submitterId BigInt @map("submitter_id") @db.UnsignedBigInt
|
||
rejectReason String? @map("reject_reason") @db.VarChar(512)
|
||
reviewedAt DateTime? @map("reviewed_at") @db.DateTime(3)
|
||
reviewerId BigInt? @map("reviewer_id") @db.UnsignedBigInt
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([storeId, status])
|
||
@@index([status, createdAt])
|
||
@@map("store_package_change_request")
|
||
}
|
||
|
||
/// 用户发票抬头(C 端"我的 → 发票管理")
|
||
model UserInvoiceTitle {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
userId BigInt @map("user_id") @db.UnsignedBigInt
|
||
titleType InvoiceTitleType @map("title_type") // PERSONAL | ENTERPRISE
|
||
titleName String @map("title_name") @db.VarChar(128)
|
||
taxNo String? @map("tax_no") @db.VarChar(32)
|
||
email String? @db.VarChar(128)
|
||
phone String? @db.VarChar(20)
|
||
addressPhone String? @map("address_phone") @db.VarChar(256)
|
||
bankAccount String? @map("bank_account") @db.VarChar(256)
|
||
isDefault Boolean @default(false) @map("is_default")
|
||
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, isDefault])
|
||
@@map("user_invoice_title")
|
||
}
|
||
|
||
/// 门店基础信息变更请求(合伙人/门店端提交 → 总部审核)
|
||
model StoreInfoChangeRequest {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
storeId BigInt @map("store_id") @db.UnsignedBigInt
|
||
status StoreInfoChangeStatus @default(PENDING)
|
||
/// 变更前 Store 全量快照
|
||
liveSnapshot Json @map("live_snapshot")
|
||
/// 提交时希望变更的字段集合(白名单内)
|
||
proposedSnapshot Json @map("proposed_snapshot")
|
||
/// 变更的字段名数组
|
||
changedFields Json @map("changed_fields")
|
||
submitterType StoreInfoChangeSubmitterType @map("submitter_type")
|
||
submitterId BigInt @map("submitter_id") @db.UnsignedBigInt
|
||
rejectReason String? @map("reject_reason") @db.VarChar(512)
|
||
reviewedAt DateTime? @map("reviewed_at") @db.DateTime(3)
|
||
reviewerId BigInt? @map("reviewer_id") @db.UnsignedBigInt
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([storeId, status])
|
||
@@index([status, createdAt])
|
||
@@map("store_info_change_request")
|
||
}
|
||
|
||
model StoreAccount {
|
||
id BigInt @id @default(autoincrement()) @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(1) @map("is_primary") @db.TinyInt
|
||
parentAccountId BigInt? @map("parent_account_id") @db.UnsignedBigInt
|
||
staffRole StoreStaffRole? @map("staff_role")
|
||
permissions Json?
|
||
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)
|
||
status AccountStatus @default(ACTIVE)
|
||
lastLoginAt DateTime? @map("last_login_at") @db.DateTime(3)
|
||
/// 测试门店账号(商户)
|
||
isTest Boolean @default(false) @map("is_test")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
parentAccount StoreAccount? @relation("StoreAccountHierarchy", fields: [parentAccountId], references: [id], onDelete: Restrict)
|
||
childAccounts StoreAccount[] @relation("StoreAccountHierarchy")
|
||
bindings StoreAccountStore[]
|
||
redeemPendingRecords RedeemPendingRecord[]
|
||
withdrawRequests StoreWithdrawRequest[]
|
||
|
||
@@index([parentAccountId])
|
||
@@index([isTest])
|
||
@@map("store_account")
|
||
}
|
||
|
||
model StoreAccountStore {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
storeAccountId BigInt @map("store_account_id") @db.UnsignedBigInt
|
||
storeId BigInt @map("store_id") @db.UnsignedBigInt
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
storeAccount StoreAccount @relation(fields: [storeAccountId], references: [id], onDelete: Cascade)
|
||
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([storeAccountId, storeId])
|
||
@@index([storeId])
|
||
@@map("store_account_store")
|
||
}
|
||
|
||
// ─── ORDER ────────────────────────────────────────────
|
||
|
||
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)
|
||
payStatus PayStatus @default(UNPAID) @map("pay_status")
|
||
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)
|
||
productId BigInt @map("product_id") @db.UnsignedBigInt
|
||
barcode69 String @map("barcode_69") @db.VarChar(32)
|
||
productName String @map("product_name") @db.VarChar(128)
|
||
productSpec String @map("product_spec") @db.VarChar(128)
|
||
imageResourceId BigInt? @map("image_resource_id") @db.UnsignedBigInt
|
||
quantity Int
|
||
listUnitPrice Decimal @map("list_unit_price") @db.Decimal(10, 2)
|
||
listAmount Decimal @map("list_amount") @db.Decimal(10, 2)
|
||
discountAmount Decimal @default(0) @map("discount_amount") @db.Decimal(10, 2)
|
||
productAmount Decimal @map("product_amount") @db.Decimal(10, 2)
|
||
freightAmount Decimal @default(0) @map("freight_amount") @db.Decimal(10, 2)
|
||
freightPayType FreightPayType? @map("freight_pay_type")
|
||
payAmount Decimal @map("pay_amount") @db.Decimal(10, 2)
|
||
benefitAmount Decimal @default(0) @map("benefit_amount") @db.Decimal(10, 2)
|
||
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)
|
||
payExternalNo String? @map("pay_external_no") @db.VarChar(64)
|
||
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)
|
||
partnerAccountIdAtPay BigInt? @map("partner_account_id_at_pay") @db.UnsignedBigInt
|
||
orderCommissionRateAtPay Decimal? @map("order_commission_rate_at_pay") @db.Decimal(5, 4)
|
||
/// 代下单操作人(与佣金归属 partnerAccountIdAtPay 分离)
|
||
proxyPartnerAccountId BigInt? @map("proxy_partner_account_id") @db.UnsignedBigInt
|
||
proxyPartnerName String? @map("proxy_partner_name") @db.VarChar(64)
|
||
proxyPartnerPhone String? @map("proxy_partner_phone") @db.VarChar(20)
|
||
fulfillmentWarehouseId BigInt? @map("fulfillment_warehouse_id") @db.UnsignedBigInt
|
||
/// 大单等场景拦截自动推承运商,待总部确认后推单或自配送
|
||
fulfillmentHold Boolean @default(false) @map("fulfillment_hold")
|
||
fulfillmentHoldReason String? @map("fulfillment_hold_reason") @db.VarChar(64)
|
||
remark String? @db.VarChar(512)
|
||
/// 测试订单快照(下单时取自 User.isTest)
|
||
isTest Boolean @default(false) @map("is_test")
|
||
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 CommonCity @relation(fields: [cityId], references: [id], onDelete: Restrict)
|
||
fulfillmentWarehouse CityWarehouse? @relation("OrderFulfillmentWarehouse", fields: [fulfillmentWarehouseId], references: [id], onDelete: SetNull)
|
||
originOrder Order? @relation("OrderReshipment", fields: [originOrderId], references: [id], onDelete: SetNull)
|
||
reshipments Order[] @relation("OrderReshipment")
|
||
promoCode CommonPromoCode? @relation(fields: [promoCodeId], references: [id], onDelete: SetNull)
|
||
product CommonProductItem @relation(fields: [productId], references: [id], onDelete: Restrict)
|
||
imageResource CommonResource? @relation("OrderProductImage", fields: [imageResourceId], references: [id], onDelete: SetNull)
|
||
delivery OrderDelivery?
|
||
benefitCoupon BenefitCoupon?
|
||
invoices UserInvoice[]
|
||
|
||
@@index([userId, status])
|
||
@@index([cityId, createdAt])
|
||
@@index([productId])
|
||
@@index([barcode69])
|
||
@@index([payExternalNo])
|
||
@@index([ipCity])
|
||
@@index([gpsCity])
|
||
@@index([fulfillmentWarehouseId])
|
||
@@index([proxyPartnerAccountId])
|
||
@@index([isTest])
|
||
@@index([status, payExpireAt])
|
||
@@map("user_order")
|
||
}
|
||
|
||
model UserInvoice {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
invoiceNo String @unique @map("invoice_no") @db.VarChar(32)
|
||
orderId BigInt @map("order_id") @db.UnsignedBigInt
|
||
userId BigInt @map("user_id") @db.UnsignedBigInt
|
||
titleType InvoiceTitleType @map("title_type")
|
||
invoiceKind InvoiceKind @map("invoice_kind")
|
||
titleName String @map("title_name") @db.VarChar(128)
|
||
taxNo String? @map("tax_no") @db.VarChar(32)
|
||
addressPhone String? @map("address_phone") @db.VarChar(256)
|
||
bankAccount String? @map("bank_account") @db.VarChar(256)
|
||
email String @db.VarChar(128)
|
||
phone String @db.VarChar(20)
|
||
status InvoiceStatus @default(PENDING)
|
||
fileUrl String? @map("file_url") @db.VarChar(512)
|
||
resourceId BigInt? @map("resource_id") @db.UnsignedBigInt
|
||
issuedAt DateTime? @map("issued_at") @db.DateTime(3)
|
||
operatorId BigInt? @map("operator_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)
|
||
user User @relation(fields: [userId], references: [id], onDelete: Restrict)
|
||
|
||
@@index([userId, status])
|
||
@@index([orderId])
|
||
@@index([status, createdAt])
|
||
@@map("user_invoice")
|
||
}
|
||
|
||
model OrderDelivery {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
orderId BigInt @unique @map("order_id") @db.UnsignedBigInt
|
||
provider DeliveryProvider
|
||
fulfillmentProviderId BigInt? @map("fulfillment_provider_id") @db.UnsignedBigInt
|
||
providerOrderNo String? @map("provider_order_no") @db.VarChar(64)
|
||
trackingNo String? @map("tracking_no") @db.VarChar(64)
|
||
logisticsCompany String? @map("logistics_company") @db.VarChar(64)
|
||
manualQueryUrl String? @map("manual_query_url") @db.VarChar(512)
|
||
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)
|
||
signPhotoResourceId BigInt? @map("sign_photo_resource_id") @db.UnsignedBigInt
|
||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||
|
||
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
|
||
fulfillmentProvider FulfillmentProvider? @relation(fields: [fulfillmentProviderId], references: [id], onDelete: SetNull)
|
||
signPhotoResource CommonResource? @relation("DeliverySignPhoto", fields: [signPhotoResourceId], references: [id], onDelete: SetNull)
|
||
|
||
@@index([fulfillmentProviderId])
|
||
@@map("user_order_delivery")
|
||
}
|
||
|
||
// ─── BENEFIT & REDEEM ─────────────────────────────────
|
||
|
||
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? @unique @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)
|
||
redeemRecords RedeemRecord[]
|
||
redeemAllocations RedeemRecordAllocation[]
|
||
|
||
@@index([userId, status])
|
||
@@map("user_benefit_coupon")
|
||
}
|
||
|
||
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)
|
||
/// SCAN=qrcode, PHONE=phone
|
||
channel RedeemChannel @default(SCAN)
|
||
/// 测试核销快照(User.isTest || Store.isTest)
|
||
isTest Boolean @default(false) @map("is_test")
|
||
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)
|
||
rating StoreRating?
|
||
payout StorePayout?
|
||
pending RedeemPendingRecord?
|
||
allocations RedeemRecordAllocation[]
|
||
|
||
@@index([storeId, createdAt])
|
||
@@index([storeId, channel, createdAt])
|
||
@@index([isTest])
|
||
@@map("user_redeem_record")
|
||
}
|
||
|
||
// 核销单跨券 FIFO 分摊明细(含次券可追溯)
|
||
model RedeemRecordAllocation {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
redeemRecordId BigInt @map("redeem_record_id") @db.UnsignedBigInt
|
||
couponId BigInt @map("coupon_id") @db.UnsignedBigInt
|
||
amount Decimal @db.Decimal(10, 2)
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
redeemRecord RedeemRecord @relation(fields: [redeemRecordId], references: [id], onDelete: Cascade)
|
||
coupon BenefitCoupon @relation(fields: [couponId], references: [id], onDelete: Restrict)
|
||
|
||
@@unique([redeemRecordId, couponId])
|
||
@@index([couponId])
|
||
@@index([redeemRecordId])
|
||
@@map("user_redeem_record_allocation")
|
||
}
|
||
|
||
model RedeemPendingRecord {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
pendingNo String @unique @map("pending_no") @db.VarChar(32)
|
||
redeemToken String @map("redeem_token") @db.VarChar(64)
|
||
storeId BigInt @map("store_id") @db.UnsignedBigInt
|
||
storeAccountId BigInt @map("store_account_id") @db.UnsignedBigInt
|
||
userId BigInt @map("user_id") @db.UnsignedBigInt
|
||
amount Decimal @db.Decimal(10, 2)
|
||
redeemType String @default("DIRECT") @map("redeem_type") @db.VarChar(16)
|
||
allocationsJson Json @map("allocations_json")
|
||
photoResourceId BigInt @map("photo_resource_id") @db.UnsignedBigInt
|
||
failCount Int @default(5) @map("fail_count")
|
||
status RedeemPendingStatus @default(PENDING)
|
||
redeemRecordId BigInt? @unique @map("redeem_record_id") @db.UnsignedBigInt
|
||
processedAt DateTime? @map("processed_at") @db.DateTime(3)
|
||
processedByHqId BigInt? @map("processed_by_hq_id") @db.UnsignedBigInt
|
||
rejectReason String? @map("reject_reason") @db.VarChar(256)
|
||
remark String? @db.VarChar(256)
|
||
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: Restrict)
|
||
storeAccount StoreAccount @relation(fields: [storeAccountId], references: [id], onDelete: Restrict)
|
||
user User @relation(fields: [userId], references: [id], onDelete: Restrict)
|
||
photoResource CommonResource @relation("RedeemPendingPhoto", fields: [photoResourceId], references: [id], onDelete: Restrict)
|
||
redeemRecord RedeemRecord? @relation(fields: [redeemRecordId], references: [id], onDelete: SetNull)
|
||
|
||
@@index([storeId, status, createdAt])
|
||
@@index([redeemToken])
|
||
@@index([status, createdAt])
|
||
@@map("user_redeem_pending")
|
||
}
|
||
|
||
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("user_store_rating")
|
||
}
|
||
|
||
model StoreBill {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
billNo String @unique @map("bill_no") @db.VarChar(32)
|
||
storeId BigInt @map("store_id") @db.UnsignedBigInt
|
||
billDate DateTime @map("bill_date") @db.DateTime(3)
|
||
redeemCount Int @default(0) @map("redeem_count")
|
||
redeemAmount Decimal @map("redeem_amount") @db.Decimal(10, 2)
|
||
settlementRate Decimal @map("settlement_rate") @db.Decimal(5, 4)
|
||
payoutAmount Decimal @map("payout_amount") @db.Decimal(10, 2)
|
||
status FinancePayStatus @default(UNPAID)
|
||
paidAt DateTime? @map("paid_at") @db.DateTime(3)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
store Store @relation(fields: [storeId], references: [id], onDelete: Restrict)
|
||
payouts StorePayout[]
|
||
|
||
@@unique([storeId, billDate])
|
||
@@index([status, billDate])
|
||
@@map("store_bill")
|
||
}
|
||
|
||
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
|
||
storeBillId BigInt? @map("store_bill_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)
|
||
storeBill StoreBill? @relation(fields: [storeBillId], references: [id], onDelete: SetNull)
|
||
withdrawItem StoreWithdrawPayoutItem?
|
||
|
||
@@index([storeId, status])
|
||
@@index([storeBillId])
|
||
@@map("store_payout")
|
||
}
|
||
|
||
model StoreWithdrawRequest {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
withdrawNo String @unique @map("withdraw_no") @db.VarChar(32)
|
||
storeId BigInt @map("store_id") @db.UnsignedBigInt
|
||
storeAccountId BigInt @map("store_account_id") @db.UnsignedBigInt
|
||
amount Decimal @db.Decimal(10, 2)
|
||
payoutCount Int @default(0) @map("payout_count")
|
||
status StoreWithdrawStatus @default(PENDING_REVIEW)
|
||
rejectReason String? @map("reject_reason") @db.VarChar(512)
|
||
appliedAt DateTime @default(now()) @map("applied_at") @db.DateTime(3)
|
||
reviewedAt DateTime? @map("reviewed_at") @db.DateTime(3)
|
||
reviewedByHqId BigInt? @map("reviewed_by_hq_id") @db.UnsignedBigInt
|
||
paidAt DateTime? @map("paid_at") @db.DateTime(3)
|
||
paymentRef String? @map("payment_ref") @db.VarChar(128)
|
||
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: Restrict)
|
||
storeAccount StoreAccount @relation(fields: [storeAccountId], references: [id], onDelete: Restrict)
|
||
items StoreWithdrawPayoutItem[]
|
||
|
||
@@index([storeId, status])
|
||
@@index([status, appliedAt])
|
||
@@map("store_withdraw_request")
|
||
}
|
||
|
||
model StoreWithdrawPayoutItem {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
withdrawRequestId BigInt @map("withdraw_request_id") @db.UnsignedBigInt
|
||
storePayoutId BigInt @unique @map("store_payout_id") @db.UnsignedBigInt
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
withdrawRequest StoreWithdrawRequest @relation(fields: [withdrawRequestId], references: [id], onDelete: Cascade)
|
||
storePayout StorePayout @relation(fields: [storePayoutId], references: [id], onDelete: Restrict)
|
||
|
||
@@index([withdrawRequestId])
|
||
@@map("store_withdraw_payout_item")
|
||
}
|
||
|
||
model WineryBill {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
billNo String @unique @map("bill_no") @db.VarChar(32)
|
||
billDate DateTime @map("bill_date") @db.DateTime(3)
|
||
orderCount Int @default(0) @map("order_count")
|
||
orderAmount Decimal @map("order_amount") @db.Decimal(10, 2)
|
||
wineryRate Decimal @map("winery_rate") @db.Decimal(5, 4)
|
||
wineryAmount Decimal @map("winery_amount") @db.Decimal(10, 2)
|
||
status FinancePayStatus @default(UNPAID)
|
||
paidAt DateTime? @map("paid_at") @db.DateTime(3)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
items WineryBillItem[]
|
||
|
||
@@unique([billDate])
|
||
@@index([status, billDate])
|
||
@@map("winery_bill")
|
||
}
|
||
|
||
model WineryBillItem {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
wineryBillId BigInt @map("winery_bill_id") @db.UnsignedBigInt
|
||
orderId BigInt @unique @map("order_id") @db.UnsignedBigInt
|
||
orderNo String @map("order_no") @db.VarChar(32)
|
||
deliveryType String @map("delivery_type") @db.VarChar(16)
|
||
payAmount Decimal @map("pay_amount") @db.Decimal(10, 2)
|
||
wineryAmount Decimal @map("winery_amount") @db.Decimal(10, 2)
|
||
paidAt DateTime @map("paid_at") @db.DateTime(3)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
wineryBill WineryBill @relation(fields: [wineryBillId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([wineryBillId])
|
||
@@map("winery_bill_item")
|
||
}
|
||
|
||
// ─── LOG ──────────────────────────────────────────────
|
||
|
||
model LogThirdParty {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
provider ThirdPartyProvider
|
||
scene String @db.VarChar(64)
|
||
refType String? @map("ref_type") @db.VarChar(32)
|
||
refId BigInt? @map("ref_id") @db.UnsignedBigInt
|
||
requestUrl String? @map("request_url") @db.VarChar(512)
|
||
requestBody Json? @map("request_body")
|
||
responseBody Json? @map("response_body")
|
||
externalNo String? @map("external_no") @db.VarChar(128)
|
||
amount Decimal? @db.Decimal(10, 2)
|
||
status ThirdPartyLogStatus @default(PENDING)
|
||
errorMessage String? @map("error_message") @db.VarChar(512)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
@@index([refType, refId])
|
||
@@index([provider, scene, createdAt])
|
||
@@index([externalNo])
|
||
@@map("log_third_party")
|
||
}
|
||
|
||
model LogPromoEvent {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
promoCodeId BigInt @map("promo_code_id") @db.UnsignedBigInt
|
||
eventType PromoMetricEventType @map("event_type")
|
||
userId BigInt? @map("user_id") @db.UnsignedBigInt
|
||
orderId BigInt? @map("order_id") @db.UnsignedBigInt
|
||
sessionId String? @map("session_id") @db.VarChar(64)
|
||
clientIp String? @map("client_ip") @db.VarChar(45)
|
||
ipProvince String? @map("ip_province") @db.VarChar(32)
|
||
ipCity String? @map("ip_city") @db.VarChar(32)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
promoCode CommonPromoCode @relation(fields: [promoCodeId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([promoCodeId, eventType, createdAt])
|
||
@@index([createdAt])
|
||
@@map("log_promo_event")
|
||
}
|
||
|
||
model LogUserAnalytics {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
userId BigInt? @map("user_id") @db.UnsignedBigInt
|
||
sessionId String? @map("session_id") @db.VarChar(64)
|
||
eventName String @map("event_name") @db.VarChar(64)
|
||
clientApp ClientApp? @map("client_app")
|
||
pagePath String? @map("page_path") @db.VarChar(128)
|
||
refType String? @map("ref_type") @db.VarChar(32)
|
||
refId BigInt? @map("ref_id") @db.UnsignedBigInt
|
||
keyword String? @db.VarChar(128)
|
||
sourceType String? @map("source_type") @db.VarChar(32)
|
||
sourceRefId BigInt? @map("source_ref_id") @db.UnsignedBigInt
|
||
extraJson Json? @map("extra_json")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
@@index([userId, createdAt])
|
||
@@index([eventName, createdAt])
|
||
@@index([sessionId])
|
||
@@index([refType, refId])
|
||
@@map("log_user_analytics")
|
||
}
|
||
|
||
model LogStoreAnalytics {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
storeAccountId BigInt? @map("store_account_id") @db.UnsignedBigInt
|
||
storeId BigInt @map("store_id") @db.UnsignedBigInt
|
||
eventName String @map("event_name") @db.VarChar(64)
|
||
clientApp ClientApp? @map("client_app")
|
||
refType String? @map("ref_type") @db.VarChar(32)
|
||
refId BigInt? @map("ref_id") @db.UnsignedBigInt
|
||
extraJson Json? @map("extra_json")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
@@index([storeId, createdAt])
|
||
@@index([storeAccountId, createdAt])
|
||
@@index([eventName, createdAt])
|
||
@@map("log_store_analytics")
|
||
}
|
||
|
||
model LogPartnerAnalytics {
|
||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||
partnerAccountId BigInt @map("partner_account_id") @db.UnsignedBigInt
|
||
eventName String @map("event_name") @db.VarChar(64)
|
||
clientApp ClientApp? @map("client_app")
|
||
refType String? @map("ref_type") @db.VarChar(32)
|
||
refId BigInt? @map("ref_id") @db.UnsignedBigInt
|
||
extraJson Json? @map("extra_json")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||
|
||
@@index([partnerAccountId, createdAt])
|
||
@@index([eventName, createdAt])
|
||
@@map("log_partner_analytics")
|
||
}
|