| id |
title |
type |
project |
version |
updated_date |
| README |
Modulo Educativo |
Documentation |
trading-platform |
1.0.0 |
2026-02-06 |
OQI-002: Modulo Educativo
Estado: ✅ Implementado
Fecha: 2025-12-05
Modulo: apps/backend/src/modules/courses
Descripcion
Sistema de cursos educativos con:
- Categorias de cursos
- Cursos con niveles de dificultad
- Modulos dentro de cada curso
- Lecciones con contenido multimedia
- Sistema de progreso del usuario
Arquitectura
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Frontend │────▶│ NestJS API │────▶│ PostgreSQL │
│ /courses/* │ │ /courses/* │ │ education schema│
└─────────────────┘ └─────────────────┘ └─────────────────┘
Endpoints
| Metodo |
Ruta |
Descripcion |
Auth |
| GET |
/courses |
Listar cursos (paginado) |
- |
| GET |
/courses/:id |
Detalle de curso |
- |
| POST |
/courses |
Crear curso |
Admin |
| PATCH |
/courses/:id |
Actualizar curso |
Admin |
| DELETE |
/courses/:id |
Eliminar curso |
Admin |
| GET |
/courses/categories |
Listar categorias |
- |
| POST |
/courses/categories |
Crear categoria |
Admin |
Entidades
Category
@Entity({ name: 'categories', schema: 'education' })
class Category {
id: string; // UUID
name: string; // "Trading Basico"
slug: string; // "trading-basico"
description?: string;
color?: string; // "#3B82F6"
iconName?: string; // "chart-line"
sortOrder: number; // Para ordenar
isActive: boolean;
createdAt: Date;
updatedAt: Date;
}
Course
@Entity({ name: 'courses', schema: 'education' })
class Course {
id: string;
categoryId: string;
title: string; // "Introduccion al Trading"
slug: string; // "introduccion-trading"
subtitle?: string;
description: string;
thumbnailUrl?: string;
level: DifficultyLevel; // beginner | intermediate | advanced | expert
status: PublishStatus; // draft | published | archived
price?: number; // null = gratis
discountPrice?: number;
isFree: boolean;
isFeatured: boolean;
durationMinutes?: number;
sortOrder: number;
publishedAt?: Date;
createdAt: Date;
updatedAt: Date;
}
Module
@Entity({ name: 'modules', schema: 'education' })
class Module {
id: string;
courseId: string;
title: string; // "Fundamentos del Mercado"
description?: string;
sortOrder: number;
isActive: boolean;
createdAt: Date;
updatedAt: Date;
}
Lesson
@Entity({ name: 'lessons', schema: 'education' })
class Lesson {
id: string;
moduleId: string;
title: string; // "Que es el Trading"
slug: string;
type: LessonType; // video | article | quiz | exercise
content?: string; // Contenido markdown/HTML
videoUrl?: string;
durationMinutes?: number;
sortOrder: number;
isFree: boolean; // Preview gratuito
isActive: boolean;
createdAt: Date;
updatedAt: Date;
}
Niveles de Dificultad
| Nivel |
Label ES |
Color |
| beginner |
Principiante |
Verde |
| intermediate |
Intermedio |
Azul |
| advanced |
Avanzado |
Naranja |
| expert |
Experto |
Rojo |
Query Parameters
GET /courses
| Param |
Tipo |
Descripcion |
| page |
number |
Pagina (default: 1) |
| limit |
number |
Items por pagina (default: 10, max: 100) |
| categoryId |
uuid |
Filtrar por categoria |
| level |
string |
Filtrar por nivel |
| isFree |
boolean |
Solo cursos gratuitos |
| isFeatured |
boolean |
Solo destacados |
| status |
string |
Estado de publicacion |
| search |
string |
Buscar en titulo/descripcion |
| sortBy |
string |
Campo para ordenar |
| sortOrder |
asc/desc |
Direccion de orden |
Respuesta Paginada
{
"data": [
{
"id": "uuid",
"title": "Introduccion al Trading",
"level": "beginner",
"price": 29.99,
"isFree": false,
"thumbnailUrl": "https://...",
"durationMinutes": 180,
"category": {
"id": "uuid",
"name": "Trading Basico"
}
}
],
"meta": {
"page": 1,
"limit": 10,
"total": 45,
"totalPages": 5
}
}
Flujo de Compra
1. Usuario ve catalogo de cursos
2. Selecciona curso de pago
3. Redirige a checkout (Stripe)
4. Pago exitoso -> webhook actualiza acceso
5. Usuario puede ver contenido completo
Frontend
Pagina de Cursos
// apps/frontend/src/pages/Courses.tsx
function Courses() {
const [courses, setCourses] = useState<Course[]>([]);
const [categories, setCategories] = useState<Category[]>([]);
const [selectedCategory, setSelectedCategory] = useState('');
// Carga cursos con filtros
// Muestra grid de cards
// Paginacion
}
Servicio
// apps/frontend/src/services/courses.ts
export const coursesService = {
getCourses(params: CourseQueryParams): Promise<PaginatedResponse<Course>>,
getCourse(id: string): Promise<Course>,
getCategories(): Promise<Category[]>,
};
Archivos
apps/backend/src/modules/courses/
├── dto/
│ ├── course.dto.ts
│ ├── module.dto.ts
│ └── lesson.dto.ts
├── entities/
│ ├── category.entity.ts
│ ├── course.entity.ts
│ ├── module.entity.ts
│ └── lesson.entity.ts
├── courses.controller.ts
├── courses.service.ts
└── courses.module.ts
apps/frontend/src/
├── pages/
│ └── Courses.tsx
└── services/
└── courses.ts
Proximas Mejoras
Schemas DDL Asignados
Este modulo es owner del siguiente schema DDL:
| Schema |
Tablas |
Descripcion |
| education |
19 |
courses, course_modules, lessons, quizzes, quiz_questions, quiz_answers, quiz_attempts, user_courses, user_progress, user_achievements, achievements, certificates, gamification_rewards, gamification_levels, gamification_challenges, gamification_user_challenges, learning_paths, learning_path_courses, user_streaks |
Total tablas: 19
Nota DDL drift: Documentacion previa listaba ~13 tablas. Las 6 tablas adicionales son: gamification_levels, gamification_challenges, gamification_user_challenges, learning_paths, learning_path_courses, user_streaks. Actualizado por TASK-2026-02-06 F2.6.
Documentacion generada: 2025-12-05