xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/utils/GeodesyUtil.java
@@ -57,7 +57,7 @@ } private static double getDistanceMeter(GlobalCoordinates gpsFrom, GlobalCoordinates gpsTo, Ellipsoid ellipsoid){ public static double getDistanceMeter(GlobalCoordinates gpsFrom, GlobalCoordinates gpsTo, Ellipsoid ellipsoid){ //创建GeodeticCalculator,调用计算方法,传入坐标系、经纬度用于计算距离 GeodeticCurve geoCurve = new GeodeticCalculator().calculateGeodeticCurve(ellipsoid, gpsFrom, gpsTo); return geoCurve.getEllipsoidalDistance(); xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderController.java
@@ -16,10 +16,10 @@ import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.vo.automaticPilot.VehiclePosition; import com.xiaopeng.api.vo.operation.NearestNextVo; import com.xiaopeng.api.vo.operation.RouteVo; import com.xiaopeng.api.vo.order.*; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.exception.ServiceException; import com.xiaopeng.common.core.utils.StringUtils; import com.xiaopeng.common.core.utils.WebUtils; import com.xiaopeng.common.core.web.page.PageInfo; @@ -341,6 +341,21 @@ } /** * 获取用户快捷胶囊 历史 时段 终点高点 */ @ApiOperation(value = "获取首页快捷胶囊推荐站点", notes = "根据当前起点和用户近30天有效订单,按场景优先推荐目的地;无匹配时返回空数据", tags = {"小程序-首页快捷胶囊"}, response = XpPointInfoVo.class) @ApiImplicitParams({ @ApiImplicitParam(name = "startPointId", value = "当前首页起点站点ID", required = true, dataType = "Long", paramType = "query") }) @GetMapping("/api/v1/mini-program/order/quick-capsule") @XpClientCheck(checkToken = false, checkOtpToken = true) public R<NearestNextVo> quickCapsule(@RequestParam Long startPointId,HttpServletRequest request) { String uid = request.getHeader("XP-Uid"); return R.ok(xpOrderService.quickCapsule(uid,startPointId)); } /** * 获取订单详情 */ @PutMapping("/api/v1/mini-program/order/orderDetail/{orderId}") xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/mapper/XpOrderMapper.java
@@ -6,12 +6,12 @@ import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.order.model.query.XpHistoryOrderListQuery; import com.xiaopeng.order.model.vo.XpOrderHistoryListVO; import com.xiaopeng.order.model.vo.XpOrderHistoryVO; import org.apache.ibatis.annotations.Param; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.List; /** @@ -74,4 +74,7 @@ List<XpOrder> listUserRecentValidOrders(@Param("uid") Long uid, @Param("startTime") LocalDateTime startTime); List<Long> timeSceneOrders(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDateTime endDate, @Param("startMoment") LocalTime startMoment, @Param("endMoment") LocalTime endMoment, @Param("userId") Long userId); List<Long> frequencyOrders(LocalDate startDate, LocalDateTime endDate, LocalTime startMoment, LocalTime endMoment, Long userId); } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/IXpOrderService.java
@@ -8,6 +8,7 @@ import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO; import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.vo.operation.NearestNextVo; import com.xiaopeng.api.vo.order.FareBreakdownVo; import com.xiaopeng.api.vo.order.XpHistoryEndPointsVo; import com.xiaopeng.api.vo.order.XpHistoryStartPointsVo; @@ -105,6 +106,8 @@ * @return */ XpPointInfoVo getUserQuickCapsule(String uid, Long startPointId); NearestNextVo quickCapsule(String uid, Long startPointId); /** * 获取历史下单成功起点10条数据列表 @@ -262,4 +265,5 @@ * @return */ OrderInfoVo getOrderInfo(Long orderId); } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -31,6 +31,7 @@ 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; @@ -49,6 +50,7 @@ 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; @@ -96,6 +98,8 @@ 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; @@ -1311,6 +1315,59 @@ 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()); Set<Integer> targetHours = buildTargetHours(now.getHour()); xp-robotaxi-order-boot/src/main/resources/mapper/order/XpOrderMapper.xml
@@ -100,4 +100,28 @@ ORDER BY create_time DESC </select> <select id="timeSceneOrders" resultType="java.lang.Long"> SELECT end_point_id FROM xp_order WHERE app_user_id = #{userId} AND is_deleted = 0 AND status IN (9, 10) AND DATE(create_time) BETWEEN #{startDate} AND #{endDate} AND TIME(create_time) BETWEEN #{startTime} AND #{endTime} GROUP BY end_point_id ORDER BY COUNT(*) DESC </select> <select id="frequencyOrders" resultType="java.lang.Long"> SELECT end_point_id FROM xp_order WHERE app_user_id = #{userId} AND is_deleted = 0 AND status IN (9, 10) AND DATE(create_time) BETWEEN #{startDate} AND #{endDate} GROUP BY end_point_id ORDER BY COUNT(*) DESC </select> </mapper>