xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/operation/PointAddDto.java
@@ -90,4 +90,10 @@ @ApiModelProperty(value = "区县code") private String districtCode; @ApiModelProperty(value = "所属场站id,为空表示运营区直属点位") private Long stationId; @ApiModelProperty(value = "点位在场站内排序") private Integer stationSortNo; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/operation/PointUpdateDto.java
@@ -90,4 +90,10 @@ @ApiModelProperty(value = "区县code") private String districtCode; @ApiModelProperty(value = "所属场站id,为空表示运营区直属点位") private Long stationId; @ApiModelProperty(value = "点位在场站内排序") private Integer stationSortNo; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/operation/XpSystemSettingClientFallbackFactory.java
@@ -1,8 +1,6 @@ package com.xiaopeng.api.factory.operation; import com.xiaopeng.api.feignClient.operation.SysLogClient; import com.xiaopeng.api.feignClient.operation.XpSystemSettingClient; import com.xiaopeng.api.model.operation.SysOperLog; import com.xiaopeng.api.model.operation.XpFeedbackType; import com.xiaopeng.common.core.domain.R; import lombok.extern.slf4j.Slf4j; @@ -22,6 +20,12 @@ } @Override public R<Integer> getMinOrderDistance() { log.error("获取最小下单距离失败:{}", cause.getMessage()); return R.fail("获取最小下单距离失败"); } @Override public R<Integer> getOrderFinishTime() { log.error("获取自动结束行程时间失败:{}", cause.getMessage()); return R.fail("获取自动结束行程时间失败,"+cause.getMessage()); xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/operation/XpSystemSettingClient.java
@@ -6,9 +6,7 @@ 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.Mapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @FeignClient(contextId = "XpSystemSettingClient", value = ServiceNameConstantsEnum.XP_OPERATION_SERVICE, fallbackFactory = XpSystemSettingClientFallbackFactory.class) public interface XpSystemSettingClient { @@ -17,6 +15,9 @@ @GetMapping("/base/v1/support/getOrderDistance") R getOrderDistance(); @GetMapping("/logan/rt/v1/getMinOrderDistance") R<Integer> getMinOrderDistance(); /** * 获取自动结束行程时间 * @return xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/operation/XpPoint.java
@@ -120,4 +120,12 @@ @TableField("district_code") private String districtCode; @ApiModelProperty(value = "所属场站id,为空表示运营区直属点位") @TableField("station_id") private Long stationId; @ApiModelProperty(value = "点位在场站内排序") @TableField("station_sort_no") private Integer stationSortNo; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/operation/XpStation.java
New file @@ -0,0 +1,112 @@ package com.xiaopeng.api.model.operation; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import com.xiaopeng.common.core.web.domain.BaseModel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; import java.math.BigDecimal; import java.time.LocalDateTime; /** * <p> * 场站 * </p> * * @author mitao * @since 2026-04-12 */ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("xp_station") @ApiModel(value = "XpStation对象", description = "场站") public class XpStation extends BaseModel { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) private Long id; @ApiModelProperty(value = "场站名称,C端展示主标题") private String stationName; @ApiModelProperty(value = "场站编号唯一 规则:城市首字母-HUB-4位流水号") private String stationNumber; @ApiModelProperty(value = "场站状态 0=草稿 1=待测试 2=测试中 3=测试不通过 4=可运营 5=待生效 6=启用中 7=已停用 8=失效") private Integer status; @ApiModelProperty(value = "城市code") private String cityCode; @ApiModelProperty(value = "城市名称") private String cityName; @ApiModelProperty(value = "所属电子围栏id") private Long electronicId; @ApiModelProperty(value = "所属运营区id") private Long electronicAreaId; @ApiModelProperty(value = "默认展示点位id") private Long defaultPointId; @ApiModelProperty(value = "场站面积") private BigDecimal stationArea; @ApiModelProperty(value = "场站围栏点位 经度_纬度,经度_纬度") private String fencePoints; @ApiModelProperty(value = "中心点经度") private Double centerLon; @ApiModelProperty(value = "中心点纬度") private Double centerLat; @ApiModelProperty(value = "限停时长") private Integer parkingTimeLimit; @ApiModelProperty(value = "运营类型 1=与运营区同步 2=自定义时间段") private Integer operationType; @ApiModelProperty(value = "生效周期 1=每日 2=仅工作日 3=仅非工作日 4=仅节假日") private Integer effectivePeriod; @ApiModelProperty(value = "生效时间段") private String effectiveTimePeriod; @ApiModelProperty(value = "是否同步运营信息到子点位 0=否 1=是") private Integer syncToPoints; @ApiModelProperty(value = "备注") private String remark; @ApiModelProperty(value = "停用原因") private String stopReason; @ApiModelProperty(value = "测试不通过原因") private String testFailReason; @ApiModelProperty(value = "计划生效时间") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime effectTime; @ApiModelProperty(value = "场站内排序") private Integer sortNo; @ApiModelProperty(value = "点位总数") @TableField(exist = false) private Integer pointCount; @ApiModelProperty(value = "运营中点位数") @TableField(exist = false) private Integer operatingPointCount; } xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpPointManagementController.java
@@ -251,6 +251,8 @@ .set(XpPoint::getCityName, xpPoint.getCityName()) .set(XpPoint::getDistrictName, xpPoint.getDistrictName()) .set(XpPoint::getDistrictCode, xpPoint.getDistrictCode()) .set(XpPoint::getStationId, xpPoint.getStationId()) .set(XpPoint::getStationSortNo, xpPoint.getStationSortNo()) .update(); return R.ok(); } xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/mapper/XpStationMapper.java
New file @@ -0,0 +1,16 @@ package com.xiaopeng.map.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.xiaopeng.api.model.operation.XpStation; /** * <p> * 场站 Mapper 接口 * </p> * * @author mitao * @since 2026-04-12 */ public interface XpStationMapper extends BaseMapper<XpStation> { } xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpStationService.java
New file @@ -0,0 +1,16 @@ package com.xiaopeng.map.service; import com.baomidou.mybatisplus.extension.service.IService; import com.xiaopeng.api.model.operation.XpStation; /** * <p> * 场站 服务类 * </p> * * @author mitao * @since 2026-04-12 */ public interface XpStationService extends IService<XpStation> { } xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpStationServiceImpl.java
New file @@ -0,0 +1,20 @@ package com.xiaopeng.map.service.serviceimpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiaopeng.api.model.operation.XpStation; import com.xiaopeng.map.mapper.XpStationMapper; import com.xiaopeng.map.service.XpStationService; import org.springframework.stereotype.Service; /** * <p> * 场站 服务实现类 * </p> * * @author mitao * @since 2026-04-12 */ @Service public class XpStationServiceImpl extends ServiceImpl<XpStationMapper, XpStation> implements XpStationService { } xp-robotaxi-map-boot/src/main/resources/mapper/XpPointMapper.xml
@@ -24,11 +24,13 @@ <result column="update_time" property="updateTime" /> <result column="create_by" property="createBy" /> <result column="update_by" property="updateBy" /> <result column="station_id" property="stationId" /> <result column="station_sort_no" property="stationSortNo" /> </resultMap> <!-- 通用查询结果列 --> <sql id="Base_Column_List"> id,electronic_area_id, point_name, point_poi, point_number, operation_status, point_type, verification_status, parking_time_limit, operation_type, effective_period, effective_time_period, remarks, point_pictures, point_lon, point_lat, is_deleted, create_time, update_time, create_by, update_by id,electronic_area_id, point_name, point_poi, point_number, operation_status, point_type, verification_status, parking_time_limit, operation_type, effective_period, effective_time_period, remarks, point_pictures, point_lon, point_lat, is_deleted, create_time, update_time, create_by, update_by, station_id, station_sort_no </sql> <select id="pointMap" resultType="com.xiaopeng.api.model.operation.XpPoint"> select t1.*, @@ -121,6 +123,7 @@ xp.point_address as pointAddress, xp.point_lon as pointLon,xp.point_lat as pointLat,xp.create_time as createTime,xp.create_by as createBy ,xp.parking_time_limit as parkingTimeLimit,xp.verification_status as verificationStatus,xp.operation_status as operationStatus ,xp.station_id as stationId,xp.station_sort_no as stationSortNo from xp_point xp where xp.is_deleted = 0 <if test="query.pointName != null and query.pointName != ''"> xp-robotaxi-map-boot/src/main/resources/mapper/XpStationMapper.xml
New file @@ -0,0 +1,43 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.xiaopeng.map.mapper.XpStationMapper"> <resultMap id="BaseResultMap" type="com.xiaopeng.api.model.operation.XpStation"> <id column="id" property="id"/> <result column="station_name" property="stationName"/> <result column="station_number" property="stationNumber"/> <result column="status" property="status"/> <result column="city_code" property="cityCode"/> <result column="city_name" property="cityName"/> <result column="electronic_id" property="electronicId"/> <result column="electronic_area_id" property="electronicAreaId"/> <result column="default_point_id" property="defaultPointId"/> <result column="station_area" property="stationArea"/> <result column="fence_points" property="fencePoints"/> <result column="center_lon" property="centerLon"/> <result column="center_lat" property="centerLat"/> <result column="parking_time_limit" property="parkingTimeLimit"/> <result column="operation_type" property="operationType"/> <result column="effective_period" property="effectivePeriod"/> <result column="effective_time_period" property="effectiveTimePeriod"/> <result column="sync_to_points" property="syncToPoints"/> <result column="remark" property="remark"/> <result column="stop_reason" property="stopReason"/> <result column="test_fail_reason" property="testFailReason"/> <result column="effect_time" property="effectTime"/> <result column="sort_no" property="sortNo"/> <result column="is_deleted" property="deleted"/> <result column="create_time" property="createTime"/> <result column="update_time" property="updateTime"/> <result column="create_by" property="createBy"/> <result column="update_by" property="updateBy"/> </resultMap> <sql id="Base_Column_List"> id, station_name, station_number, status, city_code, city_name, electronic_id, electronic_area_id, default_point_id, station_area, fence_points, center_lon, center_lat, parking_time_limit, operation_type, effective_period, effective_time_period, sync_to_points, remark, stop_reason, test_fail_reason, effect_time, sort_no, is_deleted, create_time, update_time, create_by, update_by </sql> </mapper> xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpSystemSettingController.java
@@ -70,6 +70,12 @@ return R.ok(xpSystemSettingService.getOrderDistance()); } @GetMapping("/logan/rt/v1/getMinOrderDistance") @ApiOperation(value = "获取最小下单距离", notes = "内部服务调用,返回起终点允许发单的最小距离阈值,单位米", tags = {"内部接口-获取最小下单距离"}, response = Integer.class) public R<Integer> getMinOrderDistance() { return R.ok(xpSystemSettingService.getMinOrderDistance()); } @ApiOperation(value = "获取自动结束行程时间",tags = {"小程序-业务流程"},response = Integer.class) @GetMapping("/api/v1/mini-program/support/getOrderFinishTime") xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/XpSystemSettingService.java
@@ -26,6 +26,12 @@ */ Integer getOrderDistance(); /** * 获取起终点最小下单距离 * @return */ Integer getMinOrderDistance(); /** * 获取自动结束行程时间 xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpSystemSettingServiceImpl.java
@@ -46,6 +46,15 @@ } @Override public Integer getMinOrderDistance() { XpSystemSetting xpSystemSetting = this.getOne(null); if (xpSystemSetting != null) { return xpSystemSetting.getMinOrderDistance(); } return 0; } @Override public Integer getOrderFinishTime() { XpSystemSetting xpSystemSetting = this.getOne(null); if (xpSystemSetting != null) { xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderController.java
@@ -321,13 +321,16 @@ /** * 获取用户快捷胶囊 历史 时段 终点高点 */ @ApiOperation(value = "获取用户快捷胶囊 历史 时段 终点高点", tags = {"小程序-获取用户快捷胶囊 历史 时段 终点高点"}) @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/getUserQuickCapsule") @XpClientCheck(checkToken = false, checkOtpToken = true) public R<XpPointInfoVo> getUserQuickCapsule(HttpServletRequest request) { public R<XpPointInfoVo> getUserQuickCapsule(@RequestParam Long startPointId, HttpServletRequest request) { // uid已通过拦截器otp校验,可以直接使用 String uid = request.getHeader("XP-Uid"); return R.ok(xpOrderService.getUserQuickCapsule(uid)); return R.ok(xpOrderService.getUserQuickCapsule(uid, startPointId)); } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/mapper/XpOrderMapper.java
@@ -11,6 +11,7 @@ import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; /** @@ -69,5 +70,8 @@ List<XpOrder> getHistoryEndPoints(@Param("uid") Long uid, @Param("areaId") Long areaId); Long countUserValidHistoryOrders(@Param("uid") Long uid); List<XpOrder> listUserRecentValidOrders(@Param("uid") Long uid, @Param("startTime") LocalDateTime startTime); } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/IXpOrderService.java
@@ -99,7 +99,7 @@ * @param uid * @return */ XpPointInfoVo getUserQuickCapsule(String uid); XpPointInfoVo getUserQuickCapsule(String uid, Long startPointId); /** * 获取历史下单成功起点10条数据列表 xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -3,7 +3,6 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; @@ -1209,16 +1208,41 @@ } @Override public XpPointInfoVo getUserQuickCapsule(String uid) { public XpPointInfoVo getUserQuickCapsule(String uid, Long startPointId) { try { // 使用Redis的sorted set(排行榜)获取第一个元素 // 获取分数最高的元素(默认按分数从高到低排序) Set<Object> result = redisService.redisTemplate.opsForZSet().reverseRange("history_order_" + uid, 0, 0); if (result != null && !result.isEmpty()) { // 排行榜中存储的是JSON字符串 String jsonString = result.iterator().next().toString(); // 使用Hutool的JSON工具解析为XpPointInfoVo对象 return JSONUtil.toBean(jsonString, XpPointInfoVo.class); Long userId = Long.valueOf(uid); // 新用户或历史样本过少时直接隐藏胶囊,避免偶发订单干扰推荐。 Long validHistoryCount = this.baseMapper.countUserValidHistoryOrders(userId); if (validHistoryCount == null || validHistoryCount < 3) { return null; } XpPoint startPoint = getPointById(startPointId); if (startPoint == null) { log.warn("获取用户快捷胶囊失败,起点不存在,uid:{}, startPointId:{}", uid, startPointId); return null; } Integer minOrderDistance = getMinOrderDistanceSafely(uid, startPointId); if (minOrderDistance == null) { return null; } LocalDateTime now = LocalDateTime.now(); List<XpOrder> recentOrders = this.baseMapper.listUserRecentValidOrders(userId, now.minusDays(30)); if (CollectionUtils.isEmpty(recentOrders)) { return null; } // 先走“同日别 + 当前时间前后1小时”的场景推荐,命不中再退化到30天高频。 RecommendationCandidate sceneCandidate = buildSceneCandidate(recentOrders, now, startPoint, minOrderDistance); if (sceneCandidate != null) { return sceneCandidate.getPointInfo(); } RecommendationCandidate fallbackCandidate = buildHighFrequencyCandidate(recentOrders, startPoint, minOrderDistance); if (fallbackCandidate != null) { return fallbackCandidate.getPointInfo(); } } catch (Exception e) { log.error("获取用户快捷胶囊失败: {}", e.getMessage(), e); @@ -1226,6 +1250,201 @@ 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()); List<XpOrder> sceneOrders = recentOrders.stream() .filter(order -> isSameScene(order, currentSceneType, targetHours)) .collect(Collectors.toList()); List<RecommendationCandidate> candidates = buildCandidates(sceneOrders, startPoint, minOrderDistance); if (candidates.isEmpty()) { return null; } RecommendationCandidate top1 = candidates.get(0); int secondCount = candidates.size() > 1 ? candidates.get(1).getCount() : 0; // 只有 top1 至少命中2次且严格领先第二名,才认为“场景规律”足够明显。 if (top1.getCount() >= 2 && top1.getCount() > secondCount) { return top1; } return null; } private RecommendationCandidate buildHighFrequencyCandidate(List<XpOrder> recentOrders, XpPoint startPoint, Integer minOrderDistance) { List<RecommendationCandidate> candidates = buildCandidates(recentOrders, startPoint, minOrderDistance); if (candidates.isEmpty()) { return null; } return candidates.get(0); } private List<RecommendationCandidate> buildCandidates(List<XpOrder> orders, XpPoint startPoint, Integer minOrderDistance) { if (CollectionUtils.isEmpty(orders)) { return Collections.emptyList(); } Map<Long, RecommendationCandidate> candidateMap = new HashMap<>(); for (XpOrder order : orders) { Long endPointId = order.getEndPointId(); if (endPointId == null || endPointId.equals(startPoint.getId())) { continue; } // 候选点位在创建阶段就完成“点位存在 + 距离阈值”过滤,后续只累加频次。 RecommendationCandidate candidate = candidateMap.computeIfAbsent(endPointId, id -> createCandidate(id, startPoint, minOrderDistance)); if (candidate == null || !candidate.isAvailable()) { continue; } candidate.increment(order.getCreateTime()); } return candidateMap.values().stream() .filter(Objects::nonNull) .filter(RecommendationCandidate::isAvailable) .filter(candidate -> candidate.getCount() > 0) .sorted(Comparator.comparingInt(RecommendationCandidate::getCount).reversed() .thenComparing(RecommendationCandidate::getLatestCreateTime, Comparator.nullsLast(Comparator.reverseOrder()))) .collect(Collectors.toList()); } private RecommendationCandidate createCandidate(Long pointId, XpPoint startPoint, Integer minOrderDistance) { XpPoint point = getPointById(pointId); if (point == null) { return null; } Double distance = getStraightDistance(startPoint, point); if (distance == null || distance <= minOrderDistance) { return RecommendationCandidate.unavailable(); } XpPointInfoVo pointInfoVo = new XpPointInfoVo(); pointInfoVo.setId(point.getId()); pointInfoVo.setPointName(point.getPointName()); pointInfoVo.setPointLon(point.getPointLon()); pointInfoVo.setPointLat(point.getPointLat()); return RecommendationCandidate.available(pointInfoVo); } private Integer getMinOrderDistanceSafely(String uid, Long startPointId) { R<Integer> result = xpSystemSettingClient.getMinOrderDistance(); if (result == null || result.getCode() != R.SUCCESS || result.getData() == null) { log.error("获取用户快捷胶囊失败,最小下单距离配置异常,uid:{}, startPointId:{}, result:{}", uid, startPointId, JSON.toJSONString(result)); return null; } return result.getData(); } private XpPoint getPointById(Long pointId) { if (pointId == null) { return null; } R<XpPoint> pointResult = xpPointClient.queryById(pointId); if (pointResult == null || pointResult.getCode() != R.SUCCESS) { return null; } return pointResult.getData(); } private Double getStraightDistance(XpPoint startPoint, XpPoint endPoint) { if (startPoint == null || endPoint == null || startPoint.getPointLon() == null || startPoint.getPointLat() == null || endPoint.getPointLon() == null || endPoint.getPointLat() == null) { return null; } Map<String, Double> distanceMap = GeodesyUtil.getDistance(startPoint.getPointLon() + "," + startPoint.getPointLat(), endPoint.getPointLon() + "," + endPoint.getPointLat()); if (distanceMap == null) { return null; } return distanceMap.get("WGS84"); } private boolean isSameScene(XpOrder order, DaySceneType currentSceneType, Set<Integer> targetHours) { if (order.getCreateTime() == null || order.getTimeSlot() == null) { return false; } // 场景命中必须同时满足“同类日期”和“当前小时前后1小时窗口”。 if (resolveDaySceneType(order.getCreateTime().toLocalDate()) != currentSceneType) { return false; } Integer slotHour = parseTimeSlotStartHour(order.getTimeSlot()); return slotHour != null && targetHours.contains(slotHour); } private Integer parseTimeSlotStartHour(String timeSlot) { if (StringUtils.isBlank(timeSlot) || !timeSlot.contains("-")) { return null; } try { return Integer.valueOf(timeSlot.split("-")[0]); } catch (Exception ex) { log.warn("解析时段失败,timeSlot:{}", timeSlot); return null; } } private Set<Integer> buildTargetHours(int currentHour) { Set<Integer> targetHours = new HashSet<>(); targetHours.add((currentHour + 23) % 24); targetHours.add(currentHour); targetHours.add((currentHour + 1) % 24); return targetHours; } private DaySceneType resolveDaySceneType(LocalDate date) { if (holidayUtil.isHoliday(date)) { return DaySceneType.HOLIDAY; } DayOfWeek dayOfWeek = date.getDayOfWeek(); if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) { return DaySceneType.NON_WORKDAY; } return DaySceneType.WORKDAY; } private enum DaySceneType { WORKDAY, NON_WORKDAY, HOLIDAY } private static class RecommendationCandidate { private final XpPointInfoVo pointInfo; private final boolean available; private int count; private LocalDateTime latestCreateTime; private RecommendationCandidate(XpPointInfoVo pointInfo, boolean available) { this.pointInfo = pointInfo; this.available = available; } public static RecommendationCandidate available(XpPointInfoVo pointInfo) { return new RecommendationCandidate(pointInfo, true); } public static RecommendationCandidate unavailable() { return new RecommendationCandidate(null, false); } public void increment(LocalDateTime createTime) { this.count++; if (createTime != null && (this.latestCreateTime == null || createTime.isAfter(this.latestCreateTime))) { this.latestCreateTime = createTime; } } public XpPointInfoVo getPointInfo() { return pointInfo; } public boolean isAvailable() { return available; } public int getCount() { return count; } public LocalDateTime getLatestCreateTime() { return latestCreateTime; } } @Override public List<XpHistoryStartPointsVo> getHistoryStartPoints(String uid, double lon, double lat,Long electronicAreaId) { // xp-robotaxi-order-boot/src/main/resources/mapper/order/XpOrderMapper.xml
@@ -72,4 +72,32 @@ ORDER BY num DESC,create_time desc </select> <select id="countUserValidHistoryOrders" resultType="java.lang.Long"> SELECT COUNT(*) FROM xp_order WHERE app_user_id = #{uid} AND is_deleted = 0 AND status IN (9, 10) </select> <select id="listUserRecentValidOrders" resultType="com.xiaopeng.api.model.order.XpOrder"> SELECT app_user_id, start_point_id, end_point_id, start_point_name, end_point_name, time_slot, create_time, end_lon, end_lat FROM xp_order WHERE app_user_id = #{uid} AND is_deleted = 0 AND status IN (9, 10) AND create_time <![CDATA[>=]]> #{startTime} AND end_point_id IS NOT NULL ORDER BY create_time DESC </select> </mapper>