From fa8e441c82c9d336d1144dd1ec925551d07b2299 Mon Sep 17 00:00:00 2001
From: liujie <1793218484@qq.com>
Date: 星期日, 12 四月 2026 17:37:06 +0800
Subject: [PATCH] 1.0小程序功能  个人中心

---
 xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPointServiceImpl.java |  293 +++++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 225 insertions(+), 68 deletions(-)

diff --git a/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPointServiceImpl.java b/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPointServiceImpl.java
index a13992d..a9b70a7 100644
--- a/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPointServiceImpl.java
+++ b/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPointServiceImpl.java
@@ -470,85 +470,242 @@
     @Override
     public XpPointsSearchVo queryPointByName(PointByNameQuery query,String uid) {
         XpPointsSearchVo searchVo = new XpPointsSearchVo();
-        List<XpPointsListVo> list = this.baseMapper.queryPointByName(query);
-        if (list.isEmpty()) {
+        List<XpPointsListVo> stationList = this.baseMapper.queryStationByName(query);
+        List<Long> stationIds = stationList.stream()
+                .map(this::getSearchResultStationId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+        // 先把命中的场站id传给点位查询,避免同一场站下的点位和场站父胶囊重复返回。
+        List<XpPointsListVo> pointList = this.baseMapper.queryPointByName(query, stationIds);
+        List<XpPointsListVo> stationChildren = CollUtil.isEmpty(stationIds) ? Collections.emptyList() : this.baseMapper.queryStationChildren(stationIds);
+        if (CollUtil.isEmpty(stationList) && CollUtil.isEmpty(pointList)) {
             return searchVo;
         }
 
-        LocalTime now = LocalTime.now();
-        List<Long> areaIds = list.stream().map(XpPointsListVo::getElectronicAreaId).collect(Collectors.toList());
-        List<XpElectronicFenceArea> xpElectronicFenceAreas = xpElectronicFenceAreaService.listByIds(areaIds);
-        List<Long> fenceIds = xpElectronicFenceAreas.stream().map(XpElectronicFenceArea::getElectronicId).collect(Collectors.toList());
+        // 搜索页需要拆分两套收藏态:
+        // 1. 站点胶囊按 pointId 判定。
+        // 2. 场站胶囊按 stationId 判定。
+        Set<Long> collectedPointIds = getCollectedPointIds(uid);
+        Set<Long> collectedStationIds = getCollectedStationIds(uid);
+        List<XpPointsListVo> allRows = new ArrayList<>(stationList.size() + pointList.size() + stationChildren.size());
+        allRows.addAll(stationList);
+        allRows.addAll(pointList);
+        allRows.addAll(stationChildren);
 
-        List<XpElectronicFence> xpElectronicFences = new ArrayList<>();
-        if (CollUtil.isNotEmpty(fenceIds)) {
-            xpElectronicFences = xpElectronicFenceService.listByIds(fenceIds);
-        }
+        Map<Long, XpElectronicFenceArea> electronicFenceAreaMap = buildElectronicFenceAreaMap(allRows);
+        Map<Long, XpElectronicFence> electronicFenceMap = buildElectronicFenceMap(electronicFenceAreaMap);
+        markSearchRows(allRows, collectedPointIds, collectedStationIds, electronicFenceAreaMap, electronicFenceMap);
 
+        List<XpPointsListVo> resultList = new ArrayList<>();
+        List<XpPointsListVo> stationResults = buildStationSearchResults(stationList, stationChildren, query);
+        Set<Long> displayedStationIds = stationResults.stream()
+                .map(this::getSearchResultStationId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        resultList.addAll(stationResults);
+        resultList.addAll(pointList.stream()
+                .filter(vo -> vo.getState() == 1)
+                .filter(vo -> vo.getStationId() == null || !displayedStationIds.contains(vo.getStationId()))
+                .peek(vo -> fillDistance(vo, query))
+                .collect(Collectors.toList()));
 
-        R<List<Long>> pointIdsByUserId =null;
-        // 获取用户收藏的地址
-        if(uid!=null){
-            pointIdsByUserId = xpAppUserCollectAddrClient.getPointIdsByUserId(Long.valueOf(uid));
-        }
-
-        for (XpPointsListVo xpPointsListVo : list) {
-
-            // 判断是否收藏
-            if(pointIdsByUserId!=null && !pointIdsByUserId.getData().isEmpty()){
-                if(pointIdsByUserId.getData().contains(xpPointsListVo.getId())){
-                    xpPointsListVo.setIsCollect(1);
-                }
-            }
-
-
-            Integer operationType = xpPointsListVo.getOperationType();
-            if(1 == operationType){
-                //与运营区同步
-                XpElectronicFenceArea xpElectronicFenceArea = xpElectronicFenceAreas.stream().filter(s -> s.getId().equals(xpPointsListVo.getElectronicAreaId())).findFirst().orElse(null);
-                if(null == xpElectronicFenceArea){
-                    // 不在运营段
-                    xpPointsListVo.setState(0);
-                }
-                XpElectronicFence xpElectronicFence = xpElectronicFences.stream().filter(s -> s.getId().equals(xpElectronicFenceArea.getElectronicId())).findFirst().orElse(null);
-                if(null == xpElectronicFence){
-                    // 不在运营段
-                    xpPointsListVo.setState(0);
-                }
-                //失效类型 1=永不失效 2=定时失效
-                Integer failureType = xpElectronicFence.getFailureType();
-                //永不失效,但是还未到开始时间
-                if(1 == failureType && LocalDateTime.now().isBefore(xpElectronicFence.getEffectTime())){
-                    xpPointsListVo.setState(0);
-                }else if(2 == failureType && (LocalDateTime.now().isBefore(xpElectronicFence.getEffectTime()) || LocalDateTime.now().isAfter(xpElectronicFence.getFailureTime()))){
-                    //定时失效,但是当前时间不在时间范围内
-                    xpPointsListVo.setState(0);
-                }
-            }else{
-                //自定义时间段
-                // 判断是否在时间段内
-                boolean intime = getInTime(now, xpPointsListVo.getEffectivePeriod(), xpPointsListVo.getEffectiveTimePeriod());
-                if (!intime) {
-                    // 不在运营段
-                    xpPointsListVo.setState(0);
-                }
-            }
-        }
-        // 组装数据 并排序 剔除不在运营段的 数据
-        List<XpPointsListVo> resultList = list.stream()
-                .peek(vo -> vo.setDistance(BigDecimal.valueOf(FastDistanceCalculator.distanceFast(query.getLat(), query.getLon(), vo.getLatitude(), vo.getLongitude())).setScale(2, RoundingMode.HALF_UP).doubleValue()))
-                .filter(vo -> vo.getState() == 1).collect(Collectors.toList());
-
-        // 可用点位
-        List<XpPointsListVo> availablePoints = resultList.stream().filter(e -> query.getElectronicAreaId() != null && e.getElectronicAreaId().equals(query.getElectronicAreaId())).sorted(Comparator.comparing(XpPointsListVo::getDistance)).collect(Collectors.toList());//
-
-        // 不可用点位
-        List<XpPointsListVo> unavailablePoints = resultList.stream().filter(e -> query.getElectronicAreaId() == null || !e.getElectronicAreaId().equals(query.getElectronicAreaId())).sorted(Comparator.comparing(XpPointsListVo::getDistance)).collect(Collectors.toList());
+        List<XpPointsListVo> availablePoints = resultList.stream()
+                .filter(e -> query.getElectronicAreaId() != null && e.getElectronicAreaId().equals(query.getElectronicAreaId()))
+                .sorted(Comparator.comparing(this::distanceOrMax))
+                .collect(Collectors.toList());
+        List<XpPointsListVo> unavailablePoints = resultList.stream()
+                .filter(e -> query.getElectronicAreaId() == null || !e.getElectronicAreaId().equals(query.getElectronicAreaId()))
+                .sorted(Comparator.comparing(this::distanceOrMax))
+                .collect(Collectors.toList());
         searchVo.setAvailablePoints(availablePoints);
         searchVo.setUnavailablePoints(unavailablePoints);
         return searchVo;
     }
 
+    private List<XpPointsListVo> buildStationSearchResults(List<XpPointsListVo> stationList,
+                                                           List<XpPointsListVo> stationChildren,
+                                                           PointByNameQuery query) {
+        if (CollUtil.isEmpty(stationList)) {
+            return Collections.emptyList();
+        }
+        Map<Long, List<XpPointsListVo>> childrenMap = stationChildren.stream()
+                .filter(vo -> vo.getState() == 1)
+                .peek(vo -> fillDistance(vo, query))
+                .collect(Collectors.groupingBy(this::getSearchResultStationId, LinkedHashMap::new, Collectors.toList()));
+        List<XpPointsListVo> result = new ArrayList<>();
+        for (XpPointsListVo station : stationList) {
+            if (station.getState() != 1) {
+                continue;
+            }
+            Long stationId = getSearchResultStationId(station);
+            List<XpPointsListVo> children = childrenMap.getOrDefault(stationId, Collections.emptyList());
+            if (CollUtil.isEmpty(children)) {
+                continue;
+            }
+            XpPointsListVo firstChild = children.get(0);
+            if (station.getCollectPointId() == null) {
+                station.setCollectPointId(firstChild.getId());
+            }
+            if (station.getLongitude() == null || station.getLatitude() == null) {
+                station.setLongitude(firstChild.getLongitude());
+                station.setLatitude(firstChild.getLatitude());
+            }
+            if (!StringUtils.hasLength(station.getAddress())) {
+                station.setAddress(firstChild.getAddress());
+            }
+            fillDistance(station, query);
+            station.setChildren(children);
+            result.add(station);
+        }
+        return result;
+    }
+
+    /**
+     * 仅查询“站点收藏”。
+     * nearest / 历史上下车点等老接口仍然只对 pointId 打星,所以这里不能混入场站收藏。
+     */
+    private Set<Long> getCollectedPointIds(String uid) {
+        if (uid == null) {
+            return Collections.emptySet();
+        }
+        R<List<Long>> pointIdsByUserId = xpAppUserCollectAddrClient.getPointIdsByUserId(Long.valueOf(uid));
+        if (pointIdsByUserId == null || CollUtil.isEmpty(pointIdsByUserId.getData())) {
+            return Collections.emptySet();
+        }
+        return new HashSet<>(pointIdsByUserId.getData());
+    }
+
+    /**
+     * 场站收藏需要单独查询。
+     * 搜索结果里的场站父级行应按 stationId 判断收藏态,不能继续复用 pointId 集合。
+     */
+    private Set<Long> getCollectedStationIds(String uid) {
+        if (uid == null) {
+            return Collections.emptySet();
+        }
+        R<List<Long>> stationIdsByUserId = xpAppUserCollectAddrClient.getStationIdsByUserId(Long.valueOf(uid));
+        if (stationIdsByUserId == null || CollUtil.isEmpty(stationIdsByUserId.getData())) {
+            return Collections.emptySet();
+        }
+        return new HashSet<>(stationIdsByUserId.getData());
+    }
+
+    private Map<Long, XpElectronicFenceArea> buildElectronicFenceAreaMap(List<XpPointsListVo> rows) {
+        List<Long> areaIds = rows.stream()
+                .map(XpPointsListVo::getElectronicAreaId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+        if (CollUtil.isEmpty(areaIds)) {
+            return Collections.emptyMap();
+        }
+        return xpElectronicFenceAreaService.listByIds(areaIds).stream()
+                .collect(Collectors.toMap(XpElectronicFenceArea::getId, area -> area, (left, right) -> left));
+    }
+
+    private Map<Long, XpElectronicFence> buildElectronicFenceMap(Map<Long, XpElectronicFenceArea> electronicFenceAreaMap) {
+        if (electronicFenceAreaMap.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        List<Long> fenceIds = electronicFenceAreaMap.values().stream()
+                .map(XpElectronicFenceArea::getElectronicId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+        if (CollUtil.isEmpty(fenceIds)) {
+            return Collections.emptyMap();
+        }
+        return xpElectronicFenceService.listByIds(fenceIds).stream()
+                .collect(Collectors.toMap(XpElectronicFence::getId, fence -> fence, (left, right) -> left));
+    }
+
+    private void markSearchRows(List<XpPointsListVo> rows,
+                                Set<Long> collectedPointIds,
+                                Set<Long> collectedStationIds,
+                                Map<Long, XpElectronicFenceArea> electronicFenceAreaMap,
+                                Map<Long, XpElectronicFence> electronicFenceMap) {
+        LocalTime now = LocalTime.now();
+        for (XpPointsListVo row : rows) {
+            if (isCollectedSearchRow(row, collectedPointIds, collectedStationIds)) {
+                row.setIsCollect(1);
+            }
+            if (!isSearchRowAvailable(row, electronicFenceAreaMap, electronicFenceMap, now)) {
+                row.setState(0);
+            }
+        }
+    }
+
+    /**
+     * 搜索结果收藏态判定规则:
+     * 1. 站点行按 pointId / collectPointId 判定。
+     * 2. 场站行按 stationId 判定。
+     */
+    private boolean isCollectedSearchRow(XpPointsListVo row,
+                                         Set<Long> collectedPointIds,
+                                         Set<Long> collectedStationIds) {
+        if (row.getItemType() != null && row.getItemType() == 2) {
+            Long stationId = getSearchResultStationId(row);
+            return stationId != null && collectedStationIds.contains(stationId);
+        }
+        Long collectPointId = row.getCollectPointId() != null ? row.getCollectPointId() : row.getId();
+        return collectPointId != null && collectedPointIds.contains(collectPointId);
+    }
+
+    private boolean isSearchRowAvailable(XpPointsListVo row,
+                                         Map<Long, XpElectronicFenceArea> electronicFenceAreaMap,
+                                         Map<Long, XpElectronicFence> electronicFenceMap,
+                                         LocalTime now) {
+        Integer operationType = row.getOperationType();
+        if (operationType == null) {
+            return false;
+        }
+        if (operationType == 1) {
+            Long electronicAreaId = row.getElectronicAreaId();
+            if (electronicAreaId == null) {
+                return false;
+            }
+            XpElectronicFenceArea electronicFenceArea = electronicFenceAreaMap.get(electronicAreaId);
+            if (electronicFenceArea == null || electronicFenceArea.getElectronicId() == null) {
+                return false;
+            }
+            XpElectronicFence electronicFence = electronicFenceMap.get(electronicFenceArea.getElectronicId());
+            if (electronicFence == null) {
+                return false;
+            }
+            Integer failureType = electronicFence.getFailureType();
+            if (failureType == null) {
+                return true;
+            }
+            if (failureType == 1 && LocalDateTime.now().isBefore(electronicFence.getEffectTime())) {
+                return false;
+            }
+            return failureType != 2 || (!LocalDateTime.now().isBefore(electronicFence.getEffectTime())
+                    && !LocalDateTime.now().isAfter(electronicFence.getFailureTime()));
+        }
+        if (row.getEffectivePeriod() == null || !StringUtils.hasLength(row.getEffectiveTimePeriod())) {
+            return true;
+        }
+        return getInTime(now, row.getEffectivePeriod(), row.getEffectiveTimePeriod());
+    }
+
+    private void fillDistance(XpPointsListVo row, PointByNameQuery query) {
+        if (row.getLatitude() == null || row.getLongitude() == null) {
+            return;
+        }
+        row.setDistance(BigDecimal.valueOf(FastDistanceCalculator.distanceFast(query.getLat(), query.getLon(), row.getLatitude(), row.getLongitude()))
+                .setScale(2, RoundingMode.HALF_UP)
+                .doubleValue());
+    }
+
+    private Long getSearchResultStationId(XpPointsListVo row) {
+        return row.getStationId() != null ? row.getStationId() : row.getId();
+    }
+
+    private Double distanceOrMax(XpPointsListVo row) {
+        return row.getDistance() == null ? Double.MAX_VALUE : row.getDistance();
+    }
+
     @Override
     public OrderDistanceVo getOrderDistance() {
         OrderDistanceVo orderDistanceVo = new OrderDistanceVo();

--
Gitblit v1.7.1