puzhibing
2026-05-10 f160b9d5bd3754a7ab6092d9df110cb0568d2a47
手动实现翼虎登录接口集成和校验
3个文件已添加
7个文件已修改
459 ■■■■■ 已修改文件
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XtigerSSOController.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/SysLoginService.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/vo/OIDCUserInfoVo.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/vo/SSOToken.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/account/UserSubscribeMessageClient.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/web/domain/AjaxResult.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-security/src/main/java/com/xiaopeng/common/security/handler/GlobalExceptionHandler.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/impl/ActiveOrderServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpPointServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XtigerSSOController.java
New file
@@ -0,0 +1,134 @@
package com.xiaopeng.account.controller;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.xiaopeng.account.service.impl.SysLoginService;
import com.xiaopeng.account.vo.OIDCUserInfoVo;
import com.xiaopeng.account.vo.SSOToken;
import com.xiaopeng.api.model.account.SysRole;
import com.xiaopeng.api.vo.account.LoginOperationUser;
import com.xiaopeng.api.vo.account.LoginUser;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.security.service.TokenService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/ssoAuthSdk")
public class XtigerSSOController {
    @Value("${xiaopeng.xtiger.sso.issuer}")
    private String issuer;
    @Value("${xiaopeng.xtiger.sso.appId}")
    private String appId;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private SysLoginService sysLoginService;
    /**
     * 翼虎web登录重定向接口
     * 通过code换取token
     *
     * @param code
     * @param state
     */
    @GetMapping("/callback")
    public R<?> callback(String code, String state, HttpServletRequest request) {
        log.info("翼虎登录重定向接口, code: {}, state: {}", code, state);
        HttpRequest post = HttpUtil.createPost(issuer + "/oidc/token/v2");
        post.form("client_id", appId);
        post.form("code", code);
        post.form("grant_type", "authorization_code");
        HttpResponse execute = post.execute();
        if (!execute.isOk()) {
            throw new ServiceException("登录失败,请稍后重试。");
        }
        String body = execute.body();
        log.info("翼虎登录重定向接口, 返回结果: {}", body);
        SSOToken ssoToken = JSON.parseObject(body, SSOToken.class);
        String accessToken = ssoToken.getAccessToken();
        OIDCUserInfoVo userInfo = getUserInfo(accessToken);
        return login(userInfo, request);
    }
    /**
     * 翼虎app登录换取本系统token
     *
     * @param request
     * @return
     */
    @GetMapping("/appLogin")
    public R<?> appLogin(Integer deviceType, HttpServletRequest request) {
        String accessTokenHeader = request.getHeader("Authorization");
        String accessToken = StringUtils.isEmpty(accessTokenHeader) ? null : accessTokenHeader.replace("Bearer ", "");
        if (StringUtils.isEmpty(accessToken)) {
            throw new ServiceException("登录失败,请稍后重试。");
        }
        OIDCUserInfoVo userInfo = getUserInfo(accessToken);
        return appLogin(userInfo, deviceType, request);
    }
    public OIDCUserInfoVo getUserInfo(String accessToken) {
        log.info("翼虎登录使用token获取用户信息");
        HttpRequest post = HttpUtil.createGet(issuer + "/oidc/userinfo");
        post.header("Authorization", "Bearer " + accessToken);
        HttpResponse execute = post.execute();
        if (!execute.isOk()) {
            throw new ServiceException("登录失败,请稍后重试。");
        }
        String body = execute.body();
        log.info("翼虎登录使用token获取用户信息, 返回结果: {}", body);
        OIDCUserInfoVo ssoToken = JSON.parseObject(body, OIDCUserInfoVo.class);
        return ssoToken;
    }
    public R<?> login(OIDCUserInfoVo userInfo, HttpServletRequest request) {
        // 用户登录
        LoginUser loginUser = sysLoginService.login(userInfo.getPhone_number(), request);
        HashMap<String, Object> map = new HashMap<>();
        map.put("token", tokenService.createToken(loginUser));
        List<SysRole> roles = loginUser.getSysUser().getRoles();
        // if (CollectionUtils.isEmpty(roles)) {
        //     throw new ServiceException("请关联角色!", I18nKeyConstant.LOGIN_ROLE_NOT_BIND);
        // }
        // map.put("roleName",roles.get(0).getRoleName());
        map.put("info", userInfo);
        // 修改用户最后登录时间
        return R.ok(map);
    }
    public R<?> appLogin(OIDCUserInfoVo userInfo, Integer deviceType, HttpServletRequest request) {
        LoginOperationUser loginUser = sysLoginService.verificationCodeLogin(userInfo.getPhone_number(), deviceType, request);
        HashMap<String, Object> map = new HashMap<>();
        map.put("token", tokenService.createOperationUserToken(loginUser));
//        List<SysRole> roles = userInfo.getXpOperationsUser().getRoles();
//        if (CollectionUtils.isEmpty(roles)) {
//            return R.fail("请关联角色!");
//        }
//        map.put("roleName", roles.get(0).getRoleName());
        map.put("info", loginUser);
        return R.ok(map);
    }
}
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/SysLoginService.java
@@ -113,6 +113,54 @@
        // recordLogService.recordLogininfor(request, user.getId().intValue(), username, Constants.LOGIN_SUCCESS_STATUS, "登录成功");
        return userInfo;
    }
    /**
     * 登录
     */
    public LoginUser login(String username, HttpServletRequest request) {
        username = username.trim();
        // 查询用户信息
        SysUser user = userService.selectUserByUserName(username);
        if (StringUtils.isNull(user)) {
            throw new ServiceException("帐号不存在或已被删除", I18nKeyConstant.ACCOUNT_NOT_EXISTS);
        }
        // 角色集合
        Set<String> roles = permissionService.getRolePermission(user);
        // 权限集合
        Set<String> menuPermission = permissionService.getMenuPermission(user);
        LoginUser userInfo = new LoginUser();
        userInfo.setSysUser(user);
        userInfo.setRoles(roles);
        userInfo.setPermissions(menuPermission);
        if (StringUtils.isNull(user)) {
            recordLogService.recordLogininfor(request, null, username, Constants.LOGIN_FAIL_STATUS, "登录用户不存在");
            throw new ServiceException("用户名/密码错误", I18nKeyConstant.LOGIN_USERNAME_PASSWORD);
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH) {
            recordLogService.recordLogininfor(request, user.getId().intValue(), username, Constants.LOGIN_FAIL_STATUS, "用户名不在指定范围");
            throw new ServiceException("用户名不在指定范围",I18nKeyConstant.LOGIN_USERNAME_NOT_VALID);
        }
        // IP黑名单校验
        String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
            recordLogService.recordLogininfor(request, user.getId().intValue(), username, Constants.LOGIN_FAIL_STATUS, "很遗憾,访问IP已被列入系统黑名单");
            throw new ServiceException("很遗憾,访问IP已被列入系统黑名单", I18nKeyConstant.LOGIN_BLACK_LIST);
        }
        if (user.getDeleted()) {
            recordLogService.recordLogininfor(request, user.getId().intValue(), username, Constants.LOGIN_FAIL_STATUS, "对不起,您的账号已被删除");
            throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
        }
        if (Integer.valueOf("1").equals(user.getStatus())) {
            recordLogService.recordLogininfor(request, user.getId().intValue(), username, Constants.LOGIN_FAIL_STATUS, "用户已停用,请联系管理员");
            throw new ServiceException("您的账号:" + username + " 已禁用", I18nKeyConstant.LOGIN_ACCOUNT_DISABLED, new Object[]{username});
        }
        return userInfo;
    }
    
    public void logout(String loginName, HttpServletRequest request) {
        recordLogService.recordLogininfor(request, null, loginName, Constants.LOGIN_SUCCESS_STATUS, "退出成功");
@@ -178,6 +226,8 @@
        operationsUserService.updateById(operationsUser);
        return loginOperationUser;
    }
    public LoginOperationUser verificationCodeLogin(String phoneNumber, String code, Integer deviceType, HttpServletRequest request) {
        LoginOperationUser loginOperationUser = new LoginOperationUser();
        phoneNumber = phoneNumber.trim();
@@ -211,7 +261,30 @@
        operationsUserService.updateById(operationsUser);
        return loginOperationUser;
    }
    public Boolean verificationCodeUpdate(UpdatePasswordDTO updatePasswordDTO, HttpServletRequest request) {
    public LoginOperationUser verificationCodeLogin(String phoneNumber, Integer deviceType, HttpServletRequest request) {
        LoginOperationUser loginOperationUser = new LoginOperationUser();
        phoneNumber = phoneNumber.trim();
        // 查询运维用户信息
        XpOperationsUser operationsUser = operationsUserService.lambdaQuery().eq(
                XpOperationsUser::getPhoneNumber, phoneNumber
        ).last("limit 1").one();
        if (StringUtils.isNull(operationsUser)) {
            throw new ServiceException("验证码错误!");
        }
        if (!operationsUser.getStatus()){
            throw new ServiceException("账号已禁用!");
        }
        loginOperationUser.setXpOperationsUser(operationsUser);
        //更新用户登录设备类型
        operationsUser.setDeviceType(deviceType);
        operationsUserService.updateById(operationsUser);
        return loginOperationUser;
    }
    public Boolean verificationCodeUpdate(UpdatePasswordDTO updatePasswordDTO, HttpServletRequest request) {
        String phoneNumber = updatePasswordDTO.getPhoneNumber();
        String code = updatePasswordDTO.getVerificationCode();
        phoneNumber = phoneNumber.trim();
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/vo/OIDCUserInfoVo.java
New file
@@ -0,0 +1,47 @@
package com.xiaopeng.account.vo;
import lombok.Data;
@Data
public class OIDCUserInfoVo {
    /**
     * 用户id
     */
    private String phone_number;
    /**
     * 用户名
     */
    private String username;
    /**
     * 姓名
     */
    private String name;
    /**
     * 昵称
     */
    private String nickname;
    /**
     * 用户头像图片地址
     */
    private String picture;
    /**
     * 工号
     */
    private String job_num;
    /**
     * 入职时间,yyyy-MM-dd格式
     */
    private String entry_date;
    /**
     * 更新时间, 时间戳, 精确到秒
     */
    private Long updated_at;
    /**
     * 更新时间,yyyy-MM-dd HH:mm:ss格式
     */
    private String updated_time;
    /**
     * 骁龙接入翼虎使用的用户中心映射uid
     */
    private Long xuid;
}
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/vo/SSOToken.java
New file
@@ -0,0 +1,36 @@
package com.xiaopeng.account.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
 * 翼虎登录token
 */
@Data
public class SSOToken {
    /**
     * 翼虎登录token
     */
    @JsonProperty("access_token")
    private String accessToken;
    /**
     * 翼虎登录token过期时间
     */
    @JsonProperty("expires_in")
    private Long expireIn;
    /**
     * 翼虎登录token类型
     */
    @JsonProperty("token_type")
    private String tokenType;
    /**
     * 翼虎登录token刷新token
     */
    @JsonProperty("refresh_token")
    private String refreshToken;
    /**
     * 翼虎登录token范围
     */
    @JsonProperty("scope")
    private String scope;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/account/UserSubscribeMessageClient.java
@@ -20,6 +20,6 @@
     * @param uid
     * @return
     */
    @GetMapping("/logan/inner/v1/subscribeMessage/getUserOpenId/{v}")
    @GetMapping("/logan/inner/v1/subscribeMessage/getUserOpenId/{uid}")
    R<String> getUserOpenId(@PathVariable("uid") Integer uid);
}
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/web/domain/AjaxResult.java
@@ -174,6 +174,20 @@
        return new AjaxResult(code, msg, null);
    }
    /**
     * 返回错误消息
     *
     * @param code 状态码
     * @param msg 返回内容
     * @return 错误消息
     */
    public static AjaxResult error(int code, String msg, Object data)
    {
        return new AjaxResult(code, msg, data);
    }
    /**
     * 是否为成功消息
     *
xp-robotaxi-common/xp-common-security/src/main/java/com/xiaopeng/common/security/handler/GlobalExceptionHandler.java
@@ -1,5 +1,7 @@
package com.xiaopeng.common.security.handler;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaopeng.common.core.constant.HttpStatus;
@@ -13,6 +15,7 @@
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.common.core.web.domain.AjaxResult;
import feign.FeignException;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,6 +30,7 @@
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
/**
 * 全局异常处理器
@@ -41,6 +45,15 @@
    private String maxFileSize;
    @Value("${spring.servlet.multipart.max-request-size:100MB}")
    private String maxRequestSize;
    @Value("${xiaopeng.xtiger.sso.issuer}")
    private String issuer;
    @Value("${xiaopeng.xtiger.sso.appId}")
    private String appId;
    @Value("${xiaopeng.xtiger.sso.serverHost}")
    private String serverHost;
    @Autowired
    private MessageSource messageSource;
@@ -76,7 +89,19 @@
    public AjaxResult notLoginException(NotLoginException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',登录校验失败'{}'", requestURI, e.getMessage());
        return AjaxResult.error(HttpStatus.UNAUTHORIZED, e.getMessage());
        String s = UUID.randomUUID().toString();
        String state1 = s.substring(0, 8) + s.substring(9, 13) + s.substring(14, 18) + s.substring(19, 23) + s.substring(24);
        String s1 = UUID.randomUUID().toString();
        String state2 = s1.substring(0, 8) + s.substring(9, 13) + s.substring(14, 18) + s.substring(19, 23) + s.substring(24);
        String nonce = RandomStringUtils.randomAlphanumeric(6);
        JSONObject jsonObject = JSON.parseObject("{\n" +
                "    \"uid\": null,\n" +
                "    \"username\": null,\n" +
                "    \"authUrl\": \"" + issuer + "/oidc/auth?scope=openid+profile+offline_access+phone+email&response_type=code&redirect_uri=" + serverHost + "/ssoAuthSdk/callback&state=" + state1 + "&nonce=" + nonce + "&client_id=" + appId + "\",\n" +
                "    \"logoutUrl\": null,\n" +
                "    \"callbackUrl\": \"" + serverHost + "/ssoAuthSdk/callback?state=" + state2 + "\"\n" +
                "  }");
        return AjaxResult.error(HttpStatus.UNAUTHORIZED, e.getMessage(), jsonObject);
    }
    /**
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/impl/ActiveOrderServiceImpl.java
@@ -178,7 +178,7 @@
                RouteVo routeRemain = JSON.parseObject(cacheObject.toString(), RouteVo.class);
                Integer xczSyDistance = routeRemain.getDistance();//行程中剩余里程
                Integer xczSyTime = routeRemain.getDuration();//行程中剩余时间
                if(systemSetting.getXczSyDistance()<xczSyDistance || systemSetting.getXczSyTime() < xczSyTime){
                if(systemSetting.getXczSyDistance() > xczSyDistance || systemSetting.getXczSyTime() * 60 > xczSyTime){
                    throw new ServiceException("即将达到终点,禁止修改目的地");
                }
            }
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpPointServiceImpl.java
@@ -231,7 +231,7 @@
            if (!locked) {
                throw new ServiceException("点位编号生成繁忙,请稍后重试", I18nKeyConstant.POINT_NUMBER_LOCK_FAIL);
            }
            validateFencePointCountLimit(pointAddDto.getElectronicAreaId());
            validateFencePointCountLimit(electronicAreaId);
            String maxPointNumber = this.baseMapper.selectMaxPointNumber(cityPrefix, POINT_BIZ_TYPE);
            int nextSerial = 1;
            if (StringUtils.hasText(maxPointNumber)) {
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java
@@ -57,6 +57,7 @@
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
@@ -1188,79 +1189,76 @@
            if (servingVehicles.getCode() != 200) {
                continue;
            }
            LonLatDto userPostion = new LonLatDto(Double.parseDouble(split[0]), Double.parseDouble(split[1]));
            log.info("连环派单查询车辆结果 {} {}", orderNo, JSON.toJSONString(servingVehicles.getData()));
            XpTradingStrategyDetailVO strategyDetailVO = tradingStrategyClient.queryById().getData();
            log.info("连环排查推单逻辑 {} {}", orderNo, JSON.toJSONString(strategyDetailVO));
            Integer distanceRegardAsEnding = strategyDetailVO.getDistanceRegardAsEnding();
            Integer timeRegardAsEnding = strategyDetailVO.getTimeRegardAsEnding();
            Integer backupPower = strategyDetailVO.getBackupPower();
            Integer destToPassenger = strategyDetailVO.getDestToPassenger();
            Integer endingTimeToUseTime = strategyDetailVO.getEndingTimeToUseTime();
            LocalDateTime rideTime = xpOrder.getRideTime();
            LocalDateTime now = LocalDateTime.now();
            Optional<XpOrder> luckyOrderOpt = servingVehicles.getData().stream()
                    //获取位置
                    .map(vin -> mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), VehiclePosition.class))
                    .map(VehiclePosition::getOrderId)
                    .map(xpOrderClient::queryOrderById)
                    .filter(R::isSuccess)
                    .map(R::getData)
                    .filter(o -> {
                        XpVehicle xpVehicle = xpVehicleClient.queryById(o.getVehicleId()).getData();
                        Object cacheObject = redisService.getCacheObject(CacheConstants.navigationEnd(o.getAppUserId().toString()));
                        RouteVo routeRemain = JSON.parseObject(cacheObject.toString(), RouteVo.class);
                        if (routeRemain.getDuration() / 60 > timeRegardAsEnding) {
                            return false;
                        }
                        if (routeRemain.getDistance() > distanceRegardAsEnding) {
                            return false;
                        }
                        RouteAndFareDto next = new RouteAndFareDto();
                        next.setOrigin(new LonLatDto(o.getStartLon(),o.getStartLat()));
                        next.setDestination(new LonLatDto(o.getEndLon(),o.getEndLat()));
                        RouteVo route = xpMapClient.getDistanceAndDuration(next).getData();
                        if (route.getDistance() > backupPower + xpVehicle.getMileageRange())
                            return false;
                        return routeRemain.getDistance() < destToPassenger
                                || now.plusSeconds(routeRemain.getDuration())
                                .plusMinutes(endingTimeToUseTime)
                                .isBefore(rideTime);
                    })
                    .findAny();
            if (luckyOrderOpt.isPresent()) {
                XpOrder luckyOder = luckyOrderOpt.get();
                log.info("有空闲车辆 {} 订单 {}", luckyOder.getOrderNumber(), orderNo);
                XpVehicle xpVehicle = xpVehicleClient.queryById(luckyOder.getVehicleId()).getData();
                // 设置路线信息
                RouteAndFareDto routeAndFareDto = new RouteAndFareDto();
                routeAndFareDto.setDestination(userPostion);
                routeAndFareDto.setOrigin(new LonLatDto(luckyOder.getEndLon(),luckyOder.getEndLat()));
                RouteVo routeToOrigin = xpMapClient.getDistanceAndDuration(routeAndFareDto).getData();
                Object cacheObject = redisService.getCacheObject(CacheConstants.navigationEnd(luckyOder.getAppUserId().toString()));
            for (String vin : servingVehicles.getData()) {
                VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), VehiclePosition.class);
                if(null == vehiclePosition){
                    log.info("连环派单 {} 车辆查询定位数据失败 {}", vin, orderNo);
                    continue;
                }
                XpOrder xpOrder1 = xpOrderClient.queryOrderById(vehiclePosition.getOrderId()).getData();
                Object cacheObject = redisService.getCacheObject(CacheConstants.navigationEnd(xpOrder1.getAppUserId().toString()));
                RouteVo routeRemain = JSON.parseObject(cacheObject.toString(), RouteVo.class);
                routeToOrigin.getPolyline().addAll(0, routeRemain.getPolyline());
                routeToOrigin.setDuration(routeToOrigin.getDuration() + routeRemain.getDuration());
                routeToOrigin.setDistance(routeToOrigin.getDistance() + routeRemain.getDistance());
                if (routeRemain.getDuration() / 60 > timeRegardAsEnding && routeRemain.getDistance() / 1000 > distanceRegardAsEnding) {
                    log.info("连环派单 {} 车辆订单结束时间不满足条件 {} 秒 {} 米 {}", vin, routeRemain.getDuration(), routeRemain.getDistance(), orderNo);
                    continue;
                }
                //此处找到车后先不忙给车推送接单消息,先修改订单上的车辆信息
                xpOrder.setVehicleId(xpVehicle.getId());
                xpOrder.setAcceptanceTime(LocalDateTime.now());
                xpOrder.setStatus(2);
                xpOrderClient.updateOrder(xpOrder);
                //剩余里程》=下一单预估里程+50
                //预估接驾行程+送驾行程
                RouteAndFareDto next = new RouteAndFareDto();
                next.setOrigin(new LonLatDto(xpOrder1.getEndLon(),xpOrder1.getEndLat()));
                next.setDestination(new LonLatDto(xpOrder.getStartLon(),xpOrder.getStartLat()));
                RouteVo route = xpMapClient.getDistanceAndDuration(next).getData();
                Object new_cacheObject = redisService.getCacheObject(CacheConstants.navigationEnd(xpOrder.getAppUserId().toString()));
                RouteVo new_routeRemain = JSON.parseObject(new_cacheObject.toString(), RouteVo.class);
                long xh = (new_routeRemain.getDistance() + route.getDistance()) / 1000 + backupPower;
                if(vehiclePosition.getMileageRange() < xh){
                    log.info("连环派单 {} 车辆剩余续航不满足派车条件 {} KM {}", vin, xh, orderNo);
                    continue;
                }
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("orderId", xpOrder.getId());
                jsonObject.put("endLongitude", xpOrder.getEndLat());
                jsonObject.put("endLatitude", xpOrder.getEndLat());
                redisService.setCacheObject(CacheConstants.nextOrder(luckyOder.getId().toString()), jsonObject.toJSONString());
                redisService.setCacheObject(CacheConstants.navigationStart(xpOrder.getAppUserId().toString()), routeToOrigin);
                redisService.removeByPattern(quoteName,xpOrder.getOrderNumber());
                log.info("将订单 {} 从队列 {} 弹出去", orderNo, quoteName);
                break;
                //下一单的起点距离本单终点小于X公里,或者下一单的用车时间与本单的预计结束时间偏差小于X分钟
                next = new RouteAndFareDto();
                next.setOrigin(new LonLatDto(xpOrder1.getEndLon(),xpOrder1.getEndLat()));
                next.setDestination(new LonLatDto(xpOrder.getStartLon(),xpOrder.getStartLat()));
                route = xpMapClient.getDistanceAndDuration(next).getData();
                //相差秒时间
                long time = routeRemain.getDuration() - xpOrder.getCreateTime().toEpochSecond(ZoneOffset.of("+8"));
                if(route.getDistance() / 1000 <= destToPassenger || time / 60 <= endingTimeToUseTime){
                    log.info("连环派单 {} 找到满足条件的车辆 {}", vin, orderNo);
                    // 设置接驾导航数据
                    //将上一单的剩余导航添加到接驾导航中
                    route.getPolyline().addAll(0, routeRemain.getPolyline());
                    route.setDuration(route.getDuration() + routeRemain.getDuration());
                    route.setDistance(route.getDistance() + routeRemain.getDistance());
                    //此处找到车后先不忙给车推送接单消息,先修改订单上的车辆信息
                    xpOrder.setVehicleId(xpOrder1.getVehicleId());
                    xpOrder.setAcceptanceTime(LocalDateTime.now());
                    xpOrder.setStatus(2);
                    xpOrderClient.updateOrder(xpOrder);
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("orderId", xpOrder.getId());
                    jsonObject.put("endLongitude", xpOrder.getEndLat());
                    jsonObject.put("endLatitude", xpOrder.getEndLat());
                    redisService.setCacheObject(CacheConstants.nextOrder(xpOrder1.getId().toString()), jsonObject.toJSONString());
                    redisService.setCacheObject(CacheConstants.navigationStart(xpOrder.getAppUserId().toString()), JSON.toJSONString(route));
                    redisService.removeByPattern(quoteName,xpOrder.getOrderNumber());
                    log.info("将订单 {} 从队列 {} 弹出去", orderNo, quoteName);
                    return true;
                }
            }
        }
        return false;