没有不值得去解决的问题,也没有不值得去学习的技术!

导出进度调整为按 Excel 实际导出进度展示的规则

现在的 Excel 导出进度是由前端自行显示的,并未受到后端的控制

作者:

1、现在的 Excel 导出进度是由前端自行显示的,并未受到后端的控制。如图1
现在的 Excel 导出进度是由前端自行显示的,并未受到后端的控制
图1
2、决定调整为按 Excel 实际导出进度展示,最终实现的规则如下 : 现在导出 Excel 文件,一般来说,是后端接收到前端请求后,总计有 4 个步骤: (1) 在队列中排队。在此环节,前端进度一直显示 0% (2) 然后当队列作业真正开始时,如果 Excel 为 1 万行,那么每写入 1000 行,进度就增加 9.9% 了。当写入完毕后,进度就是 99% (3) 然后涉及到 Excel 文件的一些列格式的处理,还有上传文件到云上,生成下载文件链接等。此环节完成后,前端显示进度是 100% (4) 前端获取到下载链接后,到下载成功。此环节,进度不再显示。 3、最终的代码实现大致如下所示



    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、日志打印如下:


[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、接口响应结构如下


{
    "label": "order",
    "type": "export",
    "status": "pending",
    "file_path": "",
    "progress_percent": 59.4
}


需要长期技术维护或远程问题排查?

我是拥有 15+ 年经验的 PHP / Go 后端工程师,长期关注已有系统维护、Bug 修复、性能优化、服务器排查、WordPress 网站维护和小功能迭代。

如果你的项目遇到以下情况,可以先从一次小问题排查开始合作:

  • ✅ PHP / Laravel / Yii2 老项目无人维护
  • ✅ Go / Gin 后端接口需要排查或优化
  • ✅ WordPress 网站访问慢、报错或插件冲突
  • ✅ Nginx / MySQL / Redis / Linux 服务器异常
  • ✅ CDN / Cloudflare / DNS / HTTPS 配置问题
  • ✅ 需要长期远程技术支持或兼职维护

更多介绍请查看:关于我 & 合作

微信:13980074657
邮箱:shuijingwanwq@gmail.com
Telegram:@shuijingwan
GitHub:https://github.com/shuijingwan

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理