| | |
| | | @Value("${navigation.route.price-wave-percent:5}") |
| | | private double navigationPriceWavePercent; |
| | | |
| | | /** |
| | | * 后台可配置的偏好项编码,逗号分隔,如 SMART,TIME,PRICE |
| | | */ |
| | | @Value("${navigation.route.preference.enabled-codes:SMART,TIME,PRICE}") |
| | | private String navigationPreferenceEnabledCodes; |
| | | |
| | | // 自定义线程池用于并行执行任务 |
| | | private final ExecutorService executorService; |
| | | |
| | |
| | | throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION); |
| | | } |
| | | |
| | | List<RouteStrategyKind> kinds = Arrays.asList(RouteStrategyKind.SMART, RouteStrategyKind.TIME, RouteStrategyKind.PRICE); |
| | | List<RouteStrategyKind> kinds = buildStrategyKindsForPlanning(userId); |
| | | Map<String, RouteVo> routeById = new LinkedHashMap<>(); |
| | | Map<String, Set<RouteStrategyKind>> strategiesById = new LinkedHashMap<>(); |
| | | for (RouteStrategyKind kind : kinds) { |
| | |
| | | options.add(opt); |
| | | } |
| | | |
| | | RouteStrategyKind userPref = resolveUserPreferredKind(userId).orElse(RouteStrategyKind.SMART); |
| | | options.sort((a, b) -> compareRouteOptionByUserPreference(a, b, userPref, strategiesById)); |
| | | |
| | | String snapshotId = UUID.randomUUID().toString().replace("-", ""); |
| | | long now = System.currentTimeMillis(); |
| | | MultiRouteSnapshotPayload payload = new MultiRouteSnapshotPayload(); |
| | |
| | | vo.setGeneratedAtMillis(now); |
| | | vo.setShowRouteSelector(options.size() >= 2); |
| | | vo.setRoutes(options); |
| | | vo.setRoutePreferences(listRoutePreferences(routeAndFareDto.getOpenCityId())); |
| | | vo.setRoutePreferences(listRoutePreferences(routeAndFareDto.getOpenCityId(), userId)); |
| | | vo.setStaleCheckSeconds(navigationStaleSeconds); |
| | | vo.setPriceWaveThresholdPercent(navigationPriceWavePercent); |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public List<RoutePreferenceItemVo> listRoutePreferences(Long openCityId) { |
| | | public List<RoutePreferenceItemVo> listRoutePreferences(Long openCityId, String userId) { |
| | | Set<String> enabledCodes = parseEnabledPreferenceCodes(); |
| | | Optional<RouteStrategyKind> savedPref = resolveUserPreferredKind(userId); |
| | | List<RoutePreferenceItemVo> list = new ArrayList<>(); |
| | | int i = 0; |
| | | for (RouteStrategyKind k : Arrays.asList(RouteStrategyKind.SMART, RouteStrategyKind.TIME, RouteStrategyKind.PRICE)) { |
| | | if (!enabledCodes.contains(k.name())) { |
| | | continue; |
| | | } |
| | | RoutePreferenceItemVo v = new RoutePreferenceItemVo(); |
| | | v.setCode(k.name()); |
| | | v.setName(k.getDisplayName()); |
| | | v.setEnabled(true); |
| | | v.setSortOrder(i++); |
| | | v.setAmapStrategy(k.getAmapStrategy()); |
| | | v.setSelected(savedPref.isPresent() && k == savedPref.get()); |
| | | list.add(v); |
| | | } |
| | | return list; |
| | |
| | | redisService.setCacheObject(CacheConstants.USER_ROUTE_PREFERENCE + userId, String.valueOf(k.getAmapStrategy()), 365L, TimeUnit.DAYS); |
| | | } |
| | | |
| | | private Set<String> parseEnabledPreferenceCodes() { |
| | | Set<String> set = new LinkedHashSet<>(); |
| | | for (String part : navigationPreferenceEnabledCodes.split(",")) { |
| | | String p = part.trim().toUpperCase(Locale.ROOT); |
| | | if (!p.isEmpty()) { |
| | | set.add(p); |
| | | } |
| | | } |
| | | if (set.isEmpty()) { |
| | | set.add(RouteStrategyKind.SMART.name()); |
| | | set.add(RouteStrategyKind.TIME.name()); |
| | | set.add(RouteStrategyKind.PRICE.name()); |
| | | } |
| | | return set; |
| | | } |
| | | |
| | | /** |
| | | * 用户偏好策略优先请求,其余策略仍请求以便合并去重。 |
| | | */ |
| | | private List<RouteStrategyKind> buildStrategyKindsForPlanning(String userId) { |
| | | List<RouteStrategyKind> all = new ArrayList<>(); |
| | | Set<String> enabled = parseEnabledPreferenceCodes(); |
| | | for (RouteStrategyKind k : Arrays.asList(RouteStrategyKind.SMART, RouteStrategyKind.TIME, RouteStrategyKind.PRICE)) { |
| | | if (enabled.contains(k.name())) { |
| | | all.add(k); |
| | | } |
| | | } |
| | | if (all.isEmpty()) { |
| | | all.add(RouteStrategyKind.SMART); |
| | | } |
| | | Optional<RouteStrategyKind> pref = resolveUserPreferredKind(userId); |
| | | if (!pref.isPresent()) { |
| | | return all; |
| | | } |
| | | RouteStrategyKind p = pref.get(); |
| | | if (!all.contains(p)) { |
| | | return all; |
| | | } |
| | | List<RouteStrategyKind> ordered = new ArrayList<>(); |
| | | ordered.add(p); |
| | | for (RouteStrategyKind k : all) { |
| | | if (k != p) { |
| | | ordered.add(k); |
| | | } |
| | | } |
| | | return ordered; |
| | | } |
| | | |
| | | private Optional<RouteStrategyKind> resolveUserPreferredKind(String userId) { |
| | | if (userId == null || userId.isEmpty()) { |
| | | return Optional.empty(); |
| | | } |
| | | Object raw = redisService.getCacheObject(CacheConstants.USER_ROUTE_PREFERENCE + userId); |
| | | if (raw == null) { |
| | | return Optional.empty(); |
| | | } |
| | | try { |
| | | int strategy = Integer.parseInt(raw.toString()); |
| | | return RouteStrategyKind.fromAmapStrategy(strategy); |
| | | } catch (Exception e) { |
| | | return Optional.empty(); |
| | | } |
| | | } |
| | | |
| | | private int compareRouteOptionByUserPreference(RouteOptionVo a, RouteOptionVo b, RouteStrategyKind pref, |
| | | Map<String, Set<RouteStrategyKind>> strategiesById) { |
| | | Set<RouteStrategyKind> sa = strategiesById.getOrDefault(a.getPathFingerprint(), Collections.emptySet()); |
| | | Set<RouteStrategyKind> sb = strategiesById.getOrDefault(b.getPathFingerprint(), Collections.emptySet()); |
| | | boolean aHas = sa.contains(pref); |
| | | boolean bHas = sb.contains(pref); |
| | | if (aHas != bHas) { |
| | | return bHas ? 1 : -1; |
| | | } |
| | | boolean aPrimary = a.getPrimaryRoutePlanRule() != null && a.getPrimaryRoutePlanRule() == pref.getAmapStrategy(); |
| | | boolean bPrimary = b.getPrimaryRoutePlanRule() != null && b.getPrimaryRoutePlanRule() == pref.getAmapStrategy(); |
| | | if (aPrimary != bPrimary) { |
| | | return bPrimary ? 1 : -1; |
| | | } |
| | | int da = a.getRoute() != null && a.getRoute().getDuration() != null ? a.getRoute().getDuration() : Integer.MAX_VALUE; |
| | | int db = b.getRoute() != null && b.getRoute().getDuration() != null ? b.getRoute().getDuration() : Integer.MAX_VALUE; |
| | | return Integer.compare(da, db); |
| | | } |
| | | |
| | | } |