210 lines
6.9 KiB
PHP
210 lines
6.9 KiB
PHP
<?php
|
||
|
||
namespace Modules\EcommerceFinance\Repositories;
|
||
|
||
use Illuminate\Database\Query\Builder;
|
||
use Illuminate\Support\Facades\Cache;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Illuminate\Support\Facades\Schema;
|
||
|
||
/**
|
||
* 聚水潭商品推广费用月度导入数据。
|
||
*/
|
||
class PromotionCostRowRepository
|
||
{
|
||
private const TABLE = 'ops_promotion_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;
|
||
}
|
||
|
||
public function covers(string $date): bool
|
||
{
|
||
$period = self::period($date);
|
||
|
||
return $period !== null
|
||
&& $this->available()
|
||
&& DB::table(self::TABLE)->where('source_period', $period)->exists();
|
||
}
|
||
|
||
/**
|
||
* @param array<string,mixed> $filters
|
||
* @return array{rows:list<array<string,mixed>>,has_more:bool,date:string}
|
||
*/
|
||
public function paginate(array $filters, int $page, int $limit): array
|
||
{
|
||
$date = trim((string) ($filters['date'] ?? ''));
|
||
$period = self::period($date);
|
||
$limit = min(max($limit, 1), 100);
|
||
|
||
if ($period === null) {
|
||
return ['rows' => [], 'has_more' => false, 'date' => $date];
|
||
}
|
||
|
||
$records = $this->filtered($filters, $period)
|
||
->orderByDesc('promotion_fee')
|
||
->orderBy('shop_name')
|
||
->orderBy('product_code')
|
||
->orderBy('id')
|
||
->forPage(max($page, 1), $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,
|
||
'date' => $date,
|
||
];
|
||
}
|
||
|
||
/** @return list<string> */
|
||
public function shopNames(string $date): array
|
||
{
|
||
$period = self::period($date);
|
||
|
||
if ($period === null || ! $this->available()) {
|
||
return [];
|
||
}
|
||
|
||
return Cache::remember(
|
||
'order-data.promotion.imported-shop-options:' . $period,
|
||
now()->addHours(24),
|
||
static fn (): array => DB::table(self::TABLE)
|
||
->where('source_period', $period)
|
||
->whereNotNull('shop_name')
|
||
->where('shop_name', '<>', '')
|
||
->distinct()
|
||
->orderBy('shop_name')
|
||
->pluck('shop_name')
|
||
->map(static fn (mixed $name): string => trim((string) $name))
|
||
->filter()
|
||
->values()
|
||
->all(),
|
||
);
|
||
}
|
||
|
||
/**
|
||
* @param array<string,mixed> $payload
|
||
*/
|
||
public static function toRecord(
|
||
array $payload,
|
||
string $period,
|
||
?string $importedAt = null,
|
||
): array {
|
||
ksort($payload);
|
||
$rowKey = sha1((string) json_encode(
|
||
['source_period' => $period, 'payload' => $payload],
|
||
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
|
||
));
|
||
$now = $importedAt ?? now()->toDateTimeString();
|
||
|
||
return [
|
||
'row_key' => $rowKey,
|
||
'source_period' => $period,
|
||
'shop_name' => mb_substr((string) ($payload['shop_name'] ?? ''), 0, 191),
|
||
'product_code' => mb_substr((string) ($payload['product_code'] ?? ''), 0, 191),
|
||
'style_code' => mb_substr((string) ($payload['style_code'] ?? ''), 0, 191),
|
||
'shop_product_code' => mb_substr((string) ($payload['shop_product_code'] ?? ''), 0, 191),
|
||
'shop_style_code' => mb_substr((string) ($payload['shop_style_code'] ?? ''), 0, 191),
|
||
'online_color_spec' => mb_substr((string) ($payload['online_color_spec'] ?? ''), 0, 500),
|
||
'promotion_fee' => (float) ($payload['promotion_fee'] ?? 0),
|
||
'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, string $period): Builder
|
||
{
|
||
$query = DB::table(self::TABLE)->where('source_period', $period);
|
||
$shops = array_values(array_filter(array_map(
|
||
static fn (mixed $value): string => trim((string) $value),
|
||
(array) ($filters['shop_names'] ?? []),
|
||
), static fn (string $value): bool => $value !== ''));
|
||
|
||
if ($shops === [] && trim((string) ($filters['shop_name'] ?? '')) !== '') {
|
||
$shops = [trim((string) $filters['shop_name'])];
|
||
}
|
||
|
||
if ($shops !== []) {
|
||
if (! empty($filters['shop_names_exact'])) {
|
||
$query->whereIn('shop_name', $shops);
|
||
} else {
|
||
$query->where(function (Builder $shop) use ($shops): void {
|
||
foreach ($shops as $name) {
|
||
$shop->orWhereRaw("shop_name LIKE ? ESCAPE '!'", [self::contains($name)]);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
$needles = self::batchValues((string) ($filters['sku_id'] ?? ''));
|
||
if ($needles !== []) {
|
||
$query->where(function (Builder $sku) use ($needles): void {
|
||
foreach ($needles as $needle) {
|
||
$contains = self::contains($needle);
|
||
$sku->orWhere(function (Builder $group) use ($contains): void {
|
||
$group->whereRaw("product_code LIKE ? ESCAPE '!'", [$contains])
|
||
->orWhereRaw("style_code LIKE ? ESCAPE '!'", [$contains])
|
||
->orWhereRaw("shop_product_code LIKE ? ESCAPE '!'", [$contains])
|
||
->orWhereRaw("shop_style_code LIKE ? ESCAPE '!'", [$contains]);
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
return $query;
|
||
}
|
||
|
||
private static function period(string $date): ?string
|
||
{
|
||
return preg_match('/^\d{4}-\d{2}/', trim($date), $matches) === 1
|
||
? $matches[0]
|
||
: null;
|
||
}
|
||
|
||
/** @return list<string> */
|
||
private static function batchValues(string $value): array
|
||
{
|
||
$parts = preg_split('/[\s,,;;]+/u', trim($value)) ?: [];
|
||
$values = [];
|
||
|
||
foreach ($parts as $part) {
|
||
$part = trim($part);
|
||
if ($part !== '') {
|
||
$values[$part] = true;
|
||
}
|
||
if (count($values) >= 100) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
return array_keys($values);
|
||
}
|
||
|
||
private static function contains(string $value): string
|
||
{
|
||
return '%' . str_replace(['!', '%', '_'], ['!!', '!%', '!_'], trim($value)) . '%';
|
||
}
|
||
}
|