You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

500 lines
15 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Postulante;
use App\Models\Proceso;
use App\Models\Area;
use App\Models\Curso;
use App\Models\Pregunta;
use App\Models\Alternativa;
use App\Models\Examen;
use App\Models\PreguntaAsignada;
use App\Models\AlternativaAsignada;
use App\Models\DetalleResultado;
use App\Models\RespuestaPostulante;
use App\Models\ResultadoExamen;
use App\Models\Recomendaciones;
use App\Models\Pago;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Http;
use App\Services\ExamenService;
class ExamenController extends Controller
{
protected $examenService;
public function __construct(ExamenService $examenService)
{
$this->examenService = $examenService;
}
public function procesoexamen(Request $request)
{
$postulante = $request->user();
$procesos = Proceso::where('activo', 1)
->whereNotExists(function ($q) use ($postulante) {
$q->select(\DB::raw(1))
->from('examenes')
->join('area_proceso', 'area_proceso.id', '=', 'examenes.area_proceso_id')
->whereColumn('area_proceso.proceso_id', 'procesos.id')
->where('examenes.postulante_id', $postulante->id);
})
->select('id', 'nombre', 'requiere_pago')
->get();
return response()->json($procesos);
}
public function areas(Request $request)
{
$procesoId = $request->query('proceso_id');
$areas = Area::select('areas.id', 'areas.nombre')
->whereHas('procesos', function ($query) use ($procesoId) {
$query->where('procesos.id', $procesoId)
->where('procesos.activo', 1);
})
->with(['procesos' => function ($q) use ($procesoId) {
$q->where('procesos.id', $procesoId)
->select('procesos.id')
->withPivot('id', 'proceso_id', 'area_id');
}])
->get()
->map(function ($area) {
$pivot = $area->procesos->first()->pivot;
return [
'area_id' => $area->id,
'nombre' => $area->nombre,
'area_proceso_id' => $pivot->id, // 🔥 CLAVE
];
});
return response()->json($areas);
}
public function crearExamen(Request $request)
{
$postulante = $request->user();
$request->validate([
'area_proceso_id' => 'required|exists:area_proceso,id',
]);
// 🔥 Obtener TODO desde el pivot
$areaProceso = \DB::table('area_proceso')
->join('procesos', 'procesos.id', '=', 'area_proceso.proceso_id')
->join('areas', 'areas.id', '=', 'area_proceso.area_id')
->where('area_proceso.id', $request->area_proceso_id)
->select(
'area_proceso.id',
'area_proceso.area_id',
'area_proceso.proceso_id',
'procesos.requiere_pago'
)
->first();
if (!$areaProceso) {
return response()->json(['message' => 'Relación área-proceso inválida'], 400);
}
$yaDioProceso = Examen::join('area_proceso', 'area_proceso.id', '=', 'examenes.area_proceso_id')
->where('examenes.postulante_id', $postulante->id)
->where('area_proceso.proceso_id', $areaProceso->proceso_id)
->exists();
if ($yaDioProceso) {
return response()->json([
'message' => 'Ya rendiste un examen para este proceso'
], 400);
}
$pagado = 0;
$pagoId = null;
// 💰 Validación de pago
if ($areaProceso->requiere_pago) {
$request->validate([
'tipo_pago' => 'required|in:pyto_peru,banco_nacion,caja',
'codigo_pago' => 'required',
]);
$response = $this->validarPago(
$request->tipo_pago,
$request->codigo_pago,
$postulante->dni
);
if (!$response['estado']) {
return response()->json(['message' => 'Pago inválido'], 400);
}
$pago = Pago::firstOrCreate(
[
'codigo_pago' => $request->codigo_pago,
'tipo_pago' => $request->tipo_pago,
],
[
'postulante_id' => $postulante->id,
'monto' => $response['monto'],
'fecha_pago' => $response['fecha_pago'],
]
);
if ($pago->utilizado) {
return response()->json(['message' => 'Pago ya utilizado'], 400);
}
$pago->update(['utilizado' => true]);
$pagado = 1;
$pagoId = $pago->id;
}
// 🧠 Crear / actualizar examen
$examen = Examen::updateOrCreate(
['postulante_id' => $postulante->id,
'area_proceso_id' => $areaProceso->id, // 🔥 pivot
'pagado' => $pagado,
'tipo_pago' => $request->tipo_pago ?? null,
'pago_id' => $pagoId,
]
);
return response()->json([
'success' => true,
'examen_id' => $examen->id,
'mensaje' => 'Examen creado correctamente',
]);
}
public function validarPago($tipoPago, $codigoPago, $dni)
{
return match ($tipoPago) {
'pyto_peru' => $this->validarPagoPytoPeru($codigoPago, $dni),
'banco_nacion' => $this->validarPagoBancoNacion($codigoPago, $dni),
'caja' => $this->validarPagoCaja($codigoPago, $dni),
default => ['estado' => false],
};
}
private function validarPagoBancoNacion($secuencia, $dni)
{
$url = 'https://inscripciones.admision.unap.edu.pe/api/get-pagos-banco-secuencia';
$response = Http::post($url, [
'secuencia' => $secuencia,
]);
if (!$response->successful()) {
\Log::error('Error en la solicitud a la API', ['status' => $response->status(), 'body' => $response->body()]);
return ['estado' => false, 'message' => 'Error en la solicitud a la API'];
}
$data = $response->json();
if (isset($data['estado']) && $data['estado'] === true) {
foreach ($data['datos'] as $pago) {
if (isset($pago['dni']) && trim($pago['dni']) == trim($dni)) {
return [
'estado' => true,
'monto' => $pago['imp_pag'],
'fecha_pago' => $pago['fch_pag'],
];
}
}
}
return ['estado' => false, 'message' => 'Datos no válidos o no encontrados'];
}
private function validarPagoPytoPeru($authorizationCode, $dni)
{
$url = "https://service2.unap.edu.pe/PAYMENTS_MNG/v1/{$dni}/8/";
$response = Http::get($url);
if ($response->successful()) {
$data = $response->json();
if (isset($data['data'][0]['autorizationCode']) && $data['data'][0]['autorizationCode'] === $authorizationCode) {
return [
'estado' => true,
'monto' => $data['data'][0]['total'],
'fecha_pago' => $data['data'][0]['confirmedDate'],
];
}
}
return ['estado' => false, 'message' => 'El código de autorización no coincide.'];
}
private function validarPagoCaja($codigoPago, $dni)
{
$url = "https://inscripciones.admision.unap.edu.pe/api/get-pago-caja/{$dni}/{$codigoPago}";
$response = Http::get($url);
if (!$response->successful()) {
\Log::error('Error API Caja', [
'status' => $response->status(),
'body' => $response->body()
]);
return ['estado' => false, 'message' => 'Error en la API de Caja'];
}
$data = $response->json();
if (
isset($data['paymentTitle'], $data['paymentAmount'], $data['paymentDatetime']) &&
trim($data['paymentTitle']) === trim($codigoPago)
) {
return [
'estado' => true,
'monto' => (float) $data['paymentAmount'],
'fecha_pago' => $data['paymentDatetime'],
];
}
return ['estado' => false, 'message' => 'Pago no válido o no encontrado'];
}
public function miExamenActual(Request $request)
{
$postulante = $request->user();
// Obtenemos el examen más reciente junto con área y proceso usando joins
$examen = Examen::join('area_proceso', 'area_proceso.id', '=', 'examenes.area_proceso_id')
->join('areas', 'areas.id', '=', 'area_proceso.area_id')
->join('procesos', 'procesos.id', '=', 'area_proceso.proceso_id')
->where('examenes.postulante_id', $postulante->id)
->select(
'examenes.id',
'examenes.pagado',
'examenes.tipo_pago',
'examenes.intentos', // intentos del examen
'areas.id as area_id',
'areas.nombre as area_nombre',
'procesos.id as proceso_id',
'procesos.nombre as proceso_nombre',
'procesos.intentos_maximos as proceso_intentos_maximos' // intentos máximos del proceso
)
->latest('examenes.created_at') // solo el más reciente
->first();
if (!$examen) {
return response()->json([
'success' => true,
'mensaje' => 'No tienes exámenes asignados actualmente',
'examen' => null
]);
}
return response()->json([
'success' => true,
'examen' => [
'id' => $examen->id,
'intentos' => $examen->intentos,
'intentos_maximos' => $examen->proceso_intentos_maximos,
'proceso' => [
'id' => $examen->proceso_id,
'nombre' => $examen->proceso_nombre,
],
'area' => [
'id' => $examen->area_id,
'nombre' => $examen->area_nombre,
],
'pagado' => $examen->pagado,
'tipo_pago' => $examen->tipo_pago ?? null,
]
]);
}
/**
* 2. GENERAR PREGUNTAS PARA EXAMEN (si no las tiene)
*/
public function generarPreguntas($examenId)
{
$examen = Examen::findOrFail($examenId);
// Verificar que el examen pertenece al usuario autenticado
$postulante = request()->user();
if ($examen->postulante_id !== $postulante->id) {
return response()->json([
'success' => false,
'message' => 'No autorizado'
], 403);
}
// Si YA tiene preguntas, no generar nuevas, solo confirmar éxito
if ($examen->preguntasAsignadas()->exists()) {
return response()->json([
'success' => true,
'message' => 'El examen ya tiene preguntas generadas',
'ya_tiene_preguntas' => true,
'total_preguntas' => $examen->preguntasAsignadas()->count()
]);
}
// Si NO tiene preguntas, generar usando el servicio
$resultado = $this->examenService->generarPreguntasExamen($examen);
if (!$resultado['success']) {
return response()->json($resultado, 400);
}
return response()->json([
'success' => true,
'message' => 'Preguntas generadas exitosamente',
'ya_tiene_preguntas' => false,
'total_preguntas' => $examen->preguntasAsignadas()->count()
]);
}
/**
* 4. INICIAR EXAMEN (marcar hora inicio)
*/
/**
* 4. INICIAR EXAMEN (marcar hora inicio e incrementar intentos)
*/
public function iniciarExamen(Request $request)
{
$request->validate([
'examen_id' => 'required|exists:examenes,id'
]);
$examen = Examen::findOrFail($request->examen_id);
// Verificar que el examen pertenece al usuario autenticado
$postulante = $request->user();
if ($examen->postulante_id !== $postulante->id) {
return response()->json([
'success' => false,
'message' => 'No autorizado'
], 403);
}
// Obtener datos del área-proceso
$areaProceso = \DB::table('area_proceso')
->join('procesos', 'area_proceso.proceso_id', '=', 'procesos.id')
->join('areas', 'area_proceso.area_id', '=', 'areas.id')
->where('area_proceso.id', $examen->area_proceso_id)
->select(
'procesos.id as proceso_id',
'procesos.nombre as proceso_nombre',
'procesos.duracion as proceso_duracion',
'procesos.intentos_maximos as proceso_intentos_maximos',
'areas.nombre as area_nombre'
)
->first();
// Verificar que tenga preguntas
if (!$examen->preguntasAsignadas()->exists()) {
return response()->json([
'success' => false,
'message' => 'El examen no tiene preguntas generadas'
], 400);
}
// Verificar límite de intentos
if ($areaProceso && $examen->intentos >= $areaProceso->proceso_intentos_maximos) {
return response()->json([
'success' => false,
'message' => 'Has alcanzado el número máximo de intentos para este proceso'
], 403);
}
// 🔥 Incrementar intento y marcar inicio
$examen->increment('intentos');
$examen->update([
'hora_inicio' => now(), // Hora normal del servidor
'estado' => 'en_progreso'
]);
// Obtener preguntas completas
$preguntas = $this->examenService->obtenerPreguntasExamen($examen);
return response()->json([
'success' => true,
'examen' => [
'id' => $examen->id,
'estado' => $examen->estado,
'hora_inicio' => $examen->hora_inicio,
'intentos' => $examen->intentos,
'intentos_maximos' => $areaProceso->proceso_intentos_maximos ?? null,
'proceso' => $areaProceso->proceso_nombre ?? null,
'duracion' => $areaProceso->proceso_duracion ?? null,
'area' => $areaProceso->area_nombre ?? null
],
'preguntas' => $preguntas
]);
}
public function responderPregunta($preguntaAsignadaId, Request $request)
{
$request->validate([
'respuesta' => 'required|string'
]);
$preguntaAsignada = PreguntaAsignada::with(['examen', 'pregunta'])
->findOrFail($preguntaAsignadaId);
// Verificar que pertenece al usuario
$postulante = $request->user();
if ($preguntaAsignada->examen->postulante_id !== $postulante->id) {
return response()->json([
'success' => false,
'message' => 'No autorizado'
], 403);
}
// Guardar respuesta
$resultado = $this->examenService->guardarRespuesta(
$preguntaAsignada,
$request->respuesta
);
return response()->json($resultado);
}
/**
* 6. FINALIZAR EXAMEN
*/
public function finalizarExamen($examenId)
{
$examen = Examen::findOrFail($examenId);
// Verificar que el examen pertenece al usuario autenticado
$postulante = request()->user();
if ($examen->postulante_id !== $postulante->id) {
return response()->json([
'success' => false,
'message' => 'No autorizado'
], 403);
}
$this->examenService->finalizarExamen($examen);
return response()->json([
'success' => true,
'message' => 'Examen finalizado exitosamente'
]);
}
}