要实现一个基于 ThinkPHP 6.1 的排队叫号系统,我们可以按照以下步骤进行开发。系统将使用 Redis 作为队列存储,WebSocket 实现实时推送,并提供 API 接口供用户和管理员操作。
1. 环境准备
首先,确保你已经安装了以下组件:
PHP 7.4+
ThinkPHP 6.1
Redis
MySQL(可选,用于持久化历史记录)
WebSocket 服务器(如 Swoole 或 Workerman)
2. 安装依赖
在 ThinkPHP 6.1 项目中,安装 Redis 和 WebSocket 相关的扩展包。
composer require topthink/think-redis
composer require workerman/workerman
3. 配置 Redis
在 config/cache.php 中配置 Redis 连接:
return [
'default' => 'redis',
'stores' => [
'redis' => [
'type' => 'redis',
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'select' => 0,
'timeout' => 0,
'persistent' => false,
],
],
];
4. 创建队列管理类
创建一个队列管理类 app\service\QueueService.php,用于管理 Redis 队列。
namespace app\service;
use think\facade\Cache;
class QueueService
{
protected $queueKey = 'queue:numbers';
protected $webSocketService;
public function __construct()
{
$this->webSocketService = new WebSocketService();
}
// 取号
public function getNumber()
{
$number = Cache::incr('queue:counter');
Cache::rPush($this->queueKey, $number);
return $number;
}
// 叫号
public function callNumber()
{
$number = Cache::lPop($this->queueKey);
if ($number) {
// 推送叫号信息到 WebSocket
$this->webSocketService->pushToAll(['type' => 'call', 'number' => $number]);
}
return $number;
}
// 获取当前排队状态
public function getQueueStatus()
{
return Cache::lRange($this->queueKey, 0, -1);
}
}
5. 创建 WebSocket 服务类
在 app\service 目录下创建一个 WebSocketService.php 文件,用于封装 WebSocket 服务器的逻辑。
namespace app\service;
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
class WebSocketService
{
protected $worker;
public function __construct($address = 'websocket://0.0.0.0:2346')
{
$this->worker = new Worker($address);
$this->worker->onConnect = function(TcpConnection $connection) {
echo "New connection\n";
};
$this->worker->onMessage = function(TcpConnection $connection, $data) {
// 处理客户端发送的消息
$connection->send(json_encode(['type' => 'message', 'content' => 'Hello']));
};
$this->worker->onClose = function(TcpConnection $connection) {
echo "Connection closed\n";
};
}
// 启动 WebSocket 服务器
public function start()
{
Worker::runAll();
}
// 推送消息到所有客户端
public function pushToAll($message)
{
foreach ($this->worker->connections as $connection) {
$connection->send(json_encode($message));
}
}
}
6. 创建 API 接口
在 app\controller\QueueController.php 中创建 API 接口。
namespace app\controller;
use app\service\QueueService;
use think\facade\Request;
class QueueController
{
protected $queueService;
public function __construct()
{
$this->queueService = new QueueService();
}
// 取号
public function getNumber()
{
$number = $this->queueService->getNumber();
return json(['code' => 200, 'message' => '取号成功', 'number' => $number]);
}
// 叫号
public function callNumber()
{
$number = $this->queueService->callNumber();
return json(['code' => 200, 'message' => '叫号成功', 'number' => $number]);
}
// 查看当前排队状态
public function getQueueStatus()
{
$status = $this->queueService->getQueueStatus();
return json(['code' => 200, 'message' => '获取成功', 'status' => $status]);
}
}
7. 路由配置
在 route/route.php 中配置路由。
use think\facade\Route;
Route::get('queue/getNumber', 'QueueController/getNumber');
Route::get('queue/callNumber', 'QueueController/callNumber');
Route::get('queue/getQueueStatus', 'QueueController/getQueueStatus');
8. 前端实现
在前端页面中,使用 WebSocket 连接服务器,并实时接收叫号信息。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>排队叫号系统</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
padding: 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
}
.card {
margin-top: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.card-header {
background-color: #007bff;
color: white;
font-size: 1.5rem;
font-weight: bold;
}
.btn-primary {
background-color: #007bff;
border-color: #007bff;
}
.btn-primary:hover {
background-color: #0056b3;
border-color: #0056b3;
}
.btn-success {
background-color: #28a745;
border-color: #28a745;
}
.btn-success:hover {
background-color: #218838;
border-color: #218838;
}
.status-box {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
margin-top: 20px;
}
.status-box h3 {
margin-bottom: 15px;
font-size: 1.25rem;
color: #333;
}
.status-box ul {
list-style-type: none;
padding: 0;
}
.status-box ul li {
padding: 10px;
border-bottom: 1px solid #eee;
font-size: 1rem;
color: #555;
}
.status-box ul li:last-child {
border-bottom: none;
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<div class="card-header text-center">
排队叫号系统
</div>
<div class="card-body">
<div class="d-grid gap-2">
<button class="btn btn-primary btn-lg" onclick="getNumber()">
<i class="fas fa-ticket-alt"></i> 取号
</button>
<button class="btn btn-success btn-lg" onclick="callNumber()">
<i class="fas fa-bullhorn"></i> 叫号
</button>
</div>
<div class="status-box">
<h3>当前排队状态</h3>
<ul id="status"></ul>
</div>
</div>
</div>
</div>
<script>
const ws = new WebSocket('ws://127.0.0.1:2346');
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data.type === 'call') {
alert(`叫号:${data.number}`);
}
};
function getNumber() {
fetch('api/queue/getNumber')
.then(response => response.json())
.then(data => {
alert(`取号成功:${data.number}`);
});
}
function callNumber() {
fetch('api/queue/callNumber')
.then(response => response.json())
.then(data => {
alert(`叫号成功:${data.number}`);
});
}
function getQueueStatus() {
fetch('api/queue/getQueueStatus')
.then(response => response.json())
.then(data => {
const statusList = document.getElementById('status');
statusList.innerHTML = data.status.map(number => `<li>号码:${number}</li>`).join('');
});
}
// 定时获取排队状态
setInterval(getQueueStatus, 5000);
</script>
<!-- Bootstrap JS and dependencies -->
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"></script>
</body>
</html>
9. 运行 WebSocket 服务器
在命令行中运行 WebSocket 服务器:
php websocket.php start
10. 运行 ThinkPHP 项目
启动 ThinkPHP 项目: php websocket.php start