$hotelCode, 'name' => getv($h, 'profile.name', ''), 'brand' => getv($h, 'brandInfo.brandName', ''), 'brandCode' => getv($h, 'brandInfo.brandCode', ''), 'phone' => getv($h, 'callCenter.phoneNumber', ''), 'address' => trim(implode(' ', array_filter([ getv($h, 'address.street1', ''), getv($h, 'address.street2', ''), getv($h, 'address.street3', ''), getv($h, 'address.city', ''), getv($h, 'address.state.name', ''), getv($h, 'address.zip', ''), ]))), 'km' => getv($h, 'distanceFrom.kilometers', ''), 'miles' => getv($h, 'distanceFrom.miles', ''), 'welcomeMessage' => trim(strip_tags((string)getv($h, 'marketing.marketingText.welcomeMessage', ''))), 'image' => pickImage($h), 'website' => getv($h, 'profile.independentNonIHGWebsiteURL', ''), 'rating' => getv($h, 'profile.averageReview', null), 'reviews' => getv($h, 'profile.totalReviews', null), 'facilities' => array_values(array_filter(array_map( fn($x) => $x['name'] ?? '', $h['facilities'] ?? [] ))), 'tax' => getv($h, 'tax.taxAndFeeDetail', ''), 'price' => getv($h, 'rate.price', getv($h, 'price.amount', null)), 'currency' => getv($h, 'rate.currency', getv($h, 'price.currency', 'CNY')), ]; } function loadHotelsFromFiles(array $jsonFiles): array { $merged = []; foreach ($jsonFiles as $file) { if (!is_file($file)) { echo "跳过不存在文件:{$file}\n"; continue; } $data = json_decode(file_get_contents($file), true); if (!is_array($data)) { echo "跳过无效 JSON:{$file}\n"; continue; } $hotels = getv($data, 'data.getHotels.hotelInfo', []); if (!is_array($hotels)) { echo "跳过无 hotelInfo 文件:{$file}\n"; continue; } foreach ($hotels as $hotel) { if (!is_array($hotel)) { continue; } $hotelCode = trim((string)($hotel['hotelCode'] ?? '')); if ($hotelCode === '') { continue; } // 后读入的同 hotelCode 酒店覆盖前面的 $merged[$hotelCode] = normalizeHotel($hotel); } } return array_values($merged); } function pageName(string $type, int $page): string { if ($type === 'cards') { return $page === 1 ? 'index.html' : "page-{$page}.html"; } return $page === 1 ? 'table.html' : "table-{$page}.html"; } function renderLayout(string $body, string $title): string { return ' ' . e($title) . '
' . $body . '
'; } function renderPager(string $type, int $page, int $totalPages): string { $html = '
'; if ($page > 1) { $html .= '上一页'; } $html .= ' 第 ' . $page . ' / ' . $totalPages . ' 页 '; if ($page < $totalPages) { $html .= '下一页'; } $html .= '
'; return $html; } function renderCardsPage(array $hotels, int $page, int $totalPages, int $total): string { $body = '
找到 ' . $total . ' 家酒店
表格模式
'; foreach ($hotels as $h) { $features = ''; foreach (array_slice($h['facilities'], 0, 8) as $f) { $features .= '' . e($f) . ''; } $price = $h['price'] !== null ? '
' . e(number_format((float)$h['price'])) . ' ' . e($h['currency']) . '
每晚
' : '
暂无价格
'; $body .= '
' . e($h['brandCode']) . '

' . e($h['brand'] . ' ' . $h['name']) . '

' . e($h['hotelCode']) . '
' . e($h['address']) . ' | ' . e($h['phone']) . '
评分:' . e($h['rating'] === null ? 'null' : number_format((float)$h['rating'], 2, '.', '')) . '
' . e($h['welcomeMessage']) . '
' . $features . '
酒店 ID
' . e($h['hotelCode']) . '
⚡ Only a few left from
' . $price . '
' . e($h['tax']) . '
选择酒店
'; } $body .= renderPager('cards', $page, $totalPages); return renderLayout($body, '酒店列表'); } function renderTablePage(array $hotels, int $page, int $totalPages, int $total): string { $body = '
找到 ' . $total . ' 家酒店
卡片模式
'; $body .= ''; foreach ($hotels as $h) { $price = $h['price'] !== null ? number_format((float)$h['price']) . ' ' . $h['currency'] : '暂无'; $body .= ''; } $body .= '
hotelCode 酒店 品牌 地址 评分 电话 设施 价格 税费
' . e($h['hotelCode']) . ' ' . e($h['name']) . ' ' . e($h['brand']) . ' ' . e($h['address']) . ' ' . e($h['rating'] === null ? 'null' : $h['rating']) . ' ' . e($h['phone']) . ' ' . e(implode('、', array_slice($h['facilities'], 0, 8))) . ' ' . e($price) . ' ' . e($h['tax']) . '
'; $body .= renderPager('table', $page, $totalPages); return renderLayout($body, '酒店表格'); } $hotels = loadHotelsFromFiles($jsonFiles); usort($hotels, function (array $a, array $b): int { return strcmp($a['hotelCode'], $b['hotelCode']); }); $total = count($hotels); $totalCardPages = max(1, (int)ceil($total / $pageSizeCards)); $totalTablePages = max(1, (int)ceil($total / $pageSizeTable)); for ($page = 1; $page <= $totalCardPages; $page++) { $chunk = array_slice($hotels, ($page - 1) * $pageSizeCards, $pageSizeCards); file_put_contents( $outDir . '/' . pageName('cards', $page), renderCardsPage($chunk, $page, $totalCardPages, $total) ); } for ($page = 1; $page <= $totalTablePages; $page++) { $chunk = array_slice($hotels, ($page - 1) * $pageSizeTable, $pageSizeTable); file_put_contents( $outDir . '/' . pageName('table', $page), renderTablePage($chunk, $page, $totalTablePages, $total) ); } echo "输入 JSON 文件数:" . count($jsonFiles) . PHP_EOL; echo "去重后酒店数:{$total}" . PHP_EOL; echo "卡片页数:{$totalCardPages}" . PHP_EOL; echo "表格页数:{$totalTablePages}" . PHP_EOL; echo "输出目录:{$outDir}" . PHP_EOL;