93 lines
3.8 KiB
Java
93 lines
3.8 KiB
Java
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 "{}";
|
||
}
|
||
}
|
||
}
|