Initial commit: queue workspace
Made-with: Cursor
This commit is contained in:
333
pro_v3.5.1/app/webscoket/BaseHandler.php
Normal file
333
pro_v3.5.1/app/webscoket/BaseHandler.php
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\webscoket;
|
||||
|
||||
use app\services\message\service\StoreServiceLogServices;
|
||||
use app\services\message\service\StoreServiceRecordServices;
|
||||
use app\services\message\service\StoreServiceServices;
|
||||
use app\services\order\StoreOrderRefundServices;
|
||||
use app\services\order\StoreOrderServices;
|
||||
use app\services\product\product\StoreProductServices;
|
||||
use app\services\user\UserServices;
|
||||
use app\services\wechat\WechatUserServices;
|
||||
use app\jobs\notice\template\WechatTemplateJob;
|
||||
use crmeb\services\wechat\Messages;
|
||||
use crmeb\services\wechat\OfficialAccount;
|
||||
use crmeb\utils\Arr;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\facade\Log;
|
||||
use think\response\Json;
|
||||
|
||||
/**
|
||||
* socket 事件基础类
|
||||
* Class BaseHandler
|
||||
* @package app\webscoket
|
||||
*/
|
||||
abstract class BaseHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Manager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* @var Room
|
||||
*/
|
||||
protected $room;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $fd;
|
||||
|
||||
/**
|
||||
* 用户聊天端
|
||||
* @var int|null
|
||||
*/
|
||||
protected $formType;
|
||||
|
||||
/**
|
||||
* 登陆
|
||||
* @param array $data
|
||||
* @param Response $response
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function login(array $data, Response $response);
|
||||
|
||||
/**
|
||||
* 事件入口
|
||||
* @param $event
|
||||
* @return |null
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
[$method, $result, $manager, $room] = $event;
|
||||
$this->manager = $manager;
|
||||
$this->room = $room;
|
||||
$this->fd = array_shift($result);
|
||||
$this->formType = array_shift($result);
|
||||
if (method_exists($this, $method)) {
|
||||
if (($method == 'login' || $method == 'kefu_login') && is_string($result[0])) {
|
||||
$result[0] = ['token' => $result[0]];
|
||||
}
|
||||
return $this->{$method}(...$result);
|
||||
} else {
|
||||
Log::error('socket 回调事件' . $method . '不存在,消息内容为:' . json_encode($result));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天事件
|
||||
* @param array $data
|
||||
* @param Response $response
|
||||
* @return bool|Json|null
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException|TransportExceptionInterface
|
||||
*/
|
||||
public function chat(array $data = [], Response $response)
|
||||
{
|
||||
$user = $this->room->get($this->fd);
|
||||
if (!$user) {
|
||||
return $response->fail('聊天用户不存在');
|
||||
}
|
||||
$to_uid = $data['to_uid'] ?? 0;
|
||||
$msn_type = $data['type'] ?? 0;
|
||||
$msn = $data['msn'] ?? '';
|
||||
$formType = $this->formType ?? null;
|
||||
//是否为游客
|
||||
$isTourist = $data['is_tourist'] ?? 0;
|
||||
$tourist_uid = $data['tourist_uid'] ?? 0;
|
||||
$isTourist = $isTourist && $tourist_uid;
|
||||
$tourist_avatar = $data['tourist_avatar'] ?? '';
|
||||
$uid = $isTourist ? $tourist_uid : $user['uid'];
|
||||
if (!$to_uid) {
|
||||
return $response->message('err_tip', ['msg' => '用户不存在']);
|
||||
}
|
||||
if ($to_uid == $uid) {
|
||||
return $response->message('err_tip', ['msg' => '不能和自己聊天']);
|
||||
}
|
||||
|
||||
/** @var StoreServiceLogServices $logServices */
|
||||
$logServices = app()->make(StoreServiceLogServices::class);
|
||||
if (!in_array($msn_type, StoreServiceLogServices::MSN_TYPE)) {
|
||||
return $response->message('err_tip', ['msg' => '格式错误']);
|
||||
}
|
||||
$msn = trim(strip_tags(str_replace(["\n", "\t", "\r", " "], '', htmlspecialchars_decode($msn))));
|
||||
$data = compact('to_uid', 'msn_type', 'msn', 'uid');
|
||||
$data['add_time'] = time();
|
||||
$data['is_tourist'] = $data['is_tourist'] ?? 0;
|
||||
|
||||
//获取收消息人的所有fd信息
|
||||
$toUserFd = $this->manager->userFd($user['type'] == 2 ? 1 : 2, $to_uid);
|
||||
$toUser = ['to_uid' => -1];
|
||||
$fremaData = [];
|
||||
foreach ($toUserFd as $value) {
|
||||
if ($frem = $this->room->get($value)) {
|
||||
$fremaData[] = $frem;
|
||||
//如果当收消息人在和当前发消息人对话中
|
||||
if ($frem['to_uid'] == $user['uid']) {
|
||||
$toUser = $frem;
|
||||
}
|
||||
}
|
||||
}
|
||||
//是否在线
|
||||
//是否和当前用户对话
|
||||
$online = $toUserFd && $toUser && $toUser['to_uid'] !== -1;
|
||||
|
||||
$data['type'] = $online ? 1 : 0;
|
||||
$data = $logServices->save($data);
|
||||
$data = $data->toArray();
|
||||
$data['_add_time'] = $data['add_time'];
|
||||
$data['add_time'] = strtotime($data['add_time']);
|
||||
$_userInfo = [];
|
||||
if (!$isTourist) {
|
||||
/** @var StoreServiceServices $userService */
|
||||
$userService = app()->make(StoreServiceServices::class);
|
||||
$_userInfo = $userService->get(['uid' => $data['uid']], ['nickname', 'avatar']);
|
||||
if (!$_userInfo) {
|
||||
/** @var UserServices $userService */
|
||||
$userService = app()->make(UserServices::class);
|
||||
$_userInfo = $userService->getUserInfo($data['uid'], 'nickname,avatar');
|
||||
}
|
||||
}
|
||||
if ($isTourist || !$_userInfo) {
|
||||
$avatar = sys_config('tourist_avatar');
|
||||
$_userInfo['avatar'] = $tourist_avatar ? $tourist_avatar : Arr::getArrayRandKey(is_array($avatar) ? $avatar : []);
|
||||
$_userInfo['nickname'] = '游客' . $uid;
|
||||
|
||||
}
|
||||
$data['nickname'] = $_userInfo['nickname'];
|
||||
$data['avatar'] = $_userInfo['avatar'];
|
||||
|
||||
//商品消息类型
|
||||
$data['productInfo'] = [];
|
||||
if ($msn_type == StoreServiceLogServices::MSN_TYPE_GOODS && $msn) {
|
||||
/** @var StoreProductServices $productServices */
|
||||
$productServices = app()->make(StoreProductServices::class);
|
||||
$productInfo = $productServices->get((int)$msn, ['id', 'store_name', 'IFNULL(sales,0) + IFNULL(ficti,0) as sales', 'image', 'slider_image', 'price', 'vip_price', 'ot_price', 'stock']);
|
||||
$data['productInfo'] = $productInfo ? $productInfo->toArray() : [];
|
||||
}
|
||||
//订单消息类型
|
||||
$data['orderInfo'] = [];
|
||||
if ($msn_type == StoreServiceLogServices::MSN_TYPE_ORDER && $msn) {
|
||||
/** @var StoreOrderServices $orderServices */
|
||||
$orderServices = app()->make(StoreOrderServices::class);
|
||||
$order = $orderServices->getUserOrderDetail($msn, $uid);
|
||||
if ($order) {
|
||||
$order = $orderServices->tidyOrder($order->toArray(), true, true);
|
||||
$order['add_time_y'] = date('Y-m-d', $order['add_time']);
|
||||
$order['add_time_h'] = date('H:i:s', $order['add_time']);
|
||||
$data['orderInfo'] = $order;
|
||||
}
|
||||
}
|
||||
if ($msn_type == StoreServiceLogServices::MSN_TYPE_REFUND_ORDER && $msn) {
|
||||
/** @var StoreOrderRefundServices $orderRefundServices */
|
||||
$orderRefundServices = app()->make(StoreOrderRefundServices::class);
|
||||
$order = $orderRefundServices->refundDetail($msn);
|
||||
if ($order) {
|
||||
$order['add_time_y'] = date('Y-m-d', $order['add_time']);
|
||||
$order['add_time_h'] = date('H:i:s', $order['add_time']);
|
||||
$data['orderInfo'] = $order;
|
||||
}
|
||||
}
|
||||
|
||||
//用户向客服发送消息,判断当前客服是否在登录中
|
||||
/** @var StoreServiceRecordServices $serviceRecored */
|
||||
$serviceRecored = app()->make(StoreServiceRecordServices::class);
|
||||
$unMessagesCount = $logServices->getMessageNum(['uid' => $uid, 'to_uid' => $to_uid, 'type' => 0, 'is_tourist' => $isTourist ? 1 : 0]);
|
||||
//记录当前用户和他人聊天记录
|
||||
$data['recored'] = $serviceRecored->saveRecord($uid, $to_uid, $msn, $formType ?? 0, $msn_type, $unMessagesCount, $isTourist, $data['nickname'], $data['avatar']);
|
||||
//是否在线
|
||||
if ($online) {
|
||||
$this->manager->pushing($toUserFd, $response->message('reply', $data)->getData());
|
||||
} else {
|
||||
//用户在线,可是没有和当前用户进行聊天,给当前用户发送未读条数
|
||||
if ($toUserFd && $toUser && isset($toUser['to_uid']) && $toUser['to_uid'] != $uid) {
|
||||
$data['recored']['nickname'] = $_userInfo['nickname'];
|
||||
$data['recored']['avatar'] = $_userInfo['avatar'];
|
||||
|
||||
$this->manager->pushing($toUserFd, $response->message('mssage_num', [
|
||||
'uid' => $uid,
|
||||
'num' => $unMessagesCount,
|
||||
'recored' => $data['recored']
|
||||
])->getData());
|
||||
|
||||
}
|
||||
if (!$isTourist) {
|
||||
//用户不在线
|
||||
/** @var WechatUserServices $wechatUserServices */
|
||||
$wechatUserServices = app()->make(WechatUserServices::class);
|
||||
$userInfo = $wechatUserServices->getOne(['uid' => $to_uid, 'user_type' => 'wechat'], 'nickname,subscribe,openid,headimgurl');
|
||||
if ($userInfo && $userInfo['subscribe'] && $userInfo['openid']) {
|
||||
$description = '您有新的消息,请注意查收!';
|
||||
if ($formType !== null) {
|
||||
$head = '客服接待消息提醒';
|
||||
$url = sys_config('site_url') . '/kefu/mobile_chat?toUid=' . $uid . '&nickname=' . $_userInfo['nickname'];
|
||||
} else {
|
||||
$head = '客服回复消息提醒';
|
||||
$url = sys_config('site_url') . '/pages/extension/customer_list/chat?uid=' . $uid;
|
||||
}
|
||||
$message = Messages::newsMessage($head, $description, $url, $_userInfo['avatar']);
|
||||
$userInfo = $userInfo->toArray();
|
||||
try {
|
||||
OfficialAccount::staffSend($message, $userInfo['openid']);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
if ($msn_type == 3) {
|
||||
$msn = '[图片]';
|
||||
} elseif ($msn_type == 4) {
|
||||
$msn = '[语音]';
|
||||
} elseif ($msn_type == 5) {
|
||||
$msn = '[商品]';
|
||||
} elseif ($msn_type == 6) {
|
||||
$msn = '[订单]';
|
||||
} elseif ($msn_type == 7) {
|
||||
$msn = '[退款订单]';
|
||||
}
|
||||
|
||||
// $res = WechatTemplateJob::dispatchDo('sendServiceNoticeNew', [$userInfo['openid'], [
|
||||
// 'first' => $head,
|
||||
// 'keyword1' => $formType !== null ? '客服接待' : '客服回复',
|
||||
// 'keyword2' => '回复中',
|
||||
// 'keyword3' => date('Y-m-d H:i:s', time()),
|
||||
// 'remark' => '消息内容:' . $msn . ';点击查看更多消息!'
|
||||
// ], $url]);
|
||||
// if (!$res) Log::error($userInfo['nickname'] . '发送失败' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $response->message('chat', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换用户聊天
|
||||
* @param array $data
|
||||
* @param Response $response
|
||||
* @return Json
|
||||
*/
|
||||
public function to_chat(array $data = [], Response $response)
|
||||
{
|
||||
$toUid = $data['id'] ?? 0;
|
||||
$res = $this->room->get($this->fd);
|
||||
if ($res && $toUid) {
|
||||
$uid = $res['uid'];
|
||||
$this->manager->updateTabelField((int)$res['type'], (int)$uid, (int)$toUid);
|
||||
//不是游客进入记录
|
||||
if (!$res['tourist']) {
|
||||
/** @var StoreServiceRecordServices $service */
|
||||
$service = app()->make(StoreServiceRecordServices::class);
|
||||
$service->update(['user_id' => $uid, 'to_uid' => $toUid], ['mssage_num' => 0]);
|
||||
/** @var StoreServiceLogServices $logServices */
|
||||
$logServices = app()->make(StoreServiceLogServices::class);
|
||||
$logServices->update(['uid' => $toUid, 'to_uid' => $uid], ['type' => 1]);
|
||||
}
|
||||
return $response->message('mssage_num', ['uid' => $toUid, 'num' => 0, 'recored' => (object)[]]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试原样返回
|
||||
* @param array $data
|
||||
* @param Response $response
|
||||
* @return bool|Json|null
|
||||
*/
|
||||
public function test(array $data = [], Response $response)
|
||||
{
|
||||
return $response->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭连接触发
|
||||
* @param array $data
|
||||
* @param Response $response
|
||||
*/
|
||||
public function close(array $data = [], Response $response)
|
||||
{
|
||||
$uid = $data['uid'] ?? 0;
|
||||
if ($uid) {
|
||||
/** @var StoreServiceRecordServices $service */
|
||||
$service = app()->make(StoreServiceRecordServices::class);
|
||||
$service->updateRecord(['to_uid' => $uid], ['online' => 0]);
|
||||
$this->manager->pushing($this->manager->userFd(Manager::KEFU_TYPE_NUM), $response->message('online', [
|
||||
'online' => 0,
|
||||
'uid' => $uid
|
||||
]), $this->fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
268
pro_v3.5.1/app/webscoket/Manager.php
Normal file
268
pro_v3.5.1/app/webscoket/Manager.php
Normal file
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace app\webscoket;
|
||||
|
||||
use think\Config;
|
||||
use crmeb\services\CacheService;
|
||||
use Swoole\Websocket\Frame;
|
||||
use think\Event;
|
||||
use think\response\Json;
|
||||
use think\swoole\Websocket;
|
||||
use think\swoole\websocket\Room;
|
||||
use app\webscoket\Room as NowRoom;
|
||||
use think\swoole\websocket\socketio\Handler;
|
||||
|
||||
/**
|
||||
* Class Manager
|
||||
* @package app\webscoket
|
||||
*/
|
||||
class Manager extends Handler
|
||||
{
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $cache_timeout;
|
||||
|
||||
/**
|
||||
* @var Response
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* @var \Redis
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @var NowRoom
|
||||
*/
|
||||
protected $nowRoom;
|
||||
|
||||
const USER_TYPE = ['admin', 'user', 'kefu', 'supplier'];
|
||||
|
||||
const KEFU_TYPE_NUM = 2;
|
||||
const USER_TYPE_NUM = 1;
|
||||
|
||||
/**
|
||||
* Manager constructor.
|
||||
* @param Websocket $websocket
|
||||
* @param Config $config
|
||||
* @param Room $room
|
||||
* @param Event $event
|
||||
* @param Response $response
|
||||
* @param \app\webscoket\Room $nowRoom
|
||||
*/
|
||||
public function __construct(Event $event, Config $config, Websocket $websocket, Response $response, NowRoom $nowRoom)
|
||||
{
|
||||
parent::__construct($event, $config, $websocket);
|
||||
$this->response = $response;
|
||||
$this->nowRoom = $nowRoom;
|
||||
$this->cache = CacheService::redisHandler();
|
||||
$this->nowRoom->setCache($this->cache);
|
||||
$this->cache_timeout = intval(app()->config->get('swoole.websocket.ping_timeout', 60000) / 1000) + 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \think\Request $request
|
||||
* @return bool|void
|
||||
*/
|
||||
public function onOpen(\think\Request $request)
|
||||
{
|
||||
$fd = $this->websocket->getSender();
|
||||
$type = $request->get('type');
|
||||
$token = $request->get('token');
|
||||
$touristUid = $request->get('tourist_uid', '');
|
||||
$tourist = !!$touristUid;
|
||||
if (!$token || !in_array($type, self::USER_TYPE)) {
|
||||
return $this->websocket->close();
|
||||
}
|
||||
// 只有用户模式下才能使用游客模式
|
||||
if ($type !== self::USER_TYPE[1] && $tourist) {
|
||||
return $this->websocket->close();
|
||||
}
|
||||
$types = self::USER_TYPE;
|
||||
$this->nowRoom->type(array_flip($types)[$type]);
|
||||
try {
|
||||
$data = $this->exec($type, 'login', [$fd, $request->get('form_type', null), ['token' => $token, 'tourist' => $tourist], $this->response])->getData();
|
||||
} catch (\Throwable $e) {
|
||||
return $this->websocket->close();
|
||||
}
|
||||
if ($tourist) {
|
||||
$data['status'] = 200;
|
||||
$data['data']['uid'] = $touristUid;
|
||||
}
|
||||
if ($data['status'] != 200 || !($data['data']['uid'] ?? null)) {
|
||||
return $this->websocket->close();
|
||||
}
|
||||
$this->resetPingTimeout($this->pingInterval + $this->pingTimeout);
|
||||
$uid = $data['data']['uid'];
|
||||
$type = array_search($type, self::USER_TYPE);
|
||||
$this->login($type, $uid, $fd);
|
||||
$this->nowRoom->add((string)$fd, $uid, 0, $tourist ? 1 : 0);
|
||||
$this->send($fd, $this->response->message('ping', ['now' => time()]));
|
||||
return $this->send($fd, $this->response->success());
|
||||
}
|
||||
|
||||
public function login($type, $uid, $fd)
|
||||
{
|
||||
$key = '_ws_' . $type;
|
||||
$this->cache->sadd($key, $fd);
|
||||
$this->cache->sadd($key . $uid, $fd);
|
||||
$this->refresh($type, $uid);
|
||||
}
|
||||
|
||||
public function refresh($type, $uid)
|
||||
{
|
||||
$key = '_ws_' . $type;
|
||||
$this->cache->expire($key, 1800);
|
||||
$this->cache->expire($key . $uid, 1800);
|
||||
}
|
||||
|
||||
public function logout($type, $uid, $fd)
|
||||
{
|
||||
$key = '_ws_' . $type;
|
||||
$this->cache->srem($key, $fd);
|
||||
$this->cache->srem($key . $uid, $fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户所有的fd
|
||||
* @param $type
|
||||
* @param string $uid
|
||||
* @return array
|
||||
*/
|
||||
public static function userFd($type, $uid = '')
|
||||
{
|
||||
$key = '_ws_' . $type . $uid;
|
||||
return CacheService::redisHandler()->smembers($key) ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行事件调度
|
||||
* @param $type
|
||||
* @param $method
|
||||
* @param $result
|
||||
* @return null|Json
|
||||
*/
|
||||
protected function exec($type, $method, $result)
|
||||
{
|
||||
if (!in_array($type, self::USER_TYPE)) {
|
||||
return null;
|
||||
}
|
||||
if (!is_array($result)) {
|
||||
return null;
|
||||
}
|
||||
/** @var Json $response */
|
||||
return $this->event->until('swoole.websocket.' . $type, [$method, $result, $this, $this->nowRoom]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
* @return bool
|
||||
*/
|
||||
public function onMessage(Frame $frame)
|
||||
{
|
||||
$fd = $this->websocket->getSender();
|
||||
$info = $this->nowRoom->get($fd);
|
||||
|
||||
$result = json_decode($frame->data, true) ?: [];
|
||||
if (!isset($result['type']) || !$result['type']) return true;
|
||||
$this->resetPingTimeout($this->pingInterval + $this->pingTimeout);
|
||||
$this->refresh($info['type'], $info['uid']);
|
||||
if ($result['type'] == 'ping') {
|
||||
return $this->send($fd, $this->response->message('ping', ['now' => time()]));
|
||||
}
|
||||
$data = $result['data'] ?? [];
|
||||
$frame->uid = $info['uid'];
|
||||
/** @var Response $res */
|
||||
$res = $this->exec(self::USER_TYPE[$info['type']], $result['type'], [$fd, $result['form_type'] ?? null, $data, $this->response]);
|
||||
|
||||
if ($res) return $this->send($fd, $res);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $type
|
||||
* @param int $userId
|
||||
* @param int $toUserId
|
||||
* @param string $field
|
||||
*/
|
||||
public function updateTabelField(int $type, int $userId, int $toUserId, string $field = 'to_uid')
|
||||
{
|
||||
$fds = self::userFd($type, $userId);
|
||||
foreach ($fds as $fd) {
|
||||
$this->nowRoom->update($fd, $field, $toUserId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送文本响应
|
||||
* @param $fd
|
||||
* @param Json $json
|
||||
* @return bool
|
||||
*/
|
||||
public function send($fd, \think\response\Json $json)
|
||||
{
|
||||
return $this->pushing($fd, $json->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送
|
||||
* @param $data
|
||||
* @return bool
|
||||
*/
|
||||
public function pushing($fds, $data, $exclude = null)
|
||||
{
|
||||
if ($data instanceof \think\response\Json) {
|
||||
$data = $data->getData();
|
||||
}
|
||||
$data = is_array($data) ? json_encode($data) : $data;
|
||||
$fds = is_array($fds) ? $fds : [$fds];
|
||||
|
||||
foreach ($fds as $fd) {
|
||||
if (!$fd) {
|
||||
continue;
|
||||
}
|
||||
if ($exclude && is_array($exclude) && !in_array($fd, $exclude)) {
|
||||
continue;
|
||||
} elseif ($exclude && $exclude == $fd) {
|
||||
continue;
|
||||
}
|
||||
$this->websocket->to($fd)->push($data);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭连接
|
||||
*/
|
||||
public function onClose()
|
||||
{
|
||||
$fd = $this->websocket->getSender();
|
||||
$tabfd = (string)$fd;
|
||||
if ($this->nowRoom->exist($fd)) {
|
||||
$data = $this->nowRoom->get($tabfd);
|
||||
$this->logout($data['type'], $data['uid'], $fd);
|
||||
$this->nowRoom->type($data['type'])->del($tabfd);
|
||||
$this->exec(self::USER_TYPE[$data['type']], 'close', [$fd, null, ['data' => $data], $this->response]);
|
||||
}
|
||||
parent::onClose();
|
||||
}
|
||||
}
|
||||
111
pro_v3.5.1/app/webscoket/Response.php
Normal file
111
pro_v3.5.1/app/webscoket/Response.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\webscoket;
|
||||
|
||||
use think\response\Json;
|
||||
|
||||
/**
|
||||
* socket Response
|
||||
* Class Response
|
||||
* @package app\webscoket
|
||||
* @mixin Json
|
||||
*/
|
||||
class Response
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Json
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* Response constructor.
|
||||
*/
|
||||
public function __construct(Json $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置返回参数
|
||||
* @param string $type
|
||||
* @param array|null $data
|
||||
* @param int $status
|
||||
* @param array $other
|
||||
* @return Json
|
||||
*/
|
||||
public function send(string $type, ?array $data = null, int $status = 200, array $other = [])
|
||||
{
|
||||
$res = compact('type', 'status');
|
||||
if (!is_null($data)) {
|
||||
$res['data'] = $data;
|
||||
}
|
||||
$data = array_merge($res, $other);
|
||||
$this->response->data($data);
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功
|
||||
* @param string $message
|
||||
* @param array|null $data
|
||||
* @return bool|null
|
||||
*/
|
||||
public function success($type = 'success', ?array $data = null, int $status = 200)
|
||||
{
|
||||
if (is_array($type)) {
|
||||
$data = $type;
|
||||
$type = 'success';
|
||||
}
|
||||
return $this->send($type, $data, $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败
|
||||
* @param string $message
|
||||
* @param array|null $data
|
||||
* @return bool|null
|
||||
*/
|
||||
public function fail($type = 'error', ?array $data = null, int $status = 400)
|
||||
{
|
||||
if (is_array($type)) {
|
||||
$data = $type;
|
||||
$type = 'error';
|
||||
}
|
||||
return $this->send($type, $data, $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置返只有类型没有状态的返回数据
|
||||
* @param string $type
|
||||
* @param $data
|
||||
* @return Json
|
||||
*/
|
||||
public function message(string $type, $data)
|
||||
{
|
||||
$this->response->data(compact('type', 'data'));
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
return call_user_func_array([$this->response, $name], $arguments);
|
||||
}
|
||||
|
||||
}
|
||||
178
pro_v3.5.1/app/webscoket/Room.php
Normal file
178
pro_v3.5.1/app/webscoket/Room.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\webscoket;
|
||||
|
||||
|
||||
use Swoole\Table as SwooleTable;
|
||||
use think\swoole\Table;
|
||||
|
||||
/**
|
||||
* 房间管理
|
||||
* Class Room
|
||||
* @package app\webscoket
|
||||
*/
|
||||
class Room
|
||||
{
|
||||
|
||||
/**
|
||||
* 类型 只有kefu和admin有区别
|
||||
* @var string
|
||||
*/
|
||||
protected $type = '';
|
||||
|
||||
/**
|
||||
* fd前缀
|
||||
* @var string
|
||||
*/
|
||||
protected $tableFdPrefix = 'ws_fd_';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $room = [];
|
||||
|
||||
/**
|
||||
* @var \Redis
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const USER_INFO_FD_PRE = 'socket_user_list';
|
||||
|
||||
const TYPE_NAME = 'socket_user_type';
|
||||
|
||||
/**
|
||||
* 设置缓存
|
||||
* @param $cache
|
||||
* @return $this
|
||||
*/
|
||||
public function setCache($cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表
|
||||
* @param string $type
|
||||
* @return $this
|
||||
*/
|
||||
public function type(string $type)
|
||||
{
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表实例
|
||||
* @return SwooleTable
|
||||
*/
|
||||
public function getTable()
|
||||
{
|
||||
return app()->make(Table::class)->get('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加fd
|
||||
* @param string $key fd
|
||||
* @param int $uid 用户uid
|
||||
* @param int $to_uid 当前聊天人的uid
|
||||
* @param int $tourist 是否为游客
|
||||
* @return mixed
|
||||
*/
|
||||
public function add(string $key, int $uid, int $to_uid = 0, int $tourist = 0)
|
||||
{
|
||||
$nowkey = $this->tableFdPrefix . $key;
|
||||
$data = ['fd' => $key, 'type' => $this->type ?: 'user', 'uid' => $uid, 'to_uid' => $to_uid, 'tourist' => $tourist];
|
||||
$res = $this->getTable()->set($nowkey, $data);
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改数据
|
||||
* @param string $key
|
||||
* @param null $field
|
||||
* @param null $value
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function update(string $key, $field = null, $value = null)
|
||||
{
|
||||
$nowkey = $this->tableFdPrefix . $key;
|
||||
$res = true;
|
||||
if (is_array($field)) {
|
||||
$res = $this->getTable()->set($nowkey, $field);
|
||||
} else if (!is_array($field) && $value !== null) {
|
||||
$data = $this->getTable()->get($nowkey);
|
||||
if (!$data) {
|
||||
return false;
|
||||
}
|
||||
$data[$field] = $value;
|
||||
$res = $this->getTable()->set($nowkey, $data);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->type = $this->typeReset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function del(string $key)
|
||||
{
|
||||
$nowkey = $this->tableFdPrefix . $key;
|
||||
return $this->getTable()->del($nowkey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function exist(string $key)
|
||||
{
|
||||
return $this->getTable()->exist($this->tableFdPrefix . $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取fd的所有信息
|
||||
* @param string $key
|
||||
* @return array|bool|mixed
|
||||
*/
|
||||
public function get(string $key, string $field = null)
|
||||
{
|
||||
return $this->getTable()->get($this->tableFdPrefix . $key, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* fd 获取 uid
|
||||
* @param $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function fdByUid($key)
|
||||
{
|
||||
return $this->getTable()->get($this->tableFdPrefix . $key, 'uid');
|
||||
}
|
||||
|
||||
}
|
||||
BIN
pro_v3.5.1/app/webscoket/SocketPush.php
Normal file
BIN
pro_v3.5.1/app/webscoket/SocketPush.php
Normal file
Binary file not shown.
55
pro_v3.5.1/app/webscoket/handler/AdminHandler.php
Normal file
55
pro_v3.5.1/app/webscoket/handler/AdminHandler.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace app\webscoket\handler;
|
||||
|
||||
use app\services\system\admin\AdminAuthServices;
|
||||
use app\webscoket\BaseHandler;
|
||||
use app\webscoket\Response;
|
||||
use crmeb\exceptions\AuthException;
|
||||
|
||||
/**
|
||||
* Class AdminHandler
|
||||
* @package app\webscoket\handler
|
||||
*/
|
||||
class AdminHandler extends BaseHandler
|
||||
{
|
||||
/**
|
||||
* 后台登陆
|
||||
* @param array $data
|
||||
* @param Response $response
|
||||
* @return bool|\think\response\Json|null
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
*/
|
||||
public function login(array $data, Response $response)
|
||||
{
|
||||
if (!isset($data['token']) || !$token = $data['token']) {
|
||||
return $response->fail('授权失败!');
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var AdminAuthServices $adminAuthService */
|
||||
$adminAuthService = app()->make(AdminAuthServices::class);
|
||||
$authInfo = $adminAuthService->parseToken($token);
|
||||
} catch (AuthException $e) {
|
||||
return $response->fail($e->getMessage());
|
||||
}
|
||||
|
||||
if (!$authInfo || !isset($authInfo['id'])) {
|
||||
return $response->fail('授权失败!');
|
||||
}
|
||||
|
||||
return $response->success(['uid' => $authInfo['id']]);
|
||||
}
|
||||
|
||||
}
|
||||
146
pro_v3.5.1/app/webscoket/handler/KefuHandler.php
Normal file
146
pro_v3.5.1/app/webscoket/handler/KefuHandler.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\webscoket\handler;
|
||||
|
||||
|
||||
use app\services\kefu\LoginServices;
|
||||
use app\services\message\service\StoreServiceRecordServices;
|
||||
use app\services\message\service\StoreServiceServices;
|
||||
use app\services\user\UserServices;
|
||||
use app\webscoket\BaseHandler;
|
||||
use app\webscoket\Manager;
|
||||
use app\webscoket\Response;
|
||||
use crmeb\exceptions\AuthException;
|
||||
|
||||
/**
|
||||
* Class KefuHandler
|
||||
* @package app\webscoket\handler
|
||||
*/
|
||||
class KefuHandler extends BaseHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* 客服登录
|
||||
* @param array $data
|
||||
* @param Response $response
|
||||
* @return bool|mixed|\think\response\Json|null
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function login(array $data, Response $response)
|
||||
{
|
||||
if (!isset($data['token']) || !$token = $data['token']) {
|
||||
return $response->fail('授权失败!');
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var LoginServices $services */
|
||||
$services = app()->make(LoginServices::class);
|
||||
$kefuInfo = $services->parseToken($token);
|
||||
} catch (AuthException $e) {
|
||||
return $response->fail($e->getMessage());
|
||||
}
|
||||
|
||||
/** @var UserServices $userService */
|
||||
$userService = app()->make(UserServices::class);
|
||||
$user = $userService->get($kefuInfo['uid'], ['uid', 'nickname']);
|
||||
if (!isset($user['uid'])) {
|
||||
return $response->fail('您登录的客服用户不存在');
|
||||
}
|
||||
|
||||
/** @var StoreServiceRecordServices $service */
|
||||
$service = app()->make(StoreServiceRecordServices::class);
|
||||
$service->updateRecord(['to_uid' => $user['uid']], ['online' => 1]);
|
||||
/** @var StoreServiceServices $service */
|
||||
$service = app()->make(StoreServiceServices::class);
|
||||
$service->update(['uid' => $user['uid']], ['online' => 1]);
|
||||
|
||||
return $response->success($user->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容前端方法
|
||||
* @param array $data
|
||||
* @param Response $response
|
||||
*/
|
||||
public function kefu_login(array $data = [], Response $response)
|
||||
{
|
||||
return $this->login($data, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上下线
|
||||
* @param array $data
|
||||
* @param Response $response
|
||||
*/
|
||||
public function online(array $data = [], Response $response)
|
||||
{
|
||||
$online = $data['online'] ?? 0;
|
||||
$user = $this->room->get($this->fd);
|
||||
if ($user) {
|
||||
/** @var StoreServiceServices $service */
|
||||
$service = app()->make(StoreServiceServices::class);
|
||||
$service->update(['uid' => $user['uid']], ['online' => $online]);
|
||||
if ($user['to_uid']) {
|
||||
$fd = $this->manager->userFd('', $user['to_uid']);
|
||||
//给当前正在聊天的用户发送上下线消息
|
||||
$this->manager->pushing($fd, $response->message('online', [
|
||||
'online' => $online,
|
||||
'uid' => $user['uid']
|
||||
])->getData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 客服转接
|
||||
* @param array $data
|
||||
* @param Response $response
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function transfer(array $data, Response $response)
|
||||
{
|
||||
$data = $data['data'] ?? [];
|
||||
$uid = $data['recored']['uid'] ?? 0;
|
||||
if ($uid && $this->manager->userFd('', $uid)) {
|
||||
$data['recored']['online'] = 1;
|
||||
} else {
|
||||
$data['recored']['online'] = 0;
|
||||
}
|
||||
return $response->message('transfer', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
* @param array $data
|
||||
* @param Response $response
|
||||
*/
|
||||
public function logout(array $data = [], Response $response)
|
||||
{
|
||||
$user = $this->room->get($this->fd);
|
||||
$uid = $user['uid'] ?? 0;
|
||||
if ($uid) {
|
||||
/** @var StoreServiceServices $service */
|
||||
$service = app()->make(StoreServiceServices::class);
|
||||
$service->update(['uid' => $user['uid']], ['online' => 0]);
|
||||
/** @var StoreServiceRecordServices $service */
|
||||
$service = app()->make(StoreServiceRecordServices::class);
|
||||
$service->updateRecord(['to_uid' => $uid], ['online' => 0]);
|
||||
$this->manager->pushing($this->manager->userFd(Manager::KEFU_TYPE_NUM), $response->message('online', [
|
||||
'online' => 0,
|
||||
'uid' => $uid
|
||||
]), $this->fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
55
pro_v3.5.1/app/webscoket/handler/SupplierHandler.php
Normal file
55
pro_v3.5.1/app/webscoket/handler/SupplierHandler.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace app\webscoket\handler;
|
||||
|
||||
use app\services\supplier\LoginServices;
|
||||
use app\webscoket\BaseHandler;
|
||||
use app\webscoket\Response;
|
||||
use crmeb\exceptions\AuthException;
|
||||
|
||||
/**
|
||||
* Class SupplierHandler
|
||||
* @package app\webscoket\handler
|
||||
*/
|
||||
class SupplierHandler extends BaseHandler
|
||||
{
|
||||
/**
|
||||
* 供应商登录
|
||||
* @param array $data
|
||||
* @param Response $response
|
||||
* @return bool|mixed|\think\response\Json|null
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
*/
|
||||
public function login(array $data, Response $response)
|
||||
{
|
||||
if (!isset($data['token']) || !$token = $data['token']) {
|
||||
return $response->fail('授权失败!');
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var LoginServices $services */
|
||||
$services = app()->make(LoginServices::class);
|
||||
$authInfo = $services->parseToken($token);
|
||||
} catch (AuthException $e) {
|
||||
return $response->fail($e->getMessage());
|
||||
}
|
||||
|
||||
if (!$authInfo || !isset($authInfo['id'])) {
|
||||
return $response->fail('授权失败!');
|
||||
}
|
||||
|
||||
return $response->success(['uid' => $authInfo['id']]);
|
||||
}
|
||||
|
||||
}
|
||||
68
pro_v3.5.1/app/webscoket/handler/UserHandler.php
Normal file
68
pro_v3.5.1/app/webscoket/handler/UserHandler.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace app\webscoket\handler;
|
||||
|
||||
use app\services\message\service\StoreServiceRecordServices;
|
||||
use app\services\user\UserAuthServices;
|
||||
use app\webscoket\BaseHandler;
|
||||
use app\webscoket\Manager;
|
||||
use app\webscoket\Response;
|
||||
use crmeb\exceptions\AuthException;
|
||||
|
||||
/**
|
||||
* Class UserHandler
|
||||
* @package app\webscoket\handler
|
||||
*/
|
||||
class UserHandler extends BaseHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* 用户登陆
|
||||
* @param array $data
|
||||
* @param Response $response
|
||||
* @return bool|mixed|\think\response\Json|null
|
||||
*/
|
||||
public function login(array $data, Response $response)
|
||||
{
|
||||
// 游客登陆
|
||||
if (isset($data['tourist']) && $data['tourist']) {
|
||||
return $response->success();
|
||||
}
|
||||
|
||||
if (!isset($data['token']) || !$token = $data['token']) {
|
||||
return $response->fail('授权失败!');
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var UserAuthServices $services */
|
||||
$services = app()->make(UserAuthServices::class);
|
||||
$authInfo = $services->parseToken($token);
|
||||
} catch (AuthException $e) {
|
||||
return $response->fail($e->getMessage());
|
||||
}
|
||||
|
||||
$user = $authInfo['user'];
|
||||
/** @var StoreServiceRecordServices $service */
|
||||
$service = app()->make(StoreServiceRecordServices::class);
|
||||
$service->updateRecord(['to_uid' => $user->uid], ['online' => 1, 'type' => $res['form_type'] ?? 1]);
|
||||
//给所有在线客服人员发送当前用户上线消息
|
||||
$this->manager->pushing($this->manager->userFd(Manager::KEFU_TYPE_NUM), $response->message('user_online', [
|
||||
'uid' => $user->uid,
|
||||
'online' => 1
|
||||
])->getData(), $this->fd);
|
||||
|
||||
return $response->success('login', $user->toArray());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user