refactor: Userservice

This commit is contained in:
Julien Froidefond
2025-08-24 16:44:31 +02:00
parent 2e195ca5cf
commit f4dcc89c11
7 changed files with 211 additions and 234 deletions

View File

@@ -1,6 +1,6 @@
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
import { EvaluationService } from "@/services/evaluation-service"; import { userService } from "@/services/user-service";
import { UserProfile } from "@/lib/types"; import { UserProfile } from "@/lib/types";
const COOKIE_NAME = "peakSkills_userId"; const COOKIE_NAME = "peakSkills_userId";
@@ -18,8 +18,7 @@ export async function GET() {
return NextResponse.json({ user: null }, { status: 200 }); return NextResponse.json({ user: null }, { status: 200 });
} }
const evaluationService = new EvaluationService(); const userProfile = await userService.getUserByUuid(userUuid);
const userProfile = await evaluationService.getUserByUuid(userUuid);
if (!userProfile) { if (!userProfile) {
// Cookie invalide, le supprimer // Cookie invalide, le supprimer
@@ -52,8 +51,6 @@ export async function POST(request: NextRequest) {
); );
} }
const evaluationService = new EvaluationService();
// Vérifier s'il y a déjà un cookie d'authentification // Vérifier s'il y a déjà un cookie d'authentification
const cookieStore = await cookies(); const cookieStore = await cookies();
const existingUserUuid = cookieStore.get(COOKIE_NAME)?.value; const existingUserUuid = cookieStore.get(COOKIE_NAME)?.value;
@@ -62,11 +59,11 @@ export async function POST(request: NextRequest) {
if (existingUserUuid) { if (existingUserUuid) {
// Mettre à jour l'utilisateur existant // Mettre à jour l'utilisateur existant
await evaluationService.updateUserByUuid(existingUserUuid, profile); await userService.updateUserByUuid(existingUserUuid, profile);
userUuid = existingUserUuid; userUuid = existingUserUuid;
} else { } else {
// Créer un nouvel utilisateur // Créer un nouvel utilisateur
userUuid = await evaluationService.upsertUserUuid(profile); userUuid = await userService.upsertUserUuid(profile);
} }
// Créer la réponse avec le cookie // Créer la réponse avec le cookie

View File

@@ -1,6 +1,7 @@
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
import { evaluationService } from "@/services/evaluation-service"; import { evaluationService } from "@/services/evaluation-service";
import { userService } from "@/services/user-service";
import { UserEvaluation, UserProfile } from "@/lib/types"; import { UserEvaluation, UserProfile } from "@/lib/types";
import { COOKIE_NAME } from "@/lib/auth-utils"; import { COOKIE_NAME } from "@/lib/auth-utils";
@@ -29,7 +30,7 @@ export async function GET(request: NextRequest) {
} }
// Mode authentifié par cookie UUID // Mode authentifié par cookie UUID
const userProfile = await evaluationService.getUserByUuid(userUuid); const userProfile = await userService.getUserByUuid(userUuid);
if (!userProfile) { if (!userProfile) {
return NextResponse.json( return NextResponse.json(
{ error: "Utilisateur non trouvé" }, { error: "Utilisateur non trouvé" },

View File

@@ -1,6 +1,7 @@
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
import { evaluationService } from "@/services/evaluation-service"; import { evaluationService } from "@/services/evaluation-service";
import { userService } from "@/services/user-service";
import { UserProfile, SkillLevel } from "@/lib/types"; import { UserProfile, SkillLevel } from "@/lib/types";
const COOKIE_NAME = "peakSkills_userId"; const COOKIE_NAME = "peakSkills_userId";
@@ -18,7 +19,7 @@ export async function PUT(request: NextRequest) {
); );
} }
const userProfile = await evaluationService.getUserByUuid(userUuid); const userProfile = await userService.getUserByUuid(userUuid);
if (!userProfile) { if (!userProfile) {
return NextResponse.json( return NextResponse.json(
{ error: "Utilisateur introuvable" }, { error: "Utilisateur introuvable" },

View File

@@ -1,6 +1,6 @@
import { cookies } from "next/headers"; import { cookies } from "next/headers";
import { COOKIE_NAME } from "./auth-utils"; import { COOKIE_NAME } from "./auth-utils";
import { EvaluationService } from "@/services/evaluation-service"; import { evaluationService } from "@/services/evaluation-service";
import { TeamsService } from "@/services/teams-service"; import { TeamsService } from "@/services/teams-service";
import { SkillsService } from "@/services/skills-service"; import { SkillsService } from "@/services/skills-service";
import { SkillCategory, Team } from "./types"; import { SkillCategory, Team } from "./types";
@@ -46,8 +46,6 @@ export async function getServerUserEvaluation() {
} }
try { try {
const evaluationService = new EvaluationService();
// Charger directement l'évaluation par UUID // Charger directement l'évaluation par UUID
const userEvaluation = await evaluationService.loadUserEvaluationByUuid( const userEvaluation = await evaluationService.loadUserEvaluationByUuid(
userUuid userUuid

View File

@@ -1,4 +1,5 @@
import { getPool } from "./database"; import { getPool } from "./database";
import { userService } from "./user-service";
import { import {
UserEvaluation, UserEvaluation,
UserProfile, UserProfile,
@@ -8,133 +9,6 @@ import {
} from "../lib/types"; } from "../lib/types";
export class EvaluationService { export class EvaluationService {
/**
* Crée ou met à jour un utilisateur et retourne son UUID
*/
async upsertUserUuid(profile: UserProfile): Promise<string> {
const pool = getPool();
const client = await pool.connect();
try {
// Créer un nouvel utilisateur avec UUID auto-généré
const insertQuery = `
INSERT INTO users (first_name, last_name, team_id, uuid_id)
VALUES ($1, $2, $3, uuid_generate_v4())
RETURNING uuid_id
`;
const result = await client.query(insertQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
]);
return result.rows[0].uuid_id;
} catch (error: any) {
// Si erreur de contrainte unique, l'utilisateur existe déjà
if (
error.code === "23505" &&
error.constraint === "users_first_name_last_name_team_id_key"
) {
// Récupérer l'utilisateur existant
const existingUserQuery = `
SELECT uuid_id FROM users
WHERE first_name = $1 AND last_name = $2 AND team_id = $3
`;
const existingUser = await client.query(existingUserQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
]);
return existingUser.rows[0].uuid_id;
}
throw error;
} finally {
client.release();
}
}
/**
* Met à jour un utilisateur existant par son UUID
*/
async updateUserByUuid(
userUuid: string,
profile: UserProfile
): Promise<void> {
const pool = getPool();
const client = await pool.connect();
try {
// Vérifier s'il existe déjà un utilisateur avec ce nom/prénom dans la nouvelle équipe
const conflictCheckQuery = `
SELECT uuid_id FROM users
WHERE first_name = $1 AND last_name = $2 AND team_id = $3 AND uuid_id != $4
`;
const conflictResult = await client.query(conflictCheckQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
userUuid,
]);
if (conflictResult.rows.length > 0) {
throw new Error(
"Un utilisateur avec ce nom et prénom existe déjà dans cette équipe"
);
}
// Mettre à jour l'utilisateur existant
const updateQuery = `
UPDATE users
SET first_name = $1, last_name = $2, team_id = $3, updated_at = CURRENT_TIMESTAMP
WHERE uuid_id = $4
`;
await client.query(updateQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
userUuid,
]);
} finally {
client.release();
}
}
/**
* Récupère un utilisateur par son UUID
*/
async getUserByUuid(userUuid: string): Promise<UserProfile | null> {
const pool = getPool();
const client = await pool.connect();
try {
const query = `
SELECT u.first_name, u.last_name, u.team_id
FROM users u
WHERE u.uuid_id = $1
`;
const result = await client.query(query, [userUuid]);
if (result.rows.length === 0) {
return null;
}
const user = result.rows[0];
return {
firstName: user.first_name,
lastName: user.last_name,
teamId: user.team_id,
};
} finally {
client.release();
}
}
/** /**
* Charge une évaluation utilisateur directement par UUID * Charge une évaluation utilisateur directement par UUID
*/ */
@@ -233,95 +107,6 @@ export class EvaluationService {
} }
} }
/**
* Crée ou met à jour un utilisateur (legacy - retourne l'ID numérique)
*/
async upsertUser(profile: UserProfile): Promise<number> {
const pool = getPool();
const client = await pool.connect();
try {
// Vérifier si l'utilisateur existe déjà (par firstName + lastName + teamId)
const existingUserQuery = `
SELECT id FROM users
WHERE first_name = $1 AND last_name = $2 AND team_id = $3
`;
const existingUser = await client.query(existingUserQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
]);
if (existingUser.rows.length > 0) {
// Mettre à jour l'utilisateur existant
const updateQuery = `
UPDATE users
SET first_name = $1, last_name = $2, team_id = $3, updated_at = CURRENT_TIMESTAMP
WHERE id = $4
RETURNING id
`;
const result = await client.query(updateQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
existingUser.rows[0].id,
]);
return result.rows[0].id;
} else {
// Créer un nouvel utilisateur
const insertQuery = `
INSERT INTO users (first_name, last_name, team_id)
VALUES ($1, $2, $3)
RETURNING id
`;
const result = await client.query(insertQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
]);
return result.rows[0].id;
}
} finally {
client.release();
}
}
/**
* Récupère un utilisateur par son ID
*/
async getUserById(userId: number): Promise<UserProfile | null> {
const pool = getPool();
const client = await pool.connect();
try {
const query = `
SELECT u.first_name, u.last_name, u.team_id
FROM users u
WHERE u.id = $1
`;
const result = await client.query(query, [userId]);
if (result.rows.length === 0) {
return null;
}
const user = result.rows[0];
return {
firstName: user.first_name,
lastName: user.last_name,
teamId: user.team_id,
};
} finally {
client.release();
}
}
/** /**
* Sauvegarde une évaluation utilisateur complète (version UUID) * Sauvegarde une évaluation utilisateur complète (version UUID)
*/ */
@@ -333,7 +118,7 @@ export class EvaluationService {
await client.query("BEGIN"); await client.query("BEGIN");
// 1. Upsert user avec UUID // 1. Upsert user avec UUID
const userUuid = await this.upsertUserUuid(evaluation.profile); const userUuid = await userService.upsertUserUuid(evaluation.profile);
// 2. Upsert user_evaluation avec user_uuid // 2. Upsert user_evaluation avec user_uuid
const userEvalQuery = ` const userEvalQuery = `
@@ -376,7 +161,7 @@ export class EvaluationService {
await client.query("BEGIN"); await client.query("BEGIN");
// 1. Upsert user // 1. Upsert user
const userId = await this.upsertUser(evaluation.profile); const userId = await userService.upsertUser(evaluation.profile);
// 2. Upsert user_evaluation - d'abord supprimer l'ancienne si elle existe // 2. Upsert user_evaluation - d'abord supprimer l'ancienne si elle existe
await client.query("DELETE FROM user_evaluations WHERE user_id = $1", [ await client.query("DELETE FROM user_evaluations WHERE user_id = $1", [
@@ -625,7 +410,7 @@ export class EvaluationService {
try { try {
await client.query("BEGIN"); await client.query("BEGIN");
const userUuid = await this.upsertUserUuid(profile); const userUuid = await userService.upsertUserUuid(profile);
// Upsert user_evaluation avec user_uuid // Upsert user_evaluation avec user_uuid
const userEvalResult = await client.query( const userEvalResult = await client.query(
@@ -731,7 +516,7 @@ export class EvaluationService {
try { try {
await client.query("BEGIN"); await client.query("BEGIN");
const userId = await this.upsertUser(profile); const userId = await userService.upsertUser(profile);
// Upsert user_evaluation // Upsert user_evaluation
const userEvalResult = await client.query( const userEvalResult = await client.query(
@@ -821,7 +606,7 @@ export class EvaluationService {
try { try {
await client.query("BEGIN"); await client.query("BEGIN");
const userUuid = await this.upsertUserUuid(profile); const userUuid = await userService.upsertUserUuid(profile);
// Supprimer directement la skill evaluation // Supprimer directement la skill evaluation
const deleteQuery = ` const deleteQuery = `
@@ -871,7 +656,7 @@ export class EvaluationService {
try { try {
await client.query("BEGIN"); await client.query("BEGIN");
const userId = await this.upsertUser(profile); const userId = await userService.upsertUser(profile);
// Supprimer directement la skill evaluation // Supprimer directement la skill evaluation
const deleteQuery = ` const deleteQuery = `

View File

@@ -5,6 +5,9 @@
// Database services (server-only) // Database services (server-only)
export { getPool, closePool } from "./database"; export { getPool, closePool } from "./database";
// User services (server-only)
export { UserService, userService } from "./user-service";
// Evaluation services (server-only) // Evaluation services (server-only)
export { EvaluationService, evaluationService } from "./evaluation-service"; export { EvaluationService, evaluationService } from "./evaluation-service";

192
services/user-service.ts Normal file
View File

@@ -0,0 +1,192 @@
import { getPool } from "./database";
import { UserProfile } from "../lib/types";
export class UserService {
/**
* Crée ou met à jour un utilisateur et retourne son UUID
*/
async upsertUserUuid(profile: UserProfile): Promise<string> {
const pool = getPool();
const client = await pool.connect();
try {
// Créer un nouvel utilisateur avec UUID auto-généré
const insertQuery = `
INSERT INTO users (first_name, last_name, team_id, uuid_id)
VALUES ($1, $2, $3, uuid_generate_v4())
RETURNING uuid_id
`;
const result = await client.query(insertQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
]);
return result.rows[0].uuid_id;
} catch (error: any) {
// Si erreur de contrainte unique, l'utilisateur existe déjà
if (
error.code === "23505" &&
error.constraint === "users_first_name_last_name_team_id_key"
) {
// Récupérer l'utilisateur existant
const existingUserQuery = `
SELECT uuid_id FROM users
WHERE first_name = $1 AND last_name = $2 AND team_id = $3
`;
const existingUser = await client.query(existingUserQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
]);
return existingUser.rows[0].uuid_id;
}
throw error;
} finally {
client.release();
}
}
/**
* Met à jour un utilisateur existant par son UUID
*/
async updateUserByUuid(
userUuid: string,
profile: UserProfile
): Promise<void> {
const pool = getPool();
const client = await pool.connect();
try {
// Vérifier s'il existe déjà un utilisateur avec ce nom/prénom dans la nouvelle équipe
const conflictCheckQuery = `
SELECT uuid_id FROM users
WHERE first_name = $1 AND last_name = $2 AND team_id = $3 AND uuid_id != $4
`;
const conflictResult = await client.query(conflictCheckQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
userUuid,
]);
if (conflictResult.rows.length > 0) {
throw new Error(
"Un utilisateur avec ce nom et prénom existe déjà dans cette équipe"
);
}
// Mettre à jour l'utilisateur existant
const updateQuery = `
UPDATE users
SET first_name = $1, last_name = $2, team_id = $3, updated_at = CURRENT_TIMESTAMP
WHERE uuid_id = $4
`;
await client.query(updateQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
userUuid,
]);
} finally {
client.release();
}
}
/**
* Récupère un utilisateur par son UUID
*/
async getUserByUuid(userUuid: string): Promise<UserProfile | null> {
const pool = getPool();
const client = await pool.connect();
try {
const query = `
SELECT u.first_name, u.last_name, u.team_id
FROM users u
WHERE u.uuid_id = $1
`;
const result = await client.query(query, [userUuid]);
if (result.rows.length === 0) {
return null;
}
const user = result.rows[0];
return {
firstName: user.first_name,
lastName: user.last_name,
teamId: user.team_id,
};
} finally {
client.release();
}
}
/**
* Crée ou met à jour un utilisateur (legacy - retourne l'ID numérique)
*/
async upsertUser(profile: UserProfile): Promise<number> {
const pool = getPool();
const client = await pool.connect();
try {
// Vérifier si l'utilisateur existe déjà (par firstName + lastName + teamId)
const existingUserQuery = `
SELECT id FROM users
WHERE first_name = $1 AND last_name = $2 AND team_id = $3
`;
const existingUser = await client.query(existingUserQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
]);
if (existingUser.rows.length > 0) {
// Mettre à jour l'utilisateur existant
const updateQuery = `
UPDATE users
SET first_name = $1, last_name = $2, team_id = $3, updated_at = CURRENT_TIMESTAMP
WHERE id = $4
RETURNING id
`;
const result = await client.query(updateQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
existingUser.rows[0].id,
]);
return result.rows[0].id;
} else {
// Créer un nouvel utilisateur
const insertQuery = `
INSERT INTO users (first_name, last_name, team_id)
VALUES ($1, $2, $3)
RETURNING id
`;
const result = await client.query(insertQuery, [
profile.firstName,
profile.lastName,
profile.teamId,
]);
return result.rows[0].id;
}
} finally {
client.release();
}
}
}
// Instance singleton
export const userService = new UserService();