feat: implement user and guild existence checks to prevent foreign key constraint errors
This commit is contained in:
@@ -2,6 +2,7 @@ import { prisma } from '../../core/database/prisma';
|
||||
import { giveRewards, type Reward } from '../rewards/service';
|
||||
import { getOrCreatePlayerStats } from '../stats/service';
|
||||
import logger from '../../core/lib/logger';
|
||||
import { ensureUserAndGuildExist } from '../core/userService';
|
||||
|
||||
/**
|
||||
* Verificar y desbloquear logros según un trigger
|
||||
@@ -12,6 +13,9 @@ export async function checkAchievements(
|
||||
trigger: string
|
||||
): Promise<any[]> {
|
||||
try {
|
||||
// Asegurar que User y Guild existan antes de buscar achievements
|
||||
await ensureUserAndGuildExist(userId, guildId);
|
||||
|
||||
// Obtener todos los logros del servidor que no estén desbloqueados
|
||||
const achievements = await prisma.achievement.findMany({
|
||||
where: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { prisma } from '../../core/database/prisma';
|
||||
import type { ItemProps } from '../economy/types';
|
||||
import { ensureUserAndGuildExist } from '../core/userService';
|
||||
|
||||
function parseItemProps(json: unknown): ItemProps {
|
||||
if (!json || typeof json !== 'object') return {};
|
||||
@@ -7,6 +8,9 @@ function parseItemProps(json: unknown): ItemProps {
|
||||
}
|
||||
|
||||
export async function ensurePlayerState(userId: string, guildId: string) {
|
||||
// Asegurar que User y Guild existan antes de crear/buscar state
|
||||
await ensureUserAndGuildExist(userId, guildId);
|
||||
|
||||
return prisma.playerState.upsert({
|
||||
where: { userId_guildId: { userId, guildId } },
|
||||
update: {},
|
||||
@@ -15,6 +19,9 @@ export async function ensurePlayerState(userId: string, guildId: string) {
|
||||
}
|
||||
|
||||
export async function getEquipment(userId: string, guildId: string) {
|
||||
// Asegurar que User y Guild existan antes de crear/buscar equipment
|
||||
await ensureUserAndGuildExist(userId, guildId);
|
||||
|
||||
const eq = await prisma.playerEquipment.upsert({
|
||||
where: { userId_guildId: { userId, guildId } },
|
||||
update: {},
|
||||
@@ -27,6 +34,9 @@ export async function getEquipment(userId: string, guildId: string) {
|
||||
}
|
||||
|
||||
export async function setEquipmentSlot(userId: string, guildId: string, slot: 'weapon'|'armor'|'cape', itemId: string | null) {
|
||||
// Asegurar que User y Guild existan antes de crear/actualizar equipment
|
||||
await ensureUserAndGuildExist(userId, guildId);
|
||||
|
||||
const data = slot === 'weapon' ? { weaponItemId: itemId }
|
||||
: slot === 'armor' ? { armorItemId: itemId }
|
||||
: { capeItemId: itemId };
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { prisma } from '../../core/database/prisma';
|
||||
import { ensureUserAndGuildExist } from '../core/userService';
|
||||
|
||||
export async function getCooldown(userId: string, guildId: string, key: string) {
|
||||
return prisma.actionCooldown.findUnique({ where: { userId_guildId_key: { userId, guildId, key } } });
|
||||
}
|
||||
|
||||
export async function setCooldown(userId: string, guildId: string, key: string, seconds: number) {
|
||||
// Asegurar que User y Guild existan antes de crear/actualizar cooldown
|
||||
await ensureUserAndGuildExist(userId, guildId);
|
||||
|
||||
const until = new Date(Date.now() + Math.max(0, seconds) * 1000);
|
||||
return prisma.actionCooldown.upsert({
|
||||
where: { userId_guildId_key: { userId, guildId, key } },
|
||||
|
||||
89
src/game/core/userService.ts
Normal file
89
src/game/core/userService.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { prisma } from '../../core/database/prisma';
|
||||
import logger from '../../core/lib/logger';
|
||||
|
||||
/**
|
||||
* Asegura que existan los registros de User y Guild en la base de datos.
|
||||
*
|
||||
* **PROBLEMA RESUELTO**: Cuando un usuario nuevo usa comandos de juego (como !inventario, !craftear, etc.),
|
||||
* las funciones como getOrCreateWallet(), getOrCreatePlayerStats(), etc. intentaban crear registros
|
||||
* con foreign keys a User y Guild que no existían, causando errores de constraint.
|
||||
*
|
||||
* **SOLUCIÓN**: Esta función garantiza que User y Guild existan ANTES de crear cualquier dato relacionado.
|
||||
*
|
||||
* @param userId - Discord User ID
|
||||
* @param guildId - Discord Guild ID
|
||||
* @param guildName - Nombre del servidor (opcional, para crear Guild si no existe)
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
export async function ensureUserAndGuildExist(
|
||||
userId: string,
|
||||
guildId: string,
|
||||
guildName?: string
|
||||
): Promise<void> {
|
||||
try {
|
||||
// Verificar y crear User si no existe
|
||||
await prisma.user.upsert({
|
||||
where: { id: userId },
|
||||
update: {}, // No actualizamos nada si ya existe
|
||||
create: { id: userId }
|
||||
});
|
||||
|
||||
// Verificar y crear Guild si no existe
|
||||
await prisma.guild.upsert({
|
||||
where: { id: guildId },
|
||||
update: {}, // No actualizamos nada si ya existe
|
||||
create: {
|
||||
id: guildId,
|
||||
name: guildName || 'Unknown Server',
|
||||
prefix: '!'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error({ userId, guildId, error }, 'Error ensuring User and Guild exist');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asegura que un User exista en la base de datos.
|
||||
* Útil cuando solo necesitas garantizar que el usuario existe.
|
||||
*
|
||||
* @param userId - Discord User ID
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
export async function ensureUserExists(userId: string): Promise<void> {
|
||||
try {
|
||||
await prisma.user.upsert({
|
||||
where: { id: userId },
|
||||
update: {},
|
||||
create: { id: userId }
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error({ userId, error }, 'Error ensuring User exists');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asegura que un Guild exista en la base de datos.
|
||||
*
|
||||
* @param guildId - Discord Guild ID
|
||||
* @param guildName - Nombre del servidor (opcional)
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
export async function ensureGuildExists(guildId: string, guildName?: string): Promise<void> {
|
||||
try {
|
||||
await prisma.guild.upsert({
|
||||
where: { id: guildId },
|
||||
update: {},
|
||||
create: {
|
||||
id: guildId,
|
||||
name: guildName || 'Unknown Server',
|
||||
prefix: '!'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error({ guildId, error }, 'Error ensuring Guild exists');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { prisma } from '../../core/database/prisma';
|
||||
import type { ItemProps, InventoryState, Price, OpenChestResult } from './types';
|
||||
import type { Prisma } from '@prisma/client';
|
||||
import { ensureUserAndGuildExist } from '../core/userService';
|
||||
|
||||
// Utilidades de tiempo
|
||||
function now(): Date {
|
||||
@@ -30,6 +31,9 @@ export async function findItemByKey(guildId: string, key: string) {
|
||||
}
|
||||
|
||||
export async function getOrCreateWallet(userId: string, guildId: string) {
|
||||
// Asegurar que User y Guild existan antes de crear/buscar wallet
|
||||
await ensureUserAndGuildExist(userId, guildId);
|
||||
|
||||
return prisma.economyWallet.upsert({
|
||||
where: { userId_guildId: { userId, guildId } },
|
||||
update: {},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { prisma } from '../../core/database/prisma';
|
||||
import { giveRewards, type Reward } from '../rewards/service';
|
||||
import logger from '../../core/lib/logger';
|
||||
import { ensureUserAndGuildExist } from '../core/userService';
|
||||
|
||||
/**
|
||||
* Actualizar progreso de misiones del jugador
|
||||
@@ -12,6 +13,9 @@ export async function updateQuestProgress(
|
||||
increment: number = 1
|
||||
) {
|
||||
try {
|
||||
// Asegurar que User y Guild existan antes de crear/buscar quest progress
|
||||
await ensureUserAndGuildExist(userId, guildId);
|
||||
|
||||
// Obtener misiones activas que coincidan con el tipo
|
||||
const quests = await prisma.quest.findMany({
|
||||
where: {
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { prisma } from '../../core/database/prisma';
|
||||
import type { Prisma } from '@prisma/client';
|
||||
import logger from '../../core/lib/logger';
|
||||
import { ensureUserAndGuildExist } from '../core/userService';
|
||||
|
||||
/**
|
||||
* Obtener o crear las estadísticas de un jugador
|
||||
*/
|
||||
export async function getOrCreatePlayerStats(userId: string, guildId: string) {
|
||||
// Asegurar que User y Guild existan antes de crear/buscar stats
|
||||
await ensureUserAndGuildExist(userId, guildId);
|
||||
|
||||
let stats = await prisma.playerStats.findUnique({
|
||||
where: { userId_guildId: { userId, guildId } }
|
||||
});
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { prisma } from '../../core/database/prisma';
|
||||
import { giveRewards, type Reward } from '../rewards/service';
|
||||
import logger from '../../core/lib/logger';
|
||||
import { ensureUserAndGuildExist } from '../core/userService';
|
||||
|
||||
/**
|
||||
* Obtener o crear racha del jugador
|
||||
*/
|
||||
export async function getOrCreateStreak(userId: string, guildId: string) {
|
||||
// Asegurar que User y Guild existan antes de crear/buscar streak
|
||||
await ensureUserAndGuildExist(userId, guildId);
|
||||
|
||||
let streak = await prisma.playerStreak.findUnique({
|
||||
where: { userId_guildId: { userId, guildId } }
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user