245 lines
5.4 KiB
TypeScript
245 lines
5.4 KiB
TypeScript
/**
|
|
* Queries de personagens
|
|
* CRUD completo para a tabela user_characters
|
|
*/
|
|
|
|
import { query } from "../index";
|
|
|
|
export interface Character {
|
|
character_id: number;
|
|
user_id: number;
|
|
character_name: string;
|
|
server_name: string;
|
|
is_main: boolean;
|
|
verified: boolean;
|
|
created_at: Date;
|
|
}
|
|
|
|
export interface CreateCharacterData {
|
|
character_name: string;
|
|
server_name: string;
|
|
is_main?: boolean;
|
|
}
|
|
|
|
export interface UpdateCharacterData {
|
|
character_name?: string;
|
|
server_name?: string;
|
|
is_main?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Cria um novo personagem
|
|
* Se isMain=true, desmarca outros personagens como main
|
|
*/
|
|
export async function createCharacter(
|
|
userId: number,
|
|
data: CreateCharacterData,
|
|
): Promise<Character> {
|
|
// Usar transação para garantir atomicidade
|
|
const { getPool } = await import("../index");
|
|
const pool = getPool();
|
|
const client = await pool.connect();
|
|
|
|
try {
|
|
await client.query("BEGIN");
|
|
|
|
// Se este personagem será main, desmarcar outros
|
|
if (data.is_main) {
|
|
await client.query(
|
|
`
|
|
UPDATE user_characters
|
|
SET is_main = FALSE
|
|
WHERE user_id = $1
|
|
`,
|
|
[userId],
|
|
);
|
|
}
|
|
|
|
// Inserir novo personagem
|
|
const result = await client.query<Character>(
|
|
`
|
|
INSERT INTO user_characters (user_id, character_name, server_name, is_main)
|
|
VALUES ($1, $2, $3, $4)
|
|
RETURNING *
|
|
`,
|
|
[userId, data.character_name, data.server_name, data.is_main || false],
|
|
);
|
|
|
|
await client.query("COMMIT");
|
|
|
|
return result.rows[0];
|
|
} catch (error) {
|
|
await client.query("ROLLBACK");
|
|
throw error;
|
|
} finally {
|
|
client.release();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lista todos os personagens de um usuário
|
|
*/
|
|
export async function getUserCharacters(userId: number): Promise<Character[]> {
|
|
const result = await query<Character>(
|
|
`
|
|
SELECT * FROM user_characters
|
|
WHERE user_id = $1
|
|
ORDER BY is_main DESC, created_at ASC
|
|
`,
|
|
[userId],
|
|
);
|
|
|
|
return result.rows;
|
|
}
|
|
|
|
/**
|
|
* Busca personagem principal de um usuário em um servidor específico
|
|
* Retorna o personagem principal (is_main = true) ou o primeiro personagem do servidor
|
|
*/
|
|
export async function getMainCharacterByUserId(
|
|
userId: number,
|
|
serverName: string,
|
|
): Promise<Character | null> {
|
|
const result = await query<Character>(
|
|
`
|
|
SELECT * FROM user_characters
|
|
WHERE user_id = $1 AND server_name = $2
|
|
ORDER BY is_main DESC, created_at ASC
|
|
LIMIT 1
|
|
`,
|
|
[userId, serverName],
|
|
);
|
|
|
|
return result.rows[0] || null;
|
|
}
|
|
|
|
/**
|
|
* Busca personagem por ID
|
|
*/
|
|
export async function getCharacterById(
|
|
characterId: number,
|
|
): Promise<Character | null> {
|
|
const result = await query<Character>(
|
|
`
|
|
SELECT * FROM user_characters WHERE character_id = $1
|
|
`,
|
|
[characterId],
|
|
);
|
|
|
|
return result.rows[0] || null;
|
|
}
|
|
|
|
/**
|
|
* Atualiza personagem
|
|
* Verifica ownership e se isMain=true, desmarca outros
|
|
*/
|
|
export async function updateCharacter(
|
|
characterId: number,
|
|
userId: number,
|
|
data: UpdateCharacterData,
|
|
): Promise<Character> {
|
|
// Verificar ownership primeiro
|
|
const existing = await getCharacterById(characterId);
|
|
if (!existing) {
|
|
throw new Error("Personagem não encontrado");
|
|
}
|
|
|
|
if (existing.user_id !== userId) {
|
|
throw new Error("Você não tem permissão para atualizar este personagem");
|
|
}
|
|
|
|
// Usar transação para garantir atomicidade
|
|
const { getPool } = await import("../index");
|
|
const pool = getPool();
|
|
const client = await pool.connect();
|
|
|
|
try {
|
|
await client.query("BEGIN");
|
|
|
|
// Se este personagem será main, desmarcar outros
|
|
if (data.is_main) {
|
|
await client.query(
|
|
`
|
|
UPDATE user_characters
|
|
SET is_main = FALSE
|
|
WHERE user_id = $1 AND character_id != $2
|
|
`,
|
|
[userId, characterId],
|
|
);
|
|
}
|
|
|
|
// Construir query de update
|
|
const updates: string[] = [];
|
|
const values: any[] = [];
|
|
let paramIndex = 1;
|
|
|
|
if (data.character_name !== undefined) {
|
|
updates.push(`character_name = $${paramIndex++}`);
|
|
values.push(data.character_name);
|
|
}
|
|
|
|
if (data.server_name !== undefined) {
|
|
updates.push(`server_name = $${paramIndex++}`);
|
|
values.push(data.server_name);
|
|
}
|
|
|
|
if (data.is_main !== undefined) {
|
|
updates.push(`is_main = $${paramIndex++}`);
|
|
values.push(data.is_main);
|
|
}
|
|
|
|
if (updates.length === 0) {
|
|
// Se não há updates, retorna personagem atual
|
|
await client.query("COMMIT");
|
|
return existing;
|
|
}
|
|
|
|
values.push(characterId);
|
|
|
|
const result = await client.query<Character>(
|
|
`
|
|
UPDATE user_characters
|
|
SET ${updates.join(", ")}
|
|
WHERE character_id = $${paramIndex}
|
|
RETURNING *
|
|
`,
|
|
values,
|
|
);
|
|
|
|
await client.query("COMMIT");
|
|
|
|
return result.rows[0];
|
|
} catch (error) {
|
|
await client.query("ROLLBACK");
|
|
throw error;
|
|
} finally {
|
|
client.release();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove personagem
|
|
* Verifica ownership antes de remover
|
|
*/
|
|
export async function deleteCharacter(
|
|
characterId: number,
|
|
userId: number,
|
|
): Promise<void> {
|
|
// Verificar ownership
|
|
const existing = await getCharacterById(characterId);
|
|
if (!existing) {
|
|
throw new Error("Personagem não encontrado");
|
|
}
|
|
|
|
if (existing.user_id !== userId) {
|
|
throw new Error("Você não tem permissão para remover este personagem");
|
|
}
|
|
|
|
await query(
|
|
`
|
|
DELETE FROM user_characters WHERE character_id = $1
|
|
`,
|
|
[characterId],
|
|
);
|
|
}
|