init
- 框架初始化 - 安装插件 - 修复PHP8.4报错
This commit is contained in:
117
addons/shopro/library/chat/traits/BindUId.php
Normal file
117
addons/shopro/library/chat/traits/BindUId.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library\chat\traits;
|
||||
|
||||
/**
|
||||
* 绑定 uid
|
||||
*/
|
||||
trait BindUId
|
||||
{
|
||||
|
||||
/**
|
||||
* 绑定类型
|
||||
*/
|
||||
protected $bindType = [
|
||||
'user', // 用户
|
||||
'admin', // 管理员
|
||||
'customer', // 顾客 (用户或者管理员的身份提升)
|
||||
'customer_service' // 客服 (用户或者管理员的身份提升)
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* 根据类型获取 uid
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $type 目前有三种, user,admin,customer_service
|
||||
* @return string|array
|
||||
*/
|
||||
public function getUId($id, $type)
|
||||
{
|
||||
$ids = is_array($id) ? $id : [$id];
|
||||
foreach ($ids as &$i) {
|
||||
$i = $type . '-' . $i;
|
||||
}
|
||||
|
||||
return is_array($id) ? $ids : $ids[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 绑定 uid
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
public function bindUId($id, $type)
|
||||
{
|
||||
$uId = $this->getUId($id, $type);
|
||||
|
||||
// 当前客户端标示
|
||||
$client_id = $this->socket->id;
|
||||
|
||||
$this->nsp->bind[$uId][$client_id] = $client_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过 uid 获取当前 uid 绑定的所有clientid
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $type
|
||||
* @return array
|
||||
*/
|
||||
public function getClientIdByUId($id, $type) {
|
||||
$uId = $this->getUId($id, $type);
|
||||
|
||||
return $this->nsp->bind[$uId] ?? [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解绑 uid,将当前客户端与当前 uid 解绑,如果 uid 下没有客户端了,则将该 uid 删除
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
public function unbindUId($id, $type)
|
||||
{
|
||||
$uId = $this->getUId($id, $type);
|
||||
|
||||
// 当前客户端标示
|
||||
$client_id = $this->socket->id;
|
||||
|
||||
if (isset($this->nsp->bind[$uId][$client_id])) {
|
||||
unset($this->nsp->bind[$uId][$client_id]);
|
||||
|
||||
if (!$this->nsp->bind[$uId]) {
|
||||
unset($this->nsp->bind[$uId]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* UId 是否在线
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $type
|
||||
* @return boolean
|
||||
*/
|
||||
public function isUIdOnline($id, $type)
|
||||
{
|
||||
$uId = $this->getUId($id, $type);
|
||||
|
||||
if (isset($this->nsp->bind[$uId]) && $this->nsp->bind[$uId]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
75
addons/shopro/library/chat/traits/DebugEvent.php
Normal file
75
addons/shopro/library/chat/traits/DebugEvent.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library\chat\traits;
|
||||
|
||||
use addons\shopro\exception\ShoproException;
|
||||
|
||||
/**
|
||||
* debug 方式注册事件
|
||||
*/
|
||||
trait DebugEvent
|
||||
{
|
||||
|
||||
/**
|
||||
* 注册事件
|
||||
*/
|
||||
public function register($event, \Closure $cb)
|
||||
{
|
||||
$this->socket->on($event, function ($data, $callback = null) use ($cb) {
|
||||
try {
|
||||
$cb($data, $callback);
|
||||
} catch (\Exception $e) {
|
||||
$this->errorHandler($e, 'register');
|
||||
|
||||
// 将错误报告给前端
|
||||
$this->sender->errorSocket('custom_error', ($this->io->debug || $e instanceof ShoproException) ? $e->getMessage() : 'socket 服务器异常');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 执行代码
|
||||
*
|
||||
* @param object $httpConnection 当前 socket 连接
|
||||
* @param \Closure $callback
|
||||
* @return void
|
||||
*/
|
||||
public function exec($httpConnection, \Closure $callback)
|
||||
{
|
||||
try {
|
||||
$callback();
|
||||
} catch (\Exception $e) {
|
||||
$this->errorHandler($e, 'exec');
|
||||
|
||||
// 将错误报告给前端
|
||||
$httpConnection->send(($this->io->debug || $e instanceof ShoproException) ? $e->getMessage() : 'socket 服务器异常');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 判断处理异常
|
||||
*
|
||||
* @param \Exception $e
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
private function errorHandler(\Exception $e, $type)
|
||||
{
|
||||
$error = [
|
||||
'line' => $e->getLine(),
|
||||
'file' => $e->getFile(),
|
||||
'error' => $e->getMessage()
|
||||
// 'trace' => $e->getTrace(),
|
||||
];
|
||||
|
||||
if ($this->io->debug) {
|
||||
echo 'websocket:' . $type . ':执行失败,错误信息:' . json_encode($error, JSON_UNESCAPED_UNICODE);
|
||||
} else {
|
||||
format_log_error($e, 'WebSocket', 'WebSocket 执行失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
94
addons/shopro/library/chat/traits/Helper.php
Normal file
94
addons/shopro/library/chat/traits/Helper.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library\chat\traits;
|
||||
|
||||
use addons\shopro\exception\ShoproException;
|
||||
use addons\shopro\library\chat\Getter;
|
||||
|
||||
/**
|
||||
* 助手方法,需要全局地方可用
|
||||
*/
|
||||
trait Helper
|
||||
{
|
||||
|
||||
/**
|
||||
* 客服配置
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $chatConfig;
|
||||
|
||||
|
||||
/**
|
||||
* 获取客服配置
|
||||
*
|
||||
* @param string $name 要获取的配置项,不能获取第二级的配置项
|
||||
* @return void
|
||||
*/
|
||||
public function getConfig($name = null)
|
||||
{
|
||||
// 初始化 workerman 的时候不能读取数据库,会导致数据库连接异常
|
||||
if (!$this->chatConfig) {
|
||||
$config_path = ROOT_PATH . 'application' . DS . 'extra' . DS . 'chat.php';
|
||||
if (!file_exists($config_path)) {
|
||||
throw new ShoproException('客服配置文件不存在,请在后台->商城配置->客服配置,修改并保存客服配置');
|
||||
}
|
||||
|
||||
$this->chatConfig = require($config_path);
|
||||
}
|
||||
|
||||
return $name ? ($this->chatConfig[$name] ?? null) : $this->chatConfig;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据类型获取房间完整名字,主要为了记录当前系统总共有多少房间
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function getRoomName($type, $data = [])
|
||||
{
|
||||
switch ($type) {
|
||||
case 'online': // 当前在线客户端,包含所有连接着
|
||||
$group_name = 'online';
|
||||
break;
|
||||
case 'auth': // 当前用户认证分组,包含 $this->auth 的所有身份分组
|
||||
$group_name = 'auth:' . $data['auth'];
|
||||
break;
|
||||
case 'identify': // 当前身份分组,customer 顾客,customer_service 客服
|
||||
$group_name = 'identify:' . $data['identify'];
|
||||
break;
|
||||
case 'customer_service_room': // 当前在线客服数组, 这里的客服的状态都是 在线的,如果手动切换为离线,则会被移除该房间
|
||||
$group_name = 'customer_service_room:' . ($data['room_id'] ?? 'admin');
|
||||
break;
|
||||
case 'customer_service_room_waiting': // 当前在线用户所在的客服分组的等待房间种
|
||||
$group_name = 'customer_service_room_waiting:' . ($data['room_id'] ?? 'admin');
|
||||
break;
|
||||
case 'customer_service_room_user': // 当前在线用户所在的客服分组
|
||||
$group_name = 'customer_service_room_user:' . ($data['room_id'] ?? 'admin') . ':' . ($data['customer_service_id'] ?? 0);
|
||||
break;
|
||||
default:
|
||||
$group_name = $type;
|
||||
break;
|
||||
}
|
||||
|
||||
return $group_name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取 getter 实例,类中有 getter 属性才可以用
|
||||
*
|
||||
* @param string $driver
|
||||
* @return Getter
|
||||
*/
|
||||
protected function getter($driver = null)
|
||||
{
|
||||
if ($driver) {
|
||||
return $this->getter->driver($driver);
|
||||
}
|
||||
return $this->getter;
|
||||
}
|
||||
}
|
||||
302
addons/shopro/library/chat/traits/NspData.php
Normal file
302
addons/shopro/library/chat/traits/NspData.php
Normal file
@@ -0,0 +1,302 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library\chat\traits;
|
||||
|
||||
/**
|
||||
* 在 nsp 连接实例绑定数据
|
||||
*/
|
||||
trait NspData
|
||||
{
|
||||
// data 的格式
|
||||
// 'data' => [
|
||||
// 'room_ids' => ['admin'] // 客服房间数组,目前只有 admin
|
||||
// 'session_ids' => ['user' => [1,2,3], 'admin' => [1,2,3]] // 登录的身份的 ids
|
||||
// 'connections' => [
|
||||
// room_id => [
|
||||
// 'session_id' => customer_service_id
|
||||
// ]
|
||||
// ],
|
||||
// 'waitings' => [
|
||||
// 'room_id' => [排队的用户]
|
||||
// ]
|
||||
// ]
|
||||
|
||||
|
||||
/**
|
||||
* 获取存储的数据,当前 Nsp
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function nspData($name = null, $value = '')
|
||||
{
|
||||
$data = $this->nsp->nspData ?? [];
|
||||
|
||||
if (is_null($name)) {
|
||||
// 获取全部数据
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ('' === $value) {
|
||||
// 获取缓存
|
||||
return 0 === strpos($name, '?') ? isset($data[$name]) : ($data[$name] ?? null);
|
||||
} elseif (is_null($value)) {
|
||||
// 删除缓存
|
||||
unset($data[$name]);
|
||||
$this->nsp->nspData = $data;
|
||||
}
|
||||
|
||||
// 存数据
|
||||
$data[$name] = $value;
|
||||
$this->nsp->nspData = $data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取等待中的排队,如果有房间,返回当前房间的
|
||||
*
|
||||
* @param string $room_id
|
||||
* @return array
|
||||
*/
|
||||
// public function nspGetSessionIds($auth)
|
||||
// {
|
||||
// $sessionIds = $this->nspData('session_ids');
|
||||
// $sessionIds = $sessionIds ?: [];
|
||||
|
||||
// return $auth ? $sessionIds[$auth] ?? [] : $sessionIds;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 保存当前在线的 auth id
|
||||
*
|
||||
* @param integer $session_id
|
||||
* @return void
|
||||
*/
|
||||
// public function nspSessionIdAdd($session_id, $auth)
|
||||
// {
|
||||
// $sessionIds = $this->nspData('session_ids');
|
||||
// $sessionIds = $sessionIds ?: [];
|
||||
|
||||
// // 当前 auth 的 ids
|
||||
// $currentsessionIds = $sessionIds[$auth] ?? [];
|
||||
|
||||
// // 已经存在直接忽略
|
||||
// if (!in_array($session_id, $currentsessionIds)) {
|
||||
// // 追加auth
|
||||
// $currentsessionIds[] = $session_id;
|
||||
|
||||
// $sessionIds[$auth] = $currentsessionIds;
|
||||
// $this->nspData('session_ids', $sessionIds);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 删除一个 auth id (离线了)
|
||||
*
|
||||
* @param integer $session_id
|
||||
* @return void
|
||||
*/
|
||||
// public function nspSessionIdDel($session_id, $auth)
|
||||
// {
|
||||
// $sessionIds = $this->nspData('session_ids');
|
||||
// $sessionIds = $sessionIds ?: [];
|
||||
|
||||
// // 当前 auth 的 ids
|
||||
// $currentsessionIds = $sessionIds[$auth] ?? [];
|
||||
|
||||
// $key = array_search($session_id, $currentsessionIds);
|
||||
// if ($key !== false) {
|
||||
// // 移除
|
||||
// unset($currentsessionIds[$key]);
|
||||
|
||||
// // 重新赋值
|
||||
// $sessionIds[$auth] = array_values($currentsessionIds);
|
||||
|
||||
// $this->nspData('session_ids', $sessionIds);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取顾客的连接客服(这个信息只会存 10s,防止用户刷新,重新分配客服问题)
|
||||
*
|
||||
* @param string $room_id
|
||||
* @return array
|
||||
*/
|
||||
public function nspGetConnectionCustomerServiceId($room_id, $session_id)
|
||||
{
|
||||
$connections = $this->nspData('connections');
|
||||
$connections = $connections ?: [];
|
||||
|
||||
// 当前房间的 waitings
|
||||
$roomConnections = $connections[$room_id] ?? [];
|
||||
|
||||
$data_str = $roomConnections[$session_id] ?? '';
|
||||
if ($data_str) {
|
||||
// 删除 connections
|
||||
unset($roomConnections[$session_id]);
|
||||
$connections[$room_id] = $roomConnections;
|
||||
$this->nspData('connections', $connections);
|
||||
|
||||
// 获取 客服 id
|
||||
$data = explode('-', $data_str);
|
||||
if ($data[0] >= (time() - 300)) {
|
||||
return $data[1] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 记录用户的服务客服
|
||||
*
|
||||
* @param string $room_id
|
||||
* @param string $session_id
|
||||
* @param string $customer_service_id
|
||||
* @return void
|
||||
*/
|
||||
public function nspConnectionAdd($room_id, $session_id, $customer_service_id)
|
||||
{
|
||||
// 所有 waitings
|
||||
$connections = $this->nspData('connections');
|
||||
$connections = $connections ?: [];
|
||||
|
||||
// 当前房间的 waitings
|
||||
$roomConnections = $connections[$room_id] ?? [];
|
||||
|
||||
if (!in_array($session_id, $roomConnections)) {
|
||||
// 将 session_id 和对应的 客服 id 关联
|
||||
$roomConnections[$session_id] = time() . '-' . $customer_service_id;
|
||||
// 重新赋值
|
||||
$connections[$room_id] = $roomConnections;
|
||||
// 保存
|
||||
$this->nspData('connections', $connections);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 记录要创建的 客服 room 房间
|
||||
*
|
||||
* @param string $room_id
|
||||
* @return void
|
||||
*/
|
||||
public function nspRoomIdAdd($room_id)
|
||||
{
|
||||
$roomIds = $this->nspData('room_ids');
|
||||
$roomIds = $roomIds ?: [];
|
||||
|
||||
// 已经存在直接忽略
|
||||
if (!in_array($room_id, $roomIds)) {
|
||||
// 追加房间
|
||||
$roomIds[] = $room_id;
|
||||
$this->nspData('room_ids', $roomIds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取等待中的排队,如果有房间,返回当前房间的
|
||||
*
|
||||
* @param string $room_id
|
||||
* @return array
|
||||
*/
|
||||
public function nspGetWaitings($room_id = null)
|
||||
{
|
||||
$waitings = $this->nspData('waitings');
|
||||
$waitings = $waitings ?: [];
|
||||
|
||||
return $room_id ? $waitings[$room_id] ?? [] : $waitings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 给对应房间的排队中追加顾客,如果已经存在,忽略
|
||||
*
|
||||
* @param string $room_id
|
||||
* @param string $session_id
|
||||
* @return void
|
||||
*/
|
||||
public function nspWaitingAdd($room_id, $session_id)
|
||||
{
|
||||
// 所有 waitings
|
||||
$waitings = $this->nspData('waitings');
|
||||
$waitings = $waitings ? : [];
|
||||
|
||||
// 当前房间的 waitings
|
||||
$roomWaitings = $waitings[$room_id] ?? [];
|
||||
|
||||
if (!in_array($session_id, $roomWaitings)) {
|
||||
// 将 session_id 加入 房间 waitings,如果存在,忽略
|
||||
$roomWaitings[] = $session_id;
|
||||
// 重新赋值
|
||||
$waitings[$room_id] = $roomWaitings;
|
||||
// 保存
|
||||
$this->nspData('waitings', $waitings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 删除对应房间中排队的顾客,如果不存在忽略
|
||||
*
|
||||
* @param string $room_id
|
||||
* @param string $session_id
|
||||
* @return void
|
||||
*/
|
||||
public function nspWaitingDel($room_id, $session_id)
|
||||
{
|
||||
// 所有 waitings
|
||||
$waitings = $this->nspData('waitings');
|
||||
$waitings = $waitings ?: [];
|
||||
|
||||
// 当前房间的 waitings
|
||||
$roomWaitings = $waitings[$room_id] ?? [];
|
||||
|
||||
$key = array_search($session_id, $roomWaitings);
|
||||
if ($key !== false) {
|
||||
// 移除
|
||||
unset($roomWaitings[$key]);
|
||||
|
||||
// 重新赋值
|
||||
$waitings[$room_id] = array_values($roomWaitings);
|
||||
|
||||
$this->nspData('waitings', $waitings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取在房间中的排名
|
||||
*
|
||||
* @param string $room_id
|
||||
* @param string $session_id
|
||||
* @return integer
|
||||
*/
|
||||
public function nspWaitingRank($room_id, $session_id)
|
||||
{
|
||||
// 所有 waitings
|
||||
$waitings = $this->nspData('waitings');
|
||||
$waitings = $waitings ?: [];
|
||||
|
||||
// 当前房间的 waitings
|
||||
$roomWaitings = $waitings[$room_id] ?? [];
|
||||
|
||||
// 获取 session_id 的下标,就是当前顾客前面还有多少位 顾客
|
||||
$key = array_search($session_id, $roomWaitings);
|
||||
|
||||
return $key !== false ? $key : 0;
|
||||
}
|
||||
}
|
||||
158
addons/shopro/library/chat/traits/Session.php
Normal file
158
addons/shopro/library/chat/traits/Session.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library\chat\traits;
|
||||
|
||||
/**
|
||||
* 客服 session 工具类
|
||||
*/
|
||||
trait Session
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取存储的数据,当前 socket
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function session($name = null, $value = '')
|
||||
{
|
||||
$data = $this->socket->session ?? [];
|
||||
|
||||
if (is_null($name)) {
|
||||
// 获取全部数据
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ('' === $value) {
|
||||
// 获取缓存
|
||||
return 0 === strpos($name, '?') ? isset($data[$name]) : ($data[$name] ?? null);
|
||||
} elseif (is_null($value)) {
|
||||
// 删除缓存
|
||||
unset($data[$name]);
|
||||
$this->socket->session = $data;
|
||||
}
|
||||
|
||||
// 存数据
|
||||
$data[$name] = $value;
|
||||
$this->socket->session = $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过 client_id 获取指定 client_id 的 session 数据
|
||||
*
|
||||
* @param string $client_id 要获取 session 的 client_id
|
||||
* @param string $name 要获取 session 中的特定 name
|
||||
* @return string|array
|
||||
*/
|
||||
public function getSession($client_id, $name = null)
|
||||
{
|
||||
$client = $this->nsp->sockets[$client_id] ?? null;
|
||||
|
||||
$session = is_null($client) ? null : (isset($client->session) && $client->session ? $client->session : []);
|
||||
|
||||
return $name ? ($session[$name] ?? null) : $session;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 调用 getSession,别名
|
||||
*
|
||||
* @param string $client_id 要获取 session 的 client_id
|
||||
* @param string $name 要获取 session 中的特定 name
|
||||
* @return string|array
|
||||
*/
|
||||
public function getSessionByClientId($client_id, $name = null)
|
||||
{
|
||||
return $this->getSession($client_id, $name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过 client_ids 获取指定 client_ids 的 session 数据集合
|
||||
*
|
||||
* @param string $client_id 要获取 session 的 client_id
|
||||
* @param string $name 要获取 session 中的特定 name
|
||||
* @return array
|
||||
*/
|
||||
public function getSessionByClientIds($clientIds, $name = null)
|
||||
{
|
||||
$customerServices = [];
|
||||
foreach ($clientIds as $client_id) {
|
||||
$currentCustomerService = $this->getSessionByClientId($client_id, $name);
|
||||
if ($currentCustomerService) {
|
||||
$customerServices[] = $currentCustomerService;
|
||||
}
|
||||
}
|
||||
|
||||
return $customerServices;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 通过 client_id 更新指定 client_id 的 session 数据
|
||||
*
|
||||
* @param string $client_id 要获取 session 的 client_id
|
||||
* @param array $data 要更新 session 中的特定 name
|
||||
* @return boolean
|
||||
*/
|
||||
public function updateSession($client_id, $data = [])
|
||||
{
|
||||
$client = $this->nsp->sockets[$client_id] ?? null;
|
||||
|
||||
if (!$client) {
|
||||
// client 没有,可能下线了,直接返回
|
||||
return false;
|
||||
}
|
||||
|
||||
// session 不存在,初始化
|
||||
if (!isset($client->session)) {
|
||||
$this->nsp->sockets[$client_id]->session = [];
|
||||
}
|
||||
|
||||
// 更新对应的键
|
||||
foreach ($data as $key => $value) {
|
||||
$current = $this->nsp->sockets[$client_id]->session[$key] ?? null;
|
||||
if (is_array($value) && $current) {
|
||||
// 合并数组,比如修改 session 中 customer_service 客服信息 中的 status 在线状态(注意,如果value 为空,则覆盖,如果有值,则合并)
|
||||
$this->nsp->sockets[$client_id]->session[$key] = $value ? array_merge($current, $value) : $value;
|
||||
} else {
|
||||
$this->nsp->sockets[$client_id]->session[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 调用 updateSession,别名
|
||||
*
|
||||
* @param string $client_id 要获取 session 的 client_id
|
||||
* @param array $data 要更新 session 中的特定 name
|
||||
* @return boolean
|
||||
*/
|
||||
public function updateSessionByClientId($client_id, $data = [])
|
||||
{
|
||||
return $this->updateSession($client_id, $data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过 client_id 更新指定 client_id 的 session 数据集合
|
||||
*
|
||||
* @param string $client_id 要获取 session 的 client_id
|
||||
* @param string $name 要获取 session 中的特定 name
|
||||
* @return array
|
||||
*/
|
||||
public function updateSessionByClientIds($clientIds, $data = [])
|
||||
{
|
||||
foreach ($clientIds as $client_id) {
|
||||
$this->updateSessionByClientId($client_id, $data);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
444
addons/shopro/library/chat/traits/sender/SenderFunc.php
Normal file
444
addons/shopro/library/chat/traits/sender/SenderFunc.php
Normal file
@@ -0,0 +1,444 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library\chat\traits\sender;
|
||||
|
||||
use addons\shopro\library\chat\traits\Helper;
|
||||
use addons\shopro\library\chat\traits\Session;
|
||||
use addons\shopro\library\chat\traits\NspData;
|
||||
|
||||
/**
|
||||
* 绑定 uid
|
||||
*/
|
||||
trait SenderFunc
|
||||
{
|
||||
use Session;
|
||||
|
||||
use Helper;
|
||||
|
||||
use NspData;
|
||||
|
||||
/**
|
||||
* 用户自己触发:发送给自己,登录成功
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return void
|
||||
*/
|
||||
public function authSuccess($callback)
|
||||
{
|
||||
// 通过回调发送给自己
|
||||
$this->successSocket($callback, '连接成功', [
|
||||
'session_id' => $this->session('session_id'),
|
||||
'chat_user' => $this->session('chat_user') ?: null,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 用户自己触发:通知所有房间中的客服,用户上线了
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function customerOnline()
|
||||
{
|
||||
$session_id = $this->session('session_id');
|
||||
$room_id = $this->session('room_id');
|
||||
$chatUser = $this->session('chat_user');
|
||||
$chatUser = $this->getter()->chatUsersFormat($room_id, [$chatUser], ['first' => true]);
|
||||
|
||||
$this->successRoom('customer_online', '顾客上线', [
|
||||
'session_id' => $session_id,
|
||||
'chat_user' => $chatUser,
|
||||
], $this->getRoomName('customer_service_room', ['room_id' => $room_id]));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用户自己触发:通知所有房间中的客服,用户下线了
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function customerOffline()
|
||||
{
|
||||
$session_id = $this->session('session_id');
|
||||
$room_id = $this->session('room_id');
|
||||
$chatUser = $this->session('chat_user');
|
||||
$chatUser = $this->getter()->chatUsersFormat($room_id, [$chatUser], ['first' => true]);
|
||||
|
||||
$this->successRoom('customer_offline', '顾客下线', [
|
||||
'session_id' => $session_id,
|
||||
'chat_user' => $chatUser,
|
||||
], $this->getRoomName('customer_service_room', ['room_id' => $room_id]));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用户自己触发:通知用户的客户端,排队中,通知客服,有新的等待中用户
|
||||
*
|
||||
* @param array $waiting 等待中的顾客
|
||||
* @return void
|
||||
*/
|
||||
public function waiting()
|
||||
{
|
||||
$session_id = $this->session('session_id');
|
||||
$room_id = $this->session('room_id');
|
||||
|
||||
// 通知客服更新 等待中列表,返回整个等待的列表
|
||||
$this->successRoom('customer_waiting', '顾客等待', [
|
||||
'waitings' => $this->getter()->getCustomersFormatWaiting($room_id),
|
||||
], $this->getRoomName('customer_service_room', ['room_id' => $room_id]));
|
||||
|
||||
// 通知用户前面排队人数
|
||||
$this->waitingQueue($room_id, $session_id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用户自己触发,服务器定时触发:通知用户的客户端,当前有客服,但是需要排队 (传参是因为别的地方也需要调用)
|
||||
*
|
||||
* @param string $room_id
|
||||
* @param string $session_id
|
||||
* @return void
|
||||
*/
|
||||
public function waitingQueue($room_id, $session_id)
|
||||
{
|
||||
$rank = $this->nspWaitingRank($room_id, $session_id);
|
||||
|
||||
$title = $rank > 0 ? '当前还有 ' . $rank . ' 位顾客,请耐心等待' : '客服马上为您服务,请稍等';
|
||||
|
||||
// 通知用户等待接入
|
||||
$this->successUId('waiting_queue', '排队等待', [
|
||||
'title' => $title,
|
||||
], ['id' => $session_id, 'type' => 'customer']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 当有变动,批量通知排队等待排名
|
||||
*
|
||||
* @param string|null $room_id
|
||||
* @return void
|
||||
*/
|
||||
public function allWaitingQueue($room_id = null)
|
||||
{
|
||||
$waitings = $this->nspGetWaitings($room_id);
|
||||
|
||||
if ($room_id) {
|
||||
// 只处理该房间的
|
||||
if ($this->getter->driver('socket')->hasCustomerServiceByRoomId($room_id)) {
|
||||
foreach ($waitings as $key => $session_id) {
|
||||
$this->waitingQueue($room_id, $session_id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 定时器调用, 处理所有房间的
|
||||
foreach ($waitings as $current_room_id => $roomWaitings) {
|
||||
// 判断是否有客服在线
|
||||
if ($this->getter->driver('socket')->hasCustomerServiceByRoomId($current_room_id)) {
|
||||
foreach ($roomWaitings as $key => $session_id) {
|
||||
$this->waitingQueue($current_room_id, $session_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用户自己触发:通知用户的客户端,当前没有客服在线
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function noCustomerService()
|
||||
{
|
||||
$session_id = $this->session('session_id');
|
||||
|
||||
$this->successUId('no_customer_service', '暂无客服在线', [
|
||||
'message' => [
|
||||
'message_type' => 'system',
|
||||
'message' => '当前客服不在线',
|
||||
'createtime' => time()
|
||||
],
|
||||
], ['id' => $session_id, 'type' => 'customer']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 给客服发消息
|
||||
*
|
||||
* @param array $message 消息原始内容
|
||||
* @param array $sender 发送者信息
|
||||
* @param integer $customer_service_id 客服 id
|
||||
* @return void
|
||||
*/
|
||||
public function messageToCustomerService($message, $sender, $customer_service_id)
|
||||
{
|
||||
// 给客服发送消息
|
||||
$this->messageUId('message', '收到消息', [
|
||||
'message' => $message,
|
||||
'sender' => $sender
|
||||
], ['id' => $customer_service_id, 'type' => 'customer_service']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 给顾客发消息
|
||||
*
|
||||
* @param array $message 消息原始内容
|
||||
* @param array $sender 发送者信息
|
||||
* @param integer $session_id 顾客 session_id
|
||||
* @return void
|
||||
*/
|
||||
public function messageToCustomer($message, $sender, $session_id)
|
||||
{
|
||||
// 给用户发送消息
|
||||
$this->messageUId('message', '收到消息', [
|
||||
'message' => $message,
|
||||
'sender' => $sender
|
||||
], ['id' => $session_id, 'type' => 'customer']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 同时通知客服和顾客
|
||||
*
|
||||
* @param array $question 要回答的问题
|
||||
* @param string $session_id 用户表示
|
||||
* @param string $customer_service_id 客服
|
||||
* @return void
|
||||
*/
|
||||
public function messageToBoth($question, $session_id, $customer_service_id)
|
||||
{
|
||||
$customerService = $this->session('customer_service');
|
||||
$chatUser = $this->session('chat_user');
|
||||
|
||||
$message = [
|
||||
'message_type' => 'text',
|
||||
'message' => $question['content'],
|
||||
];
|
||||
$sender = [
|
||||
'sender_identify' => 'customer_service',
|
||||
'customer_service' => $customerService,
|
||||
];
|
||||
// 发给用户
|
||||
$this->messageUId('message', '收到消息', [
|
||||
'message' => $message,
|
||||
'sender' => $sender
|
||||
], ['id' => $session_id, 'type' => 'customer']);
|
||||
|
||||
// 发给客服
|
||||
if ($customer_service_id) {
|
||||
// 有客服,发给客服
|
||||
$message['sender_identify'] = 'customer_service';
|
||||
$message['sender_id'] = $customerService['id'];
|
||||
$message['sender'] = $customerService;
|
||||
$message['createtime'] = time();
|
||||
$sender['session_id'] = $session_id;
|
||||
$this->successUId('message', '收到消息', [
|
||||
'message' => $message,
|
||||
'sender' => $sender
|
||||
], ['id' => $customer_service_id, 'type' => 'customer_service']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通知连接的用户(在当前客服服务的房间里面的用户),客服上线了
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function customerServiceOnline()
|
||||
{
|
||||
$room_id = $this->session('room_id');
|
||||
$customerService = $this->session('customer_service');
|
||||
|
||||
// 给客服发送消息
|
||||
$this->successRoom('customer_service_online', '客服 ' . $customerService['name'] . ' 上线', [
|
||||
'customer_service' => $customerService,
|
||||
], $this->getRoomName('customer_service_room_user', ['room_id' => $room_id, 'customer_service_id' => $customerService['id']]));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通知连接的用户(在当前客服服务的房间里面的用户),客服忙碌
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function customerServiceBusy()
|
||||
{
|
||||
$room_id = $this->session('room_id');
|
||||
$customerService = $this->session('customer_service');
|
||||
|
||||
// 给客服发送消息
|
||||
$this->successRoom('customer_service_busy', '客服 ' . $customerService['name'] . ' 忙碌', [
|
||||
'customer_service' => $customerService,
|
||||
], $this->getRoomName('customer_service_room_user', ['room_id' => $room_id, 'customer_service_id' => $customerService['id']]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知连接的用户(在当前客服服务的房间里面的用户),客服下线了
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function customerServiceOffline()
|
||||
{
|
||||
$room_id = $this->session('room_id');
|
||||
$customerService = $this->session('customer_service');
|
||||
|
||||
$this->successRoom('customer_service_offline', '客服 ' . $customerService['name'] . ' 离线', [
|
||||
'customer_service' => $customerService,
|
||||
], $this->getRoomName('customer_service_room_user', ['room_id' => $room_id, 'customer_service_id' => $customerService['id']]));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通知当前房间的在线客服,更新当前在线客服列表
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function customerServiceUpdate()
|
||||
{
|
||||
$room_id = $this->session('room_id');
|
||||
|
||||
// 给客服发送消息
|
||||
$customerServices = $this->getter('socket')->getCustomerServicesByRoomId($room_id);
|
||||
$this->successRoom('customer_service_update', '更新客服列表', [
|
||||
'customer_services' => $customerServices,
|
||||
], $this->getRoomName('customer_service_room', ['room_id' => $room_id]));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 服务结束,通知顾客客服断开连接
|
||||
*
|
||||
* @param string $session_id
|
||||
* @return void
|
||||
*/
|
||||
public function customerServiceBreak($session_id)
|
||||
{
|
||||
$this->successUId('customer_service_break', '客服断开', [
|
||||
'message' => [
|
||||
'message_type' => 'system',
|
||||
'message' => '服务已结束',
|
||||
'createtime' => time()
|
||||
],
|
||||
], ['id' => $session_id, 'type' => 'customer']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 客服转接
|
||||
*
|
||||
* @param string $room_id 房间号
|
||||
* @param string $session_id 用户 UId
|
||||
* @param array $customerService 老客服
|
||||
* @param array $newCustomerService 新客服
|
||||
* @return void
|
||||
*/
|
||||
public function customerTransfer($room_id, $session_id, $customerService, $newCustomerService)
|
||||
{
|
||||
// 通知用户客服被转接
|
||||
$this->customerAccessedToCustomer($session_id, $customerService, $newCustomerService);
|
||||
|
||||
// 通知所有客服用户被接入
|
||||
$this->customerAccessedToCustomerServices($room_id, $session_id, $newCustomerService);
|
||||
|
||||
// 通知新的客服,新用户接入
|
||||
$this->customerAccessedToCustomerService($room_id, $session_id, $newCustomerService);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 客服接入
|
||||
*
|
||||
* @param string $room_id 房间号
|
||||
* @param string $session_id 用户 UId
|
||||
* @param array $customerService 老客服
|
||||
* @return void
|
||||
*/
|
||||
public function customerAccessed($room_id, $session_id, $customerService)
|
||||
{
|
||||
// 通知用户客服接入
|
||||
$this->customerAccessedToCustomer($session_id, $customerService);
|
||||
|
||||
// 通知所有客服用户被接入
|
||||
$this->customerAccessedToCustomerServices($room_id, $session_id, $customerService);
|
||||
|
||||
// 通知新的客服,新用户接入
|
||||
$this->customerAccessedToCustomerService($room_id, $session_id, $customerService);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 通知顾客 客服接入
|
||||
*
|
||||
* @param string $session_id 用户 UId
|
||||
* @param array $customerService 老客服
|
||||
* @param array $newCustomerService 新客服
|
||||
* @return void
|
||||
*/
|
||||
private function customerAccessedToCustomer($session_id, $customerService, $newCustomerService = null)
|
||||
{
|
||||
// 通知当前用户的所有客户端,客服接入
|
||||
$message = '您好,客服 ' . $customerService['name'] . " 为您服务";
|
||||
if ($newCustomerService) {
|
||||
$message = '您好,您的客服已由 ' . $customerService['name'] . " 切换为 " . $newCustomerService['name'];
|
||||
}
|
||||
|
||||
$this->successUId('customer_service_access', '客服接入', [
|
||||
'message' => [
|
||||
'message_type' => 'system',
|
||||
'message' => $message,
|
||||
'createtime' => time()
|
||||
],
|
||||
'customer_service' => $newCustomerService ? : $customerService
|
||||
], ['id' => $session_id, 'type' => 'customer']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 通知所有客服,用户被接入
|
||||
*
|
||||
* @param string $room_id 房间号
|
||||
* @param string $session_id 用户 UId
|
||||
* @param array $customerService 老客服
|
||||
* @return void
|
||||
*/
|
||||
private function customerAccessedToCustomerServices($room_id, $session_id, $customerService)
|
||||
{
|
||||
// 通知所有客服,用户被接入
|
||||
$this->successRoom('customer_accessed', '顾客被接入', [
|
||||
'session_id' => $session_id,
|
||||
'chat_user' => $this->getter('db')->getChatUserBySessionId($session_id),
|
||||
'customer_service' => $customerService,
|
||||
], $this->getRoomName('customer_service_room', ['room_id' => $room_id]));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通知被接入客服,新用户接入
|
||||
*
|
||||
* @param [type] $session_id
|
||||
* @param [type] $customerService
|
||||
* @return void
|
||||
*/
|
||||
private function customerAccessedToCustomerService($room_id, $session_id, $customerService)
|
||||
{
|
||||
// 获取chatUser
|
||||
$chatUser = $this->getter('db')->getChatUserBySessionId($session_id);
|
||||
// 格式化chatUser
|
||||
$chatUser = $this->getter()->chatUsersFormat($room_id, [$chatUser], ['first' => true]);
|
||||
|
||||
// 通知新的客服,新用户接入
|
||||
$this->successUId('customer_access', '新顾客接入', [
|
||||
'session_id' => $session_id,
|
||||
'chat_user' => $chatUser,
|
||||
], ['id' => $customerService['id'], 'type' => 'customer_service']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user