Implementados 5 módulos de analytics con agent swarm: 1. DASHBOARD ADMINISTRATIVO - Resumen ejecutivo (reservas, ingresos, usuarios) - Vista del día con alertas - Calendario semanal de ocupación 2. MÉTRICAS DE OCUPACIÓN - Ocupación por fecha, cancha, franja horaria - Horas pico (top 5 demandados) - Comparativa entre períodos - Tendencias de uso 3. MÉTRICAS FINANCIERAS - Ingresos por período, cancha, tipo - Métodos de pago más usados - Estadísticas de reembolsos - Tendencias de crecimiento - Top días de ingresos 4. MÉTRICAS DE USUARIOS - Stats generales y actividad - Top jugadores (por partidos/victorias/puntos) - Detección de churn (riesgo de abandono) - Tasa de retención - Crecimiento mensual 5. EXPORTACIÓN DE DATOS - Exportar a CSV (separado por ;) - Exportar a JSON - Exportar a Excel (múltiples hojas) - Reportes completos descargables Endpoints nuevos (solo admin): - /analytics/dashboard/* - /analytics/occupancy/* - /analytics/revenue/* - /analytics/reports/* - /analytics/users/* - /analytics/exports/* Dependencias: - xlsx - Generación de archivos Excel Utilidades: - Cálculo de crecimiento porcentual - Formateo de moneda - Agrupación por fechas - Relleno de fechas faltantes
143 lines
3.9 KiB
TypeScript
143 lines
3.9 KiB
TypeScript
import { Request, Response, NextFunction } from 'express';
|
|
import { ReportService } from '../../services/analytics/report.service';
|
|
import { ApiError } from '../../middleware/errorHandler';
|
|
|
|
// Validar fechas
|
|
const validateDates = (startDateStr: string, endDateStr: string): { startDate: Date; endDate: Date } => {
|
|
const startDate = new Date(startDateStr);
|
|
const endDate = new Date(endDateStr);
|
|
|
|
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
|
|
throw new ApiError('Fechas inválidas', 400);
|
|
}
|
|
|
|
if (startDate > endDate) {
|
|
throw new ApiError('La fecha de inicio debe ser anterior a la fecha de fin', 400);
|
|
}
|
|
|
|
// Ajustar endDate para incluir todo el día
|
|
endDate.setHours(23, 59, 59, 999);
|
|
|
|
return { startDate, endDate };
|
|
};
|
|
|
|
export class ReportController {
|
|
/**
|
|
* GET /analytics/reports/revenue
|
|
* Reporte completo de ingresos
|
|
*/
|
|
static async getRevenueReport(req: Request, res: Response, next: NextFunction) {
|
|
try {
|
|
const { startDate: startDateStr, endDate: endDateStr } = req.query;
|
|
|
|
if (!startDateStr || !endDateStr) {
|
|
throw new ApiError('Se requieren fechas de inicio y fin', 400);
|
|
}
|
|
|
|
const { startDate, endDate } = validateDates(startDateStr as string, endDateStr as string);
|
|
|
|
const report = await ReportService.generateRevenueReport(startDate, endDate);
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
data: report,
|
|
meta: {
|
|
reportType: 'revenue',
|
|
generatedAt: new Date().toISOString(),
|
|
},
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /analytics/reports/occupancy
|
|
* Reporte de ocupación
|
|
*/
|
|
static async getOccupancyReport(req: Request, res: Response, next: NextFunction) {
|
|
try {
|
|
const { startDate: startDateStr, endDate: endDateStr } = req.query;
|
|
|
|
if (!startDateStr || !endDateStr) {
|
|
throw new ApiError('Se requieren fechas de inicio y fin', 400);
|
|
}
|
|
|
|
const { startDate, endDate } = validateDates(startDateStr as string, endDateStr as string);
|
|
|
|
const report = await ReportService.generateOccupancyReport(startDate, endDate);
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
data: report,
|
|
meta: {
|
|
reportType: 'occupancy',
|
|
generatedAt: new Date().toISOString(),
|
|
},
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /analytics/reports/users
|
|
* Reporte de usuarios
|
|
*/
|
|
static async getUserReport(req: Request, res: Response, next: NextFunction) {
|
|
try {
|
|
const { startDate: startDateStr, endDate: endDateStr } = req.query;
|
|
|
|
if (!startDateStr || !endDateStr) {
|
|
throw new ApiError('Se requieren fechas de inicio y fin', 400);
|
|
}
|
|
|
|
const { startDate, endDate } = validateDates(startDateStr as string, endDateStr as string);
|
|
|
|
const report = await ReportService.generateUserReport(startDate, endDate);
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
data: report,
|
|
meta: {
|
|
reportType: 'users',
|
|
generatedAt: new Date().toISOString(),
|
|
},
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /analytics/reports/summary
|
|
* Resumen ejecutivo
|
|
*/
|
|
static async getExecutiveSummary(req: Request, res: Response, next: NextFunction) {
|
|
try {
|
|
const { startDate: startDateStr, endDate: endDateStr } = req.query;
|
|
|
|
if (!startDateStr || !endDateStr) {
|
|
throw new ApiError('Se requieren fechas de inicio y fin', 400);
|
|
}
|
|
|
|
const { startDate, endDate } = validateDates(startDateStr as string, endDateStr as string);
|
|
|
|
const summary = await ReportService.getReportSummary(startDate, endDate);
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
data: summary,
|
|
meta: {
|
|
reportType: 'executive-summary',
|
|
generatedAt: new Date().toISOString(),
|
|
},
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
export default ReportController;
|