CaiWuXiTong/app/Business/StoreViolationMapper.php

152 lines
5.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Business;
use DateTimeImmutable;
use DateTimeInterface;
use InvalidArgumentException;
use PhpOffice\PhpSpreadsheet\Shared\Date as ExcelDate;
/**
* 店铺违规信息预警:上游抓取数据与导入文件到统一表头的映射。
*/
class StoreViolationMapper
{
public function platformForSourceValue(mixed $value): ?string
{
$normalized = mb_strtolower(trim((string) $value));
if ($normalized === '') {
return null;
}
foreach ((array) config('store_violations.platforms', []) as $slug => $platform) {
if ($normalized === $slug) {
return $slug;
}
foreach ($platform['source_values'] ?? [] as $sourceValue) {
if ($normalized === mb_strtolower(trim((string) $sourceValue))) {
return $slug;
}
}
}
return null;
}
public function categoryForContent(?string $content): string
{
$content = trim((string) $content);
if ($content === '') {
return (string) config('store_violations.default_category', '其他');
}
foreach ((array) config('store_violations.category_rules', []) as $category => $keywords) {
foreach ((array) $keywords as $keyword) {
if ($keyword !== '' && mb_stripos($content, (string) $keyword) !== false) {
return (string) $category;
}
}
}
return (string) config('store_violations.default_category', '其他');
}
/**
* 上游抓取行(rpt.crawl_shop_violation)→ 统一字段。
*
* @param array<string,mixed> $row
* @return array<string,mixed>|null 平台无法识别时返回 null
*/
public function mapCrawlRow(array $row): ?array
{
$platform = $this->platformForSourceValue($row['platform'] ?? null);
if ($platform === null) {
return null;
}
$content = trim((string) ($row['violation_content'] ?? ''));
return [
'platform' => $platform,
'shop_name' => mb_substr(trim((string) ($row['shop_name'] ?? '')), 0, 191),
'violation_type' => $content === '' ? null : mb_substr($content, 0, 500),
'violation_category' => $this->categoryForContent($content),
// 上游视图补齐这两列后由同步命令自动带出(见 SyncStoreViolations 的列探测)。
'violation_reason' => $this->normalizeLabel($row['violation_reason'] ?? null, 500),
'disposition' => $this->normalizeLabel($row['disposition'] ?? null, 500),
'violation_no' => mb_substr(trim((string) ($row['violation_no'] ?? '')), 0, 100),
'violation_date' => $this->normalizeDate($row['violation_date'] ?? null),
'status' => $this->normalizeLabel($row['status'] ?? null, 64),
'appeal_deadline' => $this->normalizeDate($row['deadline'] ?? null),
'window_name' => $this->normalizeLabel($row['window_name'] ?? null, 191),
'crawl_date' => $this->normalizeDate($row['crawl_date'] ?? null),
'source' => 'crawl',
];
}
public function normalizeDate(mixed $value): ?string
{
if ($value === null || $value === '') {
return null;
}
if ($value instanceof DateTimeInterface) {
return $value->format('Y-m-d');
}
if (is_numeric($value)) {
$number = (float) $value;
if ($number >= 20000 && $number <= 80000) {
return ExcelDate::excelToDateTimeObject($number)->format('Y-m-d');
}
}
$text = trim((string) $value);
if ($text === '' || in_array($text, ['-', '—', '无'], true)) {
return null;
}
$text = str_replace(['年', '月'], '-', str_replace('日', '', $text));
$text = str_replace(['/', '.'], '-', $text);
$text = trim(preg_replace('/\s+/', ' ', $text) ?? $text);
try {
return (new DateTimeImmutable($text))->format('Y-m-d');
} catch (\Throwable) {
throw new InvalidArgumentException('无法识别的日期:' . $value);
}
}
public function normalizePoints(mixed $value): ?float
{
if ($value === null || $value === '') {
return null;
}
if (is_numeric($value)) {
return (float) $value;
}
$text = trim((string) $value);
if ($text === '' || in_array($text, ['-', '—', '无', '不扣分'], true)) {
return null;
}
$text = preg_replace('/(分|点|个?扣|扣除)/u', '', $text) ?? $text;
$text = trim($text);
if ($text === '' || ! is_numeric($text)) {
throw new InvalidArgumentException('无法识别的扣分:' . $value);
}
return (float) $text;
}
private function normalizeLabel(mixed $value, int $limit): ?string
{
$text = trim((string) $value);
return $text === '' ? null : mb_substr($text, 0, $limit);
}
}