推送
This commit is contained in:
@@ -147,6 +147,7 @@ class WordpressSiteService
|
||||
"status" => 'publish', //发布状态
|
||||
];
|
||||
//$data['categories'][] = ["id" => $site['categories']];
|
||||
//产品分类
|
||||
$data['product_cat'] = explode(',', $site['categories']);
|
||||
|
||||
//车辆档案相关参数
|
||||
@@ -795,31 +796,18 @@ class WordpressSiteService
|
||||
$tempFilePath = $tempDir . md5($processedUrl) . '.' . $extension;
|
||||
|
||||
try {
|
||||
Log::info('开始上传图片到WordPress', [
|
||||
'original_url' => $imageUrl,
|
||||
'processed_url' => $processedUrl,
|
||||
'filename' => $filename,
|
||||
'content_type' => $contentType,
|
||||
'temp_file' => $tempFilePath,
|
||||
]);
|
||||
|
||||
// 1. 下载图片到本地临时文件
|
||||
$imageContent = file_get_contents($processedUrl);
|
||||
if ($imageContent === false) {
|
||||
throw new \Exception('图片下载失败:' . $processedUrl);
|
||||
}
|
||||
|
||||
$imageSize = strlen($imageContent);
|
||||
Log::info('图片下载成功', ['size' => $imageSize, 'temp_file' => $tempFilePath]);
|
||||
|
||||
// 写入临时文件
|
||||
$bytesWritten = file_put_contents($tempFilePath, $imageContent);
|
||||
if ($bytesWritten === false) {
|
||||
throw new \Exception('写入临时文件失败:' . $tempFilePath);
|
||||
}
|
||||
|
||||
Log::info('临时文件创建成功', ['file' => $tempFilePath, 'size' => $bytesWritten]);
|
||||
|
||||
// 2. 使用 GuzzleHttp 上传本地文件到 WP 媒体库
|
||||
$fileHandle = fopen($tempFilePath, 'r');
|
||||
$response = $client->post($mediaUrl, [
|
||||
@@ -837,6 +825,151 @@ class WordpressSiteService
|
||||
RequestOptions::VERIFY => false,
|
||||
RequestOptions::TIMEOUT => 30,
|
||||
]);
|
||||
// 关闭文件句柄
|
||||
if (is_resource($fileHandle)) {
|
||||
fclose($fileHandle);
|
||||
}
|
||||
|
||||
$result = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('上传图片到WordPress失败', [
|
||||
'image_url' => $imageUrl,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
throw new \Exception('上传图片失败:' . $e->getMessage());
|
||||
} finally {
|
||||
// 3. 删除临时文件
|
||||
if (file_exists($tempFilePath)) {
|
||||
@unlink($tempFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传视频到 WordPress 媒体库
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $api_user
|
||||
* @param string $api_secret
|
||||
* @param string $videoUrl 视频URL
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function uploadVideoToWp(string $domain, string $api_user, string $api_secret, string $videoUrl): array
|
||||
{
|
||||
// 使用 OssService 生成完整的视频URL(不带处理参数)
|
||||
$ossUrl = (new OssService)->generatePrivateUrl($videoUrl, 3600);
|
||||
|
||||
$client = new Client();
|
||||
|
||||
// 从原始 URL 提取文件扩展名
|
||||
$pathInfo = pathinfo(parse_url($videoUrl, PHP_URL_PATH));
|
||||
$extension = strtolower($pathInfo['extension'] ?? 'mp4');
|
||||
$filename = ($pathInfo['filename'] ?? 'video') . '.' . $extension;
|
||||
|
||||
// 确定Content-Type
|
||||
$mimeTypes = [
|
||||
'mp4' => 'video/mp4',
|
||||
'webm' => 'video/webm',
|
||||
'ogg' => 'video/ogg',
|
||||
'mov' => 'video/quicktime',
|
||||
'avi' => 'video/x-msvideo',
|
||||
'wmv' => 'video/x-ms-wmv',
|
||||
'flv' => 'video/x-flv',
|
||||
];
|
||||
$contentType = $mimeTypes[$extension] ?? 'video/mp4';
|
||||
|
||||
$mediaUrl = $domain . '/wp-json/wp/v2/media';
|
||||
|
||||
// 本地临时文件路径
|
||||
$tempDir = runtime_path('temp') . '/wp_videos/';
|
||||
if (!is_dir($tempDir)) {
|
||||
mkdir($tempDir, 0755, true);
|
||||
}
|
||||
$tempFilePath = $tempDir . md5($videoUrl) . '.' . $extension;
|
||||
|
||||
try {
|
||||
// 1. 下载视频到本地临时文件
|
||||
$videoContent = file_get_contents($ossUrl);
|
||||
if ($videoContent === false) {
|
||||
throw new \Exception('视频下载失败:' . $ossUrl);
|
||||
}
|
||||
|
||||
// 写入临时文件
|
||||
$bytesWritten = file_put_contents($tempFilePath, $videoContent);
|
||||
if ($bytesWritten === false) {
|
||||
throw new \Exception('写入临时文件失败:' . $tempFilePath);
|
||||
}
|
||||
|
||||
// 2. 使用 GuzzleHttp 上传本地文件到 WP 媒体库
|
||||
$fileHandle = fopen($tempFilePath, 'r');
|
||||
$safeFilename = 'video_' . time() . '.' . $extension;
|
||||
|
||||
$multipart = [
|
||||
[
|
||||
'name' => 'file',
|
||||
'filename' => $safeFilename,
|
||||
'contents' => $fileHandle,
|
||||
'headers' => [
|
||||
'Content-Type' => $contentType,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
try {
|
||||
$response = $client->post($mediaUrl, [
|
||||
RequestOptions::AUTH => [$api_user, $api_secret],
|
||||
RequestOptions::MULTIPART => $multipart,
|
||||
RequestOptions::VERIFY => false,
|
||||
RequestOptions::TIMEOUT => 300,
|
||||
RequestOptions::HEADERS => [
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
]);
|
||||
} catch (\Exception $uploadException) {
|
||||
// GuzzleHttp 上传失败,尝试使用 curl
|
||||
if (is_resource($fileHandle)) {
|
||||
fclose($fileHandle);
|
||||
}
|
||||
|
||||
// 使用原生 curl 上传
|
||||
$ch = curl_init();
|
||||
$cfile = new \CURLFile($tempFilePath, $contentType, $safeFilename);
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $mediaUrl,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => ['file' => $cfile],
|
||||
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
|
||||
CURLOPT_USERPWD => $api_user . ':' . $api_secret,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_TIMEOUT => 300,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Accept: application/json',
|
||||
'Content-Type: multipart/form-data',
|
||||
],
|
||||
]);
|
||||
|
||||
$responseBody = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlError = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($curlError) {
|
||||
throw new \Exception('Curl上传失败:' . $curlError);
|
||||
}
|
||||
|
||||
if ($httpCode >= 400) {
|
||||
throw new \Exception('Curl上传失败 HTTP ' . $httpCode . ': ' . $responseBody);
|
||||
}
|
||||
|
||||
// 创建一个模拟的 Response 对象
|
||||
$response = new \GuzzleHttp\Psr7\Response($httpCode, [], $responseBody);
|
||||
}
|
||||
|
||||
// 关闭文件句柄
|
||||
if (is_resource($fileHandle)) {
|
||||
@@ -845,32 +978,23 @@ class WordpressSiteService
|
||||
|
||||
$result = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
Log::info('图片上传成功', [
|
||||
Log::info('视频上传成功', [
|
||||
'media_id' => $result['id'] ?? 'unknown',
|
||||
'source_url' => $result['source_url'] ?? 'not set',
|
||||
'url' => $result['url'] ?? 'not set',
|
||||
'link' => $result['link'] ?? 'not set',
|
||||
'guid' => $result['guid']['rendered'] ?? 'not set',
|
||||
'all_keys' => array_keys($result),
|
||||
'source_url' => $result['source_url'] ?? 'unknown',
|
||||
]);
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('上传图片到WordPress失败', [
|
||||
'domain' => $domain,
|
||||
'image_url' => $imageUrl,
|
||||
'processed_url' => $processedUrl ?? 'N/A',
|
||||
'temp_file' => $tempFilePath ?? 'N/A',
|
||||
Log::error('上传视频到WordPress失败', [
|
||||
'video_url' => $videoUrl,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
throw new \Exception('上传图片失败:' . $e->getMessage());
|
||||
throw new \Exception('上传视频失败:' . $e->getMessage());
|
||||
} finally {
|
||||
// 3. 删除临时文件
|
||||
if (file_exists($tempFilePath)) {
|
||||
@unlink($tempFilePath);
|
||||
Log::info('临时文件已删除', ['file' => $tempFilePath]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -907,15 +1031,6 @@ class WordpressSiteService
|
||||
$imagesRaw = $car->getAttribute('images'); // 获取原始值,不经过 casts
|
||||
$imagesCast = $car->images; // 经过 casts 转换后的值
|
||||
|
||||
Log::info('检查 images 字段', [
|
||||
'car_id' => $carId,
|
||||
'images_raw' => $imagesRaw,
|
||||
'images_cast' => $imagesCast,
|
||||
'images_raw_type' => gettype($imagesRaw),
|
||||
'images_cast_type' => gettype($imagesCast),
|
||||
'images_cast_is_array' => is_array($imagesCast),
|
||||
]);
|
||||
|
||||
// 解析图片列表 - 优先使用已转换的数组,否则尝试解析原始字符串
|
||||
$images = [];
|
||||
if (!empty($imagesCast) && is_array($imagesCast)) {
|
||||
@@ -931,12 +1046,6 @@ class WordpressSiteService
|
||||
}
|
||||
}
|
||||
|
||||
Log::info('开始处理车辆图片', [
|
||||
'car_id' => $carId,
|
||||
'images_final' => $images,
|
||||
'images_count' => count($images),
|
||||
]);
|
||||
|
||||
// 上传图片到WordPress媒体库
|
||||
$uploadedImages = []; // 存储完整信息 ['id' => xx, 'url' => xx, ...]
|
||||
$uploadedImageIds = []; // 仅存储ID
|
||||
@@ -944,7 +1053,6 @@ class WordpressSiteService
|
||||
foreach ($images as $index => $imageUrl) {
|
||||
if (!empty($imageUrl)) {
|
||||
try {
|
||||
Log::info('正在上传图片', ['index' => $index, 'url' => $imageUrl]);
|
||||
$uploadedImg = $this->uploadImageToWp($domain, $site->api_user, $site->api_secret, $imageUrl);
|
||||
$uploadedImages[] = [
|
||||
'id' => $uploadedImg['id'],
|
||||
@@ -962,12 +1070,19 @@ class WordpressSiteService
|
||||
}
|
||||
}
|
||||
|
||||
Log::info('图片上传完成', [
|
||||
'total' => count($images),
|
||||
'success' => count($uploadedImages),
|
||||
'uploaded_ids' => $uploadedImageIds,
|
||||
'uploaded_details' => $uploadedImages,
|
||||
]);
|
||||
// 上传视频到WordPress媒体库
|
||||
$videoMediaId = null;
|
||||
$videoSourceUrl = null;
|
||||
if (!empty($car->intro_video)) {
|
||||
try {
|
||||
$uploadedVideo = $this->uploadVideoToWp($domain, $site->api_user, $site->api_secret, $car->intro_video);
|
||||
$videoMediaId = $uploadedVideo['id'];
|
||||
$videoSourceUrl = $uploadedVideo['source_url'] ?? $uploadedVideo['url'] ?? '';
|
||||
} catch (\Exception $e) {
|
||||
Log::error('视频上传失败', ['error' => $e->getMessage()]);
|
||||
// 视频上传失败不影响整体推送,继续执行
|
||||
}
|
||||
}
|
||||
|
||||
// 获取FAQ数据
|
||||
$faqs = \app\common\model\UsedCarFaq::where('car_id', $carId)
|
||||
@@ -979,7 +1094,7 @@ class WordpressSiteService
|
||||
$title = $this->translateIfNeeded($car->product_name, '英文');
|
||||
|
||||
// 构建Gutenberg Blocks格式content
|
||||
$content = $this->buildGutenbergContent($car, $uploadedImages, $faqs);
|
||||
$content = $this->buildGutenbergContent($car, $uploadedImages, $faqs, $videoMediaId, $videoSourceUrl);
|
||||
|
||||
// 构建产品数据
|
||||
$data = [
|
||||
@@ -990,6 +1105,9 @@ class WordpressSiteService
|
||||
'regular_price' => number_format($car->price * $site->exchange_rate, 2),
|
||||
];
|
||||
|
||||
// 设置产品分类使用 product_cat 字段)
|
||||
$data['product_cat'] = [15, 16];
|
||||
|
||||
// 设置主图和车辆相册(ACF字段)
|
||||
if (!empty($uploadedImages)) {
|
||||
$allImageIds = array_map(function($img) {
|
||||
@@ -1004,13 +1122,6 @@ class WordpressSiteService
|
||||
return $img['id'];
|
||||
}, $productGalleryImages);
|
||||
$data['acf']['product_gallery'] = $productGalleryIds;
|
||||
|
||||
Log::info('设置ACF车辆相册', [
|
||||
'featured_media' => $data['featured_media'],
|
||||
'total_images' => count($uploadedImages),
|
||||
'product_gallery_count' => count($data['acf']['product_gallery']),
|
||||
'product_gallery_ids' => $data['acf']['product_gallery'],
|
||||
]);
|
||||
}
|
||||
|
||||
// 设置ACF车辆参数字段(与旧站点保持一致)
|
||||
@@ -1032,37 +1143,28 @@ class WordpressSiteService
|
||||
$value = $this->translateColorToEnglish($value);
|
||||
} elseif ($acfKey === 'source_place' && $this->containsChinese($value)) {
|
||||
$value = $this->translateIfNeeded($value, '英文');
|
||||
} elseif ($acfKey === 'Mileage' && $acfKey) {
|
||||
$value = $this->translateIfNeeded($value, '英文');
|
||||
}
|
||||
$data['acf'][$acfKey] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// 翻译并设置车况介绍和产品简介(ACF字段)
|
||||
$conditionDesc = $car->condition_desc ?? '';
|
||||
if (!empty($conditionDesc)) {
|
||||
// 产品简介(完整翻译)
|
||||
$data['acf']['condition_desc'] = $this->translateIfNeeded($conditionDesc, '英文');
|
||||
// $conditionDesc = $car->condition_desc ?? '';
|
||||
// if (!empty($conditionDesc)) {
|
||||
// // 产品简介(完整翻译)
|
||||
// $data['acf']['condition_desc'] = $this->translateIfNeeded($conditionDesc, '英文');
|
||||
//
|
||||
// // 车况介绍(简短翻译,去掉商家信息)
|
||||
// $cleanDesc = preg_replace('/[^\x{4e00}-\x{9fff}\w\s.,!?()-]/u', ' ', $conditionDesc);
|
||||
// $vehicleDesc = $this->translateIfNeeded($cleanDesc, '英文');
|
||||
// $data['acf']['pro-intro-des'] = $vehicleDesc;
|
||||
// }
|
||||
|
||||
// 车况介绍(简短翻译,去掉商家信息)
|
||||
$cleanDesc = preg_replace('/[^\x{4e00}-\x{9fff}\w\s.,!?()-]/u', ' ', $conditionDesc);
|
||||
$vehicleDesc = $this->translateIfNeeded($cleanDesc, '英文');
|
||||
$data['acf']['pro-intro-des'] = $vehicleDesc;
|
||||
}
|
||||
|
||||
Log::info('设置ACF参数字段', [
|
||||
'acf_fields' => array_keys($data['acf'] ?? []),
|
||||
'acf_values' => $data['acf'] ?? [],
|
||||
]);
|
||||
// 视频通过 content 中的 video block 展示,不需要 ACF 字段
|
||||
|
||||
try {
|
||||
Log::info('准备推送产品到WordPress', [
|
||||
'car_id' => $carId,
|
||||
'featured_media' => $data['featured_media'] ?? 'not set',
|
||||
'acf_product_gallery_count' => count($data['acf']['product_gallery'] ?? []),
|
||||
'acf_fields_count' => count($data['acf'] ?? []),
|
||||
'content_length' => strlen($data['content'] ?? ''),
|
||||
]);
|
||||
|
||||
$response = $client->post($url, [
|
||||
RequestOptions::AUTH => [$site->api_user, $site->api_secret],
|
||||
RequestOptions::JSON => $data,
|
||||
@@ -1127,7 +1229,7 @@ class WordpressSiteService
|
||||
/**
|
||||
* 构建 Gutenberg Blocks 格式 content
|
||||
*/
|
||||
private function buildGutenbergContent($car, array $uploadedImages, $faqs): string
|
||||
private function buildGutenbergContent($car, array $uploadedImages, $faqs, $videoMediaId = null, $videoSourceUrl = null): string
|
||||
{
|
||||
$content = '';
|
||||
|
||||
@@ -1143,14 +1245,6 @@ class WordpressSiteService
|
||||
$galleryImages = array_slice($uploadedImages, 5); // 第6张开始
|
||||
$firstFiveImages = array_slice($uploadedImages, 0, 5); // 前5张
|
||||
|
||||
Log::info('构建图库block', [
|
||||
'total_images' => count($uploadedImages),
|
||||
'gallery_start_from' => 6,
|
||||
'gallery_before_fill' => count($galleryImages),
|
||||
'first_five_count' => count($firstFiveImages),
|
||||
'gallery_ids_before' => array_column($galleryImages, 'id'),
|
||||
]);
|
||||
|
||||
// 不足8张时,重复使用前5张图片
|
||||
if (count($galleryImages) > 0 && count($galleryImages) < 8) {
|
||||
$repeatSource = $firstFiveImages; // 重复使用前5张
|
||||
@@ -1160,11 +1254,6 @@ class WordpressSiteService
|
||||
$galleryImages = array_slice($galleryImages, 0, 8);
|
||||
}
|
||||
|
||||
Log::info('图库图片准备完成', [
|
||||
'final_count' => count($galleryImages),
|
||||
'image_ids' => array_column($galleryImages, 'id'),
|
||||
]);
|
||||
|
||||
foreach ($galleryImages as $img) {
|
||||
$imgId = $img['id'];
|
||||
$imgUrl = $img['url'] ?? '';
|
||||
@@ -1200,11 +1289,31 @@ class WordpressSiteService
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Parameters xz-tab block
|
||||
// 4. Parameters 标题
|
||||
$content .= "<!-- wp:heading -->\n";
|
||||
$content .= "<h2 class=\"wp-block-heading\" id=\"Parameters\">Parameters</h2>\n";
|
||||
$content .= "<!-- /wp:heading -->\n\n";
|
||||
|
||||
// Parameters xz-tab block
|
||||
$tabData = $this->buildXzTabData($car);
|
||||
$content .= "<!-- wp:acf/xz-tab " . json_encode($tabData, JSON_UNESCAPED_UNICODE) . " /-->\n\n";
|
||||
|
||||
// 5. FAQ
|
||||
// 5. Video section (如果有视频)
|
||||
if ($videoMediaId && $videoSourceUrl) {
|
||||
$content .= "<!-- wp:paragraph -->\n";
|
||||
$content .= "<p></p>\n";
|
||||
$content .= "<!-- /wp:paragraph -->\n\n";
|
||||
|
||||
$content .= "<!-- wp:heading -->\n";
|
||||
$content .= "<h2 class=\"wp-block-heading\" id=\"Video\">Video</h2>\n";
|
||||
$content .= "<!-- /wp:heading -->\n\n";
|
||||
|
||||
$content .= "<!-- wp:video {\"id\":" . $videoMediaId . "} -->\n";
|
||||
$content .= "<figure class=\"wp-block-video\"><video controls src=\"" . htmlspecialchars($videoSourceUrl) . "\"></video></figure>\n";
|
||||
$content .= "<!-- /wp:video -->\n\n";
|
||||
}
|
||||
|
||||
// 6. FAQ
|
||||
if ($faqs->count() > 0) {
|
||||
$content .= "<!-- wp:heading -->\n";
|
||||
$content .= "<h2 class=\"wp-block-heading\" id=\"FAQ\">FAQ</h2>\n";
|
||||
@@ -1348,24 +1457,15 @@ class WordpressSiteService
|
||||
$html = '<table class="spec-table">' . "\n";
|
||||
$html .= '<tbody>' . "\n";
|
||||
|
||||
$translatedCount = 0;
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$value = $row['value'] ?? '';
|
||||
if (empty($value) && $value !== '0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$originalValue = $value;
|
||||
// 翻译中文值
|
||||
if (is_string($value) && $this->containsChinese($value)) {
|
||||
$value = $this->translateIfNeeded($value, '英文');
|
||||
$translatedCount++;
|
||||
Log::info('翻译参数表格字段值', [
|
||||
'label' => $row['label'],
|
||||
'original' => $originalValue,
|
||||
'translated' => $value,
|
||||
]);
|
||||
}
|
||||
|
||||
$html .= '<tr>' . "\n";
|
||||
@@ -1377,10 +1477,6 @@ class WordpressSiteService
|
||||
$html .= '</tbody>' . "\n";
|
||||
$html .= '</table>' . "\n";
|
||||
|
||||
if ($translatedCount > 0) {
|
||||
Log::info('参数表格翻译完成', ['translated_count' => $translatedCount]);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user