From 5ba3f0f2a814b527273ad05b6c1ebf2b6934a4f0 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 3 Apr 2025 11:29:46 -0600 Subject: [PATCH] generar y enviar qr por correo electronico despues de registrar un formulario --- env.example.txt | 14 +++ .../cuestionario_respondido.service.ts | 104 +++++++++++++++++- .../participante_evento.controller.ts | 16 +++ .../participante_evento.entity.ts | 3 + .../participante_evento.service.ts | 31 ++++++ src/qr/qr.controller.ts | 13 ++- src/qr/qr.service.ts | 14 +++ 7 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 env.example.txt 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:

+ Código QR +

Tus datos de registro:

+ + `, + 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 }