diff --git a/env.example.txt b/env.example.txt
new file mode 100644
index 0000000..14564ff
--- /dev/null
+++ b/env.example.txt
@@ -0,0 +1,14 @@
+PORT=4200
+
+
+
+db_type=mysql
+db_host=mariadb # <-- uso del nombre del servicio, NO IP
+db_port=3306
+db_username=root # <-- te conectarás con root
+db_password=mypass # <-- la misma que MARIADB_ROOT_PASSWORD
+db_database=formularios_test
+
+
+
+url_api_correos=http://host.docker.internal:4100 #http://localhost:4100
\ No newline at end of file
diff --git a/src/cuestionario_respondido/cuestionario_respondido.service.ts b/src/cuestionario_respondido/cuestionario_respondido.service.ts
index d007d54..2d4e56e 100644
--- a/src/cuestionario_respondido/cuestionario_respondido.service.ts
+++ b/src/cuestionario_respondido/cuestionario_respondido.service.ts
@@ -12,6 +12,8 @@ import { RespuestaParticipanteAbierta } from '../respuesta_participante_abierta/
import { RespuestaParticipanteCerrada } from '../respuesta_participante_cerrada/entities/respuesta_participante_cerrada.entity';
import { PreguntaOpcion } from '../pregunta_opcion/entities/pregunta_opcion.entity';
import { Opcion } from '../opcion/entities/opcion.entity';
+import axios from 'axios';
+import * as QRCode from 'qrcode';
@Injectable()
export class CuestionarioRespondidoService {
@@ -47,7 +49,8 @@ export class CuestionarioRespondidoService {
try {
// 1. Verificar que el cuestionario existe
const cuestionario = await this.cuestionarioRepository.findOne({
- where: { id_cuestionario: submitDto.id_formulario }
+ where: { id_cuestionario: submitDto.id_formulario },
+ relations: ['evento']
});
if (!cuestionario) {
@@ -135,15 +138,110 @@ export class CuestionarioRespondidoService {
}
}
+ // Variables para el QR y correo electrónico
+ let datosQR: {
+ id_participante: number;
+ id_evento: number;
+ } | null = null;
+
+ // Capturamos los datos para el QR
+ if (cuestionario.evento && cuestionario.evento.id_evento) {
+ datosQR = {
+ id_participante: participante.id_participante,
+ id_evento: cuestionario.evento.id_evento
+ };
+ }
+
+ // Primero completamos la transacción principal
await queryRunner.commitTransaction();
- return {
+ // Después de la transacción principal, intentamos registrar la relación participante-evento
+ // y enviar el correo como operaciones independientes
+ if (cuestionario.evento && cuestionario.evento.id_evento) {
+ try {
+ // Registrar participante en evento
+ try {
+ // Verificar si ya existe una relación participante-evento
+ const participanteEventoExistente = await this.dataSource.query(
+ `SELECT * FROM participante_evento WHERE id_participante = ? AND id_evento = ?`,
+ [participante.id_participante, cuestionario.evento.id_evento]
+ );
+
+ // Si no existe, crear la relación
+ if (!participanteEventoExistente || participanteEventoExistente.length === 0) {
+ await this.dataSource.query(
+ `INSERT INTO participante_evento (id_participante, id_evento, fecha_inscripcion, estatus) VALUES (?, ?, ?, ?)`,
+ [participante.id_participante, cuestionario.evento.id_evento, new Date(), true]
+ );
+ }
+ } catch (dbError) {
+ console.error('Error al registrar participante en evento:', dbError.message);
+ }
+
+ // Generar código QR
+ const qrJsonData = JSON.stringify(datosQR);
+ const qrBuffer = await QRCode.toBuffer(qrJsonData);
+ const qrDataUrl = await QRCode.toDataURL(qrJsonData);
+
+ console.log('QR Data URL:', qrDataUrl);
+ console.log("antes de enviar correo");
+
+ // Enviar correo con el QR
+ const urlApiCorreos = process.env.url_api_correos || 'http://localhost:3000';
+
+ await axios.post(`${urlApiCorreos}/mail/send`, {
+ to: participante.correo,
+ subject: 'Asistencia QR',
+ fecha_recibido: "2025-03-10T12:00:00Z",
+ html: `
+
¡Gracias por registrarte!
+ Has completado exitosamente el formulario: ${cuestionario.nombre_form}
+ Este es tu QR para registrar asistencia:
+
+ Tus datos de registro:
+
+ - Evento: ${cuestionario.evento.nombre_evento || 'Evento'}
+ - Correo: ${participante.correo}
+ - Fecha de registro: ${new Date().toLocaleString()}
+
+ `,
+ adjuntos: [
+ {
+ filename: 'qr.png',
+ content: qrBuffer.toString('base64'),
+ encoding: 'base64',
+ cid: 'qrCode'
+ }
+ ]
+ });
+
+ console.log(`Correo enviado exitosamente a ${participante.correo}`);
+ } catch (error) {
+ console.error('Error después de la transacción principal:', error);
+ // No lanzamos el error para que no afecte la respuesta al usuario
+ }
+ }
+
+ // Preparar respuesta
+ const response: any = {
success: true,
message: 'Respuestas guardadas correctamente',
cuestionarioRespondidoId: savedCuestionarioRespondido.idCuestionarioRespondido
};
+
+ // Incluir IDs para generar QR si hay evento asociado
+ if (datosQR) {
+ response.datos_qr = datosQR;
+ }
+
+ return response;
} catch (error) {
- await queryRunner.rollbackTransaction();
+ // Solo hacemos rollback si no hemos hecho commit todavía
+ try {
+ await queryRunner.rollbackTransaction();
+ } catch (rollbackError) {
+ console.error('Error durante rollback:', rollbackError.message);
+ }
throw error;
} finally {
await queryRunner.release();
diff --git a/src/participante_evento/participante_evento.controller.ts b/src/participante_evento/participante_evento.controller.ts
index 9d128e8..ce29d58 100644
--- a/src/participante_evento/participante_evento.controller.ts
+++ b/src/participante_evento/participante_evento.controller.ts
@@ -19,11 +19,27 @@ export class ParticipanteEventoController {
return this.participanteEventoService.getParticipanteEvento(id)
}
+ @Get(':idParticipante/:idEvento')
+ getParticipanteEventoEspecifico(
+ @Param('idParticipante', ParseIntPipe) idParticipante: number,
+ @Param('idEvento', ParseIntPipe) idEvento: number
+ ) {
+ return this.participanteEventoService.getParticipanteEventoEspecifico(idParticipante, idEvento)
+ }
+
@Post()
createParticipanteEvento(@Body() newParticipanteEvento: CreateParticipanteEventoDto) {
return this.participanteEventoService.createParticipanteEvento(newParticipanteEvento)
}
+ @Post('asistencia/:idParticipante/:idEvento')
+ registrarAsistencia(
+ @Param('idParticipante', ParseIntPipe) idParticipante: number,
+ @Param('idEvento', ParseIntPipe) idEvento: number
+ ) {
+ return this.participanteEventoService.registrarAsistencia(idParticipante, idEvento)
+ }
+
@Delete(':id')
deleteParticipanteEvento(@Param('id', ParseIntPipe) id: number) {
return this.participanteEventoService.deleteParticipanteEvento(id)
diff --git a/src/participante_evento/participante_evento.entity.ts b/src/participante_evento/participante_evento.entity.ts
index 1451525..4f537d3 100644
--- a/src/participante_evento/participante_evento.entity.ts
+++ b/src/participante_evento/participante_evento.entity.ts
@@ -15,6 +15,9 @@ export class ParticipanteEvento {
@Column()
estatus: boolean
+ @Column({ nullable: true, type: 'datetime' })
+ fecha_asistencia: Date
+
/*
@OneToOne(() => Qr, (qr) => qr.participanteEvento)
qr: Qr;
diff --git a/src/participante_evento/participante_evento.service.ts b/src/participante_evento/participante_evento.service.ts
index 38416a1..e989664 100644
--- a/src/participante_evento/participante_evento.service.ts
+++ b/src/participante_evento/participante_evento.service.ts
@@ -53,6 +53,22 @@ export class ParticipanteEventoService {
return participante_eventoFound
}
+ async getParticipanteEventoEspecifico(id_participante: number, id_evento: number) {
+ const participante_eventoFound = await this.participanteEventoRepository.findOne({
+ where: {
+ id_participante,
+ id_evento
+ },
+ relations: ['evento', 'participante']
+ })
+
+ if (!participante_eventoFound) {
+ return new HttpException('Registro no encontrado', HttpStatus.NOT_FOUND);
+ }
+
+ return participante_eventoFound
+ }
+
async deleteParticipanteEvento(id_participante: number) {
const result = await this.participanteEventoRepository.delete({ id_participante })
@@ -78,4 +94,19 @@ export class ParticipanteEventoService {
return this.participanteEventoRepository.save(participante_evento)
}
+ async registrarAsistencia(id_participante: number, id_evento: number) {
+ const participante_eventoFound = await this.participanteEventoRepository.findOne({
+ where: {
+ id_participante,
+ id_evento
+ }
+ });
+
+ if (!participante_eventoFound) {
+ return new HttpException('Registro no encontrado', HttpStatus.NOT_FOUND);
+ }
+
+ participante_eventoFound.fecha_asistencia = new Date();
+ return this.participanteEventoRepository.save(participante_eventoFound);
+ }
}
diff --git a/src/qr/qr.controller.ts b/src/qr/qr.controller.ts
index 3ed6f45..67b4ff9 100644
--- a/src/qr/qr.controller.ts
+++ b/src/qr/qr.controller.ts
@@ -13,7 +13,7 @@ import { QrService } from './qr.service';
import { Qr } from './qr.entity';
import { CreateQrDto } from './dto/create-qr.dto';
import { UpdateQrDto } from './dto/update.qr.dto';
-import { ApiOperation, ApiQuery } from '@nestjs/swagger';
+import { ApiOperation, ApiParam, ApiQuery } from '@nestjs/swagger';
@Controller('qr')
export class QrController {
@@ -30,6 +30,17 @@ export class QrController {
return this.qrService.generateQRCode(text);
}
+ @Get('asistencia/:idParticipante/:idEvento')
+ @ApiOperation({ summary: 'Genera un código QR para asistencia con IDs de participante y evento' })
+ @ApiParam({ name: 'idParticipante', description: 'ID del participante' })
+ @ApiParam({ name: 'idEvento', description: 'ID del evento' })
+ async generateAsistenciaQR(
+ @Param('idParticipante', ParseIntPipe) idParticipante: number,
+ @Param('idEvento', ParseIntPipe) idEvento: number
+ ): Promise {
+ return this.qrService.generateAsistenciaQR(idParticipante, idEvento);
+ }
+
@Get()
getQrs(): Promise {
return this.qrService.getQrs();
diff --git a/src/qr/qr.service.ts b/src/qr/qr.service.ts
index 2d24321..5fc831c 100644
--- a/src/qr/qr.service.ts
+++ b/src/qr/qr.service.ts
@@ -15,6 +15,20 @@ export class QrService {
return await QRCode.toDataURL(text);
}
+ async generateAsistenciaQR(id_participante: number, id_evento: number): Promise {
+ // Crear un objeto JSON que contenga los IDs
+ const qrData = {
+ id_participante,
+ id_evento
+ };
+
+ // Convertir el objeto a una cadena JSON
+ const jsonStr = JSON.stringify(qrData);
+
+ // Generar el código QR con la cadena JSON
+ return await QRCode.toDataURL(jsonStr);
+ }
+
async generateBuffer(text: string): Promise {
return await QRCode.toBuffer(text); // Devuelve un buffer para adjuntar en email o stream
}