添加实时聊天模块

This commit is contained in:
老李 2021-03-25 22:26:53 +08:00
parent 18332736e8
commit eb9d903988
8 changed files with 420 additions and 11 deletions

View File

@ -63,8 +63,9 @@ class SwooleWebsocket extends Command
public function request($request, $response)
{
$data = Arr::get($request->post, 'data');
$user_ids = Arr::get($request->post, 'user_ids',[]);
$parms = $request->getContent();
$data = Arr::get($parms, 'data');
$user_ids = Arr::get($parms, 'user_ids',[]);
if ($data != null && !empty($user_ids)) {
foreach ($this->fd as $k => $v) {
// 需要先判断是否是正确的websocket连接否则有可能会push失败

View File

@ -1,6 +1,8 @@
<?php
use GuzzleHttp\Exception\GuzzleException;
if (!function_exists('uuid_generate')) {
/**
* 生成唯一不重复的uuid
@ -38,24 +40,38 @@ if (!function_exists('push_message')) {
/**
* 推送websocket消息
* @param $data
* @param array $user_ids
* @throws \GuzzleHttp\Exception\GuzzleException
* @param array $accept_user_ids
* @param int $send_user_id
* @return bool
* @throws GuzzleException
*/
function push_message($data, $accept_user_ids = [],$send_user_id = 0)
function push_message($data, $accept_user_ids = [], $send_user_id = 0)
{
try {
$users = \App\Models\User::query()->pluck('nickname', 'id')->toArray();
foreach ($accept_user_ids as $accept_user_id) {
\App\Models\Message::create([
'send_user_id' => $send_user_id,
'send_user_nickname' => \Illuminate\Support\Arr::get($users, $send_user_id, null),
'accept_user_id' => $accept_user_id,
'accept_user_nickname' => \Illuminate\Support\Arr::get($users, $accept_user_id, null),
'title' => $data['title'] ?? null,
'content' => $data['content'] ?? null,
]);
}
$client = new \GuzzleHttp\Client();
$client->post('http://127.0.0.1:9502',[
'form_params' => [
$client->post('http://127.0.0.1:9502', [
'json' => [
'data' => $data,
'user_ids' => $user_ids,
'user_ids' => $accept_user_ids,
],
'timeout' => 5,
]);
}catch (Exception $exception){
\Illuminate\Support\Facades\Log::error('推送消息异常:'.$exception->getMessage());
return true;
} catch (Exception $exception) {
\Illuminate\Support\Facades\Log::error('推送消息异常:' . $exception->getMessage());
}
return;
return false;
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace App\Http\Controllers\Chat;
use App\Http\Controllers\Controller;
use App\Models\Message;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\View;
class MessageController extends Controller
{
public function index(Request $request)
{
if ($request->ajax()){
$type = $request->input('type');
$res = Message::query();
if ($type==2){ //通知
$res = $res->where('send_user_id','=',0)
->where('accept_user_id','=',$request->user()->id)
->where('read','=',0);
}elseif ($type==3){ //私信
$res = $res->where('send_user_id','>',0)->where('accept_user_id','=',$request->user()->id);
}
$res = $res->orderBy('read','asc')->orderByDesc('id')->paginate($request->input('limit',30));
return $this->success('ok',$res->items(),$res->total());
}
return View::make('chat.message.index');
}
public function create()
{
return View::make('chat.message.create');
}
public function store(Request $request)
{
$data = $request->all(['title','content','accept_user_ids']);
$res = push_message(['title'=>$data['title'],'content'=>$data['content']],$data['accept_user_ids'],$request->user()->id);
if ($res){
return $this->success();
}
return $this->success();
}
public function read(Request $request)
{
$ids = $request->input('ids');
if (empty($ids)){
return $this->error('请选择操作项');
}
try {
Message::query()->whereIn('id',$ids)->update(['read'=>1]);
return $this->success();
}catch (\Exception $exception){
Log::error('标记为已读操作异常:'.$exception->getMessage());
return $this->error();
}
}
public function show(Request $request,$id)
{
$model = Message::query()->where('id',$id)->first();
return View::make('chat.message.show',compact('model'));
}
public function destroy(Request $request)
{
$ids = $request->get('ids');
if (empty($ids)){
return $this->error('请选择删除项');
}
try{
Message::destroy($ids);
return $this->success();
}catch (\Exception $exception){
Log::error('删除消息异常:'.$exception->getMessage());
return $this->error();
}
}
}

View File

@ -174,6 +174,26 @@ class MenuTableSeeder extends Seeder
],
]
],
[
'name' => '实时聊天',
'route' => null,
'url' => null,
'icon' => 'layui-icon-cellphone-fine',
'type' => 2,
'sort' => 2,
'permission_name' => 'chat',
'child' => [
[
'name' => '消息中心',
'route' => 'chat.message',
'url' => null,
'icon' => 'layui-icon-note',
'type' => 1,
'permission_name' => 'chat.message',
],
]
],
];
$permissions = \App\Models\Permission::pluck('id','name')->toArray();
foreach ($datas as $k1 => $d1){

View File

@ -208,6 +208,23 @@ class UserTableSeeder extends Seeder
],
],
],
[
'name' => 'chat',
'display_name' => '实时聊天',
'child' => [
[
'name' => 'chat.message',
'display_name' => '消息中心',
'child' => [
['name' => 'chat.message.create', 'display_name' => '发送消息'],
['name' => 'chat.message.edit', 'display_name' => '详情'],
['name' => 'chat.message.read', 'display_name' => '已读'],
['name' => 'chat.message.destroy', 'display_name' => '删除'],
]
],
],
],
];
foreach ($permissions as $pem1) {
//生成一级权限

View File

@ -0,0 +1,124 @@
@extends('base')
@section('content')
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-tab layui-tab-brief">
<ul class="layui-tab-title">
<li class="layui-this">全部消息</li>
<li>通知<span class="layui-badge">6</span></li>
<li>私信</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<div class="LAY-app-message-btns" style="margin-bottom: 10px;">
<button class="layui-btn layui-btn-primary layui-btn-sm" data-type="all" data-events="del">删除</button>
<button class="layui-btn layui-btn-primary layui-btn-sm" data-type="all" data-events="ready">标记已读</button>
<button class="layui-btn layui-btn-primary layui-btn-sm" data-type="all" data-events="readyAll">全部已读</button>
</div>
<table id="LAY-app-message-all" lay-filter="LAY-app-message-all"></table>
</div>
<div class="layui-tab-item">
<div class="LAY-app-message-btns" style="margin-bottom: 10px;">
<button class="layui-btn layui-btn-primary layui-btn-sm" data-type="notice" data-events="del">删除</button>
<button class="layui-btn layui-btn-primary layui-btn-sm" data-type="notice" data-events="ready">标记已读</button>
<button class="layui-btn layui-btn-primary layui-btn-sm" data-type="notice" data-events="readyAll">全部已读</button>
</div>
<table id="LAY-app-message-notice" lay-filter="LAY-app-message-notice"></table>
</div>
<div class="layui-tab-item">
<div class="LAY-app-message-btns" style="margin-bottom: 10px;">
<button class="layui-btn layui-btn-primary layui-btn-sm" data-type="direct" data-events="del">删除</button>
<button class="layui-btn layui-btn-primary layui-btn-sm" data-type="direct" data-events="ready">标记已读</button>
<button class="layui-btn layui-btn-primary layui-btn-sm" data-type="direct" data-events="readyAll">全部已读</button>
</div>
<table id="LAY-app-message-direct" lay-filter="LAY-app-message-direct"></table>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('script')
<script>
layui.config({
base: '/layuiadmin/modules/'
}).extend({
treetable: 'treetable-lay/treetable'
}).use(['layer', 'table', 'form', 'treetable'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var treetable = layui.treetable;
// 渲染表格
var dataTable = function () {
treetable.render({
treeColIndex: 1, // treetable新增参数
treeSpid: 0, // treetable新增参数
treeIdName: 'id', // treetable新增参数
treePidName: 'parent_id', // treetable新增参数
treeDefaultClose: false, // treetable新增参数
treeLinkage: false, // treetable新增参数
elem: '#dataTable',
url: "{{ route('crm.department') }}",
cols: [[ //表头
{field: 'id', title: 'ID', sort: true, width: 80}
, {field: 'name', title: '名称'}
, {field: 'business_user_nickname', title: '部门经理'}
, {field: 'created_at', title: '创建时间'}
, {field: 'updated_at', title: '更新时间'}
, {fixed: 'right',title:'操作', width: 260, align: 'center', toolbar: '#options'}
]]
});
}
dataTable(); //调用此函数可重新渲染表格
//监听工具条
table.on('tool(dataTable)', function(obj){ //注tool是工具条事件名dataTable是table原始容器的属性 lay-filter="对应的值"
var data = obj.data //获得当前行数据
,layEvent = obj.event; //获得 lay-event 对应的值
if(layEvent === 'del'){
deleteData(obj,"{{ route('crm.department.destroy') }}");
} else if(layEvent === 'edit'){
layer.open({
type: 2,
title: "编辑",
shadeClose: true,
area: ["600px","400px"],
content: '/crm/department/'+data.id+'/edit',
end: function () {
dataTable();
}
})
} else if(layEvent === 'create'){
layer.open({
type: 2,
title: "添加子部门",
shadeClose: true,
area: ["600px","400px"],
content: '/crm/department/create?parent_id=' + data.id,
end: function () {
dataTable();
}
})
}
});
$("#addBtn").click(function () {
layer.open({
type: 2,
title: "添加",
shadeClose: true,
area: ["600px","400px"],
content: "{{route("crm.department.create")}}",
end: function () {
dataTable();
}
})
})
})
</script>
@endsection

View File

@ -0,0 +1,119 @@
@extends('base')
@section('content')
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-fluid" id="LAY-app-message-detail">
<div class="layui-card layuiAdmin-msg-detail">
<script type="text/html" template lay-url="{{ layui.setter.base }}json/message/detail.js?id={{ layui.router().search.id }}">
<div class="layui-card-header">
<h1>{{ d.data.title }}</h1>
<p>
<span>{{ layui.util.timeAgo(d.data.time) }}</span>
</p>
</div>
<div class="layui-card-body layui-text">
<div class="layadmin-text">
{{ d.data.content }}
<blockquote class="layui-elem-quote">
注:这里读取的是静态的模拟接口,实际应用时,您可以在该页面源代码中,修改成以下任意一种方式
<ul>
<li> <em>lay-url=""</em> 改成你的真实接口,系统会自动识别该动态模板,直接前端渲染。</li>
<li>剔除 script 动态模板标签,改成服务端渲染。</li>
</ul>
</blockquote>
</div>
<div style="padding-top: 30px;">
<a href="javascript:;" layadmin-event="back" class="layui-btn layui-btn-primary layui-btn-sm">返回上级</a>
</div>
</div>
</script>
</div>
</div>
</div>
</div>
@endsection
@section('script')
<script>
layui.config({
base: '/layuiadmin/modules/'
}).extend({
treetable: 'treetable-lay/treetable'
}).use(['layer', 'table', 'form', 'treetable'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var treetable = layui.treetable;
// 渲染表格
var dataTable = function () {
treetable.render({
treeColIndex: 1, // treetable新增参数
treeSpid: 0, // treetable新增参数
treeIdName: 'id', // treetable新增参数
treePidName: 'parent_id', // treetable新增参数
treeDefaultClose: false, // treetable新增参数
treeLinkage: false, // treetable新增参数
elem: '#dataTable',
url: "{{ route('crm.department') }}",
cols: [[ //表头
{field: 'id', title: 'ID', sort: true, width: 80}
, {field: 'name', title: '名称'}
, {field: 'business_user_nickname', title: '部门经理'}
, {field: 'created_at', title: '创建时间'}
, {field: 'updated_at', title: '更新时间'}
, {fixed: 'right',title:'操作', width: 260, align: 'center', toolbar: '#options'}
]]
});
}
dataTable(); //调用此函数可重新渲染表格
//监听工具条
table.on('tool(dataTable)', function(obj){ //注tool是工具条事件名dataTable是table原始容器的属性 lay-filter="对应的值"
var data = obj.data //获得当前行数据
,layEvent = obj.event; //获得 lay-event 对应的值
if(layEvent === 'del'){
deleteData(obj,"{{ route('crm.department.destroy') }}");
} else if(layEvent === 'edit'){
layer.open({
type: 2,
title: "编辑",
shadeClose: true,
area: ["600px","400px"],
content: '/crm/department/'+data.id+'/edit',
end: function () {
dataTable();
}
})
} else if(layEvent === 'create'){
layer.open({
type: 2,
title: "添加子部门",
shadeClose: true,
area: ["600px","400px"],
content: '/crm/department/create?parent_id=' + data.id,
end: function () {
dataTable();
}
})
}
});
$("#addBtn").click(function () {
layer.open({
type: 2,
title: "添加",
shadeClose: true,
area: ["600px","400px"],
content: "{{route("crm.department.create")}}",
end: function () {
dataTable();
}
})
})
})
</script>
@endsection

View File

@ -298,3 +298,27 @@ Route::group(['prefix'=>'crm','namespace'=>'Crm','middleware'=>['auth','permissi
});
});
/*
|--------------------------------------------------------------------------
| 实时聊天模块
|--------------------------------------------------------------------------
*/
Route::group(['prefix'=>'chat','namespace'=>'Chat','middleware'=>['auth','permission:chat']],function (){
//消息中心
Route::group([],function (){
Route::get('message','MessageController@index')->name('chat.message')->middleware('permission:chat.message');
//添加
Route::get('message/create','MessageController@create')->name('chat.message.create')->middleware('permission:chat.message.create');
Route::post('message/store','MessageController@store')->name('chat.message.store')->middleware('permission:chat.message.create');
//详情
Route::get('message/{id}/show','MessageController@show')->name('chat.message.edit')->middleware('permission:chat.message.edit');
//已读
Route::post('message/read','MessageController@read')->name('chat.message.read')->middleware('permission:chat.message.read');
//删除
Route::delete('message/destroy','MessageController@destroy')->name('chat.message.destroy')->middleware('permission:chat.message.destroy');
});
});