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") }