Files
presensi/app/Modules/Mobile/Controllers/RegistrationController.php
2026-03-05 14:37:36 +07:00

276 lines
10 KiB
PHP

<?php
namespace App\Modules\Mobile\Controllers;
use App\Core\BaseApiController;
use App\Modules\Academic\Models\ClassModel;
use App\Modules\Academic\Models\StudentModel;
use App\Modules\Mobile\Models\StudentMobileAccountModel;
use CodeIgniter\HTTP\ResponseInterface;
class RegistrationController extends BaseApiController
{
/**
* POST /api/mobile/login
*
* Body: { "nisn": "...", "pin": "..." }
*/
public function login(): ResponseInterface
{
$payload = $this->request->getJSON(true) ?? [];
$nisn = trim((string) ($payload['nisn'] ?? ''));
$pin = (string) ($payload['pin'] ?? '');
if ($nisn === '' || $pin === '') {
return $this->errorResponse('NISN dan PIN wajib diisi', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY);
}
$studentModel = new StudentModel();
$student = $studentModel->findByNisn($nisn);
if (! $student) {
return $this->errorResponse('Siswa dengan NISN tersebut tidak ditemukan', null, null, ResponseInterface::HTTP_NOT_FOUND);
}
if ((int) ($student->is_active ?? 1) === 0) {
return $this->errorResponse('Siswa ini tidak aktif', null, null, ResponseInterface::HTTP_FORBIDDEN);
}
$accountModel = new StudentMobileAccountModel();
$accountRow = $accountModel->where('student_id', (int) $student->id)->first();
if (! $accountRow || empty($accountRow['pin_hash']) || ! password_verify($pin, $accountRow['pin_hash'])) {
return $this->errorResponse('PIN salah', null, null, ResponseInterface::HTTP_UNAUTHORIZED);
}
$classModel = new ClassModel();
/** @var \App\Modules\Academic\Entities\ClassEntity|null $class */
$class = null;
if ($student->class_id) {
$class = $classModel->find($student->class_id);
}
$classLabel = '-';
if ($class !== null) {
$parts = array_filter([
trim((string) ($class->grade ?? '')),
trim((string) ($class->major ?? '')),
trim((string) ($class->name ?? '')),
]);
$label = implode(' ', $parts);
$classLabel = $label !== '' ? $label : ('Kelas #' . (int) $class->id);
}
$data = [
'student_id' => (int) $student->id,
'name' => (string) ($student->name ?? ''),
'nisn' => (string) ($student->nisn ?? ''),
'class_id' => $student->class_id ? (int) $student->class_id : null,
'class_label' => $classLabel,
];
return $this->successResponse($data, 'Login berhasil');
}
/**
* POST /api/mobile/register/nisn
*
* Body: { "nisn": "..." }
*/
public function checkNisn(): ResponseInterface
{
$payload = $this->request->getJSON(true) ?? [];
$nisn = trim((string) ($payload['nisn'] ?? ''));
if ($nisn === '') {
return $this->errorResponse('NISN wajib diisi', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY);
}
$studentModel = new StudentModel();
$student = $studentModel->findByNisn($nisn);
if (! $student) {
return $this->errorResponse('Siswa dengan NISN tersebut tidak ditemukan', null, null, ResponseInterface::HTTP_NOT_FOUND);
}
if ((int) ($student->is_active ?? 1) === 0) {
return $this->errorResponse('Siswa ini tidak aktif', null, null, ResponseInterface::HTTP_FORBIDDEN);
}
// Sudah punya akun mobile (PIN) → arahkan ke login
$accountModel = new StudentMobileAccountModel();
$accountRow = $accountModel->where('student_id', (int) $student->id)->first();
if ($accountRow && ! empty($accountRow['pin_hash'])) {
return $this->errorResponse('NISN ini sudah terdaftar. Silakan masuk.', null, null, ResponseInterface::HTTP_CONFLICT);
}
$classModel = new ClassModel();
$classes = $classModel->orderBy('grade', 'ASC')
->orderBy('major', 'ASC')
->orderBy('name', 'ASC')
->findAll();
$availableClasses = [];
foreach ($classes as $c) {
$parts = array_filter([
trim((string) ($c->grade ?? '')),
trim((string) ($c->major ?? '')),
trim((string) ($c->name ?? '')),
]);
$label = implode(' ', $parts);
$availableClasses[] = [
'id' => (int) $c->id,
'label' => $label !== '' ? $label : ('Kelas #' . (int) $c->id),
];
}
$currentClassId = $student->class_id ? (int) $student->class_id : null;
$currentClassLabel = null;
if ($currentClassId !== null) {
foreach ($availableClasses as $cls) {
if ($cls['id'] === $currentClassId) {
$currentClassLabel = $cls['label'];
break;
}
}
}
$data = [
'student_id' => (int) $student->id,
'name' => (string) ($student->name ?? ''),
'nisn' => (string) ($student->nisn ?? ''),
'current_class_id' => $currentClassId,
'current_class_label' => $currentClassLabel,
'available_classes' => $availableClasses,
];
return $this->successResponse($data, 'NISN valid');
}
/**
* POST /api/mobile/register/complete
*
* Body: { "student_id": 0, "class_id": 0, "pin": "123456" }
*/
public function complete(): ResponseInterface
{
$payload = $this->request->getJSON(true) ?? [];
$studentId = (int) ($payload['student_id'] ?? 0);
$classId = (int) ($payload['class_id'] ?? 0);
$pin = (string) ($payload['pin'] ?? '');
if ($studentId <= 0) {
return $this->errorResponse('student_id wajib diisi', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY);
}
if ($classId <= 0) {
return $this->errorResponse('Kelas wajib dipilih', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY);
}
$pin = trim($pin);
if ($pin === '' || strlen($pin) < 4) {
return $this->errorResponse('PIN minimal 4 karakter', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY);
}
$studentModel = new StudentModel();
$classModel = new ClassModel();
$student = $studentModel->find($studentId);
if (! $student) {
return $this->errorResponse('Siswa tidak ditemukan', null, null, ResponseInterface::HTTP_NOT_FOUND);
}
$class = $classModel->find($classId);
if (! $class) {
return $this->errorResponse('Kelas tidak ditemukan', null, null, ResponseInterface::HTTP_NOT_FOUND);
}
// Update class_id siswa (mapping pertama kali / koreksi)
$studentModel->update($studentId, ['class_id' => $classId]);
// Simpan / update akun mobile siswa
$accountModel = new StudentMobileAccountModel();
$pinHash = password_hash($pin, PASSWORD_BCRYPT);
$exists = $accountModel->where('student_id', $studentId)->first();
if ($exists) {
$accountModel->update((int) $exists['id'], [
'pin_hash' => $pinHash,
]);
} else {
$accountModel->insert([
'student_id' => $studentId,
'pin_hash' => $pinHash,
]);
}
$classLabelParts = array_filter([
trim((string) ($class->grade ?? '')),
trim((string) ($class->major ?? '')),
trim((string) ($class->name ?? '')),
]);
$classLabel = implode(' ', $classLabelParts);
$data = [
'student_id' => $studentId,
'name' => (string) ($student->name ?? ''),
'nisn' => (string) ($student->nisn ?? ''),
'class_id' => (int) $class->id,
'class_label' => $classLabel !== '' ? $classLabel : ('Kelas #' . (int) $class->id),
];
return $this->successResponse($data, 'Registrasi mobile berhasil');
}
/**
* POST /api/mobile/forgot-pin
*
* Body: { "nisn": "...", "new_pin": "1234", "new_pin_confirm": "1234" }
*/
public function forgotPin(): ResponseInterface
{
$payload = $this->request->getJSON(true) ?? [];
$nisn = trim((string) ($payload['nisn'] ?? ''));
$newPin = (string) ($payload['new_pin'] ?? '');
$newPinConfirm = (string) ($payload['new_pin_confirm'] ?? '');
if ($nisn === '') {
return $this->errorResponse('NISN wajib diisi', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY);
}
$newPin = trim($newPin);
$newPinConfirm = trim($newPinConfirm);
if ($newPin === '' || strlen($newPin) < 4) {
return $this->errorResponse('PIN baru minimal 4 karakter', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY);
}
if ($newPin !== $newPinConfirm) {
return $this->errorResponse('PIN baru dan konfirmasi tidak sama', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY);
}
$studentModel = new StudentModel();
$student = $studentModel->findByNisn($nisn);
if (! $student) {
return $this->errorResponse('Siswa dengan NISN tersebut tidak ditemukan', null, null, ResponseInterface::HTTP_NOT_FOUND);
}
if ((int) ($student->is_active ?? 1) === 0) {
return $this->errorResponse('Siswa ini tidak aktif', null, null, ResponseInterface::HTTP_FORBIDDEN);
}
$accountModel = new StudentMobileAccountModel();
$accountRow = $accountModel->where('student_id', (int) $student->id)->first();
if (! $accountRow || empty($accountRow['pin_hash'])) {
return $this->errorResponse('NISN ini belum terdaftar. Silakan daftar dulu.', null, null, ResponseInterface::HTTP_CONFLICT);
}
$pinHash = password_hash($newPin, PASSWORD_BCRYPT);
$accountModel->update((int) $accountRow['id'], ['pin_hash' => $pinHash]);
return $this->successResponse(
['student_id' => (int) $student->id],
'PIN berhasil direset. Silakan masuk dengan PIN baru.'
);
}
}