316 lines
9.0 KiB
Plaintext
316 lines
9.0 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
// native = ویندوز/مک لوکال؛ debian = ایمیج node:20-bookworm-slim در Docker
|
|
binaryTargets = ["native", "debian-openssl-3.0.x"]
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
// ==================== ENUMS ====================
|
|
|
|
enum AdminStatus {
|
|
ACTIVE
|
|
DISABLED
|
|
}
|
|
|
|
enum AdminRole {
|
|
SUPER_ADMIN
|
|
OPERATOR
|
|
SUPPORT
|
|
}
|
|
|
|
enum UserStatus {
|
|
ACTIVE
|
|
BLOCKED
|
|
}
|
|
|
|
enum CampaignStatus {
|
|
DRAFT
|
|
ACTIVE
|
|
PAUSED
|
|
FINISHED
|
|
}
|
|
|
|
enum PrizeType {
|
|
PHYSICAL
|
|
DISCOUNT_CODE
|
|
CREDIT
|
|
CASH
|
|
NO_PRIZE
|
|
OTHER
|
|
}
|
|
|
|
enum StockType {
|
|
LIMITED
|
|
UNLIMITED
|
|
}
|
|
|
|
enum PrizeStatus {
|
|
ACTIVE
|
|
INACTIVE
|
|
}
|
|
|
|
enum SpinStatus {
|
|
SUCCESS
|
|
FAILED
|
|
DUPLICATE
|
|
}
|
|
|
|
enum ChanceTxnType {
|
|
SIGNUP_BONUS
|
|
ADMIN_ADJUSTMENT
|
|
SPIN_CONSUME
|
|
CAMPAIGN_BONUS
|
|
REFUND
|
|
}
|
|
|
|
enum UserPrizeStatus {
|
|
NEW
|
|
CONTACTED
|
|
DELIVERED
|
|
ACTIVE_CODE
|
|
USED
|
|
EXPIRED
|
|
}
|
|
|
|
enum SmsType {
|
|
OTP
|
|
WINNING
|
|
NO_PRIZE
|
|
PRIZE_REMINDER
|
|
}
|
|
|
|
enum SmsStatus {
|
|
PENDING
|
|
SENT
|
|
FAILED
|
|
}
|
|
|
|
enum WheelDesign {
|
|
CLASSIC_CASINO
|
|
NEON_CYBER
|
|
MINIMAL_FLAT
|
|
LUXURY_GOLD
|
|
PLAYFUL_CARTOON
|
|
}
|
|
|
|
// ==================== MODELS ====================
|
|
|
|
model Admin {
|
|
id String @id @default(uuid())
|
|
name String
|
|
mobile String @unique
|
|
email String @unique
|
|
passwordHash String @map("password_hash")
|
|
role AdminRole @default(OPERATOR)
|
|
status AdminStatus @default(ACTIVE)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
auditLogs AdminLog[]
|
|
chanceTxns ChanceTransaction[] @relation("AdminCreatedTxn")
|
|
|
|
@@map("admins")
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(uuid())
|
|
mobile String @unique
|
|
name String?
|
|
status UserStatus @default(ACTIVE)
|
|
lastLoginAt DateTime? @map("last_login_at")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
userCampaigns UserCampaign[]
|
|
chanceTransactions ChanceTransaction[]
|
|
spins Spin[]
|
|
userPrizes UserPrize[]
|
|
otpCodes OtpCode[]
|
|
smsLogs SmsLog[]
|
|
|
|
@@index([mobile])
|
|
@@map("users")
|
|
}
|
|
|
|
model OtpCode {
|
|
id String @id @default(uuid())
|
|
mobile String
|
|
code String
|
|
userId String? @map("user_id")
|
|
user User? @relation(fields: [userId], references: [id])
|
|
attempts Int @default(0)
|
|
consumed Boolean @default(false)
|
|
expiresAt DateTime @map("expires_at")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
@@index([mobile, consumed])
|
|
@@map("otp_codes")
|
|
}
|
|
|
|
model Campaign {
|
|
id String @id @default(uuid())
|
|
title String
|
|
slug String @unique
|
|
description String?
|
|
image String?
|
|
banner String?
|
|
logo String?
|
|
startAt DateTime @map("start_at")
|
|
endAt DateTime @map("end_at")
|
|
status CampaignStatus @default(DRAFT)
|
|
defaultChances Int @default(0) @map("default_chances")
|
|
rules String?
|
|
ctaText String? @map("cta_text")
|
|
wheelDesign WheelDesign @default(CLASSIC_CASINO) @map("wheel_design")
|
|
settings Json? // primaryColor, secondaryColor, headerText, buttonText, headerImage, celebrationEffect...
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
prizes Prize[]
|
|
userCampaigns UserCampaign[]
|
|
chanceTxns ChanceTransaction[]
|
|
spins Spin[]
|
|
userPrizes UserPrize[]
|
|
|
|
@@index([status, startAt, endAt])
|
|
@@map("campaigns")
|
|
}
|
|
|
|
model Prize {
|
|
id String @id @default(uuid())
|
|
campaignId String @map("campaign_id")
|
|
campaign Campaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
|
|
title String
|
|
description String?
|
|
image String?
|
|
type PrizeType
|
|
stockType StockType @map("stock_type")
|
|
initialStock Int? @map("initial_stock")
|
|
remainingStock Int? @map("remaining_stock")
|
|
weight Int
|
|
status PrizeStatus @default(ACTIVE)
|
|
sortOrder Int @default(0) @map("sort_order")
|
|
segmentColor String @map("segment_color")
|
|
expiresAt DateTime? @map("expires_at")
|
|
smsTemplate String? @map("sms_template")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
spins Spin[]
|
|
userPrizes UserPrize[]
|
|
|
|
@@index([campaignId, status])
|
|
@@map("prizes")
|
|
}
|
|
|
|
model UserCampaign {
|
|
id String @id @default(uuid())
|
|
userId String @map("user_id")
|
|
user User @relation(fields: [userId], references: [id])
|
|
campaignId String @map("campaign_id")
|
|
campaign Campaign @relation(fields: [campaignId], references: [id])
|
|
totalChances Int @default(0) @map("total_chances")
|
|
usedChances Int @default(0) @map("used_chances")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
@@unique([userId, campaignId])
|
|
@@map("user_campaigns")
|
|
}
|
|
|
|
model ChanceTransaction {
|
|
id String @id @default(uuid())
|
|
userId String @map("user_id")
|
|
user User @relation(fields: [userId], references: [id])
|
|
campaignId String @map("campaign_id")
|
|
campaign Campaign @relation(fields: [campaignId], references: [id])
|
|
amount Int // مثبت یا منفی
|
|
type ChanceTxnType
|
|
referenceType String? @map("reference_type") // e.g. "spin", "manual"
|
|
referenceId String? @map("reference_id")
|
|
description String?
|
|
createdById String? @map("created_by") // admin id در صورت تغییر دستی
|
|
createdBy Admin? @relation("AdminCreatedTxn", fields: [createdById], references: [id])
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
@@index([userId, campaignId])
|
|
@@map("chance_transactions")
|
|
}
|
|
|
|
model Spin {
|
|
id String @id @default(uuid())
|
|
userId String @map("user_id")
|
|
user User @relation(fields: [userId], references: [id])
|
|
campaignId String @map("campaign_id")
|
|
campaign Campaign @relation(fields: [campaignId], references: [id])
|
|
prizeId String? @map("prize_id")
|
|
prize Prize? @relation(fields: [prizeId], references: [id])
|
|
status SpinStatus @default(SUCCESS)
|
|
idempotencyKey String @unique @map("idempotency_key")
|
|
ip String?
|
|
userAgent String? @map("user_agent")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
userPrize UserPrize?
|
|
|
|
@@index([userId, campaignId])
|
|
@@index([campaignId, createdAt])
|
|
@@map("spins")
|
|
}
|
|
|
|
model UserPrize {
|
|
id String @id @default(uuid())
|
|
userId String @map("user_id")
|
|
user User @relation(fields: [userId], references: [id])
|
|
campaignId String @map("campaign_id")
|
|
campaign Campaign @relation(fields: [campaignId], references: [id])
|
|
prizeId String @map("prize_id")
|
|
prize Prize @relation(fields: [prizeId], references: [id])
|
|
spinId String @unique @map("spin_id")
|
|
spin Spin @relation(fields: [spinId], references: [id])
|
|
code String? @unique
|
|
status UserPrizeStatus @default(NEW)
|
|
wonAt DateTime @default(now()) @map("won_at")
|
|
redeemedAt DateTime? @map("redeemed_at")
|
|
|
|
@@index([userId])
|
|
@@index([status])
|
|
@@map("user_prizes")
|
|
}
|
|
|
|
model SmsLog {
|
|
id String @id @default(uuid())
|
|
userId String? @map("user_id")
|
|
user User? @relation(fields: [userId], references: [id])
|
|
mobile String
|
|
type SmsType
|
|
message String
|
|
provider String @default("raygansms")
|
|
status SmsStatus @default(PENDING)
|
|
providerResponse Json? @map("provider_response")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
@@index([mobile, type])
|
|
@@map("sms_logs")
|
|
}
|
|
|
|
model AdminLog {
|
|
id String @id @default(uuid())
|
|
adminId String @map("admin_id")
|
|
admin Admin @relation(fields: [adminId], references: [id])
|
|
action String // e.g. "prize.update", "chance.add"
|
|
entityType String @map("entity_type")
|
|
entityId String @map("entity_id")
|
|
oldValue Json? @map("old_value")
|
|
newValue Json? @map("new_value")
|
|
ip String?
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
@@index([entityType, entityId])
|
|
@@map("admin_logs")
|
|
}
|