- Tambah endpoint GET /tariffs (list tariffs)
- Tambah endpoint GET /locations/{code} (detail location)
- Tambah endpoint GET /gates/{location_code}/{gate_code} (detail gate)
- Tambah endpoint GET /tariffs/{location_code}/{gate_code}/{category} (detail tariff)
- Tambah endpoint GET /audit-logs (audit trail history)
- Tambah endpoint GET /entry-events (raw entry events)
- Tambah endpoint GET /realtime/events (realtime events list)
- Tambah field camera di gates (support HLS, RTSP, HTTP streaming)
- Migration 004: add camera column to gates table
- Update validasi dan service untuk support camera field
389 lines
11 KiB
PHP
389 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Modules\Retribusi\Frontend;
|
|
|
|
use App\Support\ResponseHelper;
|
|
use App\Support\Validator;
|
|
use PDOException;
|
|
use Psr\Http\Message\ResponseInterface;
|
|
use Psr\Http\Message\ServerRequestInterface;
|
|
|
|
class LocationController
|
|
{
|
|
private RetribusiReadService $readService;
|
|
private RetribusiWriteService $writeService;
|
|
private AuditService $auditService;
|
|
|
|
public function __construct(
|
|
RetribusiReadService $readService,
|
|
RetribusiWriteService $writeService,
|
|
AuditService $auditService
|
|
) {
|
|
$this->readService = $readService;
|
|
$this->writeService = $writeService;
|
|
$this->auditService = $auditService;
|
|
}
|
|
|
|
public function getLocations(
|
|
ServerRequestInterface $request,
|
|
ResponseInterface $response
|
|
): ResponseInterface {
|
|
$queryParams = $request->getQueryParams();
|
|
[$page, $limit] = Validator::validatePagination($queryParams);
|
|
|
|
$data = $this->readService->getLocations($page, $limit);
|
|
$total = $this->readService->getLocationsTotal();
|
|
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'success' => true,
|
|
'data' => $data,
|
|
'meta' => [
|
|
'page' => $page,
|
|
'limit' => $limit,
|
|
'total' => $total,
|
|
'pages' => (int) ceil($total / $limit)
|
|
],
|
|
'timestamp' => time()
|
|
]
|
|
);
|
|
}
|
|
|
|
public function getLocation(
|
|
ServerRequestInterface $request,
|
|
ResponseInterface $response,
|
|
array $args
|
|
): ResponseInterface {
|
|
$code = $args['code'] ?? null;
|
|
if ($code === null || !is_string($code)) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'validation_error',
|
|
'fields' => ['code' => 'Invalid location code']
|
|
],
|
|
422
|
|
);
|
|
}
|
|
|
|
$data = $this->writeService->getLocation($code);
|
|
if ($data === null) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'not_found',
|
|
'message' => 'Location not found'
|
|
],
|
|
404
|
|
);
|
|
}
|
|
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'success' => true,
|
|
'data' => $data,
|
|
'timestamp' => time()
|
|
]
|
|
);
|
|
}
|
|
|
|
public function createLocation(
|
|
ServerRequestInterface $request,
|
|
ResponseInterface $response
|
|
): ResponseInterface {
|
|
$body = $request->getParsedBody();
|
|
|
|
if (!is_array($body)) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'validation_error',
|
|
'fields' => ['body' => 'Invalid JSON body']
|
|
],
|
|
422
|
|
);
|
|
}
|
|
|
|
$errors = Validator::validateLocation($body, false);
|
|
if (!empty($errors)) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'validation_error',
|
|
'fields' => $errors
|
|
],
|
|
422
|
|
);
|
|
}
|
|
|
|
try {
|
|
// Check if location already exists
|
|
$existing = $this->writeService->getLocation($body['code']);
|
|
if ($existing !== null) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'conflict',
|
|
'message' => 'Location with this code already exists'
|
|
],
|
|
409
|
|
);
|
|
}
|
|
|
|
// Create location
|
|
$data = $this->writeService->createLocation($body);
|
|
|
|
// Audit log
|
|
$serverParams = $request->getServerParams();
|
|
$this->auditService->log(
|
|
(int) $request->getAttribute('user_id'),
|
|
$request->getAttribute('username', ''),
|
|
$request->getAttribute('role', ''),
|
|
'create',
|
|
'locations',
|
|
$body['code'],
|
|
null,
|
|
$data,
|
|
AuditService::getClientIp($serverParams),
|
|
$serverParams['HTTP_USER_AGENT'] ?? null
|
|
);
|
|
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'success' => true,
|
|
'data' => $data,
|
|
'timestamp' => time()
|
|
],
|
|
201
|
|
);
|
|
|
|
} catch (PDOException $e) {
|
|
// Check for unique constraint violation
|
|
if ($e->getCode() === '23000') {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'conflict',
|
|
'message' => 'Location with this code already exists'
|
|
],
|
|
409
|
|
);
|
|
}
|
|
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'server_error',
|
|
'message' => 'Database error occurred'
|
|
],
|
|
500
|
|
);
|
|
}
|
|
}
|
|
|
|
public function updateLocation(
|
|
ServerRequestInterface $request,
|
|
ResponseInterface $response,
|
|
array $args
|
|
): ResponseInterface {
|
|
$code = $args['code'] ?? null;
|
|
if ($code === null || !is_string($code)) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'validation_error',
|
|
'fields' => ['code' => 'Invalid location code']
|
|
],
|
|
422
|
|
);
|
|
}
|
|
|
|
$body = $request->getParsedBody();
|
|
if (!is_array($body)) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'validation_error',
|
|
'fields' => ['body' => 'Invalid JSON body']
|
|
],
|
|
422
|
|
);
|
|
}
|
|
|
|
// Prevent changing code
|
|
if (isset($body['code']) && $body['code'] !== $code) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'validation_error',
|
|
'fields' => ['code' => 'Code is immutable']
|
|
],
|
|
422
|
|
);
|
|
}
|
|
|
|
$errors = Validator::validateLocation($body, true);
|
|
if (!empty($errors)) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'validation_error',
|
|
'fields' => $errors
|
|
],
|
|
422
|
|
);
|
|
}
|
|
|
|
try {
|
|
// Check if location exists
|
|
$before = $this->writeService->getLocation($code);
|
|
if ($before === null) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'not_found',
|
|
'message' => 'Location not found'
|
|
],
|
|
404
|
|
);
|
|
}
|
|
|
|
// Update location
|
|
$after = $this->writeService->updateLocation($code, $body);
|
|
if ($after === null) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'not_found',
|
|
'message' => 'Location not found'
|
|
],
|
|
404
|
|
);
|
|
}
|
|
|
|
// Audit log
|
|
$serverParams = $request->getServerParams();
|
|
$this->auditService->log(
|
|
(int) $request->getAttribute('user_id'),
|
|
$request->getAttribute('username', ''),
|
|
$request->getAttribute('role', ''),
|
|
'update',
|
|
'locations',
|
|
$code,
|
|
$before,
|
|
$after,
|
|
AuditService::getClientIp($serverParams),
|
|
$serverParams['HTTP_USER_AGENT'] ?? null
|
|
);
|
|
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'success' => true,
|
|
'data' => $after,
|
|
'timestamp' => time()
|
|
]
|
|
);
|
|
|
|
} catch (PDOException $e) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'server_error',
|
|
'message' => 'Database error occurred'
|
|
],
|
|
500
|
|
);
|
|
}
|
|
}
|
|
|
|
public function deleteLocation(
|
|
ServerRequestInterface $request,
|
|
ResponseInterface $response,
|
|
array $args
|
|
): ResponseInterface {
|
|
$code = $args['code'] ?? null;
|
|
if ($code === null || !is_string($code)) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'validation_error',
|
|
'fields' => ['code' => 'Invalid location code']
|
|
],
|
|
422
|
|
);
|
|
}
|
|
|
|
try {
|
|
// Check if location exists
|
|
$before = $this->writeService->getLocation($code);
|
|
if ($before === null) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'not_found',
|
|
'message' => 'Location not found'
|
|
],
|
|
404
|
|
);
|
|
}
|
|
|
|
// Soft delete
|
|
$deleted = $this->writeService->deleteLocation($code);
|
|
if (!$deleted) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'server_error',
|
|
'message' => 'Failed to delete location'
|
|
],
|
|
500
|
|
);
|
|
}
|
|
|
|
// Get after state (is_active=0)
|
|
$after = $this->writeService->getLocation($code);
|
|
|
|
// Audit log
|
|
$serverParams = $request->getServerParams();
|
|
$this->auditService->log(
|
|
(int) $request->getAttribute('user_id'),
|
|
$request->getAttribute('username', ''),
|
|
$request->getAttribute('role', ''),
|
|
'delete',
|
|
'locations',
|
|
$code,
|
|
$before,
|
|
$after,
|
|
AuditService::getClientIp($serverParams),
|
|
$serverParams['HTTP_USER_AGENT'] ?? null
|
|
);
|
|
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'success' => true,
|
|
'data' => ['deleted' => true],
|
|
'timestamp' => time()
|
|
]
|
|
);
|
|
|
|
} catch (PDOException $e) {
|
|
return ResponseHelper::json(
|
|
$response,
|
|
[
|
|
'error' => 'server_error',
|
|
'message' => 'Database error occurred'
|
|
],
|
|
500
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|