Files
gravity/บริหารจัดการคิวนวดแผนไทย/app/Controllers/QueueController.php
T
2026-09-16 23:20:08 +07:00

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]);
}
}