CaiWuXiTong/modules/EcommerceFinance/Repositories/AfterSalesCostRowRepository.php

161 lines
5.3 KiB
PHP

<?php
namespace Modules\EcommerceFinance\Repositories;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
/**
* 订单售后费用明细(聚水潭「商品利润 → 导出明细数据 → 订单售后费用数据」导入)。
*
* 聚水潭报表已经按商品编码(组合装拆成组件)给出行和成本,页面直接读这张导入表,
* 不再从订单明细里自己拆组合装。
*/
class AfterSalesCostRowRepository
{
private const TABLE = 'ops_after_sales_cost_rows';
private ?bool $available = null;
public function available(): bool
{
if ($this->available === null) {
try {
$this->available = Schema::hasTable(self::TABLE)
&& DB::table(self::TABLE)->exists();
} catch (\Throwable) {
$this->available = false;
}
}
return $this->available;
}
/**
* 导入数据是否覆盖本次筛选(有至少一行命中即视为覆盖)。
*
* @param array<string,mixed> $filters
*/
public function covers(array $filters): bool
{
if (! $this->available()) {
return false;
}
return $this->filtered($filters)->limit(1)->exists();
}
/**
* @param array<string,mixed> $filters
* @return array{rows:list<array<string,mixed>>,has_more:bool}
*/
public function paginate(array $filters, int $page, int $limit): array
{
$page = max($page, 1);
$limit = min(max($limit, 1), 100);
$records = $this->filtered($filters)
->orderByDesc('order_time')
->orderByDesc('id')
->forPage($page, $limit + 1)
->get();
$rows = [];
foreach ($records as $record) {
$payload = json_decode((string) $record->payload, true);
$rows[] = is_array($payload) ? $payload : [];
}
return [
'rows' => array_slice($rows, 0, $limit),
'has_more' => count($rows) > $limit,
];
}
/**
* 导入一行(命令内部使用,批量写入)。
*
* @param array<string,mixed> $payload
*/
public static function toRecord(array $payload, ?string $importedAt = null): array
{
// 以整行内容做键:同一单同一商品可能出现多行,只有完全相同的一行才合并。
ksort($payload);
$rowKey = sha1((string) json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
$now = $importedAt ?? now()->toDateTimeString();
return [
'row_key' => $rowKey,
'shop_name' => mb_substr((string) ($payload['shop_name'] ?? ''), 0, 191),
'shop_code' => mb_substr((string) ($payload['shop_code'] ?? ''), 0, 64),
'online_order_no' => mb_substr((string) ($payload['online_order_no'] ?? ''), 0, 64),
'internal_order_no' => mb_substr((string) ($payload['internal_order_no'] ?? ''), 0, 64),
'product_code' => mb_substr((string) ($payload['product_code'] ?? ''), 0, 191),
'order_time' => self::dateTime($payload['order_time'] ?? null),
'product_quantity' => (float) ($payload['product_quantity'] ?? 0),
'product_amount' => (float) ($payload['product_amount'] ?? 0),
'product_cost' => (float) ($payload['product_cost'] ?? 0),
'cost_source' => mb_substr((string) ($payload['cost_source'] ?? ''), 0, 32),
'combine_type' => mb_substr((string) ($payload['combine_type'] ?? ''), 0, 32),
'payload' => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
'imported_at' => $now,
'created_at' => $now,
'updated_at' => $now,
];
}
/** @param array<string,mixed> $filters */
private function filtered(array $filters): Builder
{
$query = DB::table(self::TABLE);
$from = trim((string) ($filters['from'] ?? ''));
$to = trim((string) ($filters['to'] ?? ''));
$orderNo = trim((string) ($filters['order_no'] ?? ''));
$sku = trim((string) ($filters['sku_id'] ?? ''));
$shops = array_values(array_filter((array) ($filters['shop_names'] ?? []), static fn ($v) => trim((string) $v) !== ''));
if ($from !== '') {
$query->where('order_time', '>=', $from . ' 00:00:00');
}
if ($to !== '') {
$query->where('order_time', '<=', $to . ' 23:59:59');
}
if ($shops !== []) {
$query->whereIn('shop_name', $shops);
}
if ($orderNo !== '') {
$query->where(function (Builder $inner) use ($orderNo): void {
$inner->where('online_order_no', 'like', $orderNo . '%')
->orWhere('internal_order_no', 'like', $orderNo . '%')
->orWhere('internal_order_no', '=', $orderNo);
});
}
if ($sku !== '') {
$query->where('product_code', 'like', '%' . addcslashes($sku, '%_\\') . '%');
}
return $query;
}
private static function dateTime(mixed $value): ?string
{
$value = trim((string) $value);
if ($value === '') {
return null;
}
$timestamp = strtotime($value);
return $timestamp === false ? null : date('Y-m-d H:i:s', $timestamp);
}
}