Refactor cuestionario module to support event creation with associated questionnaire, including new DTO and enhanced error handling.
This commit is contained in:
@@ -20,6 +20,7 @@ import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { diskStorage } from 'multer';
|
||||
import { extname } from 'path';
|
||||
import { CuestionarioApiDocumentation } from './docs/cuestionario.documentation';
|
||||
import { CreateEventoWithCuestionarioDto } from './dto/create-evento-cuestionario.dto';
|
||||
|
||||
@Controller('cuestionario')
|
||||
@CuestionarioApiDocumentation.ApiController
|
||||
@@ -35,7 +36,7 @@ export class CuestionarioController {
|
||||
@Post('withEvento')
|
||||
@CuestionarioApiDocumentation.ApiCreateWithEvento
|
||||
createCuestionarioEvento(
|
||||
@Body() createCuestionarioDto: CreateCuestionarioEventoDto,
|
||||
@Body() createCuestionarioDto: CreateEventoWithCuestionarioDto,
|
||||
) {
|
||||
return this.cuestionarioService.createCuestionarioEvento(
|
||||
createCuestionarioDto,
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import {
|
||||
Injectable,
|
||||
InternalServerErrorException,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, DataSource, In } from 'typeorm';
|
||||
import {
|
||||
@@ -19,6 +23,7 @@ import {
|
||||
} from '../tipo_pregunta/entities/tipo_pregunta.entity';
|
||||
import { Evento } from '../evento/entities/evento.entity';
|
||||
import { EventoService } from '../evento/evento.service';
|
||||
import { CreateEventoWithCuestionarioDto } from './dto/create-evento-cuestionario.dto';
|
||||
|
||||
@Injectable()
|
||||
export class CuestionarioService {
|
||||
@@ -165,80 +170,72 @@ export class CuestionarioService {
|
||||
return {
|
||||
success: true,
|
||||
message: 'Cuestionario creado exitosamente',
|
||||
cuestionario: savedCuestionario,
|
||||
id_cuestionario: savedCuestionario.id_cuestionario,
|
||||
id_evento: savedCuestionario.id_evento,
|
||||
};
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
throw new InternalServerErrorException(
|
||||
'Error al crear el cuestionario y evento',
|
||||
error.message,
|
||||
);
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
async createCuestionarioEvento(
|
||||
createCuestionarioDto: CreateCuestionarioEventoDto,
|
||||
createCuestionarioDto: CreateEventoWithCuestionarioDto,
|
||||
) {
|
||||
const { evento, cuestionario } = createCuestionarioDto;
|
||||
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
// Verificar si se especificó un evento y búscarlo/crearlo
|
||||
let idEvento: number | undefined = undefined;
|
||||
// Buscar si ya existe un evento con el nombre proporcionado
|
||||
let eventoExistente = await this.eventoRepository.findOne({
|
||||
where: { nombre_evento: evento.nombre_evento },
|
||||
});
|
||||
|
||||
if (createCuestionarioDto.evento) {
|
||||
// Buscar si el evento ya existe
|
||||
let evento = await this.eventoRepository.findOne({
|
||||
where: { nombre_evento: createCuestionarioDto.evento },
|
||||
});
|
||||
|
||||
// Si no existe, crearlo
|
||||
if (!evento) {
|
||||
const nuevoEvento = {
|
||||
nombre_evento: createCuestionarioDto.evento,
|
||||
tipo_evento: 'Predeterminado',
|
||||
fecha_inicio: createCuestionarioDto.fecha_inicio || new Date(),
|
||||
fecha_fin:
|
||||
createCuestionarioDto.fecha_fin ||
|
||||
new Date(Date.now() + 86400000), // 1 día después si no hay fecha
|
||||
};
|
||||
|
||||
evento = await queryRunner.manager.save(Evento, nuevoEvento);
|
||||
} else {
|
||||
console.log(
|
||||
`Evento encontrado: ${evento.nombre_evento}, ID: ${evento.id_evento}`,
|
||||
);
|
||||
}
|
||||
|
||||
idEvento = evento.id_evento;
|
||||
// Si el evento no existe, crearlo
|
||||
if (!eventoExistente) {
|
||||
eventoExistente = queryRunner.manager.create(Evento, evento);
|
||||
await queryRunner.manager.save(eventoExistente);
|
||||
console.log(`Creando nuevo evento: ${evento.nombre_evento}`);
|
||||
}
|
||||
|
||||
// 1. Crear cuestionario
|
||||
const cuestionario = new Cuestionario();
|
||||
cuestionario.nombre_form = createCuestionarioDto.nombre_form;
|
||||
cuestionario.descripcion = createCuestionarioDto.descripcion;
|
||||
cuestionario.fecha_inicio = new Date(createCuestionarioDto.fecha_inicio);
|
||||
cuestionario.fecha_fin = new Date(createCuestionarioDto.fecha_fin);
|
||||
cuestionario.id_tipo_cuestionario =
|
||||
createCuestionarioDto.id_tipo_cuestionario;
|
||||
cuestionario.contador_secciones =
|
||||
createCuestionarioDto.secciones?.length || 0;
|
||||
cuestionario.editable = true;
|
||||
// Crear el cuestionario asociado al evento
|
||||
const cuestionarioEntity = queryRunner.manager.create(Cuestionario, {
|
||||
nombre_form: cuestionario.nombre_form,
|
||||
descripcion: cuestionario.descripcion,
|
||||
fecha_inicio: new Date(cuestionario.fecha_inicio),
|
||||
fecha_fin: new Date(cuestionario.fecha_fin),
|
||||
id_tipo_cuestionario: cuestionario.id_tipo_cuestionario,
|
||||
contador_secciones: cuestionario.secciones?.length || 0,
|
||||
editable: true,
|
||||
id_evento: eventoExistente.id_evento, // Asociar el evento creado
|
||||
cupo_maximo: cuestionario.cupo_maximo,
|
||||
});
|
||||
const savedCuestionario =
|
||||
await queryRunner.manager.save(cuestionarioEntity);
|
||||
|
||||
// Asociar el cuestionario con el evento si existe
|
||||
if (idEvento !== undefined) {
|
||||
cuestionario.id_evento = idEvento;
|
||||
}
|
||||
console.log(
|
||||
`Cuestionario creado: ${savedCuestionario.nombre_form}, ID: ${savedCuestionario.id_cuestionario}`,
|
||||
);
|
||||
|
||||
const savedCuestionario = await queryRunner.manager.save(cuestionario);
|
||||
|
||||
// 2. Crear secciones y vincularlas al cuestionario
|
||||
// Crear las secciones del cuestionario
|
||||
if (
|
||||
createCuestionarioDto.secciones &&
|
||||
createCuestionarioDto.secciones.length > 0
|
||||
createCuestionarioDto.cuestionario.secciones &&
|
||||
createCuestionarioDto.cuestionario.secciones.length > 0
|
||||
) {
|
||||
for (let i = 0; i < createCuestionarioDto.secciones.length; i++) {
|
||||
const seccionDto = createCuestionarioDto.secciones[i];
|
||||
for (
|
||||
let i = 0;
|
||||
i < createCuestionarioDto.cuestionario.secciones.length;
|
||||
i++
|
||||
) {
|
||||
const seccionDto = createCuestionarioDto.cuestionario.secciones[i];
|
||||
|
||||
// Crear sección
|
||||
const seccion = new Seccion();
|
||||
@@ -335,10 +332,11 @@ export class CuestionarioService {
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
cuestionario: savedCuestionario,
|
||||
message: 'Cuestionario y evento creados exitosamente',
|
||||
id_cuestionario: savedCuestionario.id_cuestionario,
|
||||
id_evento: eventoExistente.id_evento,
|
||||
};
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
CreateCuestionarioDto,
|
||||
CreateCuestionarioEventoDto,
|
||||
} from '../dto/create-cuestionario.dto';
|
||||
import { ejemploCuestionarioAlumno } from './cuestionario.examples';
|
||||
import { ejemploCreateEventoWithCuestionario, ejemploCuestionarioAlumno } from './cuestionario.examples';
|
||||
|
||||
export class CuestionarioApiDocumentation {
|
||||
// Decoradores para toda la clase del controlador
|
||||
@@ -47,18 +47,6 @@ export class CuestionarioApiDocumentation {
|
||||
type: 'number',
|
||||
example: 1,
|
||||
}),
|
||||
/* ApiResponse({
|
||||
status: 200,
|
||||
description:
|
||||
'Formulario completo con todas sus secciones, preguntas y opciones. Las preguntas incluyen el atributo "validacion" si tienen reglas de validación específicas (como "correo", "cuenta_alumno", "telefono", "nombre", etc.)',
|
||||
type: FormularioDto,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: { $ref: getSchemaPath(FormularioDto) },
|
||||
example: feriaSexualidad,
|
||||
},
|
||||
},
|
||||
}), */
|
||||
ApiResponse({ status: 404, description: 'Cuestionario no encontrado' }),
|
||||
);
|
||||
|
||||
@@ -79,39 +67,7 @@ export class CuestionarioApiDocumentation {
|
||||
ApiBody({
|
||||
type: CreateCuestionarioEventoDto,
|
||||
examples: {
|
||||
ejemplo_con_evento_nuevo: {
|
||||
summary: 'Cuestionario que crea evento automáticamente',
|
||||
value: {
|
||||
nombre_form: 'Registro de participantes',
|
||||
descripcion: 'Formulario para inscripción de asistentes.',
|
||||
evento: 'Foro Juvenil de Tecnología 2025',
|
||||
fecha_inicio: '2025-07-20T10:00:00',
|
||||
fecha_fin: '2025-07-20T17:00:00',
|
||||
id_tipo_cuestionario: 2,
|
||||
secciones: [
|
||||
{
|
||||
titulo: 'Datos generales',
|
||||
descripcion: 'Recopilación de información personal',
|
||||
preguntas: [
|
||||
{
|
||||
titulo: '¿Cuál es tu edad?',
|
||||
tipo: 'Abierta',
|
||||
obligatoria: true,
|
||||
},
|
||||
{
|
||||
titulo: '¿Cómo te enteraste del evento?',
|
||||
tipo: 'Multiple',
|
||||
opciones: [
|
||||
{ valor: 'Redes sociales' },
|
||||
{ valor: 'Correo electrónico' },
|
||||
{ valor: 'Por un amigo' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
ejemplo_con_evento_nuevo: ejemploCreateEventoWithCuestionario,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,85 @@
|
||||
import { ejemploFeriaSexualidad } from 'src/evento/docs/evento.examples';
|
||||
import { TiposValidacion } from 'src/pregunta/entities/pregunta.entity';
|
||||
|
||||
export const ejemploCreateEventoWithCuestionario = {
|
||||
summary: 'Crear un evento con cuestionario asociado',
|
||||
value: {
|
||||
evento: ejemploFeriaSexualidad.value,
|
||||
cuestionario: {
|
||||
nombre_form: 'Registro',
|
||||
descripcion:
|
||||
'Con este formulario estaras reservando tu lugar en el evento. Por favor, completa todos los campos obligatorios.',
|
||||
fecha_inicio: '2025-08-01T08:00:00',
|
||||
fecha_fin: '2025-08-10T23:59:59',
|
||||
id_tipo_cuestionario: 1,
|
||||
cupo_maximo: 100,
|
||||
secciones: [
|
||||
{
|
||||
titulo: 'Información Personal',
|
||||
descripcion:
|
||||
'Proporciona tus datos personales para poder contactarte y enviarte información relevante sobre la Feria de la Sexualidad.',
|
||||
preguntas: [
|
||||
{
|
||||
titulo: '¿Eres parte de la comunidad de la FES Acatlán?',
|
||||
tipo: 'Cerrada',
|
||||
opciones: [{ valor: 'Si' }, { valor: 'No' }],
|
||||
obligatoria: true,
|
||||
validacion: TiposValidacion.COMUNIDAD_ALUMNO,
|
||||
},
|
||||
{
|
||||
titulo: 'Numero de cuenta',
|
||||
tipo: 'Abierta (Respuesta corta)',
|
||||
obligatoria: false,
|
||||
validacion: TiposValidacion.CUENTA_ALUMNO,
|
||||
},
|
||||
{
|
||||
titulo: 'Correo electrónico',
|
||||
tipo: 'Abierta (Respuesta corta)',
|
||||
obligatoria: true,
|
||||
validacion: TiposValidacion.CORREO,
|
||||
},
|
||||
{
|
||||
titulo: 'Nombre(s)',
|
||||
tipo: 'Abierta (Respuesta corta)',
|
||||
obligatoria: true,
|
||||
validacion: TiposValidacion.NOMBRE,
|
||||
},
|
||||
{
|
||||
titulo: 'Apellidos',
|
||||
tipo: 'Abierta (Respuesta corta)',
|
||||
obligatoria: true,
|
||||
validacion: TiposValidacion.APELLIDOS,
|
||||
},
|
||||
{
|
||||
titulo: 'Género',
|
||||
tipo: 'Cerrada',
|
||||
obligatoria: true,
|
||||
validacion: TiposValidacion.GENERO,
|
||||
opciones: [
|
||||
{ valor: 'Masculino' },
|
||||
{ valor: 'Femenino' },
|
||||
{ valor: 'Prefiero no decirlo' },
|
||||
],
|
||||
},
|
||||
{
|
||||
titulo: 'Institución de procedencia',
|
||||
tipo: 'Abierta (Respuesta corta)',
|
||||
obligatoria: true,
|
||||
validacion: TiposValidacion.INSTITUCION,
|
||||
},
|
||||
{
|
||||
titulo: 'Carrera',
|
||||
tipo: 'Abierta (Respuesta corta)',
|
||||
obligatoria: true,
|
||||
validacion: TiposValidacion.CARRERA,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const ejemploCuestionarioAlumno = {
|
||||
summary:
|
||||
'Cuestionario para el registro de asistencia a un evento para comunidad estudiantil',
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { CreateEventoDto } from 'src/evento/dto/create-evento.dto';
|
||||
import { CreateCuestionarioDto, CreateCuestionarioEventoDto } from './create-cuestionario.dto';
|
||||
|
||||
export interface CreateEventoWithCuestionarioDto {
|
||||
evento: CreateEventoDto;
|
||||
cuestionario: CreateCuestionarioEventoDto;
|
||||
}
|
||||
@@ -54,8 +54,8 @@ export const createMainDbConfig = async (
|
||||
retryDelay: 3000,
|
||||
connectTimeout: 30000,
|
||||
|
||||
synchronize: configService.get<boolean>('SYNCHRONIZE') || false,
|
||||
dropSchema: configService.get<boolean>('DROP_SCHEMA') || false,
|
||||
synchronize: configService.get<string>('SYNCHRONIZE') === 'true',
|
||||
dropSchema: configService.get<string>('DROP_SCHEMA') === 'true',
|
||||
});
|
||||
|
||||
export const createAlumnosDbConfig = async (
|
||||
|
||||
Reference in New Issue
Block a user