docker compose front, modulo de validacion de preguntas, crud de participante, get participantes por evento y evento por participantes
This commit is contained in:
@@ -1,7 +1,22 @@
|
||||
import { IsEmail } from "class-validator";
|
||||
import { IsEmail, IsNotEmpty, IsNumber } from "class-validator";
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
|
||||
export class CreateParticipanteDto {
|
||||
@IsEmail()
|
||||
correo: string
|
||||
id_tipo_user: number
|
||||
@ApiProperty({
|
||||
description: 'Correo electrónico del participante',
|
||||
example: 'usuario@ejemplo.com',
|
||||
required: true
|
||||
})
|
||||
@IsEmail({}, { message: 'El correo electrónico debe tener un formato válido' })
|
||||
@IsNotEmpty({ message: 'El correo electrónico es requerido' })
|
||||
correo: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'ID del tipo de usuario',
|
||||
example: 1,
|
||||
required: true
|
||||
})
|
||||
@IsNumber({}, { message: 'El ID del tipo de usuario debe ser un número' })
|
||||
@IsNotEmpty({ message: 'El ID del tipo de usuario es requerido' })
|
||||
id_tipo_user: number;
|
||||
}
|
||||
@@ -1,3 +1,22 @@
|
||||
import { IsEmail, IsOptional, IsNumber } from "class-validator";
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
|
||||
export class UpdateParticipanteDto {
|
||||
correo: string
|
||||
@ApiProperty({
|
||||
description: 'Nuevo correo electrónico del participante',
|
||||
example: 'nuevo_correo@ejemplo.com',
|
||||
required: false
|
||||
})
|
||||
@IsEmail({}, { message: 'El correo electrónico debe tener un formato válido' })
|
||||
@IsOptional()
|
||||
correo?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Nuevo ID del tipo de usuario',
|
||||
example: 2,
|
||||
required: false
|
||||
})
|
||||
@IsNumber({}, { message: 'El ID del tipo de usuario debe ser un número' })
|
||||
@IsOptional()
|
||||
id_tipo_user?: number;
|
||||
}
|
||||
@@ -3,58 +3,40 @@ import { Participante } from './participante.entity';
|
||||
import { ParticipanteService } from './participante.service';
|
||||
import { CreateParticipanteDto } from './dto/create-participante.dto';
|
||||
import { UpdateParticipanteDto } from './dto/update.participante.dto';
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
|
||||
import { ParticipanteApiDocumentation } from './participante.documentation';
|
||||
|
||||
@ApiTags('Participantes') // Agrupa los endpoints en Swagger
|
||||
@ParticipanteApiDocumentation.ApiController
|
||||
@Controller('participante')
|
||||
export class ParticipanteController {
|
||||
constructor(private participanteService: ParticipanteService) {}
|
||||
|
||||
@ApiOperation({ summary: 'Obtener todos los participantes' })
|
||||
@ApiResponse({ status: 200, description: 'Lista de participantes obtenida correctamente.' })
|
||||
@ParticipanteApiDocumentation.ApiGetAll
|
||||
@Get()
|
||||
getParticipantes(): Promise<Participante[]> {
|
||||
return this.participanteService.getParticipantes()
|
||||
return this.participanteService.getParticipantes();
|
||||
}
|
||||
|
||||
@ParticipanteApiDocumentation.ApiGetOne
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Obtener un participante por ID' })
|
||||
@ApiParam({ name: 'id', description: 'ID del participante', example: 1 })
|
||||
@ApiResponse({ status: 200, description: 'Participante obtenido correctamente.' })
|
||||
@ApiResponse({ status: 404, description: 'Participante no encontrado.' })
|
||||
getParticipante(@Param('id', ParseIntPipe) id: number) {
|
||||
return this.participanteService.getParticipante(id);
|
||||
}
|
||||
|
||||
@ParticipanteApiDocumentation.ApiCreate
|
||||
@Post()
|
||||
@ApiOperation({ summary: 'Registrar un nuevo participante' })
|
||||
@ApiBody({
|
||||
description: 'Datos del participante a registrar',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
correo: { type: 'string', example: 'user@example.com' },
|
||||
id_tipo_user: { type: 'integer', example: 2 }
|
||||
}
|
||||
}
|
||||
})
|
||||
@ApiResponse({ status: 201, description: 'Participante registrado exitosamente.' })
|
||||
@ApiResponse({ status: 400, description: 'Datos inválidos.' })
|
||||
createParticipante(@Body() newParticipante: CreateParticipanteDto) {
|
||||
return this.participanteService.createParticipante(newParticipante);
|
||||
}
|
||||
|
||||
@ParticipanteApiDocumentation.ApiRemove
|
||||
@Delete(':id')
|
||||
deleteParticipante(@Param('id', ParseIntPipe) id: number) {
|
||||
return this.participanteService.deleteParticipante(id)
|
||||
return this.participanteService.deleteParticipante(id);
|
||||
}
|
||||
|
||||
|
||||
@ParticipanteApiDocumentation.ApiUpdate
|
||||
@Patch(':id')
|
||||
updateParticipante(@Param('correo') id: number, @Body() participante: UpdateParticipanteDto) {
|
||||
updateParticipante(@Param('id', ParseIntPipe) id: number, @Body() participante: UpdateParticipanteDto) {
|
||||
return this.participanteService.updateParticipante(id, participante);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
import { ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { applyDecorators } from '@nestjs/common';
|
||||
|
||||
export class ParticipanteApiDocumentation {
|
||||
// Decorador para toda la clase del controlador
|
||||
static ApiController = ApiTags('Participantes');
|
||||
|
||||
// Documentación para crear un participante
|
||||
static ApiCreate = applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Registrar un nuevo participante',
|
||||
description: 'Crea un nuevo registro de participante en el sistema'
|
||||
}),
|
||||
ApiBody({
|
||||
description: 'Datos del participante a registrar',
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['correo', 'id_tipo_user'],
|
||||
properties: {
|
||||
correo: {
|
||||
type: 'string',
|
||||
format: 'email',
|
||||
example: 'usuario@ejemplo.com',
|
||||
description: 'Correo electrónico del participante'
|
||||
},
|
||||
id_tipo_user: {
|
||||
type: 'integer',
|
||||
example: 1,
|
||||
description: 'ID del tipo de usuario'
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({
|
||||
status: 201,
|
||||
description: 'Participante registrado exitosamente',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id_participante: { type: 'number', example: 1 },
|
||||
correo: { type: 'string', example: 'usuario@ejemplo.com' },
|
||||
id_tipo_user: { type: 'number', example: 1 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({ status: 400, description: 'Datos del participante inválidos' }),
|
||||
ApiResponse({ status: 409, description: 'El participante ya existe' })
|
||||
);
|
||||
|
||||
// Documentación para obtener todos los participantes
|
||||
static ApiGetAll = applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Obtener todos los participantes',
|
||||
description: 'Retorna una lista de todos los participantes registrados'
|
||||
}),
|
||||
ApiResponse({
|
||||
status: 200,
|
||||
description: 'Lista de participantes obtenida correctamente',
|
||||
schema: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id_participante: { type: 'number', example: 1 },
|
||||
correo: { type: 'string', example: 'usuario@ejemplo.com' },
|
||||
id_tipo_user: { type: 'number', example: 1 },
|
||||
tipo_user: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id_tipo_user: { type: 'number', example: 1 },
|
||||
tipo_user: { type: 'string', example: 'Estudiante' }
|
||||
}
|
||||
},
|
||||
participanteEventos: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id_participante_evento: { type: 'number', example: 1 },
|
||||
id_participante: { type: 'number', example: 1 },
|
||||
id_evento: { type: 'number', example: 1 },
|
||||
fecha_inscripcion: { type: 'string', format: 'date-time', example: '2025-04-01T10:00:00Z' },
|
||||
estatus: { type: 'boolean', example: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Documentación para obtener un participante por ID
|
||||
static ApiGetOne = applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Obtener un participante por ID',
|
||||
description: 'Retorna los datos de un participante específico según su ID'
|
||||
}),
|
||||
ApiParam({
|
||||
name: 'id',
|
||||
description: 'ID del participante',
|
||||
type: 'number',
|
||||
example: 1
|
||||
}),
|
||||
ApiResponse({
|
||||
status: 200,
|
||||
description: 'Participante obtenido correctamente',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id_participante: { type: 'number', example: 1 },
|
||||
correo: { type: 'string', example: 'usuario@ejemplo.com' },
|
||||
id_tipo_user: { type: 'number', example: 1 },
|
||||
tipo_user: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id_tipo_user: { type: 'number', example: 1 },
|
||||
tipo_user: { type: 'string', example: 'Estudiante' }
|
||||
}
|
||||
},
|
||||
participanteEventos: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id_participante_evento: { type: 'number', example: 1 },
|
||||
id_participante: { type: 'number', example: 1 },
|
||||
id_evento: { type: 'number', example: 1 },
|
||||
fecha_inscripcion: { type: 'string', format: 'date-time', example: '2025-04-01T10:00:00Z' },
|
||||
estatus: { type: 'boolean', example: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({ status: 404, description: 'Participante no encontrado' })
|
||||
);
|
||||
|
||||
// Documentación para actualizar un participante
|
||||
static ApiUpdate = applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Actualizar datos de un participante',
|
||||
description: 'Actualiza la información de un participante existente'
|
||||
}),
|
||||
ApiParam({
|
||||
name: 'id',
|
||||
description: 'ID del participante a actualizar',
|
||||
type: 'number',
|
||||
example: 1
|
||||
}),
|
||||
ApiBody({
|
||||
description: 'Datos a actualizar del participante',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
correo: {
|
||||
type: 'string',
|
||||
format: 'email',
|
||||
example: 'nuevo_correo@ejemplo.com',
|
||||
description: 'Nuevo correo electrónico del participante'
|
||||
},
|
||||
id_tipo_user: {
|
||||
type: 'integer',
|
||||
example: 2,
|
||||
description: 'Nuevo tipo de usuario'
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({
|
||||
status: 200,
|
||||
description: 'Participante actualizado correctamente',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id_participante: { type: 'number', example: 1 },
|
||||
correo: { type: 'string', example: 'nuevo_correo@ejemplo.com' },
|
||||
id_tipo_user: { type: 'number', example: 2 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({ status: 400, description: 'Datos de actualización inválidos' }),
|
||||
ApiResponse({ status: 404, description: 'Participante no encontrado' })
|
||||
);
|
||||
|
||||
// Documentación para eliminar un participante
|
||||
static ApiRemove = applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Eliminar un participante',
|
||||
description: 'Elimina permanentemente un participante por su ID'
|
||||
}),
|
||||
ApiParam({
|
||||
name: 'id',
|
||||
description: 'ID del participante a eliminar',
|
||||
type: 'number',
|
||||
example: 1
|
||||
}),
|
||||
ApiResponse({
|
||||
status: 200,
|
||||
description: 'Participante eliminado correctamente',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
affected: { type: 'number', example: 1 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({ status: 404, description: 'Participante no encontrado' })
|
||||
);
|
||||
}
|
||||
@@ -1,62 +1,99 @@
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { BadRequestException, ConflictException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Participante } from './participante.entity';
|
||||
import { Repository } from 'typeorm';
|
||||
import { CreateParticipanteDto } from './dto/create-participante.dto';
|
||||
//import { UpdateAdminDto } from 'src/admin/dto/update.admin.dto';
|
||||
import { UpdateParticipanteDto } from './dto/update.participante.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ParticipanteService {
|
||||
|
||||
constructor(
|
||||
@InjectRepository(Participante) private participanteRepository: Repository<Participante>
|
||||
) {}
|
||||
|
||||
async createParticipante(participante: CreateParticipanteDto) {
|
||||
/**
|
||||
* Crea un nuevo participante
|
||||
* @param participante Datos del participante a crear
|
||||
* @returns El participante creado
|
||||
* @throws ConflictException si ya existe un participante con el mismo correo
|
||||
*/
|
||||
async createParticipante(participante: CreateParticipanteDto): Promise<Participante> {
|
||||
// Verificar si ya existe un participante con el mismo correo
|
||||
const participanteFound = await this.participanteRepository.findOne({
|
||||
where: {
|
||||
correo: participante.correo
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (participanteFound)
|
||||
return new HttpException('Participante already exists', HttpStatus.CONFLICT)
|
||||
if (participanteFound) {
|
||||
throw new ConflictException(`Ya existe un participante con el correo ${participante.correo}`);
|
||||
}
|
||||
|
||||
return this.participanteRepository.save(participante)
|
||||
const nuevoParticipante = this.participanteRepository.create(participante);
|
||||
return this.participanteRepository.save(nuevoParticipante);
|
||||
}
|
||||
|
||||
getParticipantes() {
|
||||
/**
|
||||
* Obtiene todos los participantes
|
||||
* @returns Lista de participantes con sus relaciones
|
||||
*/
|
||||
async getParticipantes(): Promise<Participante[]> {
|
||||
return this.participanteRepository.find({
|
||||
relations: ['tipo_user', 'participanteEventos']
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async getParticipante(id_participante: number) {
|
||||
/**
|
||||
* Obtiene un participante por su ID
|
||||
* @param id_participante ID del participante a buscar
|
||||
* @returns El participante encontrado con sus relaciones
|
||||
* @throws NotFoundException si no se encuentra el participante
|
||||
*/
|
||||
async getParticipante(id_participante: number): Promise<Participante> {
|
||||
const participanteFound = await this.participanteRepository.findOne({
|
||||
where: {
|
||||
id_participante
|
||||
},
|
||||
relations: ['tipo_user', 'participanteEventos']
|
||||
})
|
||||
});
|
||||
|
||||
if (!participanteFound)
|
||||
return new HttpException('Participante not found', HttpStatus.NOT_FOUND);
|
||||
if (!participanteFound) {
|
||||
throw new NotFoundException(`Participante con ID ${id_participante} no encontrado`);
|
||||
}
|
||||
|
||||
return participanteFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Elimina un participante por su ID
|
||||
* @param id_participante ID del participante a eliminar
|
||||
* @returns Resultado de la eliminación
|
||||
* @throws NotFoundException si no se encuentra el participante
|
||||
*/
|
||||
async deleteParticipante(id_participante: number) {
|
||||
const result = await this.participanteRepository.delete({ id_participante })
|
||||
const result = await this.participanteRepository.delete({ id_participante });
|
||||
|
||||
if (result.affected === 0) {
|
||||
return new HttpException('Participante not found', HttpStatus.NOT_FOUND);
|
||||
throw new NotFoundException(`Participante con ID ${id_participante} no encontrado`);
|
||||
}
|
||||
|
||||
return result;
|
||||
return {
|
||||
success: true,
|
||||
message: `Participante con ID ${id_participante} eliminado correctamente`,
|
||||
affected: result.affected
|
||||
};
|
||||
}
|
||||
|
||||
async updateParticipante(id_participante: number, participante: UpdateParticipanteDto) {
|
||||
/**
|
||||
* Actualiza los datos de un participante
|
||||
* @param id_participante ID del participante a actualizar
|
||||
* @param participante Datos actualizados del participante
|
||||
* @returns El participante actualizado
|
||||
* @throws NotFoundException si no se encuentra el participante
|
||||
* @throws BadRequestException si los datos son inválidos
|
||||
*/
|
||||
async updateParticipante(id_participante: number, participante: UpdateParticipanteDto): Promise<Participante> {
|
||||
// Verificar si el participante existe
|
||||
const participanteFound = await this.participanteRepository.findOne({
|
||||
where: {
|
||||
id_participante
|
||||
@@ -64,11 +101,24 @@ export class ParticipanteService {
|
||||
});
|
||||
|
||||
if (!participanteFound) {
|
||||
return new HttpException('Participante not found', HttpStatus.NOT_FOUND)
|
||||
throw new NotFoundException(`Participante con ID ${id_participante} no encontrado`);
|
||||
}
|
||||
|
||||
const updateParticipante = Object.assign(participanteFound, participante)
|
||||
return this.participanteRepository.save(updateParticipante)
|
||||
}
|
||||
// Si se está actualizando el correo, verificar que no exista otro participante con ese correo
|
||||
if (participante.correo && participante.correo !== participanteFound.correo) {
|
||||
const existingParticipante = await this.participanteRepository.findOne({
|
||||
where: {
|
||||
correo: participante.correo
|
||||
}
|
||||
});
|
||||
|
||||
if (existingParticipante && existingParticipante.id_participante !== id_participante) {
|
||||
throw new ConflictException(`Ya existe otro participante con el correo ${participante.correo}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar el participante
|
||||
const updateParticipante = Object.assign(participanteFound, participante);
|
||||
return this.participanteRepository.save(updateParticipante);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user