161 lines
5.3 KiB
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);
|
|
}
|
|
}
|