feat(env-config): 管理端运行时配置接入-PropertyEnvConfigSource数据源(yh.env-config.source.<target>,target=admin),免登录GET /api/env-config由聚合starter自动装配(旁路CAS),EnvConfigApiIT随机端口匿名200/未配置{}契约(兼平台starter回归哨兵),全量301/301
build / build-push (push) Successful in 1m17s

This commit is contained in:
chenxing
2026-09-08 17:18:17 +08:00
parent 338ffb56dd
commit 12a492fc55
3 changed files with 93 additions and 0 deletions
@@ -0,0 +1,35 @@
package com.yunhai.cms.config;
import com.yunhai.platform.envconfig.EnvConfigSource;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
/**
* env-config 数据源实现:从 Spring Environment 读取管理端目标的运行时配置 JSON。
* 键 = {@code yh.env-config.source.<target>}(管理端 SPA 部署于 /{contextPath}/admin/,target 即 admin),
* 三处可配(优先级由 Environment 决定):
* 1) 启动参数 --yh.env-config.source.admin='{...}'
* 2) 环境变量 YH_ENV_CONFIG_SOURCE_ADMIN(projman .env / Gitea Secrets 注入,宽松绑定)
* 3) application.yml 内 yh.env-config.source.admin 键
* 未配置返回 null,平台 EnvConfigService 归一为 "{}" 返回(前端走同域推导兜底,接口恒 200)。
*
* <p>存在本 bean 即满足 {@code yh-env-config-spring-boot-starter} 的自动装配条件(聚合 starter 已携带该模块),
* 自动注册免登录 {@code GET /api/env-config?target=admin}:EnvConfigSsoBypassFilter(HIGHEST+3)
* 抢在 yh-auth 的 CAS 过滤链(yh.sso.url-pattern=/api/*,HIGHEST+4 起)之前 forward 旁路,SPA 登录前可拉。
*/
@Service
public class PropertyEnvConfigSource implements EnvConfigSource {
private static final String KEY_PREFIX = "yh.env-config.source.";
private final Environment environment;
public PropertyEnvConfigSource(Environment environment) {
this.environment = environment;
}
@Override
public String get(String target) {
return environment.getProperty(KEY_PREFIX + target);
}
}
+9
View File
@@ -66,6 +66,15 @@ yh:
base-path: /userbase
app-key: 100005
app-secret: 2c7885de594742cc840b8762910b2d2e
# env-config 管理端运行时配置(免登录 GET /api/env-config?target=admin,数据源见
# config/PropertyEnvConfigSource:聚合 starter 自动装配,有 bean 即注册端点)。
# 本地无需配值(前端 dev 走 .env.dev);线上值经环境变量 YH_ENV_CONFIG_SOURCE_ADMIN
# 或同名键 yh.env-config.source.admin 注入(projman 配置管理维护),未配置时端点返回 {},
# 前端走同域推导兜底(serviceHost/baseHost 正确,ssoHost 为空登录跳转不可用)。
# 如需本地联调可打开:
# env-config:
# source:
# admin: '{"baseHost":"http://base.yunhaixingtong.com","ssoHost":"http://sso.yunhaixingtong.com","serviceHost":"/yschcms","isMock":false}'
# ===== CMS 专属配置 =====
cms:
@@ -0,0 +1,49 @@
package com.yunhai.cms.config;
import com.yunhai.cms.support.CmsIntegrationTestBase;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
/**
* env-config 端点契约(UserHome 同款机制,管理端 SPA 运行时配置来源)。
* 必须起真实端口:端点靠平台 EnvConfigSsoBypassFilter(HIGHEST+3)forward 旁路 CAS 过滤链
* (yh.sso.url-pattern=/api/*),MockMvc 的模拟 RequestDispatcher 不执行 forward 派发,
* 只有真实 Tomcat 上才能验证「匿名 GET 直达 Controller 而非 302 登录页」。
* 本用例同时是平台 starter 的回归哨兵:envConfigSsoBypassFilter 的
* {@code @ConditionalOnMissingBean} 曾因 FilterRegistrationBean 泛型擦除误判跳过(已修)。
*
* <p>数据源 PropertyEnvConfigSource 读键 yh.env-config.source.&lt;target&gt;,本类注入 admin 值
* (生产由 projman 维护);未配置的 target 由平台 EnvConfigService 归一为 "{}" 返回
* (管理端部署目录名 admin,键名跟随 URL 第 2 段推导)。
*/
@DisplayName("EnvConfigApi - /api/env-config 免登录运行时配置端点(真实端口)")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties =
"yh.env-config.source.admin={\"ssoHost\":\"http://sso.yunhaixingtong.com\",\"serviceHost\":\"/yschcms\"}")
class EnvConfigApiIT extends CmsIntegrationTestBase {
@Autowired
private TestRestTemplate rest;
@Test
@DisplayName("target=admin 已配置:旁路 CAS 匿名 200,返回注入的 JSON 原文")
void configuredTarget_bypassesCasAndReturnsInjectedJson() {
ResponseEntity<String> resp = rest.getForEntity("/api/env-config?target=admin", String.class);
assertThat(resp.getStatusCode().value()).isEqualTo(200);
assertThat(resp.getBody()).isEqualTo(
"{\"ssoHost\":\"http://sso.yunhaixingtong.com\",\"serviceHost\":\"/yschcms\"}");
}
@Test
@DisplayName("target 未配置(如 front):仍免登录 200,归一返回 {}")
void unconfiguredTarget_returnsEmptyObjectAnonymously() {
ResponseEntity<String> resp = rest.getForEntity("/api/env-config?target=front", String.class);
assertThat(resp.getStatusCode().value()).isEqualTo(200);
assertThat(resp.getBody()).isEqualTo("{}");
}
}