From 7337d6b1a7eb87b858f30fafd41bbea14f148921 Mon Sep 17 00:00:00 2001
From: 无关风月 <443237572@qq.com>
Date: 星期五, 27 二月 2026 09:09:34 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'
---
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpCruisePlanServiceImpl.java | 353 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 353 insertions(+), 0 deletions(-)
diff --git a/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpCruisePlanServiceImpl.java b/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpCruisePlanServiceImpl.java
index 6ac5b1d..e5c6969 100644
--- a/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpCruisePlanServiceImpl.java
+++ b/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpCruisePlanServiceImpl.java
@@ -1,10 +1,41 @@
package com.xiaopeng.operation.service.impl;
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient;
+import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetVehicleClient;
+import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.operation.XpCruisePlan;
+import com.xiaopeng.api.model.operation.XpCruisePlanPoint;
+import com.xiaopeng.api.model.operation.XpCruisePlanVehicle;
+import com.xiaopeng.api.model.operation.XpPoint;
+import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
+import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
+import com.xiaopeng.common.core.domain.R;
+import com.xiaopeng.common.core.exception.ServiceException;
+import com.xiaopeng.common.core.web.page.PageInfo;
+import com.xiaopeng.common.security.utils.SecurityUtils;
+import com.xiaopeng.operation.constant.I18nKeyConstant;
+import com.xiaopeng.operation.dto.XpCruisePlanAddDTO;
+import com.xiaopeng.operation.dto.XpCruisePlanEditDTO;
import com.xiaopeng.operation.mapper.XpCruisePlanMapper;
+import com.xiaopeng.operation.query.CruisePlanQuery;
+import com.xiaopeng.operation.service.IXpCruisePlanPointService;
import com.xiaopeng.operation.service.IXpCruisePlanService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.xiaopeng.operation.service.IXpCruisePlanVehicleService;
+import com.xiaopeng.operation.service.IXpPointService;
+import com.xiaopeng.operation.vo.XpCruisePlanDetailVO;
+import com.xiaopeng.operation.vo.XpCruisePlanPointVO;
+import com.xiaopeng.operation.vo.XpCruisePlanVehicleVO;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.stream.Collectors;
/**
* <p>
@@ -15,6 +46,328 @@
* @since 2026-02-26
*/
@Service
+@RequiredArgsConstructor(onConstructor_ = @Lazy)
public class XpCruisePlanServiceImpl extends ServiceImpl<XpCruisePlanMapper, XpCruisePlan> implements IXpCruisePlanService {
+ private final IXpCruisePlanPointService xpCruisePlanPointService;
+ private final IXpCruisePlanVehicleService xpCruisePlanVehicleService;
+ private final IXpPointService xpPointService;
+ private final XpVehicleClient xpVehicleClient;
+ private final XpFleetVehicleClient xpFleetVehicleClient;
+ private final XpFleetClient xpFleetClient;
+ @Override
+ public PageInfo<XpCruisePlan> getPageList(CruisePlanQuery query) {
+ PageInfo<XpCruisePlan> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
+ Page<XpCruisePlan> page = lambdaQuery()
+ .like(StringUtils.isNotBlank(query.getPlanName()), XpCruisePlan::getPlanName, query.getPlanName())
+ .orderByDesc(XpCruisePlan::getCreateTime)
+ .page(pageInfo);
+
+ //收集巡游计划ID
+ List<Long> planIds = page.getRecords().stream().map(XpCruisePlan::getId).collect(Collectors.toList());
+ // 批量查询巡游计划点位和车辆信息,并组装到巡游计划对象中
+ Map<Long, List<XpCruisePlanPoint>> pointMap = CollUtil.isNotEmpty(planIds) ? xpCruisePlanPointService.lambdaQuery().in(XpCruisePlanPoint::getCruisePlanId, planIds)
+ .list().stream().collect(Collectors.groupingBy(XpCruisePlanPoint::getCruisePlanId)) : Collections.emptyMap();
+ Map<Long, List<XpCruisePlanVehicle>> vehicleMap = CollUtil.isNotEmpty(planIds) ? xpCruisePlanVehicleService.lambdaQuery().in(XpCruisePlanVehicle::getCruisePlanId, planIds)
+ .list().stream().collect(Collectors.groupingBy(XpCruisePlanVehicle::getCruisePlanId)) : Collections.emptyMap();
+ page.getRecords().forEach(plan -> {
+ plan.setPointNum(pointMap.get(plan.getId()).size());
+ plan.setVehicleNum(vehicleMap.get(plan.getId()).size());
+ });
+ pageInfo.setRecords(page.getRecords());
+ return pageInfo;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void add(XpCruisePlanAddDTO dto) {
+ //1.校验巡游计划名称
+ validatePlanName(dto.getPlanName(), null);
+ //2.校验点位参数
+ validatePointList(dto.getPointList());
+ Set<Long> vehicleIdSet = validateVehicleList(dto.getVehicleIdList());
+ //3.校验车辆是否已绑定其他巡游计划
+ 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);
+ }
+ List<XpCruisePlanPoint> pointList = buildPointList(plan.getId(), dto.getPointList());
+ boolean pointSaved = xpCruisePlanPointService.saveBatch(pointList);
+ if (!pointSaved) {
+ throw new ServiceException("新增巡游计划失败", I18nKeyConstant.CRUISE_PLAN_ADD_FAIL);
+ }
+ List<XpCruisePlanVehicle> vehicleList = buildVehicleList(plan.getId(), dto.getVehicleIdList());
+ boolean vehicleSaved = xpCruisePlanVehicleService.saveBatch(vehicleList);
+ if (!vehicleSaved) {
+ throw new ServiceException("新增巡游计划失败", I18nKeyConstant.CRUISE_PLAN_ADD_FAIL);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void edit(XpCruisePlanEditDTO dto) {
+ if (dto.getId() == null) {
+ throw new ServiceException("巡游计划ID不能为空", I18nKeyConstant.CRUISE_PLAN_ID_EMPTY);
+ }
+ XpCruisePlan plan = getById(dto.getId());
+ if (plan == null) {
+ throw new ServiceException("巡游计划不存在", I18nKeyConstant.CRUISE_PLAN_NOT_EXIST);
+ }
+ validatePlanName(dto.getPlanName(), dto.getId());
+ validatePointList(dto.getPointList());
+ 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);
+ }
+ xpCruisePlanPointService.lambdaUpdate()
+ .eq(XpCruisePlanPoint::getCruisePlanId, dto.getId())
+ .remove();
+ xpCruisePlanVehicleService.lambdaUpdate()
+ .eq(XpCruisePlanVehicle::getCruisePlanId, dto.getId())
+ .remove();
+ List<XpCruisePlanPoint> pointList = buildPointList(dto.getId(), dto.getPointList());
+ boolean pointSaved = xpCruisePlanPointService.saveBatch(pointList);
+ if (!pointSaved) {
+ throw new ServiceException("编辑巡游计划失败", I18nKeyConstant.CRUISE_PLAN_EDIT_FAIL);
+ }
+ List<XpCruisePlanVehicle> vehicleList = buildVehicleList(dto.getId(), dto.getVehicleIdList());
+ boolean vehicleSaved = xpCruisePlanVehicleService.saveBatch(vehicleList);
+ if (!vehicleSaved) {
+ throw new ServiceException("编辑巡游计划失败", I18nKeyConstant.CRUISE_PLAN_EDIT_FAIL);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteById(Long id) {
+ xpCruisePlanPointService.lambdaUpdate()
+ .eq(XpCruisePlanPoint::getCruisePlanId, id)
+ .remove();
+ xpCruisePlanVehicleService.lambdaUpdate()
+ .eq(XpCruisePlanVehicle::getCruisePlanId, id)
+ .remove();
+ removeById(id);
+ }
+
+ @Override
+ public XpCruisePlanDetailVO detail(Long id) {
+ if (id == null) {
+ throw new ServiceException("巡游计划ID不能为空", I18nKeyConstant.CRUISE_PLAN_ID_EMPTY);
+ }
+ XpCruisePlan plan = getById(id);
+ if (plan == null) {
+ throw new ServiceException("巡游计划不存在", I18nKeyConstant.CRUISE_PLAN_NOT_EXIST);
+ }
+ List<XpCruisePlanPoint> planPoints = xpCruisePlanPointService.lambdaQuery()
+ .eq(XpCruisePlanPoint::getCruisePlanId, id)
+ .orderByAsc(XpCruisePlanPoint::getSortOrder)
+ .list();
+ List<Long> pointIds = planPoints.stream()
+ .map(XpCruisePlanPoint::getPointId)
+ .filter(Objects::nonNull)
+ .map(Integer::longValue)
+ .collect(Collectors.toList());
+ Map<Long, XpPoint> pointMap = CollUtil.isNotEmpty(pointIds)
+ ? xpPointService.listByIds(pointIds).stream()
+ .collect(Collectors.toMap(XpPoint::getId, v -> v))
+ : Collections.emptyMap();
+ List<XpCruisePlanPointVO> pointList = planPoints.stream().map(planPoint -> {
+ XpCruisePlanPointVO vo = new XpCruisePlanPointVO();
+ vo.setPointId(planPoint.getPointId());
+ vo.setSortOrder(planPoint.getSortOrder());
+ XpPoint point = pointMap.get(planPoint.getPointId() == null ? null : planPoint.getPointId().longValue());
+ if (point != null) {
+ vo.setPointName(point.getPointName());
+ vo.setPointAddress(point.getPointAddress());
+ }
+ return vo;
+ }).collect(Collectors.toList());
+
+ List<XpCruisePlanVehicle> planVehicles = xpCruisePlanVehicleService.lambdaQuery()
+ .eq(XpCruisePlanVehicle::getCruisePlanId, id)
+ .list();
+ List<Long> vehicleIds = planVehicles.stream()
+ .map(XpCruisePlanVehicle::getVehicleId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ Map<Long, XpVehicle> vehicleMap;
+ if (CollUtil.isNotEmpty(vehicleIds)) {
+ R<List<XpVehicle>> vehicleResult = xpVehicleClient.listByIds(vehicleIds);
+ if (vehicleResult != null && CollUtil.isNotEmpty(vehicleResult.getData())) {
+ vehicleMap = vehicleResult.getData().stream()
+ .collect(Collectors.toMap(XpVehicle::getId, v -> v));
+ } else {
+ vehicleMap = Collections.emptyMap();
+ }
+ } else {
+ vehicleMap = Collections.emptyMap();
+ }
+ Map<Long, Long> vehicleFleetIdMap = new HashMap<>();
+ if (CollUtil.isNotEmpty(vehicleIds)) {
+ for (Long vehicleId : vehicleIds) {
+ R<Long> fleetIdResult = xpFleetVehicleClient.getFleetIdByVehicleId(vehicleId);
+ if (fleetIdResult != null && fleetIdResult.getData() != null) {
+ vehicleFleetIdMap.put(vehicleId, fleetIdResult.getData());
+ }
+ }
+ }
+ Map<Long, String> fleetNameMap;
+ if (!vehicleFleetIdMap.isEmpty()) {
+ List<Long> fleetIds = vehicleFleetIdMap.values().stream().distinct().collect(Collectors.toList());
+ R<List<XpFleet>> fleetResult = xpFleetClient.listByIds(fleetIds);
+ if (fleetResult != null && CollUtil.isNotEmpty(fleetResult.getData())) {
+ fleetNameMap = fleetResult.getData().stream()
+ .collect(Collectors.toMap(XpFleet::getId, XpFleet::getFleetName, (a, b) -> a));
+ } else {
+ fleetNameMap = Collections.emptyMap();
+ }
+ } else {
+ fleetNameMap = Collections.emptyMap();
+ }
+ List<XpCruisePlanVehicleVO> vehicleList = planVehicles.stream().map(planVehicle -> {
+ XpCruisePlanVehicleVO vo = new XpCruisePlanVehicleVO();
+ vo.setVehicleId(planVehicle.getVehicleId());
+ XpVehicle vehicle = vehicleMap.get(planVehicle.getVehicleId());
+ if (vehicle != null) {
+ vo.setVehicleCode(vehicle.getVehicleCode());
+ vo.setPlateNumber(vehicle.getPlateNumber());
+ vo.setVehicleModel(vehicle.getVehicleModel());
+ }
+ Long fleetId = vehicleFleetIdMap.get(planVehicle.getVehicleId());
+ if (fleetId != null) {
+ vo.setFleetName(fleetNameMap.get(fleetId));
+ }
+ return vo;
+ }).collect(Collectors.toList());
+
+ XpCruisePlanDetailVO detailVO = new XpCruisePlanDetailVO();
+ detailVO.setId(plan.getId());
+ detailVO.setPlanName(plan.getPlanName());
+ detailVO.setPointNum(planPoints.size());
+ detailVO.setVehicleNum(planVehicles.size());
+ detailVO.setCreateTime(plan.getCreateTime());
+ detailVO.setPointList(pointList);
+ detailVO.setVehicleList(vehicleList);
+ return detailVO;
+ }
+
+ /**
+ * 校验巡游计划名称
+ * @param planName
+ * @param excludePlanId
+ */
+ private void validatePlanName(String planName, Long excludePlanId) {
+ if (StringUtils.isBlank(planName)) {
+ throw new ServiceException("计划名称不能为空", I18nKeyConstant.CRUISE_PLAN_NAME_EMPTY);
+ }
+ Long count = lambdaQuery()
+ .eq(XpCruisePlan::getPlanName, planName)
+ .ne(excludePlanId != null, XpCruisePlan::getId, excludePlanId)
+ .count();
+ if (count > 0) {
+ throw new ServiceException("计划名称已存在", I18nKeyConstant.CRUISE_PLAN_NAME_EXISTS);
+ }
+ }
+
+ /**
+ * 校验关联点位列表
+ * @param pointList
+ */
+ private void validatePointList(List<XpCruisePlanAddDTO.CruisePlanPointDTO> pointList) {
+ if (CollUtil.isEmpty(pointList)) {
+ throw new ServiceException("关联点位列表不能为空", I18nKeyConstant.CRUISE_PLAN_POINT_EMPTY);
+ }
+ Set<Integer> pointIdSet = new HashSet<>();
+ for (XpCruisePlanAddDTO.CruisePlanPointDTO pointDTO : pointList) {
+ if (pointDTO.getPointId() == null || pointDTO.getSortOrder() == null || pointDTO.getStopFlag() == null) {
+ throw new ServiceException("点位参数不完整", I18nKeyConstant.CRUISE_PLAN_POINT_INCOMPLETE);
+ }
+ if (pointDTO.getStopFlag() != 0 && pointDTO.getStopFlag() != 1) {
+ throw new ServiceException("点位停车标识不合法", I18nKeyConstant.CRUISE_PLAN_STOP_FLAG_INVALID);
+ }
+ if (!pointIdSet.add(pointDTO.getPointId())) {
+ throw new ServiceException("点位ID重复", I18nKeyConstant.CRUISE_PLAN_POINT_DUPLICATE);
+ }
+ }
+ }
+
+ /**
+ * 校验关联车辆列表
+ * @param vehicleIdList
+ * @return
+ */
+ private Set<Long> validateVehicleList(List<Long> vehicleIdList) {
+ if (CollUtil.isEmpty(vehicleIdList)) {
+ throw new ServiceException("关联车辆列表不能为空", I18nKeyConstant.CRUISE_PLAN_VEHICLE_EMPTY);
+ }
+ Set<Long> vehicleIdSet = new HashSet<>();
+ for (Long vehicleId : vehicleIdList) {
+ if (vehicleId == null) {
+ throw new ServiceException("车辆参数不完整", I18nKeyConstant.CRUISE_PLAN_VEHICLE_INCOMPLETE);
+ }
+ if (!vehicleIdSet.add(vehicleId)) {
+ throw new ServiceException("车辆ID重复", I18nKeyConstant.CRUISE_PLAN_VEHICLE_DUPLICATE);
+ }
+ }
+ return vehicleIdSet;
+ }
+
+ /**
+ * 校验车辆是否已绑定其他巡游计划
+ * @param vehicleIdSet
+ * @param excludePlanId
+ */
+ private void validateVehicleBind(Set<Long> vehicleIdSet, Long excludePlanId) {
+ List<XpCruisePlanVehicle> boundVehicles = xpCruisePlanVehicleService.lambdaQuery()
+ .in(XpCruisePlanVehicle::getVehicleId, vehicleIdSet)
+ .ne(excludePlanId != null, XpCruisePlanVehicle::getCruisePlanId, excludePlanId)
+ .list();
+ if (CollUtil.isNotEmpty(boundVehicles)) {
+ throw new ServiceException("车辆已关联其他巡游计划", I18nKeyConstant.CRUISE_PLAN_VEHICLE_ALREADY_BOUND);
+ }
+ }
+
+ /**
+ * 构建巡游计划点位列表
+ * @param planId
+ * @param pointList
+ * @return
+ */
+ private List<XpCruisePlanPoint> buildPointList(Long planId, List<XpCruisePlanAddDTO.CruisePlanPointDTO> pointList) {
+ List<XpCruisePlanPoint> result = new ArrayList<>(pointList.size());
+ for (XpCruisePlanAddDTO.CruisePlanPointDTO pointDTO : pointList) {
+ XpCruisePlanPoint point = new XpCruisePlanPoint();
+ point.setCruisePlanId(planId);
+ point.setPointId(pointDTO.getPointId());
+ point.setSortOrder(pointDTO.getSortOrder());
+ point.setStopFlag(pointDTO.getStopFlag());
+ result.add(point);
+ }
+ return result;
+ }
+
+ /**
+ * 构建巡游计划车辆列表
+ * @param planId
+ * @param vehicleIdList
+ * @return
+ */
+ private List<XpCruisePlanVehicle> buildVehicleList(Long planId, List<Long> vehicleIdList) {
+ List<XpCruisePlanVehicle> result = new ArrayList<>(vehicleIdList.size());
+ for (Long vehicleId : vehicleIdList) {
+ XpCruisePlanVehicle vehicle = new XpCruisePlanVehicle();
+ vehicle.setCruisePlanId(planId);
+ vehicle.setVehicleId(vehicleId);
+ result.add(vehicle);
+ }
+ return result;
+ }
}
--
Gitblit v1.7.1