package com.zy.asrs.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.core.exception.CoolException; import com.zy.asrs.service.RuntimeConfigService; import com.zy.system.entity.Config; import com.zy.system.service.ConfigService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.Set; @Service("runtimeConfigService") public class RuntimeConfigServiceImpl implements RuntimeConfigService { private static final LinkedHashMap RUNTIME_CONFIG_RULE_MAP = buildRuntimeConfigRuleMap(); @Autowired private ConfigService configService; @Override @Transactional public Map updateRuntimeConfig(Map configMap) { if (configMap == null || configMap.isEmpty()) { throw new CoolException("参数不能为空"); } LinkedHashMap normalizedMap = new LinkedHashMap<>(); for (Map.Entry entry : configMap.entrySet()) { String code = entry.getKey(); RuntimeConfigRule rule = RUNTIME_CONFIG_RULE_MAP.get(code); if (rule == null) { throw new CoolException("不支持的运行参数: " + code); } Config config = getConfig(code); if (config == null) { throw new CoolException("运行参数不存在: " + code); } if (!Short.valueOf((short) 1).equals(config.getStatus())) { throw new CoolException("运行参数已禁用: " + code); } normalizedMap.put(code, rule.normalize(code, entry.getValue())); } LinkedHashMap changedMap = saveChangedConfig(normalizedMap); if (!changedMap.isEmpty()) { configService.refreshSystemConfigCache(); } LinkedHashMap result = new LinkedHashMap<>(); result.put("changed", changedMap); result.put("current", buildRuntimeConfigSnapshot(normalizedMap.keySet())); return result; } private LinkedHashMap saveChangedConfig(LinkedHashMap normalizedMap) { LinkedHashMap changedMap = new LinkedHashMap<>(); for (Map.Entry entry : normalizedMap.entrySet()) { Config config = getConfig(entry.getKey()); if (config == null) { throw new CoolException("运行参数不存在: " + entry.getKey()); } if (Objects.equals(config.getValue(), entry.getValue())) { continue; } config.setValue(entry.getValue()); if (!configService.updateById(config)) { throw new CoolException("保存失败: " + entry.getKey()); } changedMap.put(entry.getKey(), entry.getValue()); } return changedMap; } private LinkedHashMap buildRuntimeConfigSnapshot(Set codeSet) { LinkedHashMap result = new LinkedHashMap<>(); for (String code : codeSet) { Config config = getConfig(code); if (config != null) { result.put(code, config.getValue()); } } return result; } private Config getConfig(String code) { return configService.getOne(new QueryWrapper().eq("code", code).last("limit 1")); } private static LinkedHashMap buildRuntimeConfigRuleMap() { LinkedHashMap ruleMap = new LinkedHashMap<>(); putIntRule(ruleMap, "conveyorStationTaskLimit", 1, 1000); putIntRule(ruleMap, "stationCommandSendLength", 1, 200); putRatioRule(ruleMap, "stationCommandSegmentAdvanceRatio"); putIntRule(ruleMap, "stationCommandConfigRefreshSeconds", 5, 300); putIntRule(ruleMap, "stationV5SegmentExecutorPoolSize", 16, 512); putIntRule(ruleMap, "stationV5SegmentExecutorQueueCapacity", 64, 4096); putIntRule(ruleMap, "crnOutBatchRunningLimit", 0, 1000); putBooleanRule(ruleMap, "crnOutRequireStationOutEnable"); putIntRule(ruleMap, "deviceCommandAutoRollbackLimit", 1, 100); putBooleanRule(ruleMap, "checkDeepLocOutTaskBlockReport"); return ruleMap; } private static void putIntRule(LinkedHashMap ruleMap, String code, int min, int max) { ruleMap.put(code, new RuntimeConfigRule(RuntimeConfigValueType.INT, min, max)); } private static void putRatioRule(LinkedHashMap ruleMap, String code) { ruleMap.put(code, new RuntimeConfigRule(RuntimeConfigValueType.RATIO, 0, 100)); } private static void putBooleanRule(LinkedHashMap ruleMap, String code) { ruleMap.put(code, new RuntimeConfigRule(RuntimeConfigValueType.BOOLEAN, 0, 0)); } private enum RuntimeConfigValueType { INT, RATIO, BOOLEAN } private static class RuntimeConfigRule { private final RuntimeConfigValueType valueType; private final int min; private final int max; private RuntimeConfigRule(RuntimeConfigValueType valueType, int min, int max) { this.valueType = valueType; this.min = min; this.max = max; } private String normalize(String code, Object rawValue) { if (rawValue == null) { throw new CoolException(code + " 参数不能为空"); } String value = String.valueOf(rawValue).trim(); if (value.isEmpty()) { throw new CoolException(code + " 参数不能为空"); } if (RuntimeConfigValueType.BOOLEAN.equals(valueType)) { return normalizeBoolean(code, value); } if (RuntimeConfigValueType.RATIO.equals(valueType)) { return normalizeRatio(code, value); } return normalizeInt(code, value); } private String normalizeBoolean(String code, String value) { if ("Y".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value) || "1".equals(value)) { return "Y"; } if ("N".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value) || "0".equals(value)) { return "N"; } throw new CoolException(code + " 仅支持 Y/N、true/false 或 1/0"); } private String normalizeRatio(String code, String value) { String text = value; boolean percentText = text.endsWith("%"); if (percentText) { text = text.substring(0, text.length() - 1).trim(); } double parsed; try { parsed = Double.parseDouble(text); } catch (Exception e) { throw new CoolException(code + " 必须为 0~1 比例或 0~100 百分比"); } if (parsed < 0d || parsed > 100d) { throw new CoolException(code + " 必须为 0~1 比例或 0~100 百分比"); } if (percentText || parsed > 1d) { parsed = parsed / 100d; } return stripTrailingZero(parsed); } private String normalizeInt(String code, String value) { int parsed; try { parsed = Integer.parseInt(value); } catch (Exception e) { throw new CoolException(code + " 必须为整数"); } if (parsed < min || parsed > max) { throw new CoolException(code + " 必须在 " + min + "~" + max + " 范围内"); } return String.valueOf(parsed); } private String stripTrailingZero(double value) { String text = java.math.BigDecimal.valueOf(value).stripTrailingZeros().toPlainString(); return "-0".equals(text) ? "0" : text; } } }