perf: 优化导出任务功能

This commit is contained in:
kuaifan 2025-06-04 14:52:40 +08:00
parent 31879cb60b
commit 25e82d690e
4 changed files with 164 additions and 101 deletions

View File

@ -1258,7 +1258,7 @@ class ProjectController extends AbstractController
$dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid); $dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid);
// //
go(function () use ($user, $userid, $time, $type, $botUser, $dialog) { go(function () use ($user, $userid, $time, $type, $botUser, $dialog) {
Coroutine::sleep(0.1); Coroutine::sleep(1);
$headings = []; $headings = [];
$headings[] = Doo::translate('任务ID'); $headings[] = Doo::translate('任务ID');
$headings[] = Doo::translate('父级任务ID'); $headings[] = Doo::translate('父级任务ID');
@ -1394,7 +1394,7 @@ class ProjectController extends AbstractController
'type' => 'content', 'type' => 'content',
'title' => $content[0]['content'], 'title' => $content[0]['content'],
'content' => $content, 'content' => $content,
], $botUser->userid, false, false, true); ], $botUser->userid, true, false, true);
return; return;
} }
// //
@ -1428,7 +1428,7 @@ class ProjectController extends AbstractController
'type' => 'content', 'type' => 'content',
'title' => $content[0]['content'], 'title' => $content[0]['content'],
'content' => $content, 'content' => $content,
], $botUser->userid, false, false, true); ], $botUser->userid, true, false, true);
return; return;
} }
// //
@ -1455,7 +1455,7 @@ class ProjectController extends AbstractController
'name' => $fileName, 'name' => $fileName,
'size' => filesize($zipPath), 'size' => filesize($zipPath),
'url' => $fileUrl, 'url' => $fileUrl,
], $botUser->userid, false, false, true); ], $botUser->userid, true, false, true);
} else { } else {
$content[] = [ $content[] = [
'content' => "打包失败,请稍后再试...", 'content' => "打包失败,请稍后再试...",
@ -1465,9 +1465,15 @@ class ProjectController extends AbstractController
'type' => 'content', 'type' => 'content',
'title' => $content[0]['content'], 'title' => $content[0]['content'],
'content' => $content, 'content' => $content,
], $botUser->userid, false, false, true); ], $botUser->userid, true, false, true);
} }
}); });
//
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'content',
'content' => '正在导出任务统计,请稍等...',
], $botUser->userid, true, false, true);
//
return Base::retSuccess('success'); return Base::retSuccess('success');
} }
@ -1487,103 +1493,156 @@ class ProjectController extends AbstractController
{ {
$user = User::auth('admin'); $user = User::auth('admin');
// //
$headings = []; $botUser = User::botGetOrCreate('system-msg');
$headings[] = Doo::translate('任务ID'); if (empty($botUser)) {
$headings[] = Doo::translate('父级任务ID'); return Base::retError('系统机器人不存在');
$headings[] = Doo::translate('所属项目'); }
$headings[] = Doo::translate('任务标题'); $dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid);
$headings[] = Doo::translate('任务标签');
$headings[] = Doo::translate('任务开始时间');
$headings[] = Doo::translate('任务结束时间');
$headings[] = Doo::translate('任务计划用时');
$headings[] = Doo::translate('超时时间');
$headings[] = Doo::translate('负责人');
$headings[] = Doo::translate('创建人');
$data = [];
// //
ProjectTask::with(['taskTag']) go(function () use ($botUser, $dialog, $user) {
->whereNull('complete_at') Coroutine::sleep(1);
->whereNotNull('end_at') //
->where('end_at', '<=', Carbon::now()) $headings = [];
->orderBy('end_at') $headings[] = Doo::translate('任务ID');
->chunk(100, function ($tasks) use (&$data) { $headings[] = Doo::translate('父级任务ID');
/** @var ProjectTask $task */ $headings[] = Doo::translate('所属项目');
foreach ($tasks as $task) { $headings[] = Doo::translate('任务标题');
$taskStartTime = Carbon::parse($task->start_at ?: $task->created_at)->timestamp; $headings[] = Doo::translate('任务标签');
$totalTime = time() - $taskStartTime; //开发测试总用时 $headings[] = Doo::translate('任务开始时间');
$planTime = '-';//任务计划用时 $headings[] = Doo::translate('任务结束时间');
$overTime = '-';//超时时间 $headings[] = Doo::translate('任务计划用时');
if ($task->end_at) { $headings[] = Doo::translate('超时时间');
$startTime = Carbon::parse($task->start_at)->timestamp; $headings[] = Doo::translate('负责人');
$endTime = Carbon::parse($task->end_at)->timestamp; $headings[] = Doo::translate('创建人');
$planTotalTime = $endTime - $startTime; $data = [];
$residueTime = $planTotalTime - $totalTime; //
if ($residueTime < 0) { $content = [];
$overTime = Doo::translate(Timer::timeFormat(abs($residueTime))); $content[] = [
'content' => '导出超期任务已完成',
'style' => 'font-weight: bold;padding-bottom: 4px;',
];
//
ProjectTask::with(['taskTag'])
->whereNull('complete_at')
->whereNotNull('end_at')
->where('end_at', '<=', Carbon::now())
->orderBy('end_at')
->chunk(100, function ($tasks) use (&$data) {
/** @var ProjectTask $task */
foreach ($tasks as $task) {
$taskStartTime = Carbon::parse($task->start_at ?: $task->created_at)->timestamp;
$totalTime = time() - $taskStartTime; //开发测试总用时
$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 = Doo::translate(Timer::timeFormat(abs($residueTime)));
}
$planTime = Doo::translate(Timer::timeDiff($startTime, $endTime));
} }
$planTime = Doo::translate(Timer::timeDiff($startTime, $endTime)); $ownerIds = $task->taskUser->where('owner', 1)->pluck('userid')->toArray();
$ownerNames = [];
foreach ($ownerIds as $ownerId) {
$ownerNames[] = Base::filterEmoji(User::userid2nickname($ownerId)) . " (ID: {$ownerId})";
}
$data[] = [
$task->id,
$task->parent_id ?: '-',
Base::filterEmoji($task->project?->name) ?: '-',
Base::filterEmoji($task->name),
$task->taskTag->map(function ($tag) {
return Base::filterEmoji($tag->name);
})->join(', ') ?: '-',
$task->start_at ?: '-',
$task->end_at ?: '-',
$planTime,
$overTime,
implode(', ', $ownerNames),
Base::filterEmoji(User::userid2nickname($task->userid)) . " (ID: {$task->userid})",
];
} }
$ownerIds = $task->taskUser->where('owner', 1)->pluck('userid')->toArray(); });
$ownerNames = []; if (empty($data)) {
foreach ($ownerIds as $ownerId) { $content[] = [
$ownerNames[] = Base::filterEmoji(User::userid2nickname($ownerId)) . " (ID: {$ownerId})"; 'content' => '没有任何数据',
} 'style' => 'color: #ff0000;',
$data[] = [ ];
$task->id, WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
$task->parent_id ?: '-', 'type' => 'content',
Base::filterEmoji($task->project?->name) ?: '-', 'title' => $content[0]['content'],
Base::filterEmoji($task->name), 'content' => $content,
$task->taskTag->map(function ($tag) { ], $botUser->userid, true, false, true);
return Base::filterEmoji($tag->name); return;
})->join(', ') ?: '-', }
$task->start_at ?: '-', //
$task->end_at ?: '-', $title = Doo::translate('超期任务');
$planTime, $sheets = [
$overTime, BillExport::create()->setTitle($title)->setHeadings($headings)->setData($data)->setStyles(["A1:J1" => ["font" => ["bold" => true]]])
implode(', ', $ownerNames), ];
Base::filterEmoji(User::userid2nickname($task->userid)) . " (ID: {$task->userid})", //
]; $fileName = $title . '_' . Timer::time() . '.xls';
} $filePath = "temp/task/export/" . date("Ym", Timer::time());
}); $export = new BillMultipleExport($sheets);
if (empty($data)) { $res = $export->store($filePath . "/" . $fileName);
return Base::retError('没有任何数据'); if ($res != 1) {
} $content[] = [
'content' => "导出失败,{$fileName}",
'style' => 'color: #ff0000;',
];
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'content',
'title' => $content[0]['content'],
'content' => $content,
], $botUser->userid, true, false, true);
return;
}
$xlsPath = storage_path("app/" . $filePath . "/" . $fileName);
$zipFile = "app/" . $filePath . "/" . Base::rightDelete($fileName, '.xls') . ".zip";
$zipPath = storage_path($zipFile);
if (file_exists($zipPath)) {
Base::deleteDirAndFile($zipPath, true);
}
try {
Madzipper::make($zipPath)->add($xlsPath)->close();
} catch (\Throwable) {
}
//
if (file_exists($zipPath)) {
$base64 = base64_encode(Base::array2string([
'file' => $zipFile,
]));
$fileUrl = Base::fillUrl('api/project/task/down?key=' . urlencode($base64));
Session::put('task::export:userid', $user->userid);
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'file_download',
'title' => '导出超期任务已完成',
'name' => $fileName,
'size' => filesize($zipPath),
'url' => $fileUrl,
], $botUser->userid, true, false, true);
} else {
$content[] = [
'content' => "打包失败,请稍后再试...",
'style' => 'color: #ff0000;',
];
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
'type' => 'content',
'title' => $content[0]['content'],
'content' => $content,
], $botUser->userid, true, false, true);
}
});
// //
$title = Doo::translate('超期任务'); WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [
$sheets = [ 'type' => 'content',
BillExport::create()->setTitle($title)->setHeadings($headings)->setData($data)->setStyles(["A1:J1" => ["font" => ["bold" => true]]]) 'content' => '正在导出超期任务,请稍等...',
]; ], $botUser->userid, true, false, true);
// //
$fileName = $title . '_' . Timer::time() . '.xls'; return Base::retSuccess('success');
$filePath = "temp/task/export/" . date("Ym", Timer::time());
$export = new BillMultipleExport($sheets);
$res = $export->store($filePath . "/" . $fileName);
if ($res != 1) {
return Base::retError('导出失败,' . $fileName . '');
}
$xlsPath = storage_path("app/" . $filePath . "/" . $fileName);
$zipFile = "app/" . $filePath . "/" . Base::rightDelete($fileName, '.xls') . ".zip";
$zipPath = storage_path($zipFile);
if (file_exists($zipPath)) {
Base::deleteDirAndFile($zipPath, true);
}
try {
Madzipper::make($zipPath)->add($xlsPath)->close();
} catch (\Throwable) {
}
//
if (file_exists($zipPath)) {
$base64 = base64_encode(Base::array2string([
'file' => $zipFile,
]));
Session::put('task::export:userid', $user->userid);
return Base::retSuccess('success', [
'size' => Base::twoFloat(filesize($zipPath) / 1024, true),
'url' => Base::fillUrl('api/project/task/down?key=' . urlencode($base64)),
]);
} else {
return Base::retError('打包失败,请稍后再试...');
}
} }
/** /**

View File

@ -87,6 +87,9 @@
没有任何数据 没有任何数据
导出失败,(*) 导出失败,(*)
打包失败,请稍后再试... 打包失败,请稍后再试...
正在导出任务统计,请稍等...
导出超期任务已完成
正在导出超期任务,请稍等...
任务列表不存在或已被删除 任务列表不存在或已被删除
主任务已完成无法添加子任务 主任务已完成无法添加子任务
子任务不支持此功能 子任务不支持此功能

View File

@ -1639,6 +1639,9 @@ Token
导出任务统计已完成 导出任务统计已完成
没有任何数据 没有任何数据
打包失败,请稍后再试... 打包失败,请稍后再试...
正在导出任务统计,请稍等...
导出超期任务已完成
正在导出超期任务,请稍等...
(*)查看了(*)的联系电话 (*)查看了(*)的联系电话
标记任务未完成 标记任务未完成
标记任务已完成 标记任务已完成

View File

@ -844,11 +844,9 @@ export default {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.$store.dispatch("call", { this.$store.dispatch("call", {
url: 'project/task/exportoverdue', url: 'project/task/exportoverdue',
}).then(({data}) => { }).then(() => {
resolve(); resolve();
this.$store.dispatch('downUrl', { $A.modalSuccess('正在打包,请留意系统消息。');
url: data.url
});
}).catch(({msg}) => { }).catch(({msg}) => {
reject(msg); reject(msg);
}); });