Merge commit 'ed96e0f8589042fb8e9d6cfee713c5150612bb34'
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
namespace app\common\controller;
|
||||
|
||||
use app\common\controller\BaseController;
|
||||
use app\common\traits\crud\CrudControllerTrait;
|
||||
use app\common\service\MessageNotificationsService;
|
||||
use app\common\model\MessageNotifications;
|
||||
use support\Request;
|
||||
|
||||
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']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function showFormat($data)
|
||||
{
|
||||
if (isset($data['created_at']))
|
||||
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
public function push(Request $request)
|
||||
{
|
||||
try {
|
||||
$data = $this->validate($this->service->getValidator(), $request->post(), 'store');
|
||||
$res = $this->service->servicePush($request, $data);
|
||||
return $this->success($res, '推送成功');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
namespace app\common\controller;
|
||||
|
||||
use app\common\controller\BaseController;
|
||||
use app\common\traits\crud\CrudControllerTrait;
|
||||
use app\common\service\MessageUserStatusService;
|
||||
use support\Request;
|
||||
|
||||
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['read_at']) && $data['read_at'] !== null) {
|
||||
$data['read_at'] = date('Y-m-d H:i:s', $data['read_at']);
|
||||
} else {
|
||||
$data['read_at'] = ' - ';
|
||||
}
|
||||
|
||||
if (isset($data['message']))
|
||||
$data['message']['created_at'] = date('Y-m-d H:i:s', $data['message']['created_at']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function showFormat($data)
|
||||
{
|
||||
if (isset($data['read_at']) && $data['read_at'] !== null) {
|
||||
$data['read_at'] = date('Y-m-d H:i:s', $data['read_at']);
|
||||
} else {
|
||||
$data['read_at'] = ' - ';
|
||||
}
|
||||
|
||||
if (isset($data['message']))
|
||||
$data['message']['created_at'] = date('Y-m-d H:i:s', $data['message']['created_at']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function unReadFormat($data)
|
||||
{
|
||||
foreach ($data as &$item) {
|
||||
if (isset($item['read_at']) && $item['read_at'] !== null) {
|
||||
$item['read_at'] = date('Y-m-d H:i:s', $item['read_at']);
|
||||
} else {
|
||||
$item['read_at'] = ' - ';
|
||||
}
|
||||
|
||||
if (isset($item['message'])) {
|
||||
$item['message']['created_at'] = date('Y-m-d H:i:s', $item['message']['created_at']);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function read(Request $request)
|
||||
{
|
||||
try {
|
||||
$data = $this->validate($this->service->getValidator(), $request->post(), 'read');
|
||||
$this->service->serviceRead($request, $data);
|
||||
return $this->success([], '操作成功');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function listUnRead(Request $request)
|
||||
{
|
||||
try {
|
||||
$raw = $this->service->serviceListUnRead($request);
|
||||
$data = $this->transformData($raw, method_exists($this, 'unReadFormat')
|
||||
? [$this, 'unReadFormat']
|
||||
: null);
|
||||
$data = [
|
||||
'list' => $data,
|
||||
'counts' => count($data)
|
||||
];
|
||||
return $this->success($data);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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;
|
||||
|
||||
|
||||
public function logicDeleteBy($config)
|
||||
{
|
||||
$msg = $this->logicShow($config);
|
||||
if ($msg) {
|
||||
// 删除关联的用户状态记录
|
||||
$msg->userStatuses()->delete();
|
||||
// 删除消息本身
|
||||
$msg->delete();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?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 = false;
|
||||
|
||||
// const UPDATED_AT = false;
|
||||
|
||||
// protected $dateFormat = 'U';
|
||||
|
||||
/**
|
||||
* 允许自动填充模型字段
|
||||
*/
|
||||
protected $fillable = ['id', 'msg_no', 'sender_id', 'sender_type', 'recipient', 'title', 'content', 'scene', 'push_method', 'created_at'];
|
||||
|
||||
/**
|
||||
* 不允许自动填充模型字段
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
// 消息创建
|
||||
const SCENE = [
|
||||
self::SCENE0 => '系统消息',
|
||||
self::SCENE1 => '系统通知'
|
||||
];
|
||||
const SCENE0 = 0;
|
||||
const SCENE1 = 1;
|
||||
|
||||
// 推送方式
|
||||
const PUSH_METHOD = [
|
||||
self::PUSH_METHOD0 => '系统推送',
|
||||
self::PUSH_METHOD1 => '手动推送'
|
||||
];
|
||||
const PUSH_METHOD0 = 0;
|
||||
const PUSH_METHOD1 = 1;
|
||||
|
||||
|
||||
/**
|
||||
* 关联用户消息状态 (一对多)
|
||||
*/
|
||||
public function userStatuses()
|
||||
{
|
||||
return $this->hasMany(MessageUserStatus::class, 'msg_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已读用户状态
|
||||
*/
|
||||
public function readUsers()
|
||||
{
|
||||
return $this->hasMany(MessageUserStatus::class, 'msg_id', 'id')
|
||||
->where('status', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未读用户状态
|
||||
*/
|
||||
public function unreadUsers()
|
||||
{
|
||||
return $this->hasMany(MessageUserStatus::class, 'msg_id', 'id')
|
||||
->where('status', 0);
|
||||
}
|
||||
|
||||
public function sender()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'sender_id', 'id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?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 = false;
|
||||
|
||||
|
||||
// protected $dateFormat = 'U';
|
||||
|
||||
/**
|
||||
* 允许自动填充模型字段
|
||||
*/
|
||||
protected $fillable = ['id', 'msg_id', 'uid', 'status', 'read_at'];
|
||||
|
||||
/**
|
||||
* 不允许自动填充模型字段
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
|
||||
/**
|
||||
* 关联消息 (多对一)
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return $this->belongsTo(MessageNotifications::class, 'msg_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联用户 (假设你有 User 模型)
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'uid', 'id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
use app\common\logic\MessageNotificationsLogic;
|
||||
use app\common\validate\MessageNotificationsValidate;
|
||||
use app\common\traits\crud\CrudServiceTrait;
|
||||
use app\common\model\MessageNotifications;
|
||||
use app\common\model\MessageUserStatus;
|
||||
use app\common\model\User;
|
||||
use support\Db;
|
||||
use support\Request;
|
||||
|
||||
class MessageNotificationsService
|
||||
{
|
||||
|
||||
use CrudServiceTrait;
|
||||
|
||||
/**
|
||||
* 指定当前要操作的模型
|
||||
*/
|
||||
protected string $modelClass = MessageNotifications::class;
|
||||
|
||||
/**
|
||||
* 验证器
|
||||
*/
|
||||
protected $validator = MessageNotificationsValidate::class;
|
||||
|
||||
/**
|
||||
* 获取验证器
|
||||
*/
|
||||
public function getValidator()
|
||||
{
|
||||
return new $this->validator;
|
||||
}
|
||||
|
||||
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
|
||||
{
|
||||
$p = $request->all();
|
||||
|
||||
$config = [];
|
||||
$config['model'] = $this->modelClass;
|
||||
switch ($scene) {
|
||||
case 'index':
|
||||
$config['order']['id'] = 'desc';
|
||||
break;
|
||||
case 'show':
|
||||
$config['where']['id'] = $p['id'];
|
||||
break;
|
||||
case 'store':
|
||||
break;
|
||||
case 'update':
|
||||
$config['where']['id'] = $p['id'];
|
||||
break;
|
||||
case 'destroy':
|
||||
$config['where']['id'] = $p['id'];
|
||||
break;
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
|
||||
public function serviceDestroy(Request $request)
|
||||
{
|
||||
$config = $this->buildQueryConfig($request, 'destroy');
|
||||
$deleted = (new MessageNotificationsLogic())->logicDeleteBy($config);
|
||||
return $this->postProcess('Destroy', $deleted);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加前置处理
|
||||
*/
|
||||
public function servicePush(Request $request, $data)
|
||||
{
|
||||
$pushData = [
|
||||
'sender_id' => $request->user->uid,
|
||||
'sender_type' => 'admin',
|
||||
'title' => $data['title'],
|
||||
'content' => $data['content'],
|
||||
'scene' => 1,
|
||||
'recipient' => $data['recipient'],
|
||||
'push_method' => 1
|
||||
];
|
||||
$msgId = (new MessageNotificationsService())->pushNotice($pushData);
|
||||
return ['msg_id' => $msgId];
|
||||
}
|
||||
|
||||
public function afterServiceList($data)
|
||||
{
|
||||
$data->each(function (&$item) {
|
||||
$temp = explode(',', $item->recipient);
|
||||
$item->recipient_list = User::whereIn('id', $temp)->pluck('name')->toArray();
|
||||
$item->scene_name = MessageNotifications::SCENE[$item->scene];
|
||||
$item->push_method_name = MessageNotifications::PUSH_METHOD[$item->push_method];
|
||||
});
|
||||
unset($item);
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function afterServiceShow($data)
|
||||
{
|
||||
$temp = explode(',', $data->recipient);
|
||||
$data->recipient_list = User::whereIn('id', $temp)->pluck('name')->toArray();
|
||||
$data->scene_name = MessageNotifications::SCENE[$data->scene];
|
||||
$data->push_method_name = MessageNotifications::PUSH_METHOD[$data->push_method];
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ====== ====== ====== 通知操作的处理 ====== ====== ======
|
||||
/**
|
||||
* 推送消息
|
||||
*/
|
||||
public function pushNotice(array $data = [], string $scene = "Default"): int
|
||||
{
|
||||
// 拼装数据(可能需要处理 消息标题、内容等等数据,可自行处理)
|
||||
$pushData = $this->assemblyProcess($scene, $data);
|
||||
// 写入消息
|
||||
$msgId = $this->insertMessage($pushData);
|
||||
// 推送
|
||||
$this->pushWs($msgId, $pushData);
|
||||
return $msgId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行推送
|
||||
*/
|
||||
public function pushWs(int $msgId, array $pushData)
|
||||
{
|
||||
foreach ($pushData['recipient'] as $userId) {
|
||||
// 查询消息数据
|
||||
$config['model'] = MessageUserStatus::class;
|
||||
$config['order']['id'] = 'desc';
|
||||
$config['with']['message'] = ['sender_id', 'sender_type', 'msg_no', 'title', 'content', 'scene', 'created_at'];
|
||||
$config['where']['uid'] = $userId;
|
||||
$config['where']['msg_id'] = $msgId;
|
||||
$config['where']['status'] = 0;
|
||||
$wsData = (new MessageUserStatusService())->logicShow($config)->toArray();
|
||||
|
||||
// 组装数据
|
||||
if (isset($wsData['message']) && $wsData['message']) {
|
||||
$wsData['message']['sender_name'] = User::where('id', $wsData['message']['sender_id'])->value('name') ?: '系统';
|
||||
$wsData['message']['scene_name'] = MessageNotifications::SCENE[$wsData['message']['scene']];
|
||||
$wsData['message']['created_at'] = date('Y-m-d H:i:s', $wsData['message']['created_at']);
|
||||
}
|
||||
|
||||
// 获取未读消息数
|
||||
$counts = MessageUserStatus::where('uid', $userId)->where('status', 0)->count('id');
|
||||
$data = [
|
||||
'notice_data' => $wsData,
|
||||
'unread_counts' => $counts
|
||||
];
|
||||
|
||||
// 执行推送
|
||||
(new \app\common\utils\WsEvents())->pushWsNotice($data, $userId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入消息
|
||||
* @param array $data
|
||||
* @return int
|
||||
*/
|
||||
public function insertMessage(array $data): int
|
||||
{
|
||||
// 写消息
|
||||
$msgId = $this->createMessage([
|
||||
'sender_id' => $data['sender_id'],
|
||||
'sender_type' => $data['sender_type'],
|
||||
'title' => $data['title'],
|
||||
'content' => $data['content'],
|
||||
'scene' => $data['scene'],
|
||||
'recipient' => implode(',', $data['recipient']),
|
||||
'push_method' => $data['push_method'],
|
||||
]);
|
||||
// 写用户状态
|
||||
$this->addUserStatus($msgId, $data['recipient']);
|
||||
return $msgId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据拼装方法调用
|
||||
*/
|
||||
protected function assemblyProcess(string $op, mixed $data): mixed
|
||||
{
|
||||
$hook = "assembly{$op}";
|
||||
return method_exists($this, $hook)
|
||||
? $this->{$hook}($data)
|
||||
: $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认数据拼装
|
||||
*/
|
||||
public function assemblyDefault(array $data)
|
||||
{
|
||||
return [
|
||||
'sender_id' => isset($data['sender_id']) && $data['sender_id'] ? $data['sender_id'] : 0,
|
||||
'sender_type' => isset($data['sender_type']) && $data['sender_type'] ? $data['sender_type'] : 'system',
|
||||
'title' => isset($data['title']) && $data['title'] ? $data['title'] : '系统通知',
|
||||
'content' => isset($data['content']) && $data['content'] ? $data['content'] : '系统通知-默认',
|
||||
'scene' => isset($data['scene']) && $data['scene'] ? $data['scene'] : 1,
|
||||
'recipient' => isset($data['recipient']) && count($data['recipient']) > 0 ? $data['recipient'] : $this->getAllUserIds(),
|
||||
'push_method' => isset($data['push_method']) && $data['push_method'] ? $data['push_method'] : 0,
|
||||
];
|
||||
}
|
||||
|
||||
// *** *** 可以扩展数据组拼装方法 *** ***
|
||||
|
||||
// ====== ====== ====== 通知操作的基础方法 ====== ====== ======
|
||||
|
||||
/**
|
||||
* 创建一条消息
|
||||
*/
|
||||
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'],
|
||||
'push_method' => $data['push_method'],
|
||||
'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)->where('status', 0);
|
||||
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,136 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
use app\common\model\MessageNotifications;
|
||||
use app\common\validate\MessageUserStatusValidate;
|
||||
use app\common\traits\crud\CrudServiceTrait;
|
||||
use app\common\model\MessageUserStatus;
|
||||
use support\Request;
|
||||
use app\common\model\User;
|
||||
|
||||
class MessageUserStatusService
|
||||
{
|
||||
|
||||
use CrudServiceTrait;
|
||||
|
||||
/**
|
||||
* 指定当前要操作的模型
|
||||
*/
|
||||
protected string $modelClass = MessageUserStatus::class;
|
||||
|
||||
/**
|
||||
* 验证器
|
||||
*/
|
||||
protected $validator = MessageUserStatusValidate::class;
|
||||
|
||||
/**
|
||||
* 获取验证器
|
||||
*/
|
||||
public function getValidator()
|
||||
{
|
||||
return new $this->validator;
|
||||
}
|
||||
|
||||
|
||||
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
|
||||
{
|
||||
$p = $request->all();
|
||||
|
||||
$config = [];
|
||||
$config['model'] = $this->modelClass;
|
||||
switch ($scene) {
|
||||
case 'unread':
|
||||
$config['order']['id'] = 'desc';
|
||||
$config['with']['message'] = ['sender_id', 'sender_type', 'msg_no', 'title', 'content', 'scene', 'created_at'];
|
||||
$config['where']['uid'] = $request->user->uid;
|
||||
$config['where']['status'] = 0;
|
||||
break;
|
||||
case 'index':
|
||||
$config['order']['id'] = 'desc';
|
||||
$config['with']['message'] = ['sender_id', 'sender_type', 'msg_no', 'title', 'content', 'scene', 'created_at'];
|
||||
$config['where']['uid'] = $request->user->uid;
|
||||
break;
|
||||
case 'show':
|
||||
$config['where']['id'] = $p['id'];
|
||||
$config['with']['message'] = ['sender_id', 'sender_type', 'msg_no', 'title', 'content', 'scene', 'created_at'];
|
||||
$config['where']['uid'] = $request->user->uid;
|
||||
break;
|
||||
case 'store':
|
||||
break;
|
||||
case 'update':
|
||||
case 'read':
|
||||
$config['where']['id'] = $p['id'];
|
||||
$config['where']['msg_id'] = $p['msg_id'];
|
||||
$config['where']['uid'] = $request->user->uid;
|
||||
break;
|
||||
case 'destroy':
|
||||
$config['where']['id'] = $p['id'];
|
||||
$config['where']['uid'] = $request->user->uid;
|
||||
break;
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
public function serviceListUnRead(Request $request)
|
||||
{
|
||||
$config = $this->buildQueryConfig($request, 'unread');
|
||||
$result = $this->logicListAll($config);
|
||||
return $this->postProcess('ListUnRead', $result);
|
||||
}
|
||||
|
||||
public function serviceRead(Request $request, array $data = [])
|
||||
{
|
||||
return $this->noticeRead($request->post('msg_ids'), $request->user->uid);
|
||||
}
|
||||
|
||||
public function noticeRead(array|string $msgIds, int $uid): bool
|
||||
{
|
||||
$model = MessageUserStatus::where('uid', $uid)->where('status', 0);
|
||||
if (is_array($msgIds)) {
|
||||
$model->whereIn('msg_id', $msgIds);
|
||||
}
|
||||
$udata['read_at'] = time();
|
||||
$udata['status'] = 1;
|
||||
return $model->update($udata);
|
||||
}
|
||||
|
||||
|
||||
protected function afterServiceList($res)
|
||||
{
|
||||
$res->each(function (&$item) {
|
||||
if (isset($item->message)) {
|
||||
$item->message->sender_name = User::where('id', $item->message->sender_id)->value('name') ?: '系统';
|
||||
$item->message->scene_name = MessageNotifications::SCENE[$item->message->scene];
|
||||
}
|
||||
});
|
||||
unset($item);
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function afterServiceShow($res)
|
||||
{
|
||||
if (isset($res->message)) {
|
||||
$res->message->sender_name = User::where('id', $res->message->sender_id)->value('name') ?: '系统';
|
||||
$res->message->scene_name = MessageNotifications::SCENE[$res->message->scene];
|
||||
}
|
||||
// 标记已读
|
||||
if ($res->status == 0) {
|
||||
$this->noticeRead([$res->msg_id], $res->uid);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function afterServiceListUnRead($res)
|
||||
{
|
||||
$res->each(function (&$item) {
|
||||
if (isset($item->message)) {
|
||||
$item->message->sender_name = User::where('id', $item->message->sender_id)->value('name') ?: '系统';
|
||||
$item->message->scene_name = MessageNotifications::SCENE[$item->message->scene];
|
||||
}
|
||||
});
|
||||
unset($item);
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\utils;
|
||||
|
||||
use GatewayWorker\Lib\Gateway;
|
||||
use app\common\utils\WsResponse;
|
||||
use app\common\service\MessageNotificationsService;
|
||||
|
||||
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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ================= 系统事件 =================
|
||||
/**
|
||||
* 认证
|
||||
*/
|
||||
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 ($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'])) {
|
||||
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);
|
||||
|
||||
// 响应
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统心跳
|
||||
*
|
||||
* @param string|null $traceId
|
||||
*/
|
||||
public function systemPing(?string $traceId)
|
||||
{
|
||||
Gateway::sendToClient($this->clientId, WsResponse::success(
|
||||
'system.pong',
|
||||
['time' => time()],
|
||||
$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
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送消息通知
|
||||
*/
|
||||
public function pushWsNotice(array $wsData, int $userId)
|
||||
{
|
||||
Gateway::sendToUid($userId, WsResponse::success('notify.receive', $wsData));
|
||||
}
|
||||
|
||||
// ================= 辅助方法 =================
|
||||
protected function eventToMethod(string $event): string
|
||||
{
|
||||
$parts = explode('.', $event);
|
||||
$method = array_shift($parts);
|
||||
foreach ($parts as $part) {
|
||||
$method .= ucfirst($part);
|
||||
}
|
||||
return $method;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace app\common\utils;
|
||||
|
||||
class WsResponse
|
||||
{
|
||||
/**
|
||||
* 成功返回
|
||||
*
|
||||
* @param string $event 事件名称
|
||||
* @param array $data 返回数据
|
||||
* @param string|null $traceId 请求追踪ID,可选,默认 null
|
||||
* @param int $code 错误码
|
||||
* @param string $message 错误信息
|
||||
* @return string JSON
|
||||
*/
|
||||
public static function success(string $event, array $data = [], ?string $traceId = null, string $message = 'ok', int $code = 0): string
|
||||
{
|
||||
return json_encode([
|
||||
'event' => $event,
|
||||
'code' => $code,
|
||||
'message' => $message,
|
||||
'data' => $data,
|
||||
'trace_id' => $traceId, // 如果没有传就是 null
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误返回
|
||||
*
|
||||
* @param string $event 事件名称
|
||||
* @param int $code 错误码
|
||||
* @param string $message 错误信息
|
||||
* @param string|null $traceId 请求追踪ID,可选,默认 null
|
||||
* @param array $data 附加数据(可选)
|
||||
* @return string JSON
|
||||
*/
|
||||
public static function error(string $event, int $code, string $message, ?string $traceId = null, array $data = []): string
|
||||
{
|
||||
return json_encode([
|
||||
'event' => $event,
|
||||
'code' => $code,
|
||||
'message' => $message,
|
||||
'data' => $data,
|
||||
'trace_id' => $traceId, // 如果没有传就是 null
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
}
|
||||
@@ -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' => [
|
||||
|
||||
]
|
||||
];
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use app\common\model\InquiryLog;
|
||||
use app\common\model\User;
|
||||
use app\common\model\UserRoles;
|
||||
use app\common\model\UserSystems;
|
||||
use app\common\service\MessageNotificationsService;
|
||||
use app\common\traits\DataPermission;
|
||||
use app\common\utils\email\MailSend;
|
||||
use app\common\utils\scaffold\Generate;
|
||||
@@ -77,25 +78,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();
|
||||
@@ -107,20 +108,35 @@ class IndexController
|
||||
{
|
||||
|
||||
// $data = [
|
||||
// 'code' => 'GD20250703-1DC80',
|
||||
// 'category' => '账号类问题',
|
||||
// 'content' => '这是一条测试工单内容。'
|
||||
// 'sender_id' => 0,
|
||||
// 'sender_type' => 'system',
|
||||
// 'title' => '测试消息 - 标题',
|
||||
// 'content' => '测试消息 - 内容',
|
||||
// 'scene' => 1,
|
||||
// 'recipient' => [1],
|
||||
// ];
|
||||
// \app\common\utils\email\MailSend::send('mzl@hnxinyouhe.com', '【XYH】新的工单提醒', replaceTpl($data, 'work_order'));
|
||||
|
||||
// $options = [
|
||||
// 'corp_id' => 3,
|
||||
// 'user_id' => 666
|
||||
// ];
|
||||
// \Webman\RedisQueue\Redis::send('perm-agt', $options);
|
||||
$data['recipient'] = [1, 74];
|
||||
// // 同步推送
|
||||
// (new MessageNotificationsService())->pushNotice($data);
|
||||
|
||||
$res = Cache::delete("user_resources_74_fdesk");
|
||||
|
||||
return 22;
|
||||
|
||||
// 异步推送
|
||||
\Webman\RedisQueue\Redis::send('notice', $data);
|
||||
|
||||
|
||||
return 'success';
|
||||
|
||||
// return json($data);
|
||||
|
||||
// $phone = '';
|
||||
// $email = '';
|
||||
|
||||
// $userByPhone = User::where('corp_id', 2)->where('phone', $phone)->where('phone', '!=', '')->first();
|
||||
// $userByEmail = User::where('corp_id', 2)->where('email', $email)->where('email', '!=', '')->first();
|
||||
// $user = $userByPhone ?: $userByEmail;
|
||||
|
||||
// return json($user);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace app\crmAdmin\controller;
|
||||
|
||||
use app\common\model\Corp;
|
||||
use app\crm\model\Customer;
|
||||
use app\crm\utils\Util;
|
||||
use app\crmAdmin\service\CustomerService;
|
||||
use support\Request;
|
||||
|
||||
@@ -148,19 +149,18 @@ class CustomerController extends BaseController
|
||||
}
|
||||
|
||||
$customer_id = explode(',', $param['customer_id']);
|
||||
$customer = Customer::whereIn('id',$customer_id)->withTrashed()->first();
|
||||
if(empty($customer)){
|
||||
$query = Customer::whereIn('id',$customer_id)->withTrashed();
|
||||
if(!$query->exists()){
|
||||
return $this->error( trans('fail'));
|
||||
}
|
||||
if($param['scene'] == 'list'){ //管理端客户列表移除
|
||||
$customer->status = 4;
|
||||
|
||||
}else{ //公海软删除
|
||||
$customer->status = 3;
|
||||
|
||||
$scene = $param['scene'] ?? '';
|
||||
if ($scene === 'list') { //管理端客户列表移除
|
||||
$res = $query->update(['status' => 4, 'deleted_at' => time()]);
|
||||
} elseif ($scene === 'force') { //强删除
|
||||
$res = $query->forceDelete();
|
||||
} else { //公海软删除
|
||||
$res = $query->update(['status' => 3, 'deleted_at' => time()]);
|
||||
}
|
||||
$res = $customer->save();
|
||||
$customer->delete();
|
||||
|
||||
return $this->success($res);
|
||||
} catch (\Throwable $th) {
|
||||
@@ -168,6 +168,34 @@ class CustomerController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 客户恢复
|
||||
* @param Request $request
|
||||
* @return \support\Response
|
||||
*/
|
||||
public function restore(Request $request)
|
||||
{
|
||||
try {
|
||||
$id = $request->input('id', '');
|
||||
if (Util::isEmpty($id)) {
|
||||
return $this->error(trans('id_empty'));
|
||||
}
|
||||
$ids = explode(',', $id);
|
||||
$query = Customer::withTrashed()->whereIn('id', $ids);
|
||||
if (!$query->exists()) {
|
||||
return $this->error(trans('fail'));
|
||||
}
|
||||
// 恢复软删除(清空 deleted_at)
|
||||
$query->restore();
|
||||
// 恢复为在库状态,放入公海
|
||||
$res = Customer::whereIn('id', $ids)->update(['status' => 1, 'user_id' => 0]);
|
||||
return $this->success($res, trans('success'));
|
||||
} catch (\Throwable $th) {
|
||||
return $this->error($th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户详情
|
||||
* @param Request $request
|
||||
|
||||
@@ -51,12 +51,14 @@ class CustomerService
|
||||
$follow_last = $params['follow_last'] ?? '';
|
||||
$tagId = $params['tag_id'] ?? [];
|
||||
$user_id = $params['user_id'] ?? '';
|
||||
$seas = $params['seas'] ?? '';
|
||||
$seas = $params['seas'] ?? ''; //recyclea:管理端回收站,audit:待审核列表,seas:公海
|
||||
$scene = $params['scene'] ?? '';
|
||||
$where = [];
|
||||
|
||||
if ($scene == 'audit') {
|
||||
$where[] = ['status', '=', 2];
|
||||
} elseif ($scene == 'recyclea') {
|
||||
$where[] = ['status', '>', 2];
|
||||
} else {
|
||||
$where[] = ['status', '=', 1];
|
||||
}
|
||||
@@ -121,8 +123,8 @@ class CustomerService
|
||||
if ($recycle == 'recycle' || $scene == 'recycle' || $scene == 'audit') {
|
||||
$query->onlyTrashed();
|
||||
}
|
||||
if ($seas == 'seas' || $scene == 'seas') {
|
||||
$query->withTrashed();
|
||||
if ($seas == 'seas' || $scene == 'recyclea') {
|
||||
$query = $query->withTrashed();
|
||||
}
|
||||
|
||||
if (!$export) {
|
||||
@@ -153,6 +155,14 @@ class CustomerService
|
||||
}
|
||||
$item['audit'] = $audit;
|
||||
}
|
||||
if ($scene == 'recyclea' && $item['status'] == 3) { //回收站删除来源
|
||||
$item['del_source'] = '公海';
|
||||
}elseif ($scene == 'recyclea' && $item['status'] == 4) {
|
||||
$item['del_source'] = '管理端';
|
||||
}elseif ($scene == 'recyclea' && $item['status'] == 5) {
|
||||
$source = (!empty($item['user_id'])) ? (':' . User::where('id',$item['user_id'])->value('name')) : '';
|
||||
$item['del_source'] = '业务员' . $source;
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($item);
|
||||
|
||||
@@ -284,3 +284,27 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace app\queue\redis;
|
||||
|
||||
use support\exception\BusinessException;
|
||||
use Webman\RedisQueue\Consumer;
|
||||
|
||||
class Notice implements Consumer
|
||||
{
|
||||
// 要消费的队列名
|
||||
public $queue = 'notice';
|
||||
|
||||
// 连接名,对应 plugin/webman/redis-queue/redis.php 里的连接`
|
||||
public $connection = 'default';
|
||||
|
||||
// 消费
|
||||
public function consume($data)
|
||||
{
|
||||
try {
|
||||
(new \app\common\service\MessageNotificationsService())->pushNotice($data);
|
||||
return true;
|
||||
} catch (\Throwable $th) {
|
||||
var_dump('4 :'.$th->getFile()."|".$th->getLine()."|".$th->getMessage());
|
||||
throw new BusinessException($th->getFile()."|".$th->getLine()."|".$th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 消费失败回调
|
||||
/*
|
||||
$package = [
|
||||
'id' => 1357277951, // 消息ID
|
||||
'time' => 1709170510, // 消息时间
|
||||
'delay' => 0, // 延迟时间
|
||||
'attempts' => 2, // 消费次数
|
||||
'queue' => 'send-mail', // 队列名
|
||||
'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
|
||||
'max_attempts' => 5, // 最大重试次数
|
||||
'error' => '错误信息' // 错误信息
|
||||
]
|
||||
*/
|
||||
public function onConsumeFailure(\Throwable $e, $package)
|
||||
{
|
||||
echo "consume failure\n";
|
||||
echo $e->getMessage() . "\n";
|
||||
// 无需反序列化
|
||||
var_export($package);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -45,7 +45,8 @@
|
||||
"symfony/filesystem": "^7.3",
|
||||
"workerman/crontab": "^1.0",
|
||||
"phpoffice/phpspreadsheet": "^4.5",
|
||||
"phpmailer/phpmailer": "^6.10"
|
||||
"phpmailer/phpmailer": "^6.10",
|
||||
"webman/gateway-worker": "^1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-event": "For better performance. "
|
||||
|
||||
Generated
+88
-1
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "505fb3d97c338ea53b787196fcc3daaa",
|
||||
"content-hash": "7c70d8c8bda8a5098b8baed463db8839",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aliyuncs/oss-sdk-php",
|
||||
@@ -6634,6 +6634,39 @@
|
||||
},
|
||||
"time": "2025-04-04T13:23:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "webman/gateway-worker",
|
||||
"version": "v1.0.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/webman-php/gateway-worker.git",
|
||||
"reference": "6780c1c788c273a24699caee84feb14224234000"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/webman-php/gateway-worker/zipball/6780c1c788c273a24699caee84feb14224234000",
|
||||
"reference": "6780c1c788c273a24699caee84feb14224234000",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"workerman/gateway-worker": ">=3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Webman\\GatewayWorker\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/webman-php/gateway-worker/issues",
|
||||
"source": "https://github.com/webman-php/gateway-worker/tree/v1.0.9"
|
||||
},
|
||||
"time": "2024-04-30T01:05:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "webman/redis",
|
||||
"version": "v2.1.3",
|
||||
@@ -6793,6 +6826,60 @@
|
||||
},
|
||||
"time": "2025-01-15T07:20:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "workerman/gateway-worker",
|
||||
"version": "v4.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/walkor/GatewayWorker.git",
|
||||
"reference": "25e4430982caa98f82e9187d092ce34b2fddfc90"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/walkor/GatewayWorker/zipball/25e4430982caa98f82e9187d092ce34b2fddfc90",
|
||||
"reference": "25e4430982caa98f82e9187d092ce34b2fddfc90",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"workerman/workerman": "^4.0.30 || ^5.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GatewayWorker\\": "./src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"homepage": "http://www.workerman.net",
|
||||
"keywords": [
|
||||
"communication",
|
||||
"distributed"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/walkor/GatewayWorker/issues",
|
||||
"source": "https://github.com/walkor/GatewayWorker/tree/v4.0.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://opencollective.com/walkor",
|
||||
"type": "open_collective"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/walkor",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2025-06-27T01:30:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "workerman/redis",
|
||||
"version": "v2.0.4",
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
return [
|
||||
'enable' => true,
|
||||
];
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Webman\GatewayWorker\Gateway;
|
||||
use Webman\GatewayWorker\BusinessWorker;
|
||||
use Webman\GatewayWorker\Register;
|
||||
|
||||
return [
|
||||
'gateway' => [
|
||||
'handler' => Gateway::class,
|
||||
'listen' => 'websocket://0.0.0.0:5151',
|
||||
'count' => 2,
|
||||
'reloadable' => false,
|
||||
'constructor' => ['config' => [
|
||||
'lanIp' => '127.0.0.1',
|
||||
'startPort' => 2300,
|
||||
'pingInterval' => 55,
|
||||
'pingNotResponseLimit' => 1,
|
||||
'pingData' => '',
|
||||
'registerAddress' => '127.0.0.1:1236',
|
||||
'onConnect' => function(){},
|
||||
]]
|
||||
],
|
||||
'worker' => [
|
||||
'handler' => BusinessWorker::class,
|
||||
'count' => cpu_count()*2,
|
||||
'constructor' => ['config' => [
|
||||
'eventHandler' => plugin\webman\gateway\Events::class,
|
||||
'name' => 'ChatBusinessWorker',
|
||||
'registerAddress' => '127.0.0.1:1236',
|
||||
]]
|
||||
],
|
||||
'register' => [
|
||||
'handler' => Register::class,
|
||||
'listen' => 'text://127.0.0.1:1236',
|
||||
'count' => 1, // Must be 1
|
||||
'reloadable' => false,
|
||||
'constructor' => []
|
||||
],
|
||||
];
|
||||
@@ -64,10 +64,29 @@ Route::group('/common', function () {
|
||||
Route::any('/cache/clear', [app\common\controller\PresetController::class, 'cacheClear']);
|
||||
Route::any('/menus', [app\common\controller\PresetController::class, 'menus']);
|
||||
});
|
||||
|
||||
|
||||
// 消息通知
|
||||
Route::group('/notice', function () {
|
||||
Route::any('/list', [app\common\controller\MessageUserStatusController::class, 'index']);
|
||||
Route::any('/show', [app\common\controller\MessageUserStatusController::class, 'show']);
|
||||
Route::any('/read', [app\common\controller\MessageUserStatusController::class, 'read']);
|
||||
Route::any('/destroy', [app\common\controller\MessageUserStatusController::class, 'destroy']);
|
||||
|
||||
Route::any('/unread', [app\common\controller\MessageUserStatusController::class, 'listUnRead']);
|
||||
});
|
||||
});
|
||||
|
||||
// 后台管理
|
||||
Route::group('/admin', function () {
|
||||
// 消息通知
|
||||
Route::group('/notice', function () {
|
||||
Route::any('/list', [app\common\controller\MessageNotificationsController::class, 'index']);
|
||||
Route::any('/show', [app\common\controller\MessageNotificationsController::class, 'show']);
|
||||
Route::any('/push', [app\common\controller\MessageNotificationsController::class, 'push']);
|
||||
Route::any('/destroy', [app\common\controller\MessageNotificationsController::class, 'destroy']);
|
||||
});
|
||||
|
||||
// 角色
|
||||
Route::group('/role', function () {
|
||||
Route::any('/list', [app\admin\controller\RoleController::class, 'index']);
|
||||
@@ -157,6 +176,7 @@ Route::group('/admin', function () {
|
||||
Route::any('/transfer', [app\crmAdmin\controller\CustomerController::class, 'transfer']);
|
||||
Route::any('/detail', [app\crmAdmin\controller\CustomerController::class, 'detail']);
|
||||
Route::any('/delete', [app\crmAdmin\controller\CustomerController::class, 'delete']);
|
||||
Route::any('/restore', [app\crmAdmin\controller\CustomerController::class, 'restore']); //恢复客户
|
||||
Route::any('/cede', [app\crm\controller\CustomerController::class, 'cede']);
|
||||
Route::any('/follow', [app\crmAdmin\controller\CustomerController::class, 'follow']);
|
||||
Route::any('/allocation', [app\crmAdmin\controller\CustomerController::class, 'allocation']);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\webman\gateway;
|
||||
|
||||
use app\common\utils\WsEvents;
|
||||
use GatewayWorker\Lib\Gateway;
|
||||
|
||||
class Events
|
||||
{
|
||||
|
||||
protected static $wsEvents;
|
||||
|
||||
public static function onWorkerStart($worker)
|
||||
{
|
||||
self::$wsEvents = new WsEvents();
|
||||
}
|
||||
|
||||
public static function onConnect($client_id)
|
||||
{
|
||||
// 仅 TCP 连接,不一定有 WebSocket 握手
|
||||
}
|
||||
|
||||
public static function onWebSocketConnect($client_id, $data)
|
||||
{
|
||||
self::$wsEvents->auth($client_id, $data);
|
||||
}
|
||||
|
||||
public static function onMessage($client_id, $message)
|
||||
{
|
||||
self::$wsEvents->message($client_id, $message);
|
||||
}
|
||||
|
||||
public static function onClose($client_id)
|
||||
{
|
||||
var_dump('关闭客户端ID:'.$client_id);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user