消息推送

This commit is contained in:
mzl
2025-08-20 18:12:07 +08:00
parent 78e28a99dc
commit 9f96b8d625
13 changed files with 451 additions and 41 deletions
@@ -0,0 +1,33 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\MessageNotificationsService;
class MessageNotificationsController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new MessageNotificationsService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
@@ -0,0 +1,33 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\MessageUserStatusService;
class MessageUserStatusController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new MessageUserStatusService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\MessageNotifications;
class MessageNotificationsLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = MessageNotifications::class;
}
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\MessageUserStatus;
class MessageUserStatusLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = MessageUserStatus::class;
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class MessageNotifications extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'message_notifications';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'msg_no', 'sender_id', 'sender_type', 'recipient', 'title', 'content', 'scene', 'created_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class MessageUserStatus extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'message_user_status';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'msg_id', 'uid', 'status', 'read_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
@@ -0,0 +1,95 @@
<?php
namespace app\common\service;
use app\common\validate\MessageNotificationsValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\MessageNotifications;
use app\common\model\User;
use support\Db;
class MessageNotificationsService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = MessageNotifications::class;
/**
* 验证器
*/
protected $validator = MessageNotificationsValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/**
* 创建一条消息
*/
public function createMessage(array $data): int
{
$msgId = Db::table('message_notifications')->insertGetId([
'msg_no' => generateMsgId(),
'sender_id' => $data['sender_id'],
'sender_type' => $data['sender_type'],
'title' => $data['title'],
'content' => $data['content'],
'scene' => $data['scene'],
'recipient' => $data['recipient'],
'created_at' => time(),
]);
return $msgId;
}
/**
* 批量插入用户消息状态
*/
public function addUserStatus(int $msgId, array $uids)
{
$rows = [];
foreach ($uids as $uid) {
$rows[] = [
'msg_id' => $msgId,
'uid' => $uid,
'status' => 0,
'read_at' => null
];
}
if ($rows) {
Db::table('message_user_status')->insert($rows);
}
}
/**
* 标记消息已读
*/
public function markAsRead(array|string $msgIds, int $uid)
{
$query = Db::table('message_user_status')->where('uid', $uid);
if (is_array($msgIds)) {
$query->whereIn('msg_id', $msgIds);
}
return $query->update([
'status' => 1,
'read_at' => time()
]);
}
/**
* 获取所有用户ID
*/
public function getAllUserIds(): array
{
return User::where('status', 1)->pluck('id')->toArray();
}
}
@@ -0,0 +1,31 @@
<?php
namespace app\common\service;
use app\common\validate\MessageUserStatusValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\MessageUserStatus;
class MessageUserStatusService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = MessageUserStatus::class;
/**
* 验证器
*/
protected $validator = MessageUserStatusValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
}
+35 -21
View File
@@ -4,6 +4,7 @@ namespace app\common\utils;
use GatewayWorker\Lib\Gateway;
use app\common\utils\WsResponse;
use app\common\service\MessageNotificationsService;
class WsEvents
{
@@ -72,17 +73,17 @@ class WsEvents
return;
}
// // 连接是否满足条件
// if ($payload['server']['HTTP_ORIGIN'] != 'https://wstool.js.org') {
// 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 ($payload['server']['HTTP_ORIGIN'] != 'https://wstool.js.org') {
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'])) {
@@ -136,11 +137,6 @@ class WsEvents
// ================= 消息通知事件 =================
/**
* 管理端主动推送通知
* data = [
* 'to_uids' => [uid1, uid2], // 可选,为空表示全员
* 'title' => '标题',
* 'content' => '内容',
* ]
*/
public function notifyPush(?string $traceId)
{
@@ -158,7 +154,20 @@ class WsEvents
return;
}
$msgId = rand(1000, 9999); // 生成消息ID,可用数据库存储
// 写消息
$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']) : '',
]);
// 写用户状态
$uids = !empty($toUids) ? $toUids : $msgService->getAllUserIds();
$msgService->addUserStatus($msgId, $uids);
$payload = WsResponse::success('notify.push', [
'msg_id' => $msgId,
@@ -181,18 +190,18 @@ class WsEvents
Gateway::sendToClient($this->clientId, WsResponse::success(
'notify.push.result',
['msg_id' => $msgId],
$traceId
$traceId,
'消息发送成功'
));
}
/**
* 客户端标记通知已读
* data = ['msg_ids' => [1,2,3]]
*/
public function notifyRead(?string $traceId)
{
$msgIds = $this->eventData['msg_ids'] ?? [];
if (empty($msgIds)) {
$msgIds = $this->eventData['msg_ids'];
if (!is_array($msgIds) && $msgIds !== 'all') {
Gateway::sendToClient($this->clientId, WsResponse::error(
'notify.read',
400,
@@ -201,6 +210,11 @@ class WsEvents
));
return;
}
// 更新消息为已读
$msgService = new MessageNotificationsService();
$msgService->markAsRead($msgIds, Gateway::getUidByClientId($this->clientId));
Gateway::sendToClient($this->clientId, WsResponse::success(
'notify.read.result',
['msg_ids' => $msgIds],
@@ -0,0 +1,29 @@
<?php
namespace app\common\validate;
use think\Validate;
class MessageNotificationsValidate extends Validate
{
// 规则
protected $rule = [
];
// 提示
protected $message = [
];
// 场景
protected $scene = [
'store' => [
],
'update' => [
]
];
}
@@ -0,0 +1,29 @@
<?php
namespace app\common\validate;
use think\Validate;
class MessageUserStatusValidate extends Validate
{
// 规则
protected $rule = [
];
// 提示
protected $message = [
];
// 场景
protected $scene = [
'store' => [
],
'update' => [
]
];
}
+10 -20
View File
@@ -77,25 +77,25 @@ class IndexController
$options = [
'controller' => [
'module' => 'common',
'name' => 'resources'
'name' => 'message_notifications'
],
'model' => [
'module' => 'common',
'name' => 'resources'
'name' => 'message_notifications'
],
'service' => [
'module' => 'common',
'name' => 'resources'
'name' => 'message_notifications'
],
'logic' => [
'module' => 'common',
'name' => 'resources'
'name' => 'message_notifications'
],
'validate' => [
'module' => 'common',
'name' => 'resources'
'name' => 'message_notifications'
],
'table' => 'resources'
'table' => 'message_notifications'
];
$result = (new Generate($options))->doGenerate();
@@ -106,21 +106,11 @@ class IndexController
public function test()
{
// $data = [
// 'code' => 'GD20250703-1DC80',
// 'category' => '账号类问题',
// 'content' => '这是一条测试工单内容。'
// ];
// \app\common\utils\email\MailSend::send('mzl@hnxinyouhe.com', '【XYH】新的工单提醒', replaceTpl($data, 'work_order'));
$service = new \app\common\service\MessageNotificationsService();
$udis = $service->getAllUserIds();
// $options = [
// 'corp_id' => 3,
// 'user_id' => 666
// ];
// \Webman\RedisQueue\Redis::send('perm-agt', $options);
var_dump($udis);
$res = Cache::delete("user_resources_74_fdesk");
return 22;
return json($udis);
}
}
+24
View File
@@ -283,4 +283,28 @@ if (!function_exists('normalizeRichText')) {
// 替换所有空白实体为普通空格
return preg_replace('/\s+/u', ' ', $result);
}
}
/**
* 生成消息编号(唯一业务标识)
* 格式:年月日时分秒 + 微秒 + 随机数
* 例如:20250820175530987654321
*
* @return string
*/
if (!function_exists('generateMsgId')) {
function generateMsgId(): string
{
// 时间部分(精确到微秒)
$time = microtime(true);
$micro = sprintf("%06d", ($time - floor($time)) * 1e6);
$date = date('YmdHis', (int)$time);
// 随机部分(3位)
$rand = mt_rand(100, 999);
// 拼接:日期时间 + 微秒 + 随机数
return $date . $micro . $rand;
}
}