validator; } /** * 构建查询配置 */ protected function buildQueryConfig(Request $request, string $scene = 'default'): array { $p = $request->all(); $config = []; $config['model'] = $this->modelClass; switch ($scene) { case 'default': $config['where']['status'] = 1; $config['order']['sort'] = 'desc'; $config['order']['id'] = 'desc'; break; case 'index': // 默认只显示审核通过的数据 $config['where']['status'] = 1; // 审核状态筛选 if (isset($p['status'])) { $config['where']['status'] = $p['status']; } // 我的车辆 if (isset($p['scene']) && $p['scene'] == 'self') { $config['where']['user_id'] = $request->user['id']; unset($config['where']['status']); } // 关键字搜索 if (isset($p['keyword']) && $p['keyword']) { $k = '%' . $p['keyword'] . '%'; foreach (['product_name', 'code', 'vin', 'manufacturer'] as $col) { $config['orWhere'][] = [$col, 'like', $k]; } } // 品牌筛选 if (isset($p['crawl_brand_id']) && $p['crawl_brand_id']) { $config['where']['crawl_brand_id'] = $p['crawl_brand_id']; } // 车系筛选 if (isset($p['crawl_series_id']) && $p['crawl_series_id']) { $config['where']['crawl_series_id'] = $p['crawl_series_id']; } // 价格区间 if (isset($p['min_price']) && $p['min_price']) { $config['where'][] = ['price', '>=', $p['min_price']]; } if (isset($p['max_price']) && $p['max_price']) { $config['where'][] = ['price', '<=', $p['max_price']]; } $config['order']['sort'] = 'desc'; $config['order']['id'] = 'desc'; break; case 'show': $config['where']['id'] = $p['id']; // 关联FAQ $config['with'] = ['faqs']; break; case 'store': break; case 'update': $config['where']['id'] = $p['id']; break; case 'destroy': $config['where']['id'] = $p['id']; break; } return $config; } /** * 创建前数据处理 */ public function beforeServiceStore($data, Request $request): array { if (empty($data['product_name'])) { throw new BusinessException('产品名称不能为空'); } // 设置用户信息 $data['user_id'] = $request->user['id']; $user = User::where('id', $request->user['id'])->first(); $data['role_wp'] = $user['role_wp'] ?? 1; // 默认待审核状态 if (!isset($data['status'])) { $data['status'] = 0; } // 生成车辆编号 $data['code'] = $this->getCode($request->user['id']); // 处理图片数据 if (!empty($data['images'])) { if (is_array($data['images'])) { $data['images'] = implode(',', $data['images']); } } else { $data['images'] = ''; } // 处理附件数据 if (!empty($data['attachment'])) { if (is_array($data['attachment'])) { $data['attachment'] = json_encode($data['attachment']); } } else { $data['attachment'] = ''; } // 处理介绍图片 if (!empty($data['intro_images'])) { if (is_array($data['intro_images'])) { $data['intro_images'] = implode(',', $data['intro_images']); } } else { $data['intro_images'] = ''; } // 检查来源URL是否重复 if (!empty($data['source_url'])) { $exists = UsedCarDetail::where('source_url', $data['source_url']); $exists = $exists->where('user_id', $data['user_id']); if ($exists->exists()) { throw new BusinessException('该车源已收录'); } } // 如果提供了 crawl_car_id,自动抓取懂车帝车辆参数 if (!empty($data['crawl_car_id'])) { try { $params = $this->crawlParams($data['crawl_car_id']); if(str_contains($params['official_guide_price'],'万')){ $params['official_guide_price'] = str_replace('万','',$params['official_guide_price'])*10000; } // 合并抓取的数据,手动提交的字段优先级更高 $data = array_merge($params, $data); } catch (\Throwable $e) { // 抓取失败不阻断创建,记录日志 Log::warning('抓取车辆参数失败', [ 'crawl_car_id' => $data['crawl_car_id'], 'error' => $e->getMessage(), ]); } } return $data; } /** * 更新前数据处理 */ public function beforeServiceUpdate($data, Request $request): array { if (empty($data['product_name'])) { throw new BusinessException('产品名称不能为空'); } // 获取车辆信息 $carId = $data['id'] ?? null; if ($carId) { $car = UsedCarDetail::find($carId); if (!$car) { throw new BusinessException('车辆不存在'); } // 审核通过的车辆禁止修改 if ($car->status == 1) { throw new BusinessException('审核通过的车辆禁止修改'); } // 审核拒绝的车辆修改后状态改为待审核 if ($car->status == 2) { $data['status'] = 0; } } // 处理附件数据 if (isset($data['attachment'])) { if (is_array($data['attachment'])) { $data['attachment'] = json_encode($data['attachment']); } } // 处理介绍图片 if (isset($data['intro_images'])) { if (is_array($data['intro_images'])) { $data['intro_images'] = json_encode($data['intro_images']); } } return $data; } /** * 更新状态 */ public function serviceUpdateStatus(Request $request, array $data) { if (empty($data['id'])) { throw new BusinessException('id不能为空'); } if (!isset($data['status'])) { throw new BusinessException('status错误'); } return UsedCarDetail::where('id', $data['id'])->update(['status' => $data['status']]); } /** * 保存FAQ */ public function saveFaqs(int $carId, array $faqs) { // 删除旧的FAQ \app\common\model\UsedCarFaq::where('car_id', $carId)->delete(); // 创建新的FAQ if (!empty($faqs)) { foreach ($faqs as $faq) { \app\common\model\UsedCarFaq::create([ 'car_id' => $carId, 'question' => $faq['question'] ?? '', 'answer' => $faq['answer'] ?? '', 'sort' => $faq['sort'] ?? 0, 'status' => $faq['status'] ?? 1, ]); } } } /** * 生成车辆编号 */ public function getCode(int $userId): string { $userCount = Redis::get('used_car_detail_count_' . $userId, 0); $todayStart = strtotime('today'); $count = UsedCarDetail::where('user_id', $userId) ->where('created_at', '>=', $todayStart) ->count(); $count = $count < $userCount ? $userCount + 1 : $count + 1; $countCode = str_pad($count, 3, '0', STR_PAD_LEFT); $userCode = str_pad($userId, 3, '0', STR_PAD_LEFT); Redis::set('used_car_detail_count_' . $userId, $count, 86400); $user = User::where('id', $userId)->first(); $nameInitials = $this->getNameInitials($user['name'] ?? ''); return 'UCD' . $nameInitials . $userCode . date('Ymd') . $countCode; } /** * 获取中文名拼音首字母 */ private function getNameInitials(string $name): string { $initials = ''; $len = mb_strlen($name); for ($i = 0; $i < $len; $i++) { $char = mb_substr($name, $i, 1); $initials .= $this->getFirstChar($char); } return strtoupper($initials); } /** * 获取单个字符的首字母 */ private function getFirstChar(string $char): string { $s = iconv('UTF-8', 'GB2312//IGNORE', $char); $asc = ord($s[0]); if ($asc >= 65 && $asc <= 90) { return $char; } if ($s === false || strlen($s) < 2) { return ''; } $code = ord($s[0]) * 256 + ord($s[1]) - 65536; $map = [ [-20319, -20284, 'A'], [-20283, -19776, 'B'], [-19775, -19219, 'C'], [-19218, -18711, 'D'], [-18710, -18527, 'E'], [-18526, -18240, 'F'], [-18239, -17923, 'G'], [-17922, -17418, 'H'], [-17417, -16475, 'J'], [-16474, -16213, 'K'], [-16212, -15641, 'L'], [-15640, -15166, 'M'], [-15165, -14923, 'N'], [-14922, -14915, 'O'], [-14914, -14631, 'P'], [-14630, -14150, 'Q'], [-14149, -14091, 'R'], [-14090, -13319, 'S'], [-13318, -12839, 'T'], [-12838, -12557, 'W'], [-12556, -11848, 'X'], [-11847, -11056, 'Y'], [-11055, -10247, 'Z'] ]; foreach ($map as [$start, $end, $letter]) { if ($code >= $start && $code <= $end) { return $letter; } } return ''; } /** * 提交审核 */ public function submitAudit(Request $request, int $carId) { $car = UsedCarDetail::find($carId); if (!$car) { throw new BusinessException('车辆不存在'); } // 检查权限:只能提交自己创建的车辆 if ($car->user_id != $request->user['id']) { throw new BusinessException('无权操作'); } // 只有待审核状态才能提交 if ($car->status != 0) { throw new BusinessException('当前状态不允许提交审核'); } return UsedCarDetail::where('id', $carId)->update(['status' => 0]); } /** * 审核车辆 */ public function audit(Request $request, array $data) { $carId = $data['car_id'] ?? null; $auditStatus = $data['audit_status'] ?? null; $auditReason = $data['audit_reason'] ?? ''; if (empty($carId)) { throw new BusinessException('车辆ID不能为空'); } if (!isset($auditStatus) || !in_array($auditStatus, [1, 2])) { throw new BusinessException('审核状态错误'); } $car = UsedCarDetail::find($carId); if (!$car) { throw new BusinessException('车辆不存在'); } $beforeStatus = $car->status; // 创建审核记录 \app\common\model\UsedCarDetailAudit::create([ 'car_id' => $carId, 'auditor_id' => $request->user['id'], 'audit_status' => $auditStatus, 'audit_reason' => $auditReason, 'before_status' => $beforeStatus, 'after_status' => $auditStatus, ]); // 更新车辆状态 $result = UsedCarDetail::where('id', $carId)->update(['status' => $auditStatus]); // 审核通过后,将推送任务发送到队列 $pushQueued = false; if ($auditStatus == 1) { try { // 发送到 Redis 队列异步处理 \Webman\RedisQueue\Redis::send('wordpress-push', [ 'car_id' => $carId, ]); $pushQueued = true; Log::info('WordPress推送任务已加入队列', ['car_id' => $carId]); } catch (\Throwable $e) { Log::error('WordPress推送任务加入队列失败', [ 'car_id' => $carId, 'error' => $e->getMessage(), ]); $pushQueued = false; } } // 返回审核结果 return [ 'audit_success' => true, 'push_queued' => $pushQueued, 'message' => $pushQueued ? '审核通过,WordPress推送任务已加入队列' : '审核通过', ]; } /** * 获取审核记录列表 */ public function getAuditRecords(int $carId) { return \app\common\model\UsedCarDetailAudit::where('car_id', $carId) ->with(['auditor:id,name']) ->orderBy('created_at', 'desc') ->get() ->map(function ($item) { if(!is_numeric($item->created_at)){ $created_at = $item->created_at->timestamp; }else{ $created_at = $item->created_at; } return [ 'id' => $item->id, 'audit_status' => $item->audit_status, 'audit_status_text' => $item->audit_status_text, 'audit_reason' => $item->audit_reason, 'before_status' => $item->before_status, 'after_status' => $item->after_status, 'auditor_name' => $item->auditor->name ?? '', 'created_at' => date('Y-m-d H:i:s', $created_at), ]; }); } /** * 获取审核统计数据 */ public function getAuditStats(Request $request) { $query = UsedCarDetail::query(); // 如果不是管理员,只能看到自己的数据 if ($request->user['role_wp'] != 3) { $query->where('user_id', $request->user['id']); } return [ 'pending' => (clone $query)->where('status', 0)->count(), 'approved' => (clone $query)->where('status', 1)->count(), 'rejected' => (clone $query)->where('status', 2)->count(), 'total' => $query->count(), ]; } /** * 抓取懂车帝车辆参数 * * @param string $crawlCarId 车型ID * @return array 格式化后的参数数据 * @throws BusinessException */ public function crawlParams(string $crawlCarId): array { $url = 'https://www.dongchedi.com/auto/params-carIds-' . $crawlCarId; // 请求头配置 $headers = [ 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept-Language' => 'zh-CN,zh;q=0.9', 'Referer' => 'https://www.dongchedi.com/', ]; try { $client = new Client(); $response = $client->request('GET', $url, [ 'headers' => $headers, 'timeout' => 10, 'verify' => false, // 禁用SSL验证 ]); $html = (string)$response->getBody(); if (empty($html)) { throw new BusinessException('页面内容为空'); } // 解析HTML获取JSON数据 $ql = QueryList::html($html); $jsonText = $ql->find('#__NEXT_DATA__')->text(); if (empty($jsonText)) { throw new BusinessException('未找到 __NEXT_DATA__ JSON'); } $raw = json_decode($jsonText, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new BusinessException('JSON解析失败: ' . json_last_error_msg()); } // 提取数据 $properties = $raw['props']['pageProps']['rawData']['properties'] ?? []; $carInfo = $raw['props']['pageProps']['rawData']['car_info'][0] ?? []; if (empty($properties) || empty($carInfo)) { throw new BusinessException('数据结构异常,无法提取参数'); } // 构建值映射表和分组 $valuesMap = $this->buildValuesMap($carInfo); $groups = $this->buildCarSpecGroups($properties, $valuesMap); // 映射到数据库字段 return $this->mapParamsToDbFields($groups, $carInfo); } catch (GuzzleException $e) { Log::error('抓取懂车帝参数失败', [ 'crawl_car_id' => $crawlCarId, 'error' => $e->getMessage(), ]); throw new BusinessException('抓取参数失败: ' . $e->getMessage()); } catch (\Throwable $e) { Log::error('解析懂车帝参数失败', [ 'crawl_car_id' => $crawlCarId, 'error' => $e->getMessage(), ]); throw new BusinessException('解析参数失败: ' . $e->getMessage()); } } /** * 构建值映射表 */ private function buildValuesMap(array $carInfo): array { $valuesMap = []; // 顶层字段 foreach ($carInfo as $k => $v) { if ($k === 'info' || is_array($v)) { continue; } $valuesMap[$k] = ['value' => $v]; } // info中的配置项 if (!empty($carInfo['info']) && is_array($carInfo['info'])) { foreach ($carInfo['info'] as $k => $v) { if (is_array($v) && array_key_exists('value', $v)) { $valuesMap[$k] = $v; } } } return $valuesMap; } /** * 构建车辆规格分组 */ private function buildCarSpecGroups(array $properties, array $valuesMap): array { $groups = []; $currentKey = null; foreach ($properties as $item) { $type = (int)($item['type'] ?? 1); $key = $item['key'] ?? ''; $text = $item['text'] ?? ''; $subList = $item['sub_list'] ?? null; // 分类字段:type = 0 / 2 if (in_array($type, [0, 2], true)) { $currentKey = $key ?: ($text ?: ('group_' . count($groups))); if (!isset($groups[$currentKey])) { $groups[$currentKey] = [ 'group_key' => $currentKey, 'group_text' => $text, 'group_type' => $type, 'items' => [], ]; } continue; } // 普通字段:挂到最近的分类下 if ($currentKey === null) { $currentKey = '_default'; $groups[$currentKey] = [ 'group_key' => '_default', 'group_text' => '其他', 'group_type' => -1, 'items' => [], ]; } $value = ''; if ($key && isset($valuesMap[$key])) { $value = trim((string)($valuesMap[$key]['value'] ?? '')); } $node = [ 'key' => $key, 'text' => $text, 'value' => $value, ]; if (is_array($subList) && !empty($subList)) { $children = []; foreach ($subList as $sub) { $subKey = $sub['key'] ?? ''; $subText = $sub['text'] ?? ''; $subValue = ''; if ($subKey && isset($valuesMap[$subKey])) { $subValue = trim((string)($valuesMap[$subKey]['value'] ?? '')); } $children[] = [ 'key' => $subKey, 'text' => $subText, 'value' => $subValue, ]; } $node['children'] = $children; } $groups[$currentKey]['items'][] = $node; } return array_values($groups); } /** * 从分组数据中获取字段值 */ private function getFieldValue($groups, string $field): ?string { if (is_string($groups)) { $groups = json_decode($groups, true); if (json_last_error() !== JSON_ERROR_NONE) { return null; } } if (!is_array($groups)) { return null; } foreach ($groups as $group) { if (isset($group['items']) && is_array($group['items'])) { foreach ($group['items'] as $item) { if (($item['key'] ?? null) === $field) { return $item['value'] ?? null; } if (!empty($item['children']) && is_array($item['children'])) { foreach ($item['children'] as $child) { if (($child['key'] ?? null) === $field) { return $child['value'] ?? null; } } } } } } return null; } /** * 解析尺寸字符串 (如 "4890x1890x1680" 转为长宽高) */ private function parseDimensions(string $dimensionStr): array { $result = ['body_length' => '', 'body_width' => '', 'body_height' => '']; if (empty($dimensionStr)) { return $result; } $parts = explode('x', strtolower(str_replace(['×', 'X', 'X'], 'x', $dimensionStr))); if (count($parts) >= 3) { $result['body_length'] = trim($parts[0]); $result['body_width'] = trim($parts[1]); $result['body_height'] = trim($parts[2]); } return $result; } /** * 转换安全配置值 (●→标配, ○→选装, -→空) */ private function convertSafetyValue(?string $value): string { if ($value === '●') { return '标配'; } elseif ($value === '○') { return '选装'; } elseif ($value === '-') { return ''; } return $value ?? ''; } /** * 映射参数到数据库字段 * DongChedi 字段 → 数据库字段 */ private function mapParamsToDbFields(array $groups, array $carInfo): array { $result = []; // 从 car_info 直接获取的字段,清理掉"万"等字符 $officialPrice = $carInfo['official_price'] ?? ''; $result['official_guide_price'] = $this->convertPriceToYuan($officialPrice); // 1. 基本信息 $result['manufacturer'] = $this->getFieldValue($groups, 'sub_brand_name') ?? ''; $result['level'] = $this->getFieldValue($groups, 'jb') ?? ''; $result['energy_type'] = $this->getFieldValue($groups, 'fuel_form') ?? ''; $result['listing_time'] = $this->getFieldValue($groups, 'market_time') ?? ''; $result['engine'] = $this->getFieldValue($groups, 'engine_description') ?? ''; $result['electric_motor'] = $this->getFieldValue($groups, 'electric_description') ?? ''; $result['nedc_range'] = $this->parseNumeric($this->getFieldValue($groups, 'nedc_range')); $result['cltc_range'] = $this->parseNumeric($this->getFieldValue($groups, 'cltc_range')); $result['fast_charge_platform'] = $this->getFieldValue($groups, 'fast_charge_platform') ?? ''; $result['charging_time'] = $this->parseDecimal($this->getFieldValue($groups, 'charging_time')); $result['fast_charge_percent'] = $this->parseNumeric($this->getFieldValue($groups, 'fast_charge_percent')); $result['max_power_kw'] = $this->parseNumeric($this->getFieldValue($groups, 'energy_elect_max_power')); $result['max_torque_nm'] = $this->parseNumeric($this->getFieldValue($groups, 'energy_elect_max_torque')); $result['transmission_desc'] = $this->getFieldValue($groups, 'gearbox_description') ?? ''; $result['dimensions'] = $this->getFieldValue($groups, 'length_width_height') ?? ''; $result['body_structure'] = $this->getFieldValue($groups, 'car_body_struct') ?? ''; $result['max_speed'] = $this->parseNumeric($this->getFieldValue($groups, 'max_speed')); $result['acceleration_100'] = $this->parseDecimal($this->getFieldValue($groups, 'acceleration_time')); $result['nedc_fuel_consumption'] = $this->parseDecimal($this->getFieldValue($groups, 'fuel_comprehensive')); $result['power_consumption_100'] = $this->parseDecimal($this->getFieldValue($groups, 'power_consumption_100')); $result['equivalent_fuel_consumption'] = $this->parseDecimal($this->getFieldValue($groups, 'equivalent_fuel_consumption')); $result['maintenance_cost_60k'] = $this->parseDecimal($this->getFieldValue($groups, 'maintain_cost')); // 2. 车身信息 (解析尺寸) $dimensionStr = $this->getFieldValue($groups, 'length_width_height'); $dimensions = $this->parseDimensions($dimensionStr ?? ''); $result['body_length'] = $dimensions['body_length']; $result['body_width'] = $dimensions['body_width']; $result['body_height'] = $dimensions['body_height']; $result['wheelbase'] = $this->parseNumeric($this->getFieldValue($groups, 'wheelbase')); $result['door_count'] = $this->parseNumeric($this->getFieldValue($groups, 'door_nums')); $result['door_open_type'] = $this->getFieldValue($groups, 'door_open_way') ?? ''; $result['seat_count'] = $this->parseNumeric($this->getFieldValue($groups, 'seat_count')); // 3. 变速箱 $result['gear_count'] = $this->parseNumeric($this->getFieldValue($groups, 'stalls')); $result['transmission_type'] = $this->getFieldValue($groups, 'gearbox_type') ?? ''; // 4. 底盘/转向 $result['drive_mode'] = $this->getFieldValue($groups, 'driver_form') ?? ''; $result['front_suspension'] = $this->getFieldValue($groups, 'front_suspension_form') ?? ''; $result['rear_suspension'] = $this->getFieldValue($groups, 'rear_suspension_form') ?? ''; $result['steering_type'] = $this->getFieldValue($groups, 'power_steering_type') ?? ''; // 5. 主动安全配置 $result['has_abs'] = $this->convertSafetyValue($this->getFieldValue($groups, 'abs_anti_lock')); $result['has_ebd'] = $this->convertSafetyValue($this->getFieldValue($groups, 'brake_force')); $result['has_bra'] = $this->convertSafetyValue($this->getFieldValue($groups, 'brake_assist')); $result['has_tcs'] = $this->convertSafetyValue($this->getFieldValue($groups, 'traction_control')); $result['has_esp'] = $this->convertSafetyValue($this->getFieldValue($groups, 'body_stability_system')); $result['has_active_brake'] = $this->convertSafetyValue($this->getFieldValue($groups, 'active_brake')); $result['has_lane_assist'] = $this->convertSafetyValue($this->getFieldValue($groups, 'line_support')); $result['has_lka'] = $this->convertSafetyValue($this->getFieldValue($groups, 'lane_keeping_assist')); $result['has_lane_centering'] = $this->convertSafetyValue($this->getFieldValue($groups, 'lane_center')); $result['has_fatigue_remind'] = $this->convertSafetyValue($this->getFieldValue($groups, 'fatigue_driving_warning')); // 6. 车轮/制动 $result['front_brake_type'] = $this->getFieldValue($groups, 'front_brake_type') ?? ''; $result['rear_brake_type'] = $this->getFieldValue($groups, 'rear_brake_type') ?? ''; $result['parking_brake_type'] = $this->getFieldValue($groups, 'park_brake_type') ?? ''; $result['front_tire_spec'] = $this->getFieldValue($groups, 'front_tire_size') ?? ''; $result['rear_tire_spec'] = $this->getFieldValue($groups, 'rear_tire_size') ?? ''; // 确保空字符串不被转为null foreach ($result as $key => $value) { if ($value === null) { $result[$key] = ''; } } return $result; } /** * 解析数值字段 */ private function parseNumeric(?string $value): string { if ($value === null || $value === '') { return ''; } // 提取数字部分 if (preg_match('/\d+/', $value, $matches)) { return $matches[0]; } return ''; } /** * 解析小数 */ private function parseDecimal(?string $value): string { if ($value === null || $value === '') { return ''; } // 提取数字和小数点 if (preg_match('/[\d.]+/', $value, $matches)) { return $matches[0]; } return ''; } /** * 抓取懂车帝二手车数据并保存 * * @param string $url 懂车帝二手车链接 * @param int $userId 用户ID * @return array 返回创建的车辆数据 * @throws \Exception */ public function crawl(string $url, int $userId): array { // 检查该用户是否已抓取过此URL的车辆 $existingCar = UsedCarDetail::where('user_id', $userId) ->where('source_url', $url) ->first(); if ($existingCar) { throw new \Exception('该车辆信息已存在,无需重复抓取'); } // 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' => 3, // 待推送状态 'images' => $imagesValue, 'power' => $info['horsepower'] ?? '', ]; // 4. 合并详细参数,过滤掉数组类型的值 $params = array_filter($params, function($value) { return !is_array($value); }); $data = array_merge($data, $params); return $data; // 5. 创建记录 /*$car = UsedCarDetail::create($data); // 6. 将推送任务发送到队列 try { \Webman\RedisQueue\Redis::send('wordpress-push', [ 'car_id' => $car->id, ]); Log::info('WordPress推送任务已加入队列', ['car_id' => $car->id]); } catch (\Throwable $e) { Log::error('WordPress推送任务加入队列失败', [ 'car_id' => $car->id, 'error' => $e->getMessage(), ]); } return $car->toArray();*/ } /** * 保存懂车帝二手车数据 * * @param string $url 懂车帝二手车链接 * @param int $userId 用户ID * @return array 返回创建的车辆数据 * @throws \Exception */ public function crawlStore(array $data, int $userId): array { $data['user_id'] = $userId; // 5. 创建记录 $car = UsedCarDetail::create($data); // 6. 将推送任务发送到队列 try { \Webman\RedisQueue\Redis::send('wordpress-push', [ 'car_id' => $car->id, ]); Log::info('WordPress推送任务已加入队列', ['car_id' => $car->id]); } catch (\Throwable $e) { Log::error('WordPress推送任务加入队列失败', [ 'car_id' => $car->id, 'error' => $e->getMessage(), ]); } return $car->toArray(); } /** * 生成车辆编码 */ private function generateCode(): string { return 'ST' . date('mdHis') . 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; } /** * 重新推送单辆失败的车到WordPress * * @param int $carId 车辆ID * @return int 成功加入队列返回1,否则返回0 */ public function retryPush(int $carId): int { $car = UsedCarDetail::where('id', $carId) ->where('status', 5) // 只处理推送失败的 ->first(); if (!$car) { return 0; } // 更新状态为待推送 $car->update(['status' => 3]); // 加入队列 \Webman\RedisQueue\Redis::send('wordpress-push', ['car_id' => $carId]); Log::info('车辆重新加入推送队列', ['car_id' => $carId]); return 1; } }