package com.hotwj.platform.resourceManagement.vehicleManagement.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hotwj.platform.config.vehicleThreeInspect.service.IHotVehicleThreeInspectService;
import com.hotwj.platform.driverManagement.driver.domain.HotDriver;
import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper;
import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo;
import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService;
import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager;
import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper;
import com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.HotVehicleCertReissue;
import com.hotwj.platform.resourceManagement.vehicleCertReissue.mapper.HotVehicleCertReissueMapper;
import com.hotwj.platform.resourceManagement.vehicleContract.domain.HotVehicleContract;
import com.hotwj.platform.resourceManagement.vehicleContract.domain.bo.HotVehicleContractBo;
import com.hotwj.platform.resourceManagement.vehicleContract.mapper.HotVehicleContractMapper;
import com.hotwj.platform.resourceManagement.vehicleDevice.domain.HotVehicleDevice;
import com.hotwj.platform.resourceManagement.vehicleDevice.domain.bo.HotVehicleDeviceBo;
import com.hotwj.platform.resourceManagement.vehicleDevice.mapper.HotVehicleDeviceMapper;
import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.HotVehicleInsurance;
import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.bo.HotVehicleInsuranceBo;
import com.hotwj.platform.resourceManagement.vehicleInsurance.mapper.HotVehicleInsuranceMapper;
import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle;
import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleBo;
import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleStatusBo;
import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.DriverBindingVehicleItemVo;
import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.DriverBindingVehicleOptionsVo;
import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleCategoryStatVo;
import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleImportVo;
import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo;
import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper;
import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService;
import com.hotwj.platform.resourceManagement.vehicleRepair.domain.HotVehicleRepair;
import com.hotwj.platform.resourceManagement.vehicleRepair.mapper.HotVehicleRepairMapper;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@Service
public class HotVehicleServiceImpl implements IHotVehicleService {

    private final HotVehicleMapper baseMapper;
    private final HotVehicleContractMapper vehicleContractMapper;
    private final HotVehicleInsuranceMapper vehicleInsuranceMapper;
    private final HotVehicleDeviceMapper vehicleDeviceMapper;
    private final HotDriverMapper driverMapper;
    private final HotCompanySafetyManagerMapper managerMapper;
    private final IHotSystemNotificationService notificationService;
    private final HotVehicleRepairMapper repairMapper;
    private final HotVehicleCertReissueMapper certReissueMapper;
    private final DictService dictService;
    private final IHotVehicleThreeInspectService hotVehicleThreeInspectService;
    private static final Set<Long> CLEAR_DRIVER_OPERATION_STATUS = new HashSet<>(Arrays.asList(3L, 4L, 5L));

    /**
     * 统计公司车辆分类数量
     *
     * @param companyId 公司ID
     * @return 统计结果
     */
    @Override
    public List<HotVehicleCategoryStatVo> queryVehicleCategoryStat(Long companyId) {
        return baseMapper.selectVehicleCategoryStat(companyId);
    }

    /**
     * 导入车辆数据
     *
     * @param vehicleList 车辆数据列表
     * @param companyId   公司ID
     * @return 导入结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String importData(List<HotVehicleImportVo> vehicleList, Long companyId) {
        if (CollUtil.isEmpty(vehicleList)) {
            throw new ServiceException("导入数据不能为空！");
        }
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();

        for (HotVehicleImportVo vo : vehicleList) {
            try {
                // 验证数据格式
                ValidatorUtils.validate(vo);

                // 验证是否存在这个车牌号
                boolean exists = baseMapper.selectCount(new LambdaQueryWrapper<HotVehicle>()
                        .eq(HotVehicle::getPlateNumber, vo.getPlateNumber())
                        .eq(HotVehicle::getCompanyId, companyId)) > 0;

                if (exists) {
                    failureNum++;
                    failureMsg.append("<br/>" + failureNum + "、车牌号 " + vo.getPlateNumber() + " 已存在");
                    continue;
                }

                HotVehicle vehicle = MapstructUtils.convert(vo, HotVehicle.class);
                vehicle.setCompanyId(companyId);

                // 处理日期格式转换
                if (vo.getTransportValidMonth() != null) {
                    vehicle.setTransportValidMonth(DateUtil.format(vo.getTransportValidMonth(), "yyyy-MM"));
                }
                if (vo.getInspectionValidMonth() != null) {
                    vehicle.setInspectionValidMonth(DateUtil.format(vo.getInspectionValidMonth(), "yyyy-MM"));
                }

                // 备注
                vehicle.setRemark("Excel导入");

                validEntityBeforeSave(vehicle);

                baseMapper.insert(vehicle);
                successNum++;
                successMsg.append("<br/>" + successNum + "、车牌号 " + vo.getPlateNumber() + " 导入成功");
            } catch (ConstraintViolationException e) {
                failureNum++;
                String msg = e.getConstraintViolations().stream()
                        .map(ConstraintViolation::getMessage)
                        .collect(Collectors.joining(", "));
                failureMsg.append("<br/>" + failureNum + "、车牌号 " + vo.getPlateNumber() + " 导入失败：" + msg);
            } catch (Exception e) {
                failureNum++;
                String msg = "<br/>" + failureNum + "、车牌号 " + vo.getPlateNumber() + " 导入失败：";
                failureMsg.append(msg + e.getMessage());
                log.error(msg, e);
            }
        }
        if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉，导入失败！共 " + failureNum + " 条数据格式不正确，错误如下：");
            throw new ServiceException(failureMsg.toString());
        } else {
            successMsg.insert(0, "恭喜您，数据已全部导入成功！共 " + successNum + " 条，数据如下：");
        }
        return successMsg.toString();
    }

    /**
     * 查询公司车辆信息
     *
     * @param id 主键
     * @return 公司车辆信息
     */
    @Override
    public HotVehicleVo queryById(Long id) {
        HotVehicleVo vo = baseMapper.selectVoById(id);
        fillCurrentBindingPersonName(vo);
        return vo;
    }

    /**
     * 分页查询公司车辆信息列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 公司车辆信息分页列表
     */
    @Override
    public TableDataInfo<HotVehicleVo> queryPageList(HotVehicleBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<HotVehicle> lqw = buildQueryWrapper(bo);
        Page<HotVehicleVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        fillCurrentBindingPersonName(result.getRecords());
        return TableDataInfo.build(result);
    }

    /**
     * 分页查询公司车辆信息列表
     *
     * @param bo        查询条件 车辆状态为字符串逗号拼接
     * @param pageQuery 分页参数
     * @return 公司车辆信息分页列表
     */
    @Override
    public TableDataInfo<HotVehicleVo> queryPageListByStatus(HotVehicleStatusBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<HotVehicle> lqw = buildQueryWrapperByStatus(bo);
        Page<HotVehicleVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        fillCurrentBindingPersonName(result.getRecords());
        return TableDataInfo.build(result);
    }

    /**
     * 查询当前企业可绑定的车辆列表
     *
     * @param bo 查询条件
     * @return 可绑定车辆列表
     */
    @Override
    public List<HotVehicleVo> queryBindableList(HotVehicleBo bo) {
        LambdaQueryWrapper<HotVehicle> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(HotVehicle::getArchiveNo)
                .orderByAsc(HotVehicle::getPlateNumber)
                .eq(bo.getCompanyId() != null, HotVehicle::getCompanyId, bo.getCompanyId())
                .eq(HotVehicle::getVehicleStatus, 1L)
                .eq(HotVehicle::getOperationStatus, 1L)
                .eq(HotVehicle::getIsDeleted, 0L)
                .and(wrapper -> wrapper.isNull(HotVehicle::getCurrentDriver)
                        .or()
                        .eq(HotVehicle::getCurrentDriver, ""))
                .like(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicle::getPlateNumber, bo.getPlateNumber())
                .eq(StringUtils.isNotBlank(bo.getVehicleType()), HotVehicle::getVehicleType, bo.getVehicleType())
                .eq(StringUtils.isNotBlank(bo.getVehicleCategory()), HotVehicle::getVehicleCategory, bo.getVehicleCategory());
        return baseMapper.selectVoList(lqw);
    }

    @Override
    public DriverBindingVehicleOptionsVo queryDriverBindingVehicleOptions(Long companyId, String driverId) {
        DriverBindingVehicleOptionsVo result = new DriverBindingVehicleOptionsVo();
        if (companyId == null || StringUtils.isBlank(driverId)) {
            result.setBoundVehicleList(Collections.emptyList());
            result.setBindableVehicleList(Collections.emptyList());
            return result;
        }

        List<HotVehicle> companyVehicles = baseMapper.selectList(Wrappers.<HotVehicle>lambdaQuery()
                .eq(HotVehicle::getCompanyId, companyId)
                .eq(HotVehicle::getIsDeleted, 0L)
                .orderByAsc(HotVehicle::getArchiveNo)
                .orderByAsc(HotVehicle::getPlateNumber));

        List<DriverBindingVehicleItemVo> boundVehicleList = new ArrayList<>();
        List<DriverBindingVehicleItemVo> bindableVehicleList = new ArrayList<>();
        for (HotVehicle vehicle : companyVehicles) {
            List<String> currentDriverIds = splitCsv(vehicle.getCurrentDriver());
            if (currentDriverIds.contains(driverId)) {
                boundVehicleList.add(toDriverBindingVehicleItem(vehicle));
                continue;
            }
            if (isBindableVehicle(vehicle)) {
                bindableVehicleList.add(toDriverBindingVehicleItem(vehicle));
            }
        }
        result.setBoundVehicleList(boundVehicleList);
        result.setBindableVehicleList(bindableVehicleList);
        return result;
    }

    @Override
    public List<DriverBindingVehicleItemVo> queryDriverBoundVehicleList(Long companyId, String driverId) {
        if (companyId == null || StringUtils.isBlank(driverId)) {
            return Collections.emptyList();
        }
        List<HotVehicle> companyVehicles = baseMapper.selectList(Wrappers.<HotVehicle>lambdaQuery()
                .eq(HotVehicle::getCompanyId, companyId)
                .eq(HotVehicle::getIsDeleted, 0L)
                .orderByAsc(HotVehicle::getArchiveNo)
                .orderByAsc(HotVehicle::getPlateNumber));
        List<DriverBindingVehicleItemVo> boundVehicleList = new ArrayList<>();
        for (HotVehicle vehicle : companyVehicles) {
            if (splitCsv(vehicle.getCurrentDriver()).contains(driverId)) {
                boundVehicleList.add(toDriverBindingVehicleItem(vehicle));
            }
        }
        return boundVehicleList;
    }

    /**
     * 查询符合条件的公司车辆信息列表
     *
     * @param bo 查询条件
     * @return 公司车辆信息列表
     */
    @Override
    public List<HotVehicleVo> queryList(HotVehicleBo bo) {
        LambdaQueryWrapper<HotVehicle> lqw = buildQueryWrapper(bo);
        List<HotVehicleVo> list = baseMapper.selectVoList(lqw);
        fillCurrentBindingPersonName(list);
        return list;
    }

    private void fillCurrentBindingPersonName(List<HotVehicleVo> list) {
        if (CollUtil.isEmpty(list)) {
            return;
        }
        for (HotVehicleVo vo : list) {
            fillCurrentBindingPersonName(vo);
        }
    }

    private void fillCurrentBindingPersonName(HotVehicleVo vo) {
        if (vo == null || StringUtils.isBlank(vo.getCurrentDriver())) {
            return;
        }
        List<String> displayParts = new ArrayList<>();
        String[] ids = vo.getCurrentDriver().split(",");
        for (String idRaw : ids) {
            String id = idRaw == null ? null : idRaw.trim();
            if (StringUtils.isBlank(id)) {
                continue;
            }
            HotDriver driver = driverMapper.selectById(id);
            if (driver == null || StringUtils.isBlank(driver.getName())) {
                continue;
            }
            String personTypeLabel = "未知";
            if (StringUtils.isNotBlank(driver.getPersonType())) {
                String label = dictService.getDictLabel("hot_person_type", driver.getPersonType());
                personTypeLabel = StringUtils.isNotBlank(label) ? label : driver.getPersonType();
            }
            displayParts.add(driver.getName() + "(" + personTypeLabel + ")");
        }
        if (CollUtil.isNotEmpty(displayParts)) {
            vo.setCurrentBindingPersonName(String.join(",", displayParts));
        }
    }

    private LambdaQueryWrapper<HotVehicle> buildQueryWrapper(HotVehicleBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<HotVehicle> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(HotVehicle::getOperationStatus)
                .orderByAsc(HotVehicle::getArchiveNo);
        lqw.eq(bo.getCompanyId() != null, HotVehicle::getCompanyId, bo.getCompanyId());
        lqw.in(CollUtil.isNotEmpty(bo.getIds()), HotVehicle::getId, bo.getIds());
        lqw.eq(StringUtils.isNotBlank(bo.getGpsParams()), HotVehicle::getGpsParams, bo.getGpsParams());
        lqw.eq(bo.getIsTrailer() != null, HotVehicle::getIsTrailer, bo.getIsTrailer());
        lqw.eq(bo.getVehicleStatus() != null, HotVehicle::getVehicleStatus, bo.getVehicleStatus());
        lqw.eq(bo.getOperationStatus() != null, HotVehicle::getOperationStatus, bo.getOperationStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getCurrentDriver()), HotVehicle::getCurrentDriver, bo.getCurrentDriver());
        lqw.eq(StringUtils.isNotBlank(bo.getBodyImageUrls()), HotVehicle::getBodyImageUrls, bo.getBodyImageUrls());
        lqw.eq(StringUtils.isNotBlank(bo.getOtherAttachmentUrls()), HotVehicle::getOtherAttachmentUrls, bo.getOtherAttachmentUrls());
        lqw.eq(StringUtils.isNotBlank(bo.getColor()), HotVehicle::getColor, bo.getColor());
        lqw.eq(bo.getManufactureDate() != null, HotVehicle::getManufactureDate, bo.getManufactureDate());
        lqw.eq(StringUtils.isNotBlank(bo.getFuelType()), HotVehicle::getFuelType, bo.getFuelType());
        lqw.like(StringUtils.isNotBlank(bo.getManufacturerName()), HotVehicle::getManufacturerName, bo.getManufacturerName());
        lqw.eq(StringUtils.isNotBlank(bo.getPlateColor()), HotVehicle::getPlateColor, bo.getPlateColor());
        lqw.eq(bo.getVehicleRegisterDate() != null, HotVehicle::getVehicleRegisterDate, bo.getVehicleRegisterDate());
        lqw.eq(bo.getEngineDisplacementMl() != null, HotVehicle::getEngineDisplacementMl, bo.getEngineDisplacementMl());
        lqw.eq(bo.getTireCount() != null, HotVehicle::getTireCount, bo.getTireCount());
        lqw.eq(StringUtils.isNotBlank(bo.getChassisModel()), HotVehicle::getChassisModel, bo.getChassisModel());
        lqw.eq(StringUtils.isNotBlank(bo.getSatellitePositioningDevice()), HotVehicle::getSatellitePositioningDevice, bo.getSatellitePositioningDevice());
        lqw.eq(StringUtils.isNotBlank(bo.getTransmission()), HotVehicle::getTransmission, bo.getTransmission());
        lqw.eq(StringUtils.isNotBlank(bo.getRetarder()), HotVehicle::getRetarder, bo.getRetarder());
        lqw.eq(bo.getCargoInnerVolumeM3() != null, HotVehicle::getCargoInnerVolumeM3, bo.getCargoInnerVolumeM3());
        lqw.eq(StringUtils.isNotBlank(bo.getEmissionStandard()), HotVehicle::getEmissionStandard, bo.getEmissionStandard());
        lqw.eq(StringUtils.isNotBlank(bo.getBatteryType()), HotVehicle::getBatteryType, bo.getBatteryType());
        lqw.eq(bo.getInsuredSeatCount() != null, HotVehicle::getInsuredSeatCount, bo.getInsuredSeatCount());
        lqw.eq(StringUtils.isNotBlank(bo.getServiceBrakeType()), HotVehicle::getServiceBrakeType, bo.getServiceBrakeType());
        lqw.eq(StringUtils.isNotBlank(bo.getFrontBrakeType()), HotVehicle::getFrontBrakeType, bo.getFrontBrakeType());
        lqw.eq(StringUtils.isNotBlank(bo.getRearBrakeType()), HotVehicle::getRearBrakeType, bo.getRearBrakeType());
        lqw.eq(bo.getHasAbs() != null, HotVehicle::getHasAbs, bo.getHasAbs());
        lqw.eq(bo.getHasAirConditioning() != null, HotVehicle::getHasAirConditioning, bo.getHasAirConditioning());
        lqw.eq(bo.getHasTriangleBlock() != null, HotVehicle::getHasTriangleBlock, bo.getHasTriangleBlock());
        lqw.eq(bo.getHasFireExtinguisher() != null, HotVehicle::getHasFireExtinguisher, bo.getHasFireExtinguisher());
        lqw.eq(bo.getHasBreakdownWarningSign() != null, HotVehicle::getHasBreakdownWarningSign, bo.getHasBreakdownWarningSign());
        lqw.eq(bo.getDomesticMark() != null, HotVehicle::getDomesticMark, bo.getDomesticMark());
        lqw.eq(StringUtils.isNotBlank(bo.getVehicleCategory()), HotVehicle::getVehicleCategory, bo.getVehicleCategory());
        lqw.eq(bo.getMandatoryScrapEndDate() != null, HotVehicle::getMandatoryScrapEndDate, bo.getMandatoryScrapEndDate());
        lqw.eq(StringUtils.isNotBlank(bo.getUseNature()), HotVehicle::getUseNature, bo.getUseNature());
        lqw.eq(StringUtils.isNotBlank(bo.getVehicleModel()), HotVehicle::getVehicleModel, bo.getVehicleModel());
        lqw.eq(StringUtils.isNotBlank(bo.getFrameNumber()), HotVehicle::getFrameNumber, bo.getFrameNumber());
        lqw.eq(bo.getCargoInnerLengthMm() != null, HotVehicle::getCargoInnerLengthMm, bo.getCargoInnerLengthMm());
        lqw.eq(bo.getCargoInnerWidthMm() != null, HotVehicle::getCargoInnerWidthMm, bo.getCargoInnerWidthMm());
        lqw.eq(bo.getCargoInnerHeightMm() != null, HotVehicle::getCargoInnerHeightMm, bo.getCargoInnerHeightMm());
        lqw.eq(bo.getIsMortgaged() != null, HotVehicle::getIsMortgaged, bo.getIsMortgaged());
        lqw.eq(StringUtils.isNotBlank(bo.getMortgageCompany()), HotVehicle::getMortgageCompany, bo.getMortgageCompany());
        lqw.eq(StringUtils.isNotBlank(bo.getMortgageContact()), HotVehicle::getMortgageContact, bo.getMortgageContact());
        lqw.eq(StringUtils.isNotBlank(bo.getMortgageContactPhone()), HotVehicle::getMortgageContactPhone, bo.getMortgageContactPhone());
        lqw.eq(bo.getEnginePowerKw() != null, HotVehicle::getEnginePowerKw, bo.getEnginePowerKw());
        lqw.eq(bo.getMotorPowerKw() != null, HotVehicle::getMotorPowerKw, bo.getMotorPowerKw());
        lqw.eq(StringUtils.isNotBlank(bo.getEngineNumber()), HotVehicle::getEngineNumber, bo.getEngineNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getTireSpec()), HotVehicle::getTireSpec, bo.getTireSpec());
        lqw.eq(bo.getWheelbaseMm() != null, HotVehicle::getWheelbaseMm, bo.getWheelbaseMm());
        lqw.eq(bo.getAxleCount() != null, HotVehicle::getAxleCount, bo.getAxleCount());
        lqw.eq(StringUtils.isNotBlank(bo.getRegisterCertImageUrls()), HotVehicle::getRegisterCertImageUrls, bo.getRegisterCertImageUrls());
        lqw.like(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicle::getPlateNumber, bo.getPlateNumber());
        lqw.like(StringUtils.isNotBlank(bo.getArchiveNo()), HotVehicle::getArchiveNo, bo.getArchiveNo());
        lqw.eq(StringUtils.isNotBlank(bo.getVehicleType()), HotVehicle::getVehicleType, bo.getVehicleType());
        lqw.eq(bo.getFirstRegisterDate() != null, HotVehicle::getFirstRegisterDate, bo.getFirstRegisterDate());
        lqw.eq(bo.getCertificateIssueDate() != null, HotVehicle::getCertificateIssueDate, bo.getCertificateIssueDate());
        lqw.eq(StringUtils.isNotBlank(bo.getInspectionValidMonth()), HotVehicle::getInspectionValidMonth, bo.getInspectionValidMonth());
        lqw.eq(StringUtils.isNotBlank(bo.getOwner()), HotVehicle::getOwner, bo.getOwner());
        lqw.eq(StringUtils.isNotBlank(bo.getAddress()), HotVehicle::getAddress, bo.getAddress());
        lqw.eq(StringUtils.isNotBlank(bo.getBrandModel()), HotVehicle::getBrandModel, bo.getBrandModel());
        lqw.eq(bo.getRatedPassengerCount() != null, HotVehicle::getRatedPassengerCount, bo.getRatedPassengerCount());
        lqw.eq(bo.getGrossWeightKg() != null, HotVehicle::getGrossWeightKg, bo.getGrossWeightKg());
        lqw.eq(bo.getCurbWeightKg() != null, HotVehicle::getCurbWeightKg, bo.getCurbWeightKg());
        lqw.eq(bo.getRatedLoadKg() != null, HotVehicle::getRatedLoadKg, bo.getRatedLoadKg());
        lqw.eq(bo.getTowingMassKg() != null, HotVehicle::getTowingMassKg, bo.getTowingMassKg());
        lqw.eq(bo.getVehicleLengthMm() != null, HotVehicle::getVehicleLengthMm, bo.getVehicleLengthMm());
        lqw.eq(bo.getVehicleWidthMm() != null, HotVehicle::getVehicleWidthMm, bo.getVehicleWidthMm());
        lqw.eq(bo.getVehicleHeightMm() != null, HotVehicle::getVehicleHeightMm, bo.getVehicleHeightMm());
        lqw.eq(StringUtils.isNotBlank(bo.getDrivingLicenseMainUrls()), HotVehicle::getDrivingLicenseMainUrls, bo.getDrivingLicenseMainUrls());
        lqw.eq(StringUtils.isNotBlank(bo.getDrivingLicenseAuxUrls()), HotVehicle::getDrivingLicenseAuxUrls, bo.getDrivingLicenseAuxUrls());
        lqw.eq(StringUtils.isNotBlank(bo.getDrivingLicenseOtherUrl()), HotVehicle::getDrivingLicenseOtherUrl, bo.getDrivingLicenseOtherUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getTransportLicenseNo()), HotVehicle::getTransportLicenseNo, bo.getTransportLicenseNo());
        lqw.eq(bo.getTransportRegisterDate() != null, HotVehicle::getTransportRegisterDate, bo.getTransportRegisterDate());
        lqw.eq(bo.getTransportIssueDate() != null, HotVehicle::getTransportIssueDate, bo.getTransportIssueDate());
        lqw.eq(StringUtils.isNotBlank(bo.getTransportValidMonth()), HotVehicle::getTransportValidMonth, bo.getTransportValidMonth());
        lqw.eq(StringUtils.isNotBlank(bo.getTransportLicenseUrls()), HotVehicle::getTransportLicenseUrls, bo.getTransportLicenseUrls());
        lqw.like(StringUtils.isNotBlank(bo.getDriveMotorModel()), HotVehicle::getDriveMotorModel, bo.getDriveMotorModel());
        return lqw;
    }

    private LambdaQueryWrapper<HotVehicle> buildQueryWrapperByStatus(HotVehicleStatusBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<HotVehicle> lqw = Wrappers.lambdaQuery();
        lqw.orderByDesc(HotVehicle::getCreateTime);
        lqw.eq(bo.getCompanyId() != null, HotVehicle::getCompanyId, bo.getCompanyId());
        lqw.eq(StringUtils.isNotBlank(bo.getGpsParams()), HotVehicle::getGpsParams, bo.getGpsParams());
        lqw.eq(bo.getIsTrailer() != null, HotVehicle::getIsTrailer, bo.getIsTrailer());
        lqw.eq(bo.getVehicleStatus() != null, HotVehicle::getVehicleStatus, bo.getVehicleStatus());
        lqw.in(HotVehicle::getOperationStatus, Arrays.asList(bo.getOperationStatus().split(",")));
        lqw.and(wrapper -> wrapper.isNull(HotVehicle::getCurrentDriver)
                .or()
                .eq(HotVehicle::getCurrentDriver, ""));
        lqw.eq(StringUtils.isNotBlank(bo.getBodyImageUrls()), HotVehicle::getBodyImageUrls, bo.getBodyImageUrls());
        lqw.eq(StringUtils.isNotBlank(bo.getOtherAttachmentUrls()), HotVehicle::getOtherAttachmentUrls, bo.getOtherAttachmentUrls());
        lqw.eq(StringUtils.isNotBlank(bo.getColor()), HotVehicle::getColor, bo.getColor());
        lqw.eq(bo.getManufactureDate() != null, HotVehicle::getManufactureDate, bo.getManufactureDate());
        lqw.eq(StringUtils.isNotBlank(bo.getFuelType()), HotVehicle::getFuelType, bo.getFuelType());
        lqw.like(StringUtils.isNotBlank(bo.getManufacturerName()), HotVehicle::getManufacturerName, bo.getManufacturerName());
        lqw.eq(StringUtils.isNotBlank(bo.getPlateColor()), HotVehicle::getPlateColor, bo.getPlateColor());
        lqw.eq(bo.getVehicleRegisterDate() != null, HotVehicle::getVehicleRegisterDate, bo.getVehicleRegisterDate());
        lqw.eq(bo.getEngineDisplacementMl() != null, HotVehicle::getEngineDisplacementMl, bo.getEngineDisplacementMl());
        lqw.eq(bo.getTireCount() != null, HotVehicle::getTireCount, bo.getTireCount());
        lqw.eq(StringUtils.isNotBlank(bo.getChassisModel()), HotVehicle::getChassisModel, bo.getChassisModel());
        lqw.eq(StringUtils.isNotBlank(bo.getSatellitePositioningDevice()), HotVehicle::getSatellitePositioningDevice, bo.getSatellitePositioningDevice());
        lqw.eq(StringUtils.isNotBlank(bo.getTransmission()), HotVehicle::getTransmission, bo.getTransmission());
        lqw.eq(StringUtils.isNotBlank(bo.getRetarder()), HotVehicle::getRetarder, bo.getRetarder());
        lqw.eq(bo.getCargoInnerVolumeM3() != null, HotVehicle::getCargoInnerVolumeM3, bo.getCargoInnerVolumeM3());
        lqw.eq(StringUtils.isNotBlank(bo.getEmissionStandard()), HotVehicle::getEmissionStandard, bo.getEmissionStandard());
        lqw.eq(StringUtils.isNotBlank(bo.getBatteryType()), HotVehicle::getBatteryType, bo.getBatteryType());
        lqw.eq(bo.getInsuredSeatCount() != null, HotVehicle::getInsuredSeatCount, bo.getInsuredSeatCount());
        lqw.eq(StringUtils.isNotBlank(bo.getServiceBrakeType()), HotVehicle::getServiceBrakeType, bo.getServiceBrakeType());
        lqw.eq(StringUtils.isNotBlank(bo.getFrontBrakeType()), HotVehicle::getFrontBrakeType, bo.getFrontBrakeType());
        lqw.eq(StringUtils.isNotBlank(bo.getRearBrakeType()), HotVehicle::getRearBrakeType, bo.getRearBrakeType());
        lqw.eq(bo.getHasAbs() != null, HotVehicle::getHasAbs, bo.getHasAbs());
        lqw.eq(bo.getHasAirConditioning() != null, HotVehicle::getHasAirConditioning, bo.getHasAirConditioning());
        lqw.eq(bo.getHasTriangleBlock() != null, HotVehicle::getHasTriangleBlock, bo.getHasTriangleBlock());
        lqw.eq(bo.getHasFireExtinguisher() != null, HotVehicle::getHasFireExtinguisher, bo.getHasFireExtinguisher());
        lqw.eq(bo.getHasBreakdownWarningSign() != null, HotVehicle::getHasBreakdownWarningSign, bo.getHasBreakdownWarningSign());
        lqw.eq(bo.getDomesticMark() != null, HotVehicle::getDomesticMark, bo.getDomesticMark());
        lqw.eq(StringUtils.isNotBlank(bo.getVehicleCategory()), HotVehicle::getVehicleCategory, bo.getVehicleCategory());
        lqw.eq(bo.getMandatoryScrapEndDate() != null, HotVehicle::getMandatoryScrapEndDate, bo.getMandatoryScrapEndDate());
        lqw.eq(StringUtils.isNotBlank(bo.getUseNature()), HotVehicle::getUseNature, bo.getUseNature());
        lqw.eq(StringUtils.isNotBlank(bo.getVehicleModel()), HotVehicle::getVehicleModel, bo.getVehicleModel());
        lqw.eq(StringUtils.isNotBlank(bo.getFrameNumber()), HotVehicle::getFrameNumber, bo.getFrameNumber());
        lqw.eq(bo.getCargoInnerLengthMm() != null, HotVehicle::getCargoInnerLengthMm, bo.getCargoInnerLengthMm());
        lqw.eq(bo.getCargoInnerWidthMm() != null, HotVehicle::getCargoInnerWidthMm, bo.getCargoInnerWidthMm());
        lqw.eq(bo.getCargoInnerHeightMm() != null, HotVehicle::getCargoInnerHeightMm, bo.getCargoInnerHeightMm());
        lqw.eq(bo.getIsMortgaged() != null, HotVehicle::getIsMortgaged, bo.getIsMortgaged());
        lqw.eq(StringUtils.isNotBlank(bo.getMortgageCompany()), HotVehicle::getMortgageCompany, bo.getMortgageCompany());
        lqw.eq(StringUtils.isNotBlank(bo.getMortgageContact()), HotVehicle::getMortgageContact, bo.getMortgageContact());
        lqw.eq(StringUtils.isNotBlank(bo.getMortgageContactPhone()), HotVehicle::getMortgageContactPhone, bo.getMortgageContactPhone());
        lqw.eq(bo.getEnginePowerKw() != null, HotVehicle::getEnginePowerKw, bo.getEnginePowerKw());
        lqw.eq(bo.getMotorPowerKw() != null, HotVehicle::getMotorPowerKw, bo.getMotorPowerKw());
        lqw.eq(StringUtils.isNotBlank(bo.getEngineNumber()), HotVehicle::getEngineNumber, bo.getEngineNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getTireSpec()), HotVehicle::getTireSpec, bo.getTireSpec());
        lqw.eq(bo.getWheelbaseMm() != null, HotVehicle::getWheelbaseMm, bo.getWheelbaseMm());
        lqw.eq(bo.getAxleCount() != null, HotVehicle::getAxleCount, bo.getAxleCount());
        lqw.eq(StringUtils.isNotBlank(bo.getRegisterCertImageUrls()), HotVehicle::getRegisterCertImageUrls, bo.getRegisterCertImageUrls());
        lqw.like(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicle::getPlateNumber, bo.getPlateNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getArchiveNo()), HotVehicle::getArchiveNo, bo.getArchiveNo());
        lqw.eq(StringUtils.isNotBlank(bo.getVehicleType()), HotVehicle::getVehicleType, bo.getVehicleType());
        lqw.eq(bo.getFirstRegisterDate() != null, HotVehicle::getFirstRegisterDate, bo.getFirstRegisterDate());
        lqw.eq(bo.getCertificateIssueDate() != null, HotVehicle::getCertificateIssueDate, bo.getCertificateIssueDate());
        lqw.eq(StringUtils.isNotBlank(bo.getInspectionValidMonth()), HotVehicle::getInspectionValidMonth, bo.getInspectionValidMonth());
        lqw.eq(StringUtils.isNotBlank(bo.getOwner()), HotVehicle::getOwner, bo.getOwner());
        lqw.eq(StringUtils.isNotBlank(bo.getAddress()), HotVehicle::getAddress, bo.getAddress());
        lqw.eq(StringUtils.isNotBlank(bo.getBrandModel()), HotVehicle::getBrandModel, bo.getBrandModel());
        lqw.eq(bo.getRatedPassengerCount() != null, HotVehicle::getRatedPassengerCount, bo.getRatedPassengerCount());
        lqw.eq(bo.getGrossWeightKg() != null, HotVehicle::getGrossWeightKg, bo.getGrossWeightKg());
        lqw.eq(bo.getCurbWeightKg() != null, HotVehicle::getCurbWeightKg, bo.getCurbWeightKg());
        lqw.eq(bo.getRatedLoadKg() != null, HotVehicle::getRatedLoadKg, bo.getRatedLoadKg());
        lqw.eq(bo.getTowingMassKg() != null, HotVehicle::getTowingMassKg, bo.getTowingMassKg());
        lqw.eq(bo.getVehicleLengthMm() != null, HotVehicle::getVehicleLengthMm, bo.getVehicleLengthMm());
        lqw.eq(bo.getVehicleWidthMm() != null, HotVehicle::getVehicleWidthMm, bo.getVehicleWidthMm());
        lqw.eq(bo.getVehicleHeightMm() != null, HotVehicle::getVehicleHeightMm, bo.getVehicleHeightMm());
        lqw.eq(StringUtils.isNotBlank(bo.getDrivingLicenseMainUrls()), HotVehicle::getDrivingLicenseMainUrls, bo.getDrivingLicenseMainUrls());
        lqw.eq(StringUtils.isNotBlank(bo.getDrivingLicenseAuxUrls()), HotVehicle::getDrivingLicenseAuxUrls, bo.getDrivingLicenseAuxUrls());
        lqw.eq(StringUtils.isNotBlank(bo.getDrivingLicenseOtherUrl()), HotVehicle::getDrivingLicenseOtherUrl, bo.getDrivingLicenseOtherUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getTransportLicenseNo()), HotVehicle::getTransportLicenseNo, bo.getTransportLicenseNo());
        lqw.eq(bo.getTransportRegisterDate() != null, HotVehicle::getTransportRegisterDate, bo.getTransportRegisterDate());
        lqw.eq(bo.getTransportIssueDate() != null, HotVehicle::getTransportIssueDate, bo.getTransportIssueDate());
        lqw.eq(StringUtils.isNotBlank(bo.getTransportValidMonth()), HotVehicle::getTransportValidMonth, bo.getTransportValidMonth());
        lqw.eq(StringUtils.isNotBlank(bo.getTransportLicenseUrls()), HotVehicle::getTransportLicenseUrls, bo.getTransportLicenseUrls());
        return lqw;
    }
    /**
     * 新增公司车辆信息
     *
     * @param bo 公司车辆信息
     * @return 是否新增成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(HotVehicleBo bo) {
        // 新增车辆时不允许绑定驾驶员，驾驶员需在车辆创建后通过编辑流程再绑定
        bo.setCurrentDriver(null);
        HotVehicle add = MapstructUtils.convert(bo, HotVehicle.class);
        if (add.getOperationStatus() != null && CLEAR_DRIVER_OPERATION_STATUS.contains(add.getOperationStatus())) {
            add.setCurrentDriver(null);
            bo.setCurrentDriver(null);
        }
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (!flag) {
            return false;
        }
        String vehicleId = add.getId();
        if (StringUtils.isBlank(vehicleId)) {
            throw new ServiceException("新增车辆后未获取到车辆ID");
        }
        bo.setId(vehicleId);
        insertContracts(bo, vehicleId);
        insertVehicleInsurances(bo, vehicleId);
        insertVehicleDevices(bo, vehicleId);

        return true;
    }

    private void insertContracts(HotVehicleBo bo, String id) {
        if (CollUtil.isNotEmpty(bo.getVehicleContracts())) {
            List<HotVehicleContract> contracts = MapstructUtils.convert(bo.getVehicleContracts(), HotVehicleContract.class);
            for (HotVehicleContract contract : contracts) {
                contract.setId(IdWorker.get32UUID());
                contract.setVehicleId(id);
                contract.setCompanyId(bo.getCompanyId());
                contract.setIsDeleted(0L);

                vehicleContractMapper.insert(contract);
            }
        }
    }

    private void insertVehicleInsurances(HotVehicleBo bo, String id) {
        if (CollUtil.isNotEmpty(bo.getVehicleInsurances())) {
            List<HotVehicleInsurance> insurances = MapstructUtils.convert(bo.getVehicleInsurances(), HotVehicleInsurance.class);
            for (HotVehicleInsurance insurance : insurances) {
                insurance.setId(IdWorker.get32UUID());
                insurance.setVehicleId(id);
                insurance.setCompanyId(bo.getCompanyId());
                insurance.setIsDeleted(0L);

                vehicleInsuranceMapper.insert(insurance);
            }
        }
    }

    private void insertVehicleDevices(HotVehicleBo bo, String id) {
        if (CollUtil.isNotEmpty(bo.getVehicleDevices())) {
            List<HotVehicleDevice> devices = MapstructUtils.convert(bo.getVehicleDevices(), HotVehicleDevice.class);
            for (HotVehicleDevice device : devices) {
                device.setId(IdWorker.get32UUID());
                device.setVehicleId(id);
                device.setCompanyId(bo.getCompanyId());
                device.setIsDeleted(0L);

                vehicleDeviceMapper.insert(device);
            }
        }
    }

    /**
     * 修改公司车辆信息
     *
     * @param bo 公司车辆信息
     * @return 是否修改成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByBo(HotVehicleBo bo) {
        validateSingleCurrentDriverBinding(bo.getCurrentDriver());
        // 获取旧数据用于比对
        HotVehicle oldVehicle = baseMapper.selectById(bo.getId());

        HotVehicle update = MapstructUtils.convert(bo, HotVehicle.class);
        String normalizedCurrentDriver = normalizeSingleBindingValue(update.getCurrentDriver());
        update.setCurrentDriver(normalizedCurrentDriver);
        bo.setCurrentDriver(normalizedCurrentDriver);
        if (update.getOperationStatus() != null && CLEAR_DRIVER_OPERATION_STATUS.contains(update.getOperationStatus())) {
            update.setCurrentDriver(null);
            bo.setCurrentDriver(null);
        }
        validEntityBeforeSave(update);
        boolean flag = baseMapper.updateById(update) > 0;

        String vehicleId = update.getId();
        Long companyId = update.getCompanyId();

        // 处理驾驶员变更：车辆侧只允许单个驾驶员，驾驶员侧回写多车列表
        List<String> oldDriverIds = splitCsv(oldVehicle != null ? oldVehicle.getCurrentDriver() : null);
        List<String> newDriverIds = splitCsv(update.getCurrentDriver());
        String oldDriverId = oldDriverIds.isEmpty() ? null : oldDriverIds.get(0);
        String newDriverId = newDriverIds.isEmpty() ? null : newDriverIds.get(0);
        String oldPlate = oldVehicle != null ? oldVehicle.getPlateNumber() : null;
        String newPlate = update.getPlateNumber();
        boolean bindingChanged = !oldDriverIds.equals(newDriverIds) || !StringUtils.equals(oldPlate, newPlate);
        boolean currentDriverChanged = !oldDriverIds.equals(newDriverIds);

        if (currentDriverChanged) {
            hotVehicleThreeInspectService.validateNoUnfinishedProcess(update.getId(), "当前车辆三检流程未完成，不可进行换绑操作");
        }

        // 更新合同信息
        if (CollUtil.isNotEmpty(bo.getVehicleContracts())) {
            for (HotVehicleContractBo contractBo : bo.getVehicleContracts()) {
                if (StringUtils.isBlank(contractBo.getId())) {
                    // 新增
                    HotVehicleContract contract = MapstructUtils.convert(contractBo, HotVehicleContract.class);
                    contract.setId(IdWorker.get32UUID());
                    contract.setVehicleId(vehicleId);
                    contract.setCompanyId(companyId);
                    contract.setIsDeleted(0L);
                    vehicleContractMapper.insert(contract);
                } else if (contractBo.getIsDeleted() != null && contractBo.getIsDeleted() == 1L) {
                    // 删除
                    vehicleContractMapper.deleteById(contractBo.getId());
                } else {
                    // 更新
                    HotVehicleContract contract = MapstructUtils.convert(contractBo, HotVehicleContract.class);
                    contract.setVehicleId(vehicleId);
                    contract.setCompanyId(companyId);
                    vehicleContractMapper.updateById(contract);
                }
            }
        }

        // 更新保险信息
        if (CollUtil.isNotEmpty(bo.getVehicleInsurances())) {
            for (HotVehicleInsuranceBo insuranceBo : bo.getVehicleInsurances()) {
                if (StringUtils.isBlank(insuranceBo.getId())) {
                    // 新增
                    HotVehicleInsurance insurance = MapstructUtils.convert(insuranceBo, HotVehicleInsurance.class);
                    insurance.setId(IdWorker.get32UUID());
                    insurance.setVehicleId(vehicleId);
                    insurance.setCompanyId(companyId);
                    insurance.setIsDeleted(0L);
                    vehicleInsuranceMapper.insert(insurance);
                } else if (insuranceBo.getIsDeleted() != null && insuranceBo.getIsDeleted() == 1L) {
                    // 删除
                    vehicleInsuranceMapper.deleteById(insuranceBo.getId());
                } else {
                    // 更新
                    HotVehicleInsurance insurance = MapstructUtils.convert(insuranceBo, HotVehicleInsurance.class);
                    insurance.setVehicleId(vehicleId);
                    insurance.setCompanyId(companyId);
                    vehicleInsuranceMapper.updateById(insurance);
                }
            }
        }

        // 更新设备信息
        if (CollUtil.isNotEmpty(bo.getVehicleDevices())) {
            for (HotVehicleDeviceBo deviceBo : bo.getVehicleDevices()) {
                if (StringUtils.isBlank(deviceBo.getId())) {
                    // 新增
                    HotVehicleDevice device = MapstructUtils.convert(deviceBo, HotVehicleDevice.class);
                    device.setId(IdWorker.get32UUID());
                    device.setVehicleId(vehicleId);
                    device.setCompanyId(companyId);
                    device.setIsDeleted(0L);
                    vehicleDeviceMapper.insert(device);
                } else if (deviceBo.getIsDeleted() != null && deviceBo.getIsDeleted() == 1L) {
                    // 删除
                    vehicleDeviceMapper.deleteById(deviceBo.getId());
                } else {
                    // 更新
                    HotVehicleDevice device = MapstructUtils.convert(deviceBo, HotVehicleDevice.class);
                    device.setVehicleId(vehicleId);
                    device.setCompanyId(companyId);
                    vehicleDeviceMapper.updateById(device);
                }
            }
        }

        if (flag) {
            if (bindingChanged && companyId != null) {
                Set<String> impactedDriverIds = new LinkedHashSet<>();
                impactedDriverIds.addAll(oldDriverIds);
                impactedDriverIds.addAll(newDriverIds);
                syncDriverVehicleRelationsByDriverIds(companyId, impactedDriverIds);
            }

            if (StringUtils.isNotBlank(newDriverId) && !oldDriverIds.contains(newDriverId) && StringUtils.isNotBlank(newPlate)) {
                HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
                bos.setLevel("普通");
                bos.setContent("您已经绑定车辆【" + newPlate + "】。");
                bos.setSourceType("驾驶员管理");
                bos.setSenderType("SYSTEM");
                bos.setReceiverType("驾驶员");
                bos.setReceiverIds(Collections.singletonList(newDriverId));
                bos.setIsDeleted(0L);
                try {
                    notificationService.insertByBo(bos);
                } catch (Exception e) {
                    log.warn("发送驾驶员绑定车辆通知失败 companyId={} vehicleId={} plate={}", companyId, vehicleId, newPlate, e);
                }
            }

            Long oldVehicleStatus = oldVehicle != null ? oldVehicle.getVehicleStatus() : null;
            Long newVehicleStatus = update.getVehicleStatus();
            Long oldOperationStatus = oldVehicle != null ? oldVehicle.getOperationStatus() : null;
            Long newOperationStatus = update.getOperationStatus();
            boolean vsChanged = oldVehicleStatus != null && newVehicleStatus != null && !oldVehicleStatus.equals(newVehicleStatus);
            boolean osChanged = oldOperationStatus != null && newOperationStatus != null && !oldOperationStatus.equals(newOperationStatus);
            if (vsChanged || osChanged) {
                // 如果运营状态发生变更，同步更新 维修记录 和 证件补办记录 中的车辆运营状态
                if (osChanged && newOperationStatus != null) {
                    // 同步维修记录
                    repairMapper.update(null, new LambdaUpdateWrapper<HotVehicleRepair>()
                            .set(HotVehicleRepair::getOperationStatus, newOperationStatus)
                            .eq(HotVehicleRepair::getVehicleId, vehicleId)
                            .eq(HotVehicleRepair::getCompanyId, companyId));

                    // 同步证件补办记录
                    certReissueMapper.update(null, new LambdaUpdateWrapper<HotVehicleCertReissue>()
                            .set(HotVehicleCertReissue::getOperationStatus, newOperationStatus)
                            .eq(HotVehicleCertReissue::getVehicleId, vehicleId)
                            .eq(HotVehicleCertReissue::getCompanyId, companyId));
                }

                List<HotCompanySafetyManager> managers = managerMapper.selectList(
                        Wrappers.<HotCompanySafetyManager>lambdaQuery()
                                .eq(HotCompanySafetyManager::getCompanyId, companyId)
                                .eq(HotCompanySafetyManager::getIsDeleted, 0L)
                                .eq(HotCompanySafetyManager::getIsResigned, 0L)
                                .eq(HotCompanySafetyManager::getStatus, 1L)
                );
                if (CollUtil.isNotEmpty(managers)) {
                    List<String> receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).collect(Collectors.toList());
                    String vehicleStatusLabelOld = oldVehicleStatus == null ? "-" : (oldVehicleStatus == 1L ? "正常" : "停用");
                    String vehicleStatusLabelNew = newVehicleStatus == null ? "-" : (newVehicleStatus == 1L ? "正常" : "停用");
                    String operationStatusLabelOld = oldOperationStatus == null ? "-" : switch (oldOperationStatus.intValue()) { case 1 -> "正常"; case 2 -> "暂停"; case 3 -> "过户"; case 4 -> "报废"; default -> String.valueOf(oldOperationStatus); };
                    String operationStatusLabelNew = newOperationStatus == null ? "-" : switch (newOperationStatus.intValue()) { case 1 -> "正常"; case 2 -> "暂停"; case 3 -> "过户"; case 4 -> "报废"; default -> String.valueOf(newOperationStatus); };
                    String content;
                    if (vsChanged && osChanged) {
                        content = "车辆【" + update.getPlateNumber() + "】状态变更：车辆状态【" + vehicleStatusLabelOld + "】→【" + vehicleStatusLabelNew + "】，运营状态【" + operationStatusLabelOld + "】→【" + operationStatusLabelNew + "】。";
                    } else if (vsChanged) {
                        content = "车辆【" + update.getPlateNumber() + "】状态变更：车辆状态【" + vehicleStatusLabelOld + "】→【" + vehicleStatusLabelNew + "】。";
                    } else {
                        content = "车辆【" + update.getPlateNumber() + "】状态变更：运营状态【" + operationStatusLabelOld + "】→【" + operationStatusLabelNew + "】。";
                    }
                    HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
                    bos.setLevel("普通");
                    bos.setContent(content);
                    bos.setSourceType("车辆管理");
                    bos.setSenderType("SYSTEM");
                    bos.setReceiverType("管理员");
                    bos.setReceiverIds(receiverIds);
                    bos.setIsDeleted(0L);
                    notificationService.insertByBo(bos);
                }
            }
        }

        return flag;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(HotVehicle entity) {
        if (org.dromara.common.core.utils.StringUtils.isNotBlank(entity.getPlateNumber()) && entity.getCompanyId() != null) {
            LambdaQueryWrapper<HotVehicle> lqw = Wrappers.lambdaQuery();
            lqw.eq(HotVehicle::getPlateNumber, entity.getPlateNumber());
            lqw.eq(HotVehicle::getIsDeleted, 0L);
            lqw.ne(entity.getId() != null, HotVehicle::getId, entity.getId());
            lqw.last("limit 1");
            HotVehicle existsVehicle = baseMapper.selectOne(lqw);
            if (existsVehicle != null) {
                if (existsVehicle.getCompanyId() != null
                        && !Objects.equals(existsVehicle.getCompanyId(), entity.getCompanyId())) {
                    throw new ServiceException("同一车牌号的车辆不能存在于不同企业");
                }
                throw new ServiceException("车牌号已被录入");
            }
        }
    }

    private void validateSingleCurrentDriverBinding(String currentDriver) {
        if (splitCsv(currentDriver).size() > 1) {
            throw new ServiceException("一辆车只能绑定一个驾驶员");
        }
    }

    private boolean isBindableVehicle(HotVehicle vehicle) {
        return vehicle != null
                && Long.valueOf(1L).equals(vehicle.getVehicleStatus())
                && Long.valueOf(1L).equals(vehicle.getOperationStatus())
                && StringUtils.isBlank(vehicle.getCurrentDriver());
    }

    private DriverBindingVehicleItemVo toDriverBindingVehicleItem(HotVehicle vehicle) {
        DriverBindingVehicleItemVo item = new DriverBindingVehicleItemVo();
        item.setVehicleId(vehicle.getId());
        item.setPlateNumber(vehicle.getPlateNumber());
        item.setCurrentDriverId(normalizeSingleBindingValue(vehicle.getCurrentDriver()));
        item.setCurrentDriverName(resolveCurrentDriverName(item.getCurrentDriverId()));
        item.setVehicleType(vehicle.getVehicleType());
        return item;
    }

    private String resolveCurrentDriverName(String driverId) {
        if (StringUtils.isBlank(driverId)) {
            return null;
        }
        HotDriver driver = driverMapper.selectById(driverId);
        return driver == null ? null : driver.getName();
    }

    private String normalizeSingleBindingValue(String value) {
        List<String> values = splitCsv(value);
        return values.isEmpty() ? null : values.get(0);
    }

    private List<String> splitCsv(String value) {
        if (StringUtils.isBlank(value)) {
            return new ArrayList<>();
        }
        return Arrays.stream(value.split(","))
                .map(String::trim)
                .filter(StringUtils::isNotBlank)
                .distinct()
                .collect(Collectors.toList());
    }

    private String joinCsv(Collection<String> values) {
        if (CollUtil.isEmpty(values)) {
            return null;
        }
        return values.stream()
                .filter(StringUtils::isNotBlank)
                .distinct()
                .collect(Collectors.joining(","));
    }

    private void syncDriverVehicleRelationsByDriverIds(Long companyId, Collection<String> driverIds) {
        if (companyId == null || CollUtil.isEmpty(driverIds)) {
            return;
        }
        List<HotVehicle> vehicles = baseMapper.selectList(Wrappers.<HotVehicle>lambdaQuery()
                .eq(HotVehicle::getCompanyId, companyId)
                .eq(HotVehicle::getIsDeleted, 0L));
        for (String driverId : driverIds) {
            if (StringUtils.isBlank(driverId)) {
                continue;
            }
            List<String> vehicleIds = new ArrayList<>();
            List<HotVehicle> boundVehicles = new ArrayList<>();
            for (HotVehicle vehicle : vehicles) {
                if (!splitCsv(vehicle.getCurrentDriver()).contains(driverId)) {
                    continue;
                }
                boundVehicles.add(vehicle);
                vehicleIds.add(vehicle.getId());
            }
            HotDriver driver = driverMapper.selectById(driverId);
            String primaryVehicleId = resolvePrimaryVehicleId(driver, vehicleIds);
            String primaryPlateNumber = resolvePrimaryPlateNumber(primaryVehicleId, boundVehicles);
            driverMapper.update(null, new LambdaUpdateWrapper<HotDriver>()
                    .set(HotDriver::getVehicleId, primaryVehicleId)
                    .set(HotDriver::getVehicleIds, joinCsv(vehicleIds))
                    .set(HotDriver::getPlateNumber, primaryPlateNumber)
                    .eq(HotDriver::getId, driverId)
                    .eq(HotDriver::getCompanyId, companyId));
        }
    }

    private String resolvePrimaryVehicleId(HotDriver driver, Collection<String> boundVehicleIds) {
        if (CollUtil.isEmpty(boundVehicleIds)) {
            return null;
        }
        if (driver != null && StringUtils.isNotBlank(driver.getVehicleId()) && boundVehicleIds.contains(driver.getVehicleId())) {
            return driver.getVehicleId();
        }
        return boundVehicleIds.stream()
                .filter(StringUtils::isNotBlank)
                .findFirst()
                .orElse(null);
    }

    private String resolvePrimaryPlateNumber(String primaryVehicleId, Collection<HotVehicle> boundVehicles) {
        if (StringUtils.isBlank(primaryVehicleId) || CollUtil.isEmpty(boundVehicles)) {
            return null;
        }
        return boundVehicles.stream()
                .filter(vehicle -> StringUtils.equals(primaryVehicleId, vehicle.getId()))
                .map(HotVehicle::getPlateNumber)
                .filter(StringUtils::isNotBlank)
                .findFirst()
                .orElse(null);
    }

    /**
     * 校验并批量删除公司车辆信息信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // 校验车辆是否绑定驾驶员
            List<HotVehicle> vehicles = baseMapper.selectBatchIds(ids);
            if (CollUtil.isNotEmpty(vehicles)) {
                for (HotVehicle vehicle : vehicles) {
                    if (StringUtils.isNotBlank(vehicle.getCurrentDriver())) {
                        throw new ServiceException("车辆【" + vehicle.getPlateNumber() + "】已绑定驾驶员，不可删除！");
                    }
                }
            }
        }
        return baseMapper.deleteByIds(ids) > 0;
    }
}
