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<BasStationPathProfile> profiles = basStationPathProfileService.list(new QueryWrapper<BasStationPathProfile>()
|
.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<BasStationPathRule> rules = basStationPathRuleService.list(new QueryWrapper<BasStationPathRule>()
|
.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<BasStationPathRule> ruleList, Integer startStationId, Integer endStationId) {
|
if (ruleList == null || ruleList.isEmpty()) {
|
return null;
|
}
|
|
List<BasStationPathRule> 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<BasStationPathProfile> profileList = new ArrayList<>();
|
private final List<BasStationPathRule> ruleList = new ArrayList<>();
|
private final Map<String, BasStationPathProfile> profileMap = new HashMap<>();
|
}
|
}
|