ML Engine Updates: - Updated BTCUSD with Polygon API data (2024-2025): 215,699 new records - Re-trained all ML models: Attention (R²: 0.223), Base, Metamodel (87.3% confidence) - Backtest results: +176.71R profit with aggressive_filter strategy Documentation Consolidation: - Created docs/99-analisis/_MAP.md index with 13 new analysis documents - Consolidated inventories: removed duplicates from orchestration/inventarios/ - Updated ML_INVENTORY.yml with BTCUSD metrics and training results - Added execution reports: FASE11-BTCUSD, correction issues, alignment validation Architecture & Integration: - Updated all module documentation with NEXUS v3.4 frontmatter - Fixed _MAP.md indexes across all folders - Updated orchestration plans and traces Files: 229 changed, 5064 insertions(+), 1872 deletions(-) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
450 lines
18 KiB
Markdown
450 lines
18 KiB
Markdown
---
|
|
id: "US-EDU-008"
|
|
title: "Obtener Certificado"
|
|
type: "User Story"
|
|
status: "Done"
|
|
priority: "Media"
|
|
epic: "OQI-002"
|
|
story_points: 3
|
|
created_date: "2025-12-05"
|
|
updated_date: "2026-01-04"
|
|
---
|
|
|
|
# US-EDU-008: Obtener Certificado
|
|
|
|
## Metadata
|
|
|
|
| Campo | Valor |
|
|
|-------|-------|
|
|
| **ID** | US-EDU-008 |
|
|
| **Épica** | OQI-002 - Módulo Educativo |
|
|
| **Módulo** | education |
|
|
| **Prioridad** | P2 |
|
|
| **Story Points** | 3 |
|
|
| **Sprint** | Sprint 5 |
|
|
| **Estado** | Pendiente |
|
|
| **Asignado a** | Por asignar |
|
|
|
|
---
|
|
|
|
## Historia de Usuario
|
|
|
|
**Como** usuario que completó un curso,
|
|
**quiero** obtener un certificado digital verificable,
|
|
**para** validar mi logro, agregarlo a mi perfil profesional y compartirlo en redes sociales.
|
|
|
|
## Descripción Detallada
|
|
|
|
El usuario debe recibir automáticamente un certificado digital en formato PDF al completar el 100% de un curso. El certificado debe tener diseño profesional con logo de Trading Platform, nombre del usuario, título del curso, fecha de finalización, ID único de verificación, y QR code. El usuario debe poder descargar el PDF, compartir en LinkedIn, y el certificado debe ser verificable públicamente.
|
|
|
|
## Mockups/Wireframes
|
|
|
|
```
|
|
[MODAL DE CURSO COMPLETADO]
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ ✨ 🎓 ✨ │
|
|
│ │
|
|
│ ¡FELICIDADES! CURSO COMPLETADO │
|
|
│ │
|
|
│ Fibonacci Retracement Básico │
|
|
│ │
|
|
│ +200 XP ganados │
|
|
│ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ │ │
|
|
│ │ [PREVIEW DEL CERTIFICADO] │ │
|
|
│ │ │ │
|
|
│ │ ┌──────────────────────────────────────┐ │ │
|
|
│ │ │ [LOGO ORBIQUANT] │ │ │
|
|
│ │ │ CERTIFICADO DE FINALIZACIÓN │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ Se certifica que │ │ │
|
|
│ │ │ JUAN PÉREZ │ │ │
|
|
│ │ │ Ha completado exitosamente │ │ │
|
|
│ │ │ "Fibonacci Retracement Básico" │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ 05/12/2025 │ │ │
|
|
│ │ │ OQI-EDU-A3F8D291 │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ [Firma Instructor] [Firma OQI] │ │ │
|
|
│ │ │ [QR CODE] │ │ │
|
|
│ │ └──────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ [📥 Descargar PDF] [💼 Compartir en LinkedIn] [✕ Cerrar] │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
|
|
[PÁGINA DE CERTIFICADOS]
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ MIS CERTIFICADOS [🔍] │
|
|
│ │
|
|
│ Has obtenido 12 certificados │
|
|
│ │
|
|
│ Filtros: [Todos ▼] [Más recientes ▼] │
|
|
│ │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
│ │ [THUMBNAIL] │ │ [THUMBNAIL] │ │ [THUMBNAIL] │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ Fibonacci │ │ Day Trading │ │ Candlestick │ │
|
|
│ │ Básico │ │ Pro │ │ Patterns │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ 05/12/2025 │ │ 28/11/2025 │ │ 15/11/2025 │ │
|
|
│ │ OQI-...-291 │ │ OQI-...-8F2 │ │ OQI-...-4A1 │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ [Ver] [📥] │ │ [Ver] [📥] │ │ [Ver] [📥] │ │
|
|
│ │ [💼 LinkedIn]│ │ [💼 LinkedIn]│ │ [💼 LinkedIn]│ │
|
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
│ │
|
|
│ [1] 2 3 4 │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
|
|
[PÁGINA DE VERIFICACIÓN PÚBLICA]
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ [LOGO] Trading Platform │
|
|
│ │
|
|
│ VERIFICACIÓN DE CERTIFICADO │
|
|
│ │
|
|
│ Certificado: OQI-EDU-A3F8D291 │
|
|
│ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ ✓ CERTIFICADO VÁLIDO │ │
|
|
│ │ │ │
|
|
│ │ Otorgado a: Juan Pérez │ │
|
|
│ │ Curso: Fibonacci Retracement Básico │ │
|
|
│ │ Categoría: Análisis Técnico │ │
|
|
│ │ Fecha de finalización: 05/12/2025 │ │
|
|
│ │ Duración del curso: 2.5 horas │ │
|
|
│ │ Módulos: 5 | Lecciones: 23 │ │
|
|
│ │ │ │
|
|
│ │ Instructor: Carlos Mendoza │ │
|
|
│ │ Institución: Trading Platform │ │
|
|
│ │ │ │
|
|
│ │ Estado: ✅ Activo │ │
|
|
│ │ Emitido: 05/12/2025 15:45:00 UTC │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ Este certificado puede ser verificado en cualquier momento en: │
|
|
│ trading.com/verify/OQI-EDU-A3F8D291 │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Criterios de Aceptación
|
|
|
|
**Escenario 1: Completar curso genera certificado**
|
|
```gherkin
|
|
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 registra en backend con ID único (OQI-EDU-XXXXXXXX)
|
|
Y se genera PDF con diseño profesional
|
|
Y se almacena PDF en S3 o CDN
|
|
Y se muestra modal de felicitación
|
|
Y se envía email con certificado adjunto
|
|
```
|
|
|
|
**Escenario 2: Ver certificado en modal**
|
|
```gherkin
|
|
DADO que se generó el certificado
|
|
CUANDO se muestra el modal de curso completado
|
|
ENTONCES se muestra preview del certificado
|
|
Y se muestra botón "Descargar PDF"
|
|
Y se muestra botón "Compartir en LinkedIn"
|
|
Y se muestra "Ver todos mis certificados"
|
|
```
|
|
|
|
**Escenario 3: Descargar certificado en PDF**
|
|
```gherkin
|
|
DADO que el usuario tiene un certificado
|
|
CUANDO hace click en "Descargar PDF"
|
|
ENTONCES se descarga archivo PDF
|
|
Y el PDF contiene:
|
|
- Logo de Trading Platform
|
|
- Título "Certificado de Finalización"
|
|
- Nombre completo del usuario
|
|
- Título del curso
|
|
- Fecha de finalización
|
|
- ID único del certificado
|
|
- Firmas digitales (instructor + plataforma)
|
|
- QR code para verificación
|
|
- Footer con URL de verificación
|
|
```
|
|
|
|
**Escenario 4: Compartir en LinkedIn**
|
|
```gherkin
|
|
DADO que el usuario quiere compartir su certificado
|
|
CUANDO hace click en "Compartir en LinkedIn"
|
|
ENTONCES se abre nueva pestaña de LinkedIn
|
|
Y el formulario de certificación está pre-llenado con:
|
|
- Nombre: "Fibonacci Retracement Básico"
|
|
- Organización: "Trading Platform"
|
|
- Fecha de emisión: "Diciembre 2025"
|
|
- ID de certificado: "OQI-EDU-A3F8D291"
|
|
- URL de verificación: "trading.com/verify/..."
|
|
```
|
|
|
|
**Escenario 5: Ver galería de certificados**
|
|
```gherkin
|
|
DADO que el usuario tiene 12 certificados
|
|
CUANDO accede a /education/certificates
|
|
ENTONCES se muestra galería de todos los certificados
|
|
Y cada certificado muestra: thumbnail, título del curso, fecha
|
|
Y se muestra contador "Has obtenido 12 certificados"
|
|
Y se pueden filtrar por: categoría, fecha
|
|
Y se pueden ordenar por: más reciente, alfabético
|
|
```
|
|
|
|
**Escenario 6: Verificar certificado públicamente**
|
|
```gherkin
|
|
DADO que alguien tiene el ID de un certificado
|
|
CUANDO accede a trading.com/verify/OQI-EDU-A3F8D291
|
|
ENTONCES se muestra página de verificación pública
|
|
Y NO requiere login
|
|
Y se muestra:
|
|
- Estado: ✅ Válido
|
|
- Nombre del usuario
|
|
- Título del curso
|
|
- Fecha de finalización
|
|
- Detalles del curso
|
|
Y se confirma autenticidad del certificado
|
|
```
|
|
|
|
**Escenario 7: Verificar certificado inválido**
|
|
```gherkin
|
|
DADO que alguien accede con ID inválido
|
|
CUANDO accede a /verify/INVALID-ID-123
|
|
ENTONCES se muestra "Certificado no encontrado"
|
|
Y se muestra sugerencia "Verifica el ID ingresado"
|
|
Y se muestra link "¿Cómo verificar un certificado?"
|
|
```
|
|
|
|
**Escenario 8: Email de certificado**
|
|
```gherkin
|
|
DADO que se generó un certificado
|
|
CUANDO se envía el email
|
|
ENTONCES el email contiene:
|
|
- Asunto: "¡Felicidades! Certificado de [Curso]"
|
|
- Mensaje de felicitación personalizado
|
|
- Estadísticas: duración, lecciones completadas
|
|
- PDF adjunto del certificado
|
|
- Botones: Ver certificado, Compartir en LinkedIn
|
|
- Sugerencias de próximos cursos relacionados
|
|
```
|
|
|
|
**Escenario 9: Curso sin certificado disponible**
|
|
```gherkin
|
|
DADO que un curso está marcado como "no certifiable"
|
|
CUANDO el usuario completa el curso
|
|
ENTONCES NO se genera certificado
|
|
Y se muestra "Curso completado" sin opción de certificado
|
|
Y se explica "Este curso no otorga certificado"
|
|
```
|
|
|
|
**Escenario 10: Certificado con requisitos adicionales**
|
|
```gherkin
|
|
DADO que un curso requiere quiz final aprobado
|
|
Y el usuario completó todas las lecciones
|
|
PERO no aprobó el quiz final
|
|
CUANDO intenta obtener certificado
|
|
ENTONCES se muestra "Debes aprobar el quiz final"
|
|
Y se muestra score actual del quiz
|
|
Y se muestra "Intentos restantes: X"
|
|
Y el certificado NO se genera hasta aprobar
|
|
```
|
|
|
|
## Criterios Adicionales
|
|
|
|
- [ ] Watermark en PDF para evitar falsificación
|
|
- [ ] Blockchain verification (opcional, fase 2)
|
|
- [ ] Traducción del certificado a inglés
|
|
- [ ] Certificado físico por correo (premium)
|
|
- [ ] Badge de LinkedIn auto-agregado via API
|
|
- [ ] Opción de hacer certificado público/privado
|
|
- [ ] Perfil público con todos los certificados del usuario
|
|
|
|
---
|
|
|
|
## Tareas Técnicas
|
|
|
|
**Database:**
|
|
- [ ] DB-EDU-022: Tabla education.certificates
|
|
- [ ] DB-EDU-023: Campos: id, certificate_number, user_id, course_id, issued_at, pdf_url, status
|
|
- [ ] DB-EDU-024: Tabla certificate_verifications (log de verificaciones)
|
|
- [ ] DB-EDU-025: Índice único en certificate_number
|
|
|
|
**Backend:**
|
|
- [ ] BE-EDU-057: Endpoint POST /education/certificates/generate
|
|
- [ ] BE-EDU-058: Endpoint GET /education/certificates (del usuario)
|
|
- [ ] BE-EDU-059: Endpoint GET /education/certificates/:id
|
|
- [ ] BE-EDU-060: Endpoint GET /api/public/certificates/verify/:number
|
|
- [ ] BE-EDU-061: Implementar CertificateService.generate()
|
|
- [ ] BE-EDU-062: Generar PDF con Puppeteer o PDFKit
|
|
- [ ] BE-EDU-063: Generar QR code con qrcode library
|
|
- [ ] BE-EDU-064: Upload de PDF a S3 con signed URL
|
|
- [ ] BE-EDU-065: Event handler en course completion
|
|
- [ ] BE-EDU-066: Email service para enviar certificado
|
|
- [ ] BE-EDU-067: Rate limiting en verificación (100/hora por IP)
|
|
|
|
**Frontend:**
|
|
- [ ] FE-EDU-070: Modal CourseCompletedModal.tsx con preview
|
|
- [ ] FE-EDU-071: Crear CertificatesPage.tsx
|
|
- [ ] FE-EDU-072: Crear componente CertificateCard.tsx
|
|
- [ ] FE-EDU-073: Crear VerifyCertificatePage.tsx (pública)
|
|
- [ ] FE-EDU-074: Botón "Compartir en LinkedIn" con pre-fill
|
|
- [ ] FE-EDU-075: Preview de PDF en modal
|
|
- [ ] FE-EDU-076: Galería con filtros y búsqueda
|
|
- [ ] FE-EDU-077: Implementar certificatesStore
|
|
|
|
**Tests:**
|
|
- [ ] TEST-EDU-030: Test generación de certificado
|
|
- [ ] TEST-EDU-031: Test validación de certificado válido
|
|
- [ ] TEST-EDU-032: Test verificación de certificado inválido
|
|
- [ ] TEST-EDU-033: Test E2E completar curso y obtener certificado
|
|
|
|
---
|
|
|
|
## Dependencias
|
|
|
|
**Depende de:**
|
|
- [ ] US-EDU-005: Completar lección - Estado: Pendiente
|
|
- [ ] RF-EDU-005: Sistema de certificados
|
|
- [ ] PDF generation library (Puppeteer/PDFKit)
|
|
- [ ] S3 bucket para almacenar PDFs
|
|
- [ ] Email service
|
|
|
|
**Bloquea:**
|
|
- Ninguna (es funcionalidad final)
|
|
|
|
---
|
|
|
|
## Notas Técnicas
|
|
|
|
**Generación del certificado:**
|
|
```javascript
|
|
// Triggered on course completion
|
|
async function onCourseCompleted(userId, courseId) {
|
|
// 1. Validar requisitos
|
|
const isEligible = await validateCertificateEligibility(userId, courseId);
|
|
if (!isEligible) return;
|
|
|
|
// 2. Generar ID único
|
|
const certificateNumber = generateCertificateId(); // OQI-EDU-A3F8D291
|
|
|
|
// 3. Generar PDF
|
|
const pdfBuffer = await generateCertificatePDF({
|
|
userName,
|
|
courseName,
|
|
completedDate,
|
|
certificateNumber
|
|
});
|
|
|
|
// 4. Upload a S3
|
|
const pdfUrl = await uploadToS3(pdfBuffer, certificateNumber);
|
|
|
|
// 5. Guardar en DB
|
|
await saveCertificate({
|
|
userId,
|
|
courseId,
|
|
certificateNumber,
|
|
pdfUrl
|
|
});
|
|
|
|
// 6. Enviar email
|
|
await sendCertificateEmail(userId, pdfUrl);
|
|
|
|
// 7. Otorgar XP bonus
|
|
await awardXP(userId, 100, 'certificate_earned');
|
|
}
|
|
```
|
|
|
|
**Endpoint GET /api/public/certificates/verify/:number:**
|
|
```typescript
|
|
// Response para certificado válido
|
|
{
|
|
valid: true,
|
|
certificate: {
|
|
certificateNumber: "OQI-EDU-A3F8D291",
|
|
recipientName: "Juan Pérez",
|
|
courseTitle: "Fibonacci Retracement Básico",
|
|
courseCategory: "Análisis Técnico",
|
|
completedAt: "2025-12-05T15:45:00Z",
|
|
issuedAt: "2025-12-05T15:45:00Z",
|
|
courseDuration: 150, // minutos
|
|
moduleCount: 5,
|
|
lessonCount: 23,
|
|
instructor: "Carlos Mendoza",
|
|
status: "active"
|
|
}
|
|
}
|
|
|
|
// Response para certificado inválido
|
|
{
|
|
valid: false,
|
|
error: "Certificate not found"
|
|
}
|
|
```
|
|
|
|
**Template del PDF:**
|
|
- Usar HTML + CSS para diseño
|
|
- Puppeteer para generar PDF desde HTML
|
|
- Incluir logo en base64 para evitar carga externa
|
|
- QR code generado con library qrcode.js
|
|
- Firmas como imágenes PNG embebidas
|
|
|
|
**LinkedIn pre-fill URL:**
|
|
```javascript
|
|
const linkedInUrl = `https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&name=${encodeURIComponent(courseTitle)}&organizationName=Trading Platform%20IA&issueYear=${year}&issueMonth=${month}&certUrl=${encodeURIComponent(verifyUrl)}&certId=${certificateNumber}`;
|
|
```
|
|
|
|
**Seguridad:**
|
|
- Rate limiting en endpoint de verificación
|
|
- Signed URLs de S3 con expiración de 1 hora para descargas
|
|
- No exponer lista de todos los certificados (solo del usuario logueado)
|
|
- Validar que usuario solo puede descargar sus propios certificados
|
|
|
|
**Entidades/Tablas:**
|
|
- `education.certificates`
|
|
- `education.certificate_verifications` (log)
|
|
|
|
---
|
|
|
|
## Definition of Ready (DoR)
|
|
|
|
- [x] Historia claramente escrita
|
|
- [x] Criterios de aceptación definidos
|
|
- [x] Story points estimados
|
|
- [x] Dependencias identificadas
|
|
- [x] Sin bloqueadores
|
|
- [x] Diseño/mockup disponible
|
|
- [x] API spec disponible
|
|
|
|
## Definition of Done (DoD)
|
|
|
|
- [ ] Código implementado según criterios
|
|
- [ ] Tests unitarios escritos y pasando
|
|
- [ ] Tests de integración pasando
|
|
- [ ] Code review aprobado
|
|
- [ ] Documentación actualizada
|
|
- [ ] QA aprobado
|
|
- [ ] Desplegado en ambiente de pruebas
|
|
|
|
---
|
|
|
|
## Historial de Cambios
|
|
|
|
| Fecha | Cambio | Autor |
|
|
|-------|--------|-------|
|
|
| 2025-12-05 | Creación | Requirements-Analyst |
|
|
|
|
---
|
|
|
|
**Creada por:** Requirements-Analyst
|
|
**Fecha:** 2025-12-05
|
|
**Última actualización:** 2025-12-05
|