格式化代码,websocket功能完善

This commit is contained in:
2026-02-18 21:50:05 +08:00
parent 6543e2ccdd
commit b6c133952b
101 changed files with 15829 additions and 10739 deletions

View File

@@ -6,91 +6,90 @@ use Illuminate\Support\Facades\Log;
use Swoole\WebSocket\Server;
/**
* WebSocket Service
* WebSocket 服务
*
* Provides helper functions for WebSocket operations
* 提供 WebSocket 操作的便捷方法
*/
class WebSocketService
{
/**
* Get Swoole WebSocket Server instance
* 获取 Swoole Server 实例
*
* @return Server|null
* @return Server
*/
public function getServer(): ?Server
protected function getServer(): Server
{
// Check if Laravel-S is running
if (!class_exists('Hhxsv5\LaravelS\Illuminate\Laravel') || !defined('IN_LARAVELS')) {
return null;
}
try {
// Try to get the Swoole server from the Laravel-S container
$laravelS = \Hhxsv5\LaravelS\Illuminate\Laravel::getInstance();
if ($laravelS && $laravelS->getSwooleServer()) {
return $laravelS->getSwooleServer();
}
} catch (\Exception $e) {
Log::warning('Failed to get Swoole server instance', [
'error' => $e->getMessage()
]);
}
return null;
/** @var Server $server */
$server = app('swoole');
return $server;
}
/**
* Send message to a specific user
* 获取 WebSocket
*
* @param int $userId
* @param array $data
* @return \Swoole\Table
*/
protected function getWsTable(): \Swoole\Table
{
return app('swoole')->wsTable;
}
/**
* 发送消息给指定用户
*
* @param int $userId 用户 ID
* @param array $data 消息数据
* @return bool
*/
public function sendToUser(int $userId, array $data): bool
{
$server = $this->getServer();
try {
$wsTable = $this->getWsTable();
$server = $this->getServer();
if (!$server) {
Log::warning('WebSocket server not available', ['user_id' => $userId]);
// 获取用户的 fd
$fdInfo = $wsTable->get('uid:' . $userId);
if ($fdInfo === false) {
return false;
}
$fd = (int)$fdInfo['value'];
// 检查连接是否仍然建立
if (!$server->isEstablished($fd)) {
// 删除过期连接
$wsTable->del('uid:' . $userId);
$wsTable->del('fd:' . $fd);
return false;
}
// 发送消息
$result = $server->push($fd, json_encode($data));
Log::info('消息已发送给用户', [
'user_id' => $userId,
'fd' => $fd,
'success' => $result
]);
return $result;
} catch (\Exception $e) {
Log::error('发送消息给用户失败', [
'user_id' => $userId,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return false;
}
$wsTable = app('swoole')->wsTable;
$fdInfo = $wsTable->get('uid:' . $userId);
if (!$fdInfo || !$fdInfo['value']) {
Log::info('User not connected to WebSocket', ['user_id' => $userId]);
return false;
}
$fd = (int)$fdInfo['value'];
if (!$server->isEstablished($fd)) {
Log::info('WebSocket connection not established', ['user_id' => $userId, 'fd' => $fd]);
// Clean up stale connection
$wsTable->del('uid:' . $userId);
$wsTable->del('fd:' . $fd);
return false;
}
$server->push($fd, json_encode($data));
Log::info('Message sent to user via WebSocket', [
'user_id' => $userId,
'fd' => $fd,
'data' => $data
]);
return true;
}
/**
* Send message to multiple users
* 发送消息给多个用户
*
* @param array $userIds
* @param array $data
* @return array Array of user IDs who received the message
* @param array $userIds 用户 ID 数组
* @param array $data 消息数据
* @return array 成功发送的用户 ID 数组
*/
public function sendToUsers(array $userIds, array $data): array
{
@@ -106,247 +105,263 @@ class WebSocketService
}
/**
* Broadcast message to all connected clients
* 广播消息给所有用户
*
* @param array $data
* @param int|null $excludeUserId User ID to exclude from broadcast
* @return int Number of clients the message was sent to
* @param array $data 消息数据
* @param int|null $excludeUserId 要排除的用户 ID
* @return int 成功发送的用户数量
*/
public function broadcast(array $data, ?int $excludeUserId = null): int
{
$server = $this->getServer();
try {
$wsTable = $this->getWsTable();
$server = $this->getServer();
if (!$server) {
Log::warning('WebSocket server not available for broadcast');
return 0;
}
$message = json_encode($data);
$count = 0;
$wsTable = app('swoole')->wsTable;
$message = json_encode($data);
$count = 0;
foreach ($server->connections as $fd) {
if (!$server->isEstablished($fd)) {
continue;
}
// Check if we should exclude this user
if ($excludeUserId) {
$fdInfo = $wsTable->get('fd:' . $fd);
if ($fdInfo && $fdInfo['value'] == $excludeUserId) {
foreach ($wsTable as $key => $row) {
// 只处理用户映射uid:*
if (strpos($key, 'uid:') !== 0) {
continue;
}
$userId = (int)substr($key, 4); // 移除 'uid:' 前缀
$fd = (int)$row['value'];
// 跳过排除的用户
if ($excludeUserId && $userId == $excludeUserId) {
continue;
}
// 检查连接是否已建立并发送
if ($server->isEstablished($fd)) {
if ($server->push($fd, $message)) {
$count++;
}
} else {
// 删除过期连接
$wsTable->del('uid:' . $userId);
$wsTable->del('fd:' . $fd);
}
}
$server->push($fd, $message);
$count++;
Log::info('广播消息已发送', [
'exclude_user_id' => $excludeUserId,
'sent_to' => $count
]);
return $count;
} catch (\Exception $e) {
Log::error('广播消息失败', [
'exclude_user_id' => $excludeUserId,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return 0;
}
Log::info('Broadcast sent via WebSocket', [
'data' => $data,
'exclude_user_id' => $excludeUserId,
'count' => $count
]);
return $count;
}
/**
* Send message to all subscribers of a channel
* 发送消息到频道
*
* @param string $channel
* @param array $data
* @return int Number of subscribers who received the message
* @param string $channel 频道名称
* @param array $data 消息数据
* @return int 成功发送的订阅者数量
*/
public function sendToChannel(string $channel, array $data): int
{
$server = $this->getServer();
try {
$wsTable = $this->getWsTable();
$server = $this->getServer();
if (!$server) {
Log::warning('WebSocket server not available for channel broadcast', ['channel' => $channel]);
$message = json_encode($data);
$count = 0;
$channelPrefix = 'channel:' . $channel . ':fd:';
foreach ($wsTable as $key => $row) {
// 只处理该频道的订阅
if (strpos($key, $channelPrefix) !== 0) {
continue;
}
$fd = (int)substr($key, strlen($channelPrefix));
// 检查连接是否已建立并发送
if ($server->isEstablished($fd)) {
if ($server->push($fd, $message)) {
$count++;
}
} else {
// 删除过期订阅
$wsTable->del($key);
}
}
Log::info('消息已发送到频道', [
'channel' => $channel,
'sent_to' => $count
]);
return $count;
} catch (\Exception $e) {
Log::error('发送消息到频道失败', [
'channel' => $channel,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return 0;
}
$wsTable = app('swoole')->wsTable;
$count = 0;
$message = json_encode($data);
// Iterate through all connections and check if they're subscribed to the channel
foreach ($server->connections as $fd) {
if (!$server->isEstablished($fd)) {
continue;
}
$subscription = $wsTable->get('channel:' . $channel . ':fd:' . $fd);
if ($subscription) {
$server->push($fd, $message);
$count++;
}
}
Log::info('Channel message sent via WebSocket', [
'channel' => $channel,
'data' => $data,
'count' => $count
]);
return $count;
}
/**
* Get online user count
* 获取在线用户数量
*
* @return int
*/
public function getOnlineUserCount(): int
{
$server = $this->getServer();
try {
$wsTable = $this->getWsTable();
$count = 0;
if (!$server || !isset($server->wsTable)) {
foreach ($wsTable as $key => $row) {
if (strpos($key, 'uid:') === 0) {
$count++;
}
}
return $count;
} catch (\Exception $e) {
Log::error('获取在线用户数量失败', [
'error' => $e->getMessage()
]);
return 0;
}
// Count established connections
$count = 0;
foreach ($server->connections as $fd) {
if ($server->isEstablished($fd)) {
$count++;
}
}
return $count;
}
/**
* Check if a user is online
* 检查用户是否在线
*
* @param int $userId
* @param int $userId 用户 ID
* @return bool
*/
public function isUserOnline(int $userId): bool
{
$server = $this->getServer();
try {
$wsTable = $this->getWsTable();
$fdInfo = $wsTable->get('uid:' . $userId);
if (!$server) {
return false;
}
if ($fdInfo === false) {
return false;
}
$wsTable = app('swoole')->wsTable;
$server = $this->getServer();
$fd = (int)$fdInfo['value'];
$fdInfo = $wsTable->get('uid:' . $userId);
if (!$fdInfo || !$fdInfo['value']) {
return false;
}
$fd = (int)$fdInfo['value'];
return $server->isEstablished($fd);
}
/**
* Disconnect a user from WebSocket
*
* @param int $userId
* @return bool
*/
public function disconnectUser(int $userId): bool
{
$server = $this->getServer();
if (!$server) {
return false;
}
$wsTable = app('swoole')->wsTable;
$fdInfo = $wsTable->get('uid:' . $userId);
if (!$fdInfo || !$fdInfo['value']) {
return false;
}
$fd = (int)$fdInfo['value'];
if ($server->isEstablished($fd)) {
$server->push($fd, json_encode([
'type' => 'disconnect',
'data' => [
'message' => 'You have been disconnected',
'timestamp' => time()
]
]));
// Close the connection
$server->disconnect($fd);
// Clean up
$wsTable->del('uid:' . $userId);
$wsTable->del('fd:' . $fd);
Log::info('User disconnected from WebSocket by server', [
return $server->isEstablished($fd);
} catch (\Exception $e) {
Log::error('检查用户在线状态失败', [
'user_id' => $userId,
'fd' => $fd
'error' => $e->getMessage()
]);
return true;
return false;
}
return false;
}
/**
* Get all online user IDs
* 获取在线用户 ID 列表
*
* @return array
*/
public function getOnlineUserIds(): array
{
$server = $this->getServer();
try {
$wsTable = $this->getWsTable();
$userIds = [];
if (!$server) {
foreach ($wsTable as $key => $row) {
if (strpos($key, 'uid:') === 0) {
$userId = (int)substr($key, 4); // 移除 'uid:' 前缀
$userIds[] = $userId;
}
}
return $userIds;
} catch (\Exception $e) {
Log::error('获取在线用户 ID 列表失败', [
'error' => $e->getMessage()
]);
return [];
}
$wsTable = app('swoole')->wsTable;
$userIds = [];
foreach ($server->connections as $fd) {
if (!$server->isEstablished($fd)) {
continue;
}
$fdInfo = $wsTable->get('fd:' . $fd);
if ($fdInfo && $fdInfo['value']) {
$userIds[] = (int)$fdInfo['value'];
}
}
return array_unique($userIds);
}
/**
* Send system notification to all online users
* 断开用户 WebSocket 连接
*
* @param string $title
* @param string $message
* @param string $type
* @param array $extraData
* @return int
* @param int $userId 用户 ID
* @return bool
*/
public function sendSystemNotification(string $title, string $message, string $type = 'info', array $extraData = []): int
public function disconnectUser(int $userId): bool
{
try {
$wsTable = $this->getWsTable();
$server = $this->getServer();
// 获取用户的 fd
$fdInfo = $wsTable->get('uid:' . $userId);
if ($fdInfo === false) {
return false;
}
$fd = (int)$fdInfo['value'];
// 断开连接
$server->disconnect($fd);
// 删除映射
$wsTable->del('uid:' . $userId);
$wsTable->del('fd:' . $fd);
Log::info('用户已断开连接', [
'user_id' => $userId,
'fd' => $fd
]);
return true;
} catch (\Exception $e) {
Log::error('断开用户连接失败', [
'user_id' => $userId,
'error' => $e->getMessage()
]);
return false;
}
}
/**
* 发送系统通知
*
* @param string $title 标题
* @param string $message 消息内容
* @param string $type 类型
* @param array $extraData 额外数据
* @return int 成功发送的用户数量
*/
public function sendSystemNotification(
string $title,
string $message,
string $type = 'info',
array $extraData = []
): int {
$data = [
'type' => 'notification',
'data' => [
'title' => $title,
'message' => $message,
'type' => $type, // info, success, warning, error
'timestamp' => time(),
...$extraData
'type' => $type,
'data' => $extraData,
'timestamp' => time()
]
];
@@ -354,47 +369,57 @@ class WebSocketService
}
/**
* Send notification to specific users
* 发送通知给指定用户
*
* @param array $userIds
* @param string $title
* @param string $message
* @param string $type
* @param array $extraData
* @return array
* @param array $userIds 用户 ID 数组
* @param string $title 标题
* @param string $message 消息内容
* @param string $type 类型
* @param array $extraData 额外数据
* @return int 成功发送的用户数量
*/
public function sendNotificationToUsers(array $userIds, string $title, string $message, string $type = 'info', array $extraData = []): array
{
public function sendNotificationToUsers(
array $userIds,
string $title,
string $message,
string $type = 'info',
array $extraData = []
): int {
$data = [
'type' => 'notification',
'data' => [
'title' => $title,
'message' => $message,
'type' => $type,
'timestamp' => time(),
...$extraData
'data' => $extraData,
'timestamp' => time()
]
];
return $this->sendToUsers($userIds, $data);
$sentTo = $this->sendToUsers($userIds, $data);
return count($sentTo);
}
/**
* Push data update to specific users
* 推送数据更新
*
* @param array $userIds
* @param string $resourceType
* @param string $action
* @param array $data
* @return array
* @param array $userIds 用户 ID 数组
* @param string $resourceType 资源类型
* @param string $action 操作
* @param array $data 数据
* @return array 成功推送的用户 ID 数组
*/
public function pushDataUpdate(array $userIds, string $resourceType, string $action, array $data): array
{
public function pushDataUpdate(
array $userIds,
string $resourceType,
string $action,
array $data
): array {
$message = [
'type' => 'data_update',
'data' => [
'resource_type' => $resourceType, // e.g., 'user', 'order', 'product'
'action' => $action, // create, update, delete
'resource_type' => $resourceType,
'action' => $action,
'data' => $data,
'timestamp' => time()
]
@@ -404,16 +429,20 @@ class WebSocketService
}
/**
* Push data update to a channel
* 推送数据更新到频道
*
* @param string $channel
* @param string $resourceType
* @param string $action
* @param array $data
* @return int
* @param string $channel 频道名称
* @param string $resourceType 资源类型
* @param string $action 操作
* @param array $data 数据
* @return int 成功推送的订阅者数量
*/
public function pushDataUpdateToChannel(string $channel, string $resourceType, string $action, array $data): int
{
public function pushDataUpdateToChannel(
string $channel,
string $resourceType,
string $action,
array $data
): int {
$message = [
'type' => 'data_update',
'data' => [