// +---------------------------------------------------------------------- namespace app\webscoket; use app\webscoket\handler\AdminHandler; use app\webscoket\handler\MerchantHandler; use app\webscoket\handler\UserHandler; use Swoole\Server; use Swoole\Table as SwooleTable; use Swoole\Websocket\Frame; use think\Config; use think\Event; use think\facade\Cache; use think\Request; use think\response\Json; use think\swoole\Table; use think\swoole\Websocket; use think\swoole\websocket\Room; /** * Class Manager * @package app\webscoket * @author xaboy * @day 2020-04-29 */ class Manager extends Websocket { /** * @var \Swoole\WebSocket\Server */ protected $server; /** * @var Ping */ protected $pingService; /** * @var int */ protected $cache_timeout; const USER_TYPE = ['admin', 'user', 'mer']; /** * Manager constructor. * @param Server $server * @param Room $room * @param Event $event * @param Ping $ping * @param Config $config */ public function __construct(Server $server, Room $room, Event $event, Ping $ping, Config $config) { parent::__construct($server, $room, $event); $this->pingService = $ping; $this->cache_timeout = (int)($config->get('swoole.websocket.ping_timeout', 60000) / 1000) + 2; app()->bind('websocket_handler_admin', AdminHandler::class); app()->bind('websocket_handler_user', UserHandler::class); app()->bind('websocket_handler_mer', MerchantHandler::class); } /** * @param int $fd * @param Request $request * @return mixed * @author xaboy * @day 2020-05-06 */ public function onOpen($fd, Request $request) { $type = $request->get('type'); $token = $request->get('token'); if (!$token || !in_array($type, self::USER_TYPE)) { return $this->server->close($fd); } try { $data = $this->exec($type, 'login', compact('fd', 'request', 'token'))->getData(); } catch (\Exception $e) { return $this->server->close($fd); } if (!isset($data['status']) || $data['status'] != 200 || !($data['data']['uid'] ?? null)) return $this->server->close($fd); $uid = $data['data']['uid']; $type = array_search($type, self::USER_TYPE); $this->login($type, $uid, $fd, $data['data']['data']['mer_id'] ?? null); $this->getTable()->set($fd, compact('type', 'uid', 'fd')); $this->pingService->createPing($fd, time(), $this->cache_timeout); return $this->send($fd, app('json')->message('ping', ['now' => time()])); } public function login($type, $uid, $fd, $merId) { $key = '_ws_' . $type; Cache::sadd($key, $fd); Cache::sadd($key . $uid, $fd); if ($merId) { $this->merLogin($uid, $fd, $merId); } $this->refresh($type, $uid); } public function merLogin($uid, $fd, $merId) { Cache::sadd('_wsm_0' . $merId, $fd); Cache::set('_wsm_1' . $uid, $merId); $this->refreshMer($uid, $merId); } public function refreshMer($uid, $merId) { Cache::expire('_wsm_0' . $merId, 1800); Cache::expire('_wsm_1' . $uid, 1800); } public function refresh($type, $uid) { $key = '_ws_' . $type; Cache::expire($key, 1800); Cache::expire($key . $uid, 1800); } public function logout($type, $uid, $fd) { $key = '_ws_' . $type; Cache::srem($key, $fd); Cache::srem($key . $uid, $fd); $merId = Cache::get('_wsm_1' . $uid); if ($merId) { Cache::delete('_wsm_1' . $uid); Cache::srem('_wsm_0' . $merId, $fd); } } public static function merFd($merId) { return Cache::smembers('_wsm_0' . $merId) ?: []; } public static function userFd($type, $uid = '') { $key = '_ws_' . $type . $uid; return Cache::smembers($key) ?: []; } /** * @return SwooleTable * @author xaboy * @day 2020-05-06 */ protected function getTable() { return app()->make(Table::class)->get('user'); } /** * @param $type * @param $method * @param $result * @return null|Json * @author xaboy * @day 2020-05-06 */ protected function exec($type, $method, $result) { $handler = app()->make('websocket_handler_' . $type); if (!method_exists($handler, $method)) return null; /** @var Json $response */ return $handler->{$method}($result); } /** * @param Frame $frame * @return bool * @author xaboy * @day 2020-04-29 */ public function onMessage(Frame $frame) { $info = $this->getTable()->get($frame->fd); $result = json_decode($frame->data, true) ?: []; if (!isset($result['type']) || !$result['type']) return true; $this->refresh($info['type'], $info['uid']); if ($result['type'] == 'ping') { return $this->send($frame->fd, app('json')->message('ping', ['now' => time()])); } $data = $result['data'] ?? []; $frame->uid = $info['uid']; /** @var Json $response */ $response = $this->exec(self::USER_TYPE[$info['type']], $result['type'], compact('data', 'frame')); if ($response) return $this->send($frame->fd, $response); return true; } protected function send($fd, Json $json) { $this->pingService->createPing($fd, time(), $this->cache_timeout); if ($this->server->isEstablished($fd) && $this->server->exist($fd)) { $this->server->push($fd, json_encode($json->getData())); } return true; } /** * @param int $fd * @param int $reactorId * @author xaboy * @day 2020-04-29 */ public function onClose($fd, $reactorId) { // var_dump('onClose'); if ($this->getTable()->exist($fd)) { $data = $this->getTable()->get($fd); $this->logout($data['type'], $data['uid'], $fd); $this->getTable()->del($fd); $this->exec(self::USER_TYPE[$data['type']], 'close', $data); } $this->pingService->removePing($fd); } }