镜像 ops_jst_orders 只覆盖 2026-04-01 之后,缺的历史区间只能靠财务在聚水潭「导出订单」导文件补。所以:- 新增 JstOrderExportReader:ZipArchive+expat 流式解析聚水潭导出 xlsx。这类文件字符串走 inlineStr、dimension 只写 A1、单文件上万行 ×135 列(解压 78MB),常规读法会读成空表或吃满内存;实测 12286 行 3.5 秒、峰值内存 10MB。 - jst-order-labels:import 新增 --file=:按「线上订单号+标签」去重后拆行写入,可配合 --from/--to 过滤窗口;带 --replace 时先成功读到数据行再清区间,避免文件有问题却先删了线上数据。 - 筛选下拉上限 500 -> 5000:导入 4~7 月后店铺已达 604 个,原来下拉里会缺 104 个店铺;提交筛选值的截断上限同步放开。 - 补两个测试:导出文件去重拆行、窗口过滤+replace。
100 lines
2.8 KiB
PHP
100 lines
2.8 KiB
PHP
<?php
|
||
|
||
namespace App\Business;
|
||
|
||
use App\Models\Business\JstOrderLabel;
|
||
use Illuminate\Support\Facades\Cache;
|
||
|
||
/**
|
||
* 聚水潭订单标签明细的筛选下拉选项。
|
||
*
|
||
* 表里有 200 万行以上,distinct 扫描一次要几秒;这里按公司缓存 24 小时,
|
||
* 导入命令结束时会主动预热,避免每次打开页面都慢。
|
||
*
|
||
* 上限只是防呆(选项过多会拖慢页面),要高于实际店铺数:2026-10 导入 4~7 月后
|
||
* 店铺已达 604 个,原来卡在 500 会让一部分店铺选不出来。
|
||
*/
|
||
class JstOrderLabelOptions
|
||
{
|
||
private const TTL_HOURS = 24;
|
||
|
||
private const TOTAL_TTL_MINUTES = 30;
|
||
|
||
private const LIMIT = 5000;
|
||
|
||
/** @return list<string> */
|
||
public function labels(int $companyId): array
|
||
{
|
||
return $this->remember($companyId, 'label');
|
||
}
|
||
|
||
/** @return list<string> */
|
||
public function shops(int $companyId): array
|
||
{
|
||
return $this->remember($companyId, 'shop_name');
|
||
}
|
||
|
||
public function warm(int $companyId): void
|
||
{
|
||
$this->labels($companyId);
|
||
$this->shops($companyId);
|
||
$this->total($companyId);
|
||
}
|
||
|
||
public function forget(int $companyId): void
|
||
{
|
||
Cache::forget($this->key($companyId, 'label'));
|
||
Cache::forget($this->key($companyId, 'shop_name'));
|
||
Cache::forget($this->totalKey($companyId));
|
||
}
|
||
|
||
/** 无筛选时的总行数:233 万行上 COUNT(*) 要 2 秒,缓存后翻页不再重复统计。 */
|
||
public function total(int $companyId): int
|
||
{
|
||
$key = $this->totalKey($companyId);
|
||
$cached = Cache::get($key);
|
||
if (is_numeric($cached)) {
|
||
return (int) $cached;
|
||
}
|
||
|
||
$count = (int) JstOrderLabel::query()->where('company_id', $companyId)->count();
|
||
Cache::put($key, $count, now()->addMinutes(self::TOTAL_TTL_MINUTES));
|
||
|
||
return $count;
|
||
}
|
||
|
||
/** @return list<string> */
|
||
private function remember(int $companyId, string $column): array
|
||
{
|
||
$key = $this->key($companyId, $column);
|
||
$cached = Cache::get($key);
|
||
if (is_array($cached)) {
|
||
return $cached;
|
||
}
|
||
|
||
$values = JstOrderLabel::query()
|
||
->where('company_id', $companyId)
|
||
->whereNotNull($column)
|
||
->where($column, '<>', '')
|
||
->distinct()
|
||
->orderBy($column)
|
||
->limit(self::LIMIT)
|
||
->pluck($column)
|
||
->all();
|
||
|
||
Cache::put($key, $values, now()->addHours(self::TTL_HOURS));
|
||
|
||
return $values;
|
||
}
|
||
|
||
private function key(int $companyId, string $column): string
|
||
{
|
||
return "jst-order-labels:options:v1:{$companyId}:{$column}";
|
||
}
|
||
|
||
private function totalKey(int $companyId): string
|
||
{
|
||
return "jst-order-labels:total:v1:{$companyId}";
|
||
}
|
||
}
|