360 lines
11 KiB
PHP
360 lines
11 KiB
PHP
<?php
|
||
|
||
namespace app\common\utils;
|
||
|
||
use app\common\model\UserRoles;
|
||
use GatewayWorker\Lib\Gateway;
|
||
use app\common\utils\WsResponse;
|
||
use app\common\service\MessageNotificationsService;
|
||
use support\Redis;
|
||
|
||
class WsEvents
|
||
{
|
||
|
||
private $registerAddress = '127.0.0.1:1236';
|
||
protected $clientId;
|
||
protected $eventData;
|
||
|
||
public function __construct()
|
||
{
|
||
Gateway::$registerAddress = $this->registerAddress;
|
||
}
|
||
|
||
/**
|
||
* 监听消息接收
|
||
*/
|
||
public function message($client_id, $message)
|
||
{
|
||
$this->clientId = $client_id;
|
||
|
||
$payload = json_decode($message, true);
|
||
$event = $payload['event'];
|
||
$this->eventData = $payload['data'] ?? [];
|
||
$traceId = $payload['trace_id'] ?? null;
|
||
|
||
$method = $this->eventToMethod($event);
|
||
|
||
if (method_exists($this, $method)) {
|
||
try {
|
||
$this->$method($traceId);
|
||
} catch (\Throwable $th) {
|
||
Gateway::sendToClient($client_id, WsResponse::error(
|
||
$event,
|
||
500,
|
||
$th->getMessage(),
|
||
$traceId
|
||
));
|
||
}
|
||
} else {
|
||
Gateway::sendToClient($client_id, WsResponse::error(
|
||
$event,
|
||
404,
|
||
"Event handler not found: $method",
|
||
$traceId
|
||
));
|
||
}
|
||
}
|
||
|
||
protected function eventToMethod(string $event): string
|
||
{
|
||
$parts = explode('.', $event);
|
||
$method = array_shift($parts);
|
||
foreach ($parts as $part) {
|
||
$method .= ucfirst($part);
|
||
}
|
||
return $method;
|
||
}
|
||
|
||
|
||
// ================= 系统事件 =================
|
||
/**
|
||
* 认证
|
||
*/
|
||
public function auth($client_id, $payload)
|
||
{
|
||
try {
|
||
// 检查来源
|
||
if (!isset($payload['server']['HTTP_ORIGIN'])) {
|
||
flg('[x] [TOKEN认证] 未定义HTTP_ORIGIN', false);
|
||
Gateway::sendToClient($client_id, WsResponse::error(
|
||
'system.auth',
|
||
1001,
|
||
'[x] [TOKEN认证] 未定义HTTP_ORIGIN'
|
||
));
|
||
Gateway::closeClient($client_id);
|
||
return;
|
||
}
|
||
|
||
// 连接是否满足条件
|
||
if (getenv('ENVIRONMENT') === 'master' && $payload['server']['HTTP_ORIGIN'] === getenv('MAIN_DOMAIN')) {
|
||
flg('[x] [TOKEN认证] HTTP_ORIGIN不满足条件', false);
|
||
Gateway::sendToClient($client_id, WsResponse::error(
|
||
'system.auth',
|
||
1001,
|
||
'[x] [TOKEN认证] HTTP_ORIGIN不满足条件'
|
||
));
|
||
Gateway::closeClient($client_id);
|
||
return;
|
||
}
|
||
|
||
// 签名检查
|
||
if (!isset($payload['get']['token'])) {
|
||
flg('[x] [TOKEN认证] 未携带TOKEN参数', false);
|
||
Gateway::sendToClient($client_id, WsResponse::error(
|
||
'system.auth',
|
||
1001,
|
||
'[x] [TOKEN认证] 未携带TOKEN参数'
|
||
));
|
||
Gateway::closeClient($client_id);
|
||
return;
|
||
}
|
||
|
||
// 解析TOKEN
|
||
$user = (new Jwt())->verifyToken($payload['get']['token']);
|
||
|
||
// 绑定用户ID
|
||
Gateway::bindUid($client_id, $user->uid);
|
||
|
||
// 绑定SESSION
|
||
$_SESSION['uid'] = $user->uid;
|
||
// $_SESSION['name'] = $user->uname;
|
||
|
||
// 响应
|
||
Gateway::sendToClient($client_id, WsResponse::success(
|
||
'system.auth',
|
||
['uid' => $user->uid]
|
||
));
|
||
} catch (\Throwable $th) {
|
||
flg('[x] [TOKEN认证] TOKEN认证失败:'.$th->getMessage(), false);
|
||
Gateway::sendToClient($client_id, WsResponse::error(
|
||
'system.auth',
|
||
1001,
|
||
'[x] [TOKEN认证] TOKEN认证失败:'.$th->getMessage()
|
||
));
|
||
Gateway::closeClient($client_id);
|
||
return;
|
||
}
|
||
}
|
||
|
||
public function logout(string $client_id)
|
||
{
|
||
// var_dump('====== ====== ====== start ====== ====== ======');
|
||
// var_dump('关闭客户端ID:'.$client_id);
|
||
// if ($_SESSION !== null) {
|
||
// var_dump($_SESSION);
|
||
// }
|
||
// var_dump('====== ====== ====== end ====== ====== ======');
|
||
}
|
||
|
||
/**
|
||
* 系统心跳
|
||
*
|
||
* @param string|null $traceId
|
||
*/
|
||
public function systemPing(?string $traceId)
|
||
{
|
||
Gateway::sendToClient($this->clientId, WsResponse::success(
|
||
'system.pong',
|
||
['time' => time()],
|
||
$traceId
|
||
));
|
||
}
|
||
|
||
|
||
public function systemConnect(?string $traceId)
|
||
{
|
||
// 响应
|
||
Gateway::sendToClient($this->clientId, WsResponse::success(
|
||
'system.connect.receive',
|
||
['time' => time()],
|
||
$traceId
|
||
));
|
||
|
||
// 获取审批待办数量
|
||
$num = (new \app\common\utils\approval\service\ApprovalFlowService())->getInitTodoCount($_SESSION['uid']);
|
||
$this->todoCounts($_SESSION['uid'], $num);
|
||
|
||
// 绑定检查
|
||
$initData = $this->initCheck($_SESSION['uid']);
|
||
Gateway::sendToClient($this->clientId, WsResponse::success(
|
||
'system.init.check',
|
||
$initData,
|
||
$traceId
|
||
));
|
||
}
|
||
|
||
// ================= 消息通知事件 =================
|
||
/**
|
||
* 管理端主动推送通知
|
||
*/
|
||
public function notifyPush(?string $traceId)
|
||
{
|
||
$title = $this->eventData['title'] ?? null;
|
||
$content = $this->eventData['content'] ?? null;
|
||
$toUids = $this->eventData['to_uids'] ?? [];
|
||
|
||
if (!$title || !$content) {
|
||
Gateway::sendToClient($this->clientId, WsResponse::error(
|
||
'notify.push.result',
|
||
400,
|
||
'标题和内容不能为空',
|
||
$traceId
|
||
));
|
||
return;
|
||
}
|
||
|
||
// 写消息
|
||
$msgService = new MessageNotificationsService();
|
||
$msgId = $msgService->createMessage([
|
||
'sender_id' => $this->eventData['sender_id'] ?? 0,
|
||
'sender_type' => $this->eventData['sender_type'] ?? 'system',
|
||
'title' => $title,
|
||
'content' => $content,
|
||
'scene' => $this->eventData['scene'] ?? 0,
|
||
'recipient' => $this->eventData['to_uids'] ? implode(',', $this->eventData['to_uids']) : '',
|
||
'push_method' => isset($this->eventData['push_method']) ? $this->eventData['push_method'] : 0
|
||
]);
|
||
|
||
// 写用户状态
|
||
$uids = !empty($toUids) ? $toUids : $msgService->getAllUserIds();
|
||
$msgService->addUserStatus($msgId, $uids);
|
||
|
||
$payload = WsResponse::success('notify.receive', [
|
||
'msg_id' => $msgId,
|
||
'title' => $title,
|
||
'content' => $content,
|
||
'created_at' => time()
|
||
], $traceId);
|
||
|
||
if (!empty($toUids)) {
|
||
// 推送给指定用户
|
||
foreach ($toUids as $uid) {
|
||
Gateway::sendToUid($uid, $payload);
|
||
}
|
||
} else {
|
||
// 推送给全体在线用户
|
||
Gateway::sendToAll($payload);
|
||
}
|
||
|
||
// 回复发送者确认
|
||
Gateway::sendToClient($this->clientId, WsResponse::success(
|
||
'notify.push.result',
|
||
['msg_id' => $msgId],
|
||
$traceId,
|
||
'消息发送成功'
|
||
));
|
||
}
|
||
|
||
/**
|
||
* 客户端标记通知已读
|
||
*/
|
||
public function notifyRead(?string $traceId)
|
||
{
|
||
$msgIds = $this->eventData['msg_ids'];
|
||
if (!is_array($msgIds) && $msgIds !== 'all') {
|
||
Gateway::sendToClient($this->clientId, WsResponse::error(
|
||
'notify.read.result',
|
||
400,
|
||
'消息ID不能为空',
|
||
$traceId
|
||
));
|
||
return;
|
||
}
|
||
|
||
// 更新消息为已读
|
||
$msgService = new MessageNotificationsService();
|
||
$msgService->markAsRead($msgIds, Gateway::getUidByClientId($this->clientId));
|
||
|
||
Gateway::sendToClient($this->clientId, WsResponse::success(
|
||
'notify.read.result',
|
||
['msg_ids' => $msgIds],
|
||
$traceId
|
||
));
|
||
}
|
||
|
||
|
||
/*
|
||
approval.todo.counts 审批待办数量
|
||
|
||
*/
|
||
|
||
// ================= 主动推送 =================
|
||
|
||
public function pushWebsocket(string $event, int|string $uid, array $data)
|
||
{
|
||
return match (gettype($uid)) {
|
||
'integer' => Gateway::sendToUid($uid, WsResponse::success($event, $data)),
|
||
'string' => Gateway::sendToClient($uid, WsResponse::success($event, $data)),
|
||
default => throw new \InvalidArgumentException('UID must be integer or string'),
|
||
};
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
// ================= 业务处理 =================
|
||
|
||
/**
|
||
* 初始化检查
|
||
*/
|
||
public function initCheck(int $userId)
|
||
{
|
||
$data = [
|
||
// 'is_report_to' => false,
|
||
// 'is_office_clerk' => false,
|
||
'is_sign' => false
|
||
];
|
||
|
||
$user = \app\common\model\User::find($userId);
|
||
// if ($user->report_to > 0) {
|
||
// $data['is_report_to'] = true;
|
||
// }
|
||
|
||
if ($user->sign_img !== null && $user->sign_img !== '') {
|
||
$data['is_sign'] = true;
|
||
}
|
||
|
||
// $ug = \app\common\model\UserGroupMembers::with(['group' => function($query) {
|
||
// $query->where('scenario_code', 'stock_orders');
|
||
// }])->where('user_id', $userId)->first();
|
||
// if ($ug !== null) {
|
||
// $data['is_office_clerk'] = true;
|
||
// }
|
||
|
||
if ($data['is_sign']) {
|
||
Redis::hSet('user:check:sign'.$userId, 'status', 1);
|
||
}
|
||
|
||
return $data;
|
||
}
|
||
|
||
// 审批待办数量
|
||
public function todoCounts(int $userId, int $num = 0)
|
||
{
|
||
$ly = [
|
||
1 => 186,
|
||
3 => 224,
|
||
];
|
||
|
||
if ($num === 0) {
|
||
$num = (new \app\common\utils\approval\service\ApprovalFlowService())->getInitTodoCount($userId);
|
||
}
|
||
|
||
$systemIds = \app\common\model\UserSystems::where('user_id', $userId)->pluck('system_id')->toArray();
|
||
if (count($systemIds) > 0) {
|
||
foreach ($systemIds as $systemId) {
|
||
if (isset($ly[$systemId])) {
|
||
$chain = \app\common\service\MenuBadgeService::buildBadgeChain(menuId: $ly[$systemId], leafCount: $num);
|
||
$this->pushWebsocket('approval.todo.counts', $userId, $chain);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} |