1205 lines
37 KiB
Plaintext
1205 lines
37 KiB
Plaintext
// This is your Prisma schema file,
|
|
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
|
|
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
|
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
binaryTargets = ["native", "debian-openssl-3.0.x"]
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Modelo para el Servidor (Guild)
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model Guild {
|
|
id String @id
|
|
name String
|
|
prefix String @default("!")
|
|
staff Json?
|
|
aiRolePrompt String?
|
|
|
|
// Relaciones
|
|
alliances Alliance[]
|
|
partnerStats PartnershipStats[]
|
|
|
|
// ✅ CAMBIO: Ahora un Guild puede tener MÚLTIPLES configuraciones de embed.
|
|
embedConfigs EmbedConfig[]
|
|
BlockV2Config BlockV2Config[]
|
|
// ✅ NUEVAS RELACIONES
|
|
allianceChannels AllianceChannel[]
|
|
pointsHistory PointHistory[]
|
|
EconomyItem EconomyItem[]
|
|
InventoryEntry InventoryEntry[]
|
|
ShopOffer ShopOffer[]
|
|
ItemMutation ItemMutation[]
|
|
EconomyWallet EconomyWallet[]
|
|
ShopPurchase ShopPurchase[]
|
|
|
|
// Nuevas relaciones para el motor de minijuegos
|
|
gameAreas GameArea[]
|
|
minigameRuns MinigameRun[]
|
|
playerProgress PlayerProgress[]
|
|
Mob Mob[]
|
|
PlayerState PlayerState[]
|
|
PlayerEquipment PlayerEquipment[]
|
|
ActionCooldown ActionCooldown[]
|
|
SmeltJob SmeltJob[]
|
|
ScheduledMobAttack ScheduledMobAttack[]
|
|
|
|
// Nuevas relaciones para sistemas de engagement
|
|
Achievement Achievement[]
|
|
PlayerAchievement PlayerAchievement[]
|
|
Quest Quest[]
|
|
QuestProgress QuestProgress[]
|
|
PlayerStats PlayerStats[]
|
|
PlayerStreak PlayerStreak[]
|
|
AuditLog AuditLog[]
|
|
PlayerStatusEffect PlayerStatusEffect[]
|
|
DeathLog DeathLog[]
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Modelo para el Usuario
|
|
* -----------------------------------------------------------------------------
|
|
* Representa a un usuario de Discord de manera global.
|
|
*/
|
|
model User {
|
|
id String @id
|
|
|
|
// Relaciones
|
|
partnerStats PartnershipStats[]
|
|
createdAlliances Alliance[]
|
|
// ✅ NUEVA RELACIÓN
|
|
pointsHistory PointHistory[]
|
|
InventoryEntry InventoryEntry[]
|
|
EconomyWallet EconomyWallet[]
|
|
ShopPurchase ShopPurchase[]
|
|
|
|
// Nuevas relaciones para el motor de minijuegos
|
|
minigameRuns MinigameRun[]
|
|
playerProgress PlayerProgress[]
|
|
PlayerState PlayerState[]
|
|
PlayerEquipment PlayerEquipment[]
|
|
ActionCooldown ActionCooldown[]
|
|
SmeltJob SmeltJob[]
|
|
ScheduledMobAttack ScheduledMobAttack[]
|
|
|
|
// Nuevas relaciones para sistemas de engagement
|
|
PlayerAchievement PlayerAchievement[]
|
|
QuestProgress QuestProgress[]
|
|
PlayerStats PlayerStats[]
|
|
PlayerStreak PlayerStreak[]
|
|
AuditLog AuditLog[]
|
|
PlayerStatusEffect PlayerStatusEffect[]
|
|
DeathLog DeathLog[]
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Modelo para las Estadísticas de Alianza (Leaderboard)
|
|
* -----------------------------------------------------------------------------
|
|
* Almacena los puntos de un usuario EN UN SERVIDOR específico.
|
|
* Se gana 1 punto por mensaje en los canales registrados.
|
|
*/
|
|
model PartnershipStats {
|
|
// Puntos acumulados totales.
|
|
totalPoints Int @default(0)
|
|
|
|
// Puntos para la tabla de clasificación semanal.
|
|
weeklyPoints Int @default(0)
|
|
|
|
// Puntos para la tabla de clasificación mensual.
|
|
monthlyPoints Int @default(0)
|
|
|
|
// Fecha del último reinicio para controlar los contadores.
|
|
lastWeeklyReset DateTime @default(now())
|
|
lastMonthlyReset DateTime @default(now())
|
|
|
|
// --- Relaciones y Clave Primaria ---
|
|
user User @relation(fields: [userId], references: [id])
|
|
userId String
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
guildId String
|
|
|
|
// Un usuario solo puede tener un registro de estadísticas por servidor.
|
|
@@id([userId, guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Modelo para la Alianza (El mensaje publicado)
|
|
* -----------------------------------------------------------------------------
|
|
* Guarda la referencia al mensaje de alianza, pero no su contenido.
|
|
* El contenido se construye dinámicamente usando EmbedConfig y PartnershipStats.
|
|
*/
|
|
model Alliance {
|
|
id String @id @default(cuid())
|
|
channelId String
|
|
messageId String @unique
|
|
createdAt DateTime @default(now())
|
|
|
|
// --- Relaciones ---
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
guildId String
|
|
creator User @relation(fields: [creatorId], references: [id])
|
|
creatorId String
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Modelo para Canales de Alianza
|
|
* -----------------------------------------------------------------------------
|
|
* Gestiona qué canales están configurados para otorgar puntos y qué bloque enviar
|
|
*/
|
|
model AllianceChannel {
|
|
id String @id @default(cuid())
|
|
channelId String @unique // ID del canal de Discord
|
|
|
|
// Configuración del canal
|
|
blockConfigName String // Nombre del BlockV2Config a enviar
|
|
isActive Boolean @default(true)
|
|
|
|
// Timestamps
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// --- Relaciones ---
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
guildId String
|
|
|
|
// Historial de puntos otorgados en este canal
|
|
pointsHistory PointHistory[]
|
|
|
|
// Un canal solo puede estar en un servidor
|
|
@@unique([guildId, channelId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Modelo para Historial de Puntos
|
|
* -----------------------------------------------------------------------------
|
|
* Registra cada vez que un usuario gana puntos con fecha y hora
|
|
*/
|
|
model PointHistory {
|
|
id String @id @default(cuid())
|
|
|
|
// Información del punto otorgado
|
|
points Int @default(1)
|
|
timestamp DateTime @default(now())
|
|
messageId String // ID del mensaje que generó el punto
|
|
|
|
// --- Relaciones ---
|
|
user User @relation(fields: [userId], references: [id])
|
|
userId String
|
|
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
guildId String
|
|
|
|
allianceChannel AllianceChannel @relation(fields: [channelId], references: [id])
|
|
channelId String
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Modelo para la Configuración del Embed
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model EmbedConfig {
|
|
id String @id @default(cuid())
|
|
|
|
// ✅ NUEVO: Un nombre único para identificar este embed dentro del servidor.
|
|
// Ejemplos: "alianza", "bienvenida", "reglas"
|
|
name String
|
|
|
|
// Campos del Embed (título, descripción, color, etc.)
|
|
color String?
|
|
title String?
|
|
url String?
|
|
authorName String?
|
|
authorIconURL String?
|
|
authorURL String?
|
|
description String?
|
|
thumbnailURL String?
|
|
imageURL String?
|
|
footerText String?
|
|
footerIconURL String?
|
|
fields String? @default("[]")
|
|
|
|
// --- Relación ---
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
|
|
// ✅ CAMBIO: Quitamos '@unique' para permitir que un guildId aparezca múltiples veces.
|
|
guildId String
|
|
|
|
// ✅ NUEVO: Asegura que el 'name' sea único por cada servidor.
|
|
// No puedes tener dos embeds llamados "alianza" en el mismo servidor.
|
|
@@unique([guildId, name])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Modelo para la Configuración de Bloques V2
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model BlockV2Config {
|
|
id String @id @default(cuid())
|
|
|
|
// ✅ Nombre único dentro de cada servidor
|
|
name String
|
|
|
|
// Configuración en JSON (embed + componentes, botones, etc.)
|
|
config Json
|
|
|
|
// Relación con el servidor
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
guildId String
|
|
|
|
// 🔒 Asegura que un nombre no se repita dentro del mismo servidor
|
|
@@unique([guildId, name])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Economía: Catálogo de Ítems
|
|
* -----------------------------------------------------------------------------
|
|
* - Definición global o por servidor (guildId opcional)
|
|
* - Uso masivo de JSON para banderas y configuraciones opcionales
|
|
* - Campos de fecha para disponibilidad/adquisición/uso
|
|
* - maxPerInventory permite limitar cuántos puede tener un usuario
|
|
*/
|
|
model EconomyItem {
|
|
id String @id @default(cuid())
|
|
// Clave estable única por servidor (o global si guildId es null)
|
|
key String
|
|
|
|
name String
|
|
description String?
|
|
category String?
|
|
icon String?
|
|
|
|
// Si es apilable (stackable). Si es false, puedes manejar instancias en state JSON del inventario
|
|
stackable Boolean @default(true)
|
|
// Límite duro por inventario (p. ej. 1 o 2); null = ilimitado
|
|
maxPerInventory Int?
|
|
|
|
// Ámbito opcional por servidor. Si es null, el ítem es global
|
|
guildId String?
|
|
guild Guild? @relation(fields: [guildId], references: [id])
|
|
|
|
// Ventanas de disponibilidad (para adquirir) y de uso (para poder usarse)
|
|
availableFrom DateTime?
|
|
availableTo DateTime?
|
|
usableFrom DateTime?
|
|
usableTo DateTime?
|
|
|
|
// Etiquetas libres (requiere PostgreSQL)
|
|
tags String[]
|
|
|
|
// Propiedades dinámicas: banderas como breakable/craftable, chestRewards, eventCurrency, passiveEffects, shop, etc.
|
|
props Json?
|
|
// Cualquier metadato adicional (para extensiones futuras)
|
|
metadata Json?
|
|
|
|
// Relaciones
|
|
recipes ItemRecipe[]
|
|
inventories InventoryEntry[]
|
|
shopOffers ShopOffer[]
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
RecipeIngredient RecipeIngredient[]
|
|
SmeltJob SmeltJob[]
|
|
|
|
@@unique([guildId, key])
|
|
@@index([guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Economía: Inventario por Usuario x Servidor x Ítem
|
|
* -----------------------------------------------------------------------------
|
|
* - "state" JSON permite almacenar durabilidad, instancias, efectos/mutaciones aplicadas, expiraciones, etc.
|
|
* - Clave única por (userId, guildId, itemId) para stacks; id sintético para relaciones hijas
|
|
*/
|
|
model InventoryEntry {
|
|
id String @id @default(cuid())
|
|
|
|
userId String
|
|
guildId String
|
|
itemId String
|
|
|
|
quantity Int @default(0)
|
|
// JSON flexible: { instances:[{ durability: 50, effects:[...], expiresAt:null }], notes:"..." }
|
|
state Json?
|
|
|
|
acquiredAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relaciones
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
item EconomyItem @relation(fields: [itemId], references: [id])
|
|
|
|
mutations InventoryItemMutation[]
|
|
|
|
@@unique([userId, guildId, itemId])
|
|
@@index([userId, guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Economía: Recetas de Crafteo
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model ItemRecipe {
|
|
id String @id @default(cuid())
|
|
// Ítem resultante de la receta y su cantidad
|
|
productItemId String
|
|
productQuantity Int @default(1)
|
|
|
|
product EconomyItem @relation(fields: [productItemId], references: [id])
|
|
ingredients RecipeIngredient[]
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Una receta por ítem resultante (si necesitas variantes, añade un campo "variant" en metadata)
|
|
@@unique([productItemId])
|
|
}
|
|
|
|
model RecipeIngredient {
|
|
id String @id @default(cuid())
|
|
recipeId String
|
|
itemId String
|
|
quantity Int
|
|
|
|
recipe ItemRecipe @relation(fields: [recipeId], references: [id])
|
|
item EconomyItem @relation(fields: [itemId], references: [id])
|
|
|
|
@@unique([recipeId, itemId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Economía: Ofertas de Tienda por Servidor
|
|
* -----------------------------------------------------------------------------
|
|
* - price en JSON permite monedas nativas y/o ítems como pago mixto
|
|
* Ejemplo: { "coins": 500, "items": [{ "itemKey": "iron", "qty": 3 }] }
|
|
*/
|
|
model ShopOffer {
|
|
id String @id @default(cuid())
|
|
|
|
guildId String
|
|
itemId String
|
|
|
|
enabled Boolean @default(true)
|
|
price Json // { coins?: number, items?: [{ itemKey?: string, itemId?: string, qty: number }], extra?: any }
|
|
startAt DateTime?
|
|
endAt DateTime?
|
|
|
|
// Límite de compras por usuario (null = sin límite)
|
|
perUserLimit Int?
|
|
// Stock global de la oferta (null = ilimitado)
|
|
stock Int?
|
|
metadata Json?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
item EconomyItem @relation(fields: [itemId], references: [id])
|
|
ShopPurchase ShopPurchase[]
|
|
|
|
// Evita duplicados del mismo ítem en ventana exacta (puedes ajustar según tu flujo)
|
|
@@unique([guildId, itemId, startAt, endAt])
|
|
@@index([guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Economía: Mutaciones/Efectos adicionales aplicables a ítems
|
|
* -----------------------------------------------------------------------------
|
|
* - Catálogo de mutaciones opcional (global o por servidor)
|
|
* - Pueden vincularse a entradas de inventario
|
|
*/
|
|
model ItemMutation {
|
|
id String @id @default(cuid())
|
|
key String
|
|
name String
|
|
description String?
|
|
effects Json // Definición de efectos/bonos: libre
|
|
metadata Json?
|
|
|
|
guildId String?
|
|
guild Guild? @relation(fields: [guildId], references: [id])
|
|
|
|
inventories InventoryItemMutation[]
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([guildId, key])
|
|
}
|
|
|
|
model InventoryItemMutation {
|
|
id String @id @default(cuid())
|
|
inventoryId String
|
|
mutationId String
|
|
appliedAt DateTime @default(now())
|
|
data Json?
|
|
|
|
inventory InventoryEntry @relation(fields: [inventoryId], references: [id])
|
|
mutation ItemMutation @relation(fields: [mutationId], references: [id])
|
|
|
|
@@index([inventoryId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Economía: Billetera por Usuario x Servidor (moneda base)
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model EconomyWallet {
|
|
id String @id @default(cuid())
|
|
|
|
userId String
|
|
guildId String
|
|
|
|
coins Int @default(0)
|
|
metadata Json?
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, guildId])
|
|
@@index([guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Economía: Historial de compras de la tienda
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model ShopPurchase {
|
|
id String @id @default(cuid())
|
|
offerId String
|
|
userId String
|
|
guildId String
|
|
qty Int @default(1)
|
|
|
|
offer ShopOffer @relation(fields: [offerId], references: [id])
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([offerId])
|
|
@@index([userId, guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Minijuegos: Áreas (Minas, Lagunas, Arenas de pelea, Campos de cultivo)
|
|
* -----------------------------------------------------------------------------
|
|
* - Extensible vía JSON: requirements/rewards/mobs por nivel
|
|
*/
|
|
model GameArea {
|
|
id String @id @default(cuid())
|
|
// Clave única por servidor (o global si guildId es null)
|
|
key String
|
|
name String
|
|
type String // "MINE" | "LAGOON" | "FIGHT" | "FARM" | otros
|
|
|
|
guildId String?
|
|
guild Guild? @relation(fields: [guildId], references: [id])
|
|
|
|
// Configuración general del área (cooldowns, multiplicadores, etc.)
|
|
config Json?
|
|
metadata Json?
|
|
|
|
levels GameAreaLevel[]
|
|
runs MinigameRun[]
|
|
progress PlayerProgress[]
|
|
deathLogs DeathLog[]
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([guildId, key])
|
|
@@index([guildId])
|
|
}
|
|
|
|
model GameAreaLevel {
|
|
id String @id @default(cuid())
|
|
areaId String
|
|
level Int
|
|
|
|
// Requisitos para participar (tipo de herramienta, tier mínimo, etc.)
|
|
requirements Json?
|
|
// Tabla de recompensas (ítems/monedas) con pesos
|
|
rewards Json?
|
|
// Tabla de mobs con pesos
|
|
mobs Json?
|
|
metadata Json?
|
|
|
|
availableFrom DateTime?
|
|
availableTo DateTime?
|
|
|
|
area GameArea @relation(fields: [areaId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([areaId, level])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Minijuegos: Mobs (usados en minas, lagunas/pesca, peleas)
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model Mob {
|
|
id String @id @default(cuid())
|
|
key String
|
|
name String
|
|
category String?
|
|
|
|
guildId String?
|
|
guild Guild? @relation(fields: [guildId], references: [id])
|
|
|
|
stats Json? // { hp, attack, defense, luckModifiers, ... }
|
|
drops Json? // Reward table específica del mob (opcional)
|
|
metadata Json?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
ScheduledMobAttack ScheduledMobAttack[]
|
|
|
|
@@unique([guildId, key])
|
|
@@index([guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Minijuegos: Registro de partidas/ejecuciones
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model MinigameRun {
|
|
id String @id @default(cuid())
|
|
|
|
userId String
|
|
guildId String
|
|
areaId String
|
|
level Int
|
|
toolItemId String?
|
|
|
|
success Boolean
|
|
result Json // { rewards:..., mobs:..., tool:{ durabilityDelta, broken }, notes }
|
|
|
|
startedAt DateTime @default(now())
|
|
finishedAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
area GameArea @relation(fields: [areaId], references: [id])
|
|
|
|
@@index([userId, guildId])
|
|
@@index([areaId])
|
|
@@index([startedAt])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Minijuegos: Progreso del jugador por área
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model PlayerProgress {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
areaId String
|
|
highestLevel Int @default(1)
|
|
metadata Json?
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
area GameArea @relation(fields: [areaId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, guildId, areaId])
|
|
@@index([userId, guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Estado del Jugador (HP, stats base) por servidor
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model PlayerState {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
|
|
hp Int @default(100)
|
|
maxHp Int @default(100)
|
|
stats Json? // { attack, defense, strength, luck, ... }
|
|
metadata Json?
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Equipamiento del Jugador por servidor (arma, armadura, capa, etc.)
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model PlayerEquipment {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
|
|
weaponItemId String?
|
|
armorItemId String?
|
|
capeItemId String?
|
|
// accesorios u otros slots libres
|
|
accessories Json?
|
|
metadata Json?
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Cooldowns por acción arbitraria (clave libre)
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model ActionCooldown {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
key String
|
|
until DateTime
|
|
metadata Json?
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
|
|
@@unique([userId, guildId, key])
|
|
@@index([until])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Fundición (smelting) con cooldown/tiempo de preparación
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model SmeltJob {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
|
|
// entradas y parámetros de fundición
|
|
inputs Json // { items: [{ itemKey, qty }], extra?: any }
|
|
outputItemId String
|
|
outputQty Int @default(1)
|
|
|
|
startedAt DateTime @default(now())
|
|
readyAt DateTime
|
|
status String @default("pending") // pending|ready|claimed|cancelled
|
|
metadata Json?
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
outputItem EconomyItem @relation(fields: [outputItemId], references: [id])
|
|
|
|
@@index([userId, guildId])
|
|
@@index([readyAt])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Ataques programados de mobs al jugador (para eventos sin comandos)
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model ScheduledMobAttack {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
mobId String
|
|
|
|
scheduleAt DateTime
|
|
processedAt DateTime?
|
|
status String @default("scheduled") // scheduled|processing|done|failed
|
|
metadata Json?
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
mob Mob @relation(fields: [mobId], references: [id])
|
|
|
|
@@index([scheduleAt])
|
|
@@index([userId, guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Sistema de Logros (Achievements)
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model Achievement {
|
|
id String @id @default(cuid())
|
|
key String
|
|
name String
|
|
description String
|
|
icon String?
|
|
category String // "mining", "crafting", "combat", "economy", "exploration"
|
|
|
|
// Requisitos para desbloquear (JSON flexible)
|
|
requirements Json // { type: "mine_count", value: 100 }
|
|
|
|
// Recompensas al desbloquear
|
|
rewards Json? // { coins: 500, items: [...], title: "..." }
|
|
|
|
guildId String?
|
|
guild Guild? @relation(fields: [guildId], references: [id])
|
|
|
|
// Logros desbloqueados por usuarios
|
|
unlocked PlayerAchievement[]
|
|
|
|
hidden Boolean @default(false) // logros secretos
|
|
points Int @default(10) // puntos que otorga el logro
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([guildId, key])
|
|
@@index([guildId])
|
|
}
|
|
|
|
model PlayerAchievement {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
achievementId String
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
achievement Achievement @relation(fields: [achievementId], references: [id])
|
|
|
|
progress Int @default(0) // progreso actual hacia el logro
|
|
unlockedAt DateTime? // null si aún no está desbloqueado
|
|
notified Boolean @default(false) // si ya se notificó al usuario
|
|
metadata Json?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, guildId, achievementId])
|
|
@@index([userId, guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Sistema de Misiones (Quests)
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model Quest {
|
|
id String @id @default(cuid())
|
|
key String
|
|
name String
|
|
description String
|
|
icon String?
|
|
|
|
// Tipo de misión
|
|
type String // "daily", "weekly", "event", "permanent"
|
|
category String // "mining", "combat", "economy", "exploration"
|
|
|
|
// Requisitos
|
|
requirements Json // { type: "mine", count: 10 }
|
|
|
|
// Recompensas
|
|
rewards Json // { coins: 500, items: [...], xp: 100 }
|
|
|
|
// Disponibilidad
|
|
startAt DateTime?
|
|
endAt DateTime?
|
|
|
|
guildId String?
|
|
guild Guild? @relation(fields: [guildId], references: [id])
|
|
|
|
progress QuestProgress[]
|
|
|
|
active Boolean @default(true)
|
|
repeatable Boolean @default(false) // si se puede repetir después de completar
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([guildId, key])
|
|
@@index([guildId])
|
|
@@index([type])
|
|
}
|
|
|
|
model QuestProgress {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
questId String
|
|
|
|
progress Int @default(0) // progreso actual
|
|
completed Boolean @default(false)
|
|
claimed Boolean @default(false) // si ya reclamó recompensa
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
quest Quest @relation(fields: [questId], references: [id])
|
|
|
|
completedAt DateTime?
|
|
claimedAt DateTime?
|
|
expiresAt DateTime? // para misiones diarias/semanales
|
|
metadata Json?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, guildId, questId, expiresAt])
|
|
@@index([userId, guildId])
|
|
@@index([questId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Sistema de Estadísticas del Jugador
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model PlayerStats {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
|
|
// Stats de minijuegos
|
|
minesCompleted Int @default(0)
|
|
fishingCompleted Int @default(0)
|
|
fightsCompleted Int @default(0)
|
|
farmsCompleted Int @default(0)
|
|
|
|
// Stats de combate
|
|
mobsDefeated Int @default(0)
|
|
damageDealt Int @default(0)
|
|
damageTaken Int @default(0)
|
|
timesDefeated Int @default(0)
|
|
|
|
// Stats de economía
|
|
totalCoinsEarned Int @default(0)
|
|
totalCoinsSpent Int @default(0)
|
|
itemsCrafted Int @default(0)
|
|
itemsSmelted Int @default(0)
|
|
itemsPurchased Int @default(0)
|
|
|
|
// Stats de items
|
|
chestsOpened Int @default(0)
|
|
itemsConsumed Int @default(0)
|
|
itemsEquipped Int @default(0)
|
|
|
|
// Récords personales
|
|
highestDamageDealt Int @default(0)
|
|
longestWinStreak Int @default(0)
|
|
currentWinStreak Int @default(0)
|
|
mostCoinsAtOnce Int @default(0)
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, guildId])
|
|
@@index([userId, guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Efectos de Estado del Jugador (Status Effects)
|
|
* -----------------------------------------------------------------------------
|
|
* Almacena efectos temporales como FATIGUE (reduce daño/defensa), BLEED, BUFFS, etc.
|
|
* type: clave tipo string flexible (ej: "FATIGUE", "BLESSING", "POISON")
|
|
* stacking: se puede permitir múltiples efectos del mismo tipo si cambias la unique compuesta.
|
|
*/
|
|
model PlayerStatusEffect {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
type String
|
|
// magnitud genérica (ej: 0.15 para 15%); interpretación depende del tipo
|
|
magnitude Float @default(0)
|
|
// duración controlada por expiresAt; si null = permanente hasta eliminación manual
|
|
expiresAt DateTime?
|
|
data Json?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
|
|
// Un efecto único por tipo (puedes quitar esta línea si quieres stackeables):
|
|
@@unique([userId, guildId, type])
|
|
@@index([userId, guildId])
|
|
@@index([guildId])
|
|
@@index([expiresAt])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Sistema de Rachas (Streaks)
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model PlayerStreak {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
|
|
currentStreak Int @default(0)
|
|
longestStreak Int @default(0)
|
|
lastActiveDate DateTime @default(now())
|
|
totalDaysActive Int @default(0)
|
|
|
|
// Recompensas reclamadas por día
|
|
rewardsClaimed Json? // { day3: true, day7: true, etc }
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, guildId])
|
|
@@index([userId, guildId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Log de Auditoría
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model AuditLog {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
action String // "buy", "craft", "trade", "equip", "mine", "fight", etc.
|
|
target String? // ID del item/mob/área afectado
|
|
details Json? // detalles adicionales
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId, guildId])
|
|
@@index([action])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Log de Muertes (DeathLog)
|
|
* -----------------------------------------------------------------------------
|
|
* Auditoría de penalizaciones al morir para trazabilidad y balance.
|
|
*/
|
|
model DeathLog {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
areaId String?
|
|
areaKey String?
|
|
level Int?
|
|
goldLost Int @default(0)
|
|
percentApplied Float @default(0) // porcentaje calculado de penalización
|
|
autoDefeatNoWeapon Boolean @default(false)
|
|
fatigueMagnitude Float? // 0.15 = 15%
|
|
fatigueMinutes Int? // minutos aplicados
|
|
metadata Json?
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
guild Guild @relation(fields: [guildId], references: [id])
|
|
area GameArea? @relation(fields: [areaId], references: [id])
|
|
|
|
@@index([userId, guildId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Sistema de Feature Flags
|
|
* -----------------------------------------------------------------------------
|
|
* Control de features para rollouts progresivos, A/B testing y toggles
|
|
* Permite activar/desactivar funcionalidades sin deployar código
|
|
*/
|
|
model FeatureFlag {
|
|
id String @id @default(cuid())
|
|
name String @unique
|
|
|
|
description String?
|
|
status String @default("disabled") // enabled|disabled|rollout|maintenance
|
|
|
|
// Nivel de aplicación: global, guild, user, channel
|
|
target String @default("global")
|
|
|
|
// Estrategia de rollout (para status = rollout)
|
|
rolloutStrategy String? // percentage|whitelist|blacklist|gradual|random
|
|
|
|
// Configuración de la estrategia (JSON)
|
|
rolloutConfig String? // JSON serializado
|
|
|
|
// Fechas de inicio/fin
|
|
startDate DateTime?
|
|
endDate DateTime?
|
|
|
|
// Metadata adicional
|
|
metadata String? // JSON serializado
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([status])
|
|
@@index([target])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Music System: Listening History
|
|
* -----------------------------------------------------------------------------
|
|
* Tracks every song listened to by users for personalized recommendations.
|
|
* Redis: Last 50 songs per user (hot data)
|
|
* Prisma: Full history (cold storage)
|
|
*/
|
|
model ListeningHistory {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
|
|
// Track information
|
|
trackId String
|
|
title String
|
|
author String
|
|
duration Int
|
|
source String @default("youtube")
|
|
|
|
// Listening metrics
|
|
playedAt DateTime @default(now())
|
|
completedAt DateTime?
|
|
listenedMs Int @default(0)
|
|
score Int @default(0)
|
|
skipped Boolean @default(false)
|
|
skipReason String?
|
|
|
|
@@index([userId, playedAt])
|
|
@@index([guildId, playedAt])
|
|
@@index([userId, score])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Music System: User Preferences
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model UserMusicPreferences {
|
|
userId String @id
|
|
|
|
favoriteArtists Json @default("[]")
|
|
skipPatterns Json @default("{}")
|
|
recentTrends Json @default("[]")
|
|
|
|
autoplayEnabled Boolean @default(true)
|
|
|
|
totalPlays Int @default(0)
|
|
totalSkips Int @default(0)
|
|
lastUpdated DateTime @default(now())
|
|
|
|
@@index([lastUpdated])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Music System: Playlists
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model MusicPlaylist {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
name String
|
|
description String?
|
|
isDefault Boolean @default(false) // true for "Me gusta" playlist
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
tracks PlaylistTrack[]
|
|
|
|
@@unique([userId, guildId, name])
|
|
@@index([userId, guildId])
|
|
@@index([userId, isDefault])
|
|
}
|
|
|
|
model PlaylistTrack {
|
|
id String @id @default(cuid())
|
|
playlistId String
|
|
trackId String // encoded track from Lavalink
|
|
title String
|
|
author String
|
|
duration Int
|
|
thumbnail String?
|
|
url String?
|
|
addedAt DateTime @default(now())
|
|
|
|
playlist MusicPlaylist @relation(fields: [playlistId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([playlistId])
|
|
@@index([trackId])
|
|
}
|
|
|
|
/**
|
|
* -----------------------------------------------------------------------------
|
|
* Music System: Track Likes
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
model TrackLike {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
guildId String
|
|
trackId String
|
|
title String
|
|
author String
|
|
thumbnail String?
|
|
likedAt DateTime @default(now())
|
|
|
|
@@unique([userId, guildId, trackId])
|
|
@@index([userId, guildId])
|
|
@@index([trackId])
|
|
}
|