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
333 lines
9.8 KiB
Markdown
333 lines
9.8 KiB
Markdown
# FASE 3: PLAN DE CORRECCIÓN - DUPLICADOS ACHIEVEMENTS SYSTEM
|
|
|
|
**Fecha:** 2026-01-10
|
|
**Proyecto:** Gamilit
|
|
**Basado en:** ANALISIS-DUPLICADOS-ACHIEVEMENTS-2026-01-10.md
|
|
**Estado:** EN PLANEACIÓN
|
|
|
|
---
|
|
|
|
## 1. DECISIONES ARQUITECTÓNICAS
|
|
|
|
### 1.1 Modelo de Distribución de Recompensas
|
|
|
|
**DECISIÓN:** Implementar modelo de **Claim-to-Earn** (reclamar para ganar)
|
|
|
|
| Acción | Qué sucede | Quién distribuye |
|
|
|--------|-----------|------------------|
|
|
| Achievement desbloqueado | Solo marca `is_completed = true` | Backend service |
|
|
| Achievement reclamado | Distribuye XP + ML Coins | SQL function |
|
|
|
|
**Justificación:**
|
|
- Permite UX de "reclamar recompensa" (más satisfactorio)
|
|
- Evita dar recompensas automáticas si usuario no las ve
|
|
- Consistent con misiones y ejercicios que también usan "claim"
|
|
|
|
### 1.2 API Unificada
|
|
|
|
**DECISIÓN:** Mantener `gamification.api.ts` como API principal, deprecar duplicados en `achievementsAPI.ts`
|
|
|
|
**Justificación:**
|
|
- `gamification.api.ts` ya usa transformers externos (mejor mantenibilidad)
|
|
- Consolidar en `/lib/api/` para acceso global
|
|
- `achievementsAPI.ts` tiene mappers inline que duplican transformer
|
|
|
|
### 1.3 Hook de Definiciones
|
|
|
|
**DECISIÓN:** Deprecar `/hooks/useAchievements.ts` (450 líneas hardcodeadas)
|
|
|
|
**Justificación:**
|
|
- Las definiciones deben venir del backend
|
|
- El hook `/features/gamification/social/hooks/useAchievements.ts` usa el store correctamente
|
|
- La detección automática la debe hacer el backend (ya implementada en `detectAndGrantEarned`)
|
|
|
|
---
|
|
|
|
## 2. PLAN DE CORRECCIÓN POR PROBLEMA
|
|
|
|
### 2.1 P-DUP-001: Backend NO Distribuye Recompensas
|
|
|
|
**Severidad:** 🔴 CRÍTICO
|
|
|
|
**Archivo a modificar:** `/apps/backend/src/modules/gamification/services/achievements.service.ts`
|
|
|
|
**Cambio propuesto:** Llamar a función SQL `claim_achievement_reward` en lugar de solo marcar flag
|
|
|
|
```typescript
|
|
// ANTES (líneas 745-759):
|
|
async claimRewards(userId: string, achievementId: string): Promise<UserAchievement> {
|
|
const userAchievement = await this.checkProgress(userId, achievementId);
|
|
// validaciones...
|
|
userAchievement.rewards_claimed = true;
|
|
return this.userAchievementRepo.save(userAchievement);
|
|
}
|
|
|
|
// DESPUÉS:
|
|
async claimRewards(userId: string, achievementId: string): Promise<{
|
|
userAchievement: UserAchievement;
|
|
xp_granted: number;
|
|
coins_granted: number;
|
|
}> {
|
|
// Llamar función SQL que distribuye recompensas
|
|
const result = await this.dataSource.query(
|
|
`SELECT * FROM gamification_system.claim_achievement_reward($1, $2)`,
|
|
[userId, achievementId]
|
|
);
|
|
|
|
if (!result[0].success) {
|
|
throw new BadRequestException(result[0].message);
|
|
}
|
|
|
|
// Obtener userAchievement actualizado
|
|
const userAchievement = await this.checkProgress(userId, achievementId);
|
|
|
|
return {
|
|
userAchievement,
|
|
xp_granted: result[0].xp_granted,
|
|
coins_granted: result[0].coins_granted,
|
|
};
|
|
}
|
|
```
|
|
|
|
**Dependencias:**
|
|
- Función SQL `claim_achievement_reward` debe estar actualizada (✅ Ya corregida)
|
|
- Controller debe actualizarse para manejar nuevo retorno
|
|
|
|
---
|
|
|
|
### 2.2 P-DUP-002: SQL Puede Dar DOBLE Recompensa
|
|
|
|
**Severidad:** 🔴 CRÍTICO
|
|
|
|
**Archivos a modificar:**
|
|
1. `/apps/database/ddl/schemas/gamification_system/functions/check_and_award_achievements.sql`
|
|
|
|
**Cambio propuesto:** Remover distribución de recompensas de `check_and_grant_achievements` - solo debe marcar `is_completed`, no dar XP/Coins
|
|
|
|
```sql
|
|
-- ANTES (líneas 111-118):
|
|
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,
|
|
updated_at = NOW()
|
|
WHERE user_id = p_user_id;
|
|
|
|
-- Registrar transaccion de coins
|
|
INSERT INTO gamification_system.ml_coins_transactions (...)
|
|
|
|
-- DESPUÉS:
|
|
UPDATE gamification_system.user_stats
|
|
SET
|
|
achievements_earned = COALESCE(achievements_earned, 0) + 1,
|
|
updated_at = NOW()
|
|
WHERE user_id = p_user_id;
|
|
|
|
-- NO registrar transacción - se hará en claim_achievement_reward
|
|
```
|
|
|
|
**Impacto:**
|
|
- El desbloqueo automático ya no dará recompensas
|
|
- Las recompensas solo se dan al reclamar
|
|
|
|
---
|
|
|
|
### 2.3 P-DUP-003: Hook con Definiciones Hardcodeadas
|
|
|
|
**Severidad:** 🔴 CRÍTICO
|
|
|
|
**Archivo a deprecar:** `/apps/frontend/src/hooks/useAchievements.ts`
|
|
|
|
**Acción:** Agregar comentario de deprecación y redireccionar a hook correcto
|
|
|
|
```typescript
|
|
/**
|
|
* @deprecated Use useAchievements from '@/features/gamification/social/hooks/useAchievements'
|
|
* Este hook contiene definiciones hardcodeadas que están desactualizadas.
|
|
* La detección de achievements se hace en el backend (achievements.service.detectAndGrantEarned)
|
|
*/
|
|
```
|
|
|
|
**Alternativa:** Si se necesita auto-detection en frontend, llamar endpoint:
|
|
```typescript
|
|
// En lugar de polling local, usar endpoint del backend
|
|
const result = await apiClient.post(`/gamification/users/${userId}/achievements/detect`);
|
|
```
|
|
|
|
---
|
|
|
|
### 2.4 P-DUP-004: APIs Duplicadas
|
|
|
|
**Severidad:** 🟡 IMPORTANTE
|
|
|
|
**Archivo a modificar:** `/apps/frontend/src/features/gamification/social/api/achievementsAPI.ts`
|
|
|
|
**Cambio propuesto:** Deprecar métodos duplicados y re-exportar desde gamification.api.ts
|
|
|
|
```typescript
|
|
// ANTES:
|
|
export const claimAchievementRewards = async (...) => { ... };
|
|
|
|
// DESPUÉS:
|
|
/**
|
|
* @deprecated Use gamificationApi.claimAchievement from '@/lib/api/gamification.api'
|
|
*/
|
|
export const claimAchievementRewards = gamificationApi.claimAchievement;
|
|
```
|
|
|
|
**Archivos que consumen achievementsAPI.ts:**
|
|
- `achievementsStore.ts` - Actualizar imports
|
|
|
|
---
|
|
|
|
### 2.5 P-DUP-005: Transformers Inconsistentes
|
|
|
|
**Severidad:** 🟡 IMPORTANTE
|
|
|
|
**Archivo a modificar:** `/apps/frontend/src/features/gamification/social/api/achievementsAPI.ts`
|
|
|
|
**Cambio propuesto:** Remover mappers inline y usar transformer externo
|
|
|
|
```typescript
|
|
// ANTES (líneas 382-411):
|
|
export const mapToFrontendAchievement = (...) => { ... };
|
|
|
|
// DESPUÉS:
|
|
import { transformAchievement, transformUserAchievement } from
|
|
'@/features/gamification/achievements/utils/achievementTransformer';
|
|
|
|
// Re-export para compatibilidad
|
|
export { transformAchievement as mapToFrontendAchievement };
|
|
```
|
|
|
|
---
|
|
|
|
### 2.6 P-DUP-006: Schema Mismatch Admin
|
|
|
|
**Severidad:** 🟢 MENOR
|
|
|
|
**Archivo a modificar:** `/apps/backend/src/modules/admin/services/admin-progress.service.ts`
|
|
|
|
**Cambio propuesto:** Actualizar query SQL para usar campos correctos
|
|
|
|
```sql
|
|
-- ANTES:
|
|
SELECT
|
|
ua.id, ua.achievement_id, a.name, a.description, a.category, a.tier,
|
|
a.xp_reward, a.ml_coins_reward, a.icon_url,
|
|
ua.unlocked_at, ua.progress_current, ua.progress_required
|
|
|
|
-- DESPUÉS:
|
|
SELECT
|
|
ua.id, ua.achievement_id, a.name, a.description, a.category,
|
|
a.difficulty_level as tier, -- Mapear difficulty_level a tier
|
|
COALESCE((a.rewards->>'xp')::INTEGER, a.points_value, 0) as xp_reward,
|
|
a.ml_coins_reward, a.icon as icon_url,
|
|
ua.completed_at as unlocked_at, -- Usar completed_at
|
|
ua.progress as progress_current, -- Usar progress
|
|
ua.max_progress as progress_required -- Usar max_progress
|
|
```
|
|
|
|
---
|
|
|
|
## 3. ORDEN DE EJECUCIÓN
|
|
|
|
### Fase A: Correcciones SQL (Base de datos)
|
|
|
|
| Orden | Archivo | Cambio | Dependencias |
|
|
|-------|---------|--------|--------------|
|
|
| A.1 | `check_and_award_achievements.sql` | Remover distribución de rewards | Ninguna |
|
|
|
|
### Fase B: Correcciones Backend (NestJS)
|
|
|
|
| Orden | Archivo | Cambio | Dependencias |
|
|
|-------|---------|--------|--------------|
|
|
| B.1 | `achievements.service.ts` | Llamar SQL function en claimRewards | A.1 completado |
|
|
| B.2 | `achievements.controller.ts` | Actualizar response type | B.1 completado |
|
|
| B.3 | `admin-progress.service.ts` | Fix schema mismatch | Ninguna |
|
|
|
|
### Fase C: Correcciones Frontend (React)
|
|
|
|
| Orden | Archivo | Cambio | Dependencias |
|
|
|-------|---------|--------|--------------|
|
|
| C.1 | `achievementsAPI.ts` | Deprecar y usar transformer | Ninguna |
|
|
| C.2 | `achievementsStore.ts` | Actualizar imports | C.1 completado |
|
|
| C.3 | `/hooks/useAchievements.ts` | Agregar deprecation notice | Ninguna |
|
|
|
|
---
|
|
|
|
## 4. VALIDACIONES REQUERIDAS
|
|
|
|
### 4.1 Antes de Ejecutar
|
|
|
|
- [ ] Backup de base de datos
|
|
- [ ] Verificar que no hay usuarios en proceso de claim
|
|
- [ ] Tests existentes pasan
|
|
|
|
### 4.2 Durante Ejecución
|
|
|
|
- [ ] Cada cambio SQL aplicado con `\df` para verificar
|
|
- [ ] Build de backend exitoso después de cada cambio
|
|
- [ ] Build de frontend exitoso después de cada cambio
|
|
|
|
### 4.3 Después de Ejecutar
|
|
|
|
- [ ] Test E2E: Usuario desbloquea achievement → NO recibe rewards automático
|
|
- [ ] Test E2E: Usuario reclama achievement → SÍ recibe rewards
|
|
- [ ] Test: Intentar reclamar dos veces → Error apropiado
|
|
- [ ] Test: Dashboard admin muestra datos correctos
|
|
|
|
---
|
|
|
|
## 5. ROLLBACK PLAN
|
|
|
|
### Si falla Fase A (SQL):
|
|
```sql
|
|
-- Restaurar check_and_award_achievements.sql desde git
|
|
git checkout HEAD~1 -- apps/database/ddl/schemas/gamification_system/functions/check_and_award_achievements.sql
|
|
-- Re-ejecutar script de creación de función
|
|
```
|
|
|
|
### Si falla Fase B (Backend):
|
|
```bash
|
|
# Restaurar archivos modificados
|
|
git checkout HEAD~1 -- apps/backend/src/modules/gamification/services/achievements.service.ts
|
|
git checkout HEAD~1 -- apps/backend/src/modules/gamification/controllers/achievements.controller.ts
|
|
npm run build
|
|
```
|
|
|
|
### Si falla Fase C (Frontend):
|
|
```bash
|
|
# Restaurar archivos modificados
|
|
git checkout HEAD~1 -- apps/frontend/src/features/gamification/social/
|
|
npm run build
|
|
```
|
|
|
|
---
|
|
|
|
## 6. ESTIMACIÓN DE IMPACTO
|
|
|
|
| Métrica | Antes | Después |
|
|
|---------|-------|---------|
|
|
| **Archivos modificados** | - | 7 |
|
|
| **Líneas cambiadas** | - | ~150 |
|
|
| **Funciones deprecadas** | - | 3 |
|
|
| **Tests requeridos** | - | 4 E2E |
|
|
|
|
---
|
|
|
|
## 7. DOCUMENTOS RELACIONADOS
|
|
|
|
| Documento | Estado |
|
|
|-----------|--------|
|
|
| ANALISIS-DUPLICADOS-ACHIEVEMENTS-2026-01-10.md | ✅ Completado |
|
|
| PLAN-DUPLICADOS-ACHIEVEMENTS-2026-01-10.md | ✅ Actual |
|
|
| VALIDACION-PLAN-DUPLICADOS-2026-01-10.md | ⏳ Pendiente |
|
|
|
|
---
|
|
|
|
**Planeado por:** Claude (Arquitecto Técnico)
|
|
**Fecha:** 2026-01-10
|
|
**Estado:** FASE 3 COMPLETADA - Pendiente FASE 4 (Validación)
|