导出进度调整为按 Excel 实际导出进度展示的规则
1、现在的 Excel 导出进度是由前端自行显示的,并未受到后端的控制。如图1
2、决定调整为按 Excel 实际导出进度展示,最终实现的规则如下 :
现在导出 Excel 文件,一般来说,是后端接收到前端请求后,总计有 4 个步骤:
(1) 在队列中排队。在此环节,前端进度一直显示 0%
(2) 然后当队列作业真正开始时,如果 Excel 为 1 万行,那么每写入 1000 行,进度就增加 9.9% 了。当写入完毕后,进度就是 99%
(3) 然后涉及到 Excel 文件的一些列格式的处理,还有上传文件到云上,生成下载文件链接等。此环节完成后,前端显示进度是 100%
(4) 前端获取到下载链接后,到下载成功。此环节,进度不再显示。
3、最终的代码实现大致如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | private string $cachedProgressPercentKey = 'job_status:progress_percent' ; // 进度百分比的缓存键 private int $cachedProgressPercentTtl = 24 * 3600; // 进度百分比的缓存过期时间 public const EXPORT_EXCEL_WRITE_PROGRESS_PERCENT = 99; // 导出 Excel 时写入文件所占用的百分比 private int $exportExcelMaxCount = 100000; // 限制最大导出行数为 100000 private int $exportExcelChunkCount = 1000; // 导出时分块数量为 1000 // 更新进度百分比 $orderShippingLogCount = min( $orderShippingLogQueryBuilder -> count (), $this ->exportExcelMaxCount); $chunkCount = (int) ceil ( $orderShippingLogCount / $this ->exportExcelChunkCount); // 上舍入 $i = 0; $uid = $jobStatus ->uid; Log::info( '$orderShippingLog' , [ '$orderShippingLogCount' => $orderShippingLogCount , '$chunkCount' => $chunkCount , ] ); $orderShippingLogQueryBuilder ->chunk( $this ->exportExcelChunkCount, function ( $orderShippingLogs ) use ( $params , $excel , $columns , $chunkCount , $uid , & $i ) { // 限制最大导出行数为 100000 if ( $i >= $chunkCount ) { return false; } $data = []; foreach ( $orderShippingLogs as $rowKey => $orderShippingLog ) { $data [] = static ::toResource( $orderShippingLog , $params [ 'timezone' ]); } $excel ->writeRows( $data ); // 更新进度百分比 $i ++; $progressPercent = ( $i / $chunkCount ) * JobStatusService::EXPORT_EXCEL_WRITE_PROGRESS_PERCENT; app(JobStatusService:: class )->setProgressPercent( $uid , $progressPercent ); Log::info( '$orderShippingLogChunk' , [ '$i' => $i , '$chunkCount' => $chunkCount , '$progressPercent' => $progressPercent ] ); return true; }); // 更新进度百分比 $jobStatusService ->setProgressPercent( $this ->jobStatus->uid, 100); /** * 设置进度百分比 * @param string $uid * @param float $progressPercent * @return void */ public function setProgressPercent(string $uid , float $progressPercent ): void { $cachedKey = sprintf( $this ->cachedProgressPercentKey . ':%s' , $uid ); Cache::store( 'redis' )->put( $cachedKey , round ( $progressPercent , 2), $this ->cachedProgressPercentTtl); } /** * 获取进度百分比 * @param string $uid * @return float * @throws InvalidArgumentException */ private function getProgressPercent(string $uid ): float { $cachedKey = sprintf( $this ->cachedProgressPercentKey . ':%s' , $uid ); $cachedProgressPercent = Cache::store( 'redis' )->get( $cachedKey ); if (! $cachedProgressPercent ) { return 0; } return (float) $cachedProgressPercent ; } |
4、日志打印如下:
1 2 3 4 5 6 | [2024-05-10 07:33:35] local.INFO: $orderShippingLog {"$orderShippingLogCount":4696,"$chunkCount":5} [2024-05-10 07:33:37] local.INFO: $orderShippingLogChunk {"$i":1,"$chunkCount":5,"$progressPercent":19.8} [2024-05-10 07:33:40] local.INFO: $orderShippingLogChunk {"$i":2,"$chunkCount":5,"$progressPercent":39.6} [2024-05-10 07:33:41] local.INFO: $orderShippingLogChunk {"$i":3,"$chunkCount":5,"$progressPercent":59.4} [2024-05-10 07:33:42] local.INFO: $orderShippingLogChunk {"$i":4,"$chunkCount":5,"$progressPercent":79.2} [2024-05-10 07:33:43] local.INFO: $orderShippingLogChunk {"$i":5,"$chunkCount":5,"$progressPercent":99} |
5、接口响应结构如下
1 2 3 4 5 6 7 | { "label": "order", "type": "export", "status": "pending", "file_path": "", "progress_percent": 59.4 } |
近期评论