package com.xiaopeng.vehicleConvoy.controller; import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.ExcelImportUtil; import cn.afterturn.easypoi.excel.entity.ExportParams; import cn.afterturn.easypoi.excel.entity.ImportParams; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.lang.Validator; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.mongodb.client.MongoCollection; import com.xiaopeng.api.dto.operation.VehiclePositionByVinDto; import com.xiaopeng.api.dto.vehicleConvoy.ServiceVehicleListDto; import com.xiaopeng.api.dto.vehicleConvoy.UpdateVehicleStateDto; import com.xiaopeng.api.dto.vehicleConvoy.VehicleTemperatureDto; import com.xiaopeng.api.feignClient.maintenance.*; import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; import com.xiaopeng.api.model.account.XpOperationsUser;import com.xiaopeng.api.model.maintenance.XpTask; import com.xiaopeng.api.model.maintenance.XpTaskRule; import com.xiaopeng.api.model.operation.XpOfficialPartner; import com.xiaopeng.api.model.vehicleConvoy.XpFleet; import com.xiaopeng.api.model.vehicleConvoy.XpFleetVehicle; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.model.vehicleConvoy.XpVehicleAnnualRecord; import com.xiaopeng.api.vo.automaticPilot.VehiclePosition; import com.xiaopeng.api.vo.operation.VehiclePositionVo; import com.xiaopeng.api.vo.vehicleConvoy.VehicleDoorStatusVo; import com.xiaopeng.api.vo.vehicleConvoy.VehicleStateVo; import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandCallbackVo; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.exception.ServiceException; import com.xiaopeng.common.core.utils.SensitiveDataCryptoUtil; import com.xiaopeng.common.core.utils.WebUtils; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.common.log.annotation.AdminOperationLog; import com.xiaopeng.common.log.enums.AdminBusinessType; import com.xiaopeng.common.log.enums.AdminOperationType; import com.xiaopeng.common.security.service.TokenService; import com.xiaopeng.evi.oss.service.AliOssService; import com.xiaopeng.unifiedverification.interceptor.XpClientCheck; import com.xiaopeng.vehicleConvoy.constant.I18nKeyConstant; import com.xiaopeng.vehicleConvoy.model.dto.*; import com.xiaopeng.vehicleConvoy.model.exportExcel.XpVehicleExportExcel; import com.xiaopeng.vehicleConvoy.model.importExcel.XpVehicleImportExcel; import com.xiaopeng.vehicleConvoy.model.query.*; import com.xiaopeng.api.vo.vehicleConvoy.XpVehicleVO; import com.xiaopeng.vehicleConvoy.model.vo.VehiclePositionDto; import com.xiaopeng.vehicleConvoy.service.IXpFleetService; import com.xiaopeng.vehicleConvoy.service.IXpFleetVehicleService; import com.xiaopeng.vehicleConvoy.service.IXpVehicleAnnualRecordService; import com.xiaopeng.vehicleConvoy.service.IXpVehicleService; import com.xiaopeng.vehicleConvoy.util.AliOssDownloadUtil; import io.swagger.annotations.*; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException; import org.apache.poi.ss.usermodel.Workbook; import org.bson.Document; import org.springframework.beans.BeanUtils; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Lazy; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.data.geo.Distance; import org.springframework.data.geo.Metrics; import org.springframework.data.geo.Point; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.io.*; import java.net.URLEncoder; import java.time.LocalDateTime; import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; import com.xiaopeng.common.log.annotation.AdminOperationLog; import com.xiaopeng.common.log.enums.AdminBusinessType; import com.xiaopeng.common.log.enums.AdminOperationType; import java.util.stream.Collectors; /** *

* 车辆管理 前端控制器 *

* * @author mitao * @since 2026-02-03 */ @Slf4j @RestController @Api(tags = "车辆管理") @RequiredArgsConstructor public class XpVehicleController { private static final Pattern VEHICLE_CODE_PATTERN = Pattern.compile("^[A-Za-z0-9_]{1,5}$"); private static final Pattern VEHICLE_VIN_PATTERN = Pattern.compile("^[A-Z0-9]{17}$"); private final IXpVehicleService xpVehicleService; private final IXpFleetVehicleService vehicleService; private final IXpFleetService xpFleetService; private final IXpVehicleAnnualRecordService xpVehicleAnnualRecordService; private final XpVehiclePreparationClient xpVehiclePreparationClient; private final XpChargingRecordClient xpChargingRecordClient; private final XpTaskClient xpTaskClient; private final XpTaskRuleClient xpTaskRuleClient; private final MongoTemplate mongoTemplate; private final MessageSource messageSource; private final TokenService tokenService; private final XpVehicleRepairClient xpVehicleRepairClient; private final AliOssService aliOssService; private final AliOssDownloadUtil aliOssDownloadUtil; /** * 根据ID列表批量查询车辆信息(内部接口) * * @param ids 车辆ID列表 * @return 车辆列表 */ @ApiOperation(value = "批量查询车辆信息", tags = {"内部接口"}) @PostMapping("/logan/inner/vehicle/listByIds") public R> listByIds(@RequestBody List ids) { if (CollectionUtils.isEmpty(ids)) { return R.ok(new ArrayList<>()); } return R.ok(xpVehicleService.listByIds(ids)); } /** * 批量获取车辆列表 * * @return 车辆列表 */ @ApiOperation(value = "批量查询车辆信息", tags = {"内部接口"}) @PostMapping("/logan/inner/vehicle/listVehicle") public R> listVehicle() { return R.ok(xpVehicleService.list()); } /** * 根据ID查询车辆信息(内部接口) * * @param id 车辆ID * @return 车辆列表 */ @GetMapping("/logan/inner/vehicle/queryById") public R queryById(@RequestParam Long id) throws UnsupportedEncodingException { XpVehicle xpVehicle = xpVehicleService.getById(id); if (Objects.nonNull(xpVehicle)) { String vehiclePictures = xpVehicle.getVehiclePictures(); if (vehiclePictures != null) { JSONObject jsonObject = JSON.parseObject(vehiclePictures); // 1 // String downloadUrl = aliOssService.getDownloadUrl(jsonObject.getString("id"), jsonObject.getString("name"), true); // jsonObject.put("url", downloadUrl); jsonObject.put("url", ""); xpVehicle.setVehiclePictures(jsonObject.toJSONString()); } // 获取车队 XpFleetVehicle xpFleetVehicle = vehicleService.getOne(new LambdaQueryWrapper().eq(XpFleetVehicle::getVehicleId, xpVehicle.getId())); if (Objects.nonNull(xpFleetVehicle)) { xpVehicle.setFleetId(xpFleetVehicle.getFleetId()); } } return R.ok(xpVehicle); } /** * 修改车辆信息(内部接口) * * @param xpVehicle 车辆信息 * @return 车辆 */ @PostMapping("/logan/inner/vehicle/updateVehicleById") public R updateVehicleById(@RequestBody XpVehicle xpVehicle) { xpVehicleService.updateById(xpVehicle); return R.ok(); } /** * 新增车辆 * * @param dto 车辆信息 * @return 操作结果 */ @ApiOperation(value = "新增车辆", tags = {"管理后台-车辆管理-在库车辆管理"}) @PostMapping("/api/v1/vehicle/add") @AdminOperationLog( businessType = AdminBusinessType.STOCK_VEHICLE_MANAGEMENT, operationType = AdminOperationType.ADD, content = "新增车辆", appendRequestData = true ) public R addVehicle(@Valid @RequestBody VehicleAddDTO dto) { xpVehicleService.addVehicle(dto, null); return R.ok(); } /** * 编辑车辆 * * @param dto 车辆信息 * @return 操作结果 */ @ApiOperation(value = "编辑车辆", tags = {"管理后台-车辆管理-在库车辆管理"}) @PostMapping("/api/v1/vehicle/edit") @AdminOperationLog( businessType = AdminBusinessType.STOCK_VEHICLE_MANAGEMENT, operationType = AdminOperationType.EDIT, content = "编辑车辆", appendRequestData = true ) public R editVehicle(@Valid @RequestBody VehicleEditDTO dto) { xpVehicleService.editVehicle(dto, null); return R.ok(); } /** * 完善/更新车辆信息(保险、年检、运营资质等) * * @param dto 车辆信息 * @return 操作结果 */ @ApiOperation(value = "完善/更新车辆信息", tags = {"管理后台-车辆管理-在库车辆管理"}) @PostMapping("/api/v1/vehicle/improveInfo") @AdminOperationLog( businessType = AdminBusinessType.STOCK_VEHICLE_MANAGEMENT, operationType = AdminOperationType.EDIT, content = "完善/更新车辆信息", appendRequestData = true ) public R improveInfo(@RequestBody VehicleImproveInfoDTO dto) { xpVehicleService.improveInfo(dto); return R.ok(); } /** * 批量运营(在库→运营) * * @param idStr 车辆ID字符串列表 * @return 操作结果 */ @ApiOperation(value = "批量运营", tags = {"管理后台-车辆管理-在库车辆管理"}) @PostMapping("/api/v1/vehicle/batchOperation") @ApiImplicitParam(name = "idStr", value = "车辆ID字符串列表,逗号分隔", required = true, dataTypeClass = String.class) @AdminOperationLog( businessType = AdminBusinessType.STOCK_VEHICLE_MANAGEMENT, operationType = AdminOperationType.EDIT, contentExpression = "'批量运营车辆,ids=' + #p0", appendRequestData = false ) public R batchOperation(@RequestParam String idStr) { xpVehicleService.batchOperation(idStr); return R.ok(); } /** * 批量设置运营区域 * * @param dto 批量设置运营区域参数 * @return 操作结果 */ @ApiOperation(value = "批量设置运营区域", tags = {"管理后台-车辆管理-在库车辆管理"}) @PostMapping("/api/v1/vehicle/batchSetElectronic") @AdminOperationLog( businessType = AdminBusinessType.STOCK_VEHICLE_MANAGEMENT, operationType = AdminOperationType.EDIT, content = "批量设置运营区域", appendRequestData = true ) public R batchSetElectronic(@Valid @RequestBody VehicleBatchSetElectronicDTO dto) { xpVehicleService.batchSetElectronic(dto.getIds(), dto.getElectronicId()); return R.ok(); } /** * 在库车辆管理分页列表 * * @param query 查询参数 * @return 分页结果 */ @ApiOperation(value = "在库车辆管理分页列表", tags = {"管理后台-车辆管理-在库车辆管理"}) @PostMapping("/api/v1/vehicle/pageList") public R> vehiclePageList(@RequestBody VehiclePageQuery query) { return R.ok(xpVehicleService.vehiclePageList(query)); } /** * 导出在库车辆列表 * */ @ApiOperation(value = "下载导入模板", tags = {"管理后台-车辆管理-在库车辆管理"}) @PostMapping("/api/v1/vehicle/importTemplate") @AdminOperationLog( businessType = AdminBusinessType.STOCK_VEHICLE_MANAGEMENT, operationType = AdminOperationType.EXPORT, content = "下载车辆导入模板", appendRequestData = false ) public void importTemplate() { // 1. 查询导出数据 Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), XpVehicleImportExcel.class, new ArrayList<>()); // 4. 输出到响应流 HttpServletResponse response = WebUtils.response(); response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); ServletOutputStream outputStream = null; try { String fileName = URLEncoder.encode("在库车辆导入模板.xlsx", "utf-8"); response.setHeader("Content-disposition", "attachment;filename=" + fileName); outputStream = response.getOutputStream(); workbook.write(outputStream); } catch (IOException e) { log.error("下载在库车辆导入模板失败", e); } finally { try { if (outputStream != null) { outputStream.close(); } } catch (IOException e) { log.error("关闭输出流失败", e); } } } /** * 导入车辆 * * @param file Excel文件 * @return 操作结果 */ @ApiOperation(value = "导入", tags = {"管理后台-车辆管理-在库车辆管理"}) @PostMapping("/api/v1/vehicle/importExcel") @AdminOperationLog( businessType = AdminBusinessType.STOCK_VEHICLE_MANAGEMENT, operationType = AdminOperationType.IMPORT, content = "导入车辆", appendRequestData = false ) public R importExcel(@RequestParam("file") MultipartFile file) { ImportParams params = new ImportParams(); params.setHeadRows(1); InputStream inputStream = null; List importList; try { inputStream = file.getInputStream(); importList = ExcelImportUtil.importExcel(inputStream, XpVehicleImportExcel.class, params); } catch (Exception e) { log.error("车辆导入失败:{}", e.getMessage(), e); if (e instanceof NotOfficeXmlFileException || e.getCause() instanceof NotOfficeXmlFileException) { throw new ServiceException("上传文件不是有效的Excel(.xlsx)文件!", I18nKeyConstant.VEHICLE_IMPORT_FAILED); } throw new ServiceException("车辆导入失败!", I18nKeyConstant.VEHICLE_IMPORT_FAILED); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { log.error("关闭输入流失败", e); } } } if (CollectionUtils.isEmpty(importList)) { throw new ServiceException("导入数据为空!", I18nKeyConstant.VEHICLE_IMPORT_DATA_EMPTY); } validateImportRequiredFields(importList, params.getHeadRows()); validateImportBusinessFields(importList, params.getHeadRows()); uploadVehiclePicture(importList); xpVehicleService.importVehicles(importList); return R.ok(); } private void validateImportRequiredFields(List importList, int headRows) { for (int index = 0; index < importList.size(); index++) { XpVehicleImportExcel row = importList.get(index); List missingFields = new ArrayList<>(); addIfBlank(missingFields, row.getVehicleCode(), "vehicle.import.field.vehicleCode"); addIfBlank(missingFields, row.getVehicleVin(), "vehicle.import.field.vehicleVin"); addIfBlank(missingFields, row.getPlateNumber(), "vehicle.import.field.plateNumber"); addIfBlank(missingFields, row.getVehicleModel(), "vehicle.import.field.vehicleModel"); addIfBlank(missingFields, row.getVehicleColor(), "vehicle.import.field.vehicleColor"); addIfBlank(missingFields, row.getVehiclePictures(), "vehicle.import.field.vehiclePictures.tip"); addIfBlank(missingFields, row.getProcurementSource(), "vehicle.import.field.procurementSource"); if (!missingFields.isEmpty()) { int excelRowNumber = index + headRows + 1; String missing = String.join(", ", missingFields); throw new ServiceException("导入数据必填字段为空!", I18nKeyConstant.VEHICLE_IMPORT_FIELDS_REQUIRED, new Object[]{excelRowNumber, missing}); } } } private void addIfBlank(List missingFields, String value, String fieldNameKey) { if (StringUtils.isBlank(value)) { String fieldName = messageSource.getMessage(fieldNameKey, null, fieldNameKey, LocaleContextHolder.getLocale()); missingFields.add(fieldName); } } private void validateImportBusinessFields(List importList, int headRows) { List errors = new ArrayList<>(); Locale locale = LocaleContextHolder.getLocale(); for (int index = 0; index < importList.size(); index++) { XpVehicleImportExcel row = importList.get(index); int excelRowNumber = index + headRows + 1; String vehicleCode = trimToNull(row.getVehicleCode()); String vehicleVin = normalizeVin(row.getVehicleVin()); String plateNumber = trimToNull(row.getPlateNumber()); String vehicleColor = trimToNull(row.getVehicleColor()); row.setVehicleCode(vehicleCode); row.setVehicleVin(vehicleVin); row.setPlateNumber(plateNumber); row.setVehicleColor(vehicleColor); if (org.apache.commons.lang3.StringUtils.isNotBlank(vehicleCode) && !VEHICLE_CODE_PATTERN.matcher(vehicleCode).matches()) { errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_CODE_INVALID, new Object[]{excelRowNumber, vehicleCode}, I18nKeyConstant.VEHICLE_IMPORT_CODE_INVALID, locale)); } if (org.apache.commons.lang3.StringUtils.isNotBlank(vehicleVin) && !VEHICLE_VIN_PATTERN.matcher(vehicleVin).matches()) { errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_VIN_INVALID, new Object[]{excelRowNumber, vehicleVin}, I18nKeyConstant.VEHICLE_IMPORT_VIN_INVALID, locale)); } if (org.apache.commons.lang3.StringUtils.isNotBlank(plateNumber) && !Validator.isPlateNumber(plateNumber)) { errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_PLATE_INVALID, new Object[]{excelRowNumber, plateNumber}, I18nKeyConstant.VEHICLE_IMPORT_PLATE_INVALID, locale)); } if (org.apache.commons.lang3.StringUtils.isNotBlank(vehicleColor) && vehicleColor.length() > 3) { errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_COLOR_INVALID, new Object[]{excelRowNumber, vehicleColor}, I18nKeyConstant.VEHICLE_IMPORT_COLOR_INVALID, locale)); } } Set duplicatedCodes = findDuplicated(importList.stream() .map(XpVehicleImportExcel::getVehicleCode) .filter(org.apache.commons.lang3.StringUtils::isNotBlank) .collect(java.util.stream.Collectors.toList())); Set duplicatedVins = findDuplicated(importList.stream() .map(XpVehicleImportExcel::getVehicleVin) .filter(org.apache.commons.lang3.StringUtils::isNotBlank) .collect(java.util.stream.Collectors.toList())); Set duplicatedPlates = findDuplicated(importList.stream() .map(XpVehicleImportExcel::getPlateNumber) .filter(org.apache.commons.lang3.StringUtils::isNotBlank) .collect(java.util.stream.Collectors.toList())); for (int index = 0; index < importList.size(); index++) { XpVehicleImportExcel row = importList.get(index); int excelRowNumber = index + headRows + 1; if (duplicatedCodes.contains(row.getVehicleCode())) { errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_CODE_DUPLICATE, new Object[]{excelRowNumber, row.getVehicleCode()}, I18nKeyConstant.VEHICLE_IMPORT_CODE_DUPLICATE, locale)); } if (duplicatedVins.contains(row.getVehicleVin())) { errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_VIN_DUPLICATE, new Object[]{excelRowNumber, row.getVehicleVin()}, I18nKeyConstant.VEHICLE_IMPORT_VIN_DUPLICATE, locale)); } if (duplicatedPlates.contains(row.getPlateNumber())) { errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_PLATE_DUPLICATE, new Object[]{excelRowNumber, row.getPlateNumber()}, I18nKeyConstant.VEHICLE_IMPORT_PLATE_DUPLICATE, locale)); } } Set codeSet = importList.stream() .map(XpVehicleImportExcel::getVehicleCode) .filter(org.apache.commons.lang3.StringUtils::isNotBlank) .collect(java.util.stream.Collectors.toSet()); Set vinSet = importList.stream() .map(XpVehicleImportExcel::getVehicleVin) .filter(org.apache.commons.lang3.StringUtils::isNotBlank) .collect(java.util.stream.Collectors.toSet()); Set plateSet = importList.stream() .map(XpVehicleImportExcel::getPlateNumber) .filter(org.apache.commons.lang3.StringUtils::isNotBlank) .collect(java.util.stream.Collectors.toSet()); Set existsCodes = Collections.emptySet(); if (!codeSet.isEmpty()) { existsCodes = xpVehicleService.lambdaQuery() .select(XpVehicle::getVehicleCode) .in(XpVehicle::getVehicleCode, codeSet) .list() .stream() .map(XpVehicle::getVehicleCode) .filter(org.apache.commons.lang3.StringUtils::isNotBlank) .collect(java.util.stream.Collectors.toSet()); } Set existsVins = Collections.emptySet(); if (!vinSet.isEmpty()) { Set vinQueryValues = vinSet.stream() .flatMap(vin -> SensitiveDataCryptoUtil.queryValues(vin).stream()) .collect(java.util.stream.Collectors.toSet()); existsVins = xpVehicleService.lambdaQuery() .select(XpVehicle::getVehicleVin) .in(XpVehicle::getVehicleVin, buildVinQueryValues(vinSet)) .list() .stream() .map(XpVehicle::getVehicleVin) .map(SensitiveDataCryptoUtil::decryptIfPossible) .filter(org.apache.commons.lang3.StringUtils::isNotBlank) .collect(java.util.stream.Collectors.toSet()); } Set existsPlates = Collections.emptySet(); if (!plateSet.isEmpty()) { existsPlates = xpVehicleService.lambdaQuery() .select(XpVehicle::getPlateNumber) .in(XpVehicle::getPlateNumber, plateSet) .list() .stream() .map(XpVehicle::getPlateNumber) .filter(org.apache.commons.lang3.StringUtils::isNotBlank) .collect(java.util.stream.Collectors.toSet()); } for (int index = 0; index < importList.size(); index++) { XpVehicleImportExcel row = importList.get(index); int excelRowNumber = index + headRows + 1; if (existsCodes.contains(row.getVehicleCode())) { errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_CODE_EXISTS, new Object[]{excelRowNumber, row.getVehicleCode()}, I18nKeyConstant.VEHICLE_IMPORT_CODE_EXISTS, locale)); } if (existsVins.contains(row.getVehicleVin())) { errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_VIN_EXISTS, new Object[]{excelRowNumber, row.getVehicleVin()}, I18nKeyConstant.VEHICLE_IMPORT_VIN_EXISTS, locale)); } if (existsPlates.contains(row.getPlateNumber())) { errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_PLATE_EXISTS, new Object[]{excelRowNumber, row.getPlateNumber()}, I18nKeyConstant.VEHICLE_IMPORT_PLATE_EXISTS, locale)); } } if (!errors.isEmpty()) { throw new ServiceException("导入校验失败!", I18nKeyConstant.VEHICLE_IMPORT_VALIDATE_FAIL, new Object[]{String.join("\n", errors)}); } } private static String normalizeVin(String vin) { String trimmed = trimToNull(vin); return trimmed == null ? null : trimmed.toUpperCase(Locale.ROOT); } private static String trimToNull(String value) { if (value == null) { return null; } String trimmed = value.trim(); return trimmed.isEmpty() ? null : trimmed; } private static Set findDuplicated(List values) { if (values == null || values.isEmpty()) { return Collections.emptySet(); } Set seen = new HashSet<>(); Set duplicated = new HashSet<>(); for (String value : values) { if (!seen.add(value)) { duplicated.add(value); } } return duplicated; } private static List buildVinQueryValues(Iterable vins) { if (vins == null) { return Collections.emptyList(); } List values = new ArrayList<>(); for (String vin : vins) { values.addAll(SensitiveDataCryptoUtil.queryValues(vin)); } return values.stream().distinct().collect(Collectors.toList()); } private void uploadVehiclePicture(List importList) { for (XpVehicleImportExcel xpVehicleImportExcel : importList) { xpVehicleImportExcel.setVehiclePictures(getOssUrl(xpVehicleImportExcel.getVehiclePictures())); } } private String getOssUrl(String filePath) { try { // 获取到暂存的文件 File tmpFile = new File(filePath); FileInputStream fileInputStream = new FileInputStream(tmpFile); // TODO 上传oss JSONObject res = new JSONObject(); res.put("url", "https://we-park-life.oss-cn-beijing.aliyuncs.com/img/K4vqtLmVc6.png"); res.put("id", "1"); res.put("name", "测试.png"); return res.toJSONString(); } catch (Exception e) { log.error("在库车辆导入图片上传oss异常", e); } return null; } /** * 导出在库车辆列表 * * @param query 查询参数 */ @ApiOperation(value = "导出", tags = {"管理后台-车辆管理-在库车辆管理"}) @PostMapping("/api/v1/vehicle/exportExcel") @AdminOperationLog( businessType = AdminBusinessType.STOCK_VEHICLE_MANAGEMENT, operationType = AdminOperationType.EXPORT, content = "导出在库车辆", appendRequestData = true ) public void exportExcel(@RequestBody VehicleExportQuery query) { // 1. 查询导出数据 List dataList = xpVehicleService.vehicleExportList(query); // 2. 转换为导出对象 List exportList = BeanUtil.copyToList(dataList, XpVehicleExportExcel.class); for (XpVehicleExportExcel xpVehicleExportExcel : exportList) { if (StringUtils.isNotBlank(xpVehicleExportExcel.getVehiclePictures())) { JSONObject jsonObject = JSONObject.parseObject(xpVehicleExportExcel.getVehiclePictures()); xpVehicleExportExcel.setVehiclePictures(jsonObject.getString("url")); } } // 3. 生成Excel Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), XpVehicleExportExcel.class, exportList); // 4. 输出到响应流 HttpServletResponse response = WebUtils.response(); response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); ServletOutputStream outputStream = null; try { String fileName = URLEncoder.encode("在库车辆列表.xls", "utf-8"); response.setHeader("Content-disposition", "attachment;filename=" + fileName); outputStream = response.getOutputStream(); workbook.write(outputStream); } catch (IOException e) { log.error("导出在库车辆列表失败", e); } finally { try { if (outputStream != null) { outputStream.close(); } } catch (IOException e) { log.error("关闭输出流失败", e); } } } /** * 批量在库(运营→在库) * * @param idStr 车辆ID列表 * @return 操作结果 */ @ApiOperation(value = "批量在库", tags = {"管理后台-车辆管理-运营车辆管理"}) @ApiImplicitParam(name = "idStr", value = "车辆ID字符串列表,逗号分隔", required = true, dataTypeClass = String.class) @PostMapping("/api/v1/vehicle/batchStock") @AdminOperationLog( businessType = AdminBusinessType.OPERATION_VEHICLE_MANAGEMENT, operationType = AdminOperationType.EDIT, contentExpression = "'批量在库车辆,ids=' + #p0", appendRequestData = false ) public R batchStock(@RequestParam String idStr) { xpVehicleService.batchStock(idStr); return R.ok(); } /** * 车辆详情(含围栏名称) * * @param id 车辆ID * @return 车辆详情 */ @ApiOperation(value = "车辆详情", tags = {"管理后台-车辆管理-在库车辆管理"}) @GetMapping("/api/v1/vehicle/queryById") public R getVehicleDetail(@ApiParam(value = "车辆id", required = true) @RequestParam Long id) throws UnsupportedEncodingException { XpVehicle vehicleDetail = xpVehicleService.getVehicleDetail(id); if (vehicleDetail.getVehiclePictures() != null) { JSONObject jsonObject = JSONObject.parseObject(vehicleDetail.getVehiclePictures()); String downloadUrl = aliOssService.getDownloadUrl(jsonObject.getString("id"), jsonObject.getString("name"), true); jsonObject.put("url", downloadUrl); String jsonString = jsonObject.toJSONString(); vehicleDetail.setVehiclePictures(jsonString); } return R.ok(vehicleDetail); } /** * 删除在库车辆 * * @param id * @return */ @ApiOperation(value = "删除在库车辆", tags = {"管理后台-车辆管理-在库车辆管理"}) @DeleteMapping("/api/v1/vehicle/delete/{id}") @AdminOperationLog( businessType = AdminBusinessType.STOCK_VEHICLE_MANAGEMENT, operationType = AdminOperationType.DELETE, contentExpression = "'删除在库车辆,id=' + #p0", appendRequestData = false ) public R delete(@ApiParam(name = "id", value = "车辆ID") @PathVariable Long id) { XpFleetVehicle binding = vehicleService.lambdaQuery() .eq(XpFleetVehicle::getVehicleId, id) .last("limit 1") .one(); if (binding != null && binding.getFleetId() != null) { String fleetName = String.valueOf(binding.getFleetId()); try { XpFleet fleet = xpFleetService.getById(binding.getFleetId()); if (fleet != null && StringUtils.isNotBlank(fleet.getFleetName())) { fleetName = fleet.getFleetName(); } } catch (Exception e) { // ignore, fallback to fleetId } throw new ServiceException("当前车辆已关联车队:" + fleetName, I18nKeyConstant.VEHICLE_DELETE_HAS_BOUND_FLEET, new Object[]{fleetName}); } xpVehicleService.removeById(id); return R.ok(); } /** * 运营车辆管理分页列表 * * @param query 查询参数 * @return 分页结果 */ @ApiOperation(value = "运营车辆管理分页列表", tags = {"管理后台-车辆管理-运营车辆管理"}) @PostMapping("/api/v1/vehicle/operationPageList") public R> operationPageList(@RequestBody OperationVehiclePageQuery query) { return R.ok(xpVehicleService.operationPageList(query)); } /** * 导出在库车辆列表 * * @param query 查询参数 */ @ApiOperation(value = "运营车辆管理列表导出", tags = {"管理后台-车辆管理-运营车辆管理"}) @PostMapping("/api/v1/vehicle/operationExportExcel") @AdminOperationLog( businessType = AdminBusinessType.OPERATION_VEHICLE_MANAGEMENT, operationType = AdminOperationType.EXPORT, content = "导出运营车辆", appendRequestData = true ) public void exportExcel(@RequestBody OperationVehicleExportQuery query) { // 1. 查询导出数据 List dataList = xpVehicleService.operationVehicleExportList(query); // 2. 转换为导出对象 List exportList = BeanUtil.copyToList(dataList, XpVehicleExportExcel.class); for (XpVehicleExportExcel xpVehicleExportExcel : exportList) { if (StringUtils.isNotBlank(xpVehicleExportExcel.getVehiclePictures())) { String pictureJSONObject = aliOssDownloadUtil.getPictureJSONObject(xpVehicleExportExcel.getVehiclePictures()); if (StringUtils.isNotBlank(pictureJSONObject)) { JSONObject jsonObject = JSONObject.parseObject(pictureJSONObject); xpVehicleExportExcel.setVehiclePictures(jsonObject.containsKey("url") ? jsonObject.getString("url") : ""); } } if (Integer.valueOf(2).equals(xpVehicleExportExcel.getStatus()) && Integer.valueOf(1).equals(xpVehicleExportExcel.getOperatingStatus())) { xpVehicleExportExcel.setStatus(4); } } // 3. 生成Excel Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), XpVehicleExportExcel.class, exportList); // 4. 输出到响应流 HttpServletResponse response = WebUtils.response(); response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); ServletOutputStream outputStream = null; try { String fileName = URLEncoder.encode("运营车辆列表.xls", "utf-8"); response.setHeader("Content-disposition", "attachment;filename=" + fileName); outputStream = response.getOutputStream(); workbook.write(outputStream); } catch (IOException e) { log.error("导出在库车辆列表失败", e); } finally { try { if (outputStream != null) { outputStream.close(); } } catch (IOException e) { log.error("关闭输出流失败", e); } } } @ApiOperation(value = "通过车辆id查询年检分页列表", tags = {"管理后台-车辆管理-运营车辆管理"}) @PostMapping("/api/v1/vehicle/queryTaskRoutineByVehicleId") public R> queryTaskRoutineByVehicleId( @Valid @RequestBody XpVehicleAnnualRecordPageQuery query) throws UnsupportedEncodingException { return R.ok(xpVehicleAnnualRecordService.queryTaskRoutineByVehicleId(query)); } /** * 根据围栏id查询空闲车辆 * */ @GetMapping("/logan/inner/vehicle/listByFenceId") public R> listByFenceId(@RequestParam("fenceId") Long fenceId) { return R.ok(xpVehicleService.listByFenceId(fenceId)); } @GetMapping("/logan/inner/vehicle/listAllByFenceId") public R> listAllByFenceId(@RequestParam("fenceId") Long fenceId) { return R.ok(xpVehicleService.listAllByFenceId(fenceId)); } /** * 查询车辆位置信息 做筛选 * * @param dto * @return */ @PostMapping("/logan/inner/vehicle/queryVehiclePositionByVin") public R> queryVehiclePositionByVin(@RequestBody VehiclePositionByVinDto dto){ List list = xpVehicleService.list(new LambdaQueryWrapper().eq(XpVehicle::getState, Optional.ofNullable(dto.getStatus()).orElse(0)) .eq(XpVehicle::getOperatingStatus, 1).eq(XpVehicle::getElectronicId, dto.getFenceId()).in(XpVehicle::getVehicleVin, buildVinQueryValues(dto.getVinList()))); if (list.isEmpty()) { return R.ok(new ArrayList<>()); } else { List vehiclePositionVos = new ArrayList<>(list.size()); for (XpVehicle xpVehicle : list) { vehiclePositionVos.add(xpVehicle.getVehicleVin()); } return R.ok(vehiclePositionVos); } } /** * 查询车辆信息 * * @param vehicleVin * @return */ @GetMapping("/logan/inner/vehicle/findByVehicleVin") public R findByVehicleVin(@RequestParam("vehicleVin") String vehicleVin){ XpVehicle xpVehicle = xpVehicleService.getOne(new LambdaQueryWrapper().in(XpVehicle::getVehicleVin, SensitiveDataCryptoUtil.queryValues(vehicleVin))); // 获取车队 if (Objects.nonNull(xpVehicle)) { XpFleetVehicle xpFleetVehicle = vehicleService.getOne(new LambdaQueryWrapper().eq(XpFleetVehicle::getVehicleId, xpVehicle.getId())); if (Objects.nonNull(xpFleetVehicle)) { xpVehicle.setFleetId(xpFleetVehicle.getFleetId()); } } return R.ok(xpVehicle); } /** * 根据XPUID查询车辆信息 * * @param xpuid * @return */ @GetMapping("/logan/inner/vehicle/findByVehicleXpuId") public R findByVehicleXpuId(@RequestParam("xpuid") String xpuid) { XpVehicle xpVehicle = xpVehicleService.getOne(new LambdaQueryWrapper().eq(XpVehicle::getXpuId, xpuid)); return R.ok(xpVehicle); } /** * 修改车辆状态 * * @param updateVehicleStateDto * @return */ @PostMapping("/logan/inner/vehicle/updateVehicleStatus") public R updateVehicleStatus(@RequestBody UpdateVehicleStateDto updateVehicleStateDto) { if (StringUtils.isBlank(updateVehicleStateDto.getVin())) { return R.ok(); } xpVehicleService.update(new LambdaUpdateWrapper().in(XpVehicle::getVehicleVin, SensitiveDataCryptoUtil.queryValues(updateVehicleStateDto.getVin())).set(XpVehicle::getState, updateVehicleStateDto.getState())); return R.ok(); } /** * * 查询车辆所属车队ID * * @param vehicleId * @return */ @GetMapping("/logan/inner/vehicle/findFleetIdByVehicleId") public R findFleetIdByVehicleId(@RequestParam Long vehicleId) { XpFleetVehicle xpFleetVehicle = vehicleService.getOne(new LambdaQueryWrapper().eq(XpFleetVehicle::getVehicleId, vehicleId)); return R.ok(xpFleetVehicle.getFleetId()); } /** * 查询可改派的车辆列表 * * @return 车辆列表 */ @ApiOperation(value = "查询可改派的车辆列表", tags = {"管理后台-订单管理"}) @GetMapping("/api/v1/vehicle/availableList") @ApiImplicitParams({ @ApiImplicitParam(name = "orderId", value = "订单ID", required = true, dataTypeClass = Long.class), @ApiImplicitParam(name = "orderType", value = "订单类型(1=用户单,2=测试单)", required = true, dataTypeClass = Integer.class) }) public R> listAvailableVehicles(@RequestParam Long orderId, Integer orderType, String plateNumber, String fleetName) { List list = xpVehicleService.listAvailableVehicles(orderId, orderType,plateNumber,fleetName); return R.ok(list); } /** * 车辆控制消息回调通知 * 开关门、设定温度 * * @param callbackVo */ @PostMapping("/base/vehicles/command/callback") public void vehiclesCommandCallback(@RequestBody VehiclesCommandCallbackVo callbackVo) { log.info("车辆控制消息回调:{}", JSON.toJSONString(callbackVo)); if (null != callbackVo.getMsg_ref()) { //继续执行业务逻辑上的代码 } } /** * 车辆详情-车辆调度-接单、测试 */ @ApiOperation(value = "车辆详情", tags = {"运维APP-车辆管理"}) @PostMapping("/maintenance-app/api/v1/vehicle/vehicleDetail") public R vehicleDetail(@RequestParam Long id) { // 查询车辆 XpVehicle xpVehicle = xpVehicleService.getById(id); XpVehicleVO xpVehicleVO = new XpVehicleVO(); BeanUtils.copyProperties(xpVehicle, xpVehicleVO); // 查询运维规则 保养周期 XpTaskRule xpTaskRule = xpTaskRuleClient.queryDetail().getData(); if (Objects.isNull(xpTaskRule)) { return R.ok(xpVehicleVO); } Boolean maintenanceCycle = xpTaskRule.getMaintenanceCycle(); Integer maintenanceCycleTime = xpTaskRule.getMaintenanceCycleTime(); Integer maintenanceCycleTimeType = xpTaskRule.getMaintenanceCycleTimeType(); if (maintenanceCycle) { // 查询车辆上次保养记录 XpTask xpTask = xpTaskClient.queryInfoByVehicleId(id).getData(); if (Objects.nonNull(xpTask)) { // 存在保养记录 LocalDateTime handleTime = xpTask.getCreateTime(); switch (maintenanceCycleTimeType) { case 1: handleTime.plusDays(maintenanceCycleTime); break; default: handleTime.plusMonths(maintenanceCycleTime); break; } xpVehicleVO.setNextMaintenanceTime(handleTime.toLocalDate()); } else { // 没有保养记录 // 入库时间 LocalDateTime warehousingTime = xpVehicle.getWarehousingTime(); if (Objects.nonNull(warehousingTime)) { switch (maintenanceCycleTimeType) { case 1: warehousingTime.plusDays(maintenanceCycleTime); break; default: warehousingTime.plusMonths(maintenanceCycleTime); break; } xpVehicleVO.setNextMaintenanceTime(warehousingTime.toLocalDate()); } } } // 查询车辆的续航里程 VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(xpVehicleVO.getVehicleVin())), VehiclePosition.class); if (Objects.nonNull(vehiclePosition)) { xpVehicleVO.setMileageRange(vehiclePosition.getMileageRange()); xpVehicleVO.setCurrentElectricity(vehiclePosition.getCurrentElectricity()); } else { xpVehicleVO.setMileageRange(0F); xpVehicleVO.setCurrentElectricity(0); } if (StringUtils.isNotBlank(xpVehicleVO.getVehiclePictures())) { JSONObject jsonObject = JSONObject.parseObject(xpVehicleVO.getVehiclePictures()); if(jsonObject!=null){ String downloadUrl = null; try { downloadUrl= aliOssService.getDownloadUrl(jsonObject.getString("id"), jsonObject.getString("name"), true); } catch (Exception e) { e.printStackTrace(); } jsonObject.put("url", downloadUrl); xpVehicleVO.setVehiclePictures(jsonObject.toJSONString()); } } // 判断车辆维修状态 Integer maintenanceStatus = xpVehicleRepairClient.getMaintenanceStatus(xpVehicleVO.getId()).getData(); xpVehicleVO.setMaintenanceStatus(maintenanceStatus); return R.ok(xpVehicleVO); } /** * 车辆管理-地图 * * @return 车辆列表 */ @ApiOperation(value = "车辆管理-地图", tags = {"运维APP-车辆管理"}) @PostMapping("/maintenance-app/api/v1/vehicle/vehicleMap") public R> vehicleMap(@RequestBody VehicleMapQuery query) { Long operationsUserId = tokenService.getLoginOperationUser().getId(); query.setOperationsUserId(operationsUserId); return R.ok(xpVehicleService.pageList(query)); } /** * 根据车牌号查询车辆信息 * * @param plateNumber * @return */ @PostMapping("/logan/inner/vehicle/listByPlateNumber") R> listByPlateNumber(@RequestParam("plateNumber") String plateNumber) { return R.ok(xpVehicleService.listByPlateNumber(plateNumber)); } /** * 车辆详情-车门解锁/后备箱解锁 */ @ApiOperation(value = "车辆运维-车门解锁/后备箱解锁", tags = {"运维APP-车辆管理"}) @PostMapping("/maintenance-app/api/v1/vehicle/unlockByType") public R unlockByType(@RequestBody UnlockByTypeDTO dto) { // TODO 车门解锁、后备箱解锁 return R.ok(); } /** * 业务流程-车门解锁/后备箱解锁 */ @ApiOperation(value = "打开车门解锁/后备箱开关车门", tags = {"小程序-业务流程"}) @PostMapping("/api/v1/mini-program/order/vehicle/unlock") @XpClientCheck(checkToken = false, checkOtpToken = true) public R unlock(@RequestBody UnlockAppletDTO dto) { return R.ok(xpVehicleService.unlock(dto)); } /** * 业务流程-打开设置温度 */ @ApiOperation(value = "打开设置温度", tags = {"小程序-业务流程"}) @PostMapping("/api/v1/mini-program/order/ac/set-temperature") @XpClientCheck(checkToken = false, checkOtpToken = true) public R setTemperature(@RequestBody SetTemperatureDTO dto) { return R.ok(xpVehicleService.setTemperature(dto)); } /** * 业务流程-通过订单id查询当前车辆温度 */ @ApiOperation(value = "通过订单id查询当前车辆温度", tags = {"小程序-业务流程"}) @GetMapping("/api/v1/mini-program/order/queryTemperatureByOrderId") @XpClientCheck(checkToken = false, checkOtpToken = true) public R queryTemperatureByOrderId(@RequestParam Long orderId, HttpServletRequest request) { String uid = request.getHeader("XP-Uid"); // VehicleTemperatureDto vehicleTemperatureDto = xpVehicleService.queryTemperatureByOrderId(orderId, uid); // VehicleStateVo hvacInnerTemp = vehicleTemperatureDto.getStateMap().get("hvacInnerTemp"); // if(null == hvacInnerTemp){ // return R.fail("查询车内温度失败"); // } // return R.ok(hvacInnerTemp.getValue()); return R.ok("35.0"); } @ApiOperation(value = "通过订单id查询当前车辆车门状态", tags = {"小程序-业务流程"}) @GetMapping("/api/v1/mini-program/order/queryVehicleTrunkOpenStatus") public R queryVehicleTrunkOpenStatus(@RequestParam Long orderId, HttpServletRequest request) { String uid = request.getHeader("XP-Uid"); VehicleTemperatureDto vehicleTemperatureDto = xpVehicleService.queryTemperatureByOrderId(orderId,uid); return R.ok(vehicleTemperatureDto); } /** * 获取车辆不分页列表 * * @param query * @return */ @ApiOperation(value = "获取车辆分页列表", tags = {"管理后台-车队管理"}) @PostMapping("/api/v1/vehicle/page-list") public R> getVehiclePageList(@RequestBody VehicleSelectPageQuery query) { return R.ok(xpVehicleService.getVehiclePageList(query)); } /** * 临时用于修改车辆接单状态 * * @param updateVehicleStateDto * @return */ public R testUpdateVehicleStatus(@RequestBody UpdateVehicleStateDto updateVehicleStateDto) { if (StringUtils.isBlank(updateVehicleStateDto.getVin())) { return R.ok(); } xpVehicleService.update(new LambdaUpdateWrapper().in(XpVehicle::getVehicleVin, SensitiveDataCryptoUtil.queryValues(updateVehicleStateDto.getVin())).set(XpVehicle::getState, updateVehicleStateDto.getState())); return R.ok(); } /** * 删除mongodb中的文档 * * @param collectionName * @return */ @PostMapping("/base/vi/removeMongodb") public R removeMongodb(String collectionName) { MongoCollection collection = mongoTemplate.getCollection(collectionName); collection.drop(); return R.ok(); } /** * 获取车辆后备箱开关状态 * * @param uid * @param id * @return */ @PostMapping("/logan/inner/vi/vehicle/getVehicleTrunkOpenStatus") public R getVehicleTrunkOpenStatus(@RequestParam("uid") Long uid, @RequestParam("id") Long id) { Boolean vehicleTrunkOpenStatus = xpVehicleService.getVehicleTrunkOpenStatus(String.valueOf(uid), id); return R.ok(vehicleTrunkOpenStatus); } /** * 分页获取运营车辆列表 * @param dto * @return */ @PostMapping("/logan/inner/vehicle/getServiceVehicleList") public R> getServiceVehicleList(@RequestBody ServiceVehicleListDto dto){ return R.ok(xpVehicleService.getServiceVehicleList(dto)); } /** * 业务流程-车门解锁/后备箱解锁 */ @ApiOperation(value = "打开车门解锁/后备箱开关车门", tags = {"运维APP-车辆管理"}) @PostMapping("/maintenance-app/api/v1/vehicle/unlockDoor") public R unlockDoor(@RequestBody UnlockAppDTO dto) { XpOperationsUser xpOperationsUser = tokenService.getLoginOperationUser().getXpOperationsUser(); return R.ok(xpVehicleService.unlockDoor(dto, xpOperationsUser)); } @ApiOperation(value = "通过订单id查询当前车辆空调状态", tags = {"小程序-业务流程"}) @GetMapping("/api/v1/mini-program/order/queryVehicleAirConditionerSwitch") @XpClientCheck(checkToken = false, checkOtpToken = true) public R queryVehicleAirConditionerSwitch(@RequestParam Long orderId, HttpServletRequest request) { String uid = request.getHeader("XP-Uid"); Boolean hasKey = xpVehicleService.queryVehicleAirConditionerSwitch(orderId, uid); return R.ok(hasKey); } /** * 业务流程-车门解锁/后备箱解锁 */ @ApiOperation(value = "打开/关闭车内空调", tags = {"小程序-业务流程"}) @PostMapping("/api/v1/mini-program/order/airConditionerUnlock") @XpClientCheck(checkToken = false, checkOtpToken = true) public R airConditionerUnlock(@RequestBody AirConditionerUnlockDTO dto, HttpServletRequest request) { String uid = request.getHeader("XP-Uid"); return R.ok(xpVehicleService.airConditionerUnlock(dto, uid)); } /** * 根据订单id查询四门三盖状态 * @param orderId * @return */ @GetMapping("/logan/inner/vehicle/getVehicleDoorStatus/{orderId}") public R getVehicleDoorStatus(@PathVariable Long orderId){ VehicleDoorStatusVo vehicleDoorStatus = xpVehicleService.getVehicleDoorStatus(orderId); return R.ok(vehicleDoorStatus); } /** * 周围车辆点位查询 */ @ApiOperation(value = "周围车辆点位查询", notes = "", tags = {"小程序-周围车辆点位查询"}) @GetMapping("/api/v1/mini-program/vehicle/nearby") @XpClientCheck(checkToken = false, checkOtpToken = false) public R> carAround(@RequestParam double lon, @RequestParam double lat) { Point point = new Point(lon, lat); Distance distance = new Distance(3, Metrics.KILOMETERS); Criteria criteria = Criteria.where("point") .nearSphere(point) .maxDistance(distance.getNormalizedValue()); Query query = new Query(criteria); List vehiclePositions = mongoTemplate.find(query, VehiclePositionVo.class); if (CollectionUtils.isEmpty(vehiclePositions)) { return R.ok(new ArrayList<>()); } List xpVehicleList = vehiclePositions.stream() .sorted(Comparator.comparingDouble(VehiclePositionVo::getDistance)) .map(vp -> { XpVehicle xpVehicle = xpVehicleService.getOne(new LambdaQueryWrapper().in(XpVehicle::getVehicleVin, SensitiveDataCryptoUtil.queryValues(vp.getVin()))); if (xpVehicle == null) { return null; } XpVehicleVO vo = new XpVehicleVO(); BeanUtils.copyProperties(xpVehicle, vo); vo.setDirection(vp.getCourse()); return vo; }) .filter(Objects::nonNull) .filter(v -> v.getState() != null && v.getState() == 0) .limit(5) .collect(Collectors.toList()); return R.ok(xpVehicleList); } @ApiOperation(value = "新增车辆", tags = {"流量接入-基础信息"}) @PostMapping("/drainage/v1/vehicle/addVehicle") @ApiImplicitParams({ @ApiImplicitParam(value = "token", name = "token", required = true, paramType = "header") }) public R addOfficialPartnerVehicle(@Valid @RequestBody VehicleAddDTO dto) { XpOfficialPartner officialPartner = tokenService.getOfficialPartner(); XpVehicle vehicle = xpVehicleService.addVehicle(dto, officialPartner.getId()); return R.ok(vehicle); } @PostMapping("/drainage/v1/vehicle/editVehicle") @ApiOperation(value = "编辑车辆", tags = {"流量接入-基础信息"}) @ApiImplicitParams({ @ApiImplicitParam(value = "token", name = "token", required = true, paramType = "header") }) public R editOfficialPartnerVehicle(@Valid @RequestBody VehicleEditDTO dto) { XpOfficialPartner officialPartner = tokenService.getOfficialPartner(); xpVehicleService.editVehicle(dto, officialPartner.getId()); return R.ok(); } /** * 上报车辆实时位置信息 * @param dto * @return */ @PostMapping("/drainage/v1/vehicle/reportVehicleLocation") @ApiOperation(value = "上报车辆实时位置信息", notes = "流量接入-基础信息") @ApiImplicitParams({ @ApiImplicitParam(value = "token", name = "token", required = true, paramType = "header") }) public R reportVehicleLocation(@RequestBody VehiclePositionDto dto){ XpOfficialPartner officialPartner = tokenService.getOfficialPartner(); xpVehicleService.reportVehicleLocation(dto, officialPartner.getId()); return R.ok(); } /** * 根据车牌号查询数据 * @param plateNumber * @param officialPartnerId * @return */ @PostMapping("/logan/inner/vehicle/getVehicleByPlateNumber") public R getVehicleByPlateNumber(@RequestParam("plateNumber") String plateNumber, @RequestParam("officialPartnerId") Integer officialPartnerId){ XpVehicle vehicle = xpVehicleService.getOne(new LambdaQueryWrapper().eq(XpVehicle::getPlateNumber, plateNumber).eq(XpVehicle::getOfficialPartnerId, officialPartnerId)); return R.ok(vehicle); } private List buildVinQueryValues(Set vinList) { if (vinList == null || vinList.isEmpty()) { return java.util.Collections.emptyList(); } return vinList.stream() .flatMap(vin -> SensitiveDataCryptoUtil.queryValues(vin).stream()) .distinct() .collect(Collectors.toList()); } }