Merge branch 'develop' of ssh://git.gezi.vip:6006/gx/dootask

# Conflicts:
#	electron/package.json
#	package.json
#	public/css/app.css
#	public/js/app.js
#	public/js/build/189.js.LICENSE.txt
#	public/js/build/206.js
#	public/js/build/400.js.LICENSE.txt
#	public/js/build/486.js
#	public/js/build/496.js.LICENSE.txt
#	public/js/build/528.js
#	public/js/build/535.js
#	public/js/build/535.js.LICENSE.txt
#	public/js/build/578.js
#	public/js/build/78.js.LICENSE.txt
#	public/js/build/817.js
#	public/js/build/856.js
#	public/js/build/862.js
#	public/js/build/889.js
#	public/js/build/91.js.LICENSE.txt
#	public/js/build/954.js
#	public/js/build/956.js
#	resources/assets/js/pages/manage/components/TaskDetail.vue
#	resources/assets/js/pages/manage/dashboard.vue
#	resources/assets/sass/pages/common.scss
#	resources/assets/sass/pages/page-dashboard.scss
This commit is contained in:
kuaifan
2022-03-10 11:27:00 +08:00
108 changed files with 3805 additions and 127 deletions

View File

@@ -155,6 +155,12 @@ class DialogController extends AbstractController
$user->task_dialog_id = $dialog->id;
$user->save();
}
//去掉标记未读
$isMarkDialogUser = WebSocketDialogUser::whereDialogId($dialog->id)->whereUserid($user->userid)->whereIsMarkUnread(1)->first();
if ($isMarkDialogUser) {
$isMarkDialogUser->is_mark_unread = 0;
$isMarkDialogUser->save();
}
//
$data = $list->toArray();
if ($list->currentPage() === 1) {
@@ -490,4 +496,48 @@ class DialogController extends AbstractController
'top_at' => $dialogUser->top_at?->toDateTimeString(),
]);
}
/**
* @api {get} api/dialog/msg/mark 13. 消息标记操作
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName msg__mark
*
* @apiParam {Number} dialog_id 消息ID
* @apiParam {String} type 类型
* - read
* - unread
*
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function msg__mark()
{
$user = User::auth();
$dialogId = intval(Request::input('dialog_id'));
$type = Request::input('type');
$dialogUser = WebSocketDialogUser::whereUserid($user->userid)->whereDialogId($dialogId)->first();
if (!$dialogUser) {
return Base::retError("会话不存在");
}
if ($type == 'read') {
WebSocketDialogMsgRead::whereUserid($user->userid)
->whereReadAt(null)
->whereDialogId($dialogId)
->update(
[
'read_at' => Carbon::now()
]);
$dialogUser->is_mark_unread = 0;
$dialogUser->save();
} elseif ($type == 'unread') {
$dialogUser->is_mark_unread = 1;
$dialogUser->save();
}
return Base::retSuccess("success");
}
}

View File

@@ -13,6 +13,7 @@ use App\Models\ProjectInvite;
use App\Models\ProjectLog;
use App\Models\ProjectTask;
use App\Models\ProjectTaskFile;
use App\Models\ProjectTaskFlowChange;
use App\Models\ProjectUser;
use App\Models\User;
use App\Models\WebSocketDialog;
@@ -915,6 +916,7 @@ class ProjectController extends AbstractController
$time_before = Request::input('time_before');
$complete = Request::input('complete', 'all');
$archived = Request::input('archived', 'no');
$deleted = Request::input('deleted');
$keys = Request::input('keys');
$sorts = Request::input('sorts');
$keys = is_array($keys) ? $keys : [];
@@ -953,7 +955,7 @@ class ProjectController extends AbstractController
$builder->whereNotNull('project_tasks.end_at')->where('project_tasks.end_at', '<', Carbon::parse($time_before));
} elseif (is_array($time)) {
if (Base::isDateOrTime($time[0]) && Base::isDateOrTime($time[1])) {
$builder->betweenTime(Carbon::parse($time[0])->startOfDay(), Carbon::parse($time[1])->endOfDay());
$builder->betweenTime(Carbon::parse($time[0])->startOfDay(), Carbon::parse($time[1])->endOfDay(),'taskTime');
}
}
//
@@ -969,6 +971,10 @@ class ProjectController extends AbstractController
$builder->whereNull('project_tasks.archived_at');
}
//
if ($deleted == 'yes') {
$builder->onlyTrashed();
}
//
foreach ($sorts as $column => $direction) {
if (!in_array($column, ['complete_at', 'archived_at', 'end_at'])) continue;
if (!in_array($direction, ['asc', 'desc'])) continue;
@@ -1013,56 +1019,111 @@ class ProjectController extends AbstractController
if (Carbon::parse($time[1])->timestamp - Carbon::parse($time[0])->timestamp > 90 * 86400) {
return Base::retError('时间范围限制最大90天');
}
//
$type = Request::input('type','taskTime');
$headings = [];
$headings[] = '任务ID';
$headings[] = '父级任务ID';
$headings[] = '所属项目';
$headings[] = '任务标题';
$headings[] = '负责人';
$headings[] = '创建人';
$headings[] = '是否完成';
$headings[] = '完成时间';
$headings[] = '是否归档';
$headings[] = '归档时间';
$headings[] = '任务开始时间';
$headings[] = '任务结束时间';
$headings[] = '结束剩余';
$headings[] = '所属项目';
$headings[] = '父级任务ID';
$headings[] = '完成时间';
$headings[] = '归档时间';
$headings[] = '任务计划用时';
$headings[] = '实际完成用时';
$headings[] = '超时时间';
$headings[] = '开发用时';
$headings[] = '验收/测试用时';
$headings[] = '负责人';
$headings[] = '创建人';
$datas = [];
//
$builder = ProjectTask::select(['project_tasks.*', 'project_task_users.userid as ownerid'])
->join('project_task_users', 'project_tasks.id', '=', 'project_task_users.task_id')
->where('project_task_users.owner', 1)
->whereIn('project_task_users.userid', $userid)
->betweenTime(Carbon::parse($time[0])->startOfDay(), Carbon::parse($time[1])->endOfDay());
->betweenTime(Carbon::parse($time[0])->startOfDay(), Carbon::parse($time[1])->endOfDay(),$type);
$builder->orderByDesc('project_tasks.id')->chunk(100, function($tasks) use (&$datas) {
/** @var ProjectTask $task */
foreach ($tasks as $task) {
$flowChanges = ProjectTaskFlowChange::whereTaskId($task->id)->get();
$developTime = 0;//开发时间
$testTime = 0;//验收/测试时间
foreach ($flowChanges as $change) {
if (strpos($change->before_flow_item_name, 'end') === false) {
$upOne = ProjectTaskFlowChange::where('id', '<', $change->id)->whereTaskId($task->id)->orderByDesc('id')->first();
if ($upOne) {
if (strpos($change->before_flow_item_name, 'progress') !== false && strpos($change->before_flow_item_name, '进行') !== false) {
$devCtime = Carbon::parse($change->created_at)->timestamp;
$oCtime = Carbon::parse($upOne->created_at)->timestamp;
$minusNum = $devCtime - $oCtime;
$developTime += $minusNum;
}
if (strpos($change->before_flow_item_name, 'test') !== false || strpos($change->before_flow_item_name, '测试') !== false || strpos($change->before_flow_item_name, '验收') !== false) {
$testCtime = Carbon::parse($change->created_at)->timestamp;
$tTime = Carbon::parse($upOne->created_at)->timestamp;
$tMinusNum = $testCtime - $tTime;
$testTime += $tMinusNum;
}
}
}
}
if (!$task->complete_at) {
$lastChange = ProjectTaskFlowChange::whereTaskId($task->id)->orderByDesc('id')->first();
$nowTime = time();
$unFinishTime = $nowTime - Carbon::parse($lastChange->created_at)->timestamp;
if (strpos($lastChange->after_flow_item_name, 'progress') !== false || strpos($lastChange->after_flow_item_name, '进行') !== false) {
$developTime += $unFinishTime;
} elseif (strpos($lastChange->after_flow_item_name, 'test') !== false || strpos($lastChange->after_flow_item_name, '测试') !== false || strpos($lastChange->after_flow_item_name, '验收') !== false) {
$testTime += $unFinishTime;
}
}
$firstChange = ProjectTaskFlowChange::whereTaskId($task->id)->orderBy('id')->first();
if (strpos($firstChange->after_flow_item_name, 'end') !== false) {
$firstDevTime = Carbon::parse($firstChange->created_at)->timestamp - Carbon::parse($task->created_at)->timestamp;
$developTime += $firstDevTime;
}
if (count($flowChanges) === 0 && $task->start_at) {
$lastTime = $task->complete_at ? Carbon::parse($task->complete_at)->timestamp : time();
$developTime = $lastTime - Carbon::parse($task->start_at)->timestamp;
}
$totalTime = $developTime + $testTime; //任务总用时
if ($task->complete_at) {
$a = Carbon::parse($task->complete_at)->timestamp;
$b = Carbon::parse($task->end_at)->timestamp;
if ($b > $a) {
$endSurplus = Base::timeDiff($a, $b);
} else {
$endSurplus = "-" . Base::timeDiff($b, $a);
if ($task->start_at) {
$b = Carbon::parse($task->start_at)->timestamp;
$totalTime = $a - $b;
}
} else {
$endSurplus = '-';
}
$planTime = '-';//任务计划用时
$overTime = '-';//超时时间
if ($task->end_at) {
$startTime = Carbon::parse($task->start_at)->timestamp;
$endTime = Carbon::parse($task->end_at)->timestamp;
$planTotalTime = $endTime - $startTime;
$residueTime = $planTotalTime - $totalTime;
if ($residueTime < 0) {
$overTime = Base::timeFormat(abs($residueTime));
}
$planTime = Base::timeDiff($startTime, $endTime);
}
$actualTime = $task->complete_at ? $totalTime : 0;//实际完成用时
$datas[] = [
$task->id,
$task->parent_id ?: '-',
Base::filterEmoji($task->project?->name) ?: '-',
Base::filterEmoji($task->name),
Base::filterEmoji(User::userid2nickname($task->ownerid)) . " (ID: {$task->ownerid})",
Base::filterEmoji(User::userid2nickname($task->userid)) . " (ID: {$task->userid})",
$task->complete_at ? '已完成' : '-',
$task->complete_at ?: '-',
$task->archived_at ? '已归档' : '-',
$task->archived_at ?: '-',
$task->start_at ?: '-',
$task->end_at ?: '-',
$endSurplus,
Base::filterEmoji($task->project?->name) ?: '-',
$task->parent_id ?: '-',
$task->complete_at ?: '-',
$task->archived_at ?: '-',
$planTime ?: '-',
$actualTime ? Base::timeFormat($actualTime) : '-',
$overTime,
$developTime > 0? Base::timeFormat($developTime) : '-',
$testTime > 0 ? Base::timeFormat($testTime) : '-',
Base::filterEmoji(User::userid2nickname($task->ownerid)) . " (ID: {$task->ownerid})",
Base::filterEmoji(User::userid2nickname($task->userid)) . " (ID: {$task->userid})",
];
}
});
@@ -1471,6 +1532,9 @@ class ProjectController extends AbstractController
$task_id = intval($data['task_id']);
//
$task = ProjectTask::userTask($task_id, true, 2);
if ($task->deleted_at) {
throw new ApiException('任务已删除');
}
// 更新任务
$updateMarking = [];
$task->updateTask($data, $updateMarking);
@@ -1590,8 +1654,21 @@ class ProjectController extends AbstractController
User::auth();
//
$task_id = intval(Request::input('task_id'));
$type = Request::input('type');
//
$task = ProjectTask::userTask($task_id, null, true);
if($type == 'recovery'){
$task->deleted_at = null;
$task->deleted_userid = 0;
$task->save();
return Base::retSuccess('操作成功', ['id' => $task->id]);
}
if($type == 'completely_delete'){
$task->forceDelete();
return Base::retSuccess('彻底删除成功', ['id' => $task->id]);
}
$task->deleted_userid = User::userid();
$task->save();
//
$task->deleteTask();
return Base::retSuccess('删除成功', ['id' => $task->id]);

View File

@@ -41,7 +41,7 @@ class SystemController extends AbstractController
User::auth('admin');
$all = Request::input();
foreach ($all AS $key => $value) {
if (!in_array($key, ['reg', 'reg_invite', 'login_code', 'password_policy', 'project_invite', 'chat_nickname', 'auto_archived', 'archived_day'])) {
if (!in_array($key, ['reg', 'reg_invite', 'login_code', 'password_policy', 'project_invite', 'chat_nickname', 'auto_archived', 'archived_day', 'start_home', 'home_footer'])) {
unset($all[$key]);
}
}
@@ -72,6 +72,7 @@ class SystemController extends AbstractController
$setting['chat_nickname'] = $setting['chat_nickname'] ?: 'optional';
$setting['auto_archived'] = $setting['auto_archived'] ?: 'close';
$setting['archived_day'] = floatval($setting['archived_day']) ?: 7;
$setting['start_home'] = $setting['start_home'] ?: 'close';
//
return Base::retSuccess('success', $setting ?: json_decode('{}'));
}
@@ -478,4 +479,79 @@ class SystemController extends AbstractController
//
return $data;
}
/**
* @api {get} api/system/get/starthome 13. 启动首页设置信息
*
* @apiDescription 用于判断注册是否需要启动首页
* @apiVersion 1.0.0
* @apiGroup system
* @apiName get__starthome
*
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function get__starthome()
{
return Base::retSuccess('success', [
'need_start' => Base::settingFind('system', 'start_home') == 'open',
'home_footer' => Base::settingFind('system', 'home_footer')
]);
}
/**
* @api {get} api/system/emailSetting 14. 获取邮箱设置、保存邮箱设置
*
* @apiVersion 1.0.0
* @apiGroup system
* @apiName emailSetting
*
* @apiParam {String} type
* - get: 获取(默认)
* - all: 获取所有(需要管理员权限)
* - save: 保存设置(参数:smtp_server port account password reg_verify notice task_remind_hours task_remind_hours2)
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function emailSetting()
{
$type = trim(Request::input('type'));
if ($type == 'save') {
if (env("SYSTEM_SETTING") == 'disabled') {
return Base::retError('当前环境禁止修改');
}
User::auth('admin');
$all = Request::input();
foreach ($all as $key => $value) {
if (!in_array($key, ['smtp_server', 'port', 'account', 'password', 'reg_verify', 'notice', 'task_remind_hours', 'task_remind_hours2'])) {
unset($all[$key]);
}
}
$setting = Base::setting('emailSetting', Base::newTrim($all));
} else {
$setting = Base::setting('emailSetting');
}
//
if ($type == 'all' || $type == 'save') {
User::auth('admin');
$setting['reg_invite'] = $setting['reg_invite'] ?: Base::generatePassword(8);
} else {
if (isset($setting['reg_invite'])) unset($setting['reg_invite']);
}
//
$setting['smtp_server'] = $setting['smtp_server'] ?: '';
$setting['port'] = $setting['port'] ?: '';
$setting['account'] = $setting['account'] ?: '';
$setting['password'] = $setting['password'] ?: '';
$setting['reg_verify'] = $setting['reg_verify'] ?: 'close';
$setting['notice'] = $setting['notice'] ?: 'open';
$setting['task_remind_hours'] = floatval($setting['task_remind_hours']) ?: 0;
$setting['task_remind_hours2'] = floatval($setting['task_remind_hours2']) ?: 0;
//
return Base::retSuccess('success', $setting ?: json_decode('{}'));
}
}

View File

@@ -3,12 +3,14 @@
namespace App\Http\Controllers\Api;
use App\Models\User;
use App\Models\UserEmailVerification;
use App\Module\Base;
use Arr;
use Cache;
use Captcha;
use Carbon\Carbon;
use Request;
use Validator;
/**
* @apiDefine users
@@ -42,6 +44,7 @@ class UsersController extends AbstractController
$type = trim(Request::input('type'));
$email = trim(Request::input('email'));
$password = trim(Request::input('password'));
$isRegVerify = Base::settingFind('emailSetting', 'reg_verify') === 'open' ? true : false;
if ($type == 'reg') {
$setting = Base::setting('system');
if ($setting['reg'] == 'close') {
@@ -53,6 +56,10 @@ class UsersController extends AbstractController
}
}
$user = User::reg($email, $password);
if ($isRegVerify) {
UserEmailVerification::userEmailSend($user);
return Base::retError('注册成功,请验证邮箱后登录', ['code' => 1000]);
}
} else {
$needCode = !Base::isError(User::needCode($email));
if ($needCode) {
@@ -68,21 +75,25 @@ class UsersController extends AbstractController
$retError = function ($msg) use ($email) {
Cache::forever("code::" . $email, "need");
$needCode = !Base::isError(User::needCode($email));
$needData = [ 'code' => $needCode ? 'need' : 'no' ];
$needData = ['code' => $needCode ? 'need' : 'no'];
return Base::retError($msg, $needData);
};
$user = User::whereEmail($email)->first();
if (empty($user)) {
return $retError('账号或密码错误');
return $retError('账号不存在,请确认账号是否输入正确');
}
if ($user->password != Base::md52($password, $user->encrypt)) {
return $retError('账号或密码错误');
return $retError('密码错误,请输入正确密码');
}
//
if (in_array('disable', $user->identity)) {
return $retError('帐号已停用...');
}
Cache::forget("code::" . $email);
if ($isRegVerify && $user->is_email_verity === 0) {
UserEmailVerification::userEmailSend($user);
return $retError('请验证邮箱后再登录');
}
}
//
$array = [
@@ -559,4 +570,53 @@ class UsersController extends AbstractController
//
return Base::retSuccess('修改成功', $userInfo);
}
/**
* @api {get} api/users/email/verification 13. 邮箱验证
*
* @apiDescription 不需要token身份
* @apiVersion 1.0.0
* @apiGroup users
* @apiName email__verification
*
* @apiParam {String} code 验证参数
*
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据(同"获取我的信息"接口)
*/
public function email__verification()
{
$data = Request::input();
// 表单验证
$validator = Validator::make($data, [
"code" => ["required"],
], [
"code.required" => "required字段非法",
]);
if ($validator->fails())
return Base::retError($validator->errors()->first());
$res = UserEmailVerification::where('code', $data['code'])->first();
if (empty($res)) {
return Base::retError('无效连接,请重新注册');
}
// 如果已经校验过
if (intval($res->status) === 1)
return Base::retError('链接已经使用过',['code' => 2]);
$oldTime = strtotime($res->created_at);
$time = time();
//24个小时失效
if (abs($time - $oldTime) > 86400) {
return Base::retError("链接已失效,请重新登录/注册");
}
UserEmailVerification::where('code', $data['code'])
->update([
'status' => 1
]);
User::where('userid', $res->userid)->update([
'is_email_verity' => 1
]);
return Base::retSuccess('绑定邮箱成功');
}
}

View File

@@ -5,6 +5,7 @@ namespace App\Http\Controllers;
use App\Module\Base;
use App\Tasks\AutoArchivedTask;
use App\Tasks\DeleteTmpTask;
use App\Tasks\OverdueRemindEmailTask;
use Hhxsv5\LaravelS\Swoole\Task\Task;
use Redirect;
@@ -61,6 +62,8 @@ class IndexController extends InvokeController
Task::deliver(new DeleteTmpTask('tmp', 24));
// 自动归档任务
Task::deliver(new AutoArchivedTask());
// 任务到期邮件提醒
Task::deliver(new OverdueRemindEmailTask());
return "success";
}