| | |
| | | |
| | | import cn.hutool.core.bean.BeanUtil; |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.lang.Snowflake; |
| | | import cn.hutool.core.util.IdUtil; |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.alibaba.fastjson2.JSONArray; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.google.common.collect.ImmutableMap; |
| | |
| | | import com.xiaopeng.api.dto.statistics.OrderStatisticsDTO; |
| | | import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO; |
| | | import com.xiaopeng.api.dto.vehicleConvoy.BaseVehicleUserDto; |
| | | import com.xiaopeng.api.feignClient.account.SysGroupMenuClient; |
| | | import com.xiaopeng.api.feignClient.account.UserResourcesClient; |
| | | import com.xiaopeng.api.feignClient.account.XpAppUserCollectAddrClient; |
| | | import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient; |
| | | import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient; |
| | | import com.xiaopeng.api.feignClient.automaticPilot.VehicleBuriedPointClient; |
| | |
| | | import com.xiaopeng.api.feignClient.map.XpMapClient; |
| | | import com.xiaopeng.api.feignClient.operation.*; |
| | | import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient; |
| | | import com.xiaopeng.api.feignClient.vehicleConvoy.UserVehicleClient; |
| | | import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient; |
| | | import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetVehicleClient; |
| | | import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; |
| | | import com.xiaopeng.api.feignClient.vehicleConvoy.*; |
| | | import com.xiaopeng.api.model.maintenance.XpTaskRule; |
| | | import com.xiaopeng.api.model.operation.*; |
| | | import com.xiaopeng.api.model.order.*; |
| | |
| | | import com.xiaopeng.api.vo.automaticPilot.VehiclePosition; |
| | | import com.xiaopeng.api.vo.automaticPilot.protobuf.OrderOperationType; |
| | | import com.xiaopeng.api.vo.automaticPilot.protobuf.OrderStatus; |
| | | import com.xiaopeng.api.vo.operation.NearestNextVo; |
| | | import com.xiaopeng.api.vo.operation.RouteVo; |
| | | import com.xiaopeng.api.vo.order.*; |
| | | import com.xiaopeng.api.vo.vehicleConvoy.AddTaxiPassengerVo; |
| | | import com.xiaopeng.api.vo.vehicleConvoy.BluetoothDeliverVo; |
| | | import com.xiaopeng.api.vo.vehicleConvoy.CancelTaxiPassengerVo; |
| | | import com.xiaopeng.common.core.constant.CacheConstants; |
| | | import com.xiaopeng.common.core.constant.Constants; |
| | |
| | | import com.xiaopeng.common.security.utils.SecurityUtils; |
| | | import com.xiaopeng.order.constant.I18nKeyConstant; |
| | | import com.xiaopeng.order.dto.MileageDto; |
| | | import com.xiaopeng.order.dto.UserOrderListDto; |
| | | import com.xiaopeng.order.job.base.QuartzManager; |
| | | import com.xiaopeng.order.job.base.TimeJobType; |
| | | import com.xiaopeng.order.job.jobs.AddCruiseMissionJob; |
| | | import com.xiaopeng.order.job.jobs.EndTripJob; |
| | | import com.xiaopeng.order.job.jobs.OrderCancelJob; |
| | | import com.xiaopeng.order.job.jobs.PushChainReactionOrderJob; |
| | | import com.xiaopeng.order.job.jobs.*; |
| | | import com.xiaopeng.order.mangodb.model.OrderTrajectory; |
| | | import com.xiaopeng.order.mangodb.service.OrderTrajectoryService; |
| | | import com.xiaopeng.order.mapper.XpCruiseOrderMapper; |
| | |
| | | import com.xiaopeng.order.model.query.XpOrderPageQuery; |
| | | import com.xiaopeng.order.model.vo.*; |
| | | import com.xiaopeng.order.service.*; |
| | | import com.xiaopeng.order.utils.GDMapGeocodingUtil; |
| | | import com.xiaopeng.order.utils.RegionCodeFilterUtil; |
| | | import com.xiaopeng.order.utils.TaxiPricingDemoService; |
| | | import com.xiaopeng.order.utils.XpPricingUtil; |
| | | import com.xiaopeng.order.utils.*; |
| | | import com.xiaopeng.order.utils.holiday.HolidayUtil; |
| | | import com.xiaopeng.order.vo.OrderDetailVo; |
| | | import com.xiaopeng.order.vo.OrderInfoVo; |
| | | import com.xiaopeng.order.vo.UserOrderListVo; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType; |
| | | import org.apache.shardingsphere.transaction.core.TransactionType; |
| | | import org.gavaghan.geodesy.Ellipsoid; |
| | | import org.gavaghan.geodesy.GlobalCoordinates; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | |
| | | import java.math.RoundingMode; |
| | | import java.text.SimpleDateFormat; |
| | | import java.time.*; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.*; |
| | | import java.util.concurrent.LinkedBlockingQueue; |
| | | import java.util.concurrent.ThreadPoolExecutor; |
| | |
| | | @Service |
| | | @RequiredArgsConstructor(onConstructor_ = @Lazy) |
| | | public class XpOrderServiceImpl extends ServiceImpl<XpOrderMapper, XpOrder> implements IXpOrderService { |
| | | |
| | | private static final String REASSIGN_CANCEL_CONTEXT_PREFIX = "REASSIGN_CANCEL_CONTEXT:"; |
| | | private static final long REASSIGN_CANCEL_CONTEXT_TTL_MINUTES = 5L; |
| | | |
| | | private final UserResourcesClient userResourcesClient; |
| | | private final XpPointClient xpPointClient; |
| | |
| | | @Resource |
| | | @Lazy |
| | | private BootCommonService bootCommonService; |
| | | @Autowired |
| | | private SysGroupMenuClient sysGroupMenuClient; |
| | | |
| | | |
| | | @Autowired |
| | |
| | | @Autowired |
| | | private HolidayUtil holidayUtil; |
| | | |
| | | @Autowired |
| | | private BluetoothUnlockTokenUtil bluetoothUnlockTokenUtil; |
| | | |
| | | @Resource |
| | | private BluetoothUnlockClient bluetoothUnlockClient; |
| | | |
| | | @Value("${robotaxi.appId:xp_robotaxi_api}") |
| | | private String appid; |
| | | |
| | | @Resource(name = "hutoolSnowflake") |
| | | private Snowflake snowflake; |
| | | |
| | | |
| | | @Override |
| | |
| | | XpVehicle vehicle = vehicleMap.get(order.getVehicleId()); |
| | | if (Objects.nonNull(vehicle)) { |
| | | vo.setPlateNumber(vehicle.getPlateNumber()); |
| | | vo.setVehicleModel(vehicle.getVehicleModel()); |
| | | vo.setVehicleModel(vehicle.getVehicleModelName()); |
| | | } |
| | | } |
| | | |
| | |
| | | @Override |
| | | @Transactional |
| | | @ShardingSphereTransactionType(TransactionType.BASE) |
| | | public void cancelOrCloseOrder(Long orderId, Integer type) { |
| | | public void cancelOrCloseOrder(Long orderId, Integer type,Boolean isFree) { |
| | | // 1. 校验订单是否存在 |
| | | XpOrder order = this.getById(orderId); |
| | | if (Objects.isNull(order)) { |
| | | throw new ServiceException("订单不存在"); |
| | | } |
| | | |
| | | // 2. 校验操作类型 |
| | | // 2. 校验操作类型 操作类型:1=取消订单,2=关闭订单 |
| | | if (type != 1 && type != 2) { |
| | | throw new ServiceException("操作类型错误,type只能为1或2"); |
| | | } |
| | |
| | | throw new ServiceException("当前订单状态不允许取消,只有待接单、待出发、接驾中状态的订单可以取消", I18nKeyConstant.ORDER_CANCEL_FAILED); |
| | | } |
| | | } else { |
| | | if(isFree == null){ |
| | | throw new ServiceException("是否豁免费用不能为空", I18nKeyConstant.ORDER_CLOSE_FAILED); |
| | | } |
| | | // 关闭订单:0=已取消,1=待接单,2=待出发,3=接驾中,4=待客上车,5=乘客已上车,6=服务中 状态可以关闭 |
| | | if (currentStatus == 7 || currentStatus == 8 || currentStatus == 9 || currentStatus == 10) { |
| | | throw new ServiceException("当前订单状态不允许关闭", I18nKeyConstant.ORDER_CLOSE_FAILED); |
| | |
| | | } else { |
| | | // 关闭订单 |
| | | closeOrder(order); |
| | | if(isFree){ |
| | | this.setFree(order.getId()); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | vo.setEndAddress(order.getEndAddress()); |
| | | vo.setOrderMoney(order.getOrderMoney()); |
| | | vo.setStatus(order.getStatus()); |
| | | vo.setStartPointName(order.getStartPointName()); |
| | | vo.setEndPointName(order.getEndPointName()); |
| | | |
| | | // 用户信息 |
| | | if (Objects.nonNull(order.getAppUserId()) && userInfoMap != null && userInfoMap.containsKey(order.getAppUserId().toString())) { |
| | | UcUserInfo userInfo = userInfoMap.get(order.getAppUserId().toString()); |
| | | vo.setNickName(userInfo.getNickName()); |
| | | vo.setPhoneNumber(userInfo.getMobile()); |
| | | vo.setPhoneNumber(sysGroupMenuClient.getPhoneDesensitization(userInfo.getMobile()).getData()); |
| | | } |
| | | |
| | | // 车辆信息 |
| | | if (Objects.nonNull(order.getVehicleId()) && vehicleMap.containsKey(order.getVehicleId())) { |
| | | XpVehicle vehicle = vehicleMap.get(order.getVehicleId()); |
| | | vo.setPlateNumber(vehicle.getPlateNumber()); |
| | | vo.setVehicleModel(vehicle.getVehicleModel()); |
| | | vo.setVehicleModel(vehicle.getVehicleModelName()); |
| | | } |
| | | |
| | | // 车队信息 |
| | |
| | | Long userId = order.getAppUserId(); |
| | | if (order.getStatus() == 1) { |
| | | order.setStatus(0); |
| | | Long appUserId = order.getAppUserId(); |
| | | order.setAppUserId(null); |
| | | baseMapper.updateById(order); |
| | | order.setAppUserId(appUserId); |
| | | redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 0, 24L, TimeUnit.HOURS); |
| | | //删除蓝牙秘钥 |
| | | String token = redisService.getCacheObject("rt:bluetooth:unlock:order:" + order.getId()); |
| | | if(StringUtils.isNotBlank(token)){ |
| | | redisService.deleteObject("rt:bluetooth:unlock:" + token); |
| | | redisService.deleteObject("rt:bluetooth:unlock:order:" + order.getId()); |
| | | XpVehicle xpVehicle = xpVehicleClient.queryById(order.getVehicleId()).getData(); |
| | | BluetoothDeliverVo vo = new BluetoothDeliverVo(); |
| | | vo.setVim(xpVehicle.getVehicleVin()); |
| | | vo.setPassengerUid(order.getAppUserId()); |
| | | BaseVehicleUserDto delete = bluetoothUnlockClient.delete(vo); |
| | | if(null == delete || delete.getCode() != 0){ |
| | | log.error("删除蓝牙解锁失败:{} {}", delete.getMsg(), order.getOrderNumber()); |
| | | } |
| | | } |
| | | // 消息推送 |
| | | Map<String, Object> params = new HashMap<>(); |
| | | Map<String, Object> content = new HashMap<>(); |
| | |
| | | cancelDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER); |
| | | cancelDto.setCancelScene(CancelOrderSceneEnum.REASSIGN_OLD_VEHICLE.getCode()); |
| | | orderMissionClient.cancelOrder(cancelDto); |
| | | cacheReassignCancelContext(orderId, oldVehicleId, newVehicleId, |
| | | oldVehicle.getVehicleVin(), currentStatus); |
| | | log.info("已取消原车辆订单指令,订单ID: {}, VIN: {}, taskId: {}, cancelScene: {}", |
| | | orderId, oldVehicle.getVehicleVin(), cancelDto.getTaskId(), cancelDto.getCancelScene()); |
| | | |
| | |
| | | |
| | | // 11.2 下发订单到新车辆 |
| | | PushOrderDto pushOrderDto = new PushOrderDto(); |
| | | pushOrderDto.setTaskId(order.getXpuTaskId()); |
| | | pushOrderDto.setVin(newVehicle.getVehicleVin()); |
| | | pushOrderDto.setOrderId(order.getId().toString()); |
| | | pushOrderDto.setStartLatitude(order.getStartLat()); |
| | |
| | | |
| | | } |
| | | |
| | | private void cacheReassignCancelContext(Long orderId, Long oldVehicleId, Long newVehicleId, |
| | | String oldVehicleVin, Integer preReassignStatus) { |
| | | JSONObject context = new JSONObject(); |
| | | context.put("orderId", orderId); |
| | | context.put("oldVehicleId", oldVehicleId); |
| | | context.put("newVehicleId", newVehicleId); |
| | | context.put("oldVehicleVin", oldVehicleVin); |
| | | context.put("cancelScene", CancelOrderSceneEnum.REASSIGN_OLD_VEHICLE.getCode()); |
| | | context.put("operationTime", System.currentTimeMillis()); |
| | | context.put("preReassignStatus", preReassignStatus); |
| | | redisService.setCacheObject(REASSIGN_CANCEL_CONTEXT_PREFIX + orderId, context.toJSONString(), |
| | | REASSIGN_CANCEL_CONTEXT_TTL_MINUTES, TimeUnit.MINUTES); |
| | | log.info("记录改派取消上下文成功,orderId: {}, oldVehicleId: {}, newVehicleId: {}, preReassignStatus: {}", |
| | | orderId, oldVehicleId, newVehicleId, preReassignStatus); |
| | | } |
| | | |
| | | /** |
| | | * 为原车辆创建返程巡游订单 |
| | | * <p> |
| | |
| | | returnOrder.setLastOrderId(lastCruise.getId()); |
| | | returnOrder.setCreateTime(LocalDateTime.now()); |
| | | returnOrder.setMissionId(lastCruise.getMissionId()); |
| | | returnOrder.setXpuTaskId(snowflake.nextId() + ""); |
| | | |
| | | // 返程起点 = 当前订单的终点(车辆当前位置附近) |
| | | returnOrder.setStartPointId(order.getEndPointId()); |
| | |
| | | |
| | | // 下发巡游订单到原车辆 |
| | | PushOrderDto pushOrderDto = new PushOrderDto(); |
| | | pushOrderDto.setTaskId(returnOrder.getXpuTaskId()); |
| | | pushOrderDto.setVin(oldVehicle.getVehicleVin()); |
| | | pushOrderDto.setOrderId("XU" + returnOrder.getId()); |
| | | pushOrderDto.setStartLatitude(returnOrder.getStartLat()); |
| | |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | @Resource |
| | | private XpAppUserCollectAddrClient xpAppUserCollectAddrClient; |
| | | @Override |
| | | public NearestNextVo quickCapsule(String uid, Long startPointId) { |
| | | |
| | | Long userId = Long.valueOf(uid); |
| | | // - 新用户处理: 历史订单数 < 3单或无匹配数据时,隐藏该胶囊。 |
| | | Long validHistoryCount = this.baseMapper.countUserValidHistoryOrders(userId); |
| | | if (validHistoryCount == null || validHistoryCount < 3) return null; |
| | | |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | LocalTime startMoment = now.minusHours(1).toLocalTime(); |
| | | LocalTime endMoment = now.plusHours(1).toLocalTime(); |
| | | LocalDate startDate = now.minusMonths(1).toLocalDate(); |
| | | LocalDateTime endDate = now.plusMonths(1); |
| | | |
| | | // 时段强相关目的地(场景触发)当前时间点(当前时间 ± 1小时)内,过去30天内前往次数最多的目的地。 |
| | | List<Long> endPointId = this.baseMapper.timeSceneOrders(startDate, endDate, startMoment, endMoment, userId); |
| | | // 判定条件: 若当前时段内无明显的规律性目的地(例如:用户在周六下午偶尔出门,且目的地不固定),则推荐用户过去30天内订单总量最高的前1名地址(需排除当前起点)。 |
| | | endPointId.addAll(this.baseMapper.frequencyOrders(startDate, endDate, startMoment, endMoment, userId)); |
| | | |
| | | Integer minOrderDistance = getMinOrderDistanceSafely(uid, startPointId); |
| | | if (minOrderDistance == null) return null;//获取用户最短可发单距离失败 |
| | | XpPoint start = getPointById(startPointId); |
| | | GlobalCoordinates startCoordinates = new GlobalCoordinates(start.getPointLon(), start.getPointLat()); |
| | | List<Long> pointIdsByUserId = Optional.ofNullable(xpAppUserCollectAddrClient.getPointIdsByUserId(Long.valueOf(uid)).getData()) |
| | | .orElse(new ArrayList<>()); |
| | | for (Long pointId : endPointId) { |
| | | XpPoint endPoint = getPointById(pointId); |
| | | // 计算直线距离,如果endPoint到startPoint的距离大于minOrderDistance,则返回该点 |
| | | GlobalCoordinates endCoordinates = new GlobalCoordinates(endPoint.getPointLon(), endPoint.getPointLat()); |
| | | double distanceMeter = GeodesyUtil.getDistanceMeter(startCoordinates, endCoordinates, Ellipsoid.WGS84); |
| | | if (distanceMeter > minOrderDistance) { |
| | | NearestNextVo ret = new NearestNextVo(); |
| | | ret.setId(endPoint.getId()); |
| | | ret.setName(endPoint.getPointName()); |
| | | ret.setLatitude(endPoint.getPointLat()); |
| | | ret.setLongitude(endPoint.getPointLon()); |
| | | ret.setDistance(new BigDecimal(distanceMeter).setScale(2, RoundingMode.HALF_UP).doubleValue()); |
| | | ret.setAddress(endPoint.getPointAddress()); |
| | | ret.setTime(endPoint.getParkingTimeLimit()); |
| | | ret.setElectronicAreaId(endPoint.getElectronicAreaId()); |
| | | // 判断是否收藏 |
| | | if(pointIdsByUserId.contains(endPoint.getId())){ |
| | | ret.setIsCollect(1); |
| | | } |
| | | return ret; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | |
| | | private RecommendationCandidate buildSceneCandidate(List<XpOrder> recentOrders, LocalDateTime now, XpPoint startPoint, Integer minOrderDistance) { |
| | | DaySceneType currentSceneType = resolveDaySceneType(now.toLocalDate()); |
| | |
| | | addCruiseMission(xpVehicle.getId()); |
| | | } |
| | | order.setStatus(0); |
| | | Long appUserId = order.getAppUserId(); |
| | | order.setAppUserId(null); |
| | | baseMapper.updateById(order); |
| | | order.setAppUserId(appUserId); |
| | | redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 0, 24L, TimeUnit.HOURS); |
| | | //删除蓝牙秘钥 |
| | | String token = redisService.getCacheObject("rt:bluetooth:unlock:order:" + order.getId()); |
| | | if(StringUtils.isNotBlank(token)){ |
| | | redisService.deleteObject("rt:bluetooth:unlock:" + token); |
| | | redisService.deleteObject("rt:bluetooth:unlock:order:" + order.getId()); |
| | | XpVehicle xpVehicle = xpVehicleClient.queryById(order.getVehicleId()).getData(); |
| | | BluetoothDeliverVo vo = new BluetoothDeliverVo(); |
| | | vo.setVim(xpVehicle.getVehicleVin()); |
| | | vo.setPassengerUid(order.getAppUserId()); |
| | | BaseVehicleUserDto delete = bluetoothUnlockClient.delete(vo); |
| | | if(null == delete || delete.getCode() != 0){ |
| | | log.error("删除蓝牙解锁失败:{} {}", delete.getMsg(), order.getOrderNumber()); |
| | | } |
| | | } |
| | | |
| | | XpOrderCancel xpOrderCancel = new XpOrderCancel(); |
| | | xpOrderCancel.setOrderId(orderId); |
| | |
| | | order.setStatus(0); |
| | | order.setAppUserId(null); |
| | | baseMapper.updateById(order); |
| | | order.setAppUserId(appUserId); |
| | | redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 0, 24L, TimeUnit.HOURS); |
| | | //删除蓝牙秘钥 |
| | | String token = redisService.getCacheObject("rt:bluetooth:unlock:order:" + order.getId()); |
| | | if(StringUtils.isNotBlank(token)){ |
| | | redisService.deleteObject("rt:bluetooth:unlock:" + token); |
| | | redisService.deleteObject("rt:bluetooth:unlock:order:" + order.getId()); |
| | | XpVehicle xpVehicle = xpVehicleClient.queryById(order.getVehicleId()).getData(); |
| | | BluetoothDeliverVo vo = new BluetoothDeliverVo(); |
| | | vo.setVim(xpVehicle.getVehicleVin()); |
| | | vo.setPassengerUid(order.getAppUserId()); |
| | | BaseVehicleUserDto delete = bluetoothUnlockClient.delete(vo); |
| | | if(null == delete || delete.getCode() != 0){ |
| | | log.error("删除蓝牙解锁失败:{} {}", delete.getMsg(), order.getOrderNumber()); |
| | | } |
| | | } |
| | | |
| | | XpOrderCancel xpOrderCancel = new XpOrderCancel(); |
| | | xpOrderCancel.setOrderId(Long.valueOf(orderId)); |
| | | xpOrderCancel.setUserId(1l); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public R<String> updateOrderStatus(Long orderId, Integer status) { |
| | | public R<String> updateOrderStatus(Long orderId, Integer status, Boolean orderException) { |
| | | XpOrder xpOrder = this.getById(orderId); |
| | | if (Objects.isNull(xpOrder)) { |
| | | return R.fail("订单不存在"); |
| | |
| | | case 6: |
| | | // 行程开始时间 |
| | | xpOrder.setTripBegin(LocalDateTime.now()); |
| | | //添加预估到达时间 |
| | | String cacheObject = redisService.getCacheObject("ORDER_NAVIGATION_END_DATA:" + xpOrder.getAppUserId()); |
| | | if(StringUtils.isNotBlank(cacheObject)){ |
| | | RouteVo routeVo = JSONObject.parseObject(cacheObject, RouteVo.class); |
| | | xpOrder.setEstimatedArrivalTime(LocalDateTime.now().plusSeconds(routeVo.getDuration())); |
| | | } |
| | | redisService.deleteObject("ORDER_NAVIGATION_START_DATA:" + xpOrder.getAppUserId()); |
| | | break; |
| | | case 7: |
| | |
| | | }); |
| | | } |
| | | |
| | | //创建定时结束订单和等待结束后下发巡游单的定时任务 |
| | | Integer arriveTime = xpSystemSettingClient.getOrderFinishTime().getData(); |
| | | // 添加自动结束行程定时任务job |
| | | Map<String, ? extends Object> endTripJobMaps = |
| | |
| | | .put("appid", appid) |
| | | .put("orderType", OrderType.USER_ORDER) |
| | | .build(); |
| | | |
| | | //如果是异常订单,需要定时创建客服工单 |
| | | if(orderException){ |
| | | XpSystemSetting xpSystemSetting = xpSystemSettingClient.getSystemSettingOne().getData(); |
| | | QuartzManager.addJob( |
| | | OrderExceptionJob.class, |
| | | (OrderExceptionJob.name + xpOrder.getId()).toUpperCase(), |
| | | TimeJobType.ADD_CRUISE_MISSION, |
| | | DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(xpSystemSetting.getOrderExceptionalTimeout())), |
| | | jobMaps |
| | | ); |
| | | } |
| | | |
| | | //判断是否有连环单 |
| | | Long id = redisService.getCacheObject(CacheConstants.nextOrder(xpOrder.getId().toString())); |
| | |
| | | this.endTrip(xpOrder.getId(),appUserId.toString()); |
| | | |
| | | //将支付金额修改成0 并将状态修改成 订单状态9 |
| | | setFree(xpOrder.getId()); |
| | | return R.ok(); |
| | | } |
| | | |
| | | /** |
| | | * 豁免 |
| | | */ |
| | | public void setFree(Long id){ |
| | | |
| | | //将支付金额修改成0 并将状态修改成 订单状态9 |
| | | XpOrder xpOrder1 = new XpOrder(); |
| | | xpOrder1.setId(xpOrder.getId()); |
| | | xpOrder1.setId(id); |
| | | xpOrder1.setIsFree( true);//豁免 |
| | | xpOrder1.setPayMoney(BigDecimal.ZERO); |
| | | xpOrder1.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode()); |
| | | xpOrder1.setUpdateTime(LocalDateTime.now()); |
| | | baseMapper.updateById(xpOrder1); |
| | | redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), xpOrder1.getStatus(), 24L, TimeUnit.HOURS); |
| | | redisService.setCacheObject("ORDER_STATUS:" + id, xpOrder1.getStatus(), 24L, TimeUnit.HOURS); |
| | | |
| | | return R.ok(); |
| | | } |
| | | |
| | | |
| | |
| | | throw new RuntimeException("订单状态错误"); |
| | | } |
| | | order.setStatus(8); |
| | | Long appUserId = order.getAppUserId(); |
| | | order.setAppUserId(null); |
| | | order.setTripEnd(LocalDateTime.now()); |
| | | baseMapper.updateById(order); |
| | | order.setAppUserId(appUserId); |
| | | redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 8, 24L, TimeUnit.HOURS); |
| | | //删除蓝牙秘钥 |
| | | String token = redisService.getCacheObject("rt:bluetooth:unlock:order:" + orderId); |
| | | if(StringUtils.isNotBlank(token)){ |
| | | redisService.deleteObject("rt:bluetooth:unlock:" + token); |
| | | redisService.deleteObject("rt:bluetooth:unlock:order:" + orderId); |
| | | XpVehicle xpVehicle = xpVehicleClient.queryById(order.getVehicleId()).getData(); |
| | | BluetoothDeliverVo vo = new BluetoothDeliverVo(); |
| | | vo.setVim(xpVehicle.getVehicleVin()); |
| | | vo.setPassengerUid(order.getAppUserId()); |
| | | BaseVehicleUserDto delete = bluetoothUnlockClient.delete(vo); |
| | | if(null == delete || delete.getCode() != 0){ |
| | | log.error("删除蓝牙解锁失败:{} {}", delete.getMsg(), order.getOrderNumber()); |
| | | } |
| | | } |
| | | |
| | | // 根据订单所属开通城市id 查询定价 |
| | | XpPricing pricing = bootCommonService.getXpPricing(order.getCityId(),order.getVehicleId()); |
| | |
| | | xpCruiseOrder.setStartPointName(order.getEndPointName()); |
| | | xpCruiseOrder.setStartLon(order.getEndLon()); |
| | | xpCruiseOrder.setStartLat(order.getEndLat()); |
| | | xpCruiseOrder.setXpuTaskId(snowflake.nextId() + ""); |
| | | |
| | | // 完成的直接走终点 终止的走起点 |
| | | if(null != xpCruiseOrders){ |
| | |
| | | log.info("添加巡游单成功 {}", xpCruiseOrder.getId()); |
| | | //下发XUP巡游订单报文 |
| | | PushOrderDto pushOrderDto = new PushOrderDto(); |
| | | pushOrderDto.setTaskId(xpCruiseOrder.getXpuTaskId()); |
| | | pushOrderDto.setVin(xpCruiseOrder.getVehicleVin()); |
| | | pushOrderDto.setOrderId("XU" + xpCruiseOrder.getId()); |
| | | pushOrderDto.setStartLatitude(xpCruiseOrder.getStartLat()); |
| | |
| | | xpCruiseOrder.setFleetId(vehicle.getFleetId()); |
| | | xpCruiseOrder.setVehicleVin(vehicle.getVehicleVin()); |
| | | xpCruiseOrder.setOrderType(2); |
| | | xpCruiseOrder.setXpuTaskId(snowflake.nextId() + ""); |
| | | // 返程起点是当前正常订单的终点 |
| | | xpCruiseOrder.setStartPointId(null); |
| | | xpCruiseOrder.setStartPointName(null); |
| | |
| | | |
| | | //下发XPU消息报文 |
| | | PushOrderDto pushOrderDto = new PushOrderDto(); |
| | | pushOrderDto.setTaskId(xpCruiseOrder.getXpuTaskId()); |
| | | pushOrderDto.setVin(xpCruiseOrder.getVehicleVin()); |
| | | pushOrderDto.setOrderId("XU" + xpCruiseOrder.getId()); |
| | | pushOrderDto.setStartLatitude(xpCruiseOrder.getStartLat()); |
| | |
| | | this.updateById(order); |
| | | } |
| | | res.setTime(new BigDecimal(order.getDuration() == null ? 0 : order.getDuration()).multiply(new BigDecimal(60)).setScale(0, RoundingMode.HALF_UP)); |
| | | XpSystemSetting xpSystemSetting = xpSystemSettingClient.getSystemSettingOne().getData(); |
| | | if(null != xpSystemSetting){ |
| | | res.setOperateFree(xpSystemSetting.getOperateFree()); |
| | | } |
| | | return R.ok(res); |
| | | } |
| | | |
| | |
| | | |
| | | //生成前往上车点的导航数据 |
| | | //查询车辆位置 |
| | | VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(dto.getXpVehicleVin())), VehiclePosition.class); |
| | | if (Objects.isNull(vehiclePosition)) { |
| | | throw new RuntimeException("车辆位置信息不存在"); |
| | | } |
| | | //TODO 本地环境默认写死 |
| | | // VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(dto.getXpVehicleVin())), VehiclePosition.class); |
| | | // if (Objects.isNull(vehiclePosition)) { |
| | | // throw new RuntimeException("车辆位置信息不存在"); |
| | | // } |
| | | |
| | | LonLatDto destination = new LonLatDto(); |
| | | LonLatDto origin = new LonLatDto(); |
| | | //查询司机当前位置到起点的线路 |
| | | origin.setLongitude(vehiclePosition.getPoint().getX()); |
| | | origin.setLatitude(vehiclePosition.getPoint().getY()); |
| | | // origin.setLongitude(vehiclePosition.getPoint().getX()); |
| | | // origin.setLatitude(vehiclePosition.getPoint().getY()); |
| | | origin.setLongitude(104.055499); |
| | | origin.setLatitude(30.586747); |
| | | destination.setLongitude(order.getStartLon()); |
| | | destination.setLatitude(order.getStartLat()); |
| | | |
| | |
| | | |
| | | //推送乘客车辆到达预约点的导航数据 |
| | | sendRoute(addTaxiPassengerVo.getUid(), orderId, "ORDER_NAVIGATION_START_DATA", dto.getXpVehicleVin()); |
| | | |
| | | //生成蓝牙token |
| | | //生成过期时间 (接单时间+到达上车点预估时间+上车点等待时间+行程预估时间+下车点等待时间+30分钟) |
| | | XpPoint startPoint = xpPointClient.queryById(order.getStartPointId()).getData(); |
| | | Object cacheObject = redisService.getCacheObject("ORDER_NAVIGATION_END_DATA:" + appUserId); |
| | | RouteVo routeVo1 = JSON.parseObject(cacheObject.toString(), RouteVo.class); |
| | | XpPoint endPoint = xpPointClient.queryById(order.getEndPointId()).getData(); |
| | | LocalDateTime localDateTime = order.getAcceptanceTime().plusMinutes(routeVo.getDuration()).plusMinutes(startPoint.getParkingTimeLimit()) |
| | | .plusMinutes(routeVo1.getDuration()).plusMinutes(endPoint.getParkingTimeLimit()).plusMinutes(30); |
| | | long expirationTime = localDateTime.toEpochSecond(ZoneOffset.of("+8")); |
| | | //生成token |
| | | String token = bluetoothUnlockTokenUtil.generateBluetoothUnlockToken(order.getAppUserId(), |
| | | order.getId(), order.getVehicleId(), expirationTime); |
| | | log.info("生成蓝牙token:{} 有效期:{} 订单号: {}", token, expirationTime, order.getOrderNumber()); |
| | | //存储缓存 |
| | | JSONObject jsonObject = new JSONObject(); |
| | | jsonObject.put("userId", order.getAppUserId()); |
| | | jsonObject.put("orderId", order.getId()); |
| | | jsonObject.put("vehicleId", order.getVehicleId()); |
| | | jsonObject.put("expirationTime", expirationTime); |
| | | jsonObject.put("orderNumber", order.getOrderNumber()); |
| | | jsonObject.put("vehicleVin", vehicle.getVehicleVin()); |
| | | jsonObject.put("createTime", System.currentTimeMillis()); |
| | | redisService.setCacheObject("rt:bluetooth:unlock:" + token, jsonObject.toJSONString(), expirationTime, TimeUnit.SECONDS); |
| | | redisService.setCacheObject("rt:bluetooth:unlock:order:" + order.getId(), token, expirationTime, TimeUnit.SECONDS); |
| | | //开始下发蓝牙token |
| | | BluetoothDeliverVo vo = new BluetoothDeliverVo(); |
| | | vo.setCredential(token); |
| | | vo.setVim(vehicle.getVehicleVin()); |
| | | vo.setPassengerUid(order.getAppUserId()); |
| | | BaseVehicleUserDto deliver = bluetoothUnlockClient.deliver(vo); |
| | | if(null == deliver){ |
| | | throw new ServiceException("下发蓝牙token失败"); |
| | | } |
| | | if(deliver.getCode() != 0){ |
| | | log.error("下发蓝牙token失败:{} {}", deliver.getMsg(), order.getOrderNumber()); |
| | | throw new ServiceException(deliver.getMsg()); |
| | | } |
| | | log.info("下发蓝牙token成功:{} {}", deliver.getData(), order.getOrderNumber()); |
| | | } |
| | | |
| | | if(order.getStatus() == 2){ |
| | |
| | | R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(xpOrder.getVehicleId()); |
| | | XpVehicle vehicle = xpVehicleR.getData(); |
| | | xpOrderDetailVO.setVehicleVin(vehicle.getVehicleVin()); |
| | | xpOrderDetailVO.setVehicleModel(vehicle.getVehicleModel()); |
| | | xpOrderDetailVO.setVehicleModel(vehicle.getVehicleModelName()); |
| | | xpOrderDetailVO.setVehicleColor(vehicle.getVehicleColor()); |
| | | xpOrderDetailVO.setPlateNumber(vehicle.getPlateNumber()); |
| | | xpOrderDetailVO.setVehiclePictures(vehicle.getVehiclePictures()); |
| | |
| | | R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(xpOrder.getVehicleId()); |
| | | XpVehicle vehicle = xpVehicleR.getData(); |
| | | xpOrderDetailVO.setVehicleVin(vehicle.getVehicleVin()); |
| | | xpOrderDetailVO.setVehicleModel(vehicle.getVehicleModel()); |
| | | xpOrderDetailVO.setVehicleModel(vehicle.getVehicleModelName()); |
| | | xpOrderDetailVO.setVehicleColor(vehicle.getVehicleColor()); |
| | | xpOrderDetailVO.setPlateNumber(vehicle.getPlateNumber()); |
| | | |
| | |
| | | */ |
| | | private boolean isCancelShow(XpOrder xpOrder,String vehicleVin) { |
| | | boolean isCancel = false; |
| | | |
| | | //获取系统配置 |
| | | XpSystemSetting systemSetting = xpSystemSettingClient.getSystemSettingOne().getData(); |
| | | if(systemSetting == null){ |
| | | throw new ServiceException("获取行程配置错误"); |
| | | } |
| | | //获取配置文件 |
| | | int sylc = 800;//单位米 todo yyy 需要读取后台配置文件 |
| | | int sysj = 5;//单位分钟 todo yyy 需要读取后台配置文件 |
| | | int sylc = systemSetting.getXczSyDistance(); // 行程中剩余里程单位米 需要读取后台配置文件 |
| | | int sysj = systemSetting.getXczSyTime();//行程中剩余时间 单位分钟 需要读取后台配置文件 |
| | | // 减少3分钟 获取3分账内的 行程数据 |
| | | long minute =3;//todo yyy 需要读取后台配置文件 |
| | | Integer xczSpeedPerHour = systemSetting.getXczHmSpeedPerHour();//行程中豁免时速 km/h |
| | | Integer xczHmDuration = systemSetting.getXczHmDuration();//行程中豁免持续时间 分钟 需要读取后台配置文件 |
| | | |
| | | |
| | | List<OrderTrajectory> orderTrajectories = queryOrderTrajectories(xpOrder.getId()); |
| | | if(!orderTrajectories.isEmpty()){// 减少3分钟 获取3分账内的 行程数据 |
| | | //3千米/小时 转换成分钟 50米/分钟 |
| | | BigDecimal suduDefault = new BigDecimal("3");//3千米/小时 |
| | | BigDecimal suduDefault = new BigDecimal(xczSpeedPerHour);//3千米/小时 |
| | | BigDecimal suduDefault1 = suduDefault.multiply(BigDecimal.valueOf(1000)) ; |
| | | BigDecimal suduDefaultNew =suduDefault1.divide(BigDecimal.valueOf(60),2,RoundingMode.HALF_EVEN); |
| | | |
| | | LocalDateTime minutesBefore = LocalDateTime.now().minusMinutes(minute); |
| | | LocalDateTime minutesBefore = LocalDateTime.now().minusMinutes(xczHmDuration); |
| | | long milliseconds = DateUtils.getMilliseconds(minutesBefore); |
| | | List<OrderTrajectory> orderTrajectoriesNew = new ArrayList<>(); |
| | | for (OrderTrajectory orderTrajectory : orderTrajectories) { |
| | |
| | | */ |
| | | //计算行驶总距离和时长 |
| | | Double reduce = orderTrajectoriesNew.stream().map(OrderTrajectory::getDistance).reduce(0.0, Double::sum); |
| | | BigDecimal xcsudu = new BigDecimal(reduce).divide(new BigDecimal(minute),2, RoundingMode.HALF_EVEN);//米/分钟 |
| | | BigDecimal xcsudu = new BigDecimal(reduce).divide(new BigDecimal(xczHmDuration),2, RoundingMode.HALF_EVEN);//米/分钟 |
| | | |
| | | //特殊豁免:若系统检测到车辆在当前坐标时速 < 3km/h 且持续时间 > 3分钟(后台配置)(判定为极度拥堵或异常停滞),此时解除限制,允许用户修改目的地或新增途径点以寻找更近的下车位置。 |
| | | if(xcsudu.compareTo(suduDefaultNew)<0){ |
| | |
| | | ); |
| | | return num.intValue(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 根据 UID 查询用户是否有 RT 订单 & 订单列表 |
| | | * @param dto |
| | | * @return |
| | | */ |
| | | @Override |
| | | public List<UserOrderListVo> getUserOrderList(UserOrderListDto dto) { |
| | | List<XpOrder> list = this.list(new QueryWrapper<XpOrder>().eq("app_user_id", dto.getUid()) |
| | | .eq("is_deleted", 0) |
| | | .eq(dto.getOrderStatus() != null, "status", dto.getOrderStatus()) |
| | | .eq(dto.getOrderId() != null, "id", dto.getOrderId()) |
| | | .last(" order by create_time desc limit " + dto.getPageNo() + "," + dto.getPageSize()) |
| | | ); |
| | | List<UserOrderListVo> collect = list.stream().map(order -> { |
| | | UserOrderListVo vo = new UserOrderListVo(); |
| | | vo.setOrderId(order.getId()); |
| | | vo.setOrderStatus(order.getStatus()); |
| | | vo.setOrderCreatedAt(order.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); |
| | | |
| | | XpPoint startPoint = xpPointClient.queryById(order.getStartPointId()).getData(); |
| | | vo.setPickupAddress(null != startPoint ? startPoint.getPointAddress() : null); |
| | | XpPoint endPoint = xpPointClient.queryById(order.getEndPointId()).getData(); |
| | | vo.setDestAddress(null != endPoint ? endPoint.getPointAddress() : null); |
| | | vo.setTotalPrice(order.getOrderMoney()); |
| | | return vo; |
| | | }).collect(Collectors.toList()); |
| | | return collect; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取订单详情 |
| | | * @param orderId |
| | | * @return |
| | | */ |
| | | @Override |
| | | public OrderInfoVo getOrderInfo(Long orderId) { |
| | | XpOrder xpOrder = this.getById(orderId); |
| | | OrderInfoVo vo = new OrderInfoVo(); |
| | | vo.setOrderId(xpOrder.getId()); |
| | | vo.setOrderStatus(xpOrder.getStatus()); |
| | | XpPoint startPoint = xpPointClient.queryById(xpOrder.getStartPointId()).getData(); |
| | | vo.setPickupAddress(startPoint.getPointAddress()); |
| | | XpPoint endPoint = xpPointClient.queryById(xpOrder.getEndPointId()).getData(); |
| | | vo.setDestAddress(endPoint.getPointAddress()); |
| | | vo.setPullOverAddress(xpOrder.getGetoffAddress()); |
| | | if(null != xpOrder.getEstimatedArrivalTime()){ |
| | | vo.setEstimatedArrivalTime(xpOrder.getEstimatedArrivalTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); |
| | | } |
| | | if(null != xpOrder.getGetoffTime()){ |
| | | vo.setActualArrivalTime(xpOrder.getGetoffTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); |
| | | } |
| | | if(null == xpOrder.getMileageKilometers()){ |
| | | vo.setActualMileage(BigDecimal.valueOf(xpOrder.getMileageKilometers())); |
| | | } |
| | | vo.setTotalPrice(xpOrder.getOrderMoney()); |
| | | vo.setStartPrice(xpOrder.getStartMoney()); |
| | | vo.setWaitFee(xpOrder.getWaitMoney()); |
| | | vo.setMileageFee(xpOrder.getMileageMoney()); |
| | | vo.setDurationFee(xpOrder.getDurationMoney()); |
| | | vo.setLongDistanceFee(xpOrder.getLongDistancemoney()); |
| | | vo.setCouponDiscount(xpOrder.getCouponMoney()); |
| | | vo.setActualPayment(xpOrder.getPayMoney()); |
| | | XpVehicle xpVehicle = xpVehicleClient.queryById(xpOrder.getVehicleId()).getData(); |
| | | if(null != xpVehicle){ |
| | | vo.setVin(xpVehicle.getVehicleVin()); |
| | | } |
| | | XpOrderCancel orderCancel = xpOrderCancelService.getOne(new QueryWrapper<XpOrderCancel>().eq("order_id", orderId)); |
| | | if(null != orderCancel){ |
| | | vo.setCancelReasonType(orderCancel.getCancelReason()); |
| | | } |
| | | return vo; |
| | | } |
| | | } |