261 lines
9.8 KiB
Plaintext
261 lines
9.8 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地址
|
||
|
||
// 地址类型: USER (用户钱包) 或 SYSTEM (系统账户)
|
||
addressType String @default("USER") @map("address_type") @db.VarChar(20)
|
||
|
||
// 用户地址关联 (addressType = USER 时使用)
|
||
accountSequence String? @map("account_sequence") @db.VarChar(20) // 跨服务关联标识 (格式: D + YYMMDD + 5位序号)
|
||
userId BigInt? @map("user_id") // 保留兼容
|
||
|
||
// 系统账户关联 (addressType = SYSTEM 时使用)
|
||
systemAccountType String? @map("system_account_type") @db.VarChar(50) // COST_ACCOUNT, OPERATION_ACCOUNT, etc.
|
||
systemAccountId BigInt? @map("system_account_id")
|
||
regionCode String? @map("region_code") @db.VarChar(10) // 省市代码(省市账户用)
|
||
|
||
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([addressType, isActive], name: "idx_type_active")
|
||
@@index([chainType, isActive], name: "idx_chain_active")
|
||
@@index([systemAccountType], name: "idx_system_account_type")
|
||
@@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")
|
||
addressType String @default("USER") @map("address_type") @db.VarChar(20) // USER 或 SYSTEM
|
||
|
||
// 用户地址关联
|
||
accountSequence String? @map("account_sequence") @db.VarChar(20) // 跨服务关联标识 (格式: D + YYMMDD + 5位序号)
|
||
userId BigInt? @map("user_id") // 保留兼容
|
||
|
||
// 系统账户关联(当 addressType = SYSTEM 时)
|
||
systemAccountType String? @map("system_account_type") @db.VarChar(50)
|
||
systemAccountId BigInt? @map("system_account_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 String @map("account_sequence") @db.VarChar(20) // 账户序列号 (格式: D + YYMMDD + 5位序号)
|
||
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")
|
||
}
|
||
|
||
// ============================================
|
||
// Outbox 事件表 (发件箱模式)
|
||
// 保证事件发布的可靠性
|
||
// ============================================
|
||
model OutboxEvent {
|
||
id BigInt @id @default(autoincrement()) @map("event_id")
|
||
|
||
// 事件信息
|
||
eventType String @map("event_type") @db.VarChar(100)
|
||
aggregateId String @map("aggregate_id") @db.VarChar(100)
|
||
aggregateType String @map("aggregate_type") @db.VarChar(50)
|
||
payload Json @map("payload")
|
||
|
||
// 发送状态: PENDING -> SENT -> ACKED / FAILED
|
||
status String @default("PENDING") @db.VarChar(20)
|
||
|
||
// 重试信息
|
||
retryCount Int @default(0) @map("retry_count")
|
||
maxRetries Int @default(10) @map("max_retries")
|
||
lastError String? @map("last_error") @db.Text
|
||
nextRetryAt DateTime? @map("next_retry_at")
|
||
|
||
// 时间戳
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
sentAt DateTime? @map("sent_at")
|
||
ackedAt DateTime? @map("acked_at")
|
||
|
||
@@index([status, nextRetryAt], name: "idx_outbox_pending")
|
||
@@index([aggregateType, aggregateId], name: "idx_outbox_aggregate")
|
||
@@index([eventType], name: "idx_outbox_event_type")
|
||
@@index([createdAt], name: "idx_outbox_created")
|
||
@@map("outbox_events")
|
||
}
|
||
|
||
// ============================================
|
||
// 区块链事件日志 (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")
|
||
}
|