rwadurian/backend/services/identity-service/prisma/schema.prisma

308 lines
13 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model UserAccount {
userId BigInt @id @default(autoincrement()) @map("user_id")
accountSequence String @unique @map("account_sequence") @db.VarChar(12) // 格式: D + YYMMDD + 5位序号
phoneNumber String? @unique @map("phone_number") @db.VarChar(20)
nickname String @db.VarChar(100)
avatarUrl String? @map("avatar_url") @db.Text
inviterSequence String? @map("inviter_sequence") @db.VarChar(12) // 推荐人序列号
referralCode String @unique @map("referral_code") @db.VarChar(10)
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[]
@@index([phoneNumber], name: "idx_phone")
@@index([accountSequence], name: "idx_sequence")
@@index([referralCode], name: "idx_referral_code")
@@index([inviterSequence], name: "idx_inviter")
@@index([kycStatus], name: "idx_kyc_status")
@@index([status], name: "idx_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)
// Hardware Info - 设备硬件信息 (JSON 存储完整前端传递的设备信息)
deviceInfo Json? @map("device_info") // 完整的设备信息 JSON
platform String? @db.VarChar(20) // ios, android, web (冗余字段,便于查询)
deviceModel String? @map("device_model") @db.VarChar(100) // iPhone 15 Pro, Pixel 8
osVersion String? @map("os_version") @db.VarChar(50) // iOS 17.2, Android 14
appVersion String? @map("app_version") @db.VarChar(20) // 1.0.0
screenWidth Int? @map("screen_width")
screenHeight Int? @map("screen_height")
locale String? @db.VarChar(10) // zh-CN, en-US
timezone String? @db.VarChar(50) // Asia/Shanghai
addedAt DateTime @default(now()) @map("added_at")
lastActiveAt DateTime @default(now()) @map("last_active_at")
user UserAccount @relation(fields: [userId], references: [userId], onDelete: Cascade)
@@unique([userId, deviceId], name: "uk_user_device")
@@index([deviceId], name: "idx_device")
@@index([userId], name: "idx_user")
@@index([lastActiveAt], name: "idx_last_active")
@@index([platform], name: "idx_platform")
@@map("user_devices")
}
model WalletAddress {
addressId 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)
publicKey String @map("public_key") @db.VarChar(130) // MPC公钥 (压缩/非压缩格式)
// MPC 签名验证字段 - 防止地址被恶意篡改
addressDigest String @map("address_digest") @db.VarChar(66) // SHA256摘要
mpcSignatureR String @map("mpc_signature_r") @db.VarChar(66) // 签名R分量
mpcSignatureS String @map("mpc_signature_s") @db.VarChar(66) // 签名S分量
mpcSignatureV Int @map("mpc_signature_v") // 签名恢复ID
status String @default("ACTIVE") @db.VarChar(20)
boundAt DateTime @default(now()) @map("bound_at")
user UserAccount @relation(fields: [userId], references: [userId], onDelete: Cascade)
@@unique([userId, chainType], name: "uk_user_chain")
@@unique([chainType, address], name: "uk_chain_address")
@@index([userId], name: "idx_wallet_user")
@@index([address], name: "idx_address")
@@index([publicKey], name: "idx_public_key")
@@map("wallet_addresses")
}
model AccountSequenceGenerator {
id Int @id @default(autoincrement())
dateKey String @unique @map("date_key") @db.VarChar(6) // 格式: YYMMDD
currentSequence Int @default(0) @map("current_sequence") // 当日序号 (0-99999)
updatedAt DateTime @updatedAt @map("updated_at")
@@map("account_sequence_generator")
}
model UserEvent {
eventId 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.Timestamp(6)
version Int @default(1)
@@index([aggregateType, aggregateId], name: "idx_aggregate")
@@index([eventType], name: "idx_event_type")
@@index([userId], name: "idx_event_user")
@@index([occurredAt], name: "idx_occurred")
@@map("user_events")
}
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], name: "idx_user_device_token")
@@index([expiresAt], name: "idx_expires")
@@map("device_tokens")
}
model DeadLetterEvent {
id BigInt @id @default(autoincrement())
topic String @db.VarChar(100)
eventId String @map("event_id") @db.VarChar(100)
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], name: "idx_topic")
@@index([eventType], name: "idx_dead_letter_event_type")
@@index([createdAt], name: "idx_dead_letter_created")
@@index([processedAt], name: "idx_processed")
@@map("dead_letter_events")
}
model SmsCode {
id BigInt @id @default(autoincrement())
phoneNumber String @map("phone_number") @db.VarChar(20)
code String @db.VarChar(10)
purpose String @db.VarChar(50)
expiresAt DateTime @map("expires_at")
createdAt DateTime @default(now()) @map("created_at")
usedAt DateTime? @map("used_at")
@@index([phoneNumber, purpose], name: "idx_phone_purpose")
@@index([expiresAt], name: "idx_sms_expires")
@@map("sms_codes")
}
// MPC 密钥分片存储 - 服务端持有的分片
model MpcKeyShare {
shareId BigInt @id @default(autoincrement()) @map("share_id")
userId BigInt @unique @map("user_id") // 一个用户只有一组MPC密钥
publicKey String @unique @map("public_key") @db.VarChar(130) // MPC公钥
partyIndex Int @map("party_index") // 当前分片的参与方索引 (0=服务端)
threshold Int @default(2) // 阈值 t
totalParties Int @default(3) @map("total_parties") // 总参与方 n
// 加密存储的分片数据 (AES加密)
encryptedShareData String @map("encrypted_share_data") @db.Text
status String @default("ACTIVE") @db.VarChar(20) // ACTIVE, ROTATED, REVOKED
createdAt DateTime @default(now()) @map("created_at")
rotatedAt DateTime? @map("rotated_at") // 最后一次密钥轮换时间
@@index([publicKey], name: "idx_mpc_public_key")
@@index([status], name: "idx_mpc_status")
@@map("mpc_key_shares")
}
// MPC 会话记录 - 用于审计和追踪
model MpcSession {
sessionId String @id @map("session_id") @db.VarChar(50)
sessionType String @map("session_type") @db.VarChar(20) // KEYGEN, SIGNING, ROTATION
userId BigInt? @map("user_id")
publicKey String? @map("public_key") @db.VarChar(130)
status String @default("PENDING") @db.VarChar(20) // PENDING, IN_PROGRESS, COMPLETED, FAILED
errorMessage String? @map("error_message") @db.Text
// 签名相关 (仅SIGNING类型)
messageHash String? @map("message_hash") @db.VarChar(66)
signatureR String? @map("signature_r") @db.VarChar(66)
signatureS String? @map("signature_s") @db.VarChar(66)
signatureV Int? @map("signature_v")
createdAt DateTime @default(now()) @map("created_at")
completedAt DateTime? @map("completed_at")
@@index([sessionType], name: "idx_session_type")
@@index([userId], name: "idx_session_user")
@@index([status], name: "idx_session_status")
@@index([createdAt], name: "idx_session_created")
@@map("mpc_sessions")
}
// 账户恢复助记词 - 与钱包公钥关联,支持挂失和更换
model RecoveryMnemonic {
id BigInt @id @default(autoincrement())
userId BigInt @map("user_id")
publicKey String @map("public_key") @db.VarChar(130) // 关联的钱包公钥
// 助记词存储 (加密)
encryptedMnemonic String @map("encrypted_mnemonic") @db.Text // AES加密的助记词
mnemonicHash String @map("mnemonic_hash") @db.VarChar(64) // SHA256哈希用于验证
// 状态管理
status String @default("ACTIVE") @db.VarChar(20) // ACTIVE, REVOKED, REPLACED
isBackedUp Boolean @default(false) @map("is_backed_up") // 用户是否已备份
// 挂失/更换相关
revokedAt DateTime? @map("revoked_at")
revokedReason String? @map("revoked_reason") @db.VarChar(200)
replacedById BigInt? @map("replaced_by_id") // 被哪个新助记词替代
createdAt DateTime @default(now()) @map("created_at")
@@unique([userId, status], name: "uk_user_active_mnemonic") // 一个用户只有一个ACTIVE助记词
@@index([userId], name: "idx_recovery_user")
@@index([publicKey], name: "idx_recovery_public_key")
@@index([status], name: "idx_recovery_status")
@@map("recovery_mnemonics")
}
// TOTP 二次验证 - 用于敏感操作 (提现、转账等)
model UserTotp {
id BigInt @id @default(autoincrement())
userId BigInt @unique @map("user_id")
// TOTP 密钥 (AES加密存储)
encryptedSecret String @map("encrypted_secret") @db.VarChar(100)
// 状态
isEnabled Boolean @default(false) @map("is_enabled") // 是否已启用
isVerified Boolean @default(false) @map("is_verified") // 用户是否已验证过一次
createdAt DateTime @default(now()) @map("created_at")
enabledAt DateTime? @map("enabled_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@index([userId], name: "idx_totp_user")
@@map("user_totp")
}
// 推荐链接 - 用于追踪不同渠道的邀请
model ReferralLink {
linkId BigInt @id @default(autoincrement()) @map("link_id")
userId BigInt @map("user_id")
referralCode String @map("referral_code") @db.VarChar(10)
shortCode String @unique @map("short_code") @db.VarChar(10) // 短链码
channel String? @db.VarChar(50) // 渠道: wechat, telegram, twitter, etc.
campaignId String? @map("campaign_id") @db.VarChar(50) // 活动ID
clickCount Int @default(0) @map("click_count") // 点击次数
registerCount Int @default(0) @map("register_count") // 注册转化数
createdAt DateTime @default(now()) @map("created_at")
expiresAt DateTime? @map("expires_at") // 过期时间 (可选)
@@index([userId], name: "idx_referral_link_user")
@@index([referralCode], name: "idx_referral_link_code")
@@index([channel], name: "idx_referral_link_channel")
@@index([createdAt], name: "idx_referral_link_created")
@@map("referral_links")
}