“mitao”
2026-03-05 c3d281009167814da53ebdc44c7b65f6fc9db8dc
fix:管理后台车辆管理模块
29个文件已修改
269 ■■■■■ 已修改文件
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/mapper/XpOperationsUserMapper.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/SysGroupServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/XpOperationsUserServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/resources/mapper/account/XpOperationsUserMapper.xml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/operation/XpPointEnableDisableDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/operation/XpPoint.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/account/XpOperationsUserWithRoleVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-gateway-boot/src/main/java/com/xiaopeng/gateway/filter/CacheRequestFilter.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-gateway-boot/src/main/java/com/xiaopeng/gateway/filter/RequestParamGlobalFilter.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpSchedulingDetailController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpPointManagementController.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpOpenCityServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpAppVersionServiceImpl.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpCruisePlanServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpOpenCityServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpRoutePlanServiceImpl.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/constant/I18nKeyConstant.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpFleetController.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/exportExcel/XpVehicleExportExcel.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/importExcel/XpVehicleImportExcel.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/FleetVehicleVO.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpFleetServiceImpl.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleAnnualRecordServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages.properties 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_en_US.properties 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_zh_CN.properties 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/mapper/XpOperationsUserMapper.java
@@ -24,11 +24,11 @@
public interface XpOperationsUserMapper extends BaseMapper<XpOperationsUser> {
    /**
     * 查询带角色信息的运维用户分页列表
     * @param xpOperationsUserWithRoleVOPage
     * @param pageInfo
     * @param query
     * @return
     */
    Page<XpOperationsUserWithRoleVO> selectUserPageByConditions(Page<XpOperationsUserWithRoleVO> xpOperationsUserWithRoleVOPage, @Param("query") XpOperationsUserPageQuery query);
    Page<XpOperationsUserWithRoleVO> selectUserPageByConditions(PageInfo<XpOperationsUserWithRoleVO> pageInfo, @Param("query") XpOperationsUserPageQuery query);
    /**
     * 分页查询
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/SysGroupServiceImpl.java
@@ -69,7 +69,6 @@
        // 创建权限组实体
        SysGroup group = BeanUtil.copyProperties(dto, SysGroup.class);
        group.setCreateBy(SecurityUtils.getLoginUser().getSysUser().getNickName());
        // 保存权限组
        this.save(group);
@@ -100,7 +99,6 @@
        existGroup.setGroupName(dto.getGroupName());
        existGroup.setRemarks(dto.getRemarks());
        existGroup.setGroupType(dto.getGroupType());
        existGroup.setUpdateBy(SecurityUtils.getLoginUser().getSysUser().getNickName());
        this.updateById(existGroup);
        // 删除旧的菜单关联
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/XpOperationsUserServiceImpl.java
@@ -91,10 +91,9 @@
    @Override
    public PageInfo<XpOperationsUserWithRoleVO> selectUserPageByConditions(XpOperationsUserPageQuery query) {
        Page<XpOperationsUserWithRoleVO> page = baseMapper.selectUserPageByConditions(new Page<>(query.getPageNum(), query.getPageSize()), query);
        PageInfo<XpOperationsUserWithRoleVO> pageInfo = new PageInfo<>();
        PageInfo<XpOperationsUserWithRoleVO> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        Page<XpOperationsUserWithRoleVO> page = baseMapper.selectUserPageByConditions(pageInfo, query);
        pageInfo.setRecords(page.getRecords());
        pageInfo.setTotal(page.getTotal());
        return pageInfo;
    }
xp-robotaxi-account-boot/src/main/resources/mapper/account/XpOperationsUserMapper.xml
@@ -11,7 +11,7 @@
        SELECT
        u.*,
        r.id AS roleId,
        r.role_name AS roleName
        GROUP_CONCAT(r.role_name) AS roleName
        FROM xp_operations_user u
        LEFT JOIN xp_operations_role ur ON u.id = ur.operations_user_id
        LEFT JOIN sys_role r ON ur.role_id = r.id AND r.is_deleted = 0
@@ -26,6 +26,8 @@
            AND r.id = #{query.roleId}
        </if>
     </where>
        GROUP BY u.id
        ORDER BY u.create_time DESC
    </select>
    <select id="pageList" resultType="com.xiaopeng.api.vo.account.XpOperationsUserVO">
        SELECT xou.id, xou.nick_name, xou.unique_number, xou.avatar, xou.password, xou.phone_number, xou.remarks, xou.status,
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/operation/XpPointEnableDisableDto.java
@@ -12,4 +12,7 @@
    @ApiModelProperty(value = "运营状态 1=待启用 2=启用中 3=已停用 4=已失效")
    private Integer operationStatus;
    @ApiModelProperty(value = "验证状态 1=待验证、2=验证中、3=已验证 4验证失败 5待生效")
    private Integer verificationStatus;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/operation/XpPoint.java
@@ -55,7 +55,7 @@
    @ApiModelProperty(value = "点位类型 1=运营点位 2=测试点位")
    private Integer pointType;
    @ApiModelProperty(value = "验证状态 1=待验证、2=验证中、3=已验证 4验证失败")
    @ApiModelProperty(value = "验证状态 1=待验证、2=验证中、3=已验证 4验证失败 5待生效")
    private Integer verificationStatus;
    @ApiModelProperty(value = "限停时长")
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/account/XpOperationsUserWithRoleVO.java
@@ -21,4 +21,7 @@
    @ApiModelProperty(value = "关联角色")
    private List<SysRole> roles;
    @ApiModelProperty(value = "关联角色名称, 逗号拼接")
    private String roleName;
}
xp-robotaxi-gateway-boot/src/main/java/com/xiaopeng/gateway/filter/CacheRequestFilter.java
@@ -8,6 +8,7 @@
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
@@ -52,6 +53,11 @@
            if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE) {
                return chain.filter(exchange);
            }
            String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
            // multipart(二进制)请求不缓存body,避免大文件带来内存/性能风险
            if (contentType != null && contentType.toLowerCase().startsWith("multipart/")) {
                return chain.filter(exchange);
            }
            return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, (serverHttpRequest) -> {
                if (serverHttpRequest == exchange.getRequest()) {
                    return chain.filter(exchange);
@@ -72,4 +78,4 @@
            this.order = order;
        }
    }
}
}
xp-robotaxi-gateway-boot/src/main/java/com/xiaopeng/gateway/filter/RequestParamGlobalFilter.java
@@ -10,14 +10,13 @@
import org.springframework.core.io.buffer.*;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
 * 跨站脚本过滤器
@@ -44,10 +43,30 @@
        if(method != HttpMethod.POST){
            return chain.filter(exchange);
        }
        String contentType = request.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
        // multipart(二进制)请求禁止读取/重写body,否则会导致文件内容损坏
        if (StringUtils.isNotEmpty(contentType) && StringUtils.startsWithIgnoreCase(contentType, "multipart/")) {
            return chain.filter(exchange);
        }
        // 非JSON请求不处理,避免误伤其他content-type
        if (!isJsonContentType(contentType)) {
            return chain.filter(exchange);
        }
        ServerHttpRequestDecorator httpRequestDecorator = requestDecorator(exchange);
        return chain.filter(exchange.mutate().request(httpRequestDecorator).build());
    }
    private boolean isJsonContentType(String contentType) {
        if (StringUtils.isEmpty(contentType)) {
            return false;
        }
        if (StringUtils.startsWithIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE)) {
            return true;
        }
        // 兼容 application/*+json
        return StringUtils.containsIgnoreCase(contentType, "+json");
    }
    
    private ServerHttpRequestDecorator requestDecorator(ServerWebExchange exchange) {
        ServerHttpRequestDecorator serverHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
@@ -60,12 +79,10 @@
                    byte[] content = new byte[join.readableByteCount()];
                    join.read(content);
                    DataBufferUtils.release(join);
                    String bodyStr = new String(content, StandardCharsets.UTF_8);
                    // 转成字节
                    byte[] bytes = bodyStr.getBytes();
                    NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
                    DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
                    buffer.write(bytes);
                    DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(content.length);
                    buffer.write(content);
                    return buffer;
                });
            }
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpSchedulingDetailController.java
@@ -61,7 +61,7 @@
    @ApiOperation(value = "排班考勤-排班计划管理-保存排班数据", response = XpErrorType.class, tags = "管理后台-运维执行")
    @PostMapping("/api/v1/scheduling/saveSchedulingDetail")
    public R<?> saveSchedulingDetail(@RequestBody SaveSchedulingVo vo){
        SysUser sysUser = SecurityUtils.getLoginUser().getSysUser();
        SysUser sysUser = tokenService.getLoginUser().getSysUser();
        return xpSchedulingDetailService.saveSchedulingDetail(vo, sysUser.getId());
    }
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpPointManagementController.java
@@ -144,6 +144,7 @@
    public R enableDisable(@RequestBody XpPointEnableDisableDto dto) {
        XpPoint xpPoint = xpPointService.getById(dto.getId());
        xpPoint.setOperationStatus(dto.getOperationStatus());
        xpPoint.setVerificationStatus(dto.getVerificationStatus());
        xpPoint.setElectronicAreaId(null);
        xpPointService.updateById(xpPoint);
        return R.ok();
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpOpenCityServiceImpl.java
@@ -105,7 +105,6 @@
    public void add(OpenCityAddDTO dto) {
        checkIfExist(dto, null);
        XpOpenCity xpOpenCity = BeanUtil.copyProperties(dto, XpOpenCity.class);
        xpOpenCity.setCreateBy(SecurityUtils.getLoginUser().getSysUser().getNickName());
        save(xpOpenCity);
    }
@@ -129,7 +128,6 @@
    public void edit(OpenCityEditDTO dto) {
        checkIfExist(dto, dto.getId());
        XpOpenCity xpOpenCity = BeanUtil.copyProperties(dto, XpOpenCity.class);
        xpOpenCity.setUpdateBy(SecurityUtils.getLoginUser().getSysUser().getNickName());
        updateById(xpOpenCity);
    }
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpAppVersionServiceImpl.java
@@ -91,7 +91,6 @@
        if (Objects.nonNull(latestVersion)) {
            latestVersionCode = latestVersion.getVersionCode();
        }
        appVersion.setCreateBy(SecurityUtils.getLoginUser().getSysUser().getNickName());
        appVersion.setVersionCode(latestVersionCode + 1); // 版本号自增
        save(appVersion);
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpCruisePlanServiceImpl.java
@@ -88,7 +88,6 @@
        validateVehicleBind(vehicleIdSet, null);
        XpCruisePlan plan = new XpCruisePlan();
        plan.setPlanName(dto.getPlanName());
        plan.setCreateBy(SecurityUtils.getLoginUser().getSysUser().getNickName());
        boolean planSaved = save(plan);
        if (!planSaved || plan.getId() == null) {
            throw new ServiceException("新增巡游计划失败", I18nKeyConstant.CRUISE_PLAN_ADD_FAIL);
@@ -120,7 +119,6 @@
        Set<Long> vehicleIdSet = validateVehicleList(dto.getVehicleIdList());
        validateVehicleBind(vehicleIdSet, dto.getId());
        plan.setPlanName(dto.getPlanName());
        plan.setUpdateBy(SecurityUtils.getLoginUser().getSysUser().getNickName());
        boolean planUpdated = updateById(plan);
        if (!planUpdated) {
            throw new ServiceException("编辑巡游计划失败", I18nKeyConstant.CRUISE_PLAN_EDIT_FAIL);
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpOpenCityServiceImpl.java
@@ -105,7 +105,6 @@
    public void add(OpenCityAddDTO dto) {
        checkIfExist(dto, null);
        XpOpenCity xpOpenCity = BeanUtil.copyProperties(dto, XpOpenCity.class);
        xpOpenCity.setCreateBy(SecurityUtils.getLoginUser().getSysUser().getNickName());
        save(xpOpenCity);
    }
@@ -129,7 +128,6 @@
    public void edit(OpenCityEditDTO dto) {
        checkIfExist(dto, dto.getId());
        XpOpenCity xpOpenCity = BeanUtil.copyProperties(dto, XpOpenCity.class);
        xpOpenCity.setUpdateBy(SecurityUtils.getLoginUser().getSysUser().getNickName());
        updateById(xpOpenCity);
    }
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpRoutePlanServiceImpl.java
@@ -64,7 +64,6 @@
        routePlan.setCityCode(openCity.getCityCode());
        routePlan.setDistrictName(openCity.getDistrictName());
        routePlan.setDistrictCode(openCity.getDistrictCode());
        routePlan.setCreateBy(SecurityUtils.getLoginUser().getSysUser().getNickName());
        // 5. 保存到数据库
        this.save(routePlan);
    }
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -444,9 +444,9 @@
                throw new ServiceException("当前订单状态不允许取消,只有待接单、待出发、接驾中状态的订单可以取消", I18nKeyConstant.ORDER_CANCEL_FAILED);
            }
        } else {
            // 关闭订单:只有待付款(8)、待评价(9)状态可以关闭
            if (currentStatus != 7 && currentStatus != 8 && currentStatus != 9) {
                throw new ServiceException("当前订单状态不允许关闭,只有待付款、待评价状态的订单可以关闭", I18nKeyConstant.ORDER_CLOSE_FAILED);
            // 关闭订单:0=已取消,1=待接单,2=待出发,3=接驾中,4=待客上车,5=乘客已上车,6=服务中 状态可以关闭
            if (currentStatus == 7 || currentStatus == 8 || currentStatus == 9 || currentStatus == 10) {
                throw new ServiceException("当前订单状态不允许关闭", I18nKeyConstant.ORDER_CLOSE_FAILED);
            }
        }
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/constant/I18nKeyConstant.java
@@ -14,6 +14,10 @@
    // ========== 车辆导入相关 ==========
    String VEHICLE_IMPORT_FAILED = "vehicle.import.failed";
    String VEHICLE_IMPORT_DATA_EMPTY = "vehicle.import.data.empty";
    String VEHICLE_IMPORT_FIELDS_REQUIRED = "vehicle.import.fields.required";
    // ========== 车队删除相关 ==========
    String FLEET_DELETE_HAS_BOUND_VEHICLE_OR_USER = "fleet.delete.has.bound.vehicle.or.user";
    // ========== 车辆运营相关 ==========
    String VEHICLE_OPERATION_INFO_INCOMPLETE = "vehicle.operation.info.incomplete";
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpFleetController.java
@@ -198,7 +198,6 @@
    /**
     * 根据ID列表批量查询车队信息(内部接口)
     *
     * @param ids 车队ID列表
     * @return 车队列表
     */
    @ApiOperation(value = "获取车队不分页列表", tags = {"管理后台-车队管理"})
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -16,7 +16,6 @@
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;
@@ -41,9 +40,12 @@
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;
@@ -51,15 +53,15 @@
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.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;
@@ -87,6 +89,7 @@
    private final XpTaskClient xpTaskClient;
    private final XpTaskRuleClient xpTaskRuleClient;
    private final MongoTemplate mongoTemplate;
    private final MessageSource messageSource;
    /**
     * 根据ID列表批量查询车辆信息(内部接口)
     *
@@ -120,10 +123,12 @@
    @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()));
        if (Objects.nonNull(xpFleetVehicle)) {
            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);
    }
@@ -224,7 +229,7 @@
    @PostMapping("/api/v1/vehicle/importTemplate")
    public void importTemplate() {
        // 1. 查询导出数据
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), XpVehicleExportExcel.class, new ArrayList<>());
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), XpVehicleImportExcel.class, new ArrayList<>());
        // 4. 输出到响应流
        HttpServletResponse response = WebUtils.response();
@@ -232,7 +237,7 @@
        response.setCharacterEncoding("utf-8");
        ServletOutputStream outputStream = null;
        try {
            String fileName = URLEncoder.encode("在库车辆导入模板.xls", "utf-8");
            String fileName = URLEncoder.encode("在库车辆导入模板.xlsx", "utf-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName);
            outputStream = response.getOutputStream();
            workbook.write(outputStream);
@@ -266,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) {
@@ -279,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;
    }
    /**
     * 导出在库车辆列表
     *
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/exportExcel/XpVehicleExportExcel.java
@@ -59,12 +59,12 @@
    @Excel(name = "保险到期时间", width = 20, exportFormat = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime insuranceEndTime;
    @ApiModelProperty(value = "运营资质到期时间")
    @Excel(name = "运营资质到期时间", width = 20, exportFormat = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime qualificationExpireTime;
    @ApiModelProperty(value = "备注")
    @Excel(name = "备注", width = 25)
    private String remarks;
    // @ApiModelProperty(value = "运营资质到期时间")
    // @Excel(name = "运营资质到期时间", width = 20, exportFormat = "yyyy-MM-dd HH:mm:ss")
    // private LocalDateTime qualificationExpireTime;
    //
    // @ApiModelProperty(value = "备注")
    // @Excel(name = "备注", width = 25)
    // private String remarks;
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/importExcel/XpVehicleImportExcel.java
@@ -30,7 +30,7 @@
    @Excel(name = "车辆颜色", width = 12)
    private String vehicleColor;
    @Excel(name = "车辆图片", width = 12, type = 2)
    @Excel(name = "车辆图片", width = 12, type = 2, savePath = "/Users/mitao/Documents/excel/upload/img/")
    private String vehiclePictures;
    @Excel(name = "采购来源", width = 15)
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/FleetVehicleVO.java
@@ -1,5 +1,6 @@
package com.xiaopeng.vehicleConvoy.model.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -33,4 +34,15 @@
    @ApiModelProperty(value = "运营状态描述")
    private String operatingStatusDesc;
    @ApiModelProperty(value = "采购来源")
    private String procurementSource;
    @ApiModelProperty(value = "围栏id")
    private Long electronicId;
    @ApiModelProperty(value = "运营区域")
    private String electronicName;
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpFleetServiceImpl.java
@@ -1,5 +1,6 @@
package com.xiaopeng.vehicleConvoy.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -17,6 +18,7 @@
import com.xiaopeng.api.model.vehicleConvoy.XpFleetVehicle;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO;
import com.xiaopeng.api.vo.operation.ElectronicFenceDetailVo;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.web.page.PageInfo;
@@ -368,6 +370,18 @@
        if (!CollectionUtils.isEmpty(vehicleIds)) {
            R<List<XpVehicle>> vehiclesResult = xpVehicleClient.listByIds(vehicleIds);
            if (vehiclesResult.getData() != null) {
                List<Long> electronicIds = vehiclesResult.getData().stream()
                        .map(XpVehicle::getElectronicId)
                        .filter(Objects::nonNull)
                        .distinct()
                        .collect(Collectors.toList());
                Map<Long, String> electronicNameMap = new HashMap<>();
                if (CollUtil.isNotEmpty(electronicIds)) {
                    List<XpElectronicFence> data = xpElectronicClient.listByIds(electronicIds).getData();
                    electronicNameMap = CollUtil.isNotEmpty(data) ?
                            data.stream().collect(Collectors.toMap(XpElectronicFence::getId, XpElectronicFence::getElectronicName))
                            : Collections.emptyMap();
                }
                for (XpVehicle vehicle : vehiclesResult.getData()) {
                    // 如果有车牌号筛选条件,进行过滤
                    if (StringUtils.isNotBlank(query.getPlateNumber()) &&
@@ -382,6 +396,9 @@
                    vo.setVehicleColor(vehicle.getVehicleColor());
                    vo.setVehicleModel(vehicle.getVehicleModel());
                    vo.setOperatingStatus(vehicle.getOperatingStatus());
                    vo.setProcurementSource(vehicle.getProcurementSource());
                    vo.setElectronicId(vehicle.getElectronicId());
                    vo.setElectronicName(electronicNameMap.get(vehicle.getElectronicId()));
                    records.add(vo);
                }
            }
@@ -469,6 +486,13 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteById(Long id) {
        long vehicleCount = xpFleetVehicleService.count(new LambdaQueryWrapper<XpFleetVehicle>()
                .eq(XpFleetVehicle::getFleetId, id));
        long userCount = xpFleetOperationsService.count(new LambdaQueryWrapper<XpFleetOperations>()
                .eq(XpFleetOperations::getFleetId, id));
        if (vehicleCount > 0 || userCount > 0) {
            throw new ServiceException("该车队下仍有绑定车辆/人员,无法删除", I18nKeyConstant.FLEET_DELETE_HAS_BOUND_VEHICLE_OR_USER);
        }
        // 删除用户关联
        xpFleetOperationsService.lambdaUpdate().eq(XpFleetOperations::getFleetId, id).remove();
        // 删除车辆关联
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleAnnualRecordServiceImpl.java
@@ -36,7 +36,7 @@
            wrapper.ge(XpVehicleAnnualRecord::getCreateTime, LocalDateTime.of(query.getStartTime(), LocalTime.MIN));
        }
        if (Objects.nonNull(query.getEndTime())) {
            wrapper.ge(XpVehicleAnnualRecord::getCreateTime, LocalDateTime.of(query.getEndTime(), LocalTime.MAX));
            wrapper.le(XpVehicleAnnualRecord::getCreateTime, LocalDateTime.of(query.getEndTime(), LocalTime.MAX));
        }
        wrapper.like(StringUtils.isNotBlank(query.getOperationsUserName()), XpVehicleAnnualRecord::getCreateBy, query.getOperationsUserName());
        wrapper.orderByDesc(XpVehicleAnnualRecord::getCreateTime);
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java
@@ -547,7 +547,7 @@
        // 校验通过,批量转为运营状态(vehicleType: 1→2)
        LambdaUpdateWrapper<XpVehicle> wrapper = new LambdaUpdateWrapper<>();
        wrapper.in(XpVehicle::getId, idStr)
        wrapper.in(XpVehicle::getId, ids)
                .set(XpVehicle::getVehicleType, 2)
                .set(XpVehicle::getOperatingStatus, 1);
        this.update(wrapper);
@@ -585,7 +585,25 @@
    public PageInfo<XpVehicle> vehiclePageList(VehiclePageQuery query) {
        PageInfo<XpVehicle> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        List<XpVehicle> list = this.baseMapper.vehiclePageList(pageInfo, query);
        //查询运营区域名称
        List<Long> electronicIds = list.stream()
                .map(XpVehicle::getElectronicId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        Map<Long, String> electronicNameMap = new HashMap<>();
        if (CollUtil.isNotEmpty(electronicIds)) {
            List<XpElectronicFence> data = xpElectronicClient.listByIds(electronicIds).getData();
            electronicNameMap = CollUtil.isNotEmpty(data) ?
                    data.stream().collect(Collectors.toMap(XpElectronicFence::getId, XpElectronicFence::getElectronicName))
                    : Collections.emptyMap();
        }
        Map<Long, String> finalElectronicNameMap = electronicNameMap;
        list.forEach(item -> {
            if (item.getElectronicId() != null) {
                item.setElectronicName(finalElectronicNameMap.get(item.getElectronicId()));
            }
        });
        if (CollUtil.isNotEmpty(list)) {
            LocalDate today = LocalDate.now();
            List<Long> expiredVehicleIds = new ArrayList<>();
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages.properties
@@ -4,11 +4,20 @@
fleet.operation.user.bound.v1=
fleet.vehicle.bound.v2=当前车队已和该车辆关联
fleet.vehicle.bound.v1=车辆[{0}]已绑定车队[{1}]
fleet.delete.has.bound.vehicle.or.user=该车队下仍有绑定车辆/人员,无法删除
policy.application.audit.status.notNull=保单审核状态不能为空
welcome.message=欢迎来到我们的应用程序!
# 车辆管理
vehicle.import.failed=车辆导入失败!
vehicle.import.data.empty=导入数据为空!
vehicle.import.fields.required=第{0}行以下字段不能为空:{1}
vehicle.import.field.vehicleCode=车辆编码
vehicle.import.field.vehicleVin=车辆VIN码
vehicle.import.field.plateNumber=车牌号
vehicle.import.field.vehicleModel=型号
vehicle.import.field.vehicleColor=车辆颜色
vehicle.import.field.vehiclePictures=车辆图片
vehicle.import.field.procurementSource=采购来源
vehicle.operation.info.incomplete=以下信息未完善,无法开始运营:{0}
vehicle.batch.operation.condition.not.met=以下车辆不满足运营条件:{0}
vehicle.time.expired=以下时间已过期,请填写有效日期:{0}
vehicle.time.expired=以下时间已过期,请填写有效日期:{0}
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_en_US.properties
@@ -3,6 +3,7 @@
policy.application.audit.ids.notEmpty=The list of policy application ids cannot be empty
fleet.operation.user.bound.v1=Operation user [{0}] already bound fleet:[{1}]
fleet.vehicle.bound.v2=The current team has been associated with this vehicle.
fleet.delete.has.bound.vehicle.or.user=The fleet still has bound vehicles/personnel and cannot be deleted
policy.application.audit.status.notNull=The review status of the insurance policy cannot be empty
welcome.message=Welcome to our application!
order.not.exists=No valid order information found
@@ -10,9 +11,17 @@
# Vehicle Management
vehicle.import.failed=Vehicle import failed!
vehicle.import.data.empty=Import data is empty!
vehicle.import.fields.required=Row {0}: the following fields are required: {1}
vehicle.import.field.vehicleCode=Vehicle Code
vehicle.import.field.vehicleVin=VIN
vehicle.import.field.plateNumber=Plate Number
vehicle.import.field.vehicleModel=Model
vehicle.import.field.vehicleColor=Color
vehicle.import.field.vehiclePictures=Vehicle Picture
vehicle.import.field.procurementSource=Procurement Source
vehicle.operation.info.incomplete=The following information is incomplete and cannot start operation: {0}
vehicle.batch.operation.condition.not.met=The following vehicles do not meet operating conditions: {0}
vehicle.time.expired=The following dates have expired, please enter valid dates: {0}
vehicle.not.p.gear=The trunk cannot be opened when the vehicle is not in P gear
vehicle.temperature.up=The highest temperature is 28°C
vehicle.temperature.down=The lowest temperature is 22°C
vehicle.temperature.down=The lowest temperature is 22°C
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -4,6 +4,7 @@
fleet.user.bound=运维人员[{0}]已绑定车队[{1}]
fleet.operation.user.bound.v1=运维人员[{0}]已绑定车队[{1}]
fleet.vehicle.bound.v2=当前车队已和该车辆关联
fleet.delete.has.bound.vehicle.or.user=该车队下仍有绑定车辆/人员,无法删除
policy.application.audit.status.notNull=保单审核状态不能为空
welcome.message=欢迎来到我们的应用程序!
order.not.exists=未查询到有效订单信息
@@ -11,9 +12,17 @@
# 车辆管理
vehicle.import.failed=车辆导入失败!
vehicle.import.data.empty=导入数据为空!
vehicle.import.fields.required=第{0}行以下字段不能为空:{1}
vehicle.import.field.vehicleCode=车辆编码
vehicle.import.field.vehicleVin=车辆VIN码
vehicle.import.field.plateNumber=车牌号
vehicle.import.field.vehicleModel=型号
vehicle.import.field.vehicleColor=车辆颜色
vehicle.import.field.vehiclePictures=车辆图片
vehicle.import.field.procurementSource=采购来源
vehicle.operation.info.incomplete=以下信息未完善,无法开始运营:{0}
vehicle.batch.operation.condition.not.met=以下车辆不满足运营条件:{0}
vehicle.time.expired=以下时间已过期,请填写有效日期:{0}
vehicle.not.p.gear=车辆非P档,无法开启后备箱
vehicle.temperature.up=温度最高28℃
vehicle.temperature.down=温度最低22℃
vehicle.temperature.down=温度最低22℃