diff --git a/app/common/controller/UsedCarDetailController.php b/app/common/controller/UsedCarDetailController.php index 0c80b01..64a33c5 100644 --- a/app/common/controller/UsedCarDetailController.php +++ b/app/common/controller/UsedCarDetailController.php @@ -7,6 +7,7 @@ use app\common\service\UsedCarDetailService; use app\common\service\OssService; use support\Request; use support\exception\BusinessException; +use support\Log; class UsedCarDetailController extends BaseController { @@ -315,4 +316,41 @@ class UsedCarDetailController extends BaseController return $this->error($e->getMessage()); } } + + /** + * 抓取懂车帝二手车数据并保存 + * + * @param Request $request + * @return \support\Response + */ + public function crawlAndStore(Request $request) + { + try { + $url = $request->post('url'); + + if (empty($url)) { + throw new BusinessException('链接不能为空'); + } + + // 验证是否是懂车帝链接 + if (!str_contains($url, 'dongchedi.com')) { + throw new BusinessException('只支持懂车帝二手车链接'); + } + + $userId = $request->user['id'] ?? 0; + if (empty($userId)) { + throw new BusinessException('用户未登录'); + } + + $result = $this->service->crawlAndStore($url, $userId); + + return $this->success($result, '抓取成功,已保存为待审核状态'); + } catch (\Throwable $e) { + Log::error('抓取懂车帝二手车失败', [ + 'url' => $request->post('url'), + 'error' => $e->getMessage(), + ]); + return $this->error($e->getMessage()); + } + } } \ No newline at end of file diff --git a/app/common/service/UploadService.php b/app/common/service/UploadService.php index 62b838c..6a17c05 100644 --- a/app/common/service/UploadService.php +++ b/app/common/service/UploadService.php @@ -46,7 +46,8 @@ class UploadService $extensionFromPath = pathinfo($basename, PATHINFO_EXTENSION); $extension = $extensionFromName ?: $extensionFromPath; - $mime = mime_content_type($filePath) ?: 'application/octet-stream'; + // 获取 MIME 类型,如果函数不可用则使用扩展名判断 + $mime = $this->getMimeType($filePath, $extension); $size = filesize($filePath) ?: 0; $sceneUrl = $scene ? ($scene . '/') : ''; @@ -116,6 +117,49 @@ class UploadService } } + /** + * 获取文件的 MIME 类型 + * 优先使用 mime_content_type(),不可用时根据扩展名判断 + * + * @param string $filePath 文件路径 + * @param string $extension 文件扩展名 + * @return string + */ + private function getMimeType(string $filePath, string $extension = ''): string + { + // 优先使用系统函数 + if (function_exists('mime_content_type')) { + $mime = mime_content_type($filePath); + if ($mime !== false && $mime !== '') { + return $mime; + } + } + + // 降级:根据扩展名判断 + $extension = strtolower(ltrim($extension, '.')); + $mimeTypes = [ + 'jpg' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'png' => 'image/png', + 'gif' => 'image/gif', + 'webp' => 'image/webp', + 'bmp' => 'image/bmp', + 'svg' => 'image/svg+xml', + 'pdf' => 'application/pdf', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'xls' => 'application/vnd.ms-excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'zip' => 'application/zip', + 'rar' => 'application/x-rar-compressed', + 'mp4' => 'video/mp4', + 'mp3' => 'audio/mpeg', + 'txt' => 'text/plain', + ]; + + return $mimeTypes[$extension] ?? 'application/octet-stream'; + } + /** * 删除 OSS 上的文件 * diff --git a/app/common/service/UsedCarDetailService.php b/app/common/service/UsedCarDetailService.php index e214fc3..f0e98da 100644 --- a/app/common/service/UsedCarDetailService.php +++ b/app/common/service/UsedCarDetailService.php @@ -464,11 +464,11 @@ class UsedCarDetailService ->where('used_car_id', $carId) ->where('status', 1) ->first(); - if ($existing) { - $msg = '车辆已推送,跳过'; - Log::info('WordPress推送:' . $msg, ['car_id' => $carId]); - return ['success' => true, 'message' => $msg]; - } +// if ($existing) { +// $msg = '车辆已推送,跳过'; +// Log::info('WordPress推送:' . $msg, ['car_id' => $carId]); +// return ['success' => true, 'message' => $msg]; +// } // 调用推送服务 $wpService = new \app\common\service\WordpressSiteService(); @@ -793,8 +793,9 @@ class UsedCarDetailService { $result = []; - // 从 car_info 直接获取的字段 - $result['official_guide_price'] = $carInfo['official_price'] ?? ''; + // 从 car_info 直接获取的字段,清理掉"万"等字符 + $officialPrice = $carInfo['official_price'] ?? ''; + $result['official_guide_price'] = $this->convertPriceToYuan($officialPrice); // 1. 基本信息 $result['manufacturer'] = $this->getFieldValue($groups, 'sub_brand_name') ?? ''; @@ -900,4 +901,118 @@ class UsedCarDetailService } return ''; } + + /** + * 抓取懂车帝二手车数据并保存 + * + * @param string $url 懂车帝二手车链接 + * @param int $userId 用户ID + * @return array 返回创建的车辆数据 + * @throws \Exception + */ + public function crawlAndStore(string $url, int $userId): array + { + // 1. 抓取二手车基本信息 + $usedCarCrawl = new \app\common\utils\crawl\service\UsedCarCrawlSingle(); + $info = $usedCarCrawl->crawlCar($url); + + // 2. 如果有 car_id,抓取详细参数 + $params = []; + if (!empty($info['car_id'])) { + try { + $params = $this->crawlParams($info['car_id']); + } catch (\Throwable $e) { + Log::error('抓取车辆参数失败', [ + 'car_id' => $info['car_id'], + 'error' => $e->getMessage(), + ]); + // 参数抓取失败不影响主流程 + } + } + + // 3. 构建数据库记录数据 + // 处理图片:如果是数组,转为逗号分隔字符串 + $imagesValue = $info['image'] ?? ''; + if (is_array($imagesValue)) { + $imagesValue = implode(',', array_filter($imagesValue)); + } + + $data = [ + 'code' => $this->generateCode(), + 'user_id' => $userId, + 'product_name' => $info['title'] ?? $info['car_name'] ?? '', + 'price' => $this->convertPriceToYuan($info['price'] ?? 0), + 'source_place' => $info['source_place'] ?? '', + 'plate_time' => $info['plate_time'] ?? '', + 'displacement' => $info['capacity'] ?? '', + 'mileage' => $this->convertMileageToKm($info['usage_mileage'] ?? 0), + 'body_color' => $info['body_color'] ?? '', + 'condition_desc' => $info['condition_desc'] ?? '', + 'source_url' => $url, + 'crawl_title' => $info['title'] ?? '', + 'crawl_brand' => $info['brand_name'] ?? '', + 'crawl_brand_id' => $info['brand_id'] ?? 0, + 'crawl_series' => $info['series_name'] ?? '', + 'crawl_series_id' => $info['series_id'] ?? 0, + 'crawl_year' => $info['year'] ?? '', + 'crawl_msrp' => $info['new_price'] ?? '', + 'crawl_car_id' => $info['car_id'] ?? 0, + 'status' => 0, // 待审核 + 'images' => $imagesValue, + 'power' => $info['horsepower'] ?? '', + ]; + + // 4. 合并详细参数,过滤掉数组类型的值 + $params = array_filter($params, function($value) { + return !is_array($value); + }); + $data = array_merge($data, $params); + + // 5. 创建记录 + $car = UsedCarDetail::create($data); + + return $car->toArray(); + } + + /** + * 生成车辆编码 + */ + private function generateCode(): string + { + return 'UC' . date('YmdHis') . rand(1000, 9999); + } + + /** + * 将价格(万)转换为元 + */ + private function convertPriceToYuan($price): float + { + if (empty($price)) { + return 0; + } + // 如果是数字,直接乘以10000 + if (is_numeric($price)) { + return floatval($price) * 10000; + } + // 如果是字符串,移除"万"字后乘以10000 + $price = str_replace('万', '', $price); + return floatval($price) * 10000; + } + + /** + * 将里程(万公里)转换为公里 + */ + private function convertMileageToKm($mileage): float + { + if (empty($mileage)) { + return 0; + } + // 如果是数字,直接乘以10000 + if (is_numeric($mileage)) { + return floatval($mileage) * 10000; + } + // 如果是字符串,移除"万公里"后乘以10000 + $mileage = str_replace(['万公里', '万'], '', $mileage); + return floatval($mileage) * 10000; + } } \ No newline at end of file diff --git a/app/common/service/WordpressSiteService.php b/app/common/service/WordpressSiteService.php index dee006f..6020f86 100644 --- a/app/common/service/WordpressSiteService.php +++ b/app/common/service/WordpressSiteService.php @@ -1265,6 +1265,7 @@ class WordpressSiteService if (str_contains($car->mileage, '万') || str_contains($car->mileage, '万公里')) { $car->mileage = str_replace(['万', '公里', '万公里'],'',$car->mileage)*10000; } + $car->power = str_replace('马力','',$car->power); foreach ($fieldMap as $acfKey => $label) { if (!empty($car->$acfKey)) { $value = $car->$acfKey; diff --git a/app/common/utils/crawl/service/UsedCarCrawlSingle.php b/app/common/utils/crawl/service/UsedCarCrawlSingle.php index d6d7728..eb27f60 100644 --- a/app/common/utils/crawl/service/UsedCarCrawlSingle.php +++ b/app/common/utils/crawl/service/UsedCarCrawlSingle.php @@ -105,10 +105,31 @@ class UsedCarCrawlSingle if (json_last_error() !== JSON_ERROR_NONE) { $imagesArray = []; } - $saveDir = public_path('uploads/usedcar/' . $info['used_car_id']); - $image = $this->uploadAndRemove($imagesArray, $saveDir); - $info['image'] = $image['image']; - $info['image_pull'] = $image['image_pull']; + + Log::info('抓取到的图片数量', [ + 'url' => $url, + 'images_count' => count($imagesArray), + 'images' => $imagesArray, + ]); + + if (!empty($imagesArray)) { + $saveDir = public_path('uploads/usedcar/' . $info['used_car_id']); + $image = $this->uploadAndRemove($imagesArray, $saveDir); + if ($image !== false) { + $info['image'] = $image['image']; + $info['image_pull'] = $image['image_pull']; + Log::info('图片上传成功', [ + 'uploaded_count' => count($image['image']), + ]); + } else { + $info['image'] = []; + $info['image_pull'] = []; + Log::warning('图片上传失败', ['url' => $url]); + } + } else { + $info['image'] = []; + $info['image_pull'] = []; + } // 继续派发配置页任务 // if (!empty($info['car_id_url'])) { @@ -382,6 +403,8 @@ class UsedCarCrawlSingle public function downloadImageAlbum(array $urls, string $saveDir, string $referer): array { $result = []; + $successCount = 0; + $failCount = 0; foreach ($urls as $url) { $url = trim((string)$url); @@ -395,13 +418,29 @@ class UsedCarCrawlSingle try { $path = $this->downloadImage($url, $saveDir, $referer); + if ($path !== null) { + $successCount++; + } else { + $failCount++; + } } catch (Throwable $e) { + $failCount++; + Log::error('下载图片失败', [ + 'url' => $url, + 'error' => $e->getMessage(), + ]); $path = null; } $result[$url] = $path; } + Log::info('图片下载完成', [ + 'total' => count($urls), + 'success' => $successCount, + 'fail' => $failCount, + ]); + return $result; }