laravel整合workerman实现消息推送

通沛
2023-12-01

安装

composer require workerman/gateway-worker
composer require workerman/gatewayclient

创建 Workerman 启动文件app/Console/Commands/WorkermanCommand

命令:php artisan make:command WorkermanCommand

<?php

namespace App\Console\Commands;

use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
use GatewayWorker\Register;
use Illuminate\Console\Command;
use Workerman\Worker;

class WorkermanCommand extends Command
{

    protected $signature = 'workman {action} {--d}';

    protected $description = 'Start a Workerman server.';

    public function handle()
    {
        global $argv;
        $action = $this->argument('action');

        $argv[0] = 'wk';
        $argv[1] = $action;
        $argv[2] = $this->option('d') ? '-d' : '';

        $this->start();
    }

    private function start()
    {
        $this->startGateWay();
        $this->startBusinessWorker();
        $this->startRegister();
        Worker::runAll();
    }

    private function startBusinessWorker()
    {
        $worker                  = new BusinessWorker();
        $worker->name            = 'BusinessWorker';
        $worker->count           = 1;
        $worker->registerAddress = '127.0.0.1:1236';
        $worker->eventHandler    = \App\Workerman\Events::class;
    }

    private function startGateWay()
    {
        $gateway = new Gateway("websocket://0.0.0.0:2346");
        $gateway->name                 = 'Gateway';
        $gateway->count                = 1;
         $gateway->startPort            = 2300;
        $gateway->pingInterval         = 30;
        $gateway->pingNotResponseLimit = 0;
        $gateway->registerAddress      = '127.0.0.1:1236';
    }

    private function startRegister()
    {
        new Register('text://0.0.0.0:1236');
    }
}

创建事件监听文件app/Workerman/Events.php

<?php
namespace App\Workerman;
use GatewayClient\Gateway;
use Workerman\Lib\Timer;

class Events
{
    public static function onWorkerStart($businessWorker)
    {
    }

    public static function onConnect($client_id)
    {
        Gateway::sendToClient($client_id, json_encode(array(
            'type' => 'init',
            'client_id' => $client_id
        )));
		//定时检测mac地址
        Timer::add(1800, function () use ($client_id) {
            $data = array('type' => 'mac');
            Gateway::sendToClient($client_id, json_encode($data));
        });

    }

    public static function onWebSocketConnect($client_id, $data)
    {
    }

    public static function onMessage($client_id, $message)
    {
    }

    public static function onClose($client_id)
    {
    }
}

启动 Workerman 服务端

php artisan workman start --d

后台使用

<?php

namespace app\Http\Controllers;

use app\Models\User;
use GatewayClient\Gateway;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Cache;

class WorkermanController extends Controller
{
    public function __construct()
    {
        Gateway::$registerAddress = '127.0.0.1:1236';
    }

    /**
     * websocket连接的client_id绑定用户的id
     * @param Request $request
     * @return JsonResponse
     */
    public function bind(Request $request): JsonResponse
    {
        try {
            $uid = auth()->id();
            $roles = Cache::get('user_role_' . $uid);
            Gateway::bindUid($request->get('client_id'), $uid);
            foreach ($roles as $role) {
                Gateway::joinGroup($request->get('client_id'), 'role' . $role);
                $menus = Cache::get('role_menu_' . $role);
                foreach ($menus as $menu) {
                    Gateway::joinGroup($request->get('client_id'), 'menu' . $menu->id);
                }
            }
            $data = array('msg' => '绑定成功');
            return res(compact('data'));
        } catch (\Exception $e) {
            throw new \Exception('绑定失败');
        }
    }

    /**
     * 根据用户更新权限
     * @param $userId
     */
    public function update_menus($userId)
    {
        $data = [];
        $permissions = User::menus($userId);
        $user = new User();
        $data['menus'] = recursion($permissions['menus'], 'id', 'parent_id', 'children', 0, false);
        $data['permissions'] = $permissions['webPermissions'];
        $user->update_token($userId, $permissions['permissions']);
        Gateway::sendToUid($userId, json_encode(array('type' => 'menus', 'data' => $data)));
    }

    public function update_menu_user($userId)
    {
        $userIds = Gateway::getAllUidList();
        if (in_array($userId, $userIds)) {
            $this->update_menus($userId);
        }
    }

    /**
     * 如果修改了角色数据,则用户更新权限
     * @param $roleId
     */
    public function update_menu_role($roleId)
    {
        $userIds = Gateway::getUidListByGroup('role' . $roleId);
        foreach ($userIds as $userId) {
            $this->update_menus($userId);
        }
    }

    /**
     * 如果修改了菜单数据,则用户更新权限
     * @param $menuId
     */
    public function update_menu_menu($menuId)
    {
        $userIds = Gateway::getUidListByGroup('menu' . $menuId);
        foreach ($userIds as $userId) {
            $this->update_menus($userId);
        }
    }

    /**
     * 更新登陆状态
     * @param $userId
     */
    public function update_login($userId)
    {
        $userIds = Gateway::getAllUidList();
        if (in_array($userId, $userIds)) {
            $data = User::query()->find($userId);
            $data->tokens()->where('tokenable_id', '=', $userId)->delete();
            Gateway::sendToUid($userId, json_encode(array('type' => 'logout')));
        }
    }

}

前端使用

ws = new WebSocket('ws://192.168.10.106:2346');
// 连接成功发送用户信息给后端
ws.onopen = function () {
};
// 收到消息保存到vuex
ws.onmessage = async function (e) {
    const type = JSON.parse(e.data).type
    const client_id = JSON.parse(e.data).client_id
    if (type === 'init') {
        //用户绑定client_id
    } else if (type === 'menus') {
        //更新菜单
    } else if (type === 'logout') {
        //退出
    } else if (type === 'mac') {
        //检测mac地址
    }
}

注意

部署线上环境的时候,如果使用docker容易,需要将使用的端口放开。

 类似资料: