无关风月
2026-02-14 2c97ba6813d18102e15c70ab2a24d62bc2263d05
Merge remote-tracking branch 'origin/master'
5个文件已添加
43个文件已修改
986 ■■■■■ 已修改文件
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpAppUserController.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpOperationsUserController.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/IXpOperationsUserService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/SysLoginService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/XpOperationsUserServiceImpl.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/maintenance/XpClockInClientFallbackFactory.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/operation/XpPricingClientFallbackFactory.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/vehicleConvoy/XpFleetOperationsClientFallbackFactory.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/maintenance/XpClockInClient.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/operation/XpPricingClient.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/XpFleetOperationsClient.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/operation/SysMenu.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/account/FleetInfoVO.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/account/XpOperationsUserProfileVO.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/KafkaConsumerService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/constant/CacheConstants.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpClockInController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/mapper/XpClockInMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpClockInService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpClockInServiceImpl.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/resources/mapper/maintenance/XpClockInMapper.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/pom.xml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/config/MyLocaleResolver.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/SysMenuController.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpPricingController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/dto/PricingSetPriceDTO.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/dto/XpTradingStrategyEditDTO.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/ISysMenuService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/SysMenuServiceImpl.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPointServiceImpl.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/holiday/HolidayUtil.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/vo/XpTradingStrategyDetailVO.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/pom.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/config/DataUpdateHandlerConfig.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/constant/I18nKeyConstant.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderEvaluateController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/job/base/AbstractJob.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/resources/i18n/messages.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/resources/i18n/messages_en_US.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/resources/i18n/messages_zh_CN.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/pom.xml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/constant/I18nKeyConstant.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpFleetOperationsController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_en_US.properties 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_zh_CN.properties 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpAppUserController.java
@@ -9,9 +9,7 @@
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
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 java.util.ArrayList;
@@ -59,4 +57,15 @@
        }
        return R.ok(xpAppUserService.listByIds(ids));
    }
    /**
     * 根据ID查询用户信息
     *
     * @param id 用户ID
     * @return 用户信息
     */
    @GetMapping("/inner/appUser/queryOperationsUserById")
    public R<XpAppUser> queryOperationsUserById(@RequestParam Long id){
        return R.ok(xpAppUserService.getById(id));
    }
}
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpOperationsUserController.java
@@ -6,9 +6,11 @@
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.XpOperationsUserProfileVO;
import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.common.security.service.TokenService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
@@ -33,10 +35,13 @@
    private final XpOperationsRoleMapper xpOperationsRoleMapper;
    private final TokenService tokenService;
    @Autowired
    public XpOperationsUserController(IXpOperationsUserService xpOperationsUserService, XpOperationsRoleMapper xpOperationsRoleMapper) {
    public XpOperationsUserController(IXpOperationsUserService xpOperationsUserService, XpOperationsRoleMapper xpOperationsRoleMapper, TokenService tokenService) {
        this.xpOperationsUserService = xpOperationsUserService;
        this.xpOperationsRoleMapper = xpOperationsRoleMapper;
        this.tokenService = tokenService;
    }
    /**
@@ -108,4 +113,21 @@
    public R<PageInfo<XpOperationsUserWithRoleVO>> getList(@RequestBody XpOperationsUserPageQuery query) {
        return R.ok(xpOperationsUserService.selectUserPageByConditions(query));
    }
    /**
     * 获取运维人员个人中心首页信息
     *
     * @return 个人中心首页信息
     */
    @ApiOperation(value = "获取个人中心首页信息", tags = {"运维APP-个人中心"})
    @GetMapping("/api/v1/userInfo")
    public R<XpOperationsUserProfileVO> getProfile() {
        // 通过TokenService获取当前登录用户ID
        Long currentUserId = tokenService.getLoginOperationUser().getId();
        // 查询个人中心信息
        XpOperationsUserProfileVO profile = xpOperationsUserService.getUserProfile(currentUserId);
        return R.ok(profile);
    }
}
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/IXpOperationsUserService.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.account.model.query.XpOperationsUserPageQuery;
import com.xiaopeng.api.model.account.XpOperationsUser;
import com.xiaopeng.api.vo.account.XpOperationsUserProfileVO;
import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO;
import com.xiaopeng.common.core.web.page.PageInfo;
@@ -30,4 +31,12 @@
     * @return
     */
    PageInfo<XpOperationsUserWithRoleVO> selectUserPageByConditions(XpOperationsUserPageQuery query);
    /**
     * 获取运维人员个人中心首页信息
     *
     * @param userId 运维人员ID
     * @return 个人中心首页信息
     */
    XpOperationsUserProfileVO getUserProfile(Long userId);
}
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/SysLoginService.java
@@ -73,7 +73,7 @@
        LoginUser userInfo = new LoginUser();
        userInfo.setSysUser(user);
        userInfo.setRoles(roles);
        userInfo.setPermissions(menuPermission.stream().map(SysMenu::getPath).collect(Collectors.toSet()));
        userInfo.setPermissions(menuPermission.stream().map(item->item.getId().toString()).collect(Collectors.toSet()));
        if (StringUtils.isNull(user)) {
            recordLogService.recordLogininfor(request, null, username, Constants.LOGIN_FAIL_STATUS, "登录用户不存在");
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/XpOperationsUserServiceImpl.java
@@ -1,18 +1,31 @@
package com.xiaopeng.account.service.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiaopeng.account.mapper.XpOperationsRoleMapper;
import com.xiaopeng.account.mapper.XpOperationsUserMapper;
import com.xiaopeng.account.model.query.XpOperationsUserPageQuery;
import com.xiaopeng.account.service.IXpOperationsUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.api.feignClient.maintenance.XpClockInClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetOperationsClient;
import com.xiaopeng.api.model.account.XpOperationsUser;
import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
import com.xiaopeng.api.vo.account.FleetInfoVO;
import com.xiaopeng.api.vo.account.XpOperationsUserProfileVO;
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 lombok.extern.slf4j.Slf4j;
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.util.Collections;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
/**
 * <p>
@@ -22,8 +35,22 @@
 * @author mitao
 * @since 2026-02-03
 */
@Slf4j
@Service
public class XpOperationsUserServiceImpl extends ServiceImpl<XpOperationsUserMapper, XpOperationsUser> implements IXpOperationsUserService {
    @Autowired
    private XpFleetOperationsClient xpFleetOperationsClient;
    @Autowired
    private XpFleetClient xpFleetClient;
    @Autowired
    private XpOperationsRoleMapper xpOperationsRoleMapper;
    @Autowired
    private XpClockInClient xpClockInClient;
    @Override
    public List<XpOperationsUser> listByName(String nickName) {
        if (StringUtils.isBlank(nickName)) {
@@ -40,4 +67,86 @@
        pageInfo.setTotal(page.getTotal());
        return pageInfo;
    }
    @Override
    public XpOperationsUserProfileVO getUserProfile(Long userId) {
        // 1. 查询运维人员基础信息
        XpOperationsUser user = this.getById(userId);
        if (user == null) {
            throw new ServiceException("运维人员不存在");
        }
        XpOperationsUserProfileVO vo = new XpOperationsUserProfileVO();
        BeanUtils.copyProperties(user, vo);
        // 2. 查询关联的车队ID列表(一个运维人员只能关联一个车队)
        R<List<Long>> fleetIdsResult = xpFleetOperationsClient.getFleetIdsByUserId(userId);
        List<Long> fleetIds = fleetIdsResult.getData();
        if (!CollectionUtils.isEmpty(fleetIds)) {
            // 3. 获取第一个车队ID(因为只能关联一个车队)
            Long fleetId = fleetIds.get(0);
            // 4. 查询车队信息
            R<List<XpFleet>> fleetsResult = xpFleetClient.listByIds(Collections.singletonList(fleetId));
            List<XpFleet> fleets = fleetsResult.getData();
            if (!CollectionUtils.isEmpty(fleets)) {
                XpFleet fleet = fleets.get(0);
                // 5. 组装车队信息VO
                FleetInfoVO fleetVO = new FleetInfoVO();
                fleetVO.setFleetId(fleet.getId());
                fleetVO.setFleetName(fleet.getFleetName());
                fleetVO.setProvinceName(fleet.getProvinceName());
                fleetVO.setCityName(fleet.getCityName());
                fleetVO.setDistrictName(fleet.getDistrictName());
                // 6. 查询并填充车队队长信息
                if (fleet.getOperationsUserId() != null) {
                    XpOperationsUser captain = this.getById(fleet.getOperationsUserId());
                    if (captain != null) {
                        fleetVO.setCaptainId(captain.getId());
                        fleetVO.setFleetLeaderName(captain.getNickName());
                        fleetVO.setFleetLeaderAvatar(captain.getAvatar());
                    }
                }
                vo.setFleet(fleetVO);
            }
        }
        // 7. 查询是否上班中
        try {
            R<Boolean> workStatusResult = xpClockInClient.checkWorkStatus(userId);
            if (workStatusResult != null && workStatusResult.getCode() == 200) {
                vo.setWorkFlag(workStatusResult.getData());
            } else {
                // 调用失败,默认 false
                vo.setWorkFlag(false);
                log.warn("查询用户上班状态失败, userId={}, result={}", userId, workStatusResult);
            }
        } catch (Exception e) {
            // 异常情况,默认 false
            vo.setWorkFlag(false);
            log.error("查询用户上班状态异常, userId={}", userId, e);
        }
        // 8. 查询角色信息
        List<XpOperationsUserWithRoleVO> userWithRoles = xpOperationsRoleMapper.selectUsersWithRoleByIds(Collections.singletonList(userId));
        if (!CollectionUtils.isEmpty(userWithRoles)) {
            List<XpOperationsUserProfileVO.RoleInfoVO> roleVOs = userWithRoles.stream()
                    .filter(r -> r.getRoleId() != null)
                    .map(r -> {
                        XpOperationsUserProfileVO.RoleInfoVO roleVO = new XpOperationsUserProfileVO.RoleInfoVO();
                        roleVO.setRoleId(r.getRoleId());
                        roleVO.setRoleName(r.getRoleName());
                        return roleVO;
                    })
                    .collect(Collectors.toList());
            vo.setRoles(roleVOs);
        }
        return vo;
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/maintenance/XpClockInClientFallbackFactory.java
New file
@@ -0,0 +1,31 @@
package com.xiaopeng.api.factory.maintenance;
import com.xiaopeng.api.feignClient.maintenance.XpClockInClient;
import com.xiaopeng.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
 * 打卡服务降级工厂
 *
 * @author mitao
 * @date 2026-02-14
 */
@Slf4j
@Component
public class XpClockInClientFallbackFactory implements FallbackFactory<XpClockInClient> {
    @Override
    public XpClockInClient create(Throwable cause) {
        return new XpClockInClient() {
            @Override
            public R<Boolean> checkWorkStatus(Long appUserId) {
                log.error("调用打卡服务检查上班状态失败, appUserId={}, 异常: {}",
                    appUserId, cause.getMessage(), cause);
                // 降级策略: 返回 false (未上班状态)
                return R.ok(false);
            }
        };
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/operation/XpPricingClientFallbackFactory.java
@@ -28,7 +28,7 @@
            }
            @Override
            public R<XpPricing> getPricingByCityId(Long cityId) {
            public R<XpPricing> getPricingByCid(Long cityId) {
                log.error("根据开通城市获取定价失败", cause);
                return R.ok(null);
            }
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/vehicleConvoy/XpFleetOperationsClientFallbackFactory.java
@@ -1,7 +1,6 @@
package com.xiaopeng.api.factory.vehicleConvoy;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetOperationsClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetVehicleClient;
import com.xiaopeng.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
@@ -28,6 +27,12 @@
                log.error("根据人员id 获取所属车队下关联的车辆ids,人员ID: {},原因: {}", vehicleId, cause.getMessage());
                return R.ok(new ArrayList<>());
            }
            @Override
            public R<List<Long>> getFleetIdsByUserId(Long userId) {
                log.error("根据人员ID获取车队信息失败 {}",  cause.getMessage());
                return R.fail("根据人员ID获取车队信息失败");
            }
        };
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/maintenance/XpClockInClient.java
New file
@@ -0,0 +1,30 @@
package com.xiaopeng.api.feignClient.maintenance;
import com.xiaopeng.api.factory.maintenance.XpClockInClientFallbackFactory;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
 * 打卡服务 Feign 客户端
 *
 * @author mitao
 * @date 2026-02-14
 */
@FeignClient(
    contextId = "XpClockInClient",
    value = ServiceNameConstantsEnum.XP_MAINTENANCE_SERVICE,
    fallbackFactory = XpClockInClientFallbackFactory.class
)
public interface XpClockInClient {
    /**
     * 检查用户是否上班中
     * @param appUserId 用户ID
     * @return true=上班中, false=未上班或已下班
     */
    @GetMapping("/inner/clock-in/checkWorkStatus")
    R<Boolean> checkWorkStatus(@RequestParam("appUserId") Long appUserId);
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/operation/XpPricingClient.java
@@ -21,8 +21,8 @@
    R<Integer> getWaitTime(@RequestBody LonLatDto dto);
    // 根据开通城市 获取定价
    @GetMapping("/base/v1/mini-program/getPricingByCityId/{cityId}")
     R<XpPricing> getPricingByCityId(@PathVariable("cityId")Long cityId);
    @GetMapping("/base/v1/mini-program/getPricingByCid")
     R<XpPricing> getPricingByCid(@RequestParam Long cityId);
    /**
     * 保存XpGeoQueryEntity
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/XpFleetOperationsClient.java
@@ -28,4 +28,13 @@
     */
    @GetMapping("/inner/fleet-operations/getVehicleByUserId/{id}")
    R<List<Long>> getVehicleByUserId(@PathVariable("id") Long id);
    /**
     * 根据运维人员ID查询关联的车队ID列表
     *
     * @param userId 运维人员ID
     * @return 车队ID列表
     */
    @GetMapping("/inner/fleet-operations/getFleetIdsByUserId/{userId}")
    R<List<Long>> getFleetIdsByUserId(@PathVariable("userId") Long userId);
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/operation/SysMenu.java
@@ -1,17 +1,17 @@
package com.xiaopeng.api.model.operation;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
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.util.List;
/**
 * <p>
@@ -76,5 +76,7 @@
    @ApiModelProperty(value = "菜单类型 1=管理端 2=运维app")
    private Integer groupType;
    @ApiModelProperty(value = "子节点")
    @TableField(exist = false)
    List<SysMenu> children;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/account/FleetInfoVO.java
New file
@@ -0,0 +1,55 @@
package com.xiaopeng.api.vo.account;
import lombok.Data;
import java.io.Serializable;
/**
 * 车队信息VO(用于个人中心)
 *
 * @author mitao
 * @date 2026-02-14
 */
@Data
public class FleetInfoVO implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 车队ID
     */
    private Long fleetId;
    /**
     * 车队名称
     */
    private String fleetName;
    /**
     * 省份名称
     */
    private String provinceName;
    /**
     * 城市名称
     */
    private String cityName;
    /**
     * 区县名称
     */
    private String districtName;
    /**
     * 车队队长ID
     */
    private Long captainId;
    /**
     * 车队队长名称
     */
    private String fleetLeaderName;
    /**
     * 车队队长头像
     */
    private String fleetLeaderAvatar;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/account/XpOperationsUserProfileVO.java
New file
@@ -0,0 +1,88 @@
package com.xiaopeng.api.vo.account;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
 * 运维人员个人中心首页VO
 *
 * @author mitao
 * @date 2026-02-14
 */
@Data
public class XpOperationsUserProfileVO implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 用户ID
     */
    @ApiModelProperty(value = "ID")
    private Long id;
    /**
     * 名称
     */
    @ApiModelProperty(value = "姓名")
    private String nickName;
    /**
     * 唯一编号
     */
    @ApiModelProperty(value = "唯一编号")
    private String uniqueNumber;
    /**
     * 头像
     */
    @ApiModelProperty(value = "头像")
    private String avatar;
    /**
     * 手机号
     */
    @ApiModelProperty(value = "手机号")
    private String phoneNumber;
    /**
     * 状态(1=启用 0=禁用)
     */
    private Integer status;
    /**
     * 关联车队信息
     */
    @ApiModelProperty(value = "关联车队信息")
    private FleetInfoVO fleet;
    /**
     * 角色列表
     */
    @ApiModelProperty(value = "角色列表")
    private List<RoleInfoVO> roles;
    @ApiModelProperty("是否上班中")
    private Boolean workFlag;
    /**
     * 角色信息VO
     */
    @Data
    public static class RoleInfoVO implements Serializable {
        private static final long serialVersionUID = 1L;
        /**
         * 角色ID
         */
        @ApiModelProperty(value = "角色ID")
        private Long roleId;
        /**
         * 角色名称
         */
        @ApiModelProperty(value = "角色名称")
        private String roleName;
    }
}
xp-robotaxi-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -27,4 +27,5 @@
com.xiaopeng.api.factory.automaticPilot.XpuVimClientFallbackFactory
com.xiaopeng.api.factory.automaticPilot.OrderMissionClientFallbackFactory
com.xiaopeng.api.factory.maintenance.XpCheckLocationClientFallbackFactory
com.xiaopeng.api.factory.automaticPilot.OrderMissionClientFallbackFactory
com.xiaopeng.api.factory.automaticPilot.OrderMissionClientFallbackFactory
com.xiaopeng.api.factory.maintenance.XpClockInClientFallbackFactory
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/KafkaConsumerService.java
@@ -6,12 +6,15 @@
import com.alibaba.fastjson2.JSONObject;
import com.xiaopeng.api.dto.map.OrderRouteAndFareDto;
import com.xiaopeng.api.dto.operation.LonLatDto;
import com.xiaopeng.api.dto.operation.ReassignOrderDTO;
import com.xiaopeng.api.dto.order.PopOrderDto;
import com.xiaopeng.api.dto.pushMessage.PushOrderRouteDTO;
import com.xiaopeng.api.dto.pushMessage.SendMessageDTO;
import com.xiaopeng.api.feignClient.map.XpMapClient;
import com.xiaopeng.api.feignClient.operation.ReassignOrderClient;
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.UserVehicleClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.order.XpOrder;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
@@ -32,7 +35,9 @@
import com.xiaopeng.api.vo.automaticPilot.proto.up.xpuNavigationRequest.NavigationRequest;
import com.xiaopeng.api.vo.automaticPilot.proto.up.xpuNavigationRequest.OrderPhase;
import com.xiaopeng.api.vo.operation.RouteVo;
import com.xiaopeng.api.vo.vehicleConvoy.CancelTaxiPassengerVo;
import com.xiaopeng.automaticPilot.util.XPUUtil;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.OrderStatusEnum;
import com.xiaopeng.common.core.enums.PushMessageTypeEnum;
@@ -80,6 +85,10 @@
    @Resource
    private XpPushMessageClient xpPushMessageClient;
    @Resource
    private ReassignOrderClient reassignOrderClient;
    @Resource
    private UserVehicleClient userVehicleClient;
@@ -117,6 +126,7 @@
                case SYSTEM_STATUS_XPU:
                    log.info("XPU发布系统运行状态 (XPU→云端):{}", message);
                    systemStatusXpu(msgContent);
                    systemStatusXpuAndReassign(msgContent);
                    break;
                case XPU_NAVIGATION_REQUEST:
                    log.info("XPU向云端请求导航 (XPU→云端):{}", message);
@@ -278,6 +288,11 @@
        //开始处理消息对应的业务逻辑
        SystemRuntimeStatus systemRuntimeStatus = msgContent.getContent_detail().getObject("systemRuntimeStatus", SystemRuntimeStatus.class);
        AbnormalReason abnormalReason = systemRuntimeStatus.getSystem_status().getAbnormal_reason();
        // 存储车辆挡位
        GearLevel gearLevel = systemRuntimeStatus.getVehicle_status().getDynamic_status().getGear_level();
        int value = gearLevel.getValue();
        redisTemplate.opsForValue().set(CacheConstants.GEAR_LEVEL + systemRuntimeStatus.getVehicle_status().getStatic_info().getVin(), value);
        // 判断是否为故障状态
        if (abnormalReason == AbnormalReason.FAULT) {
            log.info("车辆故障,开始处理故障");
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/constant/CacheConstants.java
@@ -132,4 +132,8 @@
     * 订单车辆轨迹,禁止用于其他用处
     */
    public static final String ORDER_ROUTE_CAPTURE = "order_route_capture:";
    /**
     * 车辆挡位
     */
    public static final String GEAR_LEVEL = "gear_level:";
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpClockInController.java
@@ -11,21 +11,26 @@
import com.xiaopeng.maintenance.vo.ShiftAttendanceVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
/**
 * <p>
 * 打卡明细 前端控制器
 * </p>
 * 打卡管理 Controller
 *
 * @author xiaochen
 * @since 2026-02-06
 * @author mitao
 * @date 2026-02-14
 */
@Api(tags = "打卡明细")
@Slf4j
@RestController
@RequestMapping("")
public class XpClockInController {
@@ -39,6 +44,7 @@
    public R<Boolean> verifyRange(@RequestBody VerifyRangeDTO dto) {
        return R.ok(xpClockInService.verifyRange(dto));
    }
    private IXpClockInService clockInService;
    @ApiOperation(value = "上班打卡", tags = "运维APP-排班与考勤")
    @PostMapping("/api/v1/clock/clockIn")
@@ -48,6 +54,17 @@
        return xpClockInService.clockIn(dto);
    }
    /**
     * 检查用户是否上班中(内部接口)
     */
    @GetMapping("/inner/clock-in/checkWorkStatus")
    @ApiOperation(value = "检查用户是否上班中", tags = {"内部接口"})
    public R<Boolean> checkWorkStatus(@RequestParam("appUserId") Long appUserId) {
        log.info("检查用户上班状态, appUserId={}", appUserId);
        Boolean workFlag = clockInService.checkWorkStatus(appUserId);
        return R.ok(workFlag);
    }
    @ApiOperation(value = "下班打卡", tags = "运维APP-排班与考勤")
    @PostMapping("/api/v1/clock/clockOut")
    public R<String> clockOut(@RequestBody ClockDTO dto) {
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/mapper/XpClockInMapper.java
@@ -2,6 +2,10 @@
import com.xiaopeng.api.model.maintenance.XpClockIn;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
/**
 * <p>
@@ -13,4 +17,18 @@
 */
public interface XpClockInMapper extends BaseMapper<XpClockIn> {
    /**
     * 查询用户当天的打卡记录
     * @param tableSuffix 分表后缀 (0-9)
     * @param appUserId 用户ID
     * @param startTime 开始时间 (当天00:00:00)
     * @param endTime 结束时间 (当天23:59:59)
     * @return 打卡记录列表
     */
    List<XpClockIn> selectTodayClockInRecords(
        @Param("tableSuffix") Integer tableSuffix,
        @Param("appUserId") Long appUserId,
        @Param("startTime") LocalDateTime startTime,
        @Param("endTime") LocalDateTime endTime
    );
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpClockInService.java
@@ -39,4 +39,10 @@
     * @return
     */
    R<String> clockOut(ClockDTO dto);
    /**
     * 检查用户是否上班中
     * @param appUserId 用户ID
     * @return true=上班中(只打了上班卡), false=未上班或已下班
     */
    Boolean checkWorkStatus(Long appUserId);
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpClockInServiceImpl.java
@@ -1,9 +1,9 @@
package com.xiaopeng.maintenance.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.api.model.maintenance.XpCheckLocation;
import com.xiaopeng.api.model.maintenance.XpClockIn;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.api.model.maintenance.XpShift;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.utils.DateUtils;
@@ -14,15 +14,22 @@
import com.xiaopeng.maintenance.mapper.XpClockInMapper;
import com.xiaopeng.maintenance.mapper.XpShiftMapper;
import com.xiaopeng.maintenance.service.IXpClockInService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import java.util.Objects;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
/**
 * <p>
@@ -32,6 +39,7 @@
 * @author mitao
 * @since 2026-02-03
 */
@Slf4j
@Service
public class XpClockInServiceImpl extends ServiceImpl<XpClockInMapper, XpClockIn> implements IXpClockInService {
@@ -144,4 +152,45 @@
        this.updateById(xpClockIn);
        return R.ok();
    }
    @Override
    public Boolean checkWorkStatus(Long appUserId) {
        if (appUserId == null) {
            log.warn("检查上班状态失败: appUserId 为空");
            return false;
        }
        try {
            // 1. 计算当天时间范围
            LocalDateTime startTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN); // 00:00:00
            LocalDateTime endTime = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);   // 23:59:59
            // 2. 计算分表后缀 (userId % 10)
            Integer tableSuffix = (int) (appUserId % 10);
            // 3. 查询当天打卡记录
            List<XpClockIn> records = baseMapper.selectTodayClockInRecords(
                tableSuffix, appUserId, startTime, endTime
            );
            // 4. 判断上班状态
            if (records == null || records.isEmpty()) {
                // 无打卡记录 = 未上班
                log.debug("用户 {} 今日无打卡记录, workFlag=false", appUserId);
                return false;
            } else if (records.size() == 1) {
                // 1条记录 = 已上班未下班
                log.debug("用户 {} 今日打卡1次, workFlag=true", appUserId);
                return true;
            } else {
                // 2条及以上记录 = 已下班
                log.debug("用户 {} 今日打卡{}次, workFlag=false", appUserId, records.size());
                return false;
            }
        } catch (Exception e) {
            log.error("检查用户上班状态异常, appUserId={}", appUserId, e);
            // 异常情况返回 false
            return false;
        }
    }
}
xp-robotaxi-maintenance-boot/src/main/resources/mapper/maintenance/XpClockInMapper.xml
@@ -2,4 +2,28 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiaopeng.maintenance.mapper.XpClockInMapper">
    <resultMap id="BaseResultMap" type="com.xiaopeng.api.model.maintenance.XpClockIn">
        <id column="id" property="id" />
        <result column="app_user_id" property="appUserId" />
        <result column="clock_time" property="clockTime" />
        <result column="shift_id" property="shiftId" />
        <result column="clock_result" property="clockResult" />
        <result column="clock_lon" property="clockLon" />
        <result column="clock_lat" property="clockLat" />
        <result column="create_time" property="createTime" />
        <result column="update_time" property="updateTime" />
    </resultMap>
    <!-- 查询用户当天的打卡记录 -->
    <select id="selectTodayClockInRecords" resultMap="BaseResultMap">
        SELECT
            id, app_user_id, clock_time, shift_id, clock_result,
            clock_lon, clock_lat, create_time, update_time
        FROM xp_clock_in_${tableSuffix}
        WHERE app_user_id = #{appUserId}
          AND clock_time BETWEEN #{startTime} AND #{endTime}
          AND clock_result IN (1, 2)
        ORDER BY clock_time ASC
    </select>
</mapper>
xp-robotaxi-operation-boot/pom.xml
@@ -149,8 +149,8 @@
            <version>4.4.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>com.google.guava</artifactId>
                    <groupId>guava</groupId>
                    <artifactId>guava</artifactId>
                    <groupId>com.google.guava</groupId>
                </exclusion>
            </exclusions>
        </dependency>
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/config/MyLocaleResolver.java
@@ -23,6 +23,7 @@
        Locale locale = null;
        if (StringUtils.isEmpty(language)) {
            locale = Locale.getDefault();
            language = "1";
        }
        //如果请求不为空 
        //If the request is not empty.
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/SysMenuController.java
@@ -1,26 +1,14 @@
package com.xiaopeng.operation.controller;
import com.xiaopeng.api.model.operation.SysMenu;
import com.xiaopeng.api.model.operation.SysMenuBak;
import com.xiaopeng.api.model.operation.SysMenus;
import com.xiaopeng.common.core.constant.UserConstants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.common.core.web.controller.BaseController;
import com.xiaopeng.common.core.web.domain.AjaxResult;
import com.xiaopeng.common.log.annotation.Log;
import com.xiaopeng.common.log.enums.BusinessType;
import com.xiaopeng.common.security.annotation.Logical;
import com.xiaopeng.common.security.annotation.RequiresPermissions;
import com.xiaopeng.common.security.service.TokenService;
import com.xiaopeng.common.security.utils.SecurityUtils;
import com.xiaopeng.operation.service.ISysMenuBakService;
import com.xiaopeng.operation.service.ISysMenuService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
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 javax.annotation.Resource;
import java.util.ArrayList;
@@ -32,8 +20,6 @@
 * @author ruoyi
 */
@RestController
@RequestMapping("/menu")
@Api(tags = "菜单模块")
public class SysMenuController extends BaseController
{
    @Resource
@@ -46,8 +32,8 @@
     * @param menuIds 菜单ID列表
     * @return 菜单列表
     */
    @PostMapping("/getMenusByMenuIds")
    @ApiOperation(value = "根据菜单ID列表获取菜单列表", notes = "内部服务调用")
    @PostMapping("/menu/getMenusByMenuIds")
    @ApiOperation(value = "根据菜单ID列表获取菜单列表", tags = {"内部服务调用"})
    public R<List<SysMenu>> getMenusByMenuIds(@RequestBody List<Long> menuIds) {
        if (menuIds == null || menuIds.isEmpty()) {
            return R.ok(new ArrayList<>());
@@ -55,4 +41,22 @@
        List<SysMenu> menus = menuService.selectMenusByMenuIds(menuIds);
        return R.ok(menus);
    }
    /**
     * 获取全部菜单树
     * @return
     */
    @GetMapping("/api/v1/getMenuTree")
    @ApiOperation(value = "获取全部菜单树", tags = {"管理后台-权限组设置"})
    public R<List<SysMenu>> getMenuTree() {
        List<SysMenu> menus = menuService.getMenuTree();
        return R.ok(menus);
    }
    @GetMapping("/api/v1/getPermission")
    @ApiOperation(value = "获取获取当前登录用户菜单列表", tags = {"管理后台-菜单管理"})
    public R<List<SysMenu>> getPermission(){
        return R.ok(menuService.getPermission());
    }
}
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpPricingController.java
@@ -65,11 +65,17 @@
    }
    // 根据开通城市 获取定价
    @GetMapping("/getPricingByCityId/{cityId}")
    @GetMapping("/base/v1/mini-program/getPricingByCityId/{cityId}")
    public R<XpPricing> getPricingByCityId(@PathVariable("cityId") Long cityId) {
        XpPricing pricing = xpPricingService.lambdaQuery().eq(XpPricing::getCityId, cityId).last("limit 1").one();
        return R.ok(pricing);
    }
    // 根据开通城市 获取定价
    @GetMapping("/base/v1/mini-program/getPricingByCid")
    public R<XpPricing> getPricingByCid(@RequestParam Long cityId) {
        XpPricing pricing = xpPricingService.lambdaQuery().eq(XpPricing::getCityId, cityId).last("limit 1").one();
        return R.ok(pricing);
    }
    @GetMapping("/base/v1/mini-program/pricing/rules/{openCityId}")
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/dto/PricingSetPriceDTO.java
@@ -18,15 +18,95 @@
    @NotNull(message = "定价ID不能为空")
    private Long id;
    @ApiModelProperty(value = "基础价格json配置", required = true)
    @ApiModelProperty(value = "基础价格json配置", required = true, notes = "{\n" +
            "  \"num3\": \"起步价数值1\",\n" +
            "  \"num4\": \"起步价数值2\",\n" +
            "  \"num5\": \"起步价数值3\",\n" +
            "  \"num6\": \"起步价数值4\",\n" +
            "  \"num7\": \"起步价数值5\",\n" +
            "  \"num8\": \"起步价数值6\",\n" +
            "  \"num9\": \"起步价数值7\",\n" +
            "  \"num10\": \"起步价数值8\",\n" +
            "  \"num11\": \"起步价数值9\",\n" +
            "  \"num12\": \"起步价数值10\",\n" +
            "  \"num13\": \"起步价数值11\",\n" +
            "  \"num14\": \"起步价数值12\",\n" +
            "  \"num15\": \"起步价数值13\",\n" +
            "  \"num16\": \"起步价数值14\",\n" +
            "  \"num17\": \"起步价数值15\",\n" +
            "  \"num18\": \"起步价数值16\",\n" +
            "  \"num19\": \"起步价数值17\",\n" +
            "  \"num20\": \"起步价数值18\",\n" +
            "  \"num21\": \"起步价数值19\",\n" +
            "  \"num22\": \"起步价数值20\",\n" +
            "  \"num23\": \"起步价数值21\",\n" +
            "  \"num24\": \"起步价数值22\",\n" +
            "  \"num25\": \"起步价数值23\",\n" +
            "  \"num26\": \"起步价数值24\",\n" +
            "  \"num27\": \"起步价数值25\",\n" +
            "  \"num28\": \"起步价数值26\",\n" +
            "  \"num29\": \"起步价数值27\",\n" +
            "  \"num30\": \"起步价数值28\",\n" +
            "  \"num31\": \"起步价数值29\",\n" +
            "  \"num32\": \"起步价数值30\",\n" +
            "  \"num33\": \"起步价数值31\",\n" +
            "  \"num34\": \"起步价数值32\"\n" +
            "}")
    @NotBlank(message = "基础价格json配置不能为空")
    private String basicPricingJson;
    @ApiModelProperty(value = "起步价json配置", required = true)
    @ApiModelProperty(value = "起步价json配置", required = true, notes = "[\n" +
            "  {\n" +
            "    \"num1\": \"08:00\",\n" +
            "    \"num2\": \"10:00\",\n" +
            "    \"num3\": \"起步价数值1\",\n" +
            "    \"num4\": \"起步价数值2\",\n" +
            "    \"num5\": \"起步价数值3\",\n" +
            "    \"num6\": \"起步价数值4\",\n" +
            "    \"num7\": \"起步价数值5\"\n" +
            "  },\n" +
            "  {\n" +
            "    \"num1\": \"10:00\",\n" +
            "    \"num2\": \"12:00\",\n" +
            "    \"num3\": \"起步价数值1\",\n" +
            "    \"num4\": \"起步价数值2\",\n" +
            "    \"num5\": \"起步价数值3\",\n" +
            "    \"num6\": \"起步价数值4\",\n" +
            "    \"num7\": \"起步价数值5\"\n" +
            "  },\n" +
            "  {\n" +
            "    \"num1\": \"14:00\",\n" +
            "    \"num2\": \"16:00\",\n" +
            "    \"num3\": \"起步价数值1\",\n" +
            "    \"num4\": \"起步价数值2\",\n" +
            "    \"num5\": \"起步价数值3\",\n" +
            "    \"num6\": \"起步价数值4\",\n" +
            "    \"num7\": \"起步价数值5\"\n" +
            "  },\n" +
            "  {\n" +
            "    \"num1\": \"18:00\",\n" +
            "    \"num2\": \"20:00\",\n" +
            "    \"num3\": \"起步价数值1\",\n" +
            "    \"num4\": \"起步价数值2\",\n" +
            "    \"num5\": \"起步价数值3\",\n" +
            "    \"num6\": \"起步价数值4\",\n" +
            "    \"num7\": \"起步价数值5\"\n" +
            "  }\n" +
            "]")
    @NotBlank(message = "起步价json配置不能为空")
    private String startingFareJson;
    @ApiModelProperty(value = "动态价格json配置", required = true)
    @ApiModelProperty(value = "动态价格json配置", required = true, notes = "  {\n" +
            "    \"num1\": \"起步价数值1\",\n" +
            "    \"num2\": \"起步价数值2\",\n" +
            "    \"num3\": \"起步价数值3\",\n" +
            "    \"num4\": \"起步价数值4\",\n" +
            "    \"num5\": \"起步价数值5\",\n" +
            "    \"num6\": \"起步价数值6\",\n" +
            "    \"num7\": \"起步价数值7\",\n" +
            "    \"num8\": \"起步价数值8\",\n" +
            "    \"num9\": \"起步价数值9\"\n" +
            "  }")
    @NotBlank(message = "动态价格json配置不能为空")
    private String dynamicPricingJson;
}
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/dto/XpTradingStrategyEditDTO.java
@@ -69,6 +69,18 @@
    @ApiModelProperty(value = "三轮推单时间")
    private Integer threePushTime;
    @ApiModelProperty(value = "四轮推单距离")
    private Double fourPushDistance;
    @ApiModelProperty(value = "四轮推单时间")
    private Integer fourPushTime;
    @ApiModelProperty(value = "五轮推单距离")
    private Double fivePushDistance;
    @ApiModelProperty(value = "五轮推单时间")
    private Integer fivePushTime;
    @ApiModelProperty(value = "规则类型 1=单手举手 2=多手举手")
    private Integer ruleType;
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/ISysMenuService.java
@@ -22,4 +22,16 @@
     * @return 菜单列表
     */
    List<SysMenu> selectMenusByMenuIds(List<Long> menuIds);
    /**
     *获取全部菜单树
     * @return
     */
    List<SysMenu> getMenuTree();
    /**
     *获取获取当前登录用户菜单列表
     * @return
     */
    List<SysMenu> getPermission();
}
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/SysMenuServiceImpl.java
@@ -6,8 +6,8 @@
import com.xiaopeng.operation.service.ISysMenuService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
/**
 * <p>
@@ -35,4 +35,77 @@
        // 使用 MyBatis-Plus 的 listByIds 方法批量查询
        return this.listByIds(menuIds);
    }
    @Override
    public List<SysMenu> getMenuTree() {
        List<SysMenu> all = list();
        if (all == null || all.isEmpty()) {
            return Collections.emptyList();
        }
        // 1) 按 parentId 分组(M/C/F 都先放进去,后面再按类型筛)
        Map<Long, List<SysMenu>> childrenMap = all.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.groupingBy(m -> m.getParentId() == null ? 0L : m.getParentId()));
        // 2) 找到根目录:通常 parentId = 0 或 null 的目录(M)
        List<SysMenu> roots = childrenMap.getOrDefault(0L, Collections.emptyList()).stream()
                .filter(m -> "M".equals(m.getMenuType()))
                .sorted(Comparator.comparing(SysMenu::getOrderNum, Comparator.nullsLast(Integer::compareTo)))
                .collect(Collectors.toList());
        // 3) 递归挂载:M 下挂 C;C 下挂 F(也允许 C 下还有 C,或 M 下还有 M,按你的数据结构来)
        for (SysMenu root : roots) {
            fillChildren(root, childrenMap);
        }
        return roots;
    }
    private void fillChildren(SysMenu node, Map<Long, List<SysMenu>> childrenMap) {
        Long id = node.getId();
        if (id == null) {
            node.setChildren(Collections.emptyList());
            return;
        }
        List<SysMenu> rawChildren = childrenMap.getOrDefault(id, Collections.emptyList());
        if (rawChildren.isEmpty()) {
            node.setChildren(Collections.emptyList());
            return;
        }
        // 根据你的层级规则过滤子节点:
        // - M 只能挂 M/C(看你业务,很多系统 M 下是 C)
        // - C 只能挂 F(按钮)或(有些系统 C 下还能挂 C)
        // - F 一般不挂任何子节点
        String type = node.getMenuType();
        List<SysMenu> children = rawChildren.stream()
                .filter(Objects::nonNull)
                .filter(child -> {
                    if ("M".equals(type)) {
                        return "M".equals(child.getMenuType()) || "C".equals(child.getMenuType());
                    }
                    if ("C".equals(type)) {
                        return "F".equals(child.getMenuType()); // 若允许 C 下还有 C,可改为:return "C".equals(...) || "F".equals(...)
                    }
                    return false; // F 不再往下挂
                })
                .sorted(Comparator.comparing(SysMenu::getOrderNum, Comparator.nullsLast(Integer::compareTo)))
                .collect(Collectors.toList());
        node.setChildren(children);
        // 继续递归
        for (SysMenu child : children) {
            fillChildren(child, childrenMap);
        }
    }
    @Override
    public List<SysMenu> getPermission() {
        return Collections.emptyList();
    }
}
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPointServiceImpl.java
@@ -59,11 +59,6 @@
    @Override
    public NearestVo nearest(NearestDto dto) {
        // 获取所有的站点信息
        List<XpPoint> list = list(new LambdaQueryWrapper<XpPoint>().eq(XpPoint::getOperationStatus, 2).eq(XpPoint::getPointType,1).isNotNull(XpPoint::getElectronicAreaId));
        NearestVo nearestVo = new NearestVo();
        List<NearestNextVo> nearestNextVos = new ArrayList<>();
        LocalTime now = LocalTime.now();
        // 走缓存
        Object orderDistanceObj = redisService.getCacheObject(CacheConstants.NEAR_POINT_DISTANCE);
        Integer orderDistance;
@@ -73,6 +68,15 @@
            XpSystemSetting xpSystemSetting = xpSystemSettingMapper.selectOne(null);
            orderDistance = xpSystemSetting.getNearPointDistance();
        }
        // 获取所有的站点信息
        List<XpPoint> list = list(new LambdaQueryWrapper<XpPoint>().eq(XpPoint::getOperationStatus, 2).eq(XpPoint::getPointType,1).isNotNull(XpPoint::getElectronicAreaId)
                // 添加距离计算字段
                .apply("ST_Distance_Sphere(point({0}, {1}), point(point_lon, point_lat)) <= {2}",
                        dto.getLongitude(), dto.getLatitude(), orderDistance));
        NearestVo nearestVo = new NearestVo();
        List<NearestNextVo> nearestNextVos = new ArrayList<>();
        LocalTime now = LocalTime.now();
        for (XpPoint xpPoint : list) {
            // 判断是否在时间段内
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/holiday/HolidayUtil.java
@@ -78,4 +78,36 @@
        }
        return 0;
    }
    /**
     * 2非工作日  0工作日  1节假日 3调休日
     * @return
     */
    public  Integer isHolidayToday(LocalDate now) {
        Object cacheObject = redisService.getCacheObject(CacheConstants.IS_DAY + now);
        // 判断缓存里面是否有节假日算法
        if (cacheObject!=null) {
            return Integer.valueOf(cacheObject.toString());
        }
        String result = HttpUtils.sendGet("https://publicapi.xiaoai.me/holiday/day?date=" +now);
//        String result = HttpUtils.sendGet("https://publicapi.xiaoai.me/holiday/day?date=2026-01-04");
        JSONObject jsonObject = JSONObject.parseObject(result);
        Object data = jsonObject.get("data");
        JSONArray parse = JSONArray.parse(data.toString());
        for (int i = 0; i < parse.size(); i++) {
            JSONObject jsonObject1 = parse.getJSONObject(i);
            String daytype = jsonObject1.get("daytype").toString();
            if (daytype.equals("0")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                return 0;
            } else if (daytype.equals("1")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", 1l, TimeUnit.DAYS);
                return 1;
            } else if (daytype.equals("2")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                return 0;
            }
        }
        return 0;
    }
}
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/vo/XpTradingStrategyDetailVO.java
@@ -69,6 +69,18 @@
    @ApiModelProperty(value = "三轮推单时间")
    private Integer threePushTime;
    @ApiModelProperty(value = "四轮推单距离")
    private Double fourPushDistance;
    @ApiModelProperty(value = "四轮推单时间")
    private Integer fourPushTime;
    @ApiModelProperty(value = "五轮推单距离")
    private Double fivePushDistance;
    @ApiModelProperty(value = "五轮推单时间")
    private Integer fivePushTime;
    @ApiModelProperty(value = "规则类型 1=单手举手 2=多手举手")
    private Integer ruleType;
xp-robotaxi-order-boot/pom.xml
@@ -106,7 +106,11 @@
            <artifactId>unified-verification</artifactId>
            <version>1.5.2-v2</version>
        </dependency>
<!--        <dependency>-->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
        </dependency>
        <!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>-->
<!--            <version>5.0.0</version>-->
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/config/DataUpdateHandlerConfig.java
New file
@@ -0,0 +1,62 @@
package com.xiaopeng.order.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.common.security.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
/**
 * 数据更新操作处理
 * 自动填充 createBy、updateBy、createTime、updateTime 字段
 *
 * @author mitao
 * @date 2026-02-14
 */
@Slf4j
@Configuration
public class DataUpdateHandlerConfig implements MetaObjectHandler {
    /**
     * 新增数据执行
     *
     * @param metaObject 元对象
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        String userName = "";
        try {
            userName = SecurityUtils.getLoginUser().getSysUser().getNickName();
            log.info("DataUpdateInterceptor insertFill");
        } catch (Exception e) {
            log.error("DataUpdateInterceptor insertFill error:{}", e.getMessage());
        }
        this.setFieldValByName("createBy", userName, metaObject);
        this.setFieldValByName("updateBy", userName, metaObject);
        this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);
        this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
    }
    /**
     * 修改数据执行
     *
     * @param metaObject 元对象
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        String userName = "";
        try {
            userName = SecurityUtils.getLoginUser().getSysUser().getNickName();
            log.info("DataUpdateInterceptor updateFill");
        } catch (Exception e) {
            log.error("DataUpdateInterceptor updateFill error:{}", e.getMessage());
        }
        this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
        this.setFieldValByName("updateBy", userName, metaObject);
    }
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/constant/I18nKeyConstant.java
@@ -18,5 +18,6 @@
    String ORDER_REASSIGN_NEW_VEHICLE_OPERATING_STATUS_INVALID = "order.reassign.new.vehicle.operating.status.invalid";
    String ORDER_REASSIGN_NEW_VEHICLE_NOT_IN_ACCEPT_MODE = "order.reassign.new.vehicle.not.in.accept.mode";
    String ORDER_REASSIGN_NEW_VEHICLE_NOT_START_ACCEPT = "order.reassign.new.vehicle.not.start.accept";
    String ORDER_REASSIGN_NEW_VEHICLE_NOT_FREE = "order.reassign.new.vehicle.not.free";
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderEvaluateController.java
@@ -5,7 +5,6 @@
import com.xiaopeng.order.model.query.OrderEvaluatePageQuery;
import com.xiaopeng.order.model.vo.OrderEvaluatePageVO;
import com.xiaopeng.order.service.IXpOrderEvaluateService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
@@ -25,7 +24,6 @@
 */
@RestController
@RequestMapping("/api/v1/orderEvaluate")
@Api(tags = "订单评价管理")
public class XpOrderEvaluateController {
    @Resource
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/job/base/AbstractJob.java
@@ -10,7 +10,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractJob implements Job{
public abstract class AbstractJob implements Job {
    protected Logger logger = LoggerFactory.getLogger(getClass());
    @Override
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -33,8 +33,8 @@
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetVehicleClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.account.XpAppUser;
import com.xiaopeng.api.model.operation.XpGeoQueryEntity;
import com.xiaopeng.api.model.operation.XpFeedbackType;
import com.xiaopeng.api.model.operation.XpGeoQueryEntity;
import com.xiaopeng.api.model.operation.XpPoint;
import com.xiaopeng.api.model.operation.XpPricing;
import com.xiaopeng.api.model.order.*;
@@ -80,16 +80,13 @@
import com.xiaopeng.order.utils.TrajectoryGenerator;
import com.xiaopeng.order.utils.holiday.HolidayUtil;
import com.xiaopeng.order.vo.OrderDetailVo;
import io.swagger.annotations.ApiModelProperty;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
@@ -179,10 +176,10 @@
        wrapper.orderByDesc(XpOrder::getCreateTime);
        // 2. 分页查询
        PageInfo<XpOrder> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        PageInfo<XpOrder> orderPage = this.page(pageInfo, wrapper);
        Page<XpOrder> page = new Page<>(query.getPageNum(), query.getPageSize());
        Page<XpOrder> pageResult = this.page(page, wrapper);
        if (CollectionUtils.isEmpty(orderPage.getRecords())) {
        if (CollectionUtils.isEmpty(pageResult.getRecords())) {
            PageInfo<XpOrderPageVO> resultPage = new PageInfo<>(query.getPageNum(), query.getPageSize());
            resultPage.setTotal(0L);
            resultPage.setRecords(new ArrayList<>());
@@ -193,7 +190,7 @@
        Set<Long> userIds = new HashSet<>();
        Set<Long> vehicleIds = new HashSet<>();
        Set<Long> fleetIds = new HashSet<>();
        for (XpOrder order : orderPage.getRecords()) {
        for (XpOrder order : pageResult.getRecords()) {
            if (Objects.nonNull(order.getAppUserId())) {
                userIds.add(order.getAppUserId());
            }
@@ -211,13 +208,13 @@
        Map<Long, XpFleet> fleetMap = batchGetFleets(fleetIds);
        // 5. 组装 VO
        List<XpOrderPageVO> voList = orderPage.getRecords().stream()
        List<XpOrderPageVO> voList = pageResult.getRecords().stream()
                .map(order -> convertToPageVO(order, userMap, vehicleMap, fleetMap))
                .collect(Collectors.toList());
        // 6. 返回分页结果
        PageInfo<XpOrderPageVO> resultPage = new PageInfo<>(query.getPageNum(), query.getPageSize());
        resultPage.setTotal(orderPage.getTotal());
        resultPage.setTotal(pageResult.getTotal());
        resultPage.setRecords(voList);
        return resultPage;
    }
@@ -561,6 +558,9 @@
        if (Objects.isNull(newVehicle.getAcceptOrTestStatus()) || newVehicle.getAcceptOrTestStatus() != 1) {
            throw new ServiceException("新车辆未开始接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_START_ACCEPT);
        }
        if (Objects.isNull(newVehicle.getState()) || newVehicle.getState() != 0) {
            throw new ServiceException("新车辆已接单,请重新选择", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_FREE);
        }
        // 6. 保存原车辆和车队信息
        Long oldVehicleId = order.getVehicleId();
@@ -619,6 +619,7 @@
        log.info("订单改派成功,订单ID: {}, 原车辆ID: {}, 新车辆ID: {}, 原车队ID: {}, 新车队ID: {}, 操作人: {}",
                orderId, oldVehicleId, newVehicleId, oldFleetId, newFleetId, SecurityUtils.getUsername());
        //推送消息
    }
    @Override
@@ -955,7 +956,7 @@
            userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo);
        }
        // 根据订单所属开通城市id 查询定价
        R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCityId(order.getCityId());
        R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCid(order.getCityId());
        XpPricing pricing = pricingByCityId.getData();
        if (pricing == null) {
            throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS);
xp-robotaxi-order-boot/src/main/resources/i18n/messages.properties
@@ -9,4 +9,5 @@
order.reassign.new.vehicle.operating.status.invalid=新车辆运营状态异常,无法接单
order.reassign.new.vehicle.not.in.accept.mode=新车辆当前不在接单模式
order.reassign.new.vehicle.not.start.accept=新车辆未开始接单
order.reassign.new.vehicle.not.free=新车辆已接单,请重新选择
order.pricing.not.exists=当前城市未配置定价规则
xp-robotaxi-order-boot/src/main/resources/i18n/messages_en_US.properties
@@ -9,5 +9,6 @@
order.reassign.new.vehicle.operating.status.invalid=The operation status of the new vehicle is abnormal and orders cannot be accepted
order.reassign.new.vehicle.not.in.accept.mode=The new vehicle is currently not in order-taking mode
order.reassign.new.vehicle.not.start.accept=The new vehicle has not started taking orders yet
order.reassign.new.vehicle.not.free=The new vehicle has been ordered. Please choose again
welcome.message=Welcome to our application!
order.pricing.not.exists=The current city has not been configured with pricing rules
xp-robotaxi-order-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -9,5 +9,6 @@
order.reassign.new.vehicle.operating.status.invalid=新车辆运营状态异常,无法接单
order.reassign.new.vehicle.not.in.accept.mode=新车辆当前不在接单模式
order.reassign.new.vehicle.not.start.accept=新车辆未开始接单
order.reassign.new.vehicle.not.free=新车辆已接单,请重新选择
welcome.message=欢迎来到我们的应用程序!
order.pricing.not.exists=当前城市未配置定价规则
xp-robotaxi-vehicle-convoy-boot/pom.xml
@@ -115,8 +115,8 @@
            <version>4.4.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>com.google.guava</artifactId>
                    <groupId>guava</groupId>
                    <artifactId>guava</artifactId>
                    <groupId>com.google.guava</groupId>
                </exclusion>
            </exclusions>
        </dependency>
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/constant/I18nKeyConstant.java
@@ -25,4 +25,7 @@
    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";
    // ========== 车辆开关车门 ==========
    String VEHICLE_NOT_P_GEAR = "vehicle.not.p.gear";
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpFleetOperationsController.java
@@ -54,4 +54,23 @@
                .list().stream().map(XpFleetVehicle::getVehicleId).collect(Collectors.toList());
        return R.ok(vehicleIds);
    }
    /**
     * 根据运维人员ID查询关联的车队ID列表(内部接口)
     *
     * @param userId 运维人员ID
     * @return 车队ID列表
     */
    @ApiOperation(value = "根据运维人员ID查询关联的车队ID列表", tags = {"内部接口"})
    @GetMapping("/inner/fleet-operations/getFleetIdsByUserId/{userId}")
    public R<List<Long>> getFleetIdsByUserId(@PathVariable("userId") Long userId) {
        List<Long> fleetIds = xpFleetOperationsService.lambdaQuery()
                .eq(XpFleetOperations::getOperationsUserId, userId)
                .list()
                .stream()
                .map(XpFleetOperations::getFleetId)
                .distinct()
                .collect(Collectors.toList());
        return R.ok(fleetIds);
    }
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java
@@ -18,9 +18,11 @@
import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandEnum;
import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandParamVo;
import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandVo;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.vehicleConvoy.constant.I18nKeyConstant;
import com.xiaopeng.vehicleConvoy.mapper.XpVehicleMapper;
import com.xiaopeng.vehicleConvoy.model.dto.*;
@@ -67,9 +69,10 @@
    @Autowired
    private IXpVehicleAnnualRecordService xpVehicleAnnualRecordService;
    @Resource
    private XpuVinClient xpuVinClient;
    @Autowired
    private RedisService redisService;
    @Override
    public List<XpVehicle> listByIds(List<Long> ids) {
@@ -142,6 +145,11 @@
        if("door".equals(dto.getDoorType())){
            vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_UNLOCK);
        }else {
            // 开启后备箱 判断是否为P档
            Integer gearLevel = redisService.getCacheObject(CacheConstants.GEAR_LEVEL + xpVehicle.getVehicleVin());
            if(gearLevel == null || gearLevel != 4){
                throw new ServiceException("车辆非P档,无法开启后备箱", I18nKeyConstant.VEHICLE_NOT_P_GEAR);
            }
            vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_TRUNK_OPEN);
        }
        try{
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_en_US.properties
@@ -12,4 +12,5 @@
vehicle.import.data.empty=Import data is empty!
vehicle.operation.info.incomplete=The following information is incomplete and cannot start operation: {0}
vehicle.batch.operation.condition.not.met=The following vehicles do not meet operating conditions: {0}
vehicle.time.expired=The following dates have expired, please enter valid dates: {0}
vehicle.time.expired=The following dates have expired, please enter valid dates: {0}
vehicle.not.p.gear=The trunk cannot be opened when the vehicle is not in P gear
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -12,4 +12,5 @@
vehicle.import.data.empty=导入数据为空!
vehicle.operation.info.incomplete=以下信息未完善,无法开始运营:{0}
vehicle.batch.operation.condition.not.met=以下车辆不满足运营条件:{0}
vehicle.time.expired=以下时间已过期,请填写有效日期:{0}
vehicle.time.expired=以下时间已过期,请填写有效日期:{0}
vehicle.not.p.gear=车辆非P档,无法开启后备箱