Changes include: - Updated architecture documentation - Enhanced module definitions (OQI-001 to OQI-008) - ML integration documentation updates - Trading strategies documentation - Orchestration and inventory updates - Docker configuration updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
337 lines
11 KiB
Markdown
337 lines
11 KiB
Markdown
---
|
|
id: "RF-EDU-005"
|
|
title: "Sistema de Certificados"
|
|
type: "Requirement"
|
|
status: "Done"
|
|
priority: "Media"
|
|
module: "education"
|
|
epic: "OQI-002"
|
|
version: "1.0"
|
|
created_date: "2025-12-05"
|
|
updated_date: "2026-01-04"
|
|
---
|
|
|
|
# RF-EDU-005: Sistema de Certificados
|
|
|
|
**Versión:** 1.0.0
|
|
**Fecha:** 2025-12-05
|
|
**Épica:** OQI-002 - Módulo Educativo
|
|
**Prioridad:** P2
|
|
**Story Points:** 5
|
|
|
|
---
|
|
|
|
## Descripción
|
|
|
|
El sistema debe proporcionar certificados digitales verificables que se otorgan automáticamente al completar cursos, validando el conocimiento adquirido y permitiendo a los usuarios compartir sus logros profesionales en redes sociales y plataformas de empleo.
|
|
|
|
---
|
|
|
|
## Requisitos Funcionales
|
|
|
|
### RF-EDU-005.1: Generación de Certificados
|
|
|
|
El sistema debe:
|
|
- Generar certificado automáticamente al completar 100% de un curso
|
|
- Validar que todos los quizzes obligatorios estén aprobados
|
|
- Validar que todas las lecciones estén marcadas como completadas
|
|
- Asignar ID único de certificado (formato: OQI-EDU-XXXXXXXX)
|
|
- Generar PDF con diseño profesional
|
|
- Almacenar PDF en S3 o similar
|
|
- Registrar en blockchain para verificación (opcional, fase 2)
|
|
|
|
### RF-EDU-005.2: Contenido del Certificado
|
|
|
|
Cada certificado debe incluir:
|
|
- Logo de OrbiQuant IA
|
|
- Título: "Certificado de Finalización"
|
|
- Nombre completo del usuario
|
|
- Título del curso completado
|
|
- Fecha de finalización
|
|
- ID único del certificado
|
|
- Firma digital del instructor (imagen)
|
|
- Firma digital de la plataforma
|
|
- QR code para verificación online
|
|
- Footer: "Verifica este certificado en orbiquant.com/verify/{certificateId}"
|
|
|
|
Template:
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ │
|
|
│ [LOGO ORBIQUANT] │
|
|
│ │
|
|
│ CERTIFICADO DE FINALIZACIÓN │
|
|
│ │
|
|
│ Se certifica que │
|
|
│ │
|
|
│ [NOMBRE USUARIO] │
|
|
│ │
|
|
│ Ha completado exitosamente el curso │
|
|
│ │
|
|
│ "[TÍTULO DEL CURSO]" │
|
|
│ │
|
|
│ Fecha: [DD/MM/YYYY] │
|
|
│ Certificado: OQI-EDU-XXXXXXXX │
|
|
│ │
|
|
│ ___________________ ___________________ │
|
|
│ [Firma Instructor] [Firma Plataforma] │
|
|
│ │
|
|
│ [QR CODE] │
|
|
│ Verifica en orbiquant.com/verify/XXXX │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### RF-EDU-005.3: Verificación de Certificados
|
|
|
|
El sistema debe:
|
|
- Proveer página pública /verify/:certificateId
|
|
- Mostrar información del certificado sin login
|
|
- Validar que el ID existe en base de datos
|
|
- Mostrar: nombre, curso, fecha, estado (válido/revocado)
|
|
- Proteger contra scraping (rate limiting, captcha)
|
|
- API pública GET /api/certificates/verify/:id
|
|
- Responder en JSON para integraciones
|
|
|
|
### RF-EDU-005.4: Galería de Certificados del Usuario
|
|
|
|
El sistema debe:
|
|
- Página /education/certificates con todos los certificados del usuario
|
|
- Mostrar: thumbnail, título del curso, fecha
|
|
- Filtrar por: fecha, curso, categoría
|
|
- Buscar por nombre de curso
|
|
- Ordenar por: más reciente, alfabético, categoría
|
|
- Vista de cuadrícula o lista
|
|
- Contador: "Has obtenido X certificados"
|
|
|
|
### RF-EDU-005.5: Descarga y Compartir
|
|
|
|
El sistema debe permitir:
|
|
- Descargar PDF del certificado
|
|
- Botón "Compartir en LinkedIn" (pre-rellenado)
|
|
- Botón "Compartir en Twitter/X"
|
|
- Botón "Copiar link de verificación"
|
|
- Generar imagen social (Open Graph) para compartir
|
|
- Agregar a perfil público del usuario (opcional)
|
|
|
|
Integración LinkedIn:
|
|
```javascript
|
|
// Pre-llenar certificación en LinkedIn
|
|
const linkedInUrl = `https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&name=${encodeURIComponent(courseTitle)}&organizationName=OrbiQuant IA&issueYear=${year}&issueMonth=${month}&certUrl=${verifyUrl}&certId=${certificateId}`;
|
|
```
|
|
|
|
### RF-EDU-005.6: Perfil Público de Certificados
|
|
|
|
El sistema debe:
|
|
- Permitir al usuario crear perfil público opcional
|
|
- URL: orbiquant.com/u/:username/certificates
|
|
- Mostrar solo certificados que el usuario hizo públicos
|
|
- Galería visual de certificados
|
|
- Bio del usuario
|
|
- Enlaces a redes sociales
|
|
- No requiere login para ver
|
|
|
|
### RF-EDU-005.7: Revocación de Certificados
|
|
|
|
El sistema debe permitir (solo admins):
|
|
- Revocar certificado por fraude
|
|
- Agregar motivo de revocación
|
|
- Notificar al usuario por email
|
|
- Marcar certificado como "REVOKED" en verificación
|
|
- Mantener historial de revocaciones
|
|
|
|
### RF-EDU-005.8: Plantillas de Certificados
|
|
|
|
El sistema debe soportar:
|
|
- Múltiples plantillas (por categoría o nivel)
|
|
- Plantilla estándar para todos los cursos
|
|
- Plantilla especial para cursos premium
|
|
- Plantilla con colores de marca
|
|
- Editor de plantillas para admins (fase 2)
|
|
|
|
---
|
|
|
|
## Datos de Salida
|
|
|
|
```typescript
|
|
interface Certificate {
|
|
id: string;
|
|
certificateNumber: string; // OQI-EDU-XXXXXXXX
|
|
userId: string;
|
|
userName: string;
|
|
courseId: string;
|
|
courseTitle: string;
|
|
courseCategory: string;
|
|
completedAt: string;
|
|
issuedAt: string;
|
|
pdfUrl: string;
|
|
verifyUrl: string;
|
|
qrCodeUrl: string;
|
|
status: 'active' | 'revoked';
|
|
revocationReason?: string;
|
|
instructorSignature: string;
|
|
metadata: {
|
|
duration: number; // horas del curso
|
|
moduleCount: number;
|
|
lessonCount: number;
|
|
finalScore?: number; // Si hay quiz final
|
|
};
|
|
}
|
|
|
|
interface VerificationResult {
|
|
valid: boolean;
|
|
certificate?: {
|
|
certificateNumber: string;
|
|
recipientName: string;
|
|
courseTitle: string;
|
|
completedAt: string;
|
|
status: 'active' | 'revoked';
|
|
};
|
|
error?: string;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Reglas de Negocio
|
|
|
|
1. **Requisitos para certificado:**
|
|
- 100% de lecciones completadas
|
|
- Todos los quizzes aprobados (si aplica)
|
|
- Curso debe estar marcado como "completable" (algunos cursos no otorgan certificado)
|
|
2. **ID único:** Formato OQI-EDU-{8 dígitos hex aleatorios}
|
|
3. **Nombre en certificado:** Se usa nombre completo del perfil del usuario
|
|
4. **Fecha:** Fecha de finalización del curso (última lección completada)
|
|
5. **PDF inmutable:** Una vez generado, el PDF no se regenera aunque el usuario cambie su nombre
|
|
6. **Caducidad:** Los certificados no caducan
|
|
7. **Límite de verificaciones:** 100 verificaciones por IP por hora
|
|
8. **Perfil público:** Opt-in, deshabilitado por default
|
|
|
|
---
|
|
|
|
## Criterios de Aceptación
|
|
|
|
```gherkin
|
|
Escenario: Usuario completa curso y obtiene certificado
|
|
DADO que el usuario completó todas las lecciones de un curso
|
|
Y aprobó todos los quizzes obligatorios
|
|
CUANDO se marca la última lección como completada
|
|
ENTONCES se genera automáticamente un certificado
|
|
Y se muestra modal de felicitación
|
|
Y se envía email con el certificado adjunto
|
|
Y se muestra botón "Ver certificado"
|
|
|
|
Escenario: Usuario descarga certificado
|
|
DADO que el usuario tiene un certificado
|
|
CUANDO accede a /education/certificates
|
|
Y hace click en "Descargar PDF"
|
|
ENTONCES se descarga archivo PDF con el certificado
|
|
Y el PDF contiene: nombre, curso, fecha, ID, firmas, QR
|
|
|
|
Escenario: Usuario comparte en LinkedIn
|
|
DADO que el usuario está viendo su certificado
|
|
CUANDO hace click en "Compartir en LinkedIn"
|
|
ENTONCES se abre LinkedIn en nueva pestaña
|
|
Y el formulario está pre-llenado con:
|
|
- Nombre del curso
|
|
- Organización: OrbiQuant IA
|
|
- Fecha de emisión
|
|
- URL de verificación
|
|
- ID del certificado
|
|
|
|
Escenario: Tercero verifica certificado
|
|
DADO que alguien tiene el ID de un certificado
|
|
CUANDO accede a orbiquant.com/verify/OQI-EDU-12345678
|
|
ENTONCES se muestra página de verificación
|
|
Y se muestra: nombre del usuario, curso, fecha
|
|
Y se muestra badge "✓ Certificado Válido"
|
|
Y NO requiere login para ver
|
|
|
|
Escenario: Verificar certificado inválido
|
|
DADO que alguien accede a /verify/INVALID-ID
|
|
CUANDO el ID no existe en la base de datos
|
|
ENTONCES se muestra "Certificado no encontrado"
|
|
Y se sugiere verificar el ID ingresado
|
|
|
|
Escenario: Ver certificado revocado
|
|
DADO que un certificado fue revocado por admin
|
|
CUANDO alguien intenta verificarlo
|
|
ENTONCES se muestra "Certificado Revocado"
|
|
Y se muestra motivo de revocación
|
|
Y se marca en rojo como inválido
|
|
```
|
|
|
|
---
|
|
|
|
## Dependencias
|
|
|
|
- PDF generation library (PDFKit, Puppeteer, o similar)
|
|
- S3 para almacenar PDFs
|
|
- QR code generator
|
|
- Email service para enviar certificados
|
|
- LinkedIn API para integración
|
|
|
|
---
|
|
|
|
## Notas Técnicas
|
|
|
|
- Usar Puppeteer para generar PDFs desde HTML template
|
|
- Almacenar PDFs con nombre: certificates/{userId}/{certificateId}.pdf
|
|
- Generar QR codes con librería qrcode.js
|
|
- Implementar caché de verificaciones (Redis) para reducir load
|
|
- Considerar watermark en PDFs para prevenir falsificación
|
|
- Usar signed URLs de S3 para descargas seguras
|
|
- Implementar rate limiting agresivo en endpoint de verificación
|
|
- Para blockchain: Guardar hash del certificado en Ethereum/Polygon
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- Schema: `/backend/src/database/schemas/education.sql`
|
|
- API: `/backend/src/modules/courses/certificates.routes.ts`
|
|
- Frontend: `/frontend/src/pages/Certificates.tsx`
|
|
- Templates: `/backend/src/templates/certificate-template.html`
|
|
|
|
---
|
|
|
|
## Tareas Técnicas
|
|
|
|
**Database:**
|
|
- [ ] Tabla education.certificates
|
|
- [ ] Campos: id, certificate_number, user_id, course_id, issued_at, pdf_url, status
|
|
- [ ] Tabla certificate_verifications (log de verificaciones)
|
|
- [ ] Índice único en certificate_number
|
|
|
|
**Backend:**
|
|
- [ ] Endpoint POST /education/certificates/generate (triggered on course completion)
|
|
- [ ] Endpoint GET /education/certificates (listar del usuario)
|
|
- [ ] Endpoint GET /education/certificates/:id
|
|
- [ ] Endpoint GET /api/public/certificates/verify/:number (público)
|
|
- [ ] Endpoint POST /admin/certificates/:id/revoke (admin only)
|
|
- [ ] Implementar CertificateService.generatePDF()
|
|
- [ ] Implementar generación de QR code
|
|
- [ ] Event handler en course completion
|
|
- [ ] Rate limiting en verificación
|
|
|
|
**Frontend:**
|
|
- [ ] Crear CertificatesPage.tsx
|
|
- [ ] Crear componente CertificateCard.tsx
|
|
- [ ] Crear CertificateDetailPage.tsx
|
|
- [ ] Crear VerifyCertificatePage.tsx (pública)
|
|
- [ ] Crear modal de celebración al obtener certificado
|
|
- [ ] Botones de compartir social media
|
|
- [ ] Preview de PDF en modal
|
|
- [ ] Implementar certificatesStore
|
|
|
|
**Tests:**
|
|
- [ ] Test generación de PDF
|
|
- [ ] Test verificación de certificado válido/inválido
|
|
- [ ] Test E2E completar curso y obtener certificado
|
|
|
|
---
|
|
|
|
**Creado por:** Requirements-Analyst
|
|
**Fecha:** 2025-12-05
|
|
**Última actualización:** 2025-12-05
|