| | |
| | | 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.operation.XpElectronicFenceArea; |
| | | import com.xiaopeng.api.model.operation.XpElectronicFence; |
| | | import com.xiaopeng.api.model.operation.XpPoint; |
| | | import com.xiaopeng.api.model.operation.XpRegion; |
| | | import com.xiaopeng.api.model.operation.XpStation; |
| | | import com.xiaopeng.api.model.operation.XpSystemSetting; |
| | | 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.exception.ServiceException; |
| | | import com.xiaopeng.common.core.utils.StringUtils; |
| | | import com.xiaopeng.common.core.web.page.PageInfo; |
| | |
| | | import com.xiaopeng.evi.oss.service.AliOssService; |
| | | import com.xiaopeng.map.constant.I18nKeyConstant; |
| | | import com.xiaopeng.map.dto.NearestDto; |
| | | import com.xiaopeng.map.dto.StationDetailQueryDto; |
| | | import com.xiaopeng.map.mapper.XpPointMapper; |
| | | import com.xiaopeng.map.mapper.XpRegionMapper; |
| | | import com.xiaopeng.map.mapper.XpSystemSettingMapper; |
| | | import com.xiaopeng.map.query.PointByNameQuery; |
| | | import com.xiaopeng.map.service.XpElectronicFenceService; |
| | | import com.xiaopeng.map.service.XpElectronicFenceAreaService; |
| | | import com.xiaopeng.map.service.XpPointService; |
| | | import com.xiaopeng.map.service.XpStationService; |
| | | import com.xiaopeng.map.utils.TimeRangeChecker; |
| | | import com.xiaopeng.map.utils.holiday.HolidayUtil; |
| | | import com.xiaopeng.map.utils.map.map.FastDistanceCalculator; |
| | | import com.xiaopeng.map.vo.NearestNextVo; |
| | | import com.xiaopeng.map.vo.NearestVo; |
| | | import com.xiaopeng.map.vo.OrderDistanceVo; |
| | | import com.xiaopeng.map.vo.StationDetailVo; |
| | | import com.xiaopeng.map.vo.StationPointItemVo; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import net.sourceforge.pinyin4j.PinyinHelper; |
| | | import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType; |
| | |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDateTime; |
| | | import java.time.LocalTime; |
| | | import java.util.*; |
| | | import java.util.concurrent.TimeUnit; |
| | |
| | | |
| | | @Resource |
| | | private XpElectronicFenceAreaService xpElectronicFenceAreaService; |
| | | |
| | | @Resource |
| | | private XpElectronicFenceService xpElectronicFenceService; |
| | | |
| | | @Resource |
| | | private XpStationService xpStationService; |
| | | |
| | | @Resource |
| | | private XpAppUserCollectAddrClient xpAppUserCollectAddrClient; |
| | | |
| | | @Resource |
| | | private XpRegionMapper xpRegionMapper; |
| | |
| | | } |
| | | |
| | | @Override |
| | | public StationDetailVo queryStationDetail(StationDetailQueryDto dto, String uid) { |
| | | StationDetailVo stationDetailVo = new StationDetailVo(); |
| | | if (dto == null || dto.getStationId() == null) { |
| | | return stationDetailVo; |
| | | } |
| | | |
| | | XpStation station = xpStationService.getById(dto.getStationId()); |
| | | if (station == null || !isStationAvailable(station)) { |
| | | return stationDetailVo; |
| | | } |
| | | |
| | | // 仅查询场站下运营点位,最终结果还会叠加时段/围栏有效性过滤。 |
| | | List<XpPoint> stationPoints = this.lambdaQuery() |
| | | .eq(XpPoint::getStationId, station.getId()) |
| | | .eq(XpPoint::getPointType, 1) |
| | | .eq(XpPoint::getOperationStatus, 2) |
| | | .list(); |
| | | if (CollUtil.isEmpty(stationPoints)) { |
| | | return stationDetailVo; |
| | | } |
| | | |
| | | Map<Long, XpElectronicFenceArea> areaMap = buildAreaMap(stationPoints.stream() |
| | | .map(XpPoint::getElectronicAreaId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList())); |
| | | Map<Long, XpElectronicFence> fenceMap = buildFenceMap(areaMap.values().stream() |
| | | .map(XpElectronicFenceArea::getElectronicId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList())); |
| | | Set<Long> collectStationIds = getCollectStationIds(uid); |
| | | Set<Long> collectPointIds = getCollectPointIds(uid); |
| | | |
| | | // 默认点位优先置顶,其余点位再按场站内排序和距离排序。 |
| | | List<StationPointItemVo> points = stationPoints.stream() |
| | | .filter(point -> isPointAvailable(point, areaMap, fenceMap)) |
| | | .map(point -> toStationPointItem(point, station.getDefaultPointId(), dto.getLatitude(), dto.getLongitude(), collectPointIds)) |
| | | .sorted(buildStationPointComparator()) |
| | | .collect(Collectors.toList()); |
| | | if (points.isEmpty()) { |
| | | return new StationDetailVo(); |
| | | } |
| | | |
| | | stationDetailVo.setStationId(station.getId()); |
| | | stationDetailVo.setStationName(station.getStationName()); |
| | | stationDetailVo.setDefaultPointId(station.getDefaultPointId()); |
| | | stationDetailVo.setCenterLon(station.getCenterLon()); |
| | | stationDetailVo.setCenterLat(station.getCenterLat()); |
| | | if (collectStationIds.contains(station.getId())) { |
| | | stationDetailVo.setIsCollect(1); |
| | | } |
| | | stationDetailVo.setPoints(points); |
| | | return stationDetailVo; |
| | | } |
| | | |
| | | @Override |
| | | public XpPointsSearchVo queryPointByName(PointByNameQuery query) { |
| | | XpPointsSearchVo searchVo = new XpPointsSearchVo(); |
| | | List<XpPointsListVo> list = this.baseMapper.queryPointByName(query); |
| | |
| | | return pageInfo; |
| | | } |
| | | |
| | | private Map<Long, XpElectronicFenceArea> buildAreaMap(List<Long> areaIds) { |
| | | if (CollUtil.isEmpty(areaIds)) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | return xpElectronicFenceAreaService.listByIds(areaIds).stream() |
| | | .collect(Collectors.toMap(XpElectronicFenceArea::getId, area -> area, (a, b) -> a)); |
| | | } |
| | | |
| | | /** |
| | | * 提前拉取电子围栏,避免逐点位重复查库。 |
| | | */ |
| | | private Map<Long, XpElectronicFence> buildFenceMap(List<Long> fenceIds) { |
| | | if (CollUtil.isEmpty(fenceIds)) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | return xpElectronicFenceService.listByIds(fenceIds).stream() |
| | | .collect(Collectors.toMap(XpElectronicFence::getId, fence -> fence, (a, b) -> a)); |
| | | } |
| | | |
| | | /** |
| | | * 场站可展示判断: |
| | | * 1. 状态必须为启用中; |
| | | * 2. 与运营区同步时,依赖运营区和围栏的当前有效性; |
| | | * 3. 自定义时段时,依赖场站自身时段配置。 |
| | | */ |
| | | private boolean isStationAvailable(XpStation station) { |
| | | if (station == null || !Integer.valueOf(6).equals(station.getStatus())) { |
| | | return false; |
| | | } |
| | | if (Integer.valueOf(1).equals(station.getOperationType())) { |
| | | if (station.getElectronicAreaId() == null) { |
| | | return false; |
| | | } |
| | | XpElectronicFenceArea area = xpElectronicFenceAreaService.getById(station.getElectronicAreaId()); |
| | | if (area == null || !Integer.valueOf(1).equals(area.getAreaStatus()) || area.getElectronicId() == null) { |
| | | return false; |
| | | } |
| | | XpElectronicFence fence = xpElectronicFenceService.getById(area.getElectronicId()); |
| | | return isFenceAvailable(fence); |
| | | } |
| | | if (Integer.valueOf(2).equals(station.getOperationType()) |
| | | && station.getEffectivePeriod() != null |
| | | && StringUtils.isNotBlank(station.getEffectiveTimePeriod())) { |
| | | return getInTime(LocalTime.now(), station.getEffectivePeriod(), station.getEffectiveTimePeriod()); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * 子点位是否可展示与场站卡片中。 |
| | | * 规则与现有点位有效性保持一致,避免地图页和其他点位查询出现口径偏差。 |
| | | */ |
| | | private boolean isPointAvailable(XpPoint point, Map<Long, XpElectronicFenceArea> areaMap, Map<Long, XpElectronicFence> fenceMap) { |
| | | if (point == null || !Integer.valueOf(2).equals(point.getOperationStatus())) { |
| | | return false; |
| | | } |
| | | if (Integer.valueOf(1).equals(point.getOperationType())) { |
| | | if (point.getElectronicAreaId() == null) { |
| | | return false; |
| | | } |
| | | XpElectronicFenceArea area = areaMap.get(point.getElectronicAreaId()); |
| | | if (area == null || !Integer.valueOf(1).equals(area.getAreaStatus()) || area.getElectronicId() == null) { |
| | | return false; |
| | | } |
| | | return isFenceAvailable(fenceMap.get(area.getElectronicId())); |
| | | } |
| | | if (Integer.valueOf(2).equals(point.getOperationType()) |
| | | && point.getEffectivePeriod() != null |
| | | && StringUtils.isNotBlank(point.getEffectiveTimePeriod())) { |
| | | return getInTime(LocalTime.now(), point.getEffectivePeriod(), point.getEffectiveTimePeriod()); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * 围栏生效判断: |
| | | * 启用中,且当前时间需要落在生效时间与失效时间窗口内。 |
| | | */ |
| | | private boolean isFenceAvailable(XpElectronicFence fence) { |
| | | if (fence == null || !Integer.valueOf(2).equals(fence.getStatus())) { |
| | | return false; |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | if (fence.getEffectTime() != null && now.isBefore(fence.getEffectTime())) { |
| | | return false; |
| | | } |
| | | return !Integer.valueOf(2).equals(fence.getFailureType()) || fence.getFailureTime() == null || !now.isAfter(fence.getFailureTime()); |
| | | } |
| | | |
| | | /** |
| | | * 组装单个场站子点位返回对象,并在有默认点位配置时打标。 |
| | | */ |
| | | private StationPointItemVo toStationPointItem(XpPoint point, Long defaultPointId, Double latitude, Double longitude, Set<Long> collectPointIds) { |
| | | StationPointItemVo itemVo = new StationPointItemVo(); |
| | | itemVo.setPointId(point.getId()); |
| | | itemVo.setPointName(point.getPointName()); |
| | | itemVo.setPointAddress(point.getPointAddress()); |
| | | itemVo.setLatitude(point.getPointLat()); |
| | | itemVo.setLongitude(point.getPointLon()); |
| | | itemVo.setStationSortNo(point.getStationSortNo()); |
| | | if (collectPointIds.contains(point.getId())) { |
| | | itemVo.setIsCollect(1); |
| | | } |
| | | if (defaultPointId != null && defaultPointId.equals(point.getId())) { |
| | | itemVo.setIsDefault(1); |
| | | } |
| | | if (latitude != null && longitude != null && point.getPointLat() != null && point.getPointLon() != null) { |
| | | double distance = FastDistanceCalculator.distanceFast(latitude, longitude, point.getPointLat(), point.getPointLon()); |
| | | itemVo.setDistance(BigDecimal.valueOf(distance).setScale(2, RoundingMode.HALF_UP).doubleValue()); |
| | | } |
| | | return itemVo; |
| | | } |
| | | |
| | | /** |
| | | * 排序规则: |
| | | * 默认点位优先,其次按场站内排序,再按与当前定位距离排序。 |
| | | */ |
| | | private Comparator<StationPointItemVo> buildStationPointComparator() { |
| | | return Comparator |
| | | .comparing((StationPointItemVo item) -> item.getIsDefault() == null ? 2 : item.getIsDefault()) |
| | | .thenComparing(item -> item.getStationSortNo() == null ? Integer.MAX_VALUE : item.getStationSortNo()) |
| | | .thenComparing(item -> item.getDistance() == null ? Double.MAX_VALUE : item.getDistance()); |
| | | } |
| | | |
| | | /** |
| | | * 查询用户收藏的场站ID。 |
| | | * 收藏服务异常时降级为空集合,避免影响场站详情主流程。 |
| | | */ |
| | | private Set<Long> getCollectStationIds(String uid) { |
| | | if (!StringUtils.hasText(uid)) { |
| | | return Collections.emptySet(); |
| | | } |
| | | try { |
| | | R<List<Long>> result = xpAppUserCollectAddrClient.getStationIdsByUserId(Long.valueOf(uid)); |
| | | if (result == null || CollUtil.isEmpty(result.getData())) { |
| | | return Collections.emptySet(); |
| | | } |
| | | return new HashSet<>(result.getData()); |
| | | } catch (Exception e) { |
| | | log.warn("查询用户收藏场站失败 uid={}", uid, e); |
| | | return Collections.emptySet(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 查询用户收藏的点位ID。 |
| | | * 子点位收藏态只用于回显,不影响场站筛选和排序主逻辑。 |
| | | */ |
| | | private Set<Long> getCollectPointIds(String uid) { |
| | | if (!StringUtils.hasText(uid)) { |
| | | return Collections.emptySet(); |
| | | } |
| | | try { |
| | | R<List<Long>> result = xpAppUserCollectAddrClient.getPointIdsByUserId(Long.valueOf(uid)); |
| | | if (result == null || CollUtil.isEmpty(result.getData())) { |
| | | return Collections.emptySet(); |
| | | } |
| | | return new HashSet<>(result.getData()); |
| | | } catch (Exception e) { |
| | | log.warn("查询用户收藏点位失败 uid={}", uid, e); |
| | | return Collections.emptySet(); |
| | | } |
| | | } |
| | | |
| | | } |