Files
useds/app/common/service/UsedCarDetailService.php
T
2026-03-11 09:26:42 +08:00

903 lines
31 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace app\common\service;
use app\common\validate\UsedCarDetailValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\UsedCarDetail;
use app\common\model\User;
use support\exception\BusinessException;
use support\Log;
use support\Request;
use support\Redis;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use QL\QueryList;
class UsedCarDetailService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = UsedCarDetail::class;
/**
* 验证器
*/
protected $validator = UsedCarDetailValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->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']);
$params['official_guide_price'] = str_replace('万','',$params['official_guide_price']);
// 合并抓取的数据,手动提交的字段优先级更高
$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]);
// 审核通过后自动推送到WordPress
$pushResult = ['success' => true, 'message' => ''];
if ($auditStatus == 1) {
$pushResult = $this->pushToWordpressAfterAudit($carId);
// 推送失败,将车辆状态修改为3
if (!$pushResult['success']) {
UsedCarDetail::where('id', $carId)->update(['status' => 3]);
}
}
// 返回审核结果和推送结果
return [
'audit_success' => true,
'push_success' => $pushResult['success'],
'push_message' => $pushResult['message'],
'message' => $pushResult['success'] ? '审核通过并推送成功' : '审核通过,但推送失败:' . $pushResult['message'],
];
}
/**
* 审核通过后推送车辆到WordPress
* @return array ['success' => bool, 'message' => string]
*/
private function pushToWordpressAfterAudit(int $carId): array
{
try {
// 获取启用的WordPress站点
$site = \app\common\model\WordpressSite::where('status', 1)->first();
if (!$site) {
$msg = '没有启用的WordPress站点';
Log::warning('WordPress推送失败:' . $msg, ['car_id' => $carId]);
return ['success' => false, 'message' => $msg];
}
// 检查是否已推送过
$existing = \app\common\model\WordpressUsedCar::where('wp_site_id', $site->id)
->where('used_car_id', $carId)
->where('status', 1)
->first();
if ($existing) {
$msg = '车辆已推送,跳过';
Log::info('WordPress推送:' . $msg, ['car_id' => $carId]);
return ['success' => true, 'message' => $msg];
}
// 调用推送服务
$wpService = new \app\common\service\WordpressSiteService();
$wpService->createProductForNewSite($site, $carId);
Log::info('WordPress推送成功', ['car_id' => $carId, 'site_id' => $site->id]);
return ['success' => true, 'message' => ''];
} catch (\Throwable $e) {
$msg = $e->getMessage();
Log::error('WordPress推送失败', [
'car_id' => $carId,
'error' => $msg,
'trace' => $e->getTraceAsString(),
]);
return ['success' => false, 'message' => $msg];
}
}
/**
* 获取审核记录列表
*/
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) {
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', $item->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 直接获取的字段
$result['official_guide_price'] = $carInfo['official_price'] ?? '';
// 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 '';
}
}