feat: 新增WebSocket 服务
This commit is contained in:
207
src/app/api/controller/WebSocketBase.php
Normal file
207
src/app/api/controller/WebSocketBase.php
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\controller;
|
||||
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
|
||||
/**
|
||||
* WebSocket基类,用于各个addon继承实现自己的WebSocket控制器
|
||||
* 提供统一的接口和基本功能,确保各个addon的WebSocket相互隔离
|
||||
*/
|
||||
abstract class WebSocketBase implements MessageComponentInterface
|
||||
{
|
||||
protected $clients;
|
||||
protected $clientData;
|
||||
protected $addonName;
|
||||
|
||||
// 控制器属性
|
||||
public $params;
|
||||
public $token;
|
||||
protected $member_id;
|
||||
protected $site_id;
|
||||
protected $uniacid;
|
||||
protected $site_ids = [];
|
||||
public $app_type;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct($addonName = '')
|
||||
{
|
||||
$this->clients = new \SplObjectStorage;
|
||||
$this->clientData = [];
|
||||
$this->addonName = $addonName;
|
||||
|
||||
// 初始化控制器属性
|
||||
$this->params = [];
|
||||
$this->token = '';
|
||||
$this->member_id = 0;
|
||||
$this->site_id = 0;
|
||||
$this->uniacid = 0;
|
||||
$this->app_type = 'weapp'; // 默认微信小程序
|
||||
}
|
||||
|
||||
/**
|
||||
* 当有新客户端连接时调用
|
||||
* @param ConnectionInterface $conn
|
||||
*/
|
||||
public function onOpen(ConnectionInterface $conn)
|
||||
{
|
||||
// 存储新连接的客户端
|
||||
$this->clients->attach($conn);
|
||||
$this->clientData[$conn->resourceId] = [
|
||||
'connection' => $conn,
|
||||
'site_id' => null,
|
||||
'member_id' => null,
|
||||
'token' => null,
|
||||
'is_authenticated' => false,
|
||||
'conversation_id' => null,
|
||||
'addon_name' => $this->addonName, // 记录当前连接所属的addon
|
||||
];
|
||||
|
||||
echo "[{$this->addonName}] New connection! ({$conn->resourceId})\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 当从客户端收到消息时调用
|
||||
* @param ConnectionInterface $conn
|
||||
* @param string $message
|
||||
*/
|
||||
public function onMessage(ConnectionInterface $conn, $message)
|
||||
{
|
||||
$numRecv = count($this->clients) - 1;
|
||||
echo sprintf('[{$this->addonName}] Connection %d sending message "%s" to %d other connection%s' . "\n",
|
||||
$conn->resourceId, $message, $numRecv, $numRecv == 1 ? '' : 's');
|
||||
|
||||
// 解析消息
|
||||
try {
|
||||
$data = json_decode($message, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
throw new \Exception('Invalid JSON format');
|
||||
}
|
||||
|
||||
// 处理认证
|
||||
if (isset($data['action']) && $data['action'] === 'auth') {
|
||||
$this->handleAuth($conn, $data);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否已认证
|
||||
if (!$this->clientData[$conn->resourceId]['is_authenticated']) {
|
||||
$conn->send(json_encode(['type' => 'error', 'message' => 'Not authenticated']));
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理聊天消息
|
||||
if (isset($data['action']) && $data['action'] === 'chat') {
|
||||
$this->handleChat($conn, $data);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理心跳
|
||||
if (isset($data['action']) && $data['action'] === 'ping') {
|
||||
$conn->send(json_encode(['type' => 'pong']));
|
||||
return;
|
||||
}
|
||||
|
||||
$conn->send(json_encode(['type' => 'error', 'message' => 'Unknown action']));
|
||||
} catch (\Exception $e) {
|
||||
$conn->send(json_encode(['type' => 'error', 'message' => $e->getMessage()]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当客户端连接关闭时调用
|
||||
* @param ConnectionInterface $conn
|
||||
*/
|
||||
public function onClose(ConnectionInterface $conn)
|
||||
{
|
||||
// 移除连接
|
||||
$this->clients->detach($conn);
|
||||
unset($this->clientData[$conn->resourceId]);
|
||||
|
||||
echo "[{$this->addonName}] Connection {$conn->resourceId} has disconnected\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 当连接发生错误时调用
|
||||
* @param ConnectionInterface $conn
|
||||
* @param \Exception $e
|
||||
*/
|
||||
public function onError(ConnectionInterface $conn, \Exception $e)
|
||||
{
|
||||
echo "[{$this->addonName}] An error has occurred: {$e->getMessage()}\n";
|
||||
|
||||
$conn->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理客户端认证
|
||||
* @param ConnectionInterface $conn
|
||||
* @param array $data
|
||||
*/
|
||||
protected function handleAuth(ConnectionInterface $conn, $data)
|
||||
{
|
||||
try {
|
||||
$site_id = $data['site_id'] ?? null;
|
||||
$member_id = $data['member_id'] ?? null;
|
||||
$token = $data['token'] ?? null;
|
||||
|
||||
if (empty($site_id) || empty($member_id) || empty($token)) {
|
||||
throw new \Exception('Missing authentication parameters');
|
||||
}
|
||||
|
||||
// 子类可以重写此方法来实现更严格的认证逻辑
|
||||
$this->doAuth($conn, $site_id, $member_id, $token);
|
||||
|
||||
$this->clientData[$conn->resourceId]['site_id'] = $site_id;
|
||||
$this->clientData[$conn->resourceId]['member_id'] = $member_id;
|
||||
$this->clientData[$conn->resourceId]['token'] = $token;
|
||||
$this->clientData[$conn->resourceId]['is_authenticated'] = true;
|
||||
|
||||
$conn->send(json_encode(['type' => 'auth_success', 'message' => 'Authenticated successfully', 'addon' => $this->addonName]));
|
||||
} catch (\Exception $e) {
|
||||
$conn->send(json_encode(['type' => 'auth_error', 'message' => $e->getMessage()]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实际的认证逻辑,子类可以重写此方法
|
||||
* @param ConnectionInterface $conn
|
||||
* @param int $site_id
|
||||
* @param int $member_id
|
||||
* @param string $token
|
||||
*/
|
||||
protected function doAuth(ConnectionInterface $conn, $site_id, $member_id, $token)
|
||||
{
|
||||
// 默认实现,子类应该重写此方法
|
||||
// 这里可以添加更严格的认证逻辑,例如验证token的有效性
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理聊天消息
|
||||
* @param ConnectionInterface $conn
|
||||
* @param array $data
|
||||
*/
|
||||
abstract protected function handleChat(ConnectionInterface $conn, $data);
|
||||
|
||||
/**
|
||||
* 发送消息给指定客户端
|
||||
* @param ConnectionInterface $conn
|
||||
* @param array $message
|
||||
*/
|
||||
protected function sendMessage(ConnectionInterface $conn, $message)
|
||||
{
|
||||
$conn->send(json_encode(array_merge(['addon' => $this->addonName], $message)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前addon名称
|
||||
* @return string
|
||||
*/
|
||||
public function getAddonName()
|
||||
{
|
||||
return $this->addonName;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user