128 lines
5.4 KiB
Plaintext
128 lines
5.4 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
// ============================================
|
|
// 转让订单表 (Saga 聚合根)
|
|
// ============================================
|
|
model TransferOrder {
|
|
id BigInt @id @default(autoincrement())
|
|
transferOrderNo String @unique @map("transfer_order_no") @db.VarChar(50)
|
|
|
|
// ========== 卖方信息 ==========
|
|
sellerUserId BigInt @map("seller_user_id")
|
|
sellerAccountSequence String @map("seller_account_sequence") @db.VarChar(20)
|
|
|
|
// ========== 买方信息 ==========
|
|
buyerUserId BigInt @map("buyer_user_id")
|
|
buyerAccountSequence String @map("buyer_account_sequence") @db.VarChar(20)
|
|
|
|
// ========== 转让标的 ==========
|
|
sourceOrderNo String @map("source_order_no") @db.VarChar(50)
|
|
sourceAdoptionId BigInt @map("source_adoption_id")
|
|
treeCount Int @map("tree_count")
|
|
contributionPerTree Decimal @map("contribution_per_tree") @db.Decimal(20, 10)
|
|
originalAdoptionDate DateTime @map("original_adoption_date") @db.Date
|
|
originalExpireDate DateTime @map("original_expire_date") @db.Date
|
|
selectedProvince String @map("selected_province") @db.VarChar(10)
|
|
selectedCity String @map("selected_city") @db.VarChar(10)
|
|
|
|
// ========== 价格与费用 ==========
|
|
transferPrice Decimal @map("transfer_price") @db.Decimal(20, 8)
|
|
platformFeeRate Decimal @map("platform_fee_rate") @db.Decimal(5, 4)
|
|
platformFeeAmount Decimal @map("platform_fee_amount") @db.Decimal(20, 8)
|
|
sellerReceiveAmount Decimal @map("seller_receive_amount") @db.Decimal(20, 8)
|
|
|
|
// ========== Saga 状态 ==========
|
|
status String @default("PENDING") @map("status") @db.VarChar(30)
|
|
sagaStep String @default("INIT") @map("saga_step") @db.VarChar(30)
|
|
failReason String? @map("fail_reason") @db.VarChar(500)
|
|
retryCount Int @default(0) @map("retry_count")
|
|
|
|
// ========== 各步骤确认时间戳 ==========
|
|
sellerConfirmedAt DateTime? @map("seller_confirmed_at")
|
|
paymentFrozenAt DateTime? @map("payment_frozen_at")
|
|
treesLockedAt DateTime? @map("trees_locked_at")
|
|
ownershipTransferredAt DateTime? @map("ownership_transferred_at")
|
|
contributionAdjustedAt DateTime? @map("contribution_adjusted_at")
|
|
statsUpdatedAt DateTime? @map("stats_updated_at")
|
|
paymentSettledAt DateTime? @map("payment_settled_at")
|
|
completedAt DateTime? @map("completed_at")
|
|
cancelledAt DateTime? @map("cancelled_at")
|
|
rolledBackAt DateTime? @map("rolled_back_at")
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
statusLogs TransferStatusLog[]
|
|
|
|
@@index([sellerUserId])
|
|
@@index([buyerUserId])
|
|
@@index([sourceOrderNo])
|
|
@@index([status])
|
|
@@index([createdAt])
|
|
@@map("transfer_orders")
|
|
}
|
|
|
|
// ============================================
|
|
// 状态变更日志表 (审计)
|
|
// ============================================
|
|
model TransferStatusLog {
|
|
id BigInt @id @default(autoincrement())
|
|
transferOrderNo String @map("transfer_order_no") @db.VarChar(50)
|
|
fromStatus String @map("from_status") @db.VarChar(30)
|
|
toStatus String @map("to_status") @db.VarChar(30)
|
|
fromSagaStep String @map("from_saga_step") @db.VarChar(30)
|
|
toSagaStep String @map("to_saga_step") @db.VarChar(30)
|
|
operatorType String @map("operator_type") @db.VarChar(20)
|
|
operatorId String? @map("operator_id") @db.VarChar(50)
|
|
remark String? @db.VarChar(500)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
transferOrder TransferOrder @relation(fields: [transferOrderNo], references: [transferOrderNo])
|
|
|
|
@@index([transferOrderNo])
|
|
@@map("transfer_status_logs")
|
|
}
|
|
|
|
// ============================================
|
|
// Outbox 事件表 (标准 Outbox Pattern)
|
|
// ============================================
|
|
model OutboxEvent {
|
|
id BigInt @id @default(autoincrement())
|
|
eventType String @map("event_type") @db.VarChar(100)
|
|
topic String @db.VarChar(200)
|
|
key String @db.VarChar(200)
|
|
payload Json
|
|
aggregateId String @map("aggregate_id") @db.VarChar(100)
|
|
aggregateType String @map("aggregate_type") @db.VarChar(100)
|
|
status String @default("PENDING") @db.VarChar(20)
|
|
retryCount Int @default(0) @map("retry_count")
|
|
maxRetries Int @default(5) @map("max_retries")
|
|
lastError String? @map("last_error") @db.VarChar(1000)
|
|
publishedAt DateTime? @map("published_at")
|
|
nextRetryAt DateTime? @map("next_retry_at")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
@@index([status, createdAt])
|
|
@@index([status, nextRetryAt])
|
|
@@map("outbox_events")
|
|
}
|
|
|
|
// ============================================
|
|
// 已处理事件表 (幂等性保证)
|
|
// ============================================
|
|
model ProcessedEvent {
|
|
id BigInt @id @default(autoincrement())
|
|
eventId String @unique @map("event_id") @db.VarChar(200)
|
|
eventType String @map("event_type") @db.VarChar(100)
|
|
processedAt DateTime @default(now()) @map("processed_at")
|
|
|
|
@@map("processed_events")
|
|
}
|