perf: 项目群、任务群可添加成员

This commit is contained in:
kuaifan 2022-06-22 15:49:55 +08:00
parent bebef77e5c
commit 3aefe99bd9
8 changed files with 122 additions and 51 deletions

View File

@ -929,6 +929,8 @@ class DialogController extends AbstractController
* @api {get} api/dialog/group/adduser 21. 添加群成员
*
* @apiDescription 需要token身份
* - 有群主时:只有群主可以邀请
* - 没有群主时:群内成员都可以邀请
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName group__adduser
@ -951,7 +953,7 @@ class DialogController extends AbstractController
return Base::retError('请选择群成员');
}
//
$dialog = WebSocketDialog::checkDialog($dialog_id, true);
$dialog = WebSocketDialog::checkDialog($dialog_id, "auto");
//
$dialog->checkGroup();
$dialog->joinGroup($userids);
@ -963,6 +965,8 @@ class DialogController extends AbstractController
* @api {get} api/dialog/group/deluser 22. 移出(退出)群成员
*
* @apiDescription 需要token身份
* - 只有群主、邀请人可以踢人
* - 群主、任务人员、项目人员不可被踢或退出
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName group__adduser
@ -993,10 +997,10 @@ class DialogController extends AbstractController
return Base::retError('请选择群成员');
}
//
$dialog = WebSocketDialog::checkDialog($dialog_id, $type === 'remove');
$dialog = WebSocketDialog::checkDialog($dialog_id);
//
$dialog->checkGroup();
$dialog->exitGroup($userids);
$dialog->exitGroup($userids, $type);
$dialog->pushMsg("groupExit", null, $userids);
return Base::retSuccess($type === 'remove' ? '移出成功' : '退出成功');
}
@ -1005,6 +1009,7 @@ class DialogController extends AbstractController
* @api {get} api/dialog/group/disband 23. 解散群组
*
* @apiDescription 需要token身份
* - 只有群主且是个人类型群可以解散
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName group__disband
@ -1023,7 +1028,7 @@ class DialogController extends AbstractController
//
$dialog = WebSocketDialog::checkDialog($dialog_id, true);
//
$dialog->checkGroup();
$dialog->checkGroup('user');
$dialog->deleteDialog();
$dialog->pushMsg("groupDelete");
return Base::retSuccess('解散成功');

View File

@ -207,9 +207,11 @@ class Project extends AbstractModel
WebSocketDialogUser::updateInsert([
'dialog_id' => $this->dialog_id,
'userid' => $userid,
], [
'important' => 1
]);
}
WebSocketDialogUser::whereDialogId($this->dialog_id)->whereNotIn('userid', $userids)->delete();
WebSocketDialogUser::whereDialogId($this->dialog_id)->whereNotIn('userid', $userids)->whereImportant(1)->delete();
});
}

View File

@ -876,9 +876,11 @@ class ProjectTask extends AbstractModel
WebSocketDialogUser::updateInsert([
'dialog_id' => $this->dialog_id,
'userid' => $userid,
], [
'important' => 1
]);
}
WebSocketDialogUser::whereDialogId($this->dialog_id)->whereNotIn('userid', $userids)->delete();
WebSocketDialogUser::whereDialogId($this->dialog_id)->whereNotIn('userid', $userids)->whereImportant(1)->delete();
});
}

View File

@ -134,6 +134,8 @@ class WebSocketDialog extends AbstractModel
WebSocketDialogUser::updateInsert([
'dialog_id' => $this->id,
'userid' => $value,
], [
'inviter' => User::userid(),
]);
}
}
@ -144,27 +146,34 @@ class WebSocketDialog extends AbstractModel
/**
* 退出聊天室
* @param int|array $userid 加入的会员ID或会员ID组
* @return bool
* @param $type
*/
public function exitGroup($userid)
public function exitGroup($userid, $type = 'exit')
{
$builder = WebSocketDialogUser::whereDialogId($this->id);
if (is_array($userid)) {
$builder->whereIn('userid', $userid);
} else {
$builder->whereUserid($userid);
}
$builder->chunkById(100, function($list) {
/** @var WebSocketDialogUser $item */
foreach ($list as $item) {
if ($item->userid == $this->owner_id) {
// 群主不可退出
continue;
}
$item->delete();
$typeDesc = $type === 'remove' ? '移出' : '退出';
AbstractModel::transaction(function () use ($typeDesc, $type, $userid) {
$builder = WebSocketDialogUser::whereDialogId($this->id);
if (is_array($userid)) {
$builder->whereIn('userid', $userid);
} else {
$builder->whereUserid($userid);
}
$builder->chunkById(100, function($list) use ($typeDesc, $type) {
/** @var WebSocketDialogUser $item */
foreach ($list as $item) {
if ($type === 'remove' && !in_array(User::userid(), [$this->owner_id, $item->inviter])) {
throw new ApiException('只有群主或邀请人可以移出成员');
}
if ($item->userid == $this->owner_id) {
throw new ApiException('群主不可' . $typeDesc);
}
if ($item->important) {
throw new ApiException('项目人员或任务人员不可' . $typeDesc);
}
$item->delete();
}
});
});
return true;
}
/**
@ -196,15 +205,19 @@ class WebSocketDialog extends AbstractModel
/**
* 检查群组类型
* @param string|array|null $groupType
* @return void
*/
public function checkGroup($groupType = 'user')
public function checkGroup($groupType = null)
{
if ($this->type !== 'group') {
throw new ApiException('仅限群组操作');
}
if ($this->group_type !== $groupType) {
throw new ApiException('操作的群组类型错误');
if ($groupType) {
$groupTypes = is_array($groupType) ? $groupType : [$groupType];
if (!in_array($this->group_type, $groupTypes)) {
throw new ApiException('操作的群组类型错误');
}
}
}
@ -260,7 +273,7 @@ class WebSocketDialog extends AbstractModel
/**
* 获取对话(同时检验对话身份)
* @param $dialog_id
* @param bool $checkOwner 是否校验群组身份
* @param bool|string $checkOwner 是否校验群组身份'auto'时有群主为true无群主为false
* @return self
*/
public static function checkDialog($dialog_id, $checkOwner = false)
@ -271,11 +284,14 @@ class WebSocketDialog extends AbstractModel
}
//
$userid = User::userid();
if ($checkOwner === 'auto') {
$checkOwner = $dialog->owner_id > 0;
}
if ($checkOwner === true && $dialog->owner_id != $userid) {
throw new ApiException('仅限群主操作');
}
//
if ($dialog->type === 'group' && $dialog->group_type === 'task') {
if ($dialog->group_type === 'task') {
// 任务群对话校验是否在项目内
$project_id = intval(ProjectTask::whereDialogId($dialog->id)->value('project_id'));
if ($project_id > 0) {

View File

@ -10,6 +10,8 @@ namespace App\Models;
* @property int|null $userid 会员ID
* @property string|null $top_at 置顶时间
* @property int|null $mark_unread 是否标记为未读0否1是
* @property int|null $inviter 邀请人
* @property int|null $important 是否不可移出(项目、任务人员)
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser newModelQuery()
@ -18,6 +20,8 @@ namespace App\Models;
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereDialogId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereImportant($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereInviter($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereMarkUnread($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereTopAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereUpdatedAt($value)

View File

@ -0,0 +1,48 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddWebSocketDialogUsersInviterImportant extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$isAdd = false;
Schema::table('web_socket_dialog_users', function (Blueprint $table) use (&$isAdd) {
if (!Schema::hasColumn('web_socket_dialog_users', 'important')) {
$isAdd = true;
$table->boolean('important')->default(0)->after('mark_unread')->nullable()->comment('是否不可移出(项目、任务人员)');
$table->bigInteger('inviter')->nullable()->default(0)->after('mark_unread')->comment('邀请人');
}
});
if ($isAdd) {
\App\Models\WebSocketDialog::whereIn('group_type', ['project', 'task'])->chunkById(100, function ($lists) {
/** @var \App\Models\WebSocketDialog $item */
foreach ($lists as $item) {
\App\Models\WebSocketDialogUser::whereDialogId($item->id)->update([
'important' => 1,
]);
}
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
$table->dropColumn("important");
$table->dropColumn("inviter");
});
}
}

View File

@ -19,9 +19,9 @@
<div class="group-info-user">
<ul>
<li v-for="(item, index) in userList" :key="index">
<UserAvatar :userid="item.userid" :size="32" :user-result="userResult" showName tooltipDisabled/>
<UserAvatar :userid="item.userid" :size="32" showName tooltipDisabled/>
<div v-if="item.userid === dialogData.owner_id" class="user-tag">{{ $L("群主") }}</div>
<Icon v-else-if="dialogData.owner_id == userId" class="user-exit" type="md-exit" @click="onExit(item)"/>
<Icon v-else-if="dialogData.owner_id == userId || item.inviter == userId" class="user-exit" type="md-exit" @click="onExit(item)"/>
</li>
<li v-if="userList.length === 0" class="no">
<Loading v-if="loadIng > 0"/>
@ -30,12 +30,10 @@
</ul>
</div>
<div v-if="dialogData.owner_id == userId" class="group-info-button">
<Button @click="openAdd" type="primary">{{ $L("添加成员") }}</Button>
<Button @click="onDisband" type="error" ghost>{{ $L("解散群组") }}</Button>
</div>
<div v-else class="group-info-button">
<Button @click="onExit" type="error" ghost>{{ $L("退出群组") }}</Button>
<div class="group-info-button">
<Button v-if="dialogData.owner_id == userId || dialogData.owner_id == 0" @click="openAdd" type="primary">{{ $L("添加成员") }}</Button>
<Button v-if="dialogData.owner_id == userId" @click="onDisband" type="error" ghost>{{ $L("解散群组") }}</Button>
<Button v-else @click="onExit" type="error" ghost>{{ $L("退出群组") }}</Button>
</div>
<!--添加成员-->
@ -85,7 +83,7 @@ export default {
},
computed: {
...mapState(['cacheDialogs']),
...mapState(['cacheDialogs', 'cacheUserBasic']),
dialogData() {
return this.cacheDialogs.find(({id}) => id == this.dialogId) || {};
@ -100,8 +98,15 @@ export default {
},
userList() {
const {dialogUser, searchKey, dialogData} = this;
const list = dialogUser.filter(item => {
const {dialogUser, searchKey, cacheUserBasic, dialogData} = this;
const list = dialogUser.map(item => {
const userBasic = cacheUserBasic.find(basic => basic.userid == item.userid)
if (userBasic) {
item.nickname = userBasic.nickname
item.email = userBasic.email
}
return item
}).filter(item => {
if (searchKey && item.nickname) {
if (!$A.strExists(item.nickname, searchKey) && !$A.strExists(item.email, searchKey)) {
return false;
@ -171,16 +176,6 @@ export default {
});
},
userResult(user) {
let index = this.dialogUser.findIndex(({userid}) => userid === user.userid);
if (index > -1) {
this.dialogUser.splice(index, 1, Object.assign(user, {
id: this.dialogUser[index].id,
created_at: this.dialogUser[index].created_at
}))
}
},
openAdd() {
this.addData = {
dialog_id: this.dialogId,

View File

@ -53,7 +53,6 @@
<template v-if="dialogData.type === 'group'">
<ETooltip
v-if="dialogData.group_type === 'user'"
placement="top"
:disabled="windowSmall"
:openDelay="600"
@ -250,7 +249,7 @@
<DrawerOverlay
v-model="groupInfoShow"
placement="right"
:size="380">
:size="400">
<DialogGroupInfo v-if="groupInfoShow" :dialogId="dialogId"/>
</DrawerOverlay>
</div>