generar y enviar qr por correo electronico despues de registrar un formulario #11

Merged
eithan merged 2 commits from eithan into develop 2025-04-03 20:12:00 +00:00
7 changed files with 191 additions and 4 deletions
+14
View File
@@ -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
@@ -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: `
<h1>¡Gracias por registrarte!</h1>
<p>Has completado exitosamente el formulario: ${cuestionario.nombre_form}</p>
<p>Este es tu QR para registrar asistencia:</p>
<img src="cid:qrCode" alt="Código QR" />
<p>Tus datos de registro:</p>
<ul>
<li>Evento: ${cuestionario.evento.nombre_evento || 'Evento'}</li>
<li>Correo: ${participante.correo}</li>
<li>Fecha de registro: ${new Date().toLocaleString()}</li>
</ul>
`,
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();
@@ -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)
@@ -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;
@@ -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);
}
}
+12 -1
View File
@@ -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<string> {
return this.qrService.generateAsistenciaQR(idParticipante, idEvento);
}
@Get()
getQrs(): Promise<Qr[]> {
return this.qrService.getQrs();
+14
View File
@@ -15,6 +15,20 @@ export class QrService {
return await QRCode.toDataURL(text);
}
async generateAsistenciaQR(id_participante: number, id_evento: number): Promise<string> {
// 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<Buffer> {
return await QRCode.toBuffer(text); // Devuelve un buffer para adjuntar en email o stream
}