generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ==================== 交易账户 ==================== // 用户交易账户 model TradingAccount { id String @id @default(uuid()) accountSequence String @unique shareBalance Decimal @default(0) @db.Decimal(30, 8) // 积分股余额 cashBalance Decimal @default(0) @db.Decimal(30, 8) // 现金余额 frozenShares Decimal @default(0) @db.Decimal(30, 8) // 冻结积分股 frozenCash Decimal @default(0) @db.Decimal(30, 8) // 冻结现金 totalBought Decimal @default(0) @db.Decimal(30, 8) // 累计买入量 totalSold Decimal @default(0) @db.Decimal(30, 8) // 累计卖出量 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt orders Order[] transactions TradingTransaction[] @@map("trading_accounts") } // ==================== 订单 ==================== // 交易订单 model Order { id String @id @default(uuid()) orderNo String @unique // 订单号 accountSequence String type String // BUY, SELL status String // PENDING, PARTIAL, FILLED, CANCELLED price Decimal @db.Decimal(30, 18) // 挂单价格 quantity Decimal @db.Decimal(30, 8) // 订单数量 filledQuantity Decimal @default(0) @db.Decimal(30, 8) // 已成交数量 remainingQuantity Decimal @db.Decimal(30, 8) // 剩余数量 averagePrice Decimal @default(0) @db.Decimal(30, 18) // 平均成交价 totalAmount Decimal @default(0) @db.Decimal(30, 8) // 总成交金额 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt cancelledAt DateTime? completedAt DateTime? account TradingAccount @relation(fields: [accountSequence], references: [accountSequence]) trades Trade[] @@index([accountSequence, status]) @@index([type, status, price]) @@index([createdAt(sort: Desc)]) @@map("orders") } // 成交记录 model Trade { id String @id @default(uuid()) tradeNo String @unique buyOrderId String sellOrderId String buyerSequence String sellerSequence String price Decimal @db.Decimal(30, 18) quantity Decimal @db.Decimal(30, 8) amount Decimal @db.Decimal(30, 8) // price * quantity createdAt DateTime @default(now()) buyOrder Order @relation(fields: [buyOrderId], references: [id]) @@index([buyerSequence]) @@index([sellerSequence]) @@index([createdAt(sort: Desc)]) @@map("trades") } // ==================== 交易流水 ==================== model TradingTransaction { id String @id @default(uuid()) accountSequence String type String // TRANSFER_IN, TRANSFER_OUT, BUY, SELL, FREEZE, UNFREEZE, DEPOSIT, WITHDRAW assetType String // SHARE, CASH amount Decimal @db.Decimal(30, 8) balanceBefore Decimal @db.Decimal(30, 8) balanceAfter Decimal @db.Decimal(30, 8) referenceId String? referenceType String? // 交易对手方信息(关键:用户ID和账户序列号) counterpartyType String? @map("counterparty_type") // USER, POOL, SYSTEM counterpartyAccountSeq String? @map("counterparty_account_seq") // 对手方账户序列号 counterpartyUserId String? @map("counterparty_user_id") // 对手方用户ID // 详细备注(包含完整交易信息,格式: "卖出给用户[U123456], 价格0.5USDT") memo String? @db.Text description String? // 保留兼容旧字段 createdAt DateTime @default(now()) account TradingAccount @relation(fields: [accountSequence], references: [accountSequence]) @@index([accountSequence, createdAt(sort: Desc)]) @@index([counterpartyAccountSeq]) @@index([counterpartyUserId]) @@map("trading_transactions") } // ==================== 流通池 ==================== // 流通池(交易所流通池) model CirculationPool { id String @id @default(uuid()) totalShares Decimal @default(0) @db.Decimal(30, 8) // 流通池中的积分股 totalCash Decimal @default(0) @db.Decimal(30, 8) // 流通池中的现金(股池) totalInflow Decimal @default(0) @db.Decimal(30, 8) // 累计流入 totalOutflow Decimal @default(0) @db.Decimal(30, 8) // 累计流出 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt transactions CirculationPoolTransaction[] @@map("circulation_pools") } // 流通池变动记录(包含交易对手方信息) model CirculationPoolTransaction { id String @id @default(uuid()) poolId String @map("pool_id") type String // SHARE_IN, SHARE_OUT, CASH_IN, CASH_OUT, TRADE_BUY, TRADE_SELL assetType String // SHARE, CASH amount Decimal @db.Decimal(30, 8) balanceBefore Decimal @map("balance_before") @db.Decimal(30, 8) balanceAfter Decimal @map("balance_after") @db.Decimal(30, 8) // 交易对手方信息(关键:用户ID和账户序列号) counterpartyType String? @map("counterparty_type") // USER, POOL, SYSTEM, EXTERNAL counterpartyAccountSeq String? @map("counterparty_account_seq") // 对手方账户序列号 counterpartyUserId String? @map("counterparty_user_id") // 对手方用户ID // 关联信息 referenceId String? @map("reference_id") // 关联业务ID(如订单ID、交易ID) referenceType String? @map("reference_type") // 关联类型(ORDER, TRADE, TRANSFER) // 详细备注(包含完整交易信息) // 格式示例: "用户[U123456]买入100股, 订单号ORD20240110001" memo String? @db.Text // 扩展数据 metadata Json? createdAt DateTime @default(now()) @map("created_at") pool CirculationPool @relation(fields: [poolId], references: [id]) @@index([poolId, createdAt(sort: Desc)]) @@index([type, assetType]) @@index([counterpartyAccountSeq]) @@index([counterpartyUserId]) @@index([referenceId]) @@index([createdAt(sort: Desc)]) @@map("circulation_pool_transactions") } // 保留旧的PoolTransaction以兼容(标记为废弃,后续迁移后删除) // @deprecated 使用 CirculationPoolTransaction 替代 model PoolTransaction { id String @id @default(uuid()) type String // SHARE_IN, SHARE_OUT, CASH_IN, CASH_OUT amount Decimal @db.Decimal(30, 8) referenceId String? description String? createdAt DateTime @default(now()) @@index([createdAt(sort: Desc)]) @@map("pool_transactions") } // ==================== K线数据 ==================== // 分钟K线 model MinuteKLine { id String @id @default(uuid()) minute DateTime @unique open Decimal @db.Decimal(30, 18) high Decimal @db.Decimal(30, 18) low Decimal @db.Decimal(30, 18) close Decimal @db.Decimal(30, 18) volume Decimal @db.Decimal(30, 8) // 成交量 amount Decimal @db.Decimal(30, 8) // 成交额 tradeCount Int @default(0) // 成交笔数 createdAt DateTime @default(now()) @@index([minute(sort: Desc)]) @@map("minute_klines") } // 小时K线 model HourKLine { id String @id @default(uuid()) hour DateTime @unique open Decimal @db.Decimal(30, 18) high Decimal @db.Decimal(30, 18) low Decimal @db.Decimal(30, 18) close Decimal @db.Decimal(30, 18) volume Decimal @db.Decimal(30, 8) amount Decimal @db.Decimal(30, 8) tradeCount Int @default(0) createdAt DateTime @default(now()) @@index([hour(sort: Desc)]) @@map("hour_klines") } // 日K线 model DayKLine { id String @id @default(uuid()) date DateTime @unique @db.Date open Decimal @db.Decimal(30, 18) high Decimal @db.Decimal(30, 18) low Decimal @db.Decimal(30, 18) close Decimal @db.Decimal(30, 18) volume Decimal @db.Decimal(30, 8) amount Decimal @db.Decimal(30, 8) tradeCount Int @default(0) createdAt DateTime @default(now()) @@index([date(sort: Desc)]) @@map("day_klines") } // ==================== 划转记录 ==================== // 从挖矿账户划转记录 model TransferRecord { id String @id @default(uuid()) transferNo String @unique accountSequence String direction String // IN (从挖矿账户划入), OUT (划出到挖矿账户) amount Decimal @db.Decimal(30, 8) status String // PENDING, COMPLETED, FAILED miningTxId String? // 挖矿服务的交易ID errorMessage String? createdAt DateTime @default(now()) completedAt DateTime? @@index([accountSequence]) @@index([status]) @@map("transfer_records") } // ==================== Outbox ==================== enum OutboxStatus { PENDING PUBLISHED FAILED } model OutboxEvent { id String @id @default(uuid()) aggregateType String @map("aggregate_type") aggregateId String @map("aggregate_id") eventType String @map("event_type") payload Json topic String @default("trading.events") key String? status OutboxStatus @default(PENDING) retryCount Int @default(0) @map("retry_count") maxRetries Int @default(10) @map("max_retries") lastError String? @map("last_error") publishedAt DateTime? @map("published_at") nextRetryAt DateTime? @map("next_retry_at") createdAt DateTime @default(now()) @map("created_at") @@index([status]) @@index([nextRetryAt]) @@index([createdAt]) @@map("outbox_events") }