211 lines
7.6 KiB
Plaintext
211 lines
7.6 KiB
Plaintext
// This is your Prisma schema file,
|
||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||
|
||
generator client {
|
||
provider = "prisma-client-js"
|
||
}
|
||
|
||
datasource db {
|
||
provider = "postgresql"
|
||
url = env("DATABASE_URL")
|
||
}
|
||
|
||
// ============================================
|
||
// 监控地址表
|
||
// 存储需要监听充值的地址
|
||
// ============================================
|
||
model MonitoredAddress {
|
||
id BigInt @id @default(autoincrement()) @map("address_id")
|
||
|
||
chainType String @map("chain_type") @db.VarChar(20) // KAVA, BSC
|
||
address String @db.VarChar(42) // 0x地址
|
||
|
||
// 使用 accountSequence 作为跨服务关联标识 (全局唯一业务ID)
|
||
accountSequence BigInt @map("account_sequence")
|
||
// 保留 userId 用于兼容,但主要使用 accountSequence
|
||
userId BigInt @map("user_id")
|
||
|
||
isActive Boolean @default(true) @map("is_active") // 是否激活监听
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
deposits DepositTransaction[]
|
||
|
||
@@unique([chainType, address], name: "uk_chain_address")
|
||
@@index([accountSequence], name: "idx_account_sequence")
|
||
@@index([userId], name: "idx_user")
|
||
@@index([chainType, isActive], name: "idx_chain_active")
|
||
@@map("monitored_addresses")
|
||
}
|
||
|
||
// ============================================
|
||
// 充值交易表 (Append-Only)
|
||
// 记录检测到的所有充值交易
|
||
// ============================================
|
||
model DepositTransaction {
|
||
id BigInt @id @default(autoincrement()) @map("deposit_id")
|
||
|
||
chainType String @map("chain_type") @db.VarChar(20)
|
||
txHash String @unique @map("tx_hash") @db.VarChar(66)
|
||
|
||
fromAddress String @map("from_address") @db.VarChar(42)
|
||
toAddress String @map("to_address") @db.VarChar(42)
|
||
|
||
tokenContract String @map("token_contract") @db.VarChar(42) // USDT合约地址
|
||
amount Decimal @db.Decimal(36, 18) // 原始金额
|
||
amountFormatted Decimal @map("amount_formatted") @db.Decimal(20, 8) // 格式化金额
|
||
|
||
blockNumber BigInt @map("block_number")
|
||
blockTimestamp DateTime @map("block_timestamp")
|
||
logIndex Int @map("log_index")
|
||
|
||
// 确认状态
|
||
confirmations Int @default(0)
|
||
status String @default("DETECTED") @db.VarChar(20) // DETECTED, CONFIRMING, CONFIRMED, NOTIFIED
|
||
|
||
// 关联 - 使用 accountSequence 作为跨服务主键
|
||
addressId BigInt @map("address_id")
|
||
accountSequence BigInt @map("account_sequence") // 跨服务关联标识
|
||
userId BigInt @map("user_id") // 保留兼容
|
||
|
||
// 通知状态
|
||
notifiedAt DateTime? @map("notified_at")
|
||
notifyAttempts Int @default(0) @map("notify_attempts")
|
||
lastNotifyError String? @map("last_notify_error") @db.Text
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
monitoredAddress MonitoredAddress @relation(fields: [addressId], references: [id])
|
||
|
||
@@index([chainType, status], name: "idx_chain_status")
|
||
@@index([accountSequence], name: "idx_deposit_account")
|
||
@@index([userId], name: "idx_deposit_user")
|
||
@@index([blockNumber], name: "idx_block")
|
||
@@index([status, notifiedAt], name: "idx_pending_notify")
|
||
@@map("deposit_transactions")
|
||
}
|
||
|
||
// ============================================
|
||
// 区块扫描检查点 (每条链一条记录)
|
||
// 记录扫描进度,用于断点续扫
|
||
// ============================================
|
||
model BlockCheckpoint {
|
||
id BigInt @id @default(autoincrement()) @map("checkpoint_id")
|
||
|
||
chainType String @unique @map("chain_type") @db.VarChar(20)
|
||
|
||
lastScannedBlock BigInt @map("last_scanned_block")
|
||
lastScannedAt DateTime @map("last_scanned_at")
|
||
|
||
// 健康状态
|
||
isHealthy Boolean @default(true) @map("is_healthy")
|
||
lastError String? @map("last_error") @db.Text
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@map("block_checkpoints")
|
||
}
|
||
|
||
// ============================================
|
||
// 交易广播请求表
|
||
// 记录待广播和已广播的交易
|
||
// ============================================
|
||
model TransactionRequest {
|
||
id BigInt @id @default(autoincrement()) @map("request_id")
|
||
|
||
chainType String @map("chain_type") @db.VarChar(20)
|
||
|
||
// 请求来源
|
||
sourceService String @map("source_service") @db.VarChar(50)
|
||
sourceOrderId String @map("source_order_id") @db.VarChar(100)
|
||
|
||
// 交易数据
|
||
fromAddress String @map("from_address") @db.VarChar(42)
|
||
toAddress String @map("to_address") @db.VarChar(42)
|
||
value Decimal @db.Decimal(36, 18)
|
||
data String? @db.Text // 合约调用数据
|
||
|
||
// 签名数据 (由 MPC 服务提供)
|
||
signedTx String? @map("signed_tx") @db.Text
|
||
|
||
// 广播结果
|
||
txHash String? @map("tx_hash") @db.VarChar(66)
|
||
status String @default("PENDING") @db.VarChar(20) // PENDING, SIGNED, BROADCASTED, CONFIRMED, FAILED
|
||
|
||
// Gas 信息
|
||
gasLimit BigInt? @map("gas_limit")
|
||
gasPrice Decimal? @map("gas_price") @db.Decimal(36, 18)
|
||
nonce Int?
|
||
|
||
// 错误信息
|
||
errorMessage String? @map("error_message") @db.Text
|
||
retryCount Int @default(0) @map("retry_count")
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@unique([sourceService, sourceOrderId], name: "uk_source_order")
|
||
@@index([chainType, status], name: "idx_tx_chain_status")
|
||
@@index([txHash], name: "idx_tx_hash")
|
||
@@map("transaction_requests")
|
||
}
|
||
|
||
// ============================================
|
||
// 账户恢复助记词
|
||
// 与账户序列号关联,用于账户恢复验证
|
||
// ============================================
|
||
model RecoveryMnemonic {
|
||
id BigInt @id @default(autoincrement())
|
||
accountSequence Int @map("account_sequence") // 8位账户序列号
|
||
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([accountSequence, status], name: "uk_account_active_mnemonic") // 一个账户只有一个ACTIVE助记词
|
||
@@index([accountSequence], name: "idx_recovery_account")
|
||
@@index([publicKey], name: "idx_recovery_public_key")
|
||
@@index([status], name: "idx_recovery_status")
|
||
@@map("recovery_mnemonics")
|
||
}
|
||
|
||
// ============================================
|
||
// 区块链事件日志 (Append-Only 审计)
|
||
// ============================================
|
||
model BlockchainEvent {
|
||
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")
|
||
|
||
chainType String? @map("chain_type") @db.VarChar(20)
|
||
txHash String? @map("tx_hash") @db.VarChar(66)
|
||
|
||
occurredAt DateTime @default(now()) @map("occurred_at") @db.Timestamp(6)
|
||
|
||
@@index([aggregateType, aggregateId], name: "idx_event_aggregate")
|
||
@@index([eventType], name: "idx_event_type")
|
||
@@index([chainType], name: "idx_event_chain")
|
||
@@index([occurredAt], name: "idx_event_occurred")
|
||
@@map("blockchain_events")
|
||
}
|