feat: update appraisal return address and test packaging assets

This commit is contained in:
wushumin
2026-06-15 20:08:36 +08:00
parent fa267c4413
commit 9be60fbe17
23 changed files with 1806 additions and 393 deletions

View File

@@ -0,0 +1,296 @@
<?php
declare(strict_types=1);
require dirname(__DIR__) . '/vendor/autoload.php';
const LEGACY_CODE_ALIASES = [
'tiffany_co' => ['1'],
];
$dryRun = in_array('--dry-run', $argv, true);
$brandFile = dirname(__DIR__) . '/resources/catalog/known_brands.php';
if (!is_file($brandFile)) {
fwrite(STDERR, "Known brand file not found: {$brandFile}\n");
exit(1);
}
$brands = require $brandFile;
if (!is_array($brands)) {
fwrite(STDERR, "Known brand file must return an array.\n");
exit(1);
}
$dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__));
$dotenv->safeLoad();
$dsn = sprintf(
'mysql:host=%s;port=%s;dbname=%s;charset=%s',
$_ENV['DB_HOST'] ?? '127.0.0.1',
$_ENV['DB_PORT'] ?? '3306',
$_ENV['DB_DATABASE'] ?? '',
$_ENV['DB_CHARSET'] ?? 'utf8mb4'
);
$pdo = new PDO(
$dsn,
$_ENV['DB_USERNAME'] ?? '',
$_ENV['DB_PASSWORD'] ?? '',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]
);
function normalize_list(mixed $value): array
{
if (!is_array($value)) {
return [];
}
$items = [];
foreach ($value as $item) {
$text = trim((string)$item);
if ($text !== '') {
$items[] = $text;
}
}
return array_values(array_unique($items));
}
function decode_json_list(mixed $value): array
{
if (is_array($value)) {
return normalize_list($value);
}
if (!is_string($value) || trim($value) === '') {
return [];
}
$decoded = json_decode($value, true);
return normalize_list(is_array($decoded) ? $decoded : []);
}
function stable_json_list(array $value): string
{
$items = normalize_list($value);
sort($items, SORT_STRING);
return json_encode($items, JSON_UNESCAPED_UNICODE);
}
function load_enabled_categories(PDO $pdo): array
{
$rows = $pdo->query(
"SELECT id, name, code, supported_service_types
FROM catalog_categories
WHERE is_enabled = 1
ORDER BY sort_order ASC, id ASC"
)->fetchAll();
$byCode = [];
$byName = [];
$byId = [];
foreach ($rows as $row) {
$category = [
'id' => (int)$row['id'],
'name' => (string)$row['name'],
'code' => (string)$row['code'],
'supported_service_types' => decode_json_list($row['supported_service_types'] ?? null),
];
$byId[$category['id']] = $category;
$byCode[strtolower($category['code'])] = $category;
$byName[$category['name']] = $category;
}
return [$byCode, $byName, $byId];
}
function match_categories(array $brand, array $categoriesByCode, array $categoriesByName): array
{
$matched = [];
foreach (normalize_list($brand['category_codes'] ?? []) as $code) {
$key = strtolower($code);
if (isset($categoriesByCode[$key])) {
$matched[$categoriesByCode[$key]['id']] = $categoriesByCode[$key];
}
}
foreach (normalize_list($brand['category_names'] ?? []) as $name) {
if (isset($categoriesByName[$name])) {
$matched[$categoriesByName[$name]['id']] = $categoriesByName[$name];
}
}
return $matched;
}
function infer_supported_service_types(array $categories): array
{
$serviceTypes = [];
foreach ($categories as $category) {
foreach ($category['supported_service_types'] as $serviceType) {
$serviceTypes[$serviceType] = $serviceType;
}
}
if (!$serviceTypes) {
$serviceTypes['anxinyan'] = 'anxinyan';
}
return array_values($serviceTypes);
}
function find_existing_brand(PDO $pdo, string $code): ?array
{
$stmt = $pdo->prepare('SELECT * FROM catalog_brands WHERE code = ? LIMIT 1');
$stmt->execute([$code]);
$row = $stmt->fetch();
if ($row) {
return $row;
}
foreach (LEGACY_CODE_ALIASES[$code] ?? [] as $legacyCode) {
$stmt->execute([$legacyCode]);
$legacy = $stmt->fetch();
if ($legacy) {
return $legacy;
}
}
return null;
}
function existing_relation_ids(PDO $pdo, int $brandId): array
{
$stmt = $pdo->prepare('SELECT category_id FROM catalog_brand_categories WHERE brand_id = ?');
$stmt->execute([$brandId]);
$ids = [];
foreach ($stmt->fetchAll() as $row) {
$ids[(int)$row['category_id']] = true;
}
return $ids;
}
[$categoriesByCode, $categoriesByName, $categoriesById] = load_enabled_categories($pdo);
$stats = [
'brands_total' => count($brands),
'inserted' => 0,
'updated' => 0,
'enabled_existing' => 0,
'legacy_code_repaired' => 0,
'relations_inserted' => 0,
'skipped_no_enabled_category' => 0,
'skipped_invalid' => 0,
];
$now = date('Y-m-d H:i:s');
$insertBrand = $pdo->prepare(
'INSERT INTO catalog_brands (name, en_name, code, logo, sort_order, is_enabled, supported_service_types, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, 1, ?, ?, ?)'
);
$updateBrand = $pdo->prepare(
'UPDATE catalog_brands
SET name = ?, en_name = ?, code = ?, sort_order = ?, is_enabled = 1, supported_service_types = ?, updated_at = ?
WHERE id = ?'
);
$insertRelation = $pdo->prepare(
'INSERT INTO catalog_brand_categories (brand_id, category_id, created_at)
VALUES (?, ?, ?)'
);
if (!$dryRun) {
$pdo->beginTransaction();
}
try {
foreach ($brands as $brand) {
$code = trim((string)($brand['code'] ?? ''));
$name = trim((string)($brand['name'] ?? ''));
$enName = trim((string)($brand['en_name'] ?? ''));
if ($code === '' || $name === '' || $enName === '') {
$stats['skipped_invalid']++;
continue;
}
$matchedCategories = match_categories($brand, $categoriesByCode, $categoriesByName);
if (!$matchedCategories) {
$stats['skipped_no_enabled_category']++;
continue;
}
$serviceTypes = infer_supported_service_types($matchedCategories);
$serviceTypesJson = stable_json_list($serviceTypes);
$sortOrder = (int)($brand['sort_order'] ?? 0);
$existing = find_existing_brand($pdo, $code);
if ($existing) {
$brandId = (int)$existing['id'];
$wasDisabled = (int)$existing['is_enabled'] !== 1;
$wasLegacyCode = (string)$existing['code'] !== $code;
$existingJson = stable_json_list(decode_json_list($existing['supported_service_types'] ?? null));
$needsUpdate = $wasDisabled
|| $wasLegacyCode
|| (string)$existing['name'] !== $name
|| (string)$existing['en_name'] !== $enName
|| (int)$existing['sort_order'] !== $sortOrder
|| $existingJson !== $serviceTypesJson;
if ($needsUpdate) {
$stats['updated']++;
if ($wasDisabled) {
$stats['enabled_existing']++;
}
if ($wasLegacyCode) {
$stats['legacy_code_repaired']++;
}
if (!$dryRun) {
$updateBrand->execute([$name, $enName, $code, $sortOrder, $serviceTypesJson, $now, $brandId]);
}
}
} else {
$stats['inserted']++;
if ($dryRun) {
$brandId = 0;
} else {
$insertBrand->execute([$name, $enName, $code, '', $sortOrder, $serviceTypesJson, $now, $now]);
$brandId = (int)$pdo->lastInsertId();
}
}
$existingCategoryIds = $brandId > 0 ? existing_relation_ids($pdo, $brandId) : [];
foreach (array_keys($matchedCategories) as $categoryId) {
if (isset($existingCategoryIds[(int)$categoryId])) {
continue;
}
$stats['relations_inserted']++;
if (!$dryRun && $brandId > 0) {
$insertRelation->execute([$brandId, (int)$categoryId, $now]);
}
}
}
if (!$dryRun) {
$pdo->commit();
}
} catch (Throwable $e) {
if (!$dryRun && $pdo->inTransaction()) {
$pdo->rollBack();
}
fwrite(STDERR, "IMPORT_KNOWN_BRANDS_FAILED\n");
fwrite(STDERR, $e->getMessage() . PHP_EOL);
exit(1);
}
echo $dryRun ? "DRY_RUN\n" : "IMPORT_KNOWN_BRANDS_OK\n";
echo "ENABLED_CATEGORIES=" . count($categoriesById) . PHP_EOL;
foreach ($stats as $key => $value) {
echo strtoupper($key) . '=' . $value . PHP_EOL;
}