CaiWuXiTong/modules/EcommerceFinance/View/AccessoryReshipPageData.php

88 lines
4.7 KiB
PHP

<?php
namespace Modules\EcommerceFinance\View;
use Illuminate\Pagination\LengthAwarePaginator;
use Modules\EcommerceFinance\Repositories\ShippingOrderRepository;
use Modules\EcommerceFinance\Services\AccessoryReshipMatcher;
class AccessoryReshipPageData
{
public function __construct(private ShippingOrderRepository $shipping, private AccessoryReshipMatcher $matcher) {}
public function filters(): array
{
$month = request()->query('month', now()->format('Y-m'));
abort_unless(is_string($month) && preg_match('/^\d{4}-(0[1-9]|1[0-2])$/', $month) && (int) substr($month, 0, 4) >= 2000, 422, '请选择有效业务月份。');
$status = request()->query('match_status', 'matched');
$filters = ['month' => $month, 'match_status' => in_array($status, ['matched', 'unmatched', 'all'], true) ? $status : 'matched'];
foreach (['trade_no','express_no','courier','ship_date','barcode','normalized_barcode','freight','original_courier','original_express_no','original_month','deduction_amount'] as $field) {
$value = request()->query($field, '');
abort_unless(is_string($value) && mb_strlen($value) <= 200, 422, '筛选条件格式无效。');
$filters[$field] = trim($value);
}
return $filters;
}
public function rows(array $filters): \Generator
{
$start = \Carbon\Carbon::createFromFormat('!Y-m', $filters['month']);
foreach ($this->shipping->accessoryReshipBatches($start->format('Y-m-d'), $start->copy()->addMonth()->format('Y-m-d'), $start->copy()->subMonths(2)->format('Y-m-d')) as $batch) {
foreach ($this->matcher->match($batch['accessories'], $batch['originals']) as $row) {
if ($this->matchesFilters($row, $filters)) yield $row;
}
}
}
public function matchesFilters(array $row, array $filters): bool
{
$values = [
'trade_no' => $row['trade_no'], 'express_no' => ($row['express_no'] ?? ''),
'courier' => ($row['express_name1'] ?? '') ?: ($row['express_name2'] ?? ''),
'ship_date' => substr((string) ($row['ship_time'] ?? ''), 0, 10),
'barcode' => $row['barcode'], 'normalized_barcode' => $row['normalized_barcode'],
'freight' => $row['freight_cents'] === null ? '待核实' : number_format($row['freight_cents'] / 100, 2, '.', ''),
'deduction_amount' => $row['deduction_cents'] === null ? '待核实' : number_format($row['deduction_cents'] / 100, 2, '.', ''),
];
foreach ($values as $field => $value) {
if (($filters[$field] ?? '') !== '' && mb_stripos((string) $value, $filters[$field]) === false) return false;
}
$originalFilters = array_filter(['courier' => $filters['original_courier'] ?? '', 'express_no' => $filters['original_express_no'] ?? '', 'month' => $filters['original_month'] ?? ''], static fn ($value) => $value !== '');
if (!$originalFilters) return true;
foreach ($row['matches'] as $match) {
foreach ($originalFilters as $field => $value) {
if (mb_stripos((string) $match[$field], $value) === false) continue 2;
}
return true;
}
return false;
}
public function page(): array
{
$filters = $this->filters();
$page = max(1, (int) request('page', 1));
$summary = ['all' => 0, 'matched' => 0, 'unmatched' => 0, 'amount' => 0, 'invalid_amount' => 0, 'ambiguous' => 0];
$items = []; $total = 0; $error = null;
try {
foreach ($this->rows($filters) as $row) {
$summary['all']++; $summary[$row['status']]++;
if ($row['status'] === 'matched') {
$summary['amount'] += $row['deduction_cents'] ?? 0;
if ($row['deduction_cents'] === null) $summary['invalid_amount']++;
if (count($row['matches']) > 1) $summary['ambiguous']++;
}
if ($filters['match_status'] !== 'all' && $filters['match_status'] !== $row['status']) continue;
if ($total >= ($page - 1) * 50 && count($items) < 50) $items[] = $row;
$total++;
}
} catch (\Throwable $exception) {
report($exception); $error = '发货数据暂时无法读取,请检查数据连接或同步状态后重试。';
$items = []; $total = 0;
$summary = array_fill_keys(array_keys($summary), 0);
}
return ['view' => 'business.pages.courier-payables.accessory-reship-deductions', 'reshipFilters' => $filters, 'reshipSummary' => $summary, 'reshipError' => $error,
'reshipRows' => new LengthAwarePaginator($items, $total, 50, $page, ['path' => request()->url(), 'query' => request()->except('page')])];
}
}