package com.zy.asrs.service.impl; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.zy.asrs.domain.path.StationPathProfileConfig; import com.zy.asrs.domain.path.StationPathResolvedPolicy; import com.zy.asrs.domain.path.StationPathRuleConfig; import com.zy.asrs.entity.BasStationPathProfile; import com.zy.asrs.entity.BasStationPathRule; import com.zy.asrs.service.BasStationPathProfileService; import com.zy.asrs.service.BasStationPathRuleService; import com.zy.asrs.service.StationPathPolicyService; import com.zy.common.utils.RedisUtil; import com.zy.core.enums.RedisKeyType; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @Service("stationPathPolicyService") @Slf4j public class StationPathPolicyServiceImpl implements StationPathPolicyService { private static final long CACHE_TTL_MILLIS = 5_000L; @Autowired private BasStationPathProfileService basStationPathProfileService; @Autowired private BasStationPathRuleService basStationPathRuleService; @Autowired private RedisUtil redisUtil; private volatile CacheSnapshot cacheSnapshot = new CacheSnapshot(); private volatile long cacheTime = 0L; @Override public StationPathResolvedPolicy resolvePolicy(Integer startStationId, Integer endStationId) { StationPathResolvedPolicy resolved = new StationPathResolvedPolicy(); resolved.setScoreMode(getSystemConfig("stationPathScoreMode", "legacy")); resolved.setDefaultProfileCode(getSystemConfig("stationPathDefaultProfileCode", "default")); CacheSnapshot snapshot = getCacheSnapshot(); BasStationPathRule matchedRule = matchRule(snapshot.ruleList, startStationId, endStationId); BasStationPathProfile matchedProfile = null; if (matchedRule != null && notBlank(matchedRule.getProfileCode())) { matchedProfile = snapshot.profileMap.get(matchedRule.getProfileCode()); } if (matchedProfile == null && notBlank(resolved.getDefaultProfileCode())) { matchedProfile = snapshot.profileMap.get(resolved.getDefaultProfileCode()); } if (matchedProfile == null) { matchedProfile = snapshot.profileMap.get("default"); } if (matchedProfile == null && !snapshot.profileList.isEmpty()) { matchedProfile = snapshot.profileList.get(0); } resolved.setRuleEntity(matchedRule); resolved.setProfileEntity(matchedProfile); resolved.setProfileConfig(parseProfileConfig(matchedProfile == null ? null : matchedProfile.getConfigJson())); resolved.setRuleConfig(parseRuleConfig(matchedRule)); return resolved; } @Override public void evictCache() { cacheSnapshot = new CacheSnapshot(); cacheTime = 0L; } private CacheSnapshot getCacheSnapshot() { long now = System.currentTimeMillis(); CacheSnapshot local = cacheSnapshot; if (local.loaded && now - cacheTime < CACHE_TTL_MILLIS) { return local; } synchronized (this) { local = cacheSnapshot; if (local.loaded && now - cacheTime < CACHE_TTL_MILLIS) { return local; } cacheSnapshot = loadSnapshot(); cacheTime = System.currentTimeMillis(); return cacheSnapshot; } } private CacheSnapshot loadSnapshot() { CacheSnapshot snapshot = new CacheSnapshot(); try { List profiles = basStationPathProfileService.list(new QueryWrapper() .eq("status", 1) .orderByAsc("priority", "id")); if (profiles != null) { snapshot.profileList.addAll(profiles); for (BasStationPathProfile profile : profiles) { if (profile != null && notBlank(profile.getProfileCode())) { snapshot.profileMap.put(profile.getProfileCode(), profile); } } } } catch (Exception e) { log.warn("加载站点路径模板失败,回退默认配置: {}", e.getMessage()); } try { List rules = basStationPathRuleService.list(new QueryWrapper() .eq("status", 1) .orderByAsc("priority", "id")); if (rules != null) { snapshot.ruleList.addAll(rules); } } catch (Exception e) { log.warn("加载站点路径规则失败,忽略人工规则: {}", e.getMessage()); } snapshot.loaded = true; return snapshot; } private BasStationPathRule matchRule(List ruleList, Integer startStationId, Integer endStationId) { if (ruleList == null || ruleList.isEmpty()) { return null; } List candidates = new ArrayList<>(); for (BasStationPathRule rule : ruleList) { if (rule == null) { continue; } if (!matchNullable(rule.getStartStationId(), startStationId)) { continue; } if (!matchNullable(rule.getEndStationId(), endStationId)) { continue; } candidates.add(rule); } if (candidates.isEmpty()) { return null; } candidates.sort(Comparator .comparingInt(this::specificity).reversed() .thenComparingInt(rule -> safeInt(rule.getPriority(), 100)) .thenComparingLong(rule -> rule.getId() == null ? Long.MAX_VALUE : rule.getId())); return candidates.get(0); } private boolean matchNullable(Integer expected, Integer actual) { return expected == null || expected.equals(actual); } private int specificity(BasStationPathRule rule) { int score = 0; if (rule.getStartStationId() != null) { score += 1; } if (rule.getEndStationId() != null) { score += 1; } return score; } private StationPathProfileConfig parseProfileConfig(String configJson) { StationPathProfileConfig config = StationPathProfileConfig.defaultConfig(); if (!notBlank(configJson)) { return config; } try { StationPathProfileConfig parsed = JSON.parseObject(configJson, StationPathProfileConfig.class); config.mergeFrom(parsed); } catch (Exception e) { log.warn("解析站点路径模板配置失败,使用默认值: {}", e.getMessage()); } return config; } private StationPathRuleConfig parseRuleConfig(BasStationPathRule rule) { StationPathRuleConfig config = new StationPathRuleConfig(); if (rule == null) { return config; } try { if (notBlank(rule.getHardJson())) { StationPathRuleConfig.HardConstraint hard = JSON.parseObject(rule.getHardJson(), StationPathRuleConfig.HardConstraint.class); if (hard != null) { config.setHard(hard); } } } catch (Exception e) { log.warn("解析硬约束配置失败, ruleCode={}", rule.getRuleCode()); } try { if (notBlank(rule.getWaypointJson())) { StationPathRuleConfig.WaypointConstraint waypoint = JSON.parseObject(rule.getWaypointJson(), StationPathRuleConfig.WaypointConstraint.class); if (waypoint != null) { config.setWaypoint(waypoint); } } } catch (Exception e) { log.warn("解析途经点配置失败, ruleCode={}", rule.getRuleCode()); } try { if (notBlank(rule.getSoftJson())) { StationPathRuleConfig.SoftPreference soft = JSON.parseObject(rule.getSoftJson(), StationPathRuleConfig.SoftPreference.class); if (soft != null) { config.setSoft(soft); } } } catch (Exception e) { log.warn("解析软偏好配置失败, ruleCode={}", rule.getRuleCode()); } try { if (notBlank(rule.getFallbackJson())) { StationPathRuleConfig.FallbackPolicy fallback = JSON.parseObject(rule.getFallbackJson(), StationPathRuleConfig.FallbackPolicy.class); if (fallback != null) { config.setFallback(fallback); } } } catch (Exception e) { log.warn("解析规则降级配置失败, ruleCode={}", rule.getRuleCode()); } return config; } private String getSystemConfig(String code, String defaultValue) { try { Object mapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); if (mapObj instanceof Map) { Object value = ((Map) mapObj).get(code); if (value != null) { String text = String.valueOf(value).trim(); if (!text.isEmpty()) { return text; } } } } catch (Exception ignore) { } return defaultValue; } private boolean notBlank(String value) { return value != null && !value.trim().isEmpty(); } private int safeInt(Integer value, int defaultValue) { return value == null ? defaultValue : value; } private static class CacheSnapshot { private boolean loaded = false; private final List profileList = new ArrayList<>(); private final List ruleList = new ArrayList<>(); private final Map profileMap = new HashMap<>(); } }