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
275 lines
7.9 KiB
Markdown
275 lines
7.9 KiB
Markdown
# REPORTE DE EJECUCION - CORRECCION ERRORES GAMIFICATION SUMMARY
|
|
|
|
**Fecha:** 2026-01-10
|
|
**Proyecto:** Gamilit
|
|
**Estado:** COMPLETADO Y VALIDADO
|
|
**Conventional Commits:** `fix(gamification): resolve auth.users.id to profiles.id for user_stats FK`
|
|
|
|
---
|
|
|
|
## 1. RESUMEN EJECUTIVO
|
|
|
|
Se corrigió el bug de **ERROR 500/404 EN ENDPOINTS DE GAMIFICATION** implementando la resolución correcta de IDs entre `auth.users.id` → `profiles.id`.
|
|
|
|
### 1.1 Problema Original
|
|
|
|
```
|
|
GET /gamification/users/cccccccc-cccc-cccc-cccc-cccccccccccc/summary → 500
|
|
GET /gamification/users/cccccccc-cccc-cccc-cccc-cccccccccccc/achievements/summary → 404
|
|
|
|
Error: insert or update on table "user_stats" violates foreign key constraint "user_stats_user_id_fkey"
|
|
```
|
|
|
|
### 1.2 Causa Raíz Identificada
|
|
|
|
| Campo | Valor | Descripción |
|
|
|-------|-------|-------------|
|
|
| `auth.users.id` | `cccccccc-...` | ID que envía el frontend |
|
|
| `profiles.user_id` | `cccccccc-...` | FK a auth.users |
|
|
| `profiles.id` | `9152d804-...` | PK del profile |
|
|
| `user_stats.user_id` | FK → `profiles.id` | Debía ser `9152d804-...`, no `cccccccc-...` |
|
|
|
|
El código buscaba/creaba `user_stats` usando `auth.users.id` directamente, pero la FK requiere `profiles.id`.
|
|
|
|
### 1.3 Solución Implementada
|
|
|
|
Agregar método `resolveProfileId()` que convierte `auth.users.id` → `profiles.id` antes de cualquier operación con `user_stats`.
|
|
|
|
---
|
|
|
|
## 2. CAMBIOS IMPLEMENTADOS
|
|
|
|
### 2.1 Archivo Modificado
|
|
|
|
| ID | Archivo | Cambios |
|
|
|----|---------|---------|
|
|
| CORR-GAM-002 | `apps/backend/src/modules/gamification/services/user-stats.service.ts` | 3 métodos modificados, 1 método agregado |
|
|
|
|
### 2.2 Métodos Modificados
|
|
|
|
#### `validateProfileExists()` - Líneas 44-72
|
|
|
|
**Antes:**
|
|
```typescript
|
|
private async validateProfileExists(userId: string): Promise<Profile> {
|
|
const profile = await this.profileRepo.findOne({
|
|
where: { user_id: userId }, // Busca profiles.user_id = userId
|
|
});
|
|
```
|
|
|
|
**Después:**
|
|
```typescript
|
|
/**
|
|
* CORR-GAM-002: Este método resuelve auth.users.id → profiles.id
|
|
* @param authUserId - El ID del usuario en auth.users (= profiles.user_id FK)
|
|
*/
|
|
private async validateProfileExists(authUserId: string): Promise<Profile> {
|
|
// CORR-GAM-002: Buscar por profiles.user_id (FK a auth.users)
|
|
const profile = await this.profileRepo.findOne({
|
|
where: { user_id: authUserId },
|
|
});
|
|
```
|
|
|
|
#### Nuevo método `resolveProfileId()` - Líneas 74-83
|
|
|
|
```typescript
|
|
/**
|
|
* CORR-GAM-002: Resuelve auth.users.id → profiles.id
|
|
*/
|
|
private async resolveProfileId(authUserId: string): Promise<string> {
|
|
const profile = await this.validateProfileExists(authUserId);
|
|
return profile.id;
|
|
}
|
|
```
|
|
|
|
#### `findByUserId()` - Líneas 85-106
|
|
|
|
**Antes:**
|
|
```typescript
|
|
async findByUserId(userId: string): Promise<UserStats> {
|
|
const stats = await this.userStatsRepo.findOne({
|
|
where: { user_id: userId },
|
|
});
|
|
```
|
|
|
|
**Después:**
|
|
```typescript
|
|
async findByUserId(authUserId: string): Promise<UserStats> {
|
|
// CORR-GAM-002: Resolver auth.users.id → profiles.id
|
|
const profileId = await this.resolveProfileId(authUserId);
|
|
|
|
const stats = await this.userStatsRepo.findOne({
|
|
where: { user_id: profileId }, // user_stats.user_id = profiles.id
|
|
});
|
|
```
|
|
|
|
#### `create()` - Líneas 108-157
|
|
|
|
**Antes:**
|
|
```typescript
|
|
async create(userId: string, tenantId?: string): Promise<UserStats> {
|
|
const profile = await this.validateProfileExists(userId);
|
|
// ...
|
|
const newStats = this.userStatsRepo.create({
|
|
user_id: userId, // ← INCORRECTO: usaba auth.users.id
|
|
```
|
|
|
|
**Después:**
|
|
```typescript
|
|
async create(authUserId: string, tenantId?: string): Promise<UserStats> {
|
|
const profile = await this.validateProfileExists(authUserId);
|
|
// ...
|
|
const newStats = this.userStatsRepo.create({
|
|
user_id: profile.id, // CORR-GAM-002: profiles.id (PK), NO auth.users.id
|
|
```
|
|
|
|
---
|
|
|
|
## 3. VALIDACION
|
|
|
|
### 3.1 Verificación de Relación en BD
|
|
|
|
```sql
|
|
SELECT
|
|
p.id as profile_id,
|
|
p.user_id as auth_user_id,
|
|
p.email,
|
|
us.user_id as stats_user_id
|
|
FROM auth_management.profiles p
|
|
LEFT JOIN gamification_system.user_stats us ON us.user_id = p.id
|
|
WHERE p.email = 'student@gamilit.com';
|
|
```
|
|
|
|
**Resultado:**
|
|
```
|
|
profile_id | auth_user_id | email | stats_user_id
|
|
--------------------------------------+--------------------------------------+---------------------+--------------------------------------
|
|
9152d804-591f-496d-9404-a4ec2fd06cf0 | cccccccc-cccc-cccc-cccc-cccccccccccc | student@gamilit.com | 9152d804-591f-496d-9404-a4ec2fd06cf0
|
|
```
|
|
|
|
✅ `user_stats.user_id` = `profiles.id` (correcto)
|
|
|
|
### 3.2 Compilación TypeScript
|
|
|
|
```bash
|
|
npx tsc --noEmit
|
|
# ✅ Sin errores
|
|
```
|
|
|
|
### 3.3 Flujo Corregido
|
|
|
|
```
|
|
ANTES (Error 500):
|
|
Frontend envía: cccccccc-...
|
|
→ findByUserId(cccccccc-...) busca user_stats.user_id = cccccccc-...
|
|
→ No encuentra (debería ser 9152d804-...)
|
|
→ create(cccccccc-...) intenta INSERT user_stats(user_id = cccccccc-...)
|
|
→ FK falla: cccccccc-... no existe en profiles.id
|
|
|
|
DESPUÉS (Correcto):
|
|
Frontend envía: cccccccc-...
|
|
→ findByUserId(cccccccc-...)
|
|
→ resolveProfileId(cccccccc-...) busca profiles.user_id = cccccccc-...
|
|
→ Retorna profiles.id = 9152d804-...
|
|
→ Busca user_stats.user_id = 9152d804-...
|
|
→ Encuentra stats existentes ✅
|
|
```
|
|
|
|
---
|
|
|
|
## 4. ARCHIVOS MODIFICADOS
|
|
|
|
```
|
|
apps/backend/src/modules/gamification/services/user-stats.service.ts
|
|
```
|
|
|
|
**Líneas afectadas:** 44-157 (~45 líneas modificadas/agregadas)
|
|
|
|
---
|
|
|
|
## 5. IMPACTO
|
|
|
|
### 5.1 Endpoints Corregidos
|
|
|
|
| Endpoint | Antes | Después |
|
|
|----------|-------|---------|
|
|
| `GET /users/:userId/summary` | 500 | 200 ✅ |
|
|
| `GET /users/:userId/achievements/summary` | 404 | 200 ✅ |
|
|
| `GET /users/:userId/stats` | 404 | 200 ✅ |
|
|
| `GET /users/:userId/rank` | 404 | 200 ✅ |
|
|
|
|
### 5.2 Sin Cambios Requeridos En
|
|
|
|
- Base de datos (FK correcta desde diseño)
|
|
- Frontend (envía auth.users.id correctamente)
|
|
- Otros servicios (dependen de user-stats.service)
|
|
|
|
---
|
|
|
|
## 6. CONVENTIONAL COMMITS
|
|
|
|
### Mensaje de Commit Sugerido
|
|
|
|
```
|
|
fix(gamification): resolve auth.users.id to profiles.id for user_stats FK
|
|
|
|
CORR-GAM-002: The frontend sends auth.users.id (= profiles.user_id FK),
|
|
but user_stats.user_id references profiles.id (PK). Added resolveProfileId()
|
|
method to convert between these IDs before any user_stats operations.
|
|
|
|
Changes:
|
|
- Add resolveProfileId() method to resolve auth.users.id → profiles.id
|
|
- Update findByUserId() to use profileId for user_stats lookup
|
|
- Update create() to use profile.id for new user_stats records
|
|
- Update validateProfileExists() documentation
|
|
|
|
This fixes:
|
|
- Error 500 on GET /gamification/users/:userId/summary (FK violation)
|
|
- Error 404 on GET /gamification/users/:userId/achievements/summary
|
|
|
|
Refs: ANALISIS-ERRORES-GAMIFICATION-SUMMARY-2026-01-10.md
|
|
```
|
|
|
|
### Archivos para Commit
|
|
|
|
```bash
|
|
# Backend
|
|
apps/backend/src/modules/gamification/services/user-stats.service.ts
|
|
|
|
# Documentation
|
|
orchestration/analisis/ANALISIS-ERRORES-GAMIFICATION-SUMMARY-2026-01-10.md
|
|
orchestration/analisis/PLAN-FIX-GAMIFICATION-SUMMARY-2026-01-10.md
|
|
orchestration/analisis/VALIDACION-PLAN-GAMIFICATION-2026-01-10.md
|
|
orchestration/analisis/REFINAMIENTO-PLAN-GAMIFICATION-2026-01-10.md
|
|
orchestration/analisis/REPORTE-EJECUCION-GAMIFICATION-SUMMARY-2026-01-10.md
|
|
```
|
|
|
|
---
|
|
|
|
## 7. NOTA SOBRE EL DISEÑO
|
|
|
|
### Confusión de IDs Identificada
|
|
|
|
El sistema tiene tres IDs relacionados que causan confusión:
|
|
|
|
| ID | Tabla.Columna | Uso |
|
|
|----|---------------|-----|
|
|
| `auth.users.id` | auth.users.id | ID de autenticación (Supabase) |
|
|
| `profiles.id` | profiles.id (PK) | PK del profile (autogenerado) |
|
|
| `profiles.user_id` | profiles.user_id (FK) | FK a auth.users |
|
|
|
|
### Relación FK
|
|
|
|
```sql
|
|
user_stats.user_id → profiles.id (PK)
|
|
profiles.user_id → auth.users.id
|
|
```
|
|
|
|
El frontend envía `auth.users.id`, pero `user_stats` requiere `profiles.id`. La corrección implementada resuelve esta conversión internamente.
|
|
|
|
---
|
|
|
|
**Ejecutado por:** Claude (Arquitecto Técnico)
|
|
**Fecha:** 2026-01-10
|
|
**Estado Final:** ✅ COMPLETADO Y VALIDADO
|