其余文件
This commit is contained in:
348
app/common/websocket/Handler.php
Normal file
348
app/common/websocket/Handler.php
Normal file
@ -0,0 +1,348 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | LikeShop有特色的全开源社交分销电商系统
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 商业用途务必购买系统授权,以免引起不必要的法律纠纷
|
||||
// | 禁止对系统程序代码以任何目的,任何形式的再发布
|
||||
// | 微信公众号:好象科技
|
||||
// | 访问官网:http://www.likemarket.net
|
||||
// | 访问社区:http://bbs.likemarket.net
|
||||
// | 访问手册:http://doc.likemarket.net
|
||||
// | 好象科技开发团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: LikeShopTeam-段誉
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace app\common\websocket;
|
||||
|
||||
|
||||
use app\common\enum\ChatMsgEnum;
|
||||
use app\common\utils\Redis;
|
||||
use Swoole\Server;
|
||||
use Swoole\Websocket\Frame;
|
||||
use think\App;
|
||||
use think\Event;
|
||||
use think\Request;
|
||||
use think\swoole\Websocket;
|
||||
use think\swoole\websocket\Room;
|
||||
|
||||
class Handler extends Websocket
|
||||
{
|
||||
protected $server;
|
||||
|
||||
protected $room;
|
||||
|
||||
protected $parser;
|
||||
|
||||
protected $cache;
|
||||
|
||||
protected $prefix;
|
||||
|
||||
public function __construct(App $app, Server $server, Room $room, Event $event, Parser $parser, Redis $redis)
|
||||
{
|
||||
$this->server = $server;
|
||||
$this->room = $room;
|
||||
$this->parser = $parser;
|
||||
$this->cache = $redis;
|
||||
$this->prefix = config('default.websocket_prefix');
|
||||
parent::__construct($app, $server, $room, $event);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes open
|
||||
* @param int $fd
|
||||
* @param Request $request
|
||||
* @return bool|mixed|void
|
||||
* @author 段誉
|
||||
* @date 2021/12/15 19:13
|
||||
*/
|
||||
public function onOpen($fd, Request $request)
|
||||
{
|
||||
$token = $request->get('token/s'); // 客服
|
||||
$type = $request->get('type/s'); // user, kefu
|
||||
$client = $request->get('client/d');
|
||||
$shop_id = $request->get('shop_id/d', 0); //当前对话的商家id
|
||||
|
||||
try {
|
||||
$user = $this->triggerEvent('login', ['client' => $client, 'token' => $token, 'type' => $type]);
|
||||
|
||||
if ($user['code'] == 20001 || empty($user['data']['id'])) {
|
||||
throw new \Exception(empty($user['msg']) ? "未知错误" : $user['msg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo 'onOpen错误:' . $e->getMessage();
|
||||
return $this->server->close($fd);
|
||||
}
|
||||
|
||||
// 登录者绑定fd
|
||||
$this->bindFd($type, $user['data'], $fd, $shop_id);
|
||||
|
||||
$this->ping($fd);
|
||||
|
||||
return $this->pushData($fd, 'login', [
|
||||
'msg' => '连接成功',
|
||||
'msg_type' => ChatMsgEnum::TYPE_TEXT
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes onMessage
|
||||
* @param Frame $frame
|
||||
* @return bool|mixed|void
|
||||
* @author 段誉
|
||||
* @date 2021/12/20 10:53
|
||||
*/
|
||||
public function onMessage(Frame $frame)
|
||||
{
|
||||
$param = $this->parser->decode($frame->data);
|
||||
|
||||
try {
|
||||
// 回应ping
|
||||
if ('ping' === $param['event']) {
|
||||
return $this->ping($frame->fd);
|
||||
}
|
||||
|
||||
$param['handle'] = $this;
|
||||
$param['fd'] = $frame->fd;
|
||||
|
||||
return $this->triggerEvent($param['event'], $param);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
echo $e->getMessage();
|
||||
return $this->pushData($frame->fd, 'error', [
|
||||
'msg' => $e->getMessage(),
|
||||
'msg_type' => ChatMsgEnum::TYPE_TEXT
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @notes onClose
|
||||
* @param int $fd
|
||||
* @param int $reactorId
|
||||
* @author 段誉
|
||||
* @date 2021/12/15 19:03
|
||||
*/
|
||||
public function onClose($fd, $reactorId)
|
||||
{
|
||||
$this->triggerEvent('close', ['handle' => $this, 'fd' => $fd]);
|
||||
$this->removeBind($fd);
|
||||
$this->server->close($fd);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 触发事件
|
||||
* @param string $event
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
* @author 段誉
|
||||
* @date 2021/12/15 19:03
|
||||
*/
|
||||
public function triggerEvent(string $event, array $data)
|
||||
{
|
||||
return $this->event->until('swoole.websocket.' . $event, $data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @notes 登录者的id绑定fd
|
||||
* @param $type
|
||||
* @param $user
|
||||
* @param $fd
|
||||
* @param $shop_id
|
||||
* @author 段誉
|
||||
* @date 2021/12/15 19:02
|
||||
*/
|
||||
public function bindFd($type, $user, $fd, $shop_id)
|
||||
{
|
||||
$uid = $user['id'];
|
||||
|
||||
//检查当前用户当前终端是否已存在
|
||||
// $check = $this->getFdByUid($uid, $type);
|
||||
// if (!empty($check)) {
|
||||
// foreach ($check as $item) {
|
||||
// $info = $this->getDataByFd($item);
|
||||
// $client = $info['client'] ?? 0;
|
||||
// if ($client == $user['client'] && $fd != $item) {
|
||||
// $this->del($this->prefix . 'fd_' . $item);
|
||||
// $this->cache->srem($this->prefix . $type . '_' . $uid, $item);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// socket_fd_{fd} => ['uid' => {uid}, 'type' => {type}]
|
||||
// 以fd为键缓存当前fd的信息
|
||||
$fdKey = $this->prefix . 'fd_' . $fd;
|
||||
$fdData = [
|
||||
'uid' => $uid,
|
||||
'type' => $type,
|
||||
'nickname' => $user['nickname'],
|
||||
'avatar' => $user['avatar'],
|
||||
'client' => $user['client'],
|
||||
'shop_id' => $shop_id
|
||||
];
|
||||
$this->cache->set($fdKey, json_encode($fdData, true));
|
||||
|
||||
// socket_user_1(user_id) => {fd} 用户userid为1 的 fd
|
||||
// socket_kefu_2(kefu_id) => {fd} 客服kefu_id为2 的 fd
|
||||
$uidKey = $this->prefix . $type . '_' . $uid;
|
||||
$this->cache->sadd($uidKey, $fd);
|
||||
|
||||
// socket_user => {fd} 在线用户的所有fd
|
||||
if ($type == 'kefu') {
|
||||
$groupKey = $this->prefix . 'shop_' . $shop_id . '_kefu';
|
||||
} else {
|
||||
$groupKey = $this->prefix . 'user';
|
||||
}
|
||||
$this->cache->sadd($groupKey, $uid);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 移除绑定
|
||||
* @param $fd
|
||||
* @author 段誉
|
||||
* @date 2021/12/15 19:02
|
||||
*/
|
||||
public function removeBind($fd)
|
||||
{
|
||||
$data = $this->getDataByFd($fd);
|
||||
if ($data) {
|
||||
$key = $this->prefix . 'user';
|
||||
if($data['type'] == 'kefu') {
|
||||
$key = $this->prefix . 'shop_'. $data['shop_id'] . '_kefu';
|
||||
}
|
||||
$this->cache->srem($key, $data['uid']); // socket_user => 11
|
||||
$this->cache->srem($this->prefix . $data['type'] . '_' . $data['uid'], $fd); // socket_user_uid => fd
|
||||
}
|
||||
$this->cache->del($this->prefix . 'fd_' . $fd);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @notes 通过登录id和登录类型获取对应的fd
|
||||
* @param $uid
|
||||
* @param $type
|
||||
* @return bool
|
||||
* @author 段誉
|
||||
* @date 2021/12/15 19:02
|
||||
*/
|
||||
public function getFdByUid($uid, $type)
|
||||
{
|
||||
$key = $this->prefix . $type . '_' . $uid;
|
||||
return $this->cache->sMembers($key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @notes 根据fd获取登录的id和登录类型
|
||||
* @param $fd
|
||||
* @return mixed|string
|
||||
* @author 段誉
|
||||
* @date 2021/12/15 19:02
|
||||
*/
|
||||
public function getDataByFd($fd)
|
||||
{
|
||||
$key = $this->prefix . 'fd_' . $fd;
|
||||
$result = $this->cache->get($key);
|
||||
if (!empty($result)) {
|
||||
$result = json_decode($result, true);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes ping
|
||||
* @param $fd
|
||||
* @return bool
|
||||
* @author 段誉
|
||||
* @date 2021/12/20 15:19
|
||||
*/
|
||||
public function ping($fd)
|
||||
{
|
||||
$data = $this->getDataByFd($fd);
|
||||
if (!empty($data)) {
|
||||
return $this->pushData($fd, 'ping', ['client_time' => time()]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @notes 推送数据
|
||||
* @param $fd
|
||||
* @param $event
|
||||
* @param $data
|
||||
* @return bool
|
||||
* @author 段誉
|
||||
* @date 2021/12/15 19:02
|
||||
*/
|
||||
public function pushData($fd, $event, $data)
|
||||
{
|
||||
$data = $this->parser->encode($event, $data);
|
||||
|
||||
// fd非数组时转为数组
|
||||
if (!is_array($fd)) {
|
||||
$fd = [$fd];
|
||||
}
|
||||
|
||||
// 向fd发送消息
|
||||
foreach ($fd as $item) {
|
||||
if ($this->server->exist($item)) {
|
||||
$this->server->push($item, $data);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @notes 在线fd
|
||||
* @param $fd
|
||||
* @return array
|
||||
* @author 段誉
|
||||
* @date 2021/12/17 18:19
|
||||
*/
|
||||
public function onlineFd($fd)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
if (empty($fd)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (!is_array($fd)) {
|
||||
$fd = [$fd];
|
||||
}
|
||||
|
||||
foreach ($fd as $item) {
|
||||
|
||||
// $fd_data = $this->getDataByFd($fd);
|
||||
// $fd_shop_id = $fd_data['shop_id'] ?? 0;
|
||||
// if ($fd_shop_id != $shop_id) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if ($this->server->exist($item)) {
|
||||
$result[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
37
app/common/websocket/Parser.php
Normal file
37
app/common/websocket/Parser.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\websocket;
|
||||
|
||||
|
||||
class Parser
|
||||
{
|
||||
/**
|
||||
* @notes 数组数据转json
|
||||
* @param string $event
|
||||
* @param $data
|
||||
* @return false|string
|
||||
* @author 段誉
|
||||
* @date 2021/12/29 18:27
|
||||
*/
|
||||
public function encode(string $event, $data)
|
||||
{
|
||||
return json_encode(['event' => $event, 'data' => $data]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes json转数组数据
|
||||
* @param $data
|
||||
* @return array
|
||||
* @author 段誉
|
||||
* @date 2021/12/29 18:28
|
||||
*/
|
||||
public function decode($data)
|
||||
{
|
||||
$result = json_decode($data, true);
|
||||
return [
|
||||
'event' => $result['event'] ?? null,
|
||||
'data' => $result['data'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
80
app/common/websocket/Response.php
Normal file
80
app/common/websocket/Response.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\websocket;
|
||||
|
||||
|
||||
use app\common\enum\ChatMsgEnum;
|
||||
|
||||
class Response
|
||||
{
|
||||
/**
|
||||
* @notes 结果数据
|
||||
* @param string $msg
|
||||
* @param array $data
|
||||
* @param int $code
|
||||
* @return array
|
||||
* @author 段誉
|
||||
* @date 2021/12/29 18:26
|
||||
*/
|
||||
private function result(string $msg = 'OK', array $data = [], int $code = 10001)
|
||||
{
|
||||
$result = [
|
||||
'code' => $code,
|
||||
'msg' => $msg,
|
||||
'data' => $data
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 成功
|
||||
* @param string $msg
|
||||
* @param array $data
|
||||
* @param int $code
|
||||
* @return array
|
||||
* @author 段誉
|
||||
* @date 2021/12/29 18:26
|
||||
*/
|
||||
public function success(string $msg = 'OK', array $data = [], int $code = 10001)
|
||||
{
|
||||
return $this->result($msg, $data, $code);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 错误
|
||||
* @param string $msg
|
||||
* @param array $data
|
||||
* @param int $code
|
||||
* @return array
|
||||
* @author 段誉
|
||||
* @date 2021/12/29 18:27
|
||||
*/
|
||||
public function error(string $msg = 'Error', array $data = [], int $code = 20001)
|
||||
{
|
||||
return $this->result($msg, $data, $code);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 整理返送错误信息
|
||||
* @param string $msg
|
||||
* @param int $msg_type
|
||||
* @return array
|
||||
* @author 段誉
|
||||
* @date 2021/12/29 18:27
|
||||
*/
|
||||
public function formatSendError(string $msg, int $msg_type = 0)
|
||||
{
|
||||
if (empty($msg_type)) {
|
||||
$msg_type = ChatMsgEnum::TYPE_TEXT;
|
||||
}
|
||||
|
||||
return [
|
||||
'msg' => $msg,
|
||||
'msg_type' => $msg_type,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user