Files
got-gaming/services/houses/house.service.ts

1199 lines
30 KiB
TypeScript

import { prisma } from "../database";
import type {
House,
HouseMembership,
HouseInvitation,
HouseRequest,
HouseRole,
InvitationStatus,
RequestStatus,
Prisma,
SitePreferences,
} from "@/prisma/generated/prisma/client";
import {
ValidationError,
NotFoundError,
ConflictError,
ForbiddenError,
} from "../errors";
import { sitePreferencesService } from "../preferences/site-preferences.service";
// Type étendu pour les préférences avec les nouveaux champs de points des maisons
type SitePreferencesWithHousePoints = SitePreferences & {
houseJoinPoints?: number;
houseLeavePoints?: number;
houseCreatePoints?: number;
};
const HOUSE_NAME_MIN_LENGTH = 3;
const HOUSE_NAME_MAX_LENGTH = 50;
const HOUSE_DESCRIPTION_MAX_LENGTH = 500;
export interface CreateHouseInput {
name: string;
description?: string | null;
creatorId: string;
}
export interface UpdateHouseInput {
name?: string;
description?: string | null;
}
export interface InviteUserInput {
houseId: string;
inviterId: string;
inviteeId: string;
}
export interface RequestToJoinInput {
houseId: string;
requesterId: string;
}
/**
* Service de gestion des maisons
*/
export class HouseService {
/**
* Récupère une maison par son ID
*/
async getHouseById(
id: string,
include?: Prisma.HouseInclude
): Promise<House | null> {
return prisma.house.findUnique({
where: { id },
include,
});
}
/**
* Récupère toutes les maisons avec pagination
*/
async getAllHouses(options?: {
skip?: number;
take?: number;
include?: Prisma.HouseInclude;
orderBy?: Prisma.HouseOrderByWithRelationInput;
}): Promise<House[]> {
return prisma.house.findMany({
skip: options?.skip,
take: options?.take,
include: options?.include,
orderBy: options?.orderBy || { createdAt: "desc" },
});
}
/**
* Recherche des maisons par nom
*/
async searchHouses(
searchTerm: string,
options?: {
skip?: number;
take?: number;
include?: Prisma.HouseInclude;
}
): Promise<House[]> {
return prisma.house.findMany({
where: {
name: {
contains: searchTerm,
mode: "insensitive",
},
},
skip: options?.skip,
take: options?.take,
include: options?.include,
orderBy: { createdAt: "desc" },
});
}
/**
* Récupère les maisons d'un utilisateur
*/
async getUserHouses(
userId: string,
include?: Prisma.HouseInclude
): Promise<House[]> {
const memberships = await prisma.houseMembership.findMany({
where: { userId },
include: {
house: {
include: include,
},
},
});
return memberships.map((m) => m.house);
}
/**
* Récupère la maison d'un utilisateur (s'il en a une)
*/
async getUserHouse(
userId: string,
include?: Prisma.HouseInclude
): Promise<House | null> {
const membership = await prisma.houseMembership.findFirst({
where: { userId },
include: {
house: {
include: include,
},
},
orderBy: { joinedAt: "asc" }, // Première maison jointe
});
return membership?.house || null;
}
/**
* Vérifie si un utilisateur est membre d'une maison
*/
async isUserMemberOfHouse(userId: string, houseId: string): Promise<boolean> {
const membership = await prisma.houseMembership.findUnique({
where: {
houseId_userId: {
houseId,
userId,
},
},
});
return !!membership;
}
/**
* Vérifie si un utilisateur est propriétaire ou admin d'une maison
*/
async isUserOwnerOrAdmin(userId: string, houseId: string): Promise<boolean> {
const membership = await prisma.houseMembership.findUnique({
where: {
houseId_userId: {
houseId,
userId,
},
},
});
return membership?.role === "OWNER" || membership?.role === "ADMIN";
}
/**
* Vérifie si un utilisateur est propriétaire d'une maison
*/
async isUserOwner(userId: string, houseId: string): Promise<boolean> {
const membership = await prisma.houseMembership.findUnique({
where: {
houseId_userId: {
houseId,
userId,
},
},
});
return membership?.role === "OWNER";
}
/**
* Récupère le rôle d'un utilisateur dans une maison
*/
async getUserRole(
userId: string,
houseId: string
): Promise<HouseRole | null> {
const membership = await prisma.houseMembership.findUnique({
where: {
houseId_userId: {
houseId,
userId,
},
},
});
return membership?.role || null;
}
/**
* Récupère les membres d'une maison
*/
async getHouseMembers(
houseId: string,
include?: Prisma.HouseMembershipInclude
): Promise<HouseMembership[]> {
return prisma.houseMembership.findMany({
where: { houseId },
include,
orderBy: [
{ role: "asc" }, // OWNER, ADMIN, MEMBER
{ joinedAt: "asc" },
],
});
}
/**
* Crée une nouvelle maison
*/
async createHouse(data: CreateHouseInput): Promise<House> {
// Validation
if (!data.name || data.name.trim().length === 0) {
throw new ValidationError("Le nom de la maison est requis", "name");
}
if (
data.name.length < HOUSE_NAME_MIN_LENGTH ||
data.name.length > HOUSE_NAME_MAX_LENGTH
) {
throw new ValidationError(
`Le nom de la maison doit contenir entre ${HOUSE_NAME_MIN_LENGTH} et ${HOUSE_NAME_MAX_LENGTH} caractères`,
"name"
);
}
if (
data.description &&
data.description.length > HOUSE_DESCRIPTION_MAX_LENGTH
) {
throw new ValidationError(
`La description ne peut pas dépasser ${HOUSE_DESCRIPTION_MAX_LENGTH} caractères`,
"description"
);
}
// Vérifier si l'utilisateur est déjà dans une maison
const existingMembership = await prisma.houseMembership.findFirst({
where: { userId: data.creatorId },
});
if (existingMembership) {
throw new ConflictError(
"Vous êtes déjà membre d'une maison. Vous devez quitter votre maison actuelle avant d'en créer une nouvelle."
);
}
// Vérifier si le nom est déjà pris
const existingHouse = await prisma.house.findFirst({
where: {
name: {
equals: data.name.trim(),
mode: "insensitive",
},
},
});
if (existingHouse) {
throw new ConflictError("Ce nom de maison est déjà utilisé");
}
// Récupérer les points à attribuer depuis les préférences du site
const sitePreferences =
await sitePreferencesService.getOrCreateSitePreferences();
const pointsToAward =
(sitePreferences as SitePreferencesWithHousePoints).houseCreatePoints ??
100;
console.log(
"[HouseService] Creating house - points to award:",
pointsToAward,
"preferences:",
sitePreferences
);
// Créer la maison et ajouter le créateur comme OWNER, puis attribuer les points
return prisma.$transaction(async (tx) => {
const house = await tx.house.create({
data: {
name: data.name.trim(),
description: data.description?.trim() || null,
creatorId: data.creatorId,
memberships: {
create: {
userId: data.creatorId,
role: "OWNER",
},
},
},
});
// Attribuer les points au créateur
await tx.user.update({
where: { id: data.creatorId },
data: {
score: {
increment: pointsToAward,
},
},
});
return house;
});
}
/**
* Met à jour une maison
*/
async updateHouse(
houseId: string,
userId: string,
data: UpdateHouseInput
): Promise<House> {
// Vérifier que l'utilisateur est propriétaire ou admin
const isAuthorized = await this.isUserOwnerOrAdmin(userId, houseId);
if (!isAuthorized) {
throw new ForbiddenError(
"Vous n'avez pas les permissions pour modifier cette maison"
);
}
const updateData: Prisma.HouseUpdateInput = {};
if (data.name !== undefined) {
if (!data.name || data.name.trim().length === 0) {
throw new ValidationError("Le nom de la maison est requis", "name");
}
if (
data.name.length < HOUSE_NAME_MIN_LENGTH ||
data.name.length > HOUSE_NAME_MAX_LENGTH
) {
throw new ValidationError(
`Le nom de la maison doit contenir entre ${HOUSE_NAME_MIN_LENGTH} et ${HOUSE_NAME_MAX_LENGTH} caractères`,
"name"
);
}
// Vérifier si le nom est déjà pris par une autre maison
const existingHouse = await prisma.house.findFirst({
where: {
name: {
equals: data.name.trim(),
mode: "insensitive",
},
NOT: { id: houseId },
},
});
if (existingHouse) {
throw new ConflictError("Ce nom de maison est déjà utilisé");
}
updateData.name = data.name.trim();
}
if (data.description !== undefined) {
if (
data.description &&
data.description.length > HOUSE_DESCRIPTION_MAX_LENGTH
) {
throw new ValidationError(
`La description ne peut pas dépasser ${HOUSE_DESCRIPTION_MAX_LENGTH} caractères`,
"description"
);
}
updateData.description = data.description?.trim() || null;
}
return prisma.house.update({
where: { id: houseId },
data: updateData,
});
}
/**
* Supprime une maison
*/
async deleteHouse(houseId: string, userId: string): Promise<void> {
// Vérifier que l'utilisateur est propriétaire
const isOwner = await this.isUserOwner(userId, houseId);
if (!isOwner) {
throw new ForbiddenError("Seul le propriétaire peut supprimer la maison");
}
// Récupérer la maison pour obtenir le créateur
const house = await prisma.house.findUnique({
where: { id: houseId },
select: { creatorId: true },
});
if (!house) {
throw new NotFoundError("Maison");
}
// Récupérer les points à enlever depuis les préférences du site
const sitePreferences =
await sitePreferencesService.getOrCreateSitePreferences();
const pointsToDeduct =
(sitePreferences as SitePreferencesWithHousePoints).houseCreatePoints ??
100;
console.log(
"[HouseService] Deleting house - points to deduct:",
pointsToDeduct,
"creatorId:",
house.creatorId
);
// Supprimer la maison et enlever les points au créateur
await prisma.$transaction(async (tx) => {
// Enlever les points au créateur
await tx.user.update({
where: { id: house.creatorId },
data: {
score: {
decrement: pointsToDeduct,
},
},
});
// Supprimer la maison (cela supprimera automatiquement les membreships, invitations, etc. grâce aux CASCADE)
await tx.house.delete({
where: { id: houseId },
});
});
}
/**
* Invite un utilisateur à rejoindre une maison
*/
async inviteUser(data: InviteUserInput): Promise<HouseInvitation> {
// Vérifier que l'inviteur est membre de la maison
const isMember = await this.isUserMemberOfHouse(
data.inviterId,
data.houseId
);
if (!isMember) {
throw new ForbiddenError(
"Vous devez être membre de la maison pour inviter quelqu'un"
);
}
// Vérifier que l'invité n'est pas déjà membre
const isAlreadyMember = await this.isUserMemberOfHouse(
data.inviteeId,
data.houseId
);
if (isAlreadyMember) {
throw new ConflictError("Cet utilisateur est déjà membre de la maison");
}
// Vérifier que l'invité n'est pas déjà dans une autre maison
const existingMembership = await prisma.houseMembership.findFirst({
where: { userId: data.inviteeId },
});
if (existingMembership) {
throw new ConflictError(
"Cet utilisateur est déjà membre d'une autre maison"
);
}
// Vérifier s'il existe déjà une invitation (peu importe le statut)
const existingInvitation = await prisma.houseInvitation.findUnique({
where: {
houseId_inviteeId: {
houseId: data.houseId,
inviteeId: data.inviteeId,
},
},
});
if (existingInvitation) {
if (existingInvitation.status === "PENDING") {
throw new ConflictError(
"Une invitation est déjà en attente pour cet utilisateur"
);
}
// Si l'invitation existe avec un autre statut, on la réinitialise
return prisma.houseInvitation.update({
where: {
houseId_inviteeId: {
houseId: data.houseId,
inviteeId: data.inviteeId,
},
},
data: {
inviterId: data.inviterId,
status: "PENDING",
},
});
}
// Créer l'invitation
return prisma.houseInvitation.create({
data: {
houseId: data.houseId,
inviterId: data.inviterId,
inviteeId: data.inviteeId,
status: "PENDING",
},
});
}
/**
* Accepte une invitation
*/
async acceptInvitation(
invitationId: string,
userId: string
): Promise<HouseMembership> {
const invitation = await prisma.houseInvitation.findUnique({
where: { id: invitationId },
});
if (!invitation) {
throw new NotFoundError("Invitation");
}
if (invitation.inviteeId !== userId) {
throw new ForbiddenError("Cette invitation ne vous est pas destinée");
}
if (invitation.status !== "PENDING") {
throw new ConflictError("Cette invitation n'est plus valide");
}
// Vérifier que l'utilisateur n'est pas déjà dans une maison
const existingMembership = await prisma.houseMembership.findFirst({
where: { userId },
});
if (existingMembership) {
throw new ConflictError(
"Vous êtes déjà membre d'une maison. Vous devez quitter votre maison actuelle avant d'accepter cette invitation."
);
}
// Récupérer les points à attribuer depuis les préférences du site
const sitePreferences =
await sitePreferencesService.getOrCreateSitePreferences();
const pointsToAward =
(sitePreferences as SitePreferencesWithHousePoints).houseJoinPoints ??
100;
console.log(
"[HouseService] Accepting invitation - points to award:",
pointsToAward,
"userId:",
userId
);
// Créer le membership et mettre à jour l'invitation
return prisma.$transaction(async (tx) => {
const membership = await tx.houseMembership.create({
data: {
houseId: invitation.houseId,
userId: invitation.inviteeId,
role: "MEMBER",
},
});
await tx.houseInvitation.update({
where: { id: invitationId },
data: { status: "ACCEPTED" },
});
// Annuler toutes les autres invitations en attente pour cet utilisateur
await tx.houseInvitation.updateMany({
where: {
inviteeId: userId,
status: "PENDING",
id: { not: invitationId },
},
data: { status: "CANCELLED" },
});
// Annuler toutes les demandes en attente pour cet utilisateur
await tx.houseRequest.updateMany({
where: {
requesterId: userId,
status: "PENDING",
},
data: { status: "CANCELLED" },
});
// Attribuer les points à l'utilisateur qui rejoint
await tx.user.update({
where: { id: userId },
data: {
score: {
increment: pointsToAward,
},
},
});
return membership;
});
}
/**
* Refuse une invitation
*/
async rejectInvitation(invitationId: string, userId: string): Promise<void> {
const invitation = await prisma.houseInvitation.findUnique({
where: { id: invitationId },
});
if (!invitation) {
throw new NotFoundError("Invitation");
}
if (invitation.inviteeId !== userId) {
throw new ForbiddenError("Cette invitation ne vous est pas destinée");
}
if (invitation.status !== "PENDING") {
throw new ConflictError("Cette invitation n'est plus valide");
}
await prisma.houseInvitation.update({
where: { id: invitationId },
data: { status: "REJECTED" },
});
}
/**
* Annule une invitation (par l'inviteur)
*/
async cancelInvitation(invitationId: string, userId: string): Promise<void> {
const invitation = await prisma.houseInvitation.findUnique({
where: { id: invitationId },
});
if (!invitation) {
throw new NotFoundError("Invitation");
}
if (invitation.inviterId !== userId) {
throw new ForbiddenError(
"Vous ne pouvez annuler que vos propres invitations"
);
}
if (invitation.status !== "PENDING") {
throw new ConflictError("Cette invitation n'est plus valide");
}
await prisma.houseInvitation.update({
where: { id: invitationId },
data: { status: "CANCELLED" },
});
}
/**
* Demande à rejoindre une maison
*/
async requestToJoin(data: RequestToJoinInput): Promise<HouseRequest> {
// Vérifier que l'utilisateur n'est pas déjà membre
const isMember = await this.isUserMemberOfHouse(
data.requesterId,
data.houseId
);
if (isMember) {
throw new ConflictError("Vous êtes déjà membre de cette maison");
}
// Vérifier que l'utilisateur n'est pas déjà dans une autre maison
const existingMembership = await prisma.houseMembership.findFirst({
where: { userId: data.requesterId },
});
if (existingMembership) {
throw new ConflictError(
"Vous êtes déjà membre d'une maison. Vous devez quitter votre maison actuelle avant de faire une demande."
);
}
// Vérifier s'il existe déjà une demande
const existingRequest = await prisma.houseRequest.findUnique({
where: {
houseId_requesterId: {
houseId: data.houseId,
requesterId: data.requesterId,
},
},
});
if (existingRequest) {
if (existingRequest.status === "PENDING") {
throw new ConflictError(
"Une demande est déjà en attente pour cette maison"
);
}
// Si la demande existe mais n'est pas PENDING (REJECTED, CANCELLED), on la réactive
return prisma.houseRequest.update({
where: {
houseId_requesterId: {
houseId: data.houseId,
requesterId: data.requesterId,
},
},
data: {
status: "PENDING",
},
});
}
// Créer une nouvelle demande
return prisma.houseRequest.create({
data: {
houseId: data.houseId,
requesterId: data.requesterId,
status: "PENDING",
},
});
}
/**
* Accepte une demande d'adhésion
*/
async acceptRequest(
requestId: string,
userId: string
): Promise<HouseMembership> {
const request = await prisma.houseRequest.findUnique({
where: { id: requestId },
include: { house: true },
});
if (!request) {
throw new NotFoundError("Demande");
}
// Vérifier que l'utilisateur est propriétaire ou admin de la maison
const isAuthorized = await this.isUserOwnerOrAdmin(userId, request.houseId);
if (!isAuthorized) {
throw new ForbiddenError(
"Vous n'avez pas les permissions pour accepter cette demande"
);
}
if (request.status !== "PENDING") {
throw new ConflictError("Cette demande n'est plus valide");
}
// Vérifier que le demandeur n'est pas déjà dans une maison
const existingMembership = await prisma.houseMembership.findFirst({
where: { userId: request.requesterId },
});
if (existingMembership) {
throw new ConflictError(
"Cet utilisateur est déjà membre d'une autre maison"
);
}
// Récupérer les points à attribuer depuis les préférences du site
const sitePreferences =
await sitePreferencesService.getOrCreateSitePreferences();
const pointsToAward =
(sitePreferences as SitePreferencesWithHousePoints).houseJoinPoints ??
100;
console.log(
"[HouseService] Accepting request - points to award:",
pointsToAward,
"requesterId:",
request.requesterId
);
// Créer le membership et mettre à jour la demande
return prisma.$transaction(async (tx) => {
const membership = await tx.houseMembership.create({
data: {
houseId: request.houseId,
userId: request.requesterId,
role: "MEMBER",
},
});
await tx.houseRequest.update({
where: { id: requestId },
data: { status: "ACCEPTED" },
});
// Annuler toutes les autres demandes en attente pour cet utilisateur
await tx.houseRequest.updateMany({
where: {
requesterId: request.requesterId,
status: "PENDING",
id: { not: requestId },
},
data: { status: "CANCELLED" },
});
// Annuler toutes les invitations en attente pour cet utilisateur
await tx.houseInvitation.updateMany({
where: {
inviteeId: request.requesterId,
status: "PENDING",
},
data: { status: "CANCELLED" },
});
// Attribuer les points à l'utilisateur qui rejoint
await tx.user.update({
where: { id: request.requesterId },
data: {
score: {
increment: pointsToAward,
},
},
});
return membership;
});
}
/**
* Refuse une demande d'adhésion
*/
async rejectRequest(requestId: string, userId: string): Promise<void> {
const request = await prisma.houseRequest.findUnique({
where: { id: requestId },
});
if (!request) {
throw new NotFoundError("Demande");
}
// Vérifier que l'utilisateur est propriétaire ou admin de la maison
const isAuthorized = await this.isUserOwnerOrAdmin(userId, request.houseId);
if (!isAuthorized) {
throw new ForbiddenError(
"Vous n'avez pas les permissions pour refuser cette demande"
);
}
if (request.status !== "PENDING") {
throw new ConflictError("Cette demande n'est plus valide");
}
await prisma.houseRequest.update({
where: { id: requestId },
data: { status: "REJECTED" },
});
}
/**
* Annule une demande (par le demandeur)
*/
async cancelRequest(requestId: string, userId: string): Promise<void> {
const request = await prisma.houseRequest.findUnique({
where: { id: requestId },
});
if (!request) {
throw new NotFoundError("Demande");
}
if (request.requesterId !== userId) {
throw new ForbiddenError(
"Vous ne pouvez annuler que vos propres demandes"
);
}
if (request.status !== "PENDING") {
throw new ConflictError("Cette demande n'est plus valide");
}
await prisma.houseRequest.update({
where: { id: requestId },
data: { status: "CANCELLED" },
});
}
/**
* Retire un membre d'une maison (par un OWNER ou ADMIN)
*/
async removeMember(
houseId: string,
memberIdToRemove: string,
removerId: string
): Promise<void> {
// Vérifier que celui qui retire est OWNER ou ADMIN
const isAuthorized = await this.isUserOwnerOrAdmin(removerId, houseId);
if (!isAuthorized) {
throw new ForbiddenError(
"Vous n'avez pas les permissions pour retirer un membre"
);
}
// Récupérer les membreships
const removerMembership = await prisma.houseMembership.findUnique({
where: {
houseId_userId: {
houseId,
userId: removerId,
},
},
});
const memberToRemoveMembership = await prisma.houseMembership.findUnique({
where: {
houseId_userId: {
houseId,
userId: memberIdToRemove,
},
},
});
if (!memberToRemoveMembership) {
throw new NotFoundError("Membre");
}
// Un OWNER ne peut pas être retiré
if (memberToRemoveMembership.role === "OWNER") {
throw new ForbiddenError("Le propriétaire ne peut pas être retiré");
}
// Un ADMIN ne peut retirer que des MEMBER (pas d'autres ADMIN)
if (
removerMembership?.role === "ADMIN" &&
memberToRemoveMembership.role === "ADMIN"
) {
throw new ForbiddenError("Un admin ne peut pas retirer un autre admin");
}
// Récupérer les points à enlever depuis les préférences du site
const sitePreferences =
await sitePreferencesService.getOrCreateSitePreferences();
const pointsToDeduct =
(sitePreferences as SitePreferencesWithHousePoints).houseLeavePoints ??
100;
// Supprimer le membership et enlever les points
await prisma.$transaction(async (tx) => {
await tx.houseMembership.delete({
where: {
houseId_userId: {
houseId,
userId: memberIdToRemove,
},
},
});
// Enlever les points à l'utilisateur retiré
await tx.user.update({
where: { id: memberIdToRemove },
data: {
score: {
decrement: pointsToDeduct,
},
},
});
});
}
/**
* Quitte une maison
*/
async leaveHouse(houseId: string, userId: string): Promise<void> {
const membership = await prisma.houseMembership.findUnique({
where: {
houseId_userId: {
houseId,
userId,
},
},
});
if (!membership) {
throw new NotFoundError("Membre");
}
// Le propriétaire ne peut pas quitter sa maison
if (membership.role === "OWNER") {
throw new ForbiddenError(
"Le propriétaire ne peut pas quitter sa maison. Vous devez d'abord transférer la propriété ou supprimer la maison."
);
}
// Récupérer les points à enlever depuis les préférences du site
const sitePreferences =
await sitePreferencesService.getOrCreateSitePreferences();
const pointsToDeduct =
(sitePreferences as SitePreferencesWithHousePoints).houseLeavePoints ??
100;
console.log(
"[HouseService] Leaving house - points to deduct:",
pointsToDeduct,
"userId:",
userId
);
// Supprimer le membership et enlever les points
await prisma.$transaction(async (tx) => {
await tx.houseMembership.delete({
where: {
houseId_userId: {
houseId,
userId,
},
},
});
// Enlever les points à l'utilisateur qui quitte
await tx.user.update({
where: { id: userId },
data: {
score: {
decrement: pointsToDeduct,
},
},
});
});
}
/**
* Récupère les invitations reçues par un utilisateur
*/
async getUserInvitations(
userId: string,
status?: InvitationStatus
): Promise<HouseInvitation[]> {
return prisma.houseInvitation.findMany({
where: {
inviteeId: userId,
...(status && { status }),
},
include: {
house: true,
inviter: {
select: {
id: true,
username: true,
avatar: true,
},
},
},
orderBy: { createdAt: "desc" },
});
}
/**
* Compte les invitations en attente pour un utilisateur
*/
async getPendingInvitationsCount(userId: string): Promise<number> {
return prisma.houseInvitation.count({
where: {
inviteeId: userId,
status: "PENDING",
},
});
}
/**
* Compte les demandes d'adhésion en attente pour un utilisateur
* (demandes reçues pour les maisons dont l'utilisateur est propriétaire ou admin)
*/
async getPendingRequestsCount(userId: string): Promise<number> {
// Trouver toutes les maisons où l'utilisateur est OWNER ou ADMIN
const userHouses = await prisma.houseMembership.findMany({
where: {
userId,
role: {
in: ["OWNER", "ADMIN"],
},
},
select: {
houseId: true,
},
});
const houseIds = userHouses.map((m) => m.houseId);
if (houseIds.length === 0) {
return 0;
}
// Compter les demandes PENDING pour ces maisons
return prisma.houseRequest.count({
where: {
houseId: {
in: houseIds,
},
status: "PENDING",
},
});
}
/**
* Compte le total des invitations et demandes en attente pour un utilisateur
* - Invitations : invitations reçues par l'utilisateur (inviteeId)
* - Demandes : demandes reçues pour les maisons dont l'utilisateur est OWNER ou ADMIN
*/
async getPendingHouseActionsCount(userId: string): Promise<number> {
const [invitationsCount, requestsCount] = await Promise.all([
this.getPendingInvitationsCount(userId),
this.getPendingRequestsCount(userId),
]);
return invitationsCount + requestsCount;
}
/**
* Récupère les demandes d'une maison
*/
async getHouseRequests(
houseId: string,
status?: RequestStatus
): Promise<HouseRequest[]> {
return prisma.houseRequest.findMany({
where: {
houseId,
...(status && { status }),
},
include: {
requester: {
select: {
id: true,
username: true,
avatar: true,
},
},
},
orderBy: { createdAt: "desc" },
});
}
/**
* Récupère les invitations envoyées par une maison
*/
async getHouseInvitations(
houseId: string,
status?: InvitationStatus
): Promise<HouseInvitation[]> {
return prisma.houseInvitation.findMany({
where: {
houseId,
...(status && { status }),
},
include: {
invitee: {
select: {
id: true,
username: true,
avatar: true,
},
},
inviter: {
select: {
id: true,
username: true,
avatar: true,
},
},
},
orderBy: { createdAt: "desc" },
});
}
/**
* Récupère une invitation par son ID (avec seulement houseId)
*/
async getInvitationById(id: string): Promise<{ houseId: string } | null> {
return prisma.houseInvitation.findUnique({
where: { id },
select: { houseId: true },
});
}
}
export const houseService = new HouseService();