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; import com.xiaopeng.api.dto.account.XpOperationsUserQueryDTO; import com.xiaopeng.api.feignClient.account.XpOperationUserClient; import com.xiaopeng.api.feignClient.maintenance.XpCheckLocationClient; import com.xiaopeng.api.feignClient.operation.XpElectronicClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; import com.xiaopeng.api.model.account.SysRole; import com.xiaopeng.api.model.account.XpOperationsUser; import com.xiaopeng.api.model.maintenance.XpCheckLocation; 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.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; 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.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType; import org.apache.shardingsphere.transaction.core.TransactionType; 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.*; import java.util.stream.Collectors; /** *

* 车队管理 服务实现类 *

* * @author mitao * @since 2026-02-03 */ @Service @RequiredArgsConstructor(onConstructor_ = @Lazy) public class XpFleetServiceImpl extends ServiceImpl implements IXpFleetService { private final IXpFleetOperationsService xpFleetOperationsService; private final IXpFleetVehicleService xpFleetVehicleService; private final XpOperationUserClient xpOperationUserClient; private final XpVehicleClient xpVehicleClient; private final XpElectronicClient xpElectronicClient; private final XpCheckLocationClient xpCheckLocationClient; @Override public List listByIds(List ids) { if (CollectionUtils.isEmpty(ids)) { return new ArrayList<>(); } return super.listByIds(ids); } @Override @Transactional @ShardingSphereTransactionType(TransactionType.BASE) public R add(FleetAddDTO dto) { // 1. 校验车队名称不能重复 long count = this.count(new LambdaQueryWrapper() .eq(XpFleet::getFleetName, dto.getFleetName())); if (count > 0) { throw new ServiceException("车队名称已存在"); } // 2. 校验运维人员绑定 validateUserBinding(dto.getOperationsUserIds(), null); // 3. 处理车辆绑定(自动解绑已绑定的车辆) handleVehicleBinding(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 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 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 public R edit(FleetEditDTO dto) { // 1. 查询车队是否存在 XpFleet fleet = this.getById(dto.getId()); if (fleet == null) { throw new ServiceException("车队不存在"); } // 2. 校验车队名称不能与其他车队重复 long count = this.count(new LambdaQueryWrapper() .eq(XpFleet::getFleetName, dto.getFleetName()) .ne(XpFleet::getId, dto.getId())); if (count > 0) { throw new ServiceException("车队名称已存在"); } // 3. 校验负责人绑定(如果修改了负责人) if (!dto.getOperationsUserId().equals(fleet.getOperationsUserId())) { List 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 userIds, Long excludeFleetId) { if (CollectionUtils.isEmpty(userIds)) { return; } for (Long userId : userIds) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(XpFleetOperations::getOperationsUserId, userId); if (excludeFleetId != null) { wrapper.ne(XpFleetOperations::getFleetId, excludeFleetId); } wrapper.last("limit 1"); XpFleetOperations operations = xpFleetOperationsService.getOne(wrapper); if (operations != null) { // 查询人员姓名和车队名称 R 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 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 handleVehicleBinding(List vehicleIds, Long excludeFleetId) { if (CollectionUtils.isEmpty(vehicleIds)) { return; } for (Long vehicleId : vehicleIds) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(XpFleetVehicle::getVehicleId, vehicleId); if (excludeFleetId != null) { wrapper.ne(XpFleetVehicle::getFleetId, excludeFleetId); } wrapper.last("limit 1"); XpFleetVehicle fleetVehicle = xpFleetVehicleService.getOne(wrapper); if (fleetVehicle != null) { // 车辆已绑定其他车队,自动解绑 xpFleetVehicleService.lambdaUpdate().set(XpFleetVehicle::getVehicleId, fleetVehicle.getVehicleId()).remove(); } } } @Override public R 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. 统计人员数量(过滤已删除人员) List bindUserIds = xpFleetOperationsService.lambdaQuery() .eq(XpFleetOperations::getFleetId, id) .list() .stream() .map(XpFleetOperations::getOperationsUserId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); int userCount = 0; if (!CollectionUtils.isEmpty(bindUserIds)) { try { R> usersResult = xpOperationUserClient.listByIds(bindUserIds); if (usersResult != null && usersResult.getData() != null) { userCount = usersResult.getData().size(); } else { userCount = bindUserIds.size(); } } catch (Exception e) { userCount = bindUserIds.size(); } } vo.setUserCount(userCount); // 4. 统计车辆数量(过滤已删除车辆) List bindVehicleIds = xpFleetVehicleService.lambdaQuery() .eq(XpFleetVehicle::getFleetId, id) .list() .stream() .map(XpFleetVehicle::getVehicleId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); int vehicleCount = 0; if (!CollectionUtils.isEmpty(bindVehicleIds)) { try { R> vehiclesResult = xpVehicleClient.listByIds(bindVehicleIds); if (vehiclesResult != null && vehiclesResult.getData() != null) { vehicleCount = vehiclesResult.getData().size(); } else { vehicleCount = bindVehicleIds.size(); } } catch (Exception e) { vehicleCount = bindVehicleIds.size(); } } vo.setVehicleCount(vehicleCount); // 5. 查询负责人信息 if (fleet.getOperationsUserId() != null) { R userResult = xpOperationUserClient.queryById(fleet.getOperationsUserId()); if (userResult.getData() != null) { vo.setOperationsUserName(userResult.getData().getNickName()); vo.setPhoneNumber(userResult.getData().getPhoneNumber()); } } // 6. 查询打卡地信息 if (fleet.getLocationId() != null) { R locationResult = xpCheckLocationClient.queryById(fleet.getLocationId()); if (locationResult.getData() != null) { vo.setLocationName(locationResult.getData().getLocationAddress()); } } return R.ok(vo); } @Override public R> pageList(FleetPageQuery query) { // 1. 构建查询条件 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); if (StringUtils.isNotBlank(query.getFleetName())) { wrapper.like(XpFleet::getFleetName, query.getFleetName()); } wrapper.orderByDesc(XpFleet::getCreateTime); // 2. 执行分页查询 Page page = new Page<>(query.getPageNum(), query.getPageSize()); Page resultPage = this.page(page, wrapper); // 3. 收集所有需要查询的ID List userIds = resultPage.getRecords().stream() .map(XpFleet::getOperationsUserId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); List locationIds = resultPage.getRecords().stream() .map(XpFleet::getLocationId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); // 4. 批量查询负责人信息并转换为Map Map userMap = new HashMap<>(); if (!CollectionUtils.isEmpty(userIds)) { R> usersResult = xpOperationUserClient.listByIds(userIds); if (usersResult.getData() != null) { userMap = usersResult.getData().stream() .collect(Collectors.toMap(XpOperationsUser::getId, user -> user)); } } // 5. 批量查询打卡地信息并转换为Map Map locationNameMap = Collections.emptyMap(); if (!CollectionUtils.isEmpty(locationIds)) { R> locationsResult = xpCheckLocationClient.listByIds(locationIds); if (locationsResult != null && locationsResult.getData() != null) { locationNameMap = locationsResult.getData().stream() .filter(Objects::nonNull) .filter(location -> location.getId() != null && location.getLocationAddress() != null) .collect(Collectors.toMap( XpCheckLocation::getId, XpCheckLocation::getLocationAddress, (a, b) -> a )); } } // 6. 转换为VO并填充信息 List records = new ArrayList<>(); for (XpFleet fleet : resultPage.getRecords()) { FleetPageVO vo = new FleetPageVO(); BeanUtils.copyProperties(fleet, vo); // 统计人员数量 long userCount = xpFleetOperationsService.count(new LambdaQueryWrapper() .eq(XpFleetOperations::getFleetId, fleet.getId())); vo.setUserCount((int) userCount); // 统计车辆数量 long vehicleCount = xpFleetVehicleService.count(new LambdaQueryWrapper() .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.getLocationId() != null) { vo.setLocationName(locationNameMap.get(fleet.getLocationId())); } records.add(vo); } // 7. 构建分页结果 PageInfo pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); pageInfo.setRecords(records); pageInfo.setTotal(resultPage.getTotal()); return R.ok(pageInfo); } @Override public R> queryVehiclePageList(FleetVehiclePageQuery query) { // 1. 查询车队-车辆关联表 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(XpFleetVehicle::getFleetId, query.getFleetId()); Page page = new Page<>(query.getPageNum(), query.getPageSize()); Page resultPage = xpFleetVehicleService.page(page, wrapper); // 2. 获取车辆ID列表 List vehicleIds = resultPage.getRecords().stream() .map(XpFleetVehicle::getVehicleId) .collect(Collectors.toList()); // 3. 批量查询车辆信息 List records = new ArrayList<>(); if (!CollectionUtils.isEmpty(vehicleIds)) { R> vehiclesResult = xpVehicleClient.listByIds(vehicleIds); if (vehiclesResult.getData() != null) { List electronicIds = vehiclesResult.getData().stream() .map(XpVehicle::getElectronicId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); Map electronicNameMap = new HashMap<>(); if (CollUtil.isNotEmpty(electronicIds)) { List 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()) && !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()); vo.setProcurementSource(vehicle.getProcurementSource()); vo.setElectronicId(vehicle.getElectronicId()); vo.setElectronicName(electronicNameMap.get(vehicle.getElectronicId())); records.add(vo); } } } // 4. 构建分页结果 PageInfo pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); pageInfo.setRecords(records); pageInfo.setTotal(resultPage.getTotal()); return R.ok(pageInfo); } @Override public R> queryUserPageList(FleetUserPageQuery query) { // 1. 先根据条件查询符合条件的运维用户 List 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> usersResult = xpOperationUserClient.queryByConditions(queryDTO); if (usersResult.getData() == null || usersResult.getData().isEmpty()) { // 没有符合条件的用户,直接返回空结果 PageInfo 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 wrapper = new LambdaQueryWrapper<>(); if (query.getFleetId() != null) { wrapper.eq(XpFleetOperations::getFleetId, query.getFleetId()); } // 如果有用户ID过滤条件,添加到查询中 if (!CollectionUtils.isEmpty(filteredUserIds)) { wrapper.in(XpFleetOperations::getOperationsUserId, filteredUserIds); } Page page = new Page<>(query.getPageNum(), query.getPageSize()); Page resultPage = xpFleetOperationsService.page(page, wrapper); // 3. 获取人员ID列表 List userIds = resultPage.getRecords().stream() .map(XpFleetOperations::getOperationsUserId) .collect(Collectors.toList()); // 4. 批量查询人员及角色信息 List records = new ArrayList<>(); if (!CollectionUtils.isEmpty(userIds)) { R> 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.getRoles().stream().map(SysRole::getRoleName).collect(Collectors.joining(","))); records.add(vo); } } } // 5. 构建分页结果 PageInfo pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); pageInfo.setRecords(records); pageInfo.setTotal(resultPage.getTotal()); return R.ok(pageInfo); } @Override @Transactional @ShardingSphereTransactionType(TransactionType.BASE) public void deleteById(Long id) { long vehicleCount = xpFleetVehicleService.count(new LambdaQueryWrapper() .eq(XpFleetVehicle::getFleetId, id)); long userCount = xpFleetOperationsService.count(new LambdaQueryWrapper() .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(); // 删除车辆关联 xpFleetVehicleService.lambdaUpdate().eq(XpFleetVehicle::getFleetId, id).remove(); // 删除车队 removeById(id); } @Override public void addVehicle(Long fleetId, Long vehicleId) { // 1. 检查是否已关联当前车队 Long count = xpFleetVehicleService.lambdaQuery().eq(XpFleetVehicle::getFleetId, fleetId) .eq(XpFleetVehicle::getVehicleId, vehicleId).count(); if (count > 0) { throw new ServiceException("当前车队已和该车辆关联", I18nKeyConstant.FLEET_VEHICLE_BOUND_V2); } // 2. 检查是否已绑定其他车队,如果是则自动解绑 XpFleetVehicle existingBinding = xpFleetVehicleService.lambdaQuery() .eq(XpFleetVehicle::getVehicleId, vehicleId) .ne(XpFleetVehicle::getFleetId, fleetId) .one(); if (existingBinding != null) { // 车辆已绑定其他车队,自动解绑 xpFleetVehicleService.lambdaUpdate().eq(XpFleetVehicle::getVehicleId, vehicleId).remove(); } // 3. 保存新的关联关系 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); } @Override public void removeVehicle(Long fleetId, Long vehicleId) { // 直接删除关联关系(幂等性:不存在也不报错) xpFleetVehicleService.lambdaUpdate() .eq(XpFleetVehicle::getFleetId, fleetId) .eq(XpFleetVehicle::getVehicleId, vehicleId) .remove(); } @Override public void removeOperationUser(Long fleetId, Long operationUserId) { // 直接删除关联关系(幂等性:不存在也不报错) xpFleetOperationsService.lambdaUpdate() .eq(XpFleetOperations::getFleetId, fleetId) .eq(XpFleetOperations::getOperationsUserId, operationUserId) .remove(); } }