diff --git a/app/command/admin/SysGateway.php b/app/command/admin/SysGateway.php index 34dc311..6397190 100644 --- a/app/command/admin/SysGateway.php +++ b/app/command/admin/SysGateway.php @@ -2,7 +2,7 @@ namespace app\command\admin; -use app\event\AdminGatewayEvents; +use app\event\SysGatewayEvent; use GatewayWorker\Register; use think\console\Command; use think\console\Input; @@ -52,7 +52,7 @@ class SysGateway extends Command $worker->name = 'AdminBusinessWorker'; $worker->count = 4; $worker->registerAddress = '127.0.0.1:1236'; - $worker->eventHandler = AdminGatewayEvents::class; + $worker->eventHandler = SysGatewayEvent::class; // 运行所有服务 Worker::runAll(); diff --git a/app/entity/gateway/el/ElNotification.php b/app/entity/gateway/el/ElNotification.php new file mode 100644 index 0000000..779e970 --- /dev/null +++ b/app/entity/gateway/el/ElNotification.php @@ -0,0 +1,45 @@ +title = $title; + $this->message = $message; + $this->dangerouslyUseHTMLString = $dangerouslyUseHTMLString; + $this->type = $type; + $this->icon = $icon; + $this->duration = $duration; + $this->position = $position; + if (!empty($_attr)) { + foreach ($_attr as $key => $value) { + $this->$key = $value; + } + } + } + + public function toArray(): array + { + return get_object_vars($this); + } +} \ No newline at end of file diff --git a/app/entity/gateway/ws/OutMessage.php b/app/entity/gateway/ws/OutMessage.php new file mode 100644 index 0000000..26b2849 --- /dev/null +++ b/app/entity/gateway/ws/OutMessage.php @@ -0,0 +1,35 @@ +type = $type->value; + $this->event = $event->value; + + if($data instanceof ElNotification) { + $this->data = $data->toArray(); + }else{ + $this->data = (array)$data; + } + } + + public function toJson(): string + { + return json_encode([ + 'type' => $this->type, + 'event' => $this->event, + 'data' => $this->data, + ], JSON_UNESCAPED_UNICODE); + } +} \ No newline at end of file diff --git a/app/entity/gateway/ws/PutMessage.php b/app/entity/gateway/ws/PutMessage.php new file mode 100644 index 0000000..c9daf3c --- /dev/null +++ b/app/entity/gateway/ws/PutMessage.php @@ -0,0 +1,59 @@ +wsClientId = $ws_client_id; + $this->type = WsTypeEnum::from($type); + $this->event = WsEventEnum::from($event); + $this->data = $data; + } + + public function get($name, $default = null) + { + return $this->data[$name] ?? $default; + } + + public function login(int $user_id, string $user_type, array $user_client = []): bool + { + if (empty($user_id) || empty($user_type) || count($user_client) == 0) { + return false; + } + $this->userId = $user_id; + $this->userType = $user_type; + $this->userClient = $user_client; + + return true; + } + public function getClientId(): string + { + return $this->userClient['id'] ?? ''; + } + public function getClientName() + { + return $this->userClient['name'] ?? ''; + } + + public function getClientVersion() + { + return $this->userClient['version'] ?? ''; + } +} \ No newline at end of file diff --git a/app/enum/gateway/WsEventEnum.php b/app/enum/gateway/WsEventEnum.php new file mode 100644 index 0000000..e6627e8 --- /dev/null +++ b/app/enum/gateway/WsEventEnum.php @@ -0,0 +1,17 @@ + [ - 'sysUser' => SysUserSubscribe::class + 'sysUser' => SysUserSubscribe::class, + /* + * 网关事件订阅类 + */ + 'gateway' => GatewaySubscribe::class, ], ]; diff --git a/app/event/AdminGatewayEvents.php b/app/event/AdminGatewayEvents.php deleted file mode 100644 index a22d86d..0000000 --- a/app/event/AdminGatewayEvents.php +++ /dev/null @@ -1,122 +0,0 @@ - '', - 'event'=> '', - ], $message_data); - - - // 根据类型执行不同的业务 - switch ($message_data['type']) { - // 客户端回应服务端的心跳 - case 'pong': - return; - case 'system': - switch ($message_data['event']) { - // 客户端登录 message格式: {type:login, name:xx, room_id:1} ,添加到客户端,广播给所有客户端xx进入聊天室 - case 'login': - // 判断是否有房间号 - if (!isset($message_data['room_id'])) { - throw new \Exception("\$message_data['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']} \$message:$message"); - } - - // 把房间号昵称放到session中 - $room_id = $message_data['room_id']; - $client_name = htmlspecialchars($message_data['client_name']); - $_SESSION['room_id'] = $room_id; - $_SESSION['client_name'] = $client_name; - - // 获取房间内所有用户列表 - $clients_list = Gateway::getClientSessionsByGroup($room_id); - foreach ($clients_list as $tmp_client_id => $item) { - $clients_list[$tmp_client_id] = $item['client_name']; - } - $clients_list[$client_id] = $client_name; - - // 转播给当前房间的所有客户端,xx进入聊天室 message {type:login, client_id:xx, name:xx} - $new_message = array('type' => $message_data['type'], 'client_id' => $client_id, 'client_name' => htmlspecialchars($client_name), 'time' => date('Y-m-d H:i:s')); - Gateway::sendToGroup($room_id, json_encode($new_message)); - Gateway::joinGroup($client_id, $room_id); - - // 给当前用户发送用户列表 - $new_message['client_list'] = $clients_list; - Gateway::sendToCurrentClient(json_encode($new_message)); - return; - - // 客户端发言 message: {type:say, to_client_id:xx, content:xx} - case 'say': - // 非法请求 - if (!isset($_SESSION['room_id'])) { - throw new \Exception("\$_SESSION['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']}"); - } - $room_id = $_SESSION['room_id']; - $client_name = $_SESSION['client_name']; - - // 私聊 - if ($message_data['to_client_id'] != 'all') { - $new_message = array( - 'type' => 'say', - 'from_client_id' => $client_id, - 'from_client_name' => $client_name, - 'to_client_id' => $message_data['to_client_id'], - 'content' => "对你说: " . nl2br(htmlspecialchars($message_data['content'])), - 'time' => date('Y-m-d H:i:s'), - ); - Gateway::sendToClient($message_data['to_client_id'], json_encode($new_message)); - $new_message['content'] = "你对" . htmlspecialchars($message_data['to_client_name']) . "说: " . nl2br(htmlspecialchars($message_data['content'])); - Gateway::sendToCurrentClient(json_encode($new_message)); - return; - } - - $new_message = array( - 'type' => 'say', - 'from_client_id' => $client_id, - 'from_client_name' => $client_name, - 'to_client_id' => 'all', - 'content' => nl2br(htmlspecialchars($message_data['content'])), - 'time' => date('Y-m-d H:i:s'), - ); - Gateway::sendToGroup($room_id, json_encode($new_message)); - return; - } - return; - } - } - - public static function onClose(string $client_id) - { - var_dump(['client_id' => $client_id, 'r'=>'CLOSET']); -// // 从房间的客户端列表中删除 -// if (isset($_SESSION['room_id'])) { -// $room_id = $_SESSION['room_id']; -// $new_message = array('type' => 'logout', 'from_client_id' => $client_id, 'from_client_name' => $_SESSION['client_name'], 'time' => date('Y-m-d H:i:s')); -// Gateway::sendToGroup($room_id, json_encode($new_message)); -// } - } - -} diff --git a/app/event/SysGatewayEvent.php b/app/event/SysGatewayEvent.php new file mode 100644 index 0000000..55963c3 --- /dev/null +++ b/app/event/SysGatewayEvent.php @@ -0,0 +1,164 @@ +type == WsTypeEnum::System && $put->event == WsEventEnum::Login) { + self::authEvent($put); + return; + } + $isLogin = $put->login($_SESSION['user_id'] ?? 0, + $_SESSION['user_type'] ?? '', + $_SESSION['client'] ?? [] + ); + if (!$isLogin) { + echo "用户未登录\r\n"; + // 未登录 + return; + } + + + // 3. 调度: 根据类型执行不同的业务 + switch ($put->type) { + // 客户端回应服务端的心跳 + case WsTypeEnum::Pong: + break; + // 系统类型组操作 + case WsTypeEnum::System: + switch ($put->event) { + case WsEventEnum::Lock_Client: + /** + * 触发后续其他事件 + */ + Event::trigger("gateway.lockClient", [$put]); + break; + } + return; + } + } + + public static function onClose(string $client_id) + { + echo "客户端:{$client_id},关闭了\r\n"; + // 客户端离线的相关逻辑操作 + if (isset($_SESSION['group_list']) && is_array($_SESSION['group_list'])) { + foreach ($_SESSION['group_list'] as $group) { + /* + * 客户端退出了某个房间 + */ + GatewayClientService::sendToGroup($group, new OutMessage( + WsTypeEnum::System, + WsEventEnum::Client_quit_room, + )); + } + } + /** + * 触发后续其他事件 + */ + Event::trigger("gateway.clientClose", [$client_id]); + } + + private static function authEvent(PutMessage $put): void + { + $token = $put->get('token', ''); + $clientVersion = $put->get('clientVersion', ''); + $clientName = $put->get('clientName', ''); + $clientId = $put->get('clientId', ''); + + try { + $token = str_replace('Bearer ', '', $token); + $loginSrv = new LoginService(); + $auth = $loginSrv->checkUserAccessToken($token); + } catch (ValidateException $e) { + // 校验失败 + var_dump('E1.登录凭证校验失败: '.$e->getMessage()); + return; + } + /** + * 加入同角色房间 + */ + $sysUser = SysUser::find($auth->userId); + if ($sysUser) { + var_dump('E1.用户数据不存在'); + return; + } + + // 登录 + $put->login($auth->userId, $auth->userType->value, [ + 'id' => $clientId, + 'name' => $clientName, + 'version' => $clientVersion + ]); + /** + * 记录登录凭证信息到$SESSION中 + */ + $_SESSION['user_id'] = $put->userId; + $_SESSION['user_type'] = $put->userType; + $_SESSION['client'] = [ + 'id' => $put->getClientId(), + 'name' => $put->getClientName(), + 'version' => $put->getClientVersion() + ]; + /** + * 推送登录成功的消息到客户端 + */ + GatewayClientService::sendToClient($put->wsClientId, new OutMessage( + WsTypeEnum::System, + WsEventEnum::Login_SUCCESS, + ['userId' => $auth->userId] + )); + /** + * + */ + GatewayClientService::bindUid($put->wsClientId, $auth->userId); + /** + * 加入已登录的用户公共大厅 + */ + GatewayClientService::joinGroup($put->wsClientId, 'PUBLIC_ROOM'); + /** + * 加入同角色的房间 + */ + $sysUser->roles->each(function (SysRole $role) use ($put) { + GatewayClientService::joinGroup($put->wsClientId, "ROLE_{$role['id']}"); + }); + /** + * 触发后续其他事件 + */ + Event::trigger("gateway.clientLogin", [$put, $sysUser]); + } +} diff --git a/app/service/GatewayClientService.php b/app/service/GatewayClientService.php new file mode 100644 index 0000000..94c5e60 --- /dev/null +++ b/app/service/GatewayClientService.php @@ -0,0 +1,52 @@ +toJson()); + } + + /** + * 客户端加入某个组 + * @param string $clientId + * @param string $group + * @return void + */ + public static function joinGroup(string $clientId, string $group) + { + Gateway::joinGroup($clientId, $group); + $group_list = $_SESSION['group_list'] ?? []; + $group_list[] = $group; + $_SESSION['group_list'] = $group_list; + } + + /** + * 将client_id与uid 绑定 + * @param string $clientId + * @param int $uid + * @return void + */ + public static function bindUid(string $clientId, int $uid) + { + Gateway::bindUid($clientId, $uid); + } + + public static function sendToGroup($group, OutMessage $message, $exclude_client_id = null, $raw = false) + { + Gateway::sendToGroup($group, $message->toJson(), $exclude_client_id, $raw); + } +} \ No newline at end of file diff --git a/app/subscribe/GatewaySubscribe.php b/app/subscribe/GatewaySubscribe.php new file mode 100644 index 0000000..c6028ae --- /dev/null +++ b/app/subscribe/GatewaySubscribe.php @@ -0,0 +1,110 @@ + $put->getClientId(), + 'client_name'=> $put->getClientName(), + 'user_id' => $put->userId, + ])->find(); + /* + * 判断客户端是否锁定状态 + */ + if($client && $client['is_lock']) { + // 通知客户端锁定屏幕 + GatewayClientService::sendToClient($put->wsClientId,new OutMessage( + WsTypeEnum::System, + WsEventEnum::Lock_Client, + )); + } + /* + * 客户端登录通知 + */ + $ip = $_SERVER['REMOTE_ADDR'] ?? ''; + GatewayClientService::sendToClient($put->wsClientId, new OutMessage( + WsTypeEnum::System, + WsEventEnum::Notification, + new ElNotification(title: '登录成功,欢迎您', message: "当前登录Ip: $ip"), + )); + } + + /** + * Ws客户端锁定 + * @param PutMessage $put + * @return void + * @throws DataNotFoundException + * @throws DbException + * @throws ModelNotFoundException + */ + public function onLockClient(PutMessage $put): void + { + $client = SysUserClient::where([ + 'client_id' => $put->getClientId(), + 'client_name'=> $put->getClientName(), + 'user_id' => $put->userId, + ])->find(); + if($client) { + // 加密传递的锁屏密码 + $lock_password = password_hash($put->get('password',''), PASSWORD_DEFAULT); + // 保存锁屏信息 + $client->save([ + 'is_lock'=>1, + 'lock_password'=>$lock_password, + 'lock_time'=>date('Y-m-d H:i:s') + ]); + // 保存锁屏日志 + SysUserClientLog::create([ + 'event' => "{$put->type->value}.{$put->event->value}", + 'message'=> "A. 锁定客户端", + 'data' => json_encode(['inputPass'=> $lock_password]), + 'create_time' => date('Y-m-d H:i:s'), + 'client_data_id' => $client['id'] + ]); + // 通知客户端锁定屏幕 + GatewayClientService::sendToClient($put->wsClientId,new OutMessage( + WsTypeEnum::System, + WsEventEnum::Lock_Client, + )); + } + + } + + + /** + * Ws客户端连接被关闭 + * @return void + */ + public function onClientClose() + { + + } +} \ No newline at end of file diff --git a/z_ele/.env.development b/z_ele/.env.development index e11ec0a..9d0086f 100644 --- a/z_ele/.env.development +++ b/z_ele/.env.development @@ -1,6 +1,7 @@ # 开发环境接口地址 #VITE_API_URL=https://v2.eleadmin.com/api VITE_API_URL=http://a.tcp.run/adminapi +VITE_LICENSE=dk9mcwJyetRWQlxWRiojIiwiIzVHbQ5Wa6ICdjVmaiV3ciQWaiwCN3YDNW9ERolFcMJiOpNnclZnIsIyViQjLxIiOi42bQf0NW== # 禁请求加密 VITE_SKIP_REQUEST_ENCRYPTION=0 # 数据加密key diff --git a/z_ele/src/main.ts b/z_ele/src/main.ts index 645efd0..29fafa0 100644 --- a/z_ele/src/main.ts +++ b/z_ele/src/main.ts @@ -8,8 +8,8 @@ import i18n from './i18n'; import installer from './as-needed'; import { iconsInstaller } from '@/components/IconSelect/util'; import { WsConfig, WsPlugin } from '@/plugins/websocket'; -import "@/plugins/notification" -import "@/plugins/service-worker" +import '@/plugins/notification'; +import '@/plugins/service-worker'; import 'element-plus/theme-chalk/display.css'; import 'ele-admin-plus/es/style/nprogress.scss'; import './styles/themes/rounded.scss'; @@ -21,7 +21,7 @@ const app = createApp(App); // WebSocket配置 const websocketConfig: Partial = { - url: 'ws://139.155.146.146:19980', // 你的WebSocket服务器地址 + url: 'ws://139.155.146.146:19981', // 你的WebSocket服务器地址 reconnectAttempts: 10, reconnectDelay: 5000, autoConnect: true // 应用启动时自动连接