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

501 lines
18 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 WalletAccount {
id BigInt @id @default(autoincrement()) @map("wallet_id")
accountSequence String @unique @map("account_sequence") @db.VarChar(20) // 跨服务关联标识 (全局唯一业务ID)
userId BigInt @unique @map("user_id") // 保留兼容
// USDT 余额
usdtAvailable Decimal @default(0) @map("usdt_available") @db.Decimal(20, 8)
usdtFrozen Decimal @default(0) @map("usdt_frozen") @db.Decimal(20, 8)
// DST 余额
dstAvailable Decimal @default(0) @map("dst_available") @db.Decimal(20, 8)
dstFrozen Decimal @default(0) @map("dst_frozen") @db.Decimal(20, 8)
// BNB 余额
bnbAvailable Decimal @default(0) @map("bnb_available") @db.Decimal(20, 8)
bnbFrozen Decimal @default(0) @map("bnb_frozen") @db.Decimal(20, 8)
// OG 余额
ogAvailable Decimal @default(0) @map("og_available") @db.Decimal(20, 8)
ogFrozen Decimal @default(0) @map("og_frozen") @db.Decimal(20, 8)
// RWAD 余额
rwadAvailable Decimal @default(0) @map("rwad_available") @db.Decimal(20, 8)
rwadFrozen Decimal @default(0) @map("rwad_frozen") @db.Decimal(20, 8)
// 算力
hashpower Decimal @default(0) @map("hashpower") @db.Decimal(20, 8)
// 待领取收益
pendingUsdt Decimal @default(0) @map("pending_usdt") @db.Decimal(20, 8)
pendingHashpower Decimal @default(0) @map("pending_hashpower") @db.Decimal(20, 8)
pendingExpireAt DateTime? @map("pending_expire_at")
// 可结算收益
settleableUsdt Decimal @default(0) @map("settleable_usdt") @db.Decimal(20, 8)
settleableHashpower Decimal @default(0) @map("settleable_hashpower") @db.Decimal(20, 8)
// 已结算总额
settledTotalUsdt Decimal @default(0) @map("settled_total_usdt") @db.Decimal(20, 8)
settledTotalHashpower Decimal @default(0) @map("settled_total_hashpower") @db.Decimal(20, 8)
// 已过期总额
expiredTotalUsdt Decimal @default(0) @map("expired_total_usdt") @db.Decimal(20, 8)
expiredTotalHashpower Decimal @default(0) @map("expired_total_hashpower") @db.Decimal(20, 8)
// 状态
status String @default("ACTIVE") @map("status") @db.VarChar(20)
// 是否已认种过(认种后分享权益直接进入可结算)
hasPlanted Boolean @default(false) @map("has_planted")
// 乐观锁版本号
version Int @default(0) @map("version")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("wallet_accounts")
@@index([userId])
@@index([usdtAvailable(sort: Desc)])
@@index([hashpower(sort: Desc)])
@@index([status])
}
// ============================================
// 账本流水表 (行为表, append-only)
// ============================================
model LedgerEntry {
id BigInt @id @default(autoincrement()) @map("entry_id")
accountSequence String @map("account_sequence") @db.VarChar(20) // 跨服务关联标识
userId BigInt @map("user_id") // 保留兼容
// 流水类型
entryType String @map("entry_type") @db.VarChar(50)
// 金额变动 (正数入账, 负数支出)
amount Decimal @map("amount") @db.Decimal(20, 8)
assetType String @map("asset_type") @db.VarChar(20)
// 余额快照 (操作后余额)
balanceAfter Decimal? @map("balance_after") @db.Decimal(20, 8)
// 关联引用
refOrderId String? @map("ref_order_id") @db.VarChar(100)
refTxHash String? @map("ref_tx_hash") @db.VarChar(100)
// 备注 (使用 Text 类型,无长度限制)
memo String? @map("memo") @db.Text
// 扩展数据
payloadJson Json? @map("payload_json")
createdAt DateTime @default(now()) @map("created_at")
@@map("wallet_ledger_entries")
@@index([accountSequence, createdAt(sort: Desc)])
@@index([userId, createdAt(sort: Desc)])
@@index([entryType])
@@index([assetType])
@@index([refOrderId])
@@index([refTxHash])
@@index([createdAt])
}
// ============================================
// 充值订单表
// ============================================
model DepositOrder {
id BigInt @id @default(autoincrement()) @map("order_id")
accountSequence String @map("account_sequence") @db.VarChar(20) // 跨服务关联标识
userId BigInt @map("user_id") // 保留兼容
// 充值信息
chainType String @map("chain_type") @db.VarChar(20)
amount Decimal @map("amount") @db.Decimal(20, 8)
txHash String @unique @map("tx_hash") @db.VarChar(100)
// 状态
status String @default("PENDING") @map("status") @db.VarChar(20)
confirmedAt DateTime? @map("confirmed_at")
createdAt DateTime @default(now()) @map("created_at")
@@map("deposit_orders")
@@index([accountSequence])
@@index([userId])
@@index([txHash])
@@index([status])
@@index([chainType])
}
// ============================================
// 结算订单表
// ============================================
model SettlementOrder {
id BigInt @id @default(autoincrement()) @map("order_id")
userId BigInt @map("user_id")
// 结算信息
usdtAmount Decimal @map("usdt_amount") @db.Decimal(20, 8)
settleCurrency String @map("settle_currency") @db.VarChar(10)
// SWAP 信息
swapTxHash String? @map("swap_tx_hash") @db.VarChar(100)
receivedAmount Decimal? @map("received_amount") @db.Decimal(20, 8)
// 状态
status String @default("PENDING") @map("status") @db.VarChar(20)
settledAt DateTime? @map("settled_at")
createdAt DateTime @default(now()) @map("created_at")
@@map("settlement_orders")
@@index([userId])
@@index([status])
@@index([settleCurrency])
@@index([createdAt])
}
// ============================================
// 提现订单表
// ============================================
model WithdrawalOrder {
id BigInt @id @default(autoincrement()) @map("order_id")
orderNo String @unique @map("order_no") @db.VarChar(50)
accountSequence String @map("account_sequence") @db.VarChar(20) // 跨服务关联标识
userId BigInt @map("user_id")
// 提现信息
amount Decimal @map("amount") @db.Decimal(20, 8) // 提现金额
fee Decimal @map("fee") @db.Decimal(20, 8) // 手续费
chainType String @map("chain_type") @db.VarChar(20) // 目标链 (BSC/KAVA)
toAddress String @map("to_address") @db.VarChar(100) // 提现目标地址
// 交易信息
txHash String? @map("tx_hash") @db.VarChar(100) // 链上交易哈希
// 内部转账标识
isInternalTransfer Boolean @default(false) @map("is_internal_transfer") // 是否为内部转账ID转ID
toAccountSequence String? @map("to_account_sequence") @db.VarChar(20) // 接收方ID内部转账时有值
toUserId BigInt? @map("to_user_id") // 接收方用户ID内部转账时有值
// 状态
status String @default("PENDING") @map("status") @db.VarChar(20)
errorMessage String? @map("error_message") @db.VarChar(500)
// 时间戳
frozenAt DateTime? @map("frozen_at")
broadcastedAt DateTime? @map("broadcasted_at")
confirmedAt DateTime? @map("confirmed_at")
createdAt DateTime @default(now()) @map("created_at")
@@map("withdrawal_orders")
@@index([accountSequence])
@@index([userId])
@@index([status])
@@index([chainType])
@@index([txHash])
@@index([createdAt])
}
// ============================================
// 待领取奖励表 (逐笔记录)
// 每笔待领取奖励独立跟踪过期时间
// ============================================
model PendingReward {
id BigInt @id @default(autoincrement()) @map("pending_reward_id")
accountSequence String @map("account_sequence") @db.VarChar(20) // 跨服务关联标识
userId BigInt @map("user_id") // 保留兼容
// 奖励金额
usdtAmount Decimal @map("usdt_amount") @db.Decimal(20, 8)
hashpowerAmount Decimal @map("hashpower_amount") @db.Decimal(20, 8)
// 来源信息
sourceOrderId String @map("source_order_id") @db.VarChar(100) // 触发奖励的认种订单
allocationType String @map("allocation_type") @db.VarChar(50) // 分配类型 (SHARE_RIGHT, TEAM_RIGHT 等)
// 过期时间
expireAt DateTime @map("expire_at")
// 状态: PENDING(待领取) / SETTLED(已结算) / EXPIRED(已过期)
status String @default("PENDING") @map("status") @db.VarChar(20)
// 结算/过期时间
settledAt DateTime? @map("settled_at")
expiredAt DateTime? @map("expired_at")
createdAt DateTime @default(now()) @map("created_at")
@@map("pending_rewards")
@@index([accountSequence, status])
@@index([userId, status])
@@index([status, expireAt]) // 用于定时任务查询过期奖励
@@index([sourceOrderId])
@@index([createdAt])
}
// ============================================
// 法币提现订单表
// 用于法币提现 (银行卡/支付宝/微信)
// 与 WithdrawalOrder (区块链划转) 分开管理
// ============================================
model FiatWithdrawalOrder {
id BigInt @id @default(autoincrement()) @map("order_id")
orderNo String @unique @map("order_no") @db.VarChar(50)
accountSequence String @map("account_sequence") @db.VarChar(20)
userId BigInt @map("user_id")
// 提现金额信息
amount Decimal @map("amount") @db.Decimal(20, 8) // 提现金额 (绿积分, 1:1 人民币)
fee Decimal @map("fee") @db.Decimal(20, 8) // 手续费
// 收款方式: BANK_CARD / ALIPAY / WECHAT
paymentMethod String @map("payment_method") @db.VarChar(20)
// 银行卡信息
bankName String? @map("bank_name") @db.VarChar(100)
bankCardNo String? @map("bank_card_no") @db.VarChar(50)
cardHolderName String? @map("card_holder_name") @db.VarChar(100)
// 支付宝信息
alipayAccount String? @map("alipay_account") @db.VarChar(100)
alipayRealName String? @map("alipay_real_name") @db.VarChar(100)
// 微信信息
wechatAccount String? @map("wechat_account") @db.VarChar(100)
wechatRealName String? @map("wechat_real_name") @db.VarChar(100)
// 状态: PENDING -> FROZEN -> REVIEWING -> APPROVED -> PAYING -> COMPLETED
// 或 REJECTED / FAILED / CANCELLED
status String @default("PENDING") @map("status") @db.VarChar(20)
errorMessage String? @map("error_message") @db.VarChar(500)
// 审核信息
reviewedBy String? @map("reviewed_by") @db.VarChar(100)
reviewedAt DateTime? @map("reviewed_at")
reviewRemark String? @map("review_remark") @db.VarChar(500)
// 打款信息
paidBy String? @map("paid_by") @db.VarChar(100)
paidAt DateTime? @map("paid_at")
paymentProof String? @map("payment_proof") @db.VarChar(500)
paymentRemark String? @map("payment_remark") @db.VarChar(500)
// 详细备注 (操作记录)
detailMemo String? @map("detail_memo") @db.Text
// 时间戳
frozenAt DateTime? @map("frozen_at")
completedAt DateTime? @map("completed_at")
createdAt DateTime @default(now()) @map("created_at")
@@map("fiat_withdrawal_orders")
@@index([accountSequence])
@@index([userId])
@@index([status])
@@index([paymentMethod])
@@index([reviewedBy])
@@index([createdAt])
}
// ============================================
// 提取手续费配置表
// ============================================
model WithdrawalFeeConfig {
id BigInt @id @default(autoincrement()) @map("config_id")
// 费率类型: FIXED(固定金额) / PERCENTAGE(百分比)
feeType String @map("fee_type") @db.VarChar(20)
// 费率值: 固定金额时为具体数值,百分比时为小数 (如 0.001 = 0.1%)
feeValue Decimal @map("fee_value") @db.Decimal(20, 8)
// 最小手续费 (百分比模式下生效)
minFee Decimal @default(0) @map("min_fee") @db.Decimal(20, 8)
// 最大手续费 (百分比模式下生效0表示不限制)
maxFee Decimal @default(0) @map("max_fee") @db.Decimal(20, 8)
// 是否启用
isActive Boolean @default(true) @map("is_active")
// 备注
memo String? @map("memo") @db.VarChar(200)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("withdrawal_fee_configs")
@@index([isActive])
}
// ============================================
// 已处理事件表 (幂等性检查)
// 用于确保 Kafka 事件不会被重复处理
// ============================================
model ProcessedEvent {
id BigInt @id @default(autoincrement()) @map("processed_id")
// 事件标识 (聚合根ID + 事件类型)
eventId String @map("event_id") @db.VarChar(200)
eventType String @map("event_type") @db.VarChar(100)
// 来源服务
sourceService String @map("source_service") @db.VarChar(50)
// 处理时间
processedAt DateTime @default(now()) @map("processed_at")
@@unique([eventId, eventType])
@@map("processed_events")
@@index([eventId])
@@index([eventType])
@@index([processedAt])
}
// ============================================
// 系统账户转出订单表
// 用于记录从系统账户(S开头/区域账户)转出到用户账户的订单
// ============================================
model SystemWithdrawalOrder {
id BigInt @id @default(autoincrement()) @map("order_id")
orderNo String @unique @map("order_no") @db.VarChar(50)
// 转出方(系统账户)
fromAccountSequence String @map("from_account_sequence") @db.VarChar(20) // 系统账户序列号
fromAccountName String @map("from_account_name") @db.VarChar(100) // 系统账户名称
// 接收方(用户账户)
toAccountSequence String @map("to_account_sequence") @db.VarChar(20) // 用户充值ID
toUserId BigInt @map("to_user_id") // 用户ID
toUserName String? @map("to_user_name") @db.VarChar(100) // 用户姓名
toAddress String @map("to_address") @db.VarChar(100) // 用户区块链地址
// 金额
amount Decimal @map("amount") @db.Decimal(20, 8)
chainType String @default("KAVA") @map("chain_type") @db.VarChar(20)
// 交易信息
txHash String? @map("tx_hash") @db.VarChar(100)
// 状态: PENDING -> FROZEN -> BROADCASTED -> CONFIRMED / FAILED
status String @default("PENDING") @map("status") @db.VarChar(20)
errorMessage String? @map("error_message") @db.VarChar(500)
// 操作者
operatorId String @map("operator_id") @db.VarChar(100)
operatorName String? @map("operator_name") @db.VarChar(100)
// 备注
memo String? @map("memo") @db.Text
// 时间戳
frozenAt DateTime? @map("frozen_at")
broadcastedAt DateTime? @map("broadcasted_at")
confirmedAt DateTime? @map("confirmed_at")
createdAt DateTime @default(now()) @map("created_at")
@@map("system_withdrawal_orders")
@@index([fromAccountSequence])
@@index([toAccountSequence])
@@index([toUserId])
@@index([status])
@@index([txHash])
@@index([operatorId])
@@index([createdAt])
}
// ============================================
// 线下结算扣减记录表
// 记录哪些 REWARD_SETTLED 流水已被"线下结算"扣减过
// 用于全额扣减模式:当管理员不输入金额时,
// 自动扣减用户所有未处理的结算收益
// ============================================
model OfflineSettlementDeduction {
id BigInt @id @default(autoincrement()) @map("deduction_id")
accountSequence String @map("account_sequence") @db.VarChar(20)
userId BigInt @map("user_id")
// 关联的原结算流水ID (wallet_ledger_entries.entry_id)
originalLedgerEntryId BigInt @unique @map("original_ledger_entry_id")
// 原结算金额
originalAmount Decimal @map("original_amount") @db.Decimal(20, 8)
// 实际扣减的金额 (通常与原金额相同)
deductedAmount Decimal @map("deducted_amount") @db.Decimal(20, 8)
// 生成的扣减流水ID
deductionLedgerEntryId BigInt @map("deduction_ledger_entry_id")
// 扣减订单号
deductionOrderNo String @map("deduction_order_no") @db.VarChar(50)
// 关联的待办操作ID
pendingActionId String? @map("pending_action_id") @db.VarChar(100)
// 操作者
operatorId String @map("operator_id") @db.VarChar(100)
// 备注
memo String? @map("memo") @db.VarChar(500)
createdAt DateTime @default(now()) @map("created_at")
@@map("offline_settlement_deductions")
@@index([accountSequence])
@@index([userId])
@@index([pendingActionId])
@@index([deductionOrderNo])
@@index([createdAt])
}
// ============================================
// Outbox 事件表 (可靠事件发布)
// [2026-01-07] 新增:实现 Outbox Pattern 确保事件发布的原子性
// ============================================
model OutboxEvent {
id BigInt @id @default(autoincrement()) @map("outbox_id")
// 事件信息
eventType String @map("event_type") @db.VarChar(100)
topic String @map("topic") @db.VarChar(100)
key String @map("key") @db.VarChar(200)
payload Json @map("payload")
// 聚合根信息 (用于幂等性检查和事件确认)
aggregateId String @map("aggregate_id") @db.VarChar(100)
aggregateType String @map("aggregate_type") @db.VarChar(50)
// 发布状态: PENDING(待发送), SENT(已发送待确认), CONFIRMED(已确认), FAILED(失败)
status String @default("PENDING") @map("status") @db.VarChar(20)
retryCount Int @default(0) @map("retry_count")
maxRetries Int @default(5) @map("max_retries")
lastError String? @map("last_error") @db.Text
// 时间戳
createdAt DateTime @default(now()) @map("created_at")
publishedAt DateTime? @map("published_at")
nextRetryAt DateTime? @map("next_retry_at")
@@index([status, createdAt])
@@index([status, nextRetryAt])
@@index([aggregateType, aggregateId])
@@index([topic])
@@map("outbox_events")
}