generator client { provider = "prisma-client-js" } datasource db { provider = "mysql" url = env("DATABASE_URL") } // ===== Enums ===== enum UserRole { MEMBER ADMIN } enum CardTypeCategory { TIMES DURATION TRIAL } enum MembershipStatus { ACTIVE EXPIRED USED_UP } enum TimeSlotStatus { OPEN FULL CLOSED } enum TimeSlotSource { TEMPLATE MANUAL } enum BookingStatus { PENDING_CONFIRMATION CONFIRMED CANCELLED COMPLETED NO_SHOW } enum OrderStatus { PENDING PAID REFUNDED } enum FlashSaleStatus { DRAFT ACTIVE ENDED } enum FlashSaleOrderStatus { RESERVED PAID EXPIRED } // ===== Models ===== model User { id String @id @default(uuid()) openid String @unique unionid String? phone String? nickname String @default("") avatarUrl String? @map("avatar_url") role UserRole @default(MEMBER) adminBookingSubscriptionCount Int @default(0) @map("admin_booking_subscription_count") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") memberships Membership[] bookings Booking[] orders Order[] flashSaleOrders FlashSaleOrder[] subscriptionMessageConsents SubscriptionMessageConsent[] @@map("users") } model SubscriptionMessageConsent { id String @id @default(uuid()) userId String @map("user_id") templateId String @map("template_id") scene String totalRequestCount Int @default(0) @map("total_request_count") acceptCount Int @default(0) @map("accept_count") rejectCount Int @default(0) @map("reject_count") banCount Int @default(0) @map("ban_count") filterCount Int @default(0) @map("filter_count") sentCount Int @default(0) @map("sent_count") lastResult String @map("last_result") lastRequestedAt DateTime @default(now()) @map("last_requested_at") lastSentAt DateTime? @map("last_sent_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id]) @@unique([userId, templateId, scene]) @@index([userId]) @@index([scene]) @@map("subscription_message_consents") } model CardType { id String @id @default(uuid()) name String type CardTypeCategory totalTimes Int? @map("total_times") durationDays Int @map("duration_days") price Decimal @db.Decimal(10, 0) originalPrice Decimal? @map("original_price") @db.Decimal(10, 0) description String? coverUrl String? @map("cover_url") isActive Boolean @default(true) @map("is_active") sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") memberships Membership[] orders Order[] flashSales FlashSale[] @@map("card_types") } model Membership { id String @id @default(uuid()) userId String @map("user_id") cardTypeId String @map("card_type_id") remainingTimes Int? @map("remaining_times") startDate DateTime @map("start_date") expireDate DateTime @map("expire_date") status MembershipStatus @default(ACTIVE) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id]) cardType CardType @relation(fields: [cardTypeId], references: [id]) bookings Booking[] @@index([userId]) @@index([status]) @@map("memberships") } model WeekTemplate { id String @id @default(uuid()) dayOfWeek Int @map("day_of_week") startTime String @map("start_time") endTime String @map("end_time") capacity Int @default(1) isActive Boolean @default(true) @map("is_active") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") timeSlots TimeSlot[] @@map("week_templates") } model TimeSlot { id String @id @default(uuid()) date DateTime @db.Date startTime String @map("start_time") endTime String @map("end_time") capacity Int @default(1) bookedCount Int @default(0) @map("booked_count") status TimeSlotStatus @default(OPEN) source TimeSlotSource @default(TEMPLATE) templateId String? @map("template_id") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") template WeekTemplate? @relation(fields: [templateId], references: [id]) bookings Booking[] @@unique([date, startTime, endTime]) @@index([date]) @@index([status]) @@map("time_slots") } model Booking { id String @id @default(uuid()) userId String @map("user_id") timeSlotId String @map("time_slot_id") membershipId String @map("membership_id") status BookingStatus @default(PENDING_CONFIRMATION) cancelledAt DateTime? @map("cancelled_at") confirmedAt DateTime? @map("confirmed_at") completedAt DateTime? @map("completed_at") operatorId String? @map("operator_id") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id]) timeSlot TimeSlot @relation(fields: [timeSlotId], references: [id]) membership Membership @relation(fields: [membershipId], references: [id]) statusHistory BookingStatusHistory[] @@unique([userId, timeSlotId]) @@index([userId]) @@index([status]) @@map("bookings") } model BookingStatusHistory { id String @id @default(uuid()) bookingId String @map("booking_id") fromStatus String? @map("from_status") toStatus String @map("to_status") operatorId String? @map("operator_id") remark String? createdAt DateTime @default(now()) @map("created_at") booking Booking @relation(fields: [bookingId], references: [id]) @@index([bookingId]) @@map("booking_status_history") } model Order { id String @id @default(uuid()) userId String @map("user_id") cardTypeId String @map("card_type_id") orderNo String @unique @map("order_no") amount Decimal @db.Decimal(10, 0) status OrderStatus @default(PENDING) wxTransactionId String? @map("wx_transaction_id") paidAt DateTime? @map("paid_at") flashSaleId String? @map("flash_sale_id") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id]) cardType CardType @relation(fields: [cardTypeId], references: [id]) flashSaleOrder FlashSaleOrder? @@index([userId]) @@index([status]) @@map("orders") } model StudioConfig { id String @id @default(uuid()) name String logo String? bannerUrl String? @map("banner_url") address String @default("") phone String @default("") latitude Decimal? @db.Decimal(10, 7) longitude Decimal? @db.Decimal(10, 7) cancelHoursLimit Int @default(2) @map("cancel_hours_limit") photos Json @default("[]") updatedAt DateTime @updatedAt @map("updated_at") @@map("studio_config") } model FlashSale { id String @id @default(uuid()) cardTypeId String @map("card_type_id") title String originalPrice Decimal @map("original_price") @db.Decimal(10, 0) flashPrice Decimal @map("flash_price") @db.Decimal(10, 0) totalStock Int @map("total_stock") soldCount Int @default(0) @map("sold_count") startTime DateTime @map("start_time") endTime DateTime @map("end_time") status FlashSaleStatus @default(DRAFT) description String? @db.Text sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") cardType CardType @relation(fields: [cardTypeId], references: [id]) orders FlashSaleOrder[] @@index([status, startTime, endTime]) @@map("flash_sales") } model FlashSaleOrder { id String @id @default(uuid()) flashSaleId String @map("flash_sale_id") userId String @map("user_id") orderId String? @unique @map("order_id") status FlashSaleOrderStatus @default(RESERVED) reservedAt DateTime @default(now()) @map("reserved_at") paidAt DateTime? @map("paid_at") expiredAt DateTime? @map("expired_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") flashSale FlashSale @relation(fields: [flashSaleId], references: [id]) user User @relation(fields: [userId], references: [id]) order Order? @relation(fields: [orderId], references: [id]) @@unique([flashSaleId, userId]) @@index([userId]) @@index([status]) @@map("flash_sale_orders") }