package com.xiaopeng.operation.service.impl; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 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.*; 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; /** *

* 巡游计划 服务实现类 *

* * @author mitao * @since 2026-02-26 */ @Service @RequiredArgsConstructor(onConstructor_ = @Lazy) public class XpCruisePlanServiceImpl extends ServiceImpl implements IXpCruisePlanService { private final IXpCruisePlanPointService xpCruisePlanPointService; private final IXpCruisePlanVehicleService xpCruisePlanVehicleService; private final XpPointService xpPointService; private final XpVehicleClient xpVehicleClient; private final XpFleetVehicleClient xpFleetVehicleClient; private final XpFleetClient xpFleetClient; @Override public PageInfo getPageList(CruisePlanQuery query) { PageInfo pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); Page page = lambdaQuery() .like(StringUtils.isNotBlank(query.getPlanName()), XpCruisePlan::getPlanName, query.getPlanName()) .orderByDesc(XpCruisePlan::getCreateTime) .page(pageInfo); //收集巡游计划ID List planIds = page.getRecords().stream().map(XpCruisePlan::getId).collect(Collectors.toList()); // 批量查询巡游计划点位和车辆信息,并组装到巡游计划对象中 Map> pointMap = CollUtil.isNotEmpty(planIds) ? xpCruisePlanPointService.lambdaQuery().in(XpCruisePlanPoint::getCruisePlanId, planIds) .list().stream().collect(Collectors.groupingBy(XpCruisePlanPoint::getCruisePlanId)) : Collections.emptyMap(); Map> 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 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 pointList = buildPointList(plan.getId(), dto.getPointList()); boolean pointSaved = xpCruisePlanPointService.saveBatch(pointList); if (!pointSaved) { throw new ServiceException("新增巡游计划失败", I18nKeyConstant.CRUISE_PLAN_ADD_FAIL); } List 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 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 pointList = buildPointList(dto.getId(), dto.getPointList()); boolean pointSaved = xpCruisePlanPointService.saveBatch(pointList); if (!pointSaved) { throw new ServiceException("编辑巡游计划失败", I18nKeyConstant.CRUISE_PLAN_EDIT_FAIL); } List 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 planPoints = xpCruisePlanPointService.lambdaQuery() .eq(XpCruisePlanPoint::getCruisePlanId, id) .orderByAsc(XpCruisePlanPoint::getSortOrder) .list(); List pointIds = planPoints.stream() .map(XpCruisePlanPoint::getPointId) .filter(Objects::nonNull) .collect(Collectors.toList()); Map pointMap = CollUtil.isNotEmpty(pointIds) ? xpPointService.listByIds(pointIds).stream() .collect(Collectors.toMap(XpPoint::getId, v -> v)) : Collections.emptyMap(); List 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 planVehicles = xpCruisePlanVehicleService.lambdaQuery() .eq(XpCruisePlanVehicle::getCruisePlanId, id) .list(); List vehicleIds = planVehicles.stream() .map(XpCruisePlanVehicle::getVehicleId) .filter(Objects::nonNull) .collect(Collectors.toList()); Map vehicleMap; if (CollUtil.isNotEmpty(vehicleIds)) { R> 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 vehicleFleetIdMap = new HashMap<>(); if (CollUtil.isNotEmpty(vehicleIds)) { for (Long vehicleId : vehicleIds) { R fleetIdResult = xpFleetVehicleClient.getFleetIdByVehicleId(vehicleId); if (fleetIdResult != null && fleetIdResult.getData() != null) { vehicleFleetIdMap.put(vehicleId, fleetIdResult.getData()); } } } Map fleetNameMap; if (!vehicleFleetIdMap.isEmpty()) { List fleetIds = vehicleFleetIdMap.values().stream().distinct().collect(Collectors.toList()); R> 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 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 pointList) { if (CollUtil.isEmpty(pointList)) { throw new ServiceException("关联点位列表不能为空", I18nKeyConstant.CRUISE_PLAN_POINT_EMPTY); } Set 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 validateVehicleList(List vehicleIdList) { if (CollUtil.isEmpty(vehicleIdList)) { throw new ServiceException("关联车辆列表不能为空", I18nKeyConstant.CRUISE_PLAN_VEHICLE_EMPTY); } Set 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 vehicleIdSet, Long excludePlanId) { List 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 buildPointList(Long planId, List pointList) { List 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 buildVehicleList(Long planId, List vehicleIdList) { List result = new ArrayList<>(vehicleIdList.size()); for (Long vehicleId : vehicleIdList) { XpCruisePlanVehicle vehicle = new XpCruisePlanVehicle(); vehicle.setCruisePlanId(planId); vehicle.setVehicleId(vehicleId); result.add(vehicle); } return result; } }