perf: 优化@提醒

This commit is contained in:
kuaifan 2022-04-16 09:45:57 +08:00
parent d5ac6fc0c7
commit 8893254664
12 changed files with 149 additions and 23 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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();
// 开始推送消息 // 开始推送消息
PushTask::push([ foreach ($array as $userid => $mention) {
'userid' => $userids, PushTask::push([
'ignoreFd' => $this->ignoreFd, 'userid' => $userid,
'msg' => [ 'ignoreFd' => $this->ignoreFd,
'type' => 'dialog', 'msg' => [
'mode' => 'add', 'type' => 'dialog',
'data' => $msg->toArray(), 'mode' => 'add',
] 'data' => array_merge($msg->toArray(), [
]); 'mention' => $mention,
]),
]
]);
}
// 推送目标②:正在打开这个任务会话的会员 // 推送目标②:正在打开这个任务会话的会员
if ($dialog->type == 'group' && $dialog->group_type == 'task') { if ($dialog->type == 'group' && $dialog->group_type == 'task') {

View File

@ -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");
});
}
}

View File

@ -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
}
}); });
/** /**

View File

@ -147,7 +147,7 @@
<li @click="toggleRoute('messenger')" :class="classNameRoute('messenger')"> <li @click="toggleRoute('messenger')" :class="classNameRoute('messenger')">
<i class="taskfont">&#xe6eb;</i> <i class="taskfont">&#xe6eb;</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">&#xe6f3;</i> <i class="taskfont">&#xe6f3;</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' ? '图片信息' : '文件信息') + ']'

View File

@ -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>`;

View File

@ -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>

View File

@ -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)
} }
} }

View File

@ -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 {

View File

@ -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;