workspace-v1/orchestration/analisis/ANALISIS-DUPLICADOS-ACHIEVEMENTS-2026-01-10.md
rckrdmrd e56e927a4d [MAINT-001] docs(orchestration): Actualizacion directivas SIMCO, perfiles y documentacion
Cambios incluidos:
- INDICE-DIRECTIVAS-WORKSPACE.yml actualizado
- Perfiles de agentes: PERFIL-ML.md, PERFIL-SECURITY.md
- Directivas SIMCO actualizadas:
  - SIMCO-ASIGNACION-PERFILES.md
  - SIMCO-CCA-SUBAGENTE.md
  - SIMCO-CONTEXT-ENGINEERING.md
  - SIMCO-CONTEXT-RESOLUTION.md
  - SIMCO-DELEGACION-PARALELA.md
- Inventarios actualizados: DEVENV-MASTER, DEVENV-PORTS
- Documentos de analisis agregados:
  - Analisis y planes de fix student portal
  - Analisis scripts BD
  - Analisis achievements, duplicados, gamification
  - Auditoria documentacion gamilit
  - Backlog discrepancias NEXUS
  - Planes maestros de resolucion
- Reportes de ejecucion agregados
- Knowledge base gamilit README actualizado
- Referencia submodulo gamilit actualizada (commit beb94f7)

Validaciones:
- Plan validado contra directivas SIMCO-GIT
- Dependencias verificadas
- Build gamilit: EXITOSO
2026-01-10 04:51:28 -06:00

514 lines
20 KiB
Markdown

# FASE 1: ANÁLISIS INICIAL DE DUPLICADOS - ACHIEVEMENTS SYSTEM
**Fecha:** 2026-01-10
**Proyecto:** Gamilit
**Componente:** Sistema de Achievements (Full Stack)
**Estado:** EN ANÁLISIS
---
## 1. RESUMEN EJECUTIVO
Se identificaron **múltiples duplicaciones y conflictos críticos** en el sistema de achievements a través de las tres capas (Database, Backend, Frontend):
| Capa | Duplicados | Severidad |
|------|------------|-----------|
| **Database (SQL)** | 4 funciones con overlap | 🔴 CRÍTICO |
| **Backend (NestJS)** | 3 servicios con lógica inconsistente | 🔴 CRÍTICO |
| **Frontend (React)** | 6+ archivos duplicados | 🟡 IMPORTANTE |
---
## 2. HALLAZGOS POR CAPA
### 2.1 BASE DE DATOS - Funciones SQL Duplicadas
#### CRÍTICO: Flujo de Recompensas Inconsistente
| Función | Otorga XP | Otorga Coins | Registra Transacción | Usa Multiplicador |
|---------|-----------|--------------|----------------------|-------------------|
| `claim_achievement_reward` | ✅ | ✅ | ✅ | ❌ |
| `check_and_award_achievements` | ✅ | ✅ | ✅ | ❌ |
| `award_ml_coins` | ❌ | ✅ | ✅ | ✅ (rank) |
| `process_exercise_completion` | ✅ | ✅ | ❌ | ❌ |
| `promote_to_next_rank` | ❌ | ✅ | ✅ | ❌ |
| `consume_comodin` | ✅ | ❌ | ❌ | ❌ |
**Problema Principal:** `claim_achievement_reward` y `check_and_award_achievements` duplican funcionalidad:
- `check_and_award_achievements`: Otorga recompensas AL DESBLOQUEAR
- `claim_achievement_reward`: Otorga recompensas AL RECLAMAR
**Riesgo:** Si ambas se ejecutan, el usuario recibe recompensas DUPLICADAS.
#### Funciones SQL Relacionadas
```
/apps/database/ddl/schemas/gamification_system/functions/
├── claim_achievement_reward.sql # MODIFICADA - Reclamar recompensas
├── check_and_award_achievements.sql # DUPLICACIÓN - Otorga al desbloquear
├── award_ml_coins.sql # HELPER - Con multiplicador de rank
├── process_exercise_completion.sql # PARCIAL - XP/coins sin transacción
├── promote_to_next_rank.sql # RANK - Bonus por promoción
├── update_user_rank.sql # RANK - Similar a promote
├── check_rank_promotion.sql # RANK - Orquestación
├── consume_comodin.sql # ITEMS - XP sin transacción
└── apply_xp_boost.sql # HELPER - Solo lectura
```
---
### 2.2 BACKEND - Servicios con Lógica Inconsistente
#### CRÍTICO: claimRewards() NO Distribuye Recompensas
**Archivo:** `achievements.service.ts`
```typescript
// PROBLEMA: Solo marca como reclamado, NO distribuye XP/Coins
async claimRewards(userId: string, achievementId: string): Promise<UserAchievement> {
// ... validaciones ...
userAchievement.rewards_claimed = true;
return this.userAchievementRepo.save(userAchievement);
// ❌ FALTA: Llamar a UserStatsService.addXp()
// ❌ FALTA: Llamar a MLCoinsService.addCoins()
}
```
**Contraste con otros servicios:**
| Servicio | Método | Distribuye Rewards |
|----------|--------|-------------------|
| `achievements.service.ts` | `claimRewards()` | ❌ NO |
| `exercise-rewards.service.ts` | `claimRewards()` | ✅ SÍ (XP + Coins) |
| `mission-claim.service.ts` | `claimMission()` | ✅ SÍ (XP + Coins) |
#### CRÍTICO: Backend NO Llama a claim_achievement_reward SQL
**Hallazgo:** Ningún archivo del backend invoca la función SQL `claim_achievement_reward`.
- La función SQL está corregida pero NO se usa
- El backend usa TypeORM directamente para actualizar `rewards_claimed`
- Las recompensas (XP/Coins) NO se distribuyen en absoluto
#### Schema Mismatch en Admin
**Archivo:** `admin-progress.service.ts`
| Campo SQL Query | Campo en Entity | Problema |
|-----------------|-----------------|----------|
| `a.tier` | `a.rarity` / `a.difficulty_level` | ❌ No existe |
| `ua.progress_current` | `ua.progress` | ❌ Nombre diferente |
| `ua.progress_required` | `ua.max_progress` | ❌ Nombre diferente |
| `ua.unlocked_at` | `ua.completed_at` | ❌ Nombre diferente |
---
### 2.3 FRONTEND - Archivos Duplicados
#### APIs Duplicadas (3 archivos)
| Archivo | Ubicación | Métodos Duplicados |
|---------|-----------|-------------------|
| `achievementsAPI.ts` | `/features/gamification/social/api/` | `claimAchievementRewards()` |
| `gamification.api.ts` | `/lib/api/` | `claimAchievement()` |
| `achievementsApi.ts` | `/services/api/admin/` | Admin-only (OK) |
**Problema:** `claimAchievementRewards()` y `claimAchievement()` son implementaciones diferentes del mismo endpoint.
#### Hooks Duplicados (4 archivos)
| Hook | Ubicación | Líneas | Propósito |
|------|-----------|--------|-----------|
| `useAchievements.ts` | `/hooks/` | ~450 | Definiciones hardcodeadas + polling |
| `useAchievements.ts` | `/features/gamification/social/hooks/` | ~80 | Wrapper del store |
| `useAchievementsEnhanced.ts` | `/apps/student/hooks/` | ~300 | Filtrado avanzado |
| `useAchievementsStats.ts` | `/apps/teacher/hooks/` | ~50 | Analytics (OK - diferente) |
**Problema Principal:** `/hooks/useAchievements.ts` tiene 450+ líneas de definiciones de achievements hardcodeadas que deberían venir del backend.
#### Transformers Inconsistentes
| Archivo | Approach |
|---------|----------|
| `achievementsAPI.ts` | Mappers INLINE (`mapToFrontendAchievement`) |
| `achievementTransformer.ts` | Transformer EXTERNO (`transformAchievements`) |
| `gamification.api.ts` | Usa transformer externo |
---
## 3. MATRIZ DE DUPLICACIÓN
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ FLUJO DE CLAIM REWARDS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Frontend Backend Database │
│ ───────── ─────── ──────── │
│ │
│ achievementsAPI.ts ──► achievements.service.ts ──► [NO LLAMA SQL] │
│ claimAchievementRewards() claimRewards() claim_achievement_reward│
│ │ │ │
│ │ │ ❌ Solo marca flag │
│ │ │ ❌ NO distribuye rewards │
│ │ │ │
│ gamification.api.ts ───► [MISMO ENDPOINT] │
│ claimAchievement() │
│ │
│ ════════════════════════════════════════════════════════════════════════ │
│ PROBLEMA: Las recompensas NUNCA se distribuyen al reclamar achievements │
│ ════════════════════════════════════════════════════════════════════════ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## 4. ARCHIVOS IDENTIFICADOS PARA ANÁLISIS DETALLADO
### 4.1 Database (SQL) - 9 archivos
```
/apps/database/ddl/schemas/gamification_system/functions/
├── claim_achievement_reward.sql # MODIFICADO
├── check_and_award_achievements.sql # DUPLICACIÓN POTENCIAL
├── award_ml_coins.sql # HELPER - Debería usarse
├── process_exercise_completion.sql # INCONSISTENTE
├── promote_to_next_rank.sql # OVERLAP CON update_user_rank
├── update_user_rank.sql # OVERLAP CON promote
├── check_rank_promotion.sql # ORQUESTACIÓN
├── consume_comodin.sql # INCONSISTENTE
└── update_leaderboard_streaks.sql # MENOR
```
### 4.2 Backend (NestJS) - 6 archivos
```
/apps/backend/src/modules/
├── gamification/
│ ├── services/
│ │ ├── achievements.service.ts # CRÍTICO - NO distribuye rewards
│ │ └── missions/mission-claim.service.ts # REFERENCIA - SÍ distribuye
│ ├── controllers/achievements.controller.ts
│ └── entities/
│ ├── achievement.entity.ts
│ └── user-achievement.entity.ts
├── progress/services/
│ └── grading/exercise-rewards.service.ts # REFERENCIA - SÍ distribuye
└── admin/services/
└── admin-progress.service.ts # SCHEMA MISMATCH
```
### 4.3 Frontend (React) - 8 archivos
```
/apps/frontend/src/
├── features/gamification/
│ ├── social/
│ │ ├── api/achievementsAPI.ts # MODIFICADO
│ │ ├── store/achievementsStore.ts # MODIFICADO
│ │ ├── hooks/useAchievements.ts # WRAPPER
│ │ └── types/achievementsTypes.ts
│ └── achievements/utils/
│ └── achievementTransformer.ts # NO USADO CONSISTENTEMENTE
├── hooks/useAchievements.ts # DUPLICADO - 450 líneas hardcoded
├── apps/student/hooks/useAchievementsEnhanced.ts
└── lib/api/gamification.api.ts # DUPLICADO
```
---
## 5. PROBLEMAS CRÍTICOS IDENTIFICADOS
### P-DUP-001: Recompensas NO Distribuidas al Reclamar
- **Severidad:** 🔴 CRÍTICO
- **Impacto:** Los usuarios reclaman achievements pero NO reciben XP/ML Coins
- **Causa:** Backend no llama función SQL ni servicios de distribución
- **Archivos:** `achievements.service.ts`, `claim_achievement_reward.sql`
### P-DUP-002: Doble Otorgamiento de Recompensas
- **Severidad:** 🔴 CRÍTICO
- **Impacto:** Si se usa `check_and_award_achievements` + `claim_achievement_reward`, rewards duplicados
- **Causa:** Dos funciones con mismo propósito
- **Archivos:** `check_and_award_achievements.sql`, `claim_achievement_reward.sql`
### P-DUP-003: APIs Frontend Duplicadas
- **Severidad:** 🟡 IMPORTANTE
- **Impacto:** Inconsistencia en manejo de errores y respuestas
- **Causa:** Dos archivos con mismo endpoint
- **Archivos:** `achievementsAPI.ts`, `gamification.api.ts`
### P-DUP-004: Hook con Definiciones Hardcodeadas
- **Severidad:** 🟡 IMPORTANTE
- **Impacto:** Achievements pueden no coincidir con backend
- **Causa:** 450 líneas de definiciones en frontend
- **Archivos:** `/hooks/useAchievements.ts`
### P-DUP-005: Schema Mismatch Admin
- **Severidad:** 🟡 IMPORTANTE
- **Impacto:** Dashboard admin muestra datos incorrectos o NULL
- **Causa:** Campos SQL no coinciden con entities
- **Archivos:** `admin-progress.service.ts`
### P-DUP-006: Transformers Inconsistentes
- **Severidad:** 🟢 MENOR
- **Impacto:** Código duplicado de transformación
- **Causa:** Inline mappers vs external transformer
- **Archivos:** `achievementsAPI.ts`, `achievementTransformer.ts`
---
## 6. SIGUIENTE FASE
**FASE 2:** Análisis detallado de cada archivo identificado para:
1. Confirmar duplicaciones con comparación línea a línea
2. Identificar dependencias entre archivos
3. Determinar cuál versión mantener/consolidar
4. Mapear impacto de cambios
---
## 7. FASE 2: ANÁLISIS DETALLADO
### 7.1 Comparación SQL: claim_achievement_reward vs check_and_grant_achievements
#### Función: `check_and_grant_achievements.sql` (Líneas 92-139)
```sql
-- AL DESBLOQUEAR (is_completed = true):
-- 1. Inserta en user_achievements
INSERT INTO gamification_system.user_achievements (
user_id, achievement_id, is_completed, completed_at, progress, max_progress
) VALUES (p_user_id, v_achievement.id, true, NOW(), 100, 100);
-- 2. Actualiza user_stats (XP + ML Coins)
UPDATE gamification_system.user_stats
SET
total_xp = COALESCE(total_xp, 0) + v_xp_reward,
ml_coins = v_new_balance,
achievements_earned = COALESCE(achievements_earned, 0) + 1
WHERE user_id = p_user_id;
-- 3. Registra transacción de coins
INSERT INTO gamification_system.ml_coins_transactions (...)
VALUES (...'earned_achievement'...'Logro desbloqueado: '...);
```
#### Función: `claim_achievement_reward.sql` (Líneas 54-95)
```sql
-- AL RECLAMAR (rewards_claimed = true):
-- 1. Actualiza user_achievements
UPDATE gamification_system.user_achievements
SET rewards_claimed = TRUE
WHERE user_id = p_user_id AND achievement_id = p_achievement_id;
-- 2. Actualiza user_stats (XP + ML Coins)
UPDATE gamification_system.user_stats
SET
total_xp = total_xp + COALESCE((v_achievement.rewards->>'xp')::INTEGER, v_achievement.points_value, 0),
ml_coins = v_new_balance
WHERE user_id = p_user_id;
-- 3. Registra transacción de coins
INSERT INTO gamification_system.ml_coins_transactions (...)
VALUES (...'earned_achievement'...'Recompensa reclamada: '...);
```
#### Tabla Comparativa
| Aspecto | check_and_grant | claim_achievement_reward |
|---------|-----------------|--------------------------|
| **Cuándo se ejecuta** | Al desbloquear logro | Al reclamar recompensa |
| **Otorga XP** | ✅ Sí | ✅ Sí |
| **Otorga ML Coins** | ✅ Sí | ✅ Sí |
| **Registra transacción** | ✅ Sí | ✅ Sí |
| **Incrementa achievements_earned** | ✅ Sí | ❌ No |
| **Mensaje transacción** | "Logro desbloqueado:" | "Recompensa reclamada:" |
**⚠️ PROBLEMA CRÍTICO:** Si ambas funciones se ejecutan para el mismo achievement, el usuario recibe **DOBLE** recompensa de XP y ML Coins.
---
### 7.2 Backend: achievements.service.ts - claimRewards() (Líneas 745-759)
```typescript
async claimRewards(userId: string, achievementId: string): Promise<UserAchievement> {
const userAchievement = await this.checkProgress(userId, achievementId);
if (!userAchievement.is_completed) {
throw new BadRequestException(`Achievement ${achievementId} is not completed yet`);
}
if (userAchievement.rewards_claimed) {
throw new BadRequestException(`Rewards already claimed for achievement ${achievementId}`);
}
// ⚠️ PROBLEMA: Solo marca el flag, NO distribuye recompensas
userAchievement.rewards_claimed = true;
return this.userAchievementRepo.save(userAchievement);
// ❌ FALTA: No llama a claim_achievement_reward SQL
// ❌ FALTA: No llama a UserStatsService.addXp()
// ❌ FALTA: No llama a MLCoinsService.addCoins()
// ❌ FALTA: No registra transacción de coins
}
```
**Contraste con otros servicios que SÍ distribuyen:**
| Servicio | Distribuye XP | Distribuye Coins | Registra Trans. |
|----------|--------------|------------------|-----------------|
| `achievements.service.claimRewards()` | ❌ NO | ❌ NO | ❌ NO |
| `exercise-rewards.service.claimRewards()` | ✅ SÍ | ✅ SÍ | ✅ SÍ |
| `mission-claim.service.claimMission()` | ✅ SÍ | ✅ SÍ | ✅ SÍ |
---
### 7.3 Frontend APIs: Comparación Detallada
#### achievementsAPI.ts - claimAchievementRewards() (Líneas 303-334)
```typescript
export const claimAchievementRewards = async (
userId: string,
achievementId: string,
): Promise<{
success: boolean;
achievement_id: string;
rewards_claimed: boolean;
ml_coins_awarded?: number; // ⚠️ Espera valores que backend NO envía
xp_awarded?: number; // ⚠️ Espera valores que backend NO envía
}> => {
const { data } = await apiClient.post<ApiResponse<{...}>>(
`/gamification/users/${userId}/achievements/${achievementId}/claim`
);
return {
success: true,
achievement_id: achievementId,
rewards_claimed: data.data.rewards_claimed,
// ml_coins_awarded y xp_awarded NO vienen del backend
};
};
```
#### gamification.api.ts - claimAchievement() (Líneas 166-172)
```typescript
claimAchievement: async (userId: string, achievementId: string): Promise<UserAchievement> => {
const { data } = await apiClient.post<UserAchievement>(
`/gamification/users/${userId}/achievements/${achievementId}/claim`,
{},
);
return data; // Retorna datos sin transformar
};
```
#### Diferencias Clave
| Aspecto | achievementsAPI.ts | gamification.api.ts |
|---------|-------------------|---------------------|
| **Ubicación** | /features/gamification/social/api/ | /lib/api/ |
| **Tipo retorno** | Custom object | UserAchievement |
| **Transformación** | Inline | Sin transformar |
| **Usado por** | achievementsStore.ts | AchievementsPage.tsx |
| **Manejo errores** | handleAPIError() | throw directo |
---
### 7.4 Frontend Hook: /hooks/useAchievements.ts - Definiciones Hardcodeadas
**⚠️ PROBLEMA MAYOR:** Este hook tiene ~450 líneas de código con achievement definitions hardcodeadas:
```typescript
// Líneas 71-250 - DEFINICIONES DUPLICADAS DEL BACKEND
const ACHIEVEMENT_DEFINITIONS: AchievementDefinition[] = [
{
id: 'first_steps',
title: 'Primeros Pasos',
description: 'Completa tu primer ejercicio',
icon: '👣',
rarity: 'common',
xp_reward: 10, // ⚠️ Puede no coincidir con backend
ml_coins_reward: 5, // ⚠️ Puede no coincidir con backend
condition: { type: 'exercises_completed', value: 1, operator: '>=' },
},
// ... 20+ más achievements hardcodeados
];
```
**Problemas:**
1. Las recompensas pueden NO coincidir con la base de datos
2. Nuevos achievements no aparecen hasta modificar código
3. Difícil mantener sincronizado con seeds/backend
4. Duplica lógica de detección que el backend ya tiene
---
### 7.5 Transformer: achievementTransformer.ts vs Inline Mappers
#### achievementTransformer.ts (Externo - CORRECTO)
```typescript
// Líneas 212-266 - Bien estructurado
export const transformAchievement = (apiResponse: ApiAchievementResponse): Achievement => {
const rewards = {
xp: apiResponse.rewards?.xp ?? apiResponse.points_value ?? 0,
mlCoins: apiResponse.rewards?.ml_coins ?? apiResponse.ml_coins_reward ?? 0,
// ...
};
return {
id: apiResponse.id,
name: apiResponse.name,
// ... mapeo completo y consistente
};
};
```
#### achievementsAPI.ts (Inline - INCONSISTENTE)
```typescript
// Líneas 382-411 - Duplica lógica del transformer
export const mapToFrontendAchievement = (
backendAchievement: BackendAchievement,
userProgress?: BackendUserAchievement,
): Achievement => {
return {
id: backendAchievement.id,
title: backendAchievement.name, // ⚠️ 'title' vs 'name' en transformer
// ... mapeo diferente
mlCoinsReward: backendAchievement.rewards?.ml_coins ?? backendAchievement.ml_coins_reward ?? 0,
xpReward: backendAchievement.rewards?.xp ?? backendAchievement.points_value ?? 0,
// ... campos diferentes
};
};
```
---
## 8. RESUMEN DE PROBLEMAS CONFIRMADOS
### CRÍTICOS (Requieren corrección inmediata)
| ID | Problema | Impacto | Archivos |
|----|----------|---------|----------|
| P-DUP-001 | Backend NO distribuye recompensas al claim | Users no reciben XP/Coins | achievements.service.ts |
| P-DUP-002 | SQL puede dar DOBLE recompensa | Inflación de economía | check_and_grant + claim_achievement |
| P-DUP-003 | Hook tiene 450+ líneas hardcoded | Desincronización con backend | /hooks/useAchievements.ts |
### IMPORTANTES (Deben corregirse pronto)
| ID | Problema | Impacto | Archivos |
|----|----------|---------|----------|
| P-DUP-004 | APIs duplicadas con diferente retorno | Confusión de desarrolladores | achievementsAPI.ts, gamification.api.ts |
| P-DUP-005 | Transformers inconsistentes | Datos transformados diferente | achievementsAPI.ts inline mappers |
### MENORES (Mejoras de calidad)
| ID | Problema | Impacto | Archivos |
|----|----------|---------|----------|
| P-DUP-006 | Schema mismatch en admin | Dashboard puede fallar | admin-progress.service.ts |
---
**Analizado por:** Claude (Arquitecto Técnico)
**Fecha:** 2026-01-10
**Estado:** FASE 2 COMPLETADA - Pendiente FASE 3 (Planeación)