| | |
| | | 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.service.impl.ServiceImpl; |
| | | import com.xiaopeng.api.dto.operation.PointMapQuery; |
| | | import com.xiaopeng.api.dto.order.HistoryEndPointsDto; |
| | | import com.xiaopeng.api.dto.order.HistoryStartPointsDto; |
| | | import com.xiaopeng.api.feignClient.account.XpAppUserCollectAddrClient; |
| | | import com.xiaopeng.api.model.account.XpAppUserCollectAddr; |
| | | import com.xiaopeng.api.model.operation.*; |
| | | import com.xiaopeng.api.query.operation.PointListQuery; |
| | | import com.xiaopeng.api.query.operation.PointPageListListQuery; |
| | |
| | | import com.xiaopeng.api.vo.order.XpPointsListVo; |
| | | import com.xiaopeng.api.vo.order.XpPointsSearchVo; |
| | | import com.xiaopeng.common.core.constant.CacheConstants; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.web.page.PageInfo; |
| | | import com.xiaopeng.common.redis.service.RedisService; |
| | | import com.xiaopeng.evi.oss.service.AliOssService; |
| | |
| | | private IXpElectronicFenceService xpElectronicFenceService; |
| | | |
| | | @Resource |
| | | private XpPointMapper baseMapper; |
| | | |
| | | @Resource |
| | | private XpAppUserCollectAddrClient xpAppUserCollectAddrClient; |
| | | |
| | | @Resource |
| | | private AliOssService aliOssService; |
| | | |
| | | |
| | | @Override |
| | | public NearestVo nearest(NearestDto dto) { |
| | | public NearestVo nearest(NearestDto dto,String uid) { |
| | | // 走缓存 |
| | | Object orderDistanceObj = redisService.getCacheObject(CacheConstants.NEAR_POINT_DISTANCE); |
| | | Integer orderDistance; |
| | |
| | | if (CollUtil.isNotEmpty(fenceIds)) { |
| | | xpElectronicFences = xpElectronicFenceService.listByIds(fenceIds); |
| | | } |
| | | R<List<Long>> pointIdsByUserId = null; |
| | | // 获取用户收藏的地址 |
| | | if(uid!=null){ |
| | | pointIdsByUserId = xpAppUserCollectAddrClient.getPointIdsByUserId(Long.valueOf(uid)); |
| | | } |
| | | |
| | | |
| | | //判断当前点位是否在运营时间范围内 |
| | | for (XpPoint xpPoint : list) { |
| | |
| | | nearestNextVo.setTime(xpPoint.getParkingTimeLimit()); |
| | | nearestNextVo.setElectronicAreaId(xpPoint.getElectronicAreaId()); |
| | | |
| | | // 判断是否收藏 |
| | | if(pointIdsByUserId!=null && !pointIdsByUserId.getData().isEmpty()){ |
| | | if(pointIdsByUserId.getData().contains(xpPoint.getId())){ |
| | | nearestNextVo.setIsCollect(1); |
| | | } |
| | | } |
| | | |
| | | nearestNextVos.add(nearestNextVo); |
| | | } |
| | |
| | | xpElectronicFences = xpElectronicFenceService.listByIds(fenceIds); |
| | | } |
| | | |
| | | |
| | | // 获取用户收藏的地址 |
| | | R<List<Long>> pointIdsByUserId = xpAppUserCollectAddrClient.getPointIdsByUserId(historyStartPointsDto.getUserId()); |
| | | |
| | | for (XpPoint xpPoint : xpPoints) { |
| | | |
| | | if(xpPoint.getOperationStatus()!=2){ |
| | | continue; |
| | | } |
| | |
| | | xpHistoryStartPointsVo.setDistance(distanced); |
| | | xpHistoryStartPointsVo.setTime(xpPoint.getParkingTimeLimit()); |
| | | xpHistoryStartPointsVo.setElectronicAreaId(xpPoint.getElectronicAreaId()); |
| | | resultList.add(xpHistoryStartPointsVo); |
| | | // 判断是否收藏 |
| | | if(pointIdsByUserId!=null && !pointIdsByUserId.getData().isEmpty()){ |
| | | if(pointIdsByUserId.getData().contains(xpPoint.getId())){ |
| | | xpHistoryStartPointsVo.setIsCollect(1); |
| | | } |
| | | } |
| | | |
| | | resultList.add(xpHistoryStartPointsVo); |
| | | } |
| | | |
| | | return resultList; |
| | |
| | | } |
| | | |
| | | |
| | | |
| | | // 获取用户收藏的地址 |
| | | R<List<Long>> pointIdsByUserId = xpAppUserCollectAddrClient.getPointIdsByUserId(historyEndPointsDto.getUserId()); |
| | | |
| | | |
| | | for (XpPoint xpPoint : xpPoints) { |
| | |
| | | xpHistoryEndPointsVo.setDistance(distanced); |
| | | xpHistoryEndPointsVo.setTime(xpPoint.getParkingTimeLimit()); |
| | | xpHistoryEndPointsVo.setElectronicAreaId(xpPoint.getElectronicAreaId()); |
| | | |
| | | // 判断是否收藏 |
| | | if(pointIdsByUserId!=null && !pointIdsByUserId.getData().isEmpty()){ |
| | | if(pointIdsByUserId.getData().contains(xpPoint.getId())){ |
| | | xpHistoryEndPointsVo.setIsCollect(1); |
| | | } |
| | | } |
| | | resultList.add(xpHistoryEndPointsVo); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public XpPointsSearchVo queryPointByName(PointByNameQuery query) { |
| | | 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); |
| | | } |
| | | for (XpPointsListVo xpPointsListVo : list) { |
| | | 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()); |
| | | Map<Long, XpElectronicFenceArea> electronicFenceAreaMap = buildElectronicFenceAreaMap(allRows); |
| | | Map<Long, XpElectronicFence> electronicFenceMap = buildElectronicFenceMap(electronicFenceAreaMap); |
| | | markSearchRows(allRows, collectedPointIds, collectedStationIds, electronicFenceAreaMap, electronicFenceMap); |
| | | |
| | | // 可用点位 |
| | | 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> 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())); |
| | | |
| | | // 不可用点位 |
| | | 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(); |