Features: - React 18 + TypeScript frontend with Vite - Go + Gin backend API - PostgreSQL database - JWT authentication with refresh tokens - User management (admin panel) - Docker containerization - Progress tracking system - 4 economic modules structure Fixed: - Login with username or email - User creation without required email - Database nullable timestamps - API response field naming
90 lines
2.1 KiB
Go
90 lines
2.1 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"github.com/ren/econ/backend/internal/models"
|
|
)
|
|
|
|
type ContenidoRepository struct {
|
|
db *pgxpool.Pool
|
|
}
|
|
|
|
func NewContenidoRepository(db *pgxpool.Pool) *ContenidoRepository {
|
|
return &ContenidoRepository{db: db}
|
|
}
|
|
|
|
func (r *ContenidoRepository) GetModulos(ctx context.Context) ([]models.ModuloResumen, error) {
|
|
query := `
|
|
SELECT DISTINCT modulo_numero, titulo, contenido::text
|
|
FROM ejercicios
|
|
ORDER BY modulo_numero
|
|
`
|
|
rows, err := r.db.Query(ctx, query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var modulos []models.ModuloResumen
|
|
for rows.Next() {
|
|
var m models.ModuloResumen
|
|
var contenidoJSON string
|
|
err := rows.Scan(&m.Numero, &m.Titulo, &contenidoJSON)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Extraer descripción del contenido JSON
|
|
var contenido map[string]interface{}
|
|
json.Unmarshal([]byte(contenidoJSON), &contenido)
|
|
if desc, ok := contenido["descripcion"].(string); ok {
|
|
m.Descripcion = desc
|
|
} else {
|
|
m.Descripcion = ""
|
|
}
|
|
modulos = append(modulos, m)
|
|
}
|
|
return modulos, nil
|
|
}
|
|
|
|
func (r *ContenidoRepository) GetModulo(ctx context.Context, numero int) (*models.Modulo, error) {
|
|
query := `
|
|
SELECT id, titulo, tipo, contenido, orden
|
|
FROM ejercicios WHERE modulo_numero = $1
|
|
ORDER BY orden
|
|
`
|
|
rows, err := r.db.Query(ctx, query, numero)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
modulo := &models.Modulo{Numero: numero}
|
|
var ejercicios []models.Ejercicio
|
|
|
|
for rows.Next() {
|
|
var e models.Ejercicio
|
|
var contenidoJSON string
|
|
err := rows.Scan(&e.ID, &e.Titulo, &e.Tipo, &contenidoJSON, &e.Orden)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
json.Unmarshal([]byte(contenidoJSON), &e.Contenido)
|
|
e.Numero = e.ID
|
|
ejercicios = append(ejercicios, e)
|
|
}
|
|
|
|
if len(ejercicios) > 0 {
|
|
modulo.Titulo = ejercicios[0].Titulo
|
|
modulo.Ejercicios = ejercicios
|
|
// Extraer descripción del primer ejercicio
|
|
if desc, ok := ejercicios[0].Contenido["descripcion"].(string); ok {
|
|
modulo.Descripcion = desc
|
|
}
|
|
}
|
|
|
|
return modulo, nil
|
|
}
|