和蕙健康小程序后端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

325 lines
9.9 KiB

10 months ago
<?php
namespace addons\shopro\library\chat;
use addons\shopro\exception\ShoproException;
use addons\shopro\library\chat\traits\DebugEvent;
use addons\shopro\library\chat\traits\Helper;
use addons\shopro\library\chat\traits\Session;
use addons\shopro\library\chat\traits\NspData;
use addons\shopro\library\chat\traits\BindUId;
use PHPSocketIO\SocketIO;
use PHPSocketIO\Socket;
use PHPSocketIO\Nsp;
class Chat
{
/**
* session 存储助手
*/
use Session;
/**
* 绑定 UID 助手
*/
use BindUId;
/**
* 绑定数据到 nsp 作为全局数据
*/
use NspData;
/**
* 助手方法
*/
use Helper;
/**
* debug 方式注册事件
*/
use DebugEvent;
/**
* 当前 phpsocket.io 实例
*
* @var SocketIO
*/
public $io;
/**
* 当前连接实例
*
* @var Socket
*/
public $socket;
/**
* 当前 namespace 实例
*
* @var Nsp
*/
public $nsp;
/**
* 当前发送实例
*
* @var Sender
*/
public $sender;
/**
* 当前获取数据
*
* @var Getter
*/
public $getter;
/**
* chat 操作类
*
* @var ChatService
*/
public $chatService;
protected $auth = [
'user',
'admin',
];
/**
* 初始化 chat 系统
*
* @param SocketIo $io
* @param Socket $socket
* @param Nsp $nsp
*/
public function __construct(SocketIo $io, Nsp $nsp, Socket $socket = null)
{
$this->io = $io;
$this->socket = $socket;
$this->nsp = $nsp;
// 初始化获取更改数据实例
$this->getter = new Getter($socket, $io, $nsp);
// 初始化发送实例
$this->sender = new Sender($socket, $io, $nsp, $this->getter);
// 初始化 客服公共方法实例
$this->chatService = new ChatService($socket, $io, $nsp, $this->getter);
}
public function on()
{
// on 方法只有在连接的时候走一次
$this->register('test', function ($data, $callback) {
// $class = "\\app\\chat\\library\\provider\\auth\\User";
// $provider = new $class($this);
// $this->socket->removeAllListeners('message');
// 注册相关身份事件
// $provider->customerEvent();
$customer_service_room = $this->getRoomName('customer_service_room', ['room_id' => 'admin']);
$customerServices = $this->getter('socket')->getSessionsByRoom($customer_service_room, 'customer_service');
$this->sender->successSocket($callback, '连接成功', [
'msg' => '恭喜鏈接成功',
'bind' => $this->nsp->bind ?? [],
'nsp_room_ids' => $this->nspData('room_ids'),
'customer_service' => $customerServices,
'nsp_data' => $this->nsp->nspData ?? [],
'rooms' => isset($this->nsp->adapter->rooms) ? $this->nsp->adapter->rooms : [],
'current_rooms' => $this->socket->rooms,
'session' => $this->session(),
'client_id' => $this->socket->id,
'session_ids' => $this->nspData('session_ids')
]);
$this->sender->successUId('new message', '消息桶送', ['aaa' => 'bbb'], [
'id' => $this->session('session_id'),
'type' => $this->session('auth'),
]);
// foreach ($clientIds as $client_id) {
// $this->sender->successSocket('new message', ['aaa' => 'bbb']);
// }
$this->socket->on('test-child', function ($data, $callback) {
echo "子集消息来了";
$this->session('text:child', 'aaa');
$this->sender->successSocket($callback, '连接成功', [
'msg' => '子事件夜之星成功了'
]);
});
});
// socket 连接初始化,socket.io-client 连接后的第一步
$this->register('connection', function ($data, $callback) {
// 初始化连接
$auth = $data['auth'] ?? '';
if (!in_array($auth, $this->auth)) {
throw new ShoproException('身份错误');
}
// 存储当前 auth 驱动
$this->session('auth', $auth);
// 加入对应身份组
$this->socket->join($this->getRoomName('auth', ['auth' => $auth]));
// 加入在线连接组
$this->socket->join('online');
// 检测并自动登录
$result = $this->chatService->authLogin($data);
// 注册各自身份的事件
$this->authEvent($auth);
// 连接成功,发送给自己
$this->sender->authSuccess($callback);
});
// auth 身份登录,管理员或者用户
$this->register('login', function ($data, $callback) {
// 登录,和系统中的用户或者管理员绑定
$result = $this->chatService->authLogin($data);
if ($result) {
// 登录成功
$this->sender->authSuccess($callback);
return true;
}
// 登录失败
throw new ShoproException('登录失败');
});
/**
* 断开连接
*/
$this->register('disconnect', function ($data, $callback) {
$customer_service_id = $this->session('customer_service_id');
$session_id = $this->session('session_id');
$identify = $this->session('identify') ?: '';
// $auth = $this->session('auth');
// $authUser = $this->session('auth_user');
// 断开连接,解绑 bind 的身份
$this->chatService->disconnectUnbindAll();
// 如果登录了,并且所有客户端都下线了, 删除相关 auth 的 ids
// if ($this->chatService->isLogin() && !$this->getter('socket')->isOnlineAuthBySessionId($session_id, $auth)) {
// $this->nspSessionIdDel($session_id, $auth);
// }
// 如果是顾客
if ($identify == 'customer') {
// 顾客所在房间
$room_id = $this->session('room_id');
// 顾客断开连接
if (!$this->getter('socket')->isOnLineCustomerById($session_id)) {
// 当前所遇用户端都断开了
$waiting_room_name = $this->getRoomName('customer_service_room_waiting', ['room_id' => $room_id]);
$rooms = $this->socket->rooms;
// 判断是否在排队中
if (in_array($waiting_room_name, $rooms)) {
// 这里顾客的所有客户端都断开了,在排队排名中移除
$this->nspWaitingDel($room_id, $session_id);
// 排队发生变化,通知房间中所有排队用户
$this->sender->allWaitingQueue($room_id);
// 离开排队中房间(将离线的用户从等待中移除)
$this->socket->leave($waiting_room_name);
// 通知更新排队中列表,把当前下线用户移除
$this->sender->waiting();
}
// 通知所有客服,顾客下线
$this->sender->customerOffline();
}
}
// 如果是客服
if ($identify == 'customer_service') {
// 客服断开连接
if (!$this->getter('socket')->isOnLineCustomerServiceById($customer_service_id)) {
// 当前客服的所有客户端都下线了
// 更新客服状态为离线
$this->getter()->updateCustomerServiceStatus($customer_service_id, 'offline');
// 通知连接的用户(在当前客服服务的房间里面的用户),客服下线了
$this->sender->customerServiceOffline();
// 通知当前房间的在线客服,更新当前在线客服列表
$this->sender->customerServiceUpdate();
}
}
});
}
/**
* 注册相关身份事件
*
* @param string $auth
* @return void
*/
private function authEvent($auth)
{
// 实例化相关身份事件
$class = "\\addons\\shopro\\library\\chat\\provider\\auth\\" . ucfirst($auth);
$provider = new $class($this);
// 注册相关身份事件
$provider->on();
}
/**
* 站内信通知
*
* @param object $http_connection
* @param string $uri
* @param array $data
* @return void
*/
public function innerWorker($httpConnection, $uri, $data)
{
if ($uri == '/notification') {
$this->exec($httpConnection, function () use ($data) {
$receiver = $data['receiver'] ?? [];
$sendData = $data['data'] ?? [];
$receiver_type = $receiver['type'] ?? 'user';
$receiverIds = $receiver['ids'] ?? '';
$receiverIds = is_array($receiverIds) ? $receiverIds : explode(',', $receiverIds);
// 循环给接收者发送消息
foreach ($receiverIds as $id) {
// 获取接收人的 session_id
$session_id = $this->getter()->getSessionIdByAuth($id, $receiver_type);
if ($session_id) {
$this->sender->successUId('notification', '收到通知', $sendData, [
'id' => $session_id, 'type' => $receiver_type
]);
}
}
});
}
// 这句话必须有,否则会提示超时
$httpConnection->send('ok');
}
}