This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
package com.yunhai.userbase.service.impl;
|
||||
|
||||
import com.alibaba.cloud.nacos.NacosConfigManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 前端运行时配置读取(方案:Projman/前端动态配置方案-env-config接入落地.md)。
|
||||
* <p>
|
||||
* projman 配置管理中的 env-config-{前端目标名}.json 会双写到 Nacos
|
||||
* (dataId = front-{应用名}-{目标名},group = FRONT_GROUP,type = json),
|
||||
* 本服务按前端目标名读取并原样返回 JSON 串,经 {@code GET /api/env-config?front=xxx} 提供给前端 SPA,
|
||||
* 替代构建期写死的 VITE_* 环境变量与 config/index.js 静态文件。
|
||||
* <p>
|
||||
* 容错口径:读不到(本地未开 Nacos / dataId 不存在 / 超时)一律返回 {@code "{}"},
|
||||
* 前端拿空对象后走 import.meta.env 构建期兜底,不影响启动。
|
||||
*/
|
||||
@Service
|
||||
public class EnvConfigService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(EnvConfigService.class);
|
||||
|
||||
/** 前端目标名白名单:projman 前端目标创建时即限制为简单标识符,此处同口径校验,防 dataId 拼接注入 */
|
||||
private static final String FRONT_NAME_PATTERN = "^[A-Za-z0-9_-]{1,64}$";
|
||||
|
||||
/** Nacos dataId 前缀:front-{应用名}-,与 projman AppConfigFileService 双写规则一致 */
|
||||
@Value("${yh.env-config.data-id-prefix:front-userbase-}")
|
||||
private String dataIdPrefix;
|
||||
|
||||
/** Nacos group,与 projman 双写侧 FRONT_GROUP 常量一致 */
|
||||
@Value("${yh.env-config.group:FRONT_GROUP}")
|
||||
private String group;
|
||||
|
||||
/** 结果缓存 TTL:前端 SPA 每次打开页面都会拉一次,缓存削峰避免轮询打穿 Nacos */
|
||||
private static final long CACHE_TTL_MS = 30_000L;
|
||||
|
||||
private final Map<String, CacheEntry> cache = new ConcurrentHashMap<>();
|
||||
|
||||
private static class CacheEntry {
|
||||
final String content;
|
||||
final long ts;
|
||||
CacheEntry(String content, long ts) {
|
||||
this.content = content;
|
||||
this.ts = ts;
|
||||
}
|
||||
}
|
||||
|
||||
/** 本地 profile 未启用 Nacos 时为 null(NACOS_CONFIG_ENABLED=false 时不注册该 bean) */
|
||||
@Autowired(required = false)
|
||||
private NacosConfigManager nacosConfigManager;
|
||||
|
||||
/** 读取前端目标配置 JSON;任何失败返回 "{}"(前端走构建期兜底) */
|
||||
public String get(String front) {
|
||||
if (front == null || !front.trim().matches(FRONT_NAME_PATTERN)) {
|
||||
return "{}";
|
||||
}
|
||||
String key = front.trim();
|
||||
CacheEntry cached = cache.get(key);
|
||||
long now = System.currentTimeMillis();
|
||||
if (cached != null && now - cached.ts < CACHE_TTL_MS) {
|
||||
return cached.content;
|
||||
}
|
||||
String content = readFromNacos(key);
|
||||
cache.put(key, new CacheEntry(content, now));
|
||||
return content;
|
||||
}
|
||||
|
||||
private String readFromNacos(String front) {
|
||||
if (nacosConfigManager == null) {
|
||||
// 本地开发未接 Nacos:无运行时配置,前端走 import.meta.env 兜底
|
||||
return "{}";
|
||||
}
|
||||
try {
|
||||
String content = nacosConfigManager.getConfigService()
|
||||
.getConfig(dataIdPrefix + front, group, 3000);
|
||||
if (content == null || content.trim().isEmpty()) {
|
||||
return "{}";
|
||||
}
|
||||
return content.trim();
|
||||
} catch (Exception e) {
|
||||
// Nacos 不可达时 getConfig 自身有本地快照兜底;仍失败则返回空对象,不抛错不影响前端启动
|
||||
log.warn("[env-config] 读取 Nacos 配置失败 dataId={} group={}: {}", dataIdPrefix + front, group, e.getMessage());
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user