generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // 用户账户表 model UserAccount { id BigInt @id @default(autoincrement()) @map("user_id") accountSequence BigInt @unique @map("account_sequence") // 基本信息 phoneNumber String? @unique @map("phone_number") @db.VarChar(20) nickname String @db.VarChar(100) avatarUrl String? @map("avatar_url") @db.VarChar(500) // 推荐信息 inviterSequence BigInt? @map("inviter_sequence") referralCode String @unique @map("referral_code") @db.VarChar(10) // 区域信息 provinceCode String @map("province_code") @db.VarChar(10) cityCode String @map("city_code") @db.VarChar(10) address String? @db.VarChar(500) // KYC信息 kycStatus String @default("NOT_VERIFIED") @map("kyc_status") @db.VarChar(20) realName String? @map("real_name") @db.VarChar(100) idCardNumber String? @map("id_card_number") @db.VarChar(20) idCardFrontUrl String? @map("id_card_front_url") @db.VarChar(500) idCardBackUrl String? @map("id_card_back_url") @db.VarChar(500) kycVerifiedAt DateTime? @map("kyc_verified_at") // 账户状态 status String @default("ACTIVE") @db.VarChar(20) // 时间戳 registeredAt DateTime @default(now()) @map("registered_at") lastLoginAt DateTime? @map("last_login_at") updatedAt DateTime @updatedAt @map("updated_at") // 关联 devices UserDevice[] walletAddresses WalletAddress[] events UserEvent[] @@index([phoneNumber]) @@index([accountSequence]) @@index([referralCode]) @@index([inviterSequence]) @@index([provinceCode, cityCode]) @@index([kycStatus]) @@index([status]) @@map("user_accounts") } // 用户设备表 model UserDevice { id BigInt @id @default(autoincrement()) userId BigInt @map("user_id") deviceId String @map("device_id") @db.VarChar(100) deviceName String? @map("device_name") @db.VarChar(100) addedAt DateTime @default(now()) @map("added_at") lastActiveAt DateTime @default(now()) @map("last_active_at") user UserAccount @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([userId, deviceId]) @@index([deviceId]) @@index([userId]) @@index([lastActiveAt]) @@map("user_devices") } // 区块链钱包地址表 model WalletAddress { id BigInt @id @default(autoincrement()) @map("address_id") userId BigInt @map("user_id") chainType String @map("chain_type") @db.VarChar(20) address String @db.VarChar(100) encryptedMnemonic String? @map("encrypted_mnemonic") @db.Text status String @default("ACTIVE") @db.VarChar(20) boundAt DateTime @default(now()) @map("bound_at") user UserAccount @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([userId, chainType]) @@unique([chainType, address]) @@index([userId]) @@index([address]) @@map("wallet_addresses") } // 设备Token表 model DeviceToken { id BigInt @id @default(autoincrement()) userId BigInt @map("user_id") deviceId String @map("device_id") @db.VarChar(100) refreshTokenHash String @unique @map("refresh_token_hash") @db.VarChar(64) expiresAt DateTime @map("expires_at") createdAt DateTime @default(now()) @map("created_at") revokedAt DateTime? @map("revoked_at") @@index([userId, deviceId]) @@index([expiresAt]) @@map("device_tokens") } // 用户事件表 model UserEvent { id BigInt @id @default(autoincrement()) @map("event_id") eventType String @map("event_type") @db.VarChar(50) aggregateId String @map("aggregate_id") @db.VarChar(100) aggregateType String @map("aggregate_type") @db.VarChar(50) eventData Json @map("event_data") userId BigInt? @map("user_id") occurredAt DateTime @default(now()) @map("occurred_at") @db.Timestamptz(6) version Int @default(1) user UserAccount? @relation(fields: [userId], references: [id], onDelete: SetNull) @@index([aggregateType, aggregateId]) @@index([eventType]) @@index([userId]) @@index([occurredAt]) @@map("user_events") } // 短信验证码表 model SmsCode { id BigInt @id @default(autoincrement()) phoneNumber String @map("phone_number") @db.VarChar(20) code String @db.VarChar(6) type String @db.VarChar(20) // REGISTER, LOGIN, BIND, RECOVER expiresAt DateTime @map("expires_at") usedAt DateTime? @map("used_at") createdAt DateTime @default(now()) @map("created_at") @@index([phoneNumber, type]) @@index([expiresAt]) @@map("sms_codes") } // 死信队列表 model DeadLetterEvent { id BigInt @id @default(autoincrement()) topic String @db.VarChar(100) eventId String @map("event_id") @db.VarChar(50) eventType String @map("event_type") @db.VarChar(50) aggregateId String @map("aggregate_id") @db.VarChar(100) aggregateType String @map("aggregate_type") @db.VarChar(50) payload Json errorMessage String @map("error_message") @db.Text errorStack String? @map("error_stack") @db.Text retryCount Int @default(0) @map("retry_count") createdAt DateTime @default(now()) @map("created_at") processedAt DateTime? @map("processed_at") @@index([topic]) @@index([eventType]) @@index([createdAt]) @@index([processedAt]) @@map("dead_letter_events") }