mirror of
https://github.com/kuaifan/dootask.git
synced 2026-03-17 19:23:26 +00:00
perf: 优化@提醒
This commit is contained in:
parent
d5ac6fc0c7
commit
8893254664
@ -12,6 +12,7 @@ use App\Models\WebSocketDialogMsgRead;
|
|||||||
use App\Models\WebSocketDialogUser;
|
use App\Models\WebSocketDialogUser;
|
||||||
use App\Module\Base;
|
use App\Module\Base;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use DB;
|
||||||
use Request;
|
use Request;
|
||||||
use Response;
|
use Response;
|
||||||
|
|
||||||
@ -145,11 +146,15 @@ class DialogController extends AbstractController
|
|||||||
//
|
//
|
||||||
$dialog = WebSocketDialog::checkDialog($dialog_id);
|
$dialog = WebSocketDialog::checkDialog($dialog_id);
|
||||||
//
|
//
|
||||||
$list = WebSocketDialogMsg::whereDialogId($dialog_id)->orderByDesc('id')->paginate(Base::getPaginate(100, 50));
|
$list = WebSocketDialogMsg::select([
|
||||||
$list->transform(function (WebSocketDialogMsg $item) use ($user) {
|
'web_socket_dialog_msgs.*',
|
||||||
$item->is_read = $item->userid === $user->userid || WebSocketDialogMsgRead::whereMsgId($item->id)->whereUserid($user->userid)->value('read_at');
|
'read.mention',
|
||||||
return $item;
|
'read.read_at',
|
||||||
});
|
])->leftJoin('web_socket_dialog_msg_reads as read', function ($leftJoin) use ($user) {
|
||||||
|
$leftJoin
|
||||||
|
->on('read.userid', '=', DB::raw($user->userid))
|
||||||
|
->on('read.msg_id', '=', 'web_socket_dialog_msgs.id');
|
||||||
|
})->where('web_socket_dialog_msgs.dialog_id', $dialog_id)->orderByDesc('web_socket_dialog_msgs.id')->paginate(Base::getPaginate(100, 50));
|
||||||
//
|
//
|
||||||
if ($dialog->type == 'group' && $dialog->group_type == 'task') {
|
if ($dialog->type == 'group' && $dialog->group_type == 'task') {
|
||||||
$user->task_dialog_id = $dialog->id;
|
$user->task_dialog_id = $dialog->id;
|
||||||
|
|||||||
@ -62,7 +62,9 @@ class WebSocketDialog extends AbstractModel
|
|||||||
$last_msg = WebSocketDialogMsg::whereDialogId($this->id)->orderByDesc('id')->first();
|
$last_msg = WebSocketDialogMsg::whereDialogId($this->id)->orderByDesc('id')->first();
|
||||||
$this->last_msg = $last_msg;
|
$this->last_msg = $last_msg;
|
||||||
// 未读信息
|
// 未读信息
|
||||||
$this->unread = WebSocketDialogMsgRead::whereDialogId($this->id)->whereUserid($userid)->whereReadAt(null)->count();
|
$unreadBuilder = WebSocketDialogMsgRead::whereDialogId($this->id)->whereUserid($userid)->whereReadAt(null);
|
||||||
|
$this->unread = $unreadBuilder->count();
|
||||||
|
$this->mention = $unreadBuilder->whereMention(1)->count();
|
||||||
$this->mark_unread = $this->mark_unread ?? WebSocketDialogUser::whereDialogId($this->id)->whereUserid($userid)->value('mark_unread');
|
$this->mark_unread = $this->mark_unread ?? WebSocketDialogUser::whereDialogId($this->id)->whereUserid($userid)->value('mark_unread');
|
||||||
// 对话人数
|
// 对话人数
|
||||||
$builder = WebSocketDialogUser::whereDialogId($this->id);
|
$builder = WebSocketDialogUser::whereDialogId($this->id);
|
||||||
|
|||||||
@ -11,6 +11,7 @@ use Carbon\Carbon;
|
|||||||
* @property int|null $dialog_id 对话ID
|
* @property int|null $dialog_id 对话ID
|
||||||
* @property int|null $msg_id 消息ID
|
* @property int|null $msg_id 消息ID
|
||||||
* @property int|null $userid 发送会员ID
|
* @property int|null $userid 发送会员ID
|
||||||
|
* @property int|null $mention 是否提及(被@)
|
||||||
* @property int|null $after 在阅读之后才添加的记录
|
* @property int|null $after 在阅读之后才添加的记录
|
||||||
* @property string|null $read_at 阅读时间
|
* @property string|null $read_at 阅读时间
|
||||||
* @property-read \App\Models\WebSocketDialogMsg|null $webSocketDialogMsg
|
* @property-read \App\Models\WebSocketDialogMsg|null $webSocketDialogMsg
|
||||||
@ -20,6 +21,7 @@ use Carbon\Carbon;
|
|||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereAfter($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereAfter($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereDialogId($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereDialogId($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereId($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereMention($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereMsgId($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereMsgId($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereReadAt($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereReadAt($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereUserid($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereUserid($value)
|
||||||
|
|||||||
@ -48,30 +48,38 @@ class WebSocketDialogMsgTask extends AbstractTask
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 推送目标①:群成员
|
// 推送目标①:群成员
|
||||||
|
$array = [];
|
||||||
$userids = $dialog->dialogUser->pluck('userid')->toArray();
|
$userids = $dialog->dialogUser->pluck('userid')->toArray();
|
||||||
foreach ($userids AS $userid) {
|
foreach ($userids AS $userid) {
|
||||||
if ($userid == $msg->userid) {
|
if ($userid == $msg->userid) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
$mention = preg_match("/<span class=\"mention user\" data-id=\"[0|{$userid}]\">/", $msg->type === 'text' ? $msg->msg['text'] : '');
|
||||||
WebSocketDialogMsgRead::createInstance([
|
WebSocketDialogMsgRead::createInstance([
|
||||||
'dialog_id' => $msg->dialog_id,
|
'dialog_id' => $msg->dialog_id,
|
||||||
'msg_id' => $msg->id,
|
'msg_id' => $msg->id,
|
||||||
'userid' => $userid,
|
'userid' => $userid,
|
||||||
|
'mention' => $mention,
|
||||||
])->saveOrIgnore();
|
])->saveOrIgnore();
|
||||||
|
$array[$userid] = $mention;
|
||||||
}
|
}
|
||||||
// 更新已发送数量
|
// 更新已发送数量
|
||||||
$msg->send = WebSocketDialogMsgRead::whereMsgId($msg->id)->count();
|
$msg->send = WebSocketDialogMsgRead::whereMsgId($msg->id)->count();
|
||||||
$msg->save();
|
$msg->save();
|
||||||
// 开始推送消息
|
// 开始推送消息
|
||||||
|
foreach ($array as $userid => $mention) {
|
||||||
PushTask::push([
|
PushTask::push([
|
||||||
'userid' => $userids,
|
'userid' => $userid,
|
||||||
'ignoreFd' => $this->ignoreFd,
|
'ignoreFd' => $this->ignoreFd,
|
||||||
'msg' => [
|
'msg' => [
|
||||||
'type' => 'dialog',
|
'type' => 'dialog',
|
||||||
'mode' => 'add',
|
'mode' => 'add',
|
||||||
'data' => $msg->toArray(),
|
'data' => array_merge($msg->toArray(), [
|
||||||
|
'mention' => $mention,
|
||||||
|
]),
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
// 推送目标②:正在打开这个任务会话的会员
|
// 推送目标②:正在打开这个任务会话的会员
|
||||||
if ($dialog->type == 'group' && $dialog->group_type == 'task') {
|
if ($dialog->type == 'group' && $dialog->group_type == 'task') {
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddWebSocketDialogMsgReadsMention extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('web_socket_dialog_msg_reads', function (Blueprint $table) {
|
||||||
|
if (!Schema::hasColumn('web_socket_dialog_msg_reads', 'mention')) {
|
||||||
|
$table->boolean('mention')->default(0)->after('userid')->nullable()->comment('是否提及(被@)');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('web_socket_dialog_msg_reads', function (Blueprint $table) {
|
||||||
|
$table->dropColumn("mention");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
9
resources/assets/js/functions/web.js
vendored
9
resources/assets/js/functions/web.js
vendored
@ -367,6 +367,15 @@
|
|||||||
getDialogUnread(dialog) {
|
getDialogUnread(dialog) {
|
||||||
return dialog ? (dialog.unread || dialog.mark_unread || 0) : 0
|
return dialog ? (dialog.unread || dialog.mark_unread || 0) : 0
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回对话@提及未读数量
|
||||||
|
* @param dialog
|
||||||
|
* @returns {*|number}
|
||||||
|
*/
|
||||||
|
getDialogMention(dialog) {
|
||||||
|
return dialog ? (dialog.mention || 0) : 0
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -147,7 +147,7 @@
|
|||||||
<li @click="toggleRoute('messenger')" :class="classNameRoute('messenger')">
|
<li @click="toggleRoute('messenger')" :class="classNameRoute('messenger')">
|
||||||
<i class="taskfont"></i>
|
<i class="taskfont"></i>
|
||||||
<div class="menu-title">{{$L('消息')}}</div>
|
<div class="menu-title">{{$L('消息')}}</div>
|
||||||
<Badge class="menu-badge" :count="msgAllUnread"/>
|
<Badge class="menu-badge" :text="msgUnreadMention"/>
|
||||||
</li>
|
</li>
|
||||||
<li @click="toggleRoute('file')" :class="classNameRoute('file')">
|
<li @click="toggleRoute('file')" :class="classNameRoute('file')">
|
||||||
<i class="taskfont"></i>
|
<i class="taskfont"></i>
|
||||||
@ -497,13 +497,26 @@ export default {
|
|||||||
|
|
||||||
...mapGetters(['taskData', 'dashboardTask']),
|
...mapGetters(['taskData', 'dashboardTask']),
|
||||||
|
|
||||||
|
msgUnreadMention() {
|
||||||
|
let num = 0;
|
||||||
|
let mention = 0;
|
||||||
|
this.cacheDialogs.some(dialog => {
|
||||||
|
num += $A.getDialogUnread(dialog);
|
||||||
|
mention += $A.getDialogMention(dialog);
|
||||||
|
})
|
||||||
|
if (num <= 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (mention > 0) {
|
||||||
|
return `${num}·@${mention}`
|
||||||
|
}
|
||||||
|
return String(num);
|
||||||
|
},
|
||||||
|
|
||||||
msgAllUnread() {
|
msgAllUnread() {
|
||||||
let num = 0;
|
let num = 0;
|
||||||
this.cacheDialogs.some(dialog => {
|
this.cacheDialogs.some(dialog => {
|
||||||
let unread = $A.getDialogUnread(dialog);
|
num += $A.getDialogUnread(dialog);
|
||||||
if (unread) {
|
|
||||||
num += unread;
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return num;
|
return num;
|
||||||
},
|
},
|
||||||
@ -886,6 +899,8 @@ export default {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case 'text':
|
case 'text':
|
||||||
body = msg.text;
|
body = msg.text;
|
||||||
|
body = body.replace(/<img src=".*?"\/>/g, `[${this.$L('图片')}]`)
|
||||||
|
body = body.replace(/<[^>]+>/g,"")
|
||||||
break;
|
break;
|
||||||
case 'file':
|
case 'file':
|
||||||
body = '[' + this.$L(msg.type == 'img' ? '图片信息' : '文件信息') + ']'
|
body = '[' + this.$L(msg.type == 'img' ? '图片信息' : '文件信息') + ']'
|
||||||
|
|||||||
@ -141,6 +141,7 @@ export default {
|
|||||||
allowedChars: /^\S*$/,
|
allowedChars: /^\S*$/,
|
||||||
mentionDenotationChars: ["@", "#"],
|
mentionDenotationChars: ["@", "#"],
|
||||||
defaultMenuOrientation: this.defaultMenuOrientation,
|
defaultMenuOrientation: this.defaultMenuOrientation,
|
||||||
|
isolateCharacter: true,
|
||||||
renderItem: (data) => {
|
renderItem: (data) => {
|
||||||
if (data.disabled === true) {
|
if (data.disabled === true) {
|
||||||
return `<div class="mention-item-disabled">${data.value}</div>`;
|
return `<div class="mention-item-disabled">${data.value}</div>`;
|
||||||
|
|||||||
@ -50,6 +50,7 @@
|
|||||||
<Icon v-else class="icon-avatar" type="md-person" />
|
<Icon v-else class="icon-avatar" type="md-person" />
|
||||||
<div class="dialog-box">
|
<div class="dialog-box">
|
||||||
<div class="dialog-title">
|
<div class="dialog-title">
|
||||||
|
<div v-if="$A.getDialogMention(dialog) > 0" class="mention">[@{{$A.getDialogMention(dialog)}}]</div>
|
||||||
<template v-for="tag in $A.dialogTags(dialog)" v-if="tag.color != 'success'">
|
<template v-for="tag in $A.dialogTags(dialog)" v-if="tag.color != 'success'">
|
||||||
<Tag :color="tag.color" :fade="false">{{$L(tag.text)}}</Tag>
|
<Tag :color="tag.color" :fade="false">{{$L(tag.text)}}</Tag>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
12
resources/assets/js/store/actions.js
vendored
12
resources/assets/js/store/actions.js
vendored
@ -2122,13 +2122,16 @@ export default {
|
|||||||
*/
|
*/
|
||||||
dialogMsgRead({state, dispatch}, data) {
|
dialogMsgRead({state, dispatch}, data) {
|
||||||
if (data.userid == state.userId) return;
|
if (data.userid == state.userId) return;
|
||||||
if (data.is_read === true) return;
|
if (data.read_at) return;
|
||||||
data.is_read = true;
|
data.read_at = $A.formatDate();
|
||||||
//
|
//
|
||||||
let dialog = state.cacheDialogs.find(({id}) => id == data.dialog_id);
|
let dialog = state.cacheDialogs.find(({id}) => id == data.dialog_id);
|
||||||
if (dialog && dialog.unread > 0) {
|
if (dialog && dialog.unread > 0) {
|
||||||
dialog.unread--
|
|
||||||
dialog.mark_unread = 0
|
dialog.mark_unread = 0
|
||||||
|
dialog.unread--
|
||||||
|
if (data.mention) {
|
||||||
|
dialog.mention--
|
||||||
|
}
|
||||||
dispatch("saveDialog", dialog)
|
dispatch("saveDialog", dialog)
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
@ -2268,6 +2271,9 @@ export default {
|
|||||||
if (dialog && state.cacheUnreads[data.id] === undefined) {
|
if (dialog && state.cacheUnreads[data.id] === undefined) {
|
||||||
state.cacheUnreads[data.id] = true;
|
state.cacheUnreads[data.id] = true;
|
||||||
dialog.unread++;
|
dialog.unread++;
|
||||||
|
if (data.mention) {
|
||||||
|
dialog.mention++;
|
||||||
|
}
|
||||||
dispatch("saveDialog", dialog)
|
dispatch("saveDialog", dialog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,38 @@
|
|||||||
min-width: 220px;
|
min-width: 220px;
|
||||||
max-width: 350px;
|
max-width: 350px;
|
||||||
max-height: 360px;
|
max-height: 360px;
|
||||||
|
overflow-y: overlay;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 10px;
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb:active {
|
||||||
|
border-radius: 10px;
|
||||||
|
background: rgba(0, 0, 0, .5);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::-webkit-scrollbar-thumb {
|
||||||
|
border: 2px solid transparent;
|
||||||
|
background: rgba(0, 0, 0, .2);
|
||||||
|
background-clip: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::-webkit-scrollbar-thumb:hover {
|
||||||
|
border-top-width: 0;
|
||||||
|
border-bottom-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
border-radius: 10px;
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
.ql-mention-list {
|
.ql-mention-list {
|
||||||
> li {
|
> li {
|
||||||
|
|||||||
11
resources/assets/sass/pages/page-messenger.scss
vendored
11
resources/assets/sass/pages/page-messenger.scss
vendored
@ -126,6 +126,16 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
|
.mention {
|
||||||
|
color: #ff0000;
|
||||||
|
background-color: transparent;
|
||||||
|
font-weight: 600;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 4px;
|
||||||
|
padding: 0;
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
.ivu-tag {
|
.ivu-tag {
|
||||||
margin: 0 4px 0 0;
|
margin: 0 4px 0 0;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
@ -170,6 +180,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
.common-avatar,
|
.common-avatar,
|
||||||
.last-self {
|
.last-self {
|
||||||
|
flex-shrink: 0;
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user