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.google.common.collect.Lists;
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.XpTestPlanPoint;
import com.xiaopeng.api.model.operation.XpTestPlanVehicle;
import com.xiaopeng.api.model.operation.XpTestPlan;
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.operation.constant.I18nKeyConstant;
import com.xiaopeng.operation.dto.XpTestPlanAddDTO;
import com.xiaopeng.operation.dto.XpTestPlanEditDTO;
import com.xiaopeng.operation.mapper.XpTestPlanMapper;
import com.xiaopeng.operation.query.TestPlanQuery;
import com.xiaopeng.operation.service.*;
import com.xiaopeng.operation.vo.*;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType;
import org.apache.shardingsphere.transaction.core.TransactionType;
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 XpTestPlanServiceImpl extends ServiceImpl implements IXpTestPlanService {
private final IXpTestPlanPointService xpTestPlanPointService;
private final IXpTestPlanVehicleService xpTestPlanVehicleService;
private final XpTestPointService xpTestPointService;
private final XpVehicleClient xpVehicleClient;
private final XpFleetVehicleClient xpFleetVehicleClient;
private final XpFleetClient xpFleetClient;
@Override
public PageInfo getPageList(TestPlanQuery query) {
PageInfo pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
Page page = lambdaQuery()
.like(StringUtils.isNotBlank(query.getPlanName()), XpTestPlan::getPlanName, query.getPlanName())
.orderByDesc(XpTestPlan::getCreateTime)
.page(pageInfo);
//收集测试计划ID
List planIds = page.getRecords().stream().map(XpTestPlan::getId).collect(Collectors.toList());
// 批量查询测试计划点位和车辆信息,并组装到测试计划对象中
Map> pointMap = CollUtil.isNotEmpty(planIds) ? xpTestPlanPointService.lambdaQuery().in(XpTestPlanPoint::getTestPlanId, planIds)
.list().stream().collect(Collectors.groupingBy(XpTestPlanPoint::getTestPlanId)) : Collections.emptyMap();
Map> vehicleMap = CollUtil.isNotEmpty(planIds) ? xpTestPlanVehicleService.lambdaQuery().in(XpTestPlanVehicle::getTestPlanId, planIds)
.list().stream().collect(Collectors.groupingBy(XpTestPlanVehicle::getTestPlanId)) : 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
@ShardingSphereTransactionType(TransactionType.BASE)
public void add(XpTestPlanAddDTO dto) {
//1.校验测试计划名称
validatePlanName(dto.getPlanName(), null);
//2.校验点位参数
validatePointList(dto.getPointList());
Set vehicleIdSet = validateVehicleList(dto.getVehicleIdList());
//3.校验车辆是否已绑定其他测试计划
validateVehicleBind(vehicleIdSet, null);
XpTestPlan plan = new XpTestPlan();
plan.setPlanName(dto.getPlanName());
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 = xpTestPlanPointService.saveBatch(pointList);
if (!pointSaved) {
throw new ServiceException("新增测试计划失败", I18nKeyConstant.CRUISE_PLAN_ADD_FAIL);
}
List vehicleList = buildVehicleList(plan.getId(), dto.getVehicleIdList());
boolean vehicleSaved = xpTestPlanVehicleService.saveBatch(vehicleList);
if (!vehicleSaved) {
throw new ServiceException("新增测试计划失败", I18nKeyConstant.CRUISE_PLAN_ADD_FAIL);
}
}
@Override
@Transactional
@ShardingSphereTransactionType(TransactionType.BASE)
public void edit(XpTestPlanEditDTO dto) {
if (dto.getId() == null) {
throw new ServiceException("测试计划ID不能为空", I18nKeyConstant.CRUISE_PLAN_ID_EMPTY);
}
XpTestPlan 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());
boolean planUpdated = updateById(plan);
if (!planUpdated) {
throw new ServiceException("编辑测试计划失败", I18nKeyConstant.CRUISE_PLAN_EDIT_FAIL);
}
xpTestPlanPointService.lambdaUpdate()
.eq(XpTestPlanPoint::getTestPlanId, dto.getId())
.remove();
xpTestPlanVehicleService.lambdaUpdate()
.eq(XpTestPlanVehicle::getTestPlanId, dto.getId())
.remove();
List pointList = buildPointList(dto.getId(), dto.getPointList());
boolean pointSaved = xpTestPlanPointService.saveBatch(pointList);
if (!pointSaved) {
throw new ServiceException("编辑测试计划失败", I18nKeyConstant.CRUISE_PLAN_EDIT_FAIL);
}
List vehicleList = buildVehicleList(dto.getId(), dto.getVehicleIdList());
boolean vehicleSaved = xpTestPlanVehicleService.saveBatch(vehicleList);
if (!vehicleSaved) {
throw new ServiceException("编辑测试计划失败", I18nKeyConstant.CRUISE_PLAN_EDIT_FAIL);
}
}
@Override
@Transactional
@ShardingSphereTransactionType(TransactionType.BASE)
public void deleteById(Long id) {
xpTestPlanPointService.lambdaUpdate()
.eq(XpTestPlanPoint::getTestPlanId, id)
.remove();
xpTestPlanVehicleService.lambdaUpdate()
.eq(XpTestPlanVehicle::getTestPlanId, id)
.remove();
removeById(id);
}
@Override
public XpTestPlanDetailVO detail(Long id) {
if (id == null) {
throw new ServiceException("测试计划ID不能为空", I18nKeyConstant.CRUISE_PLAN_ID_EMPTY);
}
XpTestPlan plan = getById(id);
if (plan == null) {
throw new ServiceException("测试计划不存在", I18nKeyConstant.CRUISE_PLAN_NOT_EXIST);
}
List planPoints = xpTestPlanPointService.lambdaQuery()
.eq(XpTestPlanPoint::getTestPlanId, id)
.list();
List pointIds = planPoints.stream()
.map(XpTestPlanPoint::getStartTestPointId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
List pointIds1 = planPoints.stream()
.map(XpTestPlanPoint::getEndTestPointId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
pointIds.addAll(pointIds1);
Map pointMap = CollUtil.isNotEmpty(pointIds)
? xpTestPointService.listByIds(pointIds).stream()
.collect(Collectors.toMap(XpTestPoint::getId, v -> v))
: Collections.emptyMap();
List pointList = planPoints.stream().map(planPoint -> {
XpTestPlanPointVO vo = new XpTestPlanPointVO();
vo.setStartTestPointId(planPoint.getStartTestPointId());
vo.setEndTestPointId(planPoint.getEndTestPointId());
vo.setStopFlag(planPoint.getStopFlag());
vo.setTestOrderType(planPoint.getTestOrderType());
XpTestPoint point = pointMap.get(planPoint.getStartTestPointId() == null ? null : planPoint.getStartTestPointId().longValue());
if (point != null) {
vo.setStartTestPointName(point.getPointName());
vo.setStartTestPointAddress(point.getPointAddress());
}
XpTestPoint point1 = pointMap.get(planPoint.getEndTestPointId() == null ? null : planPoint.getEndTestPointId().longValue());
if (point != null) {
vo.setEndTestPointName(point1.getPointName());
vo.setEndTestPointAddress(point1.getPointAddress());
}
return vo;
}).collect(Collectors.toList());
List planVehicles = xpTestPlanVehicleService.lambdaQuery()
.eq(XpTestPlanVehicle::getTestPlanId, id)
.list();
List vehicleIds = planVehicles.stream()
.map(XpTestPlanVehicle::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 -> {
XpTestPlanVehicleVO vo = new XpTestPlanVehicleVO();
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());
XpTestPlanDetailVO detailVO = new XpTestPlanDetailVO();
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(XpTestPlan::getPlanName, planName)
.ne(excludePlanId != null, XpTestPlan::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 (XpTestPlanAddDTO.TestPlanPointDTO pointDTO : pointList) {
if (pointDTO.getStartTestPointId() == null || pointDTO.getEndTestPointId() == null) {
throw new ServiceException("点位参数不完整", I18nKeyConstant.CRUISE_PLAN_POINT_INCOMPLETE);
}
if (!pointIdSet.add(pointDTO.getStartTestPointId()) || !pointIdSet.add(pointDTO.getEndTestPointId())) {
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 = xpTestPlanVehicleService.lambdaQuery()
.in(XpTestPlanVehicle::getVehicleId, vehicleIdSet)
.ne(excludePlanId != null, XpTestPlanVehicle::getTestPlanId, excludePlanId)
.list();
if (CollUtil.isNotEmpty(boundVehicles)) {
List data = xpVehicleClient.listByIds(boundVehicles.stream().map(XpTestPlanVehicle::getVehicleId).collect(Collectors.toList())).getData();
if (CollUtil.isNotEmpty(data)) {
String plateNumbers = data.stream().map(XpVehicle::getPlateNumber).collect(Collectors.joining(","));
Object[] args = Lists.newArrayList(plateNumbers).toArray();
throw new ServiceException("车辆已关联其他测试计划", I18nKeyConstant.CRUISE_PLAN_VEHICLE_ALREADY_BOUND, args);
}
}
}
/**
* 构建测试计划点位列表
* @param planId
* @param pointList
* @return
*/
private List buildPointList(Long planId, List pointList) {
List result = new ArrayList<>(pointList.size());
for (XpTestPlanAddDTO.TestPlanPointDTO pointDTO : pointList) {
XpTestPlanPoint point = new XpTestPlanPoint();
point.setTestPlanId(planId);
point.setStartTestPointId(pointDTO.getStartTestPointId());
point.setEndTestPointId(pointDTO.getEndTestPointId());
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) {
XpTestPlanVehicle vehicle = new XpTestPlanVehicle();
vehicle.setTestPlanId(planId);
vehicle.setVehicleId(vehicleId);
result.add(vehicle);
}
return result;
}
}