“mitao”
2026-02-12 469bff1ff58285d21806d65da228cfdbc32b79b1
feat(management):管理后台-车队管理
14个文件已添加
24个文件已修改
1534 ■■■■■ 已修改文件
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpOperationsUserController.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/mapper/XpOperationsRoleMapper.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/resources/mapper/account/XpOperationsRoleMapper.xml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/account/XpOperationsUserQueryDTO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/account/XpOperationUserClientFallbackFactory.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/account/XpOperationUserClient.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/maintenance/XpTask.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/account/XpOperationsUserWithRoleVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/enums/TaskTypeEnum.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpChargingRecordController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpCheckLocationController.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpTaskController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpChargingRecordService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/XpTaskService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpChargingRecordServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpTaskServiceImpl.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/constant/I18nKeyConstant.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpFleetController.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/mapper/XpVehicleMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/dto/FleetAddDTO.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/dto/FleetEditDTO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/query/FleetPageQuery.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/query/FleetUserPageQuery.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/query/FleetVehiclePageQuery.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/FleetDetailVO.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/FleetPageVO.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/FleetUserVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/FleetVehicleVO.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/XpVehicleVO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/IXpFleetService.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/IXpVehicleService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpFleetServiceImpl.java 475 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/resources/i18n/messages.properties 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/resources/i18n/messages_en_US.properties 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/resources/i18n/messages_zh_CN.properties 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/resources/mapper/vehicleConvoy/XpVehicleMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpOperationsUserController.java
@@ -1,12 +1,18 @@
package com.xiaopeng.account.controller;
import com.xiaopeng.account.mapper.XpOperationsRoleMapper;
import com.xiaopeng.account.service.IXpOperationsUserService;
import com.xiaopeng.api.dto.account.XpOperationsUserQueryDTO;
import com.xiaopeng.api.model.account.XpOperationsUser;
import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO;
import com.xiaopeng.common.core.domain.R;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
@@ -23,9 +29,12 @@
    private final IXpOperationsUserService xpOperationsUserService;
    private final XpOperationsRoleMapper xpOperationsRoleMapper;
    @Autowired
    public XpOperationsUserController(IXpOperationsUserService xpOperationsUserService) {
    public XpOperationsUserController(IXpOperationsUserService xpOperationsUserService, XpOperationsRoleMapper xpOperationsRoleMapper) {
        this.xpOperationsUserService = xpOperationsUserService;
        this.xpOperationsRoleMapper = xpOperationsRoleMapper;
    }
    /**
@@ -55,4 +64,51 @@
    public R<List<XpOperationsUser>> listByName(@RequestBody String nickName) {
        return R.ok(xpOperationsUserService.listByName(nickName));
    }
    /**
     * 根据用户ID列表批量查询用户及角色信息(内部接口)
     * @param ids 用户ID列表
     * @return 用户及角色信息列表
     */
    @ApiOperation(value = "批量查询用户及角色信息", tags = {"内部接口"})
    @PostMapping("/inner/xpOperationsUser/listByIdsWithRole")
    public R<List<XpOperationsUserWithRoleVO>> listByIdsWithRole(@RequestBody List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return R.ok(new ArrayList<>());
        }
        List<XpOperationsUserWithRoleVO> result = xpOperationsRoleMapper.selectUsersWithRoleByIds(ids);
        return R.ok(result);
    }
    /**
     * 根据条件查询用户及角色信息(内部接口)
     * @param dto 查询条件
     * @return 用户及角色信息列表
     */
    @ApiOperation(value = "根据条件查询用户及角色信息", tags = {"内部接口"})
    @PostMapping("/inner/xpOperationsUser/queryByConditions")
    public R<List<XpOperationsUserWithRoleVO>> queryByConditions(@RequestBody XpOperationsUserQueryDTO dto) {
        List<XpOperationsUserWithRoleVO> result = xpOperationsRoleMapper.selectUsersByConditions(
                dto.getNickName(),
                dto.getPhoneNumber(),
                dto.getRoleId()
        );
        return R.ok(result);
    }
    /**
     * 根据条件查询用户及角色信息
     * @param dto 查询条件
     * @return 用户及角色信息列表
     */
    @ApiOperation(value = "根据条件查询用户不分页列表", tags = {"管理后台-车队管理"})
    @PostMapping("/api/v1/xpOperationsUser/queryByConditions")
    public R<List<XpOperationsUserWithRoleVO>> getList(@RequestBody XpOperationsUserQueryDTO dto) {
        List<XpOperationsUserWithRoleVO> result = xpOperationsRoleMapper.selectUsersByConditions(
                dto.getNickName(),
                dto.getPhoneNumber(),
                dto.getRoleId()
        );
        return R.ok(result);
    }
}
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/mapper/XpOperationsRoleMapper.java
@@ -2,6 +2,10 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiaopeng.api.model.account.XpOperationsRole;
import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * <p>
@@ -13,4 +17,25 @@
 */
public interface XpOperationsRoleMapper extends BaseMapper<XpOperationsRole> {
    /**
     * 根据用户ID列表查询用户及角色信息
     *
     * @param ids 用户ID列表
     * @return 用户及角色信息列表
     */
    List<XpOperationsUserWithRoleVO> selectUsersWithRoleByIds(@Param("ids") List<Long> ids);
    /**
     * 根据条件查询用户及角色信息
     *
     * @param nickName 姓名(模糊查询)
     * @param phoneNumber 手机号(模糊查询)
     * @param roleId 角色ID(精确查询)
     * @return 用户及角色信息列表
     */
    List<XpOperationsUserWithRoleVO> selectUsersByConditions(
            @Param("nickName") String nickName,
            @Param("phoneNumber") String phoneNumber,
            @Param("roleId") Long roleId
    );
}
xp-robotaxi-account-boot/src/main/resources/mapper/account/XpOperationsRoleMapper.xml
@@ -7,4 +7,40 @@
        operations_user_id, role_id
    </sql>
    <!-- 根据用户ID列表查询用户及角色信息 -->
    <select id="selectUsersWithRoleByIds" resultType="com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO">
        SELECT
            u.*,
            r.id AS roleId,
            r.role_name AS roleName
        FROM xp_operations_user u
        LEFT JOIN xp_operations_role ur ON u.id = ur.operations_user_id
        LEFT JOIN sys_role r ON ur.role_id = r.id AND r.is_deleted = 0
        WHERE u.id IN
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </select>
    <!-- 根据条件查询用户及角色信息 -->
    <select id="selectUsersByConditions" resultType="com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO">
        SELECT
            u.*,
            r.id AS roleId,
            r.role_name AS roleName
        FROM xp_operations_user u
        LEFT JOIN xp_operations_role ur ON u.id = ur.operations_user_id
        LEFT JOIN sys_role r ON ur.role_id = r.id AND r.is_deleted = 0
        WHERE 1=1
        <if test="nickName != null and nickName != ''">
            AND u.nick_name LIKE CONCAT('%', #{nickName}, '%')
        </if>
        <if test="phoneNumber != null and phoneNumber != ''">
            AND u.phone_number LIKE CONCAT('%', #{phoneNumber}, '%')
        </if>
        <if test="roleId != null">
            AND r.id = #{roleId}
        </if>
    </select>
</mapper>
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/account/XpOperationsUserQueryDTO.java
New file
@@ -0,0 +1,24 @@
package com.xiaopeng.api.dto.account;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * 运维人员查询条件DTO
 *
 * @author mitao
 */
@Data
@ApiModel("运维人员查询条件DTO")
public class XpOperationsUserQueryDTO {
    @ApiModelProperty(value = "姓名(模糊查询)")
    private String nickName;
    @ApiModelProperty(value = "手机号(模糊查询)")
    private String phoneNumber;
    @ApiModelProperty(value = "角色ID(精确查询)")
    private Long roleId;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/account/XpOperationUserClientFallbackFactory.java
@@ -1,7 +1,9 @@
package com.xiaopeng.api.factory.account;
import com.xiaopeng.api.dto.account.XpOperationsUserQueryDTO;
import com.xiaopeng.api.feignClient.account.XpOperationUserClient;
import com.xiaopeng.api.model.account.XpOperationsUser;
import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO;
import com.xiaopeng.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
@@ -34,8 +36,21 @@
            @Override
            public R<List<XpOperationsUser>> listByName(String nickName) {
                log.error("根据昵称查询运维用户信息失败,原因:{}", cause.getMessage());
                return R.fail("根据昵称查询运维用户信息失败:" + cause.getMessage());
            }
            @Override
            public R<List<XpOperationsUserWithRoleVO>> listByIdsWithRole(List<Long> ids) {
                log.error("根据id列表获取运维人员列表失败,原因:{}", cause.getMessage());
                return R.fail("根据id列表获取运维人员列表失败");
            }
            @Override
            public R<List<XpOperationsUserWithRoleVO>> queryByConditions(XpOperationsUserQueryDTO dto) {
                log.error("根据条件查询运维用户信息失败,原因:{}", cause.getMessage());
                return R.fail("根据条件查询运维用户信息失败:" + cause.getMessage());
            }
        };
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/account/XpOperationUserClient.java
@@ -1,7 +1,9 @@
package com.xiaopeng.api.feignClient.account;
import com.xiaopeng.api.dto.account.XpOperationsUserQueryDTO;
import com.xiaopeng.api.factory.account.XpAppUserClientFallbackFactory;
import com.xiaopeng.api.model.account.XpOperationsUser;
import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum;
import org.springframework.cloud.openfeign.FeignClient;
@@ -43,4 +45,20 @@
     */
    @PostMapping("/inner/xpOperationsUser/listByName")
    R<List<XpOperationsUser>> listByName(@RequestParam("nickName") String nickName);
    /**
     * 根据用户ID列表批量查询用户及角色信息
     * @param ids 用户ID列表
     * @return 用户及角色信息列表
     */
    @PostMapping("/inner/xpOperationsUser/listByIdsWithRole")
    R<List<XpOperationsUserWithRoleVO>> listByIdsWithRole(@RequestBody List<Long> ids);
    /**
     * 根据条件查询用户及角色信息
     * @param dto 查询条件
     * @return 用户及角色信息列表
     */
    @PostMapping("/inner/xpOperationsUser/queryByConditions")
    R<List<XpOperationsUserWithRoleVO>> queryByConditions(@RequestBody XpOperationsUserQueryDTO dto);
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/maintenance/XpTask.java
@@ -1,18 +1,17 @@
package com.xiaopeng.api.model.maintenance;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xiaopeng.common.core.web.domain.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * <p>
@@ -40,7 +39,7 @@
    @ApiModelProperty(value = "任务类别1常规 2事故")
    private Integer taskCategory;
//1:车辆检查,2:车辆充电,3:车辆整备,4:整备洗车,5:车辆维修,6:车辆保养,7:车辆保险到期,8:车辆年检到期9:车辆运营资质到期
    @ApiModelProperty(value = "任务类型id 任务类别为2时对应数据字典,多个逗号分割,为1的时候 枚举1车辆检查 2车辆充电 3车辆整备 4整备洗车 5车辆维修 6车辆保养 7车辆保险到期 8车辆年检到期 9车辆运营资质到期")
    private String taskTypeId;
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/account/XpOperationsUserWithRoleVO.java
New file
@@ -0,0 +1,24 @@
package com.xiaopeng.api.vo.account;
import com.xiaopeng.api.model.account.XpOperationsUser;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * 运维人员及角色信息视图对象
 *
 * @author mitao
 */
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel("运维人员及角色信息视图对象")
public class XpOperationsUserWithRoleVO extends XpOperationsUser {
    @ApiModelProperty(value = "角色id")
    private Long roleId;
    @ApiModelProperty(value = "角色名称")
    private String roleName;
}
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/enums/TaskTypeEnum.java
New file
@@ -0,0 +1,38 @@
package com.xiaopeng.common.core.enums;
import lombok.Getter;
import lombok.AllArgsConstructor;
@Getter
@AllArgsConstructor
public enum TaskTypeEnum {
    VEHICLE_INSPECTION(1, "车辆检查"),
    VEHICLE_CHARGING(2, "车辆充电"),
    VEHICLE_PREPARATION(3, "车辆整备"),
    VEHICLE_WASH(4, "整备洗车"),
    VEHICLE_REPAIR(5, "车辆维修"),
    VEHICLE_MAINTENANCE(6, "车辆保养"),
    VEHICLE_INSURANCE_EXPIRE(7, "车辆保险到期"),
    ANNUAL_INSPECTION_EXPIRE(8, "车辆年检到期"),
    LICENSE_EXPIRE(9,"车辆运营资质到期");
    private final Integer code;
    private final String desc;
    public static TaskTypeEnum getEnumByCode(Integer code) {
        for (TaskTypeEnum e : TaskTypeEnum.values()) {
            if (e.code.equals(code)) {
                return e;
            }
        }
        return null;
    }
    public static String getEnumDescByCode(Integer code) {
        for (TaskTypeEnum e : TaskTypeEnum.values()) {
            if (e.code.equals(code)) {
                return e.getDesc();
            }
        }
        return "";
    }
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpChargingRecordController.java
@@ -86,5 +86,17 @@
            @Valid @RequestBody XpChargingRecordPageQuery query) {
        return R.ok(xpChargingRecordService.queryChargingByVehicleId(query));
    }
    /**
     * 管理后台-车辆管理-运营车辆管理-删除充电记录
     * @param id
     * @return
     */
    @ApiOperation(value = "删除充电记录", tags = {"管理后台-车辆管理-运营车辆管理"})
    @DeleteMapping("/api/v1/chargingRecord/delete/{id}")
    public R<?> deleteRecordById(@PathVariable Long id){
        xpChargingRecordService.deleteRecordById(id);
        return R.ok();
    }
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpCheckLocationController.java
New file
@@ -0,0 +1,32 @@
package com.xiaopeng.maintenance.controller;
import com.xiaopeng.api.model.maintenance.XpCheckLocation;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.DisabledEnum;
import com.xiaopeng.maintenance.service.IXpCheckLocationService;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
 * @author MiTao
 * @date 2026-02-12 21:17
 *
 */
@Validated
@RestController
@RequiredArgsConstructor(onConstructor_ = @Lazy)
public class XpCheckLocationController {
    private final IXpCheckLocationService xpCheckLocationService;
    @GetMapping("/api/v1/check-location/list")
    @ApiOperation(value = "获取打卡地址列表", tags = {"管理后台-车队管理"})
    public  R<List<XpCheckLocation>> getList(){
        return R.ok(xpCheckLocationService.lambdaQuery().eq(XpCheckLocation::getStatus, DisabledEnum.YES.getCode()).list());
    }
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpTaskController.java
@@ -20,12 +20,10 @@
import com.xiaopeng.maintenance.service.XpTaskService;
import com.xiaopeng.maintenance.vo.VehicleListVO;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
@@ -171,4 +169,16 @@
    public R<PageInfo<XpTaskRecordVO>> queryTaskByVehicleId(@Valid @RequestBody XpTaskRecordPageQuery query) {
        return R.ok(xpTaskService.queryTaskByVehicleId(query));
    }
    /**
     * 管理后台-车辆管理-运营车辆管理-删除任务/事故
     * @param id
     * @return
     */
    @ApiOperation(value = "删除删除任务/事故", tags = {"管理后台-车辆管理-运营车辆管理"})
    @DeleteMapping("/api/v1/task/delete/{id}")
    public R<?> deleteTaskById(@ApiParam(name = "id", value = "任务ID") @PathVariable Long id){
        xpTaskService.deleteTaskById(id);
        return R.ok();
    }
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpChargingRecordService.java
@@ -49,4 +49,10 @@
     * @return 充电记录分页列表
     */
    PageInfo<XpChargingRecordVO> queryChargingByVehicleId(XpChargingRecordPageQuery query);
    /**
     * 根据ID删除充电记录
     * @param id
     */
    void deleteRecordById(Long id);
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/XpTaskService.java
@@ -57,4 +57,10 @@
     * @return
     */
    PageInfo<XpTaskRecordVO> queryTaskByVehicleId(@Valid XpTaskRecordPageQuery query);
    /**
     * 删除任务/事故
     * @param id
     */
    void deleteTaskById(Long id);
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpChargingRecordServiceImpl.java
@@ -186,4 +186,9 @@
        pageInfo.setTotal(resultPage.getTotal());
        return pageInfo;
    }
    @Override
    public void deleteRecordById(Long id) {
        removeById(id);
    }
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpTaskServiceImpl.java
@@ -1,6 +1,7 @@
package com.xiaopeng.maintenance.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.feignClient.account.XpOperationUserClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
@@ -8,6 +9,7 @@
import com.xiaopeng.api.model.maintenance.*;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.TaskTypeEnum;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.maintenance.constant.I18nKeyConstant;
@@ -20,15 +22,16 @@
import com.xiaopeng.maintenance.model.query.XpTaskRecordPageQuery;
import com.xiaopeng.maintenance.model.vo.*;
import com.xiaopeng.maintenance.service.XpTaskService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
@@ -41,6 +44,22 @@
 */
@Service
public class XpTaskServiceImpl extends ServiceImpl<XpTaskMapper, XpTask> implements XpTaskService {
    /**
     * 常规任务类型映射
     */
    private static final Map<String, String> TASK_TYPE_MAP = new java.util.HashMap<>();
    static {
        TASK_TYPE_MAP.put("1", "车辆检查");
        TASK_TYPE_MAP.put("2", "车辆充电");
        TASK_TYPE_MAP.put("3", "车辆整备");
        TASK_TYPE_MAP.put("4", "整备洗车");
        TASK_TYPE_MAP.put("5", "车辆维修");
        TASK_TYPE_MAP.put("6", "车辆保养");
        TASK_TYPE_MAP.put("7", "车辆保险到期");
        TASK_TYPE_MAP.put("8", "车辆年检到期");
        TASK_TYPE_MAP.put("9", "车辆运营资质到期");
    }
    @Autowired
    private XpTaskHandleDetailMapper xpTaskHandleDetailMapper;
@@ -581,6 +600,101 @@
    @Override
    public PageInfo<XpTaskRecordVO> queryTaskByVehicleId(XpTaskRecordPageQuery query) {
        return null;
        // 1. 构建查询条件
        LambdaQueryWrapper<XpTask> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(XpTask::getVehicleId, query.getVehicleId());
        if (query.getTaskCategory() != null) {
            wrapper.eq(XpTask::getTaskCategory, query.getTaskCategory());
        }
        if (StringUtils.isNotBlank(query.getTaskTypeId())) {
            wrapper.eq(XpTask::getTaskTypeId, query.getTaskTypeId());
        }
        if (query.getStartTime() != null) {
            wrapper.ge(XpTask::getCreateTime, query.getStartTime().atStartOfDay());
        }
        if (query.getEndTime() != null) {
            wrapper.le(XpTask::getCreateTime, query.getEndTime().atTime(23, 59, 59));
        }
        wrapper.orderByDesc(XpTask::getCreateTime);
        // 2. 执行分页查询
        Page<XpTask> page = new Page<>(query.getPageNum(), query.getPageSize());
        Page<XpTask> resultPage = this.page(page, wrapper);
        // 3. 转换为 VO 并填充 typeName
        List<XpTaskRecordVO> records = new ArrayList<>();
        if (!CollectionUtils.isEmpty(resultPage.getRecords())) {
            // 3.1 收集所有事故任务的 errorType ID
            Set<Long> errorTypeIds = new HashSet<>();
            for (XpTask task : resultPage.getRecords()) {
                if (task.getTaskCategory() != null && task.getTaskCategory() == 2
                    && StringUtils.isNotBlank(task.getTaskTypeId())) {
                    String[] ids = task.getTaskTypeId().split(",");
                    for (String id : ids) {
                        try {
                            errorTypeIds.add(Long.parseLong(id.trim()));
                        } catch (NumberFormatException e) {
                            // 忽略格式错误的ID
                        }
                    }
                }
            }
            // 3.2 批量查询 errorType
            Map<Long, String> errorTypeMap = new HashMap<>();
            if (!errorTypeIds.isEmpty()) {
                List<XpErrorType> errorTypes = errorTypeMapper.selectBatchIds(errorTypeIds);
                for (XpErrorType errorType : errorTypes) {
                    errorTypeMap.put(errorType.getId(), errorType.getErrorName());
                }
            }
            // 3.3 填充 typeName
            for (XpTask task : resultPage.getRecords()) {
                XpTaskRecordVO vo = new XpTaskRecordVO();
                BeanUtils.copyProperties(task, vo);
                // 填充 typeName
                if (task.getTaskCategory() != null && task.getTaskCategory() == 1) {
                    // 常规任务: 使用枚举映射
                    vo.setTypeName(TaskTypeEnum.getEnumDescByCode(Integer.valueOf(task.getTaskTypeId())));
                } else if (task.getTaskCategory() != null && task.getTaskCategory() == 2) {
                    // 事故任务: 拼接父子级名称
                    if (StringUtils.isNotBlank(task.getTaskTypeId())) {
                        String[] ids = task.getTaskTypeId().split(",");
                        if (ids.length >= 2) {
                            try {
                                String parentName = errorTypeMap.get(Long.parseLong(ids[0].trim()));
                                String childName = errorTypeMap.get(Long.parseLong(ids[1].trim()));
                                if (parentName != null && childName != null) {
                                    vo.setTypeName(parentName + "->" + childName);
                                }
                            } catch (NumberFormatException e) {
                                // 忽略格式错误的ID
                            }
                        }
                    }
                }
                records.add(vo);
            }
        }
        // 4. 构建分页结果
        PageInfo<XpTaskRecordVO> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        pageInfo.setRecords(records);
        pageInfo.setTotal(resultPage.getTotal());
        return pageInfo;
    }
    @Override
    public void deleteTaskById(Long id) {
        removeById(id);
    }
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/constant/I18nKeyConstant.java
@@ -19,4 +19,10 @@
    String VEHICLE_OPERATION_INFO_INCOMPLETE = "vehicle.operation.info.incomplete";
    String VEHICLE_BATCH_OPERATION_CONDITION_NOT_MET = "vehicle.batch.operation.condition.not.met";
    String VEHICLE_TIME_EXPIRED = "vehicle.time.expired";
    // ========== 车队相关 ==========
    String FLEET_OPERATION_USER_BOUND_V1 = "fleet.operation.user.bound.v1";
    String FLEET_OPERATION_USER_BOUND_2 = "fleet.operation.user.bound.v2";
    String FLEET_VEHICLE_BOUND_V1 = "fleet.vehicle.bound.v1";
    String FLEET_VEHICLE_BOUND_V2 = "fleet.vehicle.bound.v2";
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpFleetController.java
@@ -2,15 +2,21 @@
import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.web.page.PageInfo;
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.*;
import com.xiaopeng.vehicleConvoy.service.IXpFleetService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.*;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
@@ -22,12 +28,12 @@
 * @author mitao
 * @since 2026-02-03
 */
@RestController
@Api(tags = "车队管理")
@RequiredArgsConstructor(onConstructor_ = @Lazy)
@RestController
public class XpFleetController {
    @Resource
    private IXpFleetService xpFleetService;
    private final IXpFleetService xpFleetService;
    /**
     * 根据ID列表批量查询车队信息(内部接口)
@@ -43,4 +49,104 @@
        }
        return R.ok(xpFleetService.listByIds(ids));
    }
    /**
     * 添加车队
     *
     * @param dto 添加车队DTO
     * @return 操作结果
     */
    @ApiOperation(value = "添加车队", tags = {"管理后台-车队管理"})
    @PostMapping("/api/v1/fleet/add")
    public R<Void> add(@Valid @RequestBody FleetAddDTO dto) {
        return xpFleetService.add(dto);
    }
    /**
     * 编辑车队
     *
     * @param dto 编辑车队DTO
     * @return 操作结果
     */
    @ApiOperation(value = "编辑车队", tags = {"管理后台-车队管理"}, notes = "打卡地列表接口在xp-robotaxi-maintenance-boot,人员列表在xp-robotaxi-account-boot")
    @PostMapping("/api/v1/fleet/edit")
    public R<Void> edit(@Valid @RequestBody FleetEditDTO dto) {
        return xpFleetService.edit(dto);
    }
    /**
     * 查询车队详情
     *
     * @param id 车队id
     * @return 车队详情
     */
    @ApiOperation(value = "查询车队详情", tags = {"管理后台-车队管理"})
    @GetMapping("/api/v1/fleet/queryById")
    public R<FleetDetailVO> queryById(@RequestParam Long id) {
        return xpFleetService.queryById(id);
    }
    /**
     * 车队分页列表
     *
     * @param query 查询参数
     * @return 分页结果
     */
    @ApiOperation(value = "车队分页列表", tags = {"管理后台-车队管理"})
    @PostMapping("/api/v1/fleet/pageList")
    public R<PageInfo<FleetPageVO>> pageList(@Valid @RequestBody FleetPageQuery query) {
        return xpFleetService.pageList(query);
    }
    /**
     * 查询车队车辆分页列表
     *
     * @param query 查询参数
     * @return 分页结果
     */
    @ApiOperation(value = "查询车队车辆分页列表", tags = {"管理后台-车队管理"})
    @PostMapping("/api/v1/fleet/queryVehiclePageList")
    public R<PageInfo<FleetVehicleVO>> queryVehiclePageList(@Valid @RequestBody FleetVehiclePageQuery query) {
        return xpFleetService.queryVehiclePageList(query);
    }
    /**
     * 查询车队人员分页列表
     *
     * @param query 查询参数
     * @return 分页结果
     */
    @ApiOperation(value = "查询车队人员分页列表", tags = {"管理后台-车队管理"})
    @PostMapping("/api/v1/fleet/queryUserPageList")
    public R<PageInfo<FleetUserVO>> queryUserPageList(@Valid @RequestBody FleetUserPageQuery query) {
        return xpFleetService.queryUserPageList(query);
    }
    @ApiOperation(value = "删除车队", tags = {"管理后台-车队管理"})
    @DeleteMapping("/api/v1/fleet/delete/{id}")
    public R<?> deleteById(@ApiParam(name = "id", value = "车队ID") @PathVariable Long id) {
        xpFleetService.deleteById(id);
        return R.ok();
    }
    @ApiOperation(value = "车队添加车辆成员", tags = {"管理后台-车队管理"})
    @PostMapping("/api/v1/fleet/vehicle/add")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fleetId", value = "车队ID", dataTypeClass = Long.class),
            @ApiImplicitParam(name = "vehicleId", value = "车辆ID", dataTypeClass = Long.class)})
    public R<?> addVehicle(Long fleetId, Long vehicleId) {
        xpFleetService.addVehicle(fleetId, vehicleId);
        return R.ok();
    }
    @ApiOperation(value = "车队添加人员成员", tags = {"管理后台-车队管理"})
    @PostMapping("/api/v1/fleet/operation-user/add")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fleetId", value = "车队ID", dataTypeClass = Long.class),
            @ApiImplicitParam(name = "operationUserId", value = "车辆ID", dataTypeClass = Long.class)})
    public R<?> addOperationUserId(Long fleetId, Long operationUserId) {
        xpFleetService.addOperationUserId(fleetId, operationUserId);
        return R.ok();
    }
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -8,14 +8,11 @@
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.xiaopeng.api.dto.vehicleConvoy.VehicleTemperatureDto;
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.api.model.order.XpOrder;
import com.xiaopeng.api.feignClient.maintenance.XpChargingRecordClient;
import com.xiaopeng.api.feignClient.maintenance.XpVehiclePreparationClient;
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicleAnnualRecord;
import com.xiaopeng.api.vo.vehicleConvoy.VehicleTemperatureVo;
import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandCallbackVo;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
@@ -26,12 +23,10 @@
import com.xiaopeng.vehicleConvoy.model.exportExcel.XpVehicleExportExcel;
import com.xiaopeng.vehicleConvoy.model.importExcel.XpVehicleImportExcel;
import com.xiaopeng.vehicleConvoy.model.query.*;
import com.xiaopeng.vehicleConvoy.model.vo.XpVehicleVO;
import com.xiaopeng.vehicleConvoy.service.IXpVehicleAnnualRecordService;
import com.xiaopeng.vehicleConvoy.service.IXpVehicleService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -470,5 +465,12 @@
        VehicleTemperatureDto vehicleTemperatureDto = xpVehicleService.queryTemperatureByOrderId(orderId,uid);
        return R.ok(vehicleTemperatureDto);
    }
    @ApiOperation(value = "获取车辆不分页列表", tags = {"管理后台-车队管理"})
    @PostMapping("/api/v1/vehicle/list")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "plateNumber", value = "车牌号", dataTypeClass = String.class),
            @ApiImplicitParam(name = "fleetName", value = "车队名称", dataTypeClass = String.class)})
    public R<List<XpVehicleVO>> getList(String plateNumber, String fleetName) {
        return R.ok(xpVehicleService.getList(plateNumber, fleetName));
    }
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/mapper/XpVehicleMapper.java
@@ -7,6 +7,7 @@
import com.xiaopeng.vehicleConvoy.model.query.VehicleExportQuery;
import com.xiaopeng.vehicleConvoy.model.query.VehicleMapQuery;
import com.xiaopeng.vehicleConvoy.model.query.VehiclePageQuery;
import com.xiaopeng.vehicleConvoy.model.vo.XpVehicleVO;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDate;
@@ -79,4 +80,12 @@
     * @return 车辆列表
     */
    List<XpVehicle> vehicleExportList(@Param("query") VehicleExportQuery query);
    /**
     * 车辆不分页列表
     * @param plateNumber
     * @param fleetName
     * @return
     */
    List<XpVehicleVO> getList(String plateNumber, String fleetName);
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/dto/FleetAddDTO.java
New file
@@ -0,0 +1,40 @@
package com.xiaopeng.vehicleConvoy.model.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
 * 添加车队DTO
 *
 * @author mitao
 */
@Data
@ApiModel("添加车队数据传输对象")
public class FleetAddDTO {
    @ApiModelProperty(value = "车队名称", required = true)
    @NotBlank(message = "车队名称不能为空")
    private String fleetName;
    @ApiModelProperty(value = "负责人id", required = true)
    @NotNull(message = "负责人id不能为空")
    private Long operationsUserId;
    @ApiModelProperty(value = "打卡地id", required = true)
    @NotNull(message = "打卡地id不能为空")
    private Long locationId;
    @ApiModelProperty(value = "关联车辆ids", required = true)
    @NotEmpty(message = "关联车辆不能为空")
    private List<Long> vehicleIds;
    @ApiModelProperty(value = "关联人员ids", required = true)
    @NotEmpty(message = "关联人员不能为空")
    private List<Long> operationsUserIds;
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/dto/FleetEditDTO.java
New file
@@ -0,0 +1,33 @@
package com.xiaopeng.vehicleConvoy.model.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
 * 编辑车队DTO
 *
 * @author mitao
 */
@Data
@ApiModel("编辑车队数据传输对象")
public class FleetEditDTO {
    @ApiModelProperty(value = "车队id", required = true)
    @NotNull(message = "车队id不能为空")
    private Long id;
    @ApiModelProperty(value = "车队名称", required = true)
    @NotBlank(message = "车队名称不能为空")
    private String fleetName;
    @ApiModelProperty(value = "负责人id", required = true)
    @NotNull(message = "负责人id不能为空")
    private Long operationsUserId;
    @ApiModelProperty(value = "打卡地ID")
    private Long locationId;
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/query/FleetPageQuery.java
New file
@@ -0,0 +1,24 @@
package com.xiaopeng.vehicleConvoy.model.query;
import com.xiaopeng.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * 车队分页查询参数
 *
 * @author mitao
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "车队分页查询参数")
public class FleetPageQuery extends BasePage {
    @ApiModelProperty(value = "车队名称")
    private String fleetName;
    @ApiModelProperty(value = "负责人电话")
    private String phoneNumber;
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/query/FleetUserPageQuery.java
New file
@@ -0,0 +1,30 @@
package com.xiaopeng.vehicleConvoy.model.query;
import com.xiaopeng.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * 车队人员分页查询参数
 *
 * @author mitao
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "车队人员分页查询参数")
public class FleetUserPageQuery extends BasePage {
    @ApiModelProperty(value = "车队id")
    private Long fleetId;
    @ApiModelProperty(value = "人员姓名")
    private String nickName;
    @ApiModelProperty(value = "电话")
    private String phone;
    @ApiModelProperty(value = "角色ID")
    private Long roleId;
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/query/FleetVehiclePageQuery.java
New file
@@ -0,0 +1,27 @@
package com.xiaopeng.vehicleConvoy.model.query;
import com.xiaopeng.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull;
/**
 * 车队车辆分页查询参数
 *
 * @author mitao
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "车队车辆分页查询参数")
public class FleetVehiclePageQuery extends BasePage {
    @ApiModelProperty(value = "车队id", required = true)
    @NotNull(message = "车队id不能为空")
    private Long fleetId;
    @ApiModelProperty(value = "车牌号")
    private String plateNumber;
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/FleetDetailVO.java
New file
@@ -0,0 +1,53 @@
package com.xiaopeng.vehicleConvoy.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
 * 车队详情视图对象
 *
 * @author mitao
 */
@Data
@ApiModel("车队详情视图对象")
public class FleetDetailVO {
    @ApiModelProperty(value = "车队id")
    private Long id;
    @ApiModelProperty(value = "车队名称")
    private String fleetName;
    @ApiModelProperty(value = "负责人id")
    private Long operationsUserId;
    @ApiModelProperty(value = "负责人姓名")
    private String operationsUserName;
    @ApiModelProperty(value = "打卡地id")
    private Long locationId;
    @ApiModelProperty(value = "打卡地名称")
    private String locationName;
    @ApiModelProperty(value = "电子围栏id")
    private Long electronicId;
    @ApiModelProperty(value = "人员数量")
    private Integer userCount;
    @ApiModelProperty(value = "车辆数量")
    private Integer vehicleCount;
    @ApiModelProperty(value = "创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createTime;
    @ApiModelProperty(value = "更新时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime updateTime;
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/FleetPageVO.java
New file
@@ -0,0 +1,46 @@
package com.xiaopeng.vehicleConvoy.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
 * 车队分页列表视图对象
 *
 * @author mitao
 */
@Data
@ApiModel("车队分页列表视图对象")
public class FleetPageVO {
    @ApiModelProperty(value = "车队id")
    private Long id;
    @ApiModelProperty(value = "车队名称")
    private String fleetName;
    @ApiModelProperty(value = "负责人id")
    private Long operationsUserId;
    @ApiModelProperty(value = "负责人姓名")
    private String operationsUserName;
    @ApiModelProperty(value = "关联运营区域")
    private String electronicName;
    @ApiModelProperty(value = "负责人电话")
    private String phoneNumber;
    @ApiModelProperty(value = "人员数量")
    private Integer userCount;
    @ApiModelProperty(value = "车辆数量")
    private Integer vehicleCount;
    @ApiModelProperty(value = "创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createTime;
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/FleetUserVO.java
New file
@@ -0,0 +1,30 @@
package com.xiaopeng.vehicleConvoy.model.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * 车队人员视图对象
 *
 * @author mitao
 */
@Data
@ApiModel("车队人员视图对象")
public class FleetUserVO {
    @ApiModelProperty(value = "人员id")
    private Long userId;
    @ApiModelProperty(value = "人员姓名")
    private String nickName;
    @ApiModelProperty(value = "电话")
    private String phoneNumber;
    @ApiModelProperty(value = "工号")
    private String jobNumber;
    @ApiModelProperty(value = "角色名称")
    private String roleName;
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/FleetVehicleVO.java
New file
@@ -0,0 +1,36 @@
package com.xiaopeng.vehicleConvoy.model.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * 车队车辆视图对象
 *
 * @author mitao
 */
@Data
@ApiModel("车队车辆视图对象")
public class FleetVehicleVO {
    @ApiModelProperty(value = "车辆id")
    private Long vehicleId;
    @ApiModelProperty(value = "车辆编码")
    private String vehicleCode;
    @ApiModelProperty(value = "车牌号")
    private String plateNumber;
    @ApiModelProperty(value = "车辆颜色")
    private String vehicleColor;
    @ApiModelProperty(value = "车辆型号")
    private String vehicleModel;
    @ApiModelProperty(value = "运营状态")
    private Integer operatingStatus;
    @ApiModelProperty(value = "运营状态描述")
    private String operatingStatusDesc;
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/XpVehicleVO.java
New file
@@ -0,0 +1,21 @@
package com.xiaopeng.vehicleConvoy.model.vo;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * @author MiTao
 * @date 2026-02-12 21:36
 *
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("车辆视图对象")
public class XpVehicleVO extends XpVehicle {
    @ApiModelProperty(value = "车队名称")
    private String fleetName;
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/IXpFleetService.java
@@ -1,7 +1,15 @@
package com.xiaopeng.vehicleConvoy.service;
import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.web.page.PageInfo;
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.*;
import java.util.List;
@@ -22,4 +30,74 @@
     * @return 车队列表
     */
    List<XpFleet> listByIds(List<Long> ids);
    /**
     * 添加车队
     *
     * @param dto 添加车队DTO
     * @return 操作结果
     */
    R<Void> add(FleetAddDTO dto);
    /**
     * 编辑车队
     *
     * @param dto 编辑车队DTO
     * @return 操作结果
     */
    R<Void> edit(FleetEditDTO dto);
    /**
     * 查询车队详情
     *
     * @param id 车队id
     * @return 车队详情
     */
    R<FleetDetailVO> queryById(Long id);
    /**
     * 车队分页列表
     *
     * @param query 查询参数
     * @return 分页结果
     */
    R<PageInfo<FleetPageVO>> pageList(FleetPageQuery query);
    /**
     * 查询车队车辆分页列表
     *
     * @param query 查询参数
     * @return 分页结果
     */
    R<PageInfo<FleetVehicleVO>> queryVehiclePageList(FleetVehiclePageQuery query);
    /**
     * 查询车队人员分页列表
     *
     * @param query 查询参数
     * @return 分页结果
     */
    R<PageInfo<FleetUserVO>> queryUserPageList(FleetUserPageQuery query);
    /**
     * 根据ID删除车队
     * @param id
     */
    void deleteById(Long id);
    /**
     * 车队添加车辆成员
     * @param fleetId
     * @param vehicleId
     */
    void addVehicle(Long fleetId, Long vehicleId);
    /**
     * 车队添加人员成员
     *
     * @param fleetId
     * @param operationUserId
     */
    void addOperationUserId(Long fleetId, Long operationUserId);
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/IXpVehicleService.java
@@ -3,11 +3,11 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.api.dto.vehicleConvoy.VehicleTemperatureDto;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.vo.vehicleConvoy.VehicleTemperatureVo;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.vehicleConvoy.model.dto.*;
import com.xiaopeng.vehicleConvoy.model.importExcel.XpVehicleImportExcel;
import com.xiaopeng.vehicleConvoy.model.query.*;
import com.xiaopeng.vehicleConvoy.model.vo.XpVehicleVO;
import java.util.List;
@@ -166,4 +166,13 @@
     * @return 车辆温度
     */
    VehicleTemperatureDto queryTemperatureByOrderId(Long orderId, String uid);
    /**
     * 车辆不分页列表
     *
     * @param plateNumber
     * @param fleetName
     * @return
     */
    List<XpVehicleVO> getList(String plateNumber, String fleetName);
}
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);
    }
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java
@@ -13,7 +13,6 @@
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicleAnnualRecord;
import com.xiaopeng.api.vo.operation.ElectronicFenceDetailVo;
import com.xiaopeng.api.vo.vehicleConvoy.VehicleTemperatureVo;
import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandEnum;
import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandParamVo;
import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandVo;
@@ -26,6 +25,7 @@
import com.xiaopeng.vehicleConvoy.model.dto.*;
import com.xiaopeng.vehicleConvoy.model.importExcel.XpVehicleImportExcel;
import com.xiaopeng.vehicleConvoy.model.query.*;
import com.xiaopeng.vehicleConvoy.model.vo.XpVehicleVO;
import com.xiaopeng.vehicleConvoy.service.IXpVehicleAnnualRecordService;
import com.xiaopeng.vehicleConvoy.service.IXpVehicleService;
import com.xiaopeng.vehicleConvoy.util.VehicleControlUtil;
@@ -37,7 +37,6 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
@@ -609,4 +608,9 @@
            return new VehicleTemperatureDto();
        }
    }
    @Override
    public List<XpVehicleVO> getList(String plateNumber, String fleetName) {
        return baseMapper.getList(plateNumber, fleetName);
    }
}
xp-robotaxi-vehicleConvoy-boot/src/main/resources/i18n/messages.properties
@@ -1,5 +1,9 @@
# 中文
fleet.operation.user.bound.v2=当前车队已和该人员关联
policy.application.audit.ids.notEmpty=保单申请ID列表不能为空
fleet.user.bound=运维人员[{0}]已绑定车队[{1}]
fleet.vehicle.bound.v2=当前车队已和该车辆关联
fleet.vehicle.bound.v1=车辆[{0}]已绑定车队[{1}]
policy.application.audit.status.notNull=保单审核状态不能为空
welcome.message=欢迎来到我们的应用程序!
# 车辆管理
xp-robotaxi-vehicleConvoy-boot/src/main/resources/i18n/messages_en_US.properties
@@ -1,5 +1,8 @@
# English (US)
fleet.operation.user.bound.v2=The current fleet has been associated with this person
policy.application.audit.ids.notEmpty=The list of policy application ids cannot be empty
fleet.operation.user.bound.v1=Operation user [{0}] already bound fleet:[{1}]
fleet.vehicle.bound.v2=The current team has been associated with this vehicle.
policy.application.audit.status.notNull=The review status of the insurance policy cannot be empty
welcome.message=Welcome to our application!
order.not.exists=No valid order information found
xp-robotaxi-vehicleConvoy-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -1,5 +1,8 @@
# 中文
fleet.operation.user.bound.v2=当前车队已和该人员关联
policy.application.audit.ids.notEmpty=保单申请ID列表不能为空
fleet.user.bound=运维人员[{0}]已绑定车队[{1}]
fleet.vehicle.bound.v2=当前车队已和该车辆关联
policy.application.audit.status.notNull=保单审核状态不能为空
welcome.message=欢迎来到我们的应用程序!
order.not.exists=未查询到有效订单信息
xp-robotaxi-vehicleConvoy-boot/src/main/resources/mapper/vehicleConvoy/XpVehicleMapper.xml
@@ -108,5 +108,8 @@
        </where>
        ORDER BY create_time DESC
    </select>
    <select id="getList" resultType="com.xiaopeng.vehicleConvoy.model.vo.XpVehicleVO">
    </select>
</mapper>