From 1a9aca4e28eb23091b97ba2877b68fade7dfde4e Mon Sep 17 00:00:00 2001
From: “mitao” <“2763622819@qq.com”>
Date: 星期六, 07 三月 2026 21:12:00 +0800
Subject: [PATCH] feat(management):管理后台-车队管理

---
 xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpFleetServiceImpl.java |  475 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 473 insertions(+), 2 deletions(-)

diff --git a/xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpFleetServiceImpl.java b/xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpFleetServiceImpl.java
index 276d5e0..c470d31 100644
--- a/xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpFleetServiceImpl.java
+++ b/xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpFleetServiceImpl.java
@@ -1,14 +1,46 @@
 package com.xiaopeng.vehicleConvoy.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.xiaopeng.api.dto.account.XpOperationsUserQueryDTO;
+import com.xiaopeng.api.feignClient.account.XpOperationUserClient;
+import com.xiaopeng.api.feignClient.operation.XpElectronicClient;
+import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
+import com.xiaopeng.api.model.account.XpOperationsUser;
+import com.xiaopeng.api.model.operation.XpElectronicFence;
 import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
+import com.xiaopeng.api.model.vehicleConvoy.XpFleetOperations;
+import com.xiaopeng.api.model.vehicleConvoy.XpFleetVehicle;
+import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
+import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO;
+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.vehicleConvoy.constant.I18nKeyConstant;
 import com.xiaopeng.vehicleConvoy.mapper.XpFleetMapper;
+import com.xiaopeng.vehicleConvoy.model.dto.FleetAddDTO;
+import com.xiaopeng.vehicleConvoy.model.dto.FleetEditDTO;
+import com.xiaopeng.vehicleConvoy.model.query.FleetPageQuery;
+import com.xiaopeng.vehicleConvoy.model.query.FleetUserPageQuery;
+import com.xiaopeng.vehicleConvoy.model.query.FleetVehiclePageQuery;
+import com.xiaopeng.vehicleConvoy.model.vo.FleetDetailVO;
+import com.xiaopeng.vehicleConvoy.model.vo.FleetPageVO;
+import com.xiaopeng.vehicleConvoy.model.vo.FleetUserVO;
+import com.xiaopeng.vehicleConvoy.model.vo.FleetVehicleVO;
+import com.xiaopeng.vehicleConvoy.service.IXpFleetOperationsService;
 import com.xiaopeng.vehicleConvoy.service.IXpFleetService;
+import com.xiaopeng.vehicleConvoy.service.IXpFleetVehicleService;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -19,7 +51,18 @@
  * @since 2026-02-03
  */
 @Service
+@RequiredArgsConstructor(onConstructor_ = @Lazy)
 public class XpFleetServiceImpl extends ServiceImpl<XpFleetMapper, XpFleet> implements IXpFleetService {
+
+    private final IXpFleetOperationsService xpFleetOperationsService;
+
+    private final IXpFleetVehicleService xpFleetVehicleService;
+
+    private final XpOperationUserClient xpOperationUserClient;
+
+    private final XpVehicleClient xpVehicleClient;
+
+    private final XpElectronicClient xpElectronicClient;
 
     @Override
     public List<XpFleet> listByIds(List<Long> ids) {
@@ -28,4 +71,432 @@
         }
         return super.listByIds(ids);
     }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R<Void> add(FleetAddDTO dto) {
+        // 1. 校验车队名称不能重复
+        long count = this.count(new LambdaQueryWrapper<XpFleet>()
+                .eq(XpFleet::getFleetName, dto.getFleetName()));
+        if (count > 0) {
+            throw new ServiceException("车队名称已存在");
+        }
+
+        // 2. 校验运维人员绑定
+        validateUserBinding(dto.getOperationsUserIds(), null);
+
+        // 3. 校验车辆绑定
+        validateVehicleBinding(dto.getVehicleIds(), null);
+
+        // 4. 插入车队主表记录
+        XpFleet fleet = new XpFleet();
+        fleet.setFleetName(dto.getFleetName());
+        fleet.setOperationsUserId(dto.getOperationsUserId());
+        fleet.setLocationId(dto.getLocationId());
+        this.save(fleet);
+
+        // 5. 批量插入车队-人员关联表
+        List<XpFleetOperations> operationsList = dto.getOperationsUserIds().stream()
+                .map(userId -> {
+                    XpFleetOperations operations = new XpFleetOperations();
+                    operations.setFleetId(fleet.getId());
+                    operations.setOperationsUserId(userId);
+                    return operations;
+                }).collect(Collectors.toList());
+        if (!CollectionUtils.isEmpty(operationsList)) {
+            xpFleetOperationsService.saveBatch(operationsList);
+        }
+
+        // 6. 批量插入车队-车辆关联表
+        List<XpFleetVehicle> vehicleList = dto.getVehicleIds().stream()
+                .map(vehicleId -> {
+                    XpFleetVehicle fleetVehicle = new XpFleetVehicle();
+                    fleetVehicle.setFleetId(fleet.getId());
+                    fleetVehicle.setVehicleId(vehicleId);
+                    return fleetVehicle;
+                }).collect(Collectors.toList());
+        if (!CollectionUtils.isEmpty(vehicleList)) {
+            xpFleetVehicleService.saveBatch(vehicleList);
+        }
+
+        return R.ok();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R<Void> edit(FleetEditDTO dto) {
+        // 1. 查询车队是否存在
+        XpFleet fleet = this.getById(dto.getId());
+        if (fleet == null) {
+            throw new ServiceException("车队不存在");
+        }
+
+        // 2. 校验车队名称不能与其他车队重复
+        long count = this.count(new LambdaQueryWrapper<XpFleet>()
+                .eq(XpFleet::getFleetName, dto.getFleetName())
+                .ne(XpFleet::getId, dto.getId()));
+        if (count > 0) {
+            throw new ServiceException("车队名称已存在");
+        }
+
+        // 3. 校验负责人绑定(如果修改了负责人)
+        if (!dto.getOperationsUserId().equals(fleet.getOperationsUserId())) {
+            List<Long> userIds = new ArrayList<>();
+            userIds.add(dto.getOperationsUserId());
+            validateUserBinding(userIds, dto.getId());
+        }
+
+        // 4. 更新车队主表记录
+        fleet.setFleetName(dto.getFleetName());
+        fleet.setOperationsUserId(dto.getOperationsUserId());
+        fleet.setLocationId(dto.getLocationId());
+        this.updateById(fleet);
+
+        return R.ok();
+    }
+
+    /**
+     * 校验人员绑定
+     *
+     * @param userIds        人员ID列表
+     * @param excludeFleetId 排除的车队ID(编辑时使用)
+     */
+    private void validateUserBinding(List<Long> userIds, Long excludeFleetId) {
+        if (CollectionUtils.isEmpty(userIds)) {
+            return;
+        }
+
+        for (Long userId : userIds) {
+            LambdaQueryWrapper<XpFleetOperations> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(XpFleetOperations::getOperationsUserId, userId);
+            if (excludeFleetId != null) {
+                wrapper.ne(XpFleetOperations::getFleetId, excludeFleetId);
+            }
+            XpFleetOperations operations = xpFleetOperationsService.getOne(wrapper);
+            if (operations != null) {
+                // 查询人员姓名和车队名称
+                R<XpOperationsUser> userResult = xpOperationUserClient.queryById(userId);
+                XpFleet fleet = this.getById(operations.getFleetId());
+                String userName = userResult.getData() != null ? userResult.getData().getNickName() : "未知";
+                String fleetName = fleet != null ? fleet.getFleetName() : "未知";
+                List<Object> argsList = new ArrayList<>();
+                argsList.add(userName);
+                argsList.add(fleetName);
+                Object[] args = argsList.toArray();
+                throw new ServiceException("运维人员" + userName + "已绑定车队" + fleetName, I18nKeyConstant.FLEET_OPERATION_USER_BOUND_V1, args);
+            }
+        }
+    }
+
+    /**
+     * 校验车辆绑定
+     *
+     * @param vehicleIds     车辆ID列表
+     * @param excludeFleetId 排除的车队ID(编辑时使用)
+     */
+    private void validateVehicleBinding(List<Long> vehicleIds, Long excludeFleetId) {
+        if (CollectionUtils.isEmpty(vehicleIds)) {
+            return;
+        }
+
+        for (Long vehicleId : vehicleIds) {
+            LambdaQueryWrapper<XpFleetVehicle> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(XpFleetVehicle::getVehicleId, vehicleId);
+            if (excludeFleetId != null) {
+                wrapper.ne(XpFleetVehicle::getFleetId, excludeFleetId);
+            }
+            XpFleetVehicle fleetVehicle = xpFleetVehicleService.getOne(wrapper);
+            if (fleetVehicle != null) {
+                // 查询车牌号和车队名称
+                R<XpVehicle> vehicleResult = xpVehicleClient.queryById(vehicleId);
+                XpFleet fleet = this.getById(fleetVehicle.getFleetId());
+                String plateNumber = vehicleResult.getData() != null ? vehicleResult.getData().getPlateNumber() : "未知";
+                String fleetName = fleet != null ? fleet.getFleetName() : "未知";
+                List<Object> argList = new ArrayList<>();
+                argList.add(plateNumber);
+                argList.add(fleetName);
+                throw new ServiceException("车辆[" + plateNumber + "]已绑定车队[" + fleetName + "]", I18nKeyConstant.FLEET_VEHICLE_BOUND_V1, argList.toArray());
+            }
+        }
+    }
+
+    @Override
+    public R<FleetDetailVO> queryById(Long id) {
+        // 1. 查询车队主表
+        XpFleet fleet = this.getById(id);
+        if (fleet == null) {
+            throw new ServiceException("车队不存在");
+        }
+
+        // 2. 组装VO
+        FleetDetailVO vo = new FleetDetailVO();
+        BeanUtils.copyProperties(fleet, vo);
+
+        // 3. 统计人员数量
+        long userCount = xpFleetOperationsService.count(new LambdaQueryWrapper<XpFleetOperations>()
+                .eq(XpFleetOperations::getFleetId, id));
+        vo.setUserCount((int) userCount);
+
+        // 4. 统计车辆数量
+        long vehicleCount = xpFleetVehicleService.count(new LambdaQueryWrapper<XpFleetVehicle>()
+                .eq(XpFleetVehicle::getFleetId, id));
+        vo.setVehicleCount((int) vehicleCount);
+
+        // 5. 查询负责人信息
+        if (fleet.getOperationsUserId() != null) {
+            R<XpOperationsUser> userResult = xpOperationUserClient.queryById(fleet.getOperationsUserId());
+            if (userResult.getData() != null) {
+                vo.setOperationsUserName(userResult.getData().getNickName());
+            }
+        }
+
+        return R.ok(vo);
+    }
+
+    @Override
+    public R<PageInfo<FleetPageVO>> pageList(FleetPageQuery query) {
+        // 1. 构建查询条件
+        LambdaQueryWrapper<XpFleet> wrapper = new LambdaQueryWrapper<>();
+        if (StringUtils.isNotBlank(query.getFleetName())) {
+            wrapper.like(XpFleet::getFleetName, query.getFleetName());
+        }
+        wrapper.orderByDesc(XpFleet::getCreateTime);
+
+        // 2. 执行分页查询
+        Page<XpFleet> page = new Page<>(query.getPageNum(), query.getPageSize());
+        Page<XpFleet> resultPage = this.page(page, wrapper);
+
+        // 3. 收集所有需要查询的ID
+        List<Long> userIds = resultPage.getRecords().stream()
+                .map(XpFleet::getOperationsUserId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+
+        List<Long> electronicIds = resultPage.getRecords().stream()
+                .map(XpFleet::getElectronicId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+
+        // 4. 批量查询负责人信息并转换为Map
+        Map<Long, XpOperationsUser> userMap = new HashMap<>();
+        if (!CollectionUtils.isEmpty(userIds)) {
+            R<List<XpOperationsUser>> usersResult = xpOperationUserClient.listByIds(userIds);
+            if (usersResult.getData() != null) {
+                userMap = usersResult.getData().stream()
+                        .collect(Collectors.toMap(XpOperationsUser::getId, user -> user));
+            }
+        }
+
+        // 5. 批量查询围栏信息并转换为Map
+        Map<Long, XpElectronicFence> fenceMap = new HashMap<>();
+        if (!CollectionUtils.isEmpty(electronicIds)) {
+            R<List<XpElectronicFence>> fencesResult = xpElectronicClient.listByIds(electronicIds);
+            if (fencesResult.getData() != null) {
+                fenceMap = fencesResult.getData().stream()
+                        .collect(Collectors.toMap(XpElectronicFence::getId, fence -> fence));
+            }
+        }
+
+        // 6. 转换为VO并填充信息
+        List<FleetPageVO> records = new ArrayList<>();
+        for (XpFleet fleet : resultPage.getRecords()) {
+            FleetPageVO vo = new FleetPageVO();
+            BeanUtils.copyProperties(fleet, vo);
+
+            // 统计人员数量
+            long userCount = xpFleetOperationsService.count(new LambdaQueryWrapper<XpFleetOperations>()
+                    .eq(XpFleetOperations::getFleetId, fleet.getId()));
+            vo.setUserCount((int) userCount);
+
+            // 统计车辆数量
+            long vehicleCount = xpFleetVehicleService.count(new LambdaQueryWrapper<XpFleetVehicle>()
+                    .eq(XpFleetVehicle::getFleetId, fleet.getId()));
+            vo.setVehicleCount((int) vehicleCount);
+
+            // 从Map中获取负责人信息
+            if (fleet.getOperationsUserId() != null) {
+                XpOperationsUser user = userMap.get(fleet.getOperationsUserId());
+                if (user != null) {
+                    vo.setOperationsUserName(user.getNickName());
+                    vo.setPhoneNumber(user.getPhoneNumber());
+                }
+            }
+
+            // 从Map中获取围栏名称
+            if (fleet.getElectronicId() != null) {
+                XpElectronicFence fence = fenceMap.get(fleet.getElectronicId());
+                if (fence != null) {
+                    vo.setElectronicName(fence.getElectronicName());
+                }
+            }
+
+            records.add(vo);
+        }
+
+        // 7. 构建分页结果
+        PageInfo<FleetPageVO> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
+        pageInfo.setRecords(records);
+        pageInfo.setTotal(resultPage.getTotal());
+        return R.ok(pageInfo);
+    }
+
+    @Override
+    public R<PageInfo<FleetVehicleVO>> queryVehiclePageList(FleetVehiclePageQuery query) {
+        // 1. 查询车队-车辆关联表
+        LambdaQueryWrapper<XpFleetVehicle> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(XpFleetVehicle::getFleetId, query.getFleetId());
+
+        Page<XpFleetVehicle> page = new Page<>(query.getPageNum(), query.getPageSize());
+        Page<XpFleetVehicle> resultPage = xpFleetVehicleService.page(page, wrapper);
+
+        // 2. 获取车辆ID列表
+        List<Long> vehicleIds = resultPage.getRecords().stream()
+                .map(XpFleetVehicle::getVehicleId)
+                .collect(Collectors.toList());
+
+        // 3. 批量查询车辆信息
+        List<FleetVehicleVO> records = new ArrayList<>();
+        if (!CollectionUtils.isEmpty(vehicleIds)) {
+            R<List<XpVehicle>> vehiclesResult = xpVehicleClient.listByIds(vehicleIds);
+            if (vehiclesResult.getData() != null) {
+                for (XpVehicle vehicle : vehiclesResult.getData()) {
+                    // 如果有车牌号筛选条件,进行过滤
+                    if (StringUtils.isNotBlank(query.getPlateNumber()) &&
+                            !vehicle.getPlateNumber().contains(query.getPlateNumber())) {
+                        continue;
+                    }
+
+                    FleetVehicleVO vo = new FleetVehicleVO();
+                    vo.setVehicleId(vehicle.getId());
+                    vo.setVehicleCode(vehicle.getVehicleCode());
+                    vo.setPlateNumber(vehicle.getPlateNumber());
+                    vo.setVehicleColor(vehicle.getVehicleColor());
+                    vo.setVehicleModel(vehicle.getVehicleModel());
+                    vo.setOperatingStatus(vehicle.getOperatingStatus());
+                    records.add(vo);
+                }
+            }
+        }
+
+        // 4. 构建分页结果
+        PageInfo<FleetVehicleVO> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
+        pageInfo.setRecords(records);
+        pageInfo.setTotal(resultPage.getTotal());
+        return R.ok(pageInfo);
+    }
+
+    @Override
+    public R<PageInfo<FleetUserVO>> queryUserPageList(FleetUserPageQuery query) {
+        // 1. 先根据条件查询符合条件的运维用户
+        List<Long> filteredUserIds = new ArrayList<>();
+        if (StringUtils.isNotBlank(query.getNickName()) ||
+                StringUtils.isNotBlank(query.getPhone()) ||
+                query.getRoleId() != null) {
+
+            // 构建查询条件
+            XpOperationsUserQueryDTO queryDTO = new XpOperationsUserQueryDTO();
+            queryDTO.setNickName(query.getNickName());
+            queryDTO.setPhoneNumber(query.getPhone());
+            queryDTO.setRoleId(query.getRoleId());
+
+            // 远程查询符合条件的用户
+            R<List<XpOperationsUserWithRoleVO>> usersResult = xpOperationUserClient.queryByConditions(queryDTO);
+            if (usersResult.getData() == null || usersResult.getData().isEmpty()) {
+                // 没有符合条件的用户,直接返回空结果
+                PageInfo<FleetUserVO> emptyPageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
+                emptyPageInfo.setRecords(new ArrayList<>());
+                emptyPageInfo.setTotal(0L);
+                return R.ok(emptyPageInfo);
+            }
+
+            // 收集用户ID列表
+            filteredUserIds = usersResult.getData().stream()
+                    .map(XpOperationsUserWithRoleVO::getId)
+                    .collect(Collectors.toList());
+        }
+
+        // 2. 查询车队-人员关联表(使用用户ID列表作为条件)
+        LambdaQueryWrapper<XpFleetOperations> wrapper = new LambdaQueryWrapper<>();
+        if (query.getFleetId() != null) {
+            wrapper.eq(XpFleetOperations::getFleetId, query.getFleetId());
+        }
+        // 如果有用户ID过滤条件,添加到查询中
+        if (!CollectionUtils.isEmpty(filteredUserIds)) {
+            wrapper.in(XpFleetOperations::getOperationsUserId, filteredUserIds);
+        }
+
+        Page<XpFleetOperations> page = new Page<>(query.getPageNum(), query.getPageSize());
+        Page<XpFleetOperations> resultPage = xpFleetOperationsService.page(page, wrapper);
+
+        // 3. 获取人员ID列表
+        List<Long> userIds = resultPage.getRecords().stream()
+                .map(XpFleetOperations::getOperationsUserId)
+                .collect(Collectors.toList());
+
+        // 4. 批量查询人员及角色信息
+        List<FleetUserVO> records = new ArrayList<>();
+        if (!CollectionUtils.isEmpty(userIds)) {
+            R<List<XpOperationsUserWithRoleVO>> usersResult = xpOperationUserClient.listByIdsWithRole(userIds);
+            if (usersResult.getData() != null) {
+                for (XpOperationsUserWithRoleVO user : usersResult.getData()) {
+                    FleetUserVO vo = new FleetUserVO();
+                    vo.setUserId(user.getId());
+                    vo.setNickName(user.getNickName());
+                    vo.setPhoneNumber(user.getPhoneNumber());
+                    vo.setJobNumber(user.getUniqueNumber());
+                    vo.setRoleName(user.getRoleName());
+                    records.add(vo);
+                }
+            }
+        }
+
+        // 5. 构建分页结果
+        PageInfo<FleetUserVO> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
+        pageInfo.setRecords(records);
+        pageInfo.setTotal(resultPage.getTotal());
+        return R.ok(pageInfo);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteById(Long id) {
+        // 删除用户关联
+        xpFleetOperationsService.lambdaUpdate().eq(XpFleetOperations::getFleetId, id).remove();
+        // 删除车辆关联
+        xpFleetVehicleService.lambdaUpdate().eq(XpFleetVehicle::getFleetId, id).remove();
+        // 删除车队
+        removeById(id);
+    }
+
+    @Override
+    public void addVehicle(Long fleetId, Long vehicleId) {
+        // 查询是否已关联
+        Long count = xpFleetVehicleService.lambdaQuery().eq(XpFleetVehicle::getFleetId, fleetId)
+                .eq(XpFleetVehicle::getVehicleId, vehicleId).count();
+        if (count > 0) {
+            throw new ServiceException("当前车队已和该车辆关联", I18nKeyConstant.FLEET_VEHICLE_BOUND_V2);
+        }
+        //保存关联关系
+        XpFleetVehicle xpFleetVehicle = new XpFleetVehicle();
+        xpFleetVehicle.setFleetId(fleetId);
+        xpFleetVehicle.setVehicleId(vehicleId);
+        xpFleetVehicleService.save(xpFleetVehicle);
+    }
+
+    @Override
+    public void addOperationUserId(Long fleetId, Long operationUserId) {
+        // 查询是否已关联
+        Long count = xpFleetOperationsService.lambdaQuery().eq(XpFleetOperations::getFleetId, fleetId)
+                .eq(XpFleetOperations::getOperationsUserId, operationUserId).count();
+        if (count > 0) {
+            throw new ServiceException("当前车队已和该人员关联", I18nKeyConstant.FLEET_OPERATION_USER_BOUND_2);
+        }
+        //保存关联
+        XpFleetOperations operations = new XpFleetOperations();
+        operations.setFleetId(fleetId);
+        operations.setOperationsUserId(operationUserId);
+        xpFleetOperationsService.save(operations);
+    }
 }

--
Gitblit v1.7.1