// 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 AuthorizationRole { id String @id @default(uuid()) userId String @map("user_id") accountSequence String @map("account_sequence") roleType RoleType @map("role_type") regionCode String @map("region_code") regionName String @map("region_name") status AuthorizationStatus @default(PENDING) displayTitle String @map("display_title") // 授权信息 authorizedAt DateTime? @map("authorized_at") authorizedBy String? @map("authorized_by") revokedAt DateTime? @map("revoked_at") revokedBy String? @map("revoked_by") revokeReason String? @map("revoke_reason") // 软删除 (大厂通用做法: deleted_at 为 NULL 表示未删除) deletedAt DateTime? @map("deleted_at") // 考核配置 initialTargetTreeCount Int @map("initial_target_tree_count") monthlyTargetType MonthlyTargetType @map("monthly_target_type") // 自有团队占比 requireLocalPercentage Decimal @default(5.0) @map("require_local_percentage") @db.Decimal(5, 2) exemptFromPercentageCheck Boolean @default(false) @map("exempt_from_percentage_check") // 权益状态 benefitActive Boolean @default(false) @map("benefit_active") benefitActivatedAt DateTime? @map("benefit_activated_at") benefitDeactivatedAt DateTime? @map("benefit_deactivated_at") benefitValidUntil DateTime? @map("benefit_valid_until") // 权益有效期截止日期(当前月+下月末) // 月度考核追踪 lastAssessmentMonth String? @map("last_assessment_month") // 上次考核月份 YYYY-MM monthlyTreesAdded Int @default(0) @map("monthly_trees_added") // 当月新增树数 lastMonthTreesAdded Int @default(0) @map("last_month_trees_added") // 上月新增树数(考核用存档) // 当前考核月份索引 currentMonthIndex Int @default(0) @map("current_month_index") // 时间戳 createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") // 关联 assessments MonthlyAssessment[] bypassRecords MonthlyBypass[] // 注意: 唯一约束通过数据库层的部分索引实现,只对 deleted_at IS NULL 的记录生效 // 需要手动执行迁移 SQL 创建部分唯一索引 // @@unique([accountSequence, roleType, regionCode]) -- 已移除,改用部分索引 @@index([accountSequence]) @@index([userId]) @@index([roleType, regionCode]) @@index([status]) @@index([roleType, status]) @@index([deletedAt]) @@map("authorization_roles") } // ============ 月度考核表 ============ model MonthlyAssessment { id String @id @default(uuid()) authorizationId String @map("authorization_id") userId String @map("user_id") accountSequence String @map("account_sequence") roleType RoleType @map("role_type") regionCode String @map("region_code") // 考核月份 assessmentMonth String @map("assessment_month") // YYYY-MM monthIndex Int @map("month_index") // 第几个月考核 // 考核目标 monthlyTarget Int @map("monthly_target") cumulativeTarget Int @map("cumulative_target") // 完成情况 monthlyCompleted Int @default(0) @map("monthly_completed") cumulativeCompleted Int @default(0) @map("cumulative_completed") completedAt DateTime? @map("completed_at") // 达标时间(用于排名) // 自有团队占比 localTeamCount Int @default(0) @map("local_team_count") totalTeamCount Int @default(0) @map("total_team_count") localPercentage Decimal @default(0) @map("local_percentage") @db.Decimal(5, 2) localPercentagePass Boolean @default(false) @map("local_percentage_pass") // 超越目标占比 exceedRatio Decimal @default(0) @map("exceed_ratio") @db.Decimal(10, 4) // 考核结果 result AssessmentResult @default(NOT_ASSESSED) // 排名 rankingInRegion Int? @map("ranking_in_region") isFirstPlace Boolean @default(false) @map("is_first_place") // 豁免 isBypassed Boolean @default(false) @map("is_bypassed") bypassedBy String? @map("bypassed_by") bypassedAt DateTime? @map("bypassed_at") // 时间戳 assessedAt DateTime? @map("assessed_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") // 关联 authorization AuthorizationRole @relation(fields: [authorizationId], references: [id]) @@unique([authorizationId, assessmentMonth]) @@index([accountSequence, assessmentMonth]) @@index([userId, assessmentMonth]) @@index([roleType, regionCode, assessmentMonth]) @@index([assessmentMonth, result]) @@index([assessmentMonth, roleType, exceedRatio(sort: Desc)]) @@map("monthly_assessments") } // ============ 单月豁免记录表 ============ model MonthlyBypass { id String @id @default(uuid()) authorizationId String @map("authorization_id") userId String @map("user_id") accountSequence String @map("account_sequence") roleType RoleType @map("role_type") bypassMonth String @map("bypass_month") // YYYY-MM // 授权信息 grantedBy String @map("granted_by") grantedAt DateTime @map("granted_at") reason String? // 审批信息(三人授权) approver1Id String @map("approver1_id") approver1At DateTime @map("approver1_at") approver2Id String? @map("approver2_id") approver2At DateTime? @map("approver2_at") approver3Id String? @map("approver3_id") approver3At DateTime? @map("approver3_at") approvalStatus ApprovalStatus @default(PENDING) @map("approval_status") createdAt DateTime @default(now()) @map("created_at") authorization AuthorizationRole @relation(fields: [authorizationId], references: [id]) @@unique([authorizationId, bypassMonth]) @@index([accountSequence, bypassMonth]) @@index([userId, bypassMonth]) @@map("monthly_bypasses") } // ============ 阶梯考核目标配置表 ============ model LadderTargetConfig { id String @id @default(uuid()) roleType RoleType @map("role_type") monthIndex Int @map("month_index") monthlyTarget Int @map("monthly_target") cumulativeTarget Int @map("cumulative_target") isActive Boolean @default(true) @map("is_active") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@unique([roleType, monthIndex]) @@map("ladder_target_configs") } // ============ 认种限制配置表 ============ model PlantingRestriction { id String @id @default(uuid()) restrictionType RestrictionType @map("restriction_type") // 账户限制配置 accountLimitDays Int? @map("account_limit_days") accountLimitCount Int? @map("account_limit_count") // 总量限制配置 totalLimitDays Int? @map("total_limit_days") totalLimitCount Int? @map("total_limit_count") currentTotalCount Int @default(0) @map("current_total_count") // 生效时间 startAt DateTime @map("start_at") endAt DateTime @map("end_at") isActive Boolean @default(true) @map("is_active") createdBy String @map("created_by") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@map("planting_restrictions") } // ============ 管理员授权审批表 ============ model AdminApproval { id String @id @default(uuid()) operationType OperationType @map("operation_type") targetId String @map("target_id") targetType String @map("target_type") requestData Json @map("request_data") // 审批状态 status ApprovalStatus @default(PENDING) // 审批人 requesterId String @map("requester_id") approver1Id String? @map("approver1_id") approver1At DateTime? @map("approver1_at") approver2Id String? @map("approver2_id") approver2At DateTime? @map("approver2_at") approver3Id String? @map("approver3_id") approver3At DateTime? @map("approver3_at") // 完成信息 completedAt DateTime? @map("completed_at") rejectedBy String? @map("rejected_by") rejectedAt DateTime? @map("rejected_at") rejectReason String? @map("reject_reason") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@index([status]) @@index([targetId, targetType]) @@map("admin_approvals") } // ============ 授权操作日志表 ============ model AuthorizationAuditLog { id String @id @default(uuid()) operationType String @map("operation_type") targetUserId String @map("target_user_id") targetRoleType RoleType? @map("target_role_type") targetRegionCode String? @map("target_region_code") operatorId String @map("operator_id") operatorRole String @map("operator_role") beforeState Json? @map("before_state") afterState Json? @map("after_state") ipAddress String? @map("ip_address") userAgent String? @map("user_agent") createdAt DateTime @default(now()) @map("created_at") @@index([targetUserId]) @@index([operatorId]) @@index([createdAt]) @@map("authorization_audit_logs") } // ============ 省市热度统计表 ============ model RegionHeatMap { id String @id @default(uuid()) regionCode String @map("region_code") regionName String @map("region_name") regionType RegionType @map("region_type") totalPlantings Int @default(0) @map("total_plantings") monthlyPlantings Int @default(0) @map("monthly_plantings") weeklyPlantings Int @default(0) @map("weekly_plantings") dailyPlantings Int @default(0) @map("daily_plantings") activeUsers Int @default(0) @map("active_users") authCompanyCount Int @default(0) @map("auth_company_count") heatScore Decimal @default(0) @map("heat_score") @db.Decimal(10, 2) updatedAt DateTime @updatedAt @map("updated_at") @@unique([regionCode, regionType]) @@map("region_heat_maps") } // ============ 火柴人排名视图数据表 ============ model StickmanRanking { id String @id @default(uuid()) userId String @map("user_id") accountSequence String @map("account_sequence") authorizationId String @map("authorization_id") roleType RoleType @map("role_type") regionCode String @map("region_code") regionName String @map("region_name") // 用户信息 nickname String avatarUrl String? @map("avatar_url") // 进度信息 currentMonth String @map("current_month") cumulativeCompleted Int @map("cumulative_completed") cumulativeTarget Int @map("cumulative_target") progressPercentage Decimal @map("progress_percentage") @db.Decimal(5, 2) exceedRatio Decimal @map("exceed_ratio") @db.Decimal(10, 4) // 排名 ranking Int isFirstPlace Boolean @map("is_first_place") // 本月收益 monthlyRewardUsdt Decimal @map("monthly_reward_usdt") @db.Decimal(18, 2) monthlyRewardRwad Decimal @map("monthly_reward_rwad") @db.Decimal(18, 8) updatedAt DateTime @updatedAt @map("updated_at") @@unique([authorizationId, currentMonth]) @@index([accountSequence, currentMonth]) @@index([roleType, regionCode, currentMonth]) @@map("stickman_rankings") } // ============ 系统配置表 ============ model AuthorizationConfig { id String @id @default(uuid()) configKey String @unique @map("config_key") configValue String @map("config_value") description String? isActive Boolean @default(true) @map("is_active") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@map("authorization_configs") } // ============ 枚举定义 ============ enum RoleType { COMMUNITY // 社区 AUTH_PROVINCE_COMPANY // 授权省公司(团队权益20U) PROVINCE_COMPANY // 正式省公司(区域权益15U+1%算力) AUTH_CITY_COMPANY // 授权市公司(团队权益40U) CITY_COMPANY // 正式市公司(区域权益35U+2%算力) } enum AuthorizationStatus { PENDING // 待授权/待考核 AUTHORIZED // 已授权 REVOKED // 已撤销 } enum AssessmentResult { NOT_ASSESSED // 未考核 PASS // 达标 FAIL // 未达标 BYPASSED // 豁免 } enum MonthlyTargetType { NONE // 无月度考核(正式省市公司) FIXED // 固定目标(社区:10棵/月) LADDER // 阶梯目标(授权省市公司) } enum RestrictionType { ACCOUNT_LIMIT // 账户限时限量 TOTAL_LIMIT // 总量限制 } enum ApprovalStatus { PENDING // 待审批 APPROVED // 已通过 REJECTED // 已拒绝 } enum OperationType { GRANT_AUTHORIZATION // 授予授权 REVOKE_AUTHORIZATION // 撤销授权 GRANT_BYPASS // 授予豁免 EXEMPT_PERCENTAGE // 豁免占比考核 MODIFY_CONFIG // 修改配置 } enum RegionType { PROVINCE // 省 CITY // 市 } // ============ 系统账户类型枚举 ============ enum SystemAccountType { COST_ACCOUNT // 成本账户 OPERATION_ACCOUNT // 运营账户 HQ_COMMUNITY // 总部社区账户 RWAD_POOL_PENDING // RWAD矿池待注入 SYSTEM_PROVINCE // 系统省账户(无授权时) SYSTEM_CITY // 系统市账户(无授权时) } // ============ 系统账户流水类型枚举 ============ enum SystemLedgerEntryType { PLANTING_ALLOCATION // 认种分配收入 REWARD_EXPIRED // 过期奖励收入 TRANSFER_OUT // 转出 TRANSFER_IN // 转入 WITHDRAWAL // 提现 ADJUSTMENT // 调整 } // ============ 系统账户表 ============ // 管理成本、运营、总部社区、矿池等系统级账户 model SystemAccount { id BigInt @id @default(autoincrement()) @map("account_id") // 账户类型 accountType SystemAccountType @map("account_type") // 区域信息(仅 SYSTEM_PROVINCE/SYSTEM_CITY 需要) regionCode String? @map("region_code") @db.VarChar(10) regionName String? @map("region_name") @db.VarChar(50) // MPC 生成的钱包地址(按需生成) walletAddress String? @map("wallet_address") @db.VarChar(42) mpcPublicKey String? @map("mpc_public_key") @db.VarChar(130) // 余额(USDT) usdtBalance Decimal @default(0) @map("usdt_balance") @db.Decimal(20, 8) // 算力(仅用于省市账户的算力分配) hashpower Decimal @default(0) @map("hashpower") @db.Decimal(20, 8) // 累计统计 totalReceived Decimal @default(0) @map("total_received") @db.Decimal(20, 8) totalTransferred Decimal @default(0) @map("total_transferred") @db.Decimal(20, 8) // 状态 status String @default("ACTIVE") @map("status") @db.VarChar(20) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") ledgerEntries SystemAccountLedger[] @@unique([accountType, regionCode], name: "uk_account_region") @@index([accountType], name: "idx_system_account_type") @@index([walletAddress], name: "idx_system_wallet_address") @@map("system_accounts") } // ============ 系统账户流水表 ============ // 记录系统账户的所有资金变动(Append-only) model SystemAccountLedger { id BigInt @id @default(autoincrement()) @map("ledger_id") accountId BigInt @map("account_id") // 流水类型 entryType SystemLedgerEntryType @map("entry_type") // 金额 amount Decimal @map("amount") @db.Decimal(20, 8) balanceAfter Decimal @map("balance_after") @db.Decimal(20, 8) // 关联信息 sourceOrderId BigInt? @map("source_order_id") // 来源认种订单 sourceRewardId BigInt? @map("source_reward_id") // 来源过期奖励 txHash String? @map("tx_hash") @db.VarChar(66) // 链上交易哈希 memo String? @map("memo") @db.VarChar(500) createdAt DateTime @default(now()) @map("created_at") account SystemAccount @relation(fields: [accountId], references: [id]) @@index([accountId, createdAt(sort: Desc)], name: "idx_system_ledger_account_created") @@index([sourceOrderId], name: "idx_system_ledger_source_order") @@index([txHash], name: "idx_system_ledger_tx_hash") @@map("system_account_ledgers") } // ============================================ // Outbox 事件表 - 保证事件可靠发送 // 使用 Outbox Pattern 确保领域事件100%送达 // ============================================ 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") }