feat: 新增离职操作

This commit is contained in:
kuaifan 2022-04-13 20:06:17 +08:00
parent f76016860f
commit 7f79795c55
10 changed files with 376 additions and 49 deletions

View File

@ -2,8 +2,13 @@
namespace App\Http\Controllers\Api; namespace App\Http\Controllers\Api;
use App\Models\AbstractModel;
use App\Models\File;
use App\Models\ProjectTaskUser;
use App\Models\ProjectUser;
use App\Models\User; use App\Models\User;
use App\Models\UserEmailVerification; use App\Models\UserEmailVerification;
use App\Models\UserTransfer;
use App\Module\Base; use App\Module\Base;
use Arr; use Arr;
use Cache; use Cache;
@ -498,12 +503,14 @@ class UsersController extends AbstractController
* @apiParam {String} [type] 操作 * @apiParam {String} [type] 操作
* - setadmin 设为管理员 * - setadmin 设为管理员
* - clearadmin 取消管理员 * - clearadmin 取消管理员
* - setdisable 设为禁用 * - setdisable 设为离职(需要参数 disable_time、transfer_userid
* - cleardisable 取消禁用 * - cleardisable 取消离职
* - delete 删除会员 * - delete 删除会员
* @apiParam {String} [password] 新的密码 * @apiParam {String} [password] 新的密码
* @apiParam {String} [nickname] 昵称 * @apiParam {String} [nickname] 昵称
* @apiParam {String} [profession] 职位 * @apiParam {String} [profession] 职位
* @apiParam {String} [disable_time] 离职时间
* @apiParam {String} [transfer_userid] 离职交接人
* *
* @apiSuccess {Number} ret 返回状态码1正确、0错误 * @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {String} msg 返回信息(错误描述)
@ -524,6 +531,7 @@ class UsersController extends AbstractController
$userInfo->checkSystem(1); $userInfo->checkSystem(1);
// //
$upArray = []; $upArray = [];
$transferUser = null;
switch ($type) { switch ($type) {
case 'setadmin': case 'setadmin':
$upArray['identity'] = array_diff($userInfo->identity, ['admin']); $upArray['identity'] = array_diff($userInfo->identity, ['admin']);
@ -537,7 +545,14 @@ class UsersController extends AbstractController
case 'setdisable': case 'setdisable':
$upArray['identity'] = array_diff($userInfo->identity, ['disable']); $upArray['identity'] = array_diff($userInfo->identity, ['disable']);
$upArray['identity'][] = 'disable'; $upArray['identity'][] = 'disable';
$upArray['disable_at'] = Carbon::now(); $upArray['disable_at'] = Carbon::parse($data['disable_time']);
$transferUser = User::find(intval($data['transfer_userid']));
if (empty($transferUser)) {
return Base::retError('请选择正确的交接人');
}
if (in_array('disable', $transferUser->identity)) {
return Base::retError('交接人已离职,请选择另一个交接人');
}
break; break;
case 'cleardisable': case 'cleardisable':
@ -583,8 +598,18 @@ class UsersController extends AbstractController
} }
} }
if ($upArray) { if ($upArray) {
AbstractModel::transaction(function() use ($type, $upArray, $userInfo, $transferUser) {
$userInfo->updateInstance($upArray); $userInfo->updateInstance($upArray);
$userInfo->save(); $userInfo->save();
if ($type === 'setdisable') {
$userTransfer = UserTransfer::createInstance([
'original_userid' => $userInfo->userid,
'new_userid' => $transferUser->userid,
]);
$userTransfer->save();
$userTransfer->start();
}
});
} }
// //
return Base::retSuccess('修改成功', $userInfo); return Base::retSuccess('修改成功', $userInfo);

View File

@ -405,4 +405,55 @@ class File extends AbstractModel
} }
return $data; return $data;
} }
/**
* 移交文件
* @param $originalUserid
* @param $newUserid
* @return void
*/
public static function transfer($originalUserid, $newUserid)
{
// 创建一个文件夹存放移交的文件
$name = User::userid2nickname($originalUserid) ?: ('ID:' . $originalUserid);
$file = File::createInstance([
'pid' => 0,
'name' => "{$name}】移交的文件",
'type' => "folder",
'ext' => "",
'userid' => $newUserid,
'created_id' => 0,
]);
$file->saveBeforePids();
// 移交文件
self::whereUserid($originalUserid)->chunkById(100, function($list) use ($file, $newUserid) {
/** @var self $item */
foreach ($list as $item) {
if ($item->pid === 0) {
$item->pid = $file->id;
}
$item->userid = $newUserid;
$item->saveBeforePids();
}
});
// 移交文件权限
FileUser::whereUserid($originalUserid)->chunkById(100, function ($list) use ($newUserid) {
/** @var FileUser $item */
foreach ($list as $item) {
$row = FileUser::whereFileId($item->file_id)->whereUserid($newUserid)->first();
if ($row) {
// 已存在则删除原数据,判断改变已存在的数据
$row->permission = max($row->permission, $item->permission);
$row->save();
$item->delete();
} else {
// 不存在则改变原数据
$item->userid = $newUserid;
$item->save();
}
}
});
}
} }

View File

@ -13,6 +13,7 @@ namespace App\Models;
* @property int|null $owner 是否任务负责人 * @property int|null $owner 是否任务负责人
* @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\ProjectTask|null $projectTask
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskUser newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskUser newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskUser newQuery() * @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskUser newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskUser query() * @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskUser query()
@ -29,4 +30,45 @@ namespace App\Models;
class ProjectTaskUser extends AbstractModel class ProjectTaskUser extends AbstractModel
{ {
/**
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function projectTask(): \Illuminate\Database\Eloquent\Relations\HasOne
{
return $this->hasOne(ProjectTask::class, 'id', 'task_id');
}
/**
* 移交任务身份
* @param $originalUserid
* @param $newUserid
* @return void
*/
public static function transfer($originalUserid, $newUserid)
{
self::whereUserid($originalUserid)->chunk(100, function ($list) use ($newUserid) {
$tastIds = [];
/** @var self $item */
foreach ($list as $item) {
$row = self::whereTaskId($item->task_id)->whereUserid($newUserid)->first();
if ($row) {
// 已存在则删除原数据,判断改变已存在的数据
$row->owner = max($row->owner, $item->owner);
$row->save();
$item->delete();
} else {
// 不存在则改变原数据
$item->userid = $newUserid;
$item->save();
}
if ($item->projectTask) {
$item->projectTask->addLog("移交{任务}身份给", ['userid' => [$newUserid]]);
if (!in_array($item->task_pid, $tastIds)) {
$tastIds[] = $item->task_pid;
$item->projectTask->syncDialogUser();
}
}
}
});
}
} }

View File

@ -38,6 +38,36 @@ class ProjectUser extends AbstractModel
return $this->hasOne(Project::class, 'id', 'project_id'); return $this->hasOne(Project::class, 'id', 'project_id');
} }
/**
* 移交项目身份
* @param $originalUserid
* @param $newUserid
* @return void
*/
public static function transfer($originalUserid, $newUserid)
{
self::whereUserid($originalUserid)->chunkById(100, function ($list) use ($newUserid) {
/** @var self $item */
foreach ($list as $item) {
$row = self::whereProjectId($item->project_id)->whereUserid($newUserid)->first();
if ($row) {
// 已存在则删除原数据,判断改变已存在的数据
$row->owner = max($row->owner, $item->owner);
$row->save();
$item->delete();
} else {
// 不存在则改变原数据
$item->userid = $newUserid;
$item->save();
}
if ($item->project) {
$item->project->addLog("移交项目身份给", ['userid' => $newUserid]);
$item->project->syncDialogUser();
}
}
});
}
/** /**
* 退出项目 * 退出项目
*/ */
@ -47,15 +77,13 @@ class ProjectUser extends AbstractModel
->whereUserid($this->userid) ->whereUserid($this->userid)
->chunk(100, function ($list) { ->chunk(100, function ($list) {
$tastIds = []; $tastIds = [];
/** @var ProjectTaskUser $item */
foreach ($list as $item) { foreach ($list as $item) {
$item->delete();
if (!in_array($item->task_pid, $tastIds)) { if (!in_array($item->task_pid, $tastIds)) {
$tastIds[] = $item->task_pid; $tastIds[] = $item->task_pid;
$item->projectTask?->syncDialogUser();
} }
$item->delete();
}
$tasks = ProjectTask::whereIn('id', $tastIds)->get();
foreach ($tasks as $task) {
$task->syncDialogUser();
} }
}); });
$this->delete(); $this->delete();

View File

@ -28,7 +28,7 @@ use Carbon\Carbon;
* @property string|null $line_at 最后在线时间(接口) * @property string|null $line_at 最后在线时间(接口)
* @property int|null $task_dialog_id 最后打开的任务会话ID * @property int|null $task_dialog_id 最后打开的任务会话ID
* @property string|null $created_ip 注册IP * @property string|null $created_ip 注册IP
* @property string|null $disable_at 禁用时间 * @property string|null $disable_at 禁用时间(离职时间)
* @property int|null $email_verity 邮箱是否已验证 * @property int|null $email_verity 邮箱是否已验证
* @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $updated_at
@ -64,7 +64,6 @@ class User extends AbstractModel
protected $primaryKey = 'userid'; protected $primaryKey = 'userid';
protected $hidden = [ protected $hidden = [
'disable_at',
'updated_at', 'updated_at',
]; ];

View File

@ -0,0 +1,45 @@
<?php
namespace App\Models;
use App\Exceptions\ApiException;
use App\Module\Base;
use Carbon\Carbon;
use Guanguans\Notify\Factory;
use Guanguans\Notify\Messages\EmailMessage;
/**
* App\Models\UserTransfer
*
* @property int $id
* @property int|null $original_userid 原作者
* @property int|null $new_userid 交接人
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer query()
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer whereNewUserid($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer whereOriginalUserid($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserTransfer whereUpdatedAt($value)
* @mixin \Eloquent
*/
class UserTransfer extends AbstractModel
{
/**
* 开始移交
* @return void
*/
public function start()
{
// 移交项目身份
ProjectUser::transfer($this->original_userid, $this->new_userid);
// 移交任务身份
ProjectTaskUser::transfer($this->original_userid, $this->new_userid);
// 移交文件
File::transfer($this->original_userid, $this->new_userid);
}
}

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
class UsersChangeDisableAt extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$pre = DB::connection()->getTablePrefix();
DB::statement("ALTER TABLE `{$pre}users` MODIFY COLUMN `disable_at` timestamp NULL DEFAULT NULL COMMENT '禁用时间(离职时间)' AFTER `created_ip`");
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUserTransfersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_transfers', function (Blueprint $table) {
$table->id();
$table->bigInteger('original_userid')->nullable()->default(0)->comment('原作者');
$table->bigInteger('new_userid')->nullable()->default(0)->comment('交接人');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_transfers');
}
}

View File

@ -314,7 +314,7 @@
<DrawerOverlay <DrawerOverlay
v-model="workReportShow" v-model="workReportShow"
placement="right" placement="right"
:size="1100"> :size="1200">
<Report v-if="workReportShow" :reportType="reportTabs" :reportUnreadNumber="reportUnreadNumber" @on-read="getReportUnread" /> <Report v-if="workReportShow" :reportType="reportTabs" :reportUnreadNumber="reportUnreadNumber" @on-read="getReportUnread" />
</DrawerOverlay> </DrawerOverlay>
@ -322,7 +322,7 @@
<DrawerOverlay <DrawerOverlay
v-model="allUserShow" v-model="allUserShow"
placement="right" placement="right"
:size="1100"> :size="1200">
<TeamManagement v-if="allUserShow"/> <TeamManagement v-if="allUserShow"/>
</DrawerOverlay> </DrawerOverlay>
@ -330,7 +330,7 @@
<DrawerOverlay <DrawerOverlay
v-model="allProjectShow" v-model="allProjectShow"
placement="right" placement="right"
:size="1100"> :size="1200">
<ProjectManagement v-if="allProjectShow"/> <ProjectManagement v-if="allProjectShow"/>
</DrawerOverlay> </DrawerOverlay>
@ -338,7 +338,7 @@
<DrawerOverlay <DrawerOverlay
v-model="archivedProjectShow" v-model="archivedProjectShow"
placement="right" placement="right"
:size="1100"> :size="1200">
<ProjectArchived v-if="archivedProjectShow"/> <ProjectArchived v-if="archivedProjectShow"/>
</DrawerOverlay> </DrawerOverlay>

View File

@ -25,8 +25,18 @@
<Option value="">{{$L('全部')}}</Option> <Option value="">{{$L('全部')}}</Option>
<Option value="admin">{{$L('管理员')}}</Option> <Option value="admin">{{$L('管理员')}}</Option>
<Option value="noadmin">{{$L('非管理员')}}</Option> <Option value="noadmin">{{$L('非管理员')}}</Option>
<Option value="disable">{{$L('禁用')}}</Option> </Select>
<Option value="nodisable">{{$L('非禁用')}}</Option> </div>
</li>
<li>
<div class="search-label">
{{$L("在职状态")}}
</div>
<div class="search-content">
<Select v-model="keys.disable" :placeholder="$L('请选择')">
<Option value="">{{$L('在职')}}</Option>
<Option value="yes">{{$L('离职')}}</Option>
<Option value="all">{{$L('全部')}}</Option>
</Select> </Select>
</div> </div>
</li> </li>
@ -77,21 +87,48 @@
@on-change="setPage" @on-change="setPage"
@on-page-size-change="setPageSize"/> @on-page-size-change="setPageSize"/>
</div> </div>
<!--操作离职-->
<Modal
v-model="disableShow"
class="operate-left"
:title="$L('操作离职')">
<Form :model="disableData" label-width="auto" @submit.native.prevent>
<Alert type="error">{{$L(`正在进行帐号【ID:${disableData.userid}${disableData.nickname}】离职操作。`)}}</Alert>
<FormItem :label="$L('离职时间')">
<DatePicker
v-model="disableData.disable_time"
:editable="false"
:placeholder="$L('选择离职时间')"
style="width:100%"
format="yyyy/MM/dd HH:mm"
type="datetime"/>
</FormItem>
<FormItem :label="$L('交接人')">
<UserInput v-model="disableData.transfer_userid" :disabled-choice="[disableData.userid]" :multiple-max="1" :placeholder="$L('选择交接人')"/>
<div class="form-tip">{{ $L(`${disableData.nickname}负责的项目、任务和文件将自动移交给交接人`) }}</div>
</FormItem>
</Form>
<div slot="footer" class="adaption">
<Button type="default" @click="disableShow=false">{{$L('取消')}}</Button>
<Button type="primary" :loading="disableLoading > 0" @click="operationUser(disableData)">{{$L('确定离职')}}</Button>
</div>
</Modal>
</div> </div>
</template> </template>
<script> <script>
import {mapState} from "vuex"; import {mapState} from "vuex";
import UserInput from "../../../components/UserInput";
export default { export default {
name: "TeamManagement", name: "TeamManagement",
components: {UserInput},
data() { data() {
return { return {
loadIng: 0, loadIng: 0,
keys: { keys: {},
identity: 'nodisable'
},
keyIs: false, keyIs: false,
columns: [], columns: [],
@ -100,7 +137,11 @@ export default {
page: 1, page: 1,
pageSize: 20, pageSize: 20,
total: 0, total: 0,
noText: '' noText: '',
disableShow: false,
disableLoading: 0,
disableData: {},
} }
}, },
mounted() { mounted() {
@ -141,7 +182,7 @@ export default {
minWidth: 100, minWidth: 100,
render: (h, {row}) => { render: (h, {row}) => {
const arr = [h('AutoTip', row.email)]; const arr = [h('AutoTip', row.email)];
const {email_verity, identity} = row; const {email_verity, identity, disable_at} = row;
if (email_verity) { if (email_verity) {
arr.push(h('Icon', { arr.push(h('Icon', {
props: { props: {
@ -157,11 +198,17 @@ export default {
}, this.$L('管理员'))) }, this.$L('管理员')))
} }
if (identity.includes("disable")) { if (identity.includes("disable")) {
arr.push(h('Tag', { arr.push(h('Tooltip', {
props: {
content: this.$L('离职时间') + ': ' + disable_at,
},
}, [
h('Tag', {
props: { props: {
color: 'error' color: 'error'
} }
}, this.$L('禁用'))) }, this.$L('离职'))
]))
} }
return h('div', { return h('div', {
class: 'team-email' class: 'team-email'
@ -237,34 +284,42 @@ export default {
}, },
}, [h('div', this.$L('设为管理员'))])); }, [h('div', this.$L('设为管理员'))]));
} }
dropdownItems.push(h('EDropdownItem', {
props: {
command: 'password',
},
}, [h('div', this.$L('修改密码'))]))
if (identity.includes('disable')) { if (identity.includes('disable')) {
dropdownItems.push(h('EDropdownItem', { dropdownItems.push(h('EDropdownItem', {
props: { props: {
command: 'cleardisable', command: 'cleardisable',
}, },
}, [h('div', this.$L('取消禁用'))])); style: {
color: '#f90'
}
}, [h('div', this.$L('恢复身份(已离职)'))]));
} else { } else {
dropdownItems.push(h('EDropdownItem', { dropdownItems.push(h('EDropdownItem', {
props: { props: {
command: 'setdisable', command: 'setdisable',
}, },
}, [h('div', this.$L('设为禁用'))])); style: {
color: '#f90'
} }
dropdownItems.push(...[ }, [h('div', this.$L('操作离职'))]));
h('EDropdownItem', { }
props: {
command: 'password', dropdownItems.push(h('EDropdownItem', {
},
}, [h('div', this.$L('修改密码'))]),
h('EDropdownItem', {
props: { props: {
command: 'delete', command: 'delete',
}, },
style: { style: {
color: 'red' color: 'red'
} }
}, [h('div', this.$L('删除'))]), }, [h('div', this.$L('删除'))]))
])
const dropdownMenu = h('EDropdown', { const dropdownMenu = h('EDropdown', {
props: { props: {
size: 'small', size: 'small',
@ -355,9 +410,30 @@ export default {
}); });
break; break;
case 'setdisable':
this.disableData = {
type: 'setdisable',
userid: row.userid,
nickname: row.nickname,
};
this.disableShow = true;
break;
case 'cleardisable':
$A.modalConfirm({
content: `你确定恢复已离职帐号【ID:${row.userid}${row.nickname}】吗?(注:此操作仅恢复帐号状态,无法恢复操作离职时移交的数据)`,
onOk: () => {
this.operationUser({
userid: row.userid,
type: name
});
}
});
break;
case 'delete': case 'delete':
$A.modalConfirm({ $A.modalConfirm({
content: '你确定要删除此帐号吗?', content: `你确定要删除帐号【ID:${row.userid}${row.nickname}】吗?`,
onOk: () => { onOk: () => {
this.operationUser({ this.operationUser({
userid: row.userid, userid: row.userid,