mapper = new StoreViolationMapper(); } public function test_platform_mapping_accepts_source_values_and_slugs(): void { $this->assertSame('jd', $this->mapper->platformForSourceValue('京东')); $this->assertSame('taobao', $this->mapper->platformForSourceValue('天猫')); $this->assertSame('jingxi', $this->mapper->platformForSourceValue(' JINGXI ')); $this->assertNull($this->mapper->platformForSourceValue('未知平台')); $this->assertNull($this->mapper->platformForSourceValue(null)); } public function test_category_mapping_uses_keyword_rules(): void { $this->assertSame('发货履约', $this->mapper->categoryForContent('延迟发货/缺货无货')); $this->assertSame('交易违规', $this->mapper->categoryForContent('虚假交易(商品降权)')); $this->assertSame('发票与考核', $this->mapper->categoryForContent('您的政企订单发票及时上传率未达到90%的考核标准')); $this->assertSame('其他', $this->mapper->categoryForContent('未收录的违规内容')); $this->assertSame('其他', $this->mapper->categoryForContent(null)); } public function test_map_crawl_row_returns_unified_fields(): void { $mapped = $this->mapper->mapCrawlRow([ 'id' => 1, 'shop_name' => '京东-亿家达全屋旗舰店', 'violation_no' => '86799651', 'violation_date' => '2026-10-04', 'deadline' => '2026-10-11', 'violation_content' => '延迟发货/缺货无货', 'status' => '待处理', 'window_name' => '京东-亿家达全屋旗舰店_商家后台', 'platform' => '京东', 'crawl_date' => '2026-10-04', ]); $this->assertSame('jd', $mapped['platform']); $this->assertSame('发货履约', $mapped['violation_category']); $this->assertSame('86799651', $mapped['violation_no']); $this->assertSame('2026-10-11', $mapped['appeal_deadline']); $this->assertSame('crawl', $mapped['source']); } public function test_date_and_points_normalization(): void { $this->assertSame('2026-10-04', $this->mapper->normalizeDate('2026-10-04')); $this->assertSame('2026-10-04', $this->mapper->normalizeDate('2026/10/4')); $this->assertSame('2026-10-04', $this->mapper->normalizeDate('2026年10月4日')); $this->assertSame('2020-09-15', $this->mapper->normalizeDate(44089)); $this->assertNull($this->mapper->normalizeDate(null)); $this->assertNull($this->mapper->normalizeDate('-')); $this->assertSame(2.0, $this->mapper->normalizePoints('2分')); $this->assertSame(0.5, $this->mapper->normalizePoints('0.5')); $this->assertNull($this->mapper->normalizePoints('不扣分')); } public function test_map_crawl_row_includes_reason_and_disposition_when_upstream_provides_them(): void { $base = [ 'shop_name' => '京喜-久伴吾家具京喜自营工厂店', 'violation_no' => '86522327', 'violation_date' => '2026-09-30', 'deadline' => '2026-10-07', 'violation_content' => '订单存在揽收超时行为', 'status' => '待处理', 'window_name' => '京喜-久伴吾家具京喜自营工厂店_商家后台', 'platform' => '京喜', 'crawl_date' => '2026-10-04', ]; $mapped = $this->mapper->mapCrawlRow($base + [ 'violation_reason' => '订单存在揽收超时行为', 'disposition' => '警告;扣2分', ]); $this->assertSame('订单存在揽收超时行为', $mapped['violation_reason']); $this->assertSame('警告;扣2分', $mapped['disposition']); // 上游尚未提供两列时保持 null,避免同步误覆盖导入补充的数据。 $legacy = $this->mapper->mapCrawlRow($base); $this->assertNull($legacy['violation_reason']); $this->assertNull($legacy['disposition']); } public function test_unrecognized_points_throw(): void { $this->expectException(InvalidArgumentException::class); $this->mapper->normalizePoints('扣分若干'); } }