148 lines
5.6 KiB
PHP
148 lines
5.6 KiB
PHP
<?php
|
|
namespace App\Controllers;
|
|
|
|
use App\Helpers\Response;
|
|
use App\Helpers\Validator;
|
|
use App\Middleware\JwtAuthMiddleware;
|
|
use App\Middleware\RateLimitMiddleware;
|
|
use App\Models\Queue;
|
|
use App\Services\AuditLogger;
|
|
use App\Services\NotificationService;
|
|
use App\Services\SmartQueueEngine;
|
|
|
|
/**
|
|
* Class QueueController
|
|
* Smart Queue & Realtime Board REST API Controller
|
|
*
|
|
* @package App\Controllers
|
|
*/
|
|
class QueueController
|
|
{
|
|
private Queue $queueModel;
|
|
private SmartQueueEngine $aiEngine;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->queueModel = new Queue();
|
|
$this->aiEngine = new SmartQueueEngine();
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/queues - Realtime Queue Board
|
|
*/
|
|
public function index(): void
|
|
{
|
|
RateLimitMiddleware::handle(60, 60);
|
|
|
|
$branchId = isset($_GET['branch_id']) ? (int)$_GET['branch_id'] : null;
|
|
$board = $this->queueModel->getRealtimeBoard($branchId);
|
|
|
|
Response::success('Realtime Queue Board Data', [
|
|
'total_active' => count($board),
|
|
'branch_id' => $branchId ?: 'All',
|
|
'queues' => $board,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/queues/walkin - Create Walk-in Queue & Execute AI Assign
|
|
*/
|
|
public function walkin(): void
|
|
{
|
|
RateLimitMiddleware::handle(30, 60);
|
|
$user = JwtAuthMiddleware::handle(); // ต้องเป็นเจ้าหน้าที่ Reception/Admin ที่ล็อกอิน
|
|
|
|
$input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
|
|
$validator = Validator::make($input, [
|
|
'patient_id' => 'required|numeric',
|
|
'service_id' => 'required|numeric',
|
|
'priority' => 'in:Normal,VIP,Emergency',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
Response::error('ข้อมูลไม่ครบถ้วนหรือไม่ถูกต้อง', 422, $validator->getErrors());
|
|
}
|
|
|
|
$input['branch_id'] = $input['branch_id'] ?? $user['branch'];
|
|
$input['created_by'] = $user['id'];
|
|
$input['booking_type'] = 'Walk_in';
|
|
|
|
// Execute Smart Queue AI Engine
|
|
$res = $this->aiEngine->allocateQueue($input);
|
|
|
|
Response::success('ออกคิวนวดสำเร็จ พร้อมประเมินเวลาโดย Smart AI', $res, 201);
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/queues/smart-assign - Manual Trigger AI Allocation for a waiting queue
|
|
*/
|
|
public function smartAssign(): void
|
|
{
|
|
JwtAuthMiddleware::handle();
|
|
$input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
|
|
|
|
if (empty($input['queue_id'])) {
|
|
Response::error('กรุณาระบุหมายเลขคิวที่ต้องการจัดสรร', 400);
|
|
}
|
|
|
|
$db = \App\Models\Model::getDB();
|
|
$stmt = $db->prepare("CALL sp_assign_smart_queue(:qid, @t_id, @r_id, @e_start, @e_end, @msg)");
|
|
$stmt->execute(['qid' => (int)$input['queue_id']]);
|
|
$stmt->closeCursor();
|
|
|
|
$res = $db->query("SELECT @t_id AS t_id, @r_id AS r_id, @e_start AS e_start, @e_end AS e_end, @msg AS msg")->fetch();
|
|
|
|
Response::success('Smart AI Allocation Executed', $res);
|
|
}
|
|
|
|
/**
|
|
* PUT /api/v1/queues/{id}/status - Update Queue Status (Calling / In_Progress / Completed / Cancelled)
|
|
*/
|
|
public function updateStatus(int $queueId): void
|
|
{
|
|
$user = JwtAuthMiddleware::handle();
|
|
$input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
|
|
|
|
$newStatus = $input['status'] ?? null;
|
|
$allowed = ['Waiting', 'Assigned', 'In_Progress', 'Completed', 'Cancelled', 'No_Show'];
|
|
if (!in_array($newStatus, $allowed, true)) {
|
|
Response::error('สถานะคิวไม่ถูกต้อง', 400);
|
|
}
|
|
|
|
$existing = $this->queueModel->find($queueId);
|
|
if (!$existing) {
|
|
Response::error('ไม่พบข้อมูลคิวนี้', 404);
|
|
}
|
|
|
|
$updateData = [
|
|
'status' => $newStatus,
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
if ($newStatus === 'In_Progress') {
|
|
$updateData['actual_start_time'] = date('Y-m-d H:i:s');
|
|
// แจ้งเรียกคิวออกทีวีและ LINE Notify
|
|
if (!empty($existing['room_id'])) {
|
|
$room = (new \App\Models\Room())->find((int)$existing['room_id']);
|
|
(new NotificationService())->notifyQueueCalled($existing['queue_no'], $room['room_no'] ?? '-', 'ผู้รับบริการ');
|
|
}
|
|
} elseif ($newStatus === 'Completed') {
|
|
$updateData['actual_end_time'] = date('Y-m-d H:i:s');
|
|
if (!empty($existing['actual_start_time'])) {
|
|
$mins = round((time() - strtotime($existing['actual_start_time'])) / 60);
|
|
$updateData['service_time_mins'] = $mins > 0 ? $mins : 60;
|
|
}
|
|
}
|
|
|
|
$this->queueModel->update($queueId, $updateData);
|
|
AuditLogger::logQueueUpdate($queueId, $existing['queue_no'], $existing['status'], $newStatus);
|
|
|
|
// คำนวณ Workload ใหม่ให้หมอนวดหากคิวจบหรือยกเลิก
|
|
if (!empty($existing['therapist_id'])) {
|
|
(new \App\Services\WorkloadBalancer())->updateScore((int)$existing['therapist_id']);
|
|
}
|
|
|
|
Response::success("อัปเดตสถานะคิวเป็น {$newStatus} เรียบร้อยแล้ว", ['queue_id' => $queueId, 'status' => $newStatus]);
|
|
}
|
|
}
|