puzhibing
2026-03-05 de5f07fe1acd8d462f9c54d7a04c5c47a87264d1
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -16,12 +16,12 @@
import com.xiaopeng.api.feignClient.maintenance.XpTaskClient;
import com.xiaopeng.api.feignClient.maintenance.XpTaskRuleClient;
import com.xiaopeng.api.feignClient.maintenance.XpVehiclePreparationClient;
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.api.model.maintenance.XpTask;
import com.xiaopeng.api.model.maintenance.XpTaskRule;
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.vehicleConvoy.VehiclesCommandCallbackVo;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
@@ -40,9 +40,15 @@
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.springframework.beans.BeanUtils;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.i18n.LocaleContextHolder;
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;
@@ -51,10 +57,11 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@@ -81,6 +88,8 @@
    private final XpChargingRecordClient xpChargingRecordClient;
    private final XpTaskClient xpTaskClient;
    private final XpTaskRuleClient xpTaskRuleClient;
    private final MongoTemplate mongoTemplate;
    private final MessageSource messageSource;
    /**
     * 根据ID列表批量查询车辆信息(内部接口)
     *
@@ -114,9 +123,13 @@
    @GetMapping("/inner/vehicle/queryById")
    public R<XpVehicle> queryById(@RequestParam Long id) {
        XpVehicle xpVehicle = xpVehicleService.getById(id);
        // 获取车队
        XpFleetVehicle xpFleetVehicle = vehicleService.getOne(new LambdaQueryWrapper<XpFleetVehicle>().eq(XpFleetVehicle::getVehicleId, xpVehicle.getId()));
        xpVehicle.setFleetId(xpFleetVehicle.getFleetId());
        if (Objects.nonNull(xpVehicle)) {
            // 获取车队
            XpFleetVehicle xpFleetVehicle = vehicleService.getOne(new LambdaQueryWrapper<XpFleetVehicle>().eq(XpFleetVehicle::getVehicleId, xpVehicle.getId()));
            if (Objects.nonNull(xpFleetVehicle)) {
                xpVehicle.setFleetId(xpFleetVehicle.getFleetId());
            }
        }
        return R.ok(xpVehicle);
    }
    /**
@@ -208,7 +221,38 @@
    public R<PageInfo<XpVehicle>> vehiclePageList(@RequestBody VehiclePageQuery query) {
        return R.ok(xpVehicleService.vehiclePageList(query));
    }
    /**
     * 导出在库车辆列表
     *
     */
    @ApiOperation(value = "下载导入模板", tags = {"管理后台-车辆管理-在库车辆管理"})
    @PostMapping("/api/v1/vehicle/importTemplate")
    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);
            }
        }
    }
    /**
     * 导入车辆
     *
@@ -227,6 +271,9 @@
            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) {
@@ -240,10 +287,60 @@
        if (CollectionUtils.isEmpty(importList)) {
            throw new ServiceException("导入数据为空!", I18nKeyConstant.VEHICLE_IMPORT_DATA_EMPTY);
        }
        validateImportRequiredFields(importList, params.getHeadRows());
        uploadVehiclePicture(importList);
        xpVehicleService.importVehicles(importList);
        return R.ok();
    }
    private void validateImportRequiredFields(List<XpVehicleImportExcel> importList, int headRows) {
        for (int index = 0; index < importList.size(); index++) {
            XpVehicleImportExcel row = importList.get(index);
            List<String> 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");
            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<String> missingFields, String value, String fieldNameKey) {
        if (StringUtils.isBlank(value)) {
            String fieldName = messageSource.getMessage(fieldNameKey, null, fieldNameKey, LocaleContextHolder.getLocale());
            missingFields.add(fieldName);
        }
    }
    private void uploadVehiclePicture(List<XpVehicleImportExcel> 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;
    }
    /**
     * 导出在库车辆列表
     *
@@ -320,7 +417,7 @@
     * @param id
     * @return
     */
    @ApiOperation(value = "删除在库车辆")
    @ApiOperation(value = "删除在库车辆",tags = {"管理后台-车辆管理-在库车辆管理"})
    @DeleteMapping("/api/v1/vehicle/delete/{id}")
    public R<?> delete(@ApiParam(name = "id", value = "车辆ID") @PathVariable Long id) {
        xpVehicleService.removeById(id);
@@ -355,7 +452,8 @@
        for (XpVehicleExportExcel xpVehicleExportExcel : exportList) {
            if (StringUtils.isNotBlank(xpVehicleExportExcel.getVehiclePictures())) {
                JSONObject jsonObject = JSONObject.parseObject(xpVehicleExportExcel.getVehiclePictures());
                xpVehicleExportExcel.setVehiclePictures(jsonObject.getString("url"));
                // xpVehicleExportExcel.setVehiclePictures(jsonObject.getString("url"));
                xpVehicleExportExcel.setVehiclePictures(null);
            }
        }
        // 3. 生成Excel
@@ -407,7 +505,7 @@
     */
    @PostMapping("/inner/vehicle/queryVehiclePositionByVin")
    public R<List<String>> queryVehiclePositionByVin(@RequestBody VehiclePositionByVinDto dto){
        List<XpVehicle> list = xpVehicleService.list(new LambdaQueryWrapper<XpVehicle>().eq(XpVehicle::getState, 0).eq(XpVehicle::getStatus, 1).eq(XpVehicle::getElectronicId, dto.getFenceId()).in(XpVehicle::getVehicleVin,
        List<XpVehicle> list = xpVehicleService.list(new LambdaQueryWrapper<XpVehicle>().eq(XpVehicle::getState, 0).eq(XpVehicle::getOperatingStatus, 1).eq(XpVehicle::getElectronicId, dto.getFenceId()).in(XpVehicle::getVehicleVin,
                dto.getVinList()));
        if(list.isEmpty()){
            return R.ok(new ArrayList<>());
@@ -461,14 +559,27 @@
    }
    /**
     *
     * 查询车辆所属车队ID
     * @param vehicleId
     * @return
     */
    @GetMapping("/inner/vehicle/findFleetIdByVehicleId")
    public R<Long> findFleetIdByVehicleId(@RequestParam Long vehicleId){
        XpFleetVehicle xpFleetVehicle = vehicleService.getOne(new LambdaQueryWrapper<XpFleetVehicle>().eq(XpFleetVehicle::getVehicleId, vehicleId));
        return R.ok(xpFleetVehicle.getFleetId());
    }
    /**
     * 查询可改派的车辆列表
     *
     * @return 车辆列表
     */
    @ApiOperation(value = "查询可改派的车辆列表", tags = {"管理后台-订单管理"})
    @GetMapping("/api/v1/vehicle/availableList")
    public R<List<XpVehicle>> listAvailableVehicles() {
        List<XpVehicle> list = xpVehicleService.listAvailableVehicles();
    @ApiImplicitParam(name = "orderId", value = "订单ID", required = true, dataTypeClass = Long.class)
    public R<List<XpVehicle>> listAvailableVehicles(@RequestParam Long orderId) {
        List<XpVehicle> list = xpVehicleService.listAvailableVehicles(orderId);
        return R.ok(list);
    }
@@ -535,6 +646,15 @@
                }
            }
        }
        // 查询车辆的续航里程
        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);
        }
        return R.ok(xpVehicleVO);
    }
@@ -544,7 +664,7 @@
     */
    @ApiOperation(value = "车辆管理-地图", tags = {"运维APP-车辆管理"})
    @PostMapping("/api/v1/vehicle/vehicleMap")
    public R<PageInfo<XpVehicle>> vehicleMap(@RequestBody VehicleMapQuery query) {
    public R<PageInfo<XpVehicleVO>> vehicleMap(@RequestBody VehicleMapQuery query) {
        return R.ok(xpVehicleService.pageList(query));
    }
    /**