- Configure workspace Git repository with comprehensive .gitignore - Add Odoo as submodule for ERP reference code - Include documentation: SETUP.md, GIT-STRUCTURE.md - Add gitignore templates for projects (backend, frontend, database) - Structure supports independent repos per project/subproject level Workspace includes: - core/ - Reusable patterns, modules, orchestration system - projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.) - knowledge-base/ - Reference code and patterns (includes Odoo submodule) - devtools/ - Development tools and templates - customers/ - Client implementations template 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
🎯 Resumen Ejecutivo: Integración de Misiones en Ejercicios
Archivo modificado: apps/backend/src/modules/progress/services/exercise-attempt.service.ts
Estado: ✅ COMPLETADO Y COMPILANDO
📊 Cambios en Números
| Métrica | Valor |
|---|---|
| Líneas agregadas | ~50 |
| Líneas modificadas | 3 |
| Imports nuevos | 2 |
| Métodos nuevos | 1 |
| Dependencias inyectadas | +1 |
| Errores de compilación | 0 ✅ |
🔧 Cambios Detallados
1️⃣ Imports (Líneas 10-11)
+ import { MissionsService } from '@/modules/gamification/services/missions.service';
+ import { MissionTypeEnum } from '@/modules/gamification/entities/mission.entity';
2️⃣ Constructor (Línea 36)
constructor(
@InjectRepository(ExerciseAttempt, 'progress')
private readonly attemptRepo: Repository<ExerciseAttempt>,
@InjectRepository(Exercise, 'educational')
private readonly exerciseRepo: Repository<Exercise>,
private readonly mlCoinsService: MLCoinsService,
private readonly userStatsService: UserStatsService,
+ private readonly missionsService: MissionsService,
@InjectEntityManager('progress')
private readonly entityManager: EntityManager,
) {}
3️⃣ Método create() (Línea 78)
if (savedAttempt.is_correct && (savedAttempt.xp_earned > 0 || savedAttempt.ml_coins_earned > 0)) {
await this.updateModuleProgressAfterCompletion(
savedAttempt.user_id,
savedAttempt.exercise_id,
savedAttempt.xp_earned,
savedAttempt.ml_coins_earned,
);
+ // Actualizar progreso de misiones diarias/semanales
+ await this.updateMissionsProgress(savedAttempt.user_id, savedAttempt.is_correct);
}
4️⃣ Nuevo Método updateMissionsProgress() (Líneas 561-607)
/**
* Actualiza el progreso de misiones después de completar un ejercicio correctamente
* @param userId - ID del usuario (auth.users.id - será convertido a profiles.id internamente)
* @param isCorrect - Si el ejercicio fue completado correctamente
*/
private async updateMissionsProgress(userId: string, isCorrect: boolean): Promise<void> {
if (!isCorrect) return;
try {
// Obtener misiones diarias y semanales activas del usuario
const [dailyMissions, weeklyMissions] = await Promise.all([
this.missionsService.findByTypeAndUser(userId, MissionTypeEnum.DAILY),
this.missionsService.findByTypeAndUser(userId, MissionTypeEnum.WEEKLY),
]);
const allMissions = [...dailyMissions, ...weeklyMissions];
// Actualizar cada misión que tenga objetivo 'complete_exercises'
for (const mission of allMissions) {
// Solo procesar misiones activas o en progreso
if (mission.status !== 'active' && mission.status !== 'in_progress') continue;
// Verificar si la misión tiene objetivo de completar ejercicios
const hasExerciseObjective = mission.objectives.some(
obj => obj.type === 'complete_exercises',
);
if (hasExerciseObjective) {
try {
await this.missionsService.updateProgress(
mission.id,
userId,
'complete_exercises',
1,
);
this.logger.log(`Mission ${mission.id} progress updated for user ${userId}`);
} catch (missionError) {
// Log pero no bloquear si una misión específica falla
this.logger.warn(`Failed to update mission ${mission.id}`, missionError);
}
}
}
} catch (error) {
// No bloquear el flujo principal si falla la actualización de misiones
this.logger.warn(`Failed to update missions progress for user ${userId}`, error);
}
}
🎯 Flujo de Ejecución Completo
┌─────────────────────────────────────────────────────────┐
│ 1. Usuario completa ejercicio en Frontend │
└─────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 2. POST /api/progress/exercise-attempts │
│ { is_correct: true, xp_earned: 100, ... } │
└─────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 3. ExerciseAttemptService.create() │
│ - Crea nuevo attempt en BD │
│ - Guarda score, XP, ML Coins │
└─────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 4. Validación: is_correct && (xp > 0 || coins > 0) │
└─────────────────┬───────────────────────────────────────┘
│ ✅ TRUE
▼
┌─────────────────────────────────────────────────────────┐
│ 5. updateModuleProgressAfterCompletion() │
│ - Actualiza module_progress │
│ - Calcula % de progreso │
│ - Marca módulo como completed si 100% │
└─────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 6. updateMissionsProgress() ⬅️ NUEVO │
│ ┌──────────────────────────────────────────────────┐│
│ │ a. Obtiene misiones DAILY activas ││
│ │ b. Obtiene misiones WEEKLY activas ││
│ │ c. Filtra misiones con objetivo ││
│ │ 'complete_exercises' ││
│ │ d. Por cada misión: ││
│ │ - missionsService.updateProgress(..., +1) ││
│ │ - Incrementa objective.current ││
│ │ - Si alcanza target, marca completed ││
│ │ - Logging de éxito/error ││
│ └──────────────────────────────────────────────────┘│
└─────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 7. Retorna attempt guardado al Frontend │
│ - Frontend recibe XP, ML Coins, is_correct │
│ - Frontend actualiza UI de progreso │
│ - Frontend muestra misiones actualizadas │
└─────────────────────────────────────────────────────────┘
✅ Validaciones Realizadas
Compilación TypeScript
✅ npm run build
Sin errores de compilación
Dependencias Verificadas
✅ MissionsService exportado en GamificationModule (línea 115)
✅ GamificationModule importado en ProgressModule (línea 80)
✅ MissionTypeEnum disponible en mission.entity.ts
Métodos Verificados en MissionsService
✅ findByTypeAndUser(userId, type) - Línea 84
✅ updateProgress(missionId, userId, objectiveType, increment) - Línea 379
✅ Conversión interna auth.users.id → profiles.id
🧪 Testing
Script de Prueba E2E
Ubicación: apps/backend/scripts/test-missions-integration.sh
Flujo de prueba:
- Login como estudiante
- Obtener misiones diarias actuales
- Completar un ejercicio correctamente
- Verificar que misión se actualizó
Ejecutar:
cd apps/backend
bash scripts/test-missions-integration.sh
📝 Notas Importantes
⚠️ Conversión de IDs
MissionsService maneja internamente la conversión de auth.users.id → profiles.id mediante getProfileId(). No es necesario hacer conversión manual en ExerciseAttemptService.
⚠️ Tipos de Objetivo
Solo se actualizan misiones con objetivo type: 'complete_exercises'. Si se agregan otros tipos (ej. complete_module, earn_xp), se debe crear lógica adicional.
⚠️ Estados de Misión
Solo se procesan misiones en estado active o in_progress. Misiones en estado completed, claimed o expired se ignoran.
⚠️ Manejo de Errores
El método usa try-catch anidados para garantizar que errores en actualización de misiones NO bloqueen el flujo principal de completar ejercicios.
🚀 Próximos Pasos
-
Testing Manual:
- Ejecutar script de prueba E2E
- Verificar logs en backend
- Confirmar actualización en frontend
-
Testing Unitario:
- Crear mock de MissionsService
- Test: should update missions when exercise completed
- Test: should not update missions when exercise incorrect
- Test: should handle mission service errors gracefully
-
Monitoreo:
- Verificar logs de producción
- Confirmar tasa de éxito de actualización
- Identificar edge cases
-
Documentación:
- Actualizar Swagger si necesario
- Documentar en README del módulo
📚 Archivos Relacionados
| Archivo | Líneas | Descripción |
|---|---|---|
exercise-attempt.service.ts |
10-11 | Imports agregados |
exercise-attempt.service.ts |
36 | MissionsService inyectado |
exercise-attempt.service.ts |
78 | Llamada a updateMissionsProgress |
exercise-attempt.service.ts |
561-607 | Método updateMissionsProgress |
missions.service.ts |
84-109 | findByTypeAndUser() |
missions.service.ts |
379-445 | updateProgress() |
mission.entity.ts |
16-20 | MissionTypeEnum |
progress.module.ts |
80 | Import de GamificationModule |
gamification.module.ts |
115 | Export de MissionsService |
Implementado por: Backend-Agent Fecha: 2025-11-24 Estado: ✅ COMPLETADO Tests: ⏳ PENDIENTE Deploy: ⏳ PENDIENTE