123 lines
4.0 KiB
PHP
123 lines
4.0 KiB
PHP
<?php
|
||
|
||
namespace Modules\EcommerceFinance\Http\Concerns;
|
||
|
||
use Modules\EcommerceFinance\Services\AccountLedgerRemarkMap;
|
||
|
||
/**
|
||
* 店铺收款账户账单:给每行账单补「总账备注」(按平台 + 账单项目匹配映射表)。
|
||
*
|
||
* 只在读取时补齐,不改动上游账单与镜像数据。
|
||
*/
|
||
trait DecoratesLedgerRemarks
|
||
{
|
||
/** 「总账备注」在本页签没有对应账单项目时的占位条件(保证命中 0 行)。 */
|
||
private const LEDGER_REMARK_NO_MATCH_ITEM = '__no_such_ledger_remark_item__';
|
||
|
||
/**
|
||
* 导出进度 token(前端生成,随导出的 URL 一起传回来)。
|
||
* 只接受安全字符,避免把缓存键写脏。
|
||
*/
|
||
protected function exportProgressToken(\Illuminate\Http\Request $request): string
|
||
{
|
||
$token = trim((string) $request->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<string,mixed> $filters
|
||
* @return array<string,mixed>
|
||
*/
|
||
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<string,mixed> $data
|
||
* @return array<string,mixed>
|
||
*/
|
||
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<int,array<string,mixed>> $rows
|
||
* @return iterable<int,array<string,mixed>>
|
||
*/
|
||
protected function ledgerRemarkRows(iterable $rows, string $tab): iterable
|
||
{
|
||
return $this->ledgerRemarkMap()->decorateAll($rows, $tab);
|
||
}
|
||
}
|