get('token', '')); return preg_match('/^[A-Za-z0-9_-]{8,64}$/', $token) === 1 ? $token : ''; } /** 导出进度缓存键:按当前用户隔离。 */ protected static function exportProgressKey(string $token): string { return 'account-bills-export:' . (int) user()->id . ':' . $token; } /** * 写导出进度,供前端轮询显示(token 为空时什么都不做)。 * * @param int $rows 已写入行数 * @param int $total 预计总行数(前端传过来的列表总数,用于算百分比) */ protected function putExportProgress(string $token, int $rows, int $total, string $status = 'running', ?string $error = null): void { if ($token === '') { return; } $state = ['status' => $status, 'rows' => $rows, 'total' => max($total, $rows)]; if ($error !== null) { $state['error'] = $error; } \Illuminate\Support\Facades\Cache::put(self::exportProgressKey($token), $state, now()->addMinutes(30)); } protected function ledgerRemarkMap(): AccountLedgerRemarkMap { return app(AccountLedgerRemarkMap::class); } /** * 把「总账备注」筛选翻译成账单项目集合。 * * 页面、列设置与导出都调用这里,保证同一份筛选口径; * bill_items 供各仓库 whereIn(账单项目) 使用,bill_item_groups 供 * 淘系这种同一页签混多平台的仓库按店铺名前缀再细分。 * * @param array $filters * @return array */ protected function withLedgerRemarkFilter(array $filters, string $tab): array { $remark = trim((string) ($filters['ledger_remark'] ?? '')); if ($remark === '') { return $filters; } $groups = $this->ledgerRemarkMap()->billItemGroupsForTab($tab, $remark); $filters['bill_item_groups'] = $groups; $items = []; foreach ($groups as $group) { foreach ($group['items'] as $item) { $items[$item] = true; } } $filters['bill_items'] = array_keys($items); if ($filters['bill_items'] === []) { $filters['bill_items'] = [self::LEDGER_REMARK_NO_MATCH_ITEM]; } return $filters; } /** * 分页结果(含 data 行数组)补总账备注。 * * @param array $data * @return array */ protected function withLedgerRemarks(array $data, string $tab): array { if (! empty($data['data']) && is_array($data['data'])) { $map = $this->ledgerRemarkMap(); $data['data'] = array_map( static fn (mixed $row): mixed => is_array($row) ? $map->decorate($row, $tab) : $row, $data['data'], ); } return $data; } /** * 导出/流式行补总账备注。 * * @param iterable> $rows * @return iterable> */ protected function ledgerRemarkRows(iterable $rows, string $tab): iterable { return $this->ledgerRemarkMap()->decorateAll($rows, $tab); } }