From f816fc390e35b98f0e3fd5fca171ec98a3fdfa95 Mon Sep 17 00:00:00 2001 From: chenxing Date: Thu, 22 Oct 2026 17:14:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(openapi):=20FlowCenterClient=20=E5=88=87?= =?UTF-8?q?=20SDK=20appCode=20=E5=AF=BB=E5=9D=80=EF=BC=88=E6=98=BE?= =?UTF-8?q?=E5=BC=8F>legacy>fallback=EF=BC=89=EF=BC=8Capplication.yml=20?= =?UTF-8?q?=E5=87=BA=E5=8E=82=20base-host=20=E9=BB=98=E8=AE=A4=E5=86=85?= =?UTF-8?q?=E9=83=A8=E7=BD=91=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/FlowCenterClient.java | 21 ++++---- src/main/resources/application.yml | 15 ++---- .../impl/FlowCenterClientRoutingTest.java | 52 +++++++++++++++++++ 3 files changed, 67 insertions(+), 21 deletions(-) create mode 100644 src/test/java/com/yunhai/svchall/service/impl/FlowCenterClientRoutingTest.java diff --git a/src/main/java/com/yunhai/svchall/service/impl/FlowCenterClient.java b/src/main/java/com/yunhai/svchall/service/impl/FlowCenterClient.java index 6b13750..2a9b671 100644 --- a/src/main/java/com/yunhai/svchall/service/impl/FlowCenterClient.java +++ b/src/main/java/com/yunhai/svchall/service/impl/FlowCenterClient.java @@ -13,7 +13,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import java.net.URI; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -26,9 +25,9 @@ import java.util.Map; * *

主路径走 SDK 类型化门面 {@code YschFlowOpenApi}(服务目录 key=flow-center: * yh.call.targets > yh.open.services > 枚举 base-host 兜底,《子应用配置优化方案》§4.2 - * 零专属地址配置);目录未显式配置时回落 legacy 键 {@code svchall.flow.base-url}(ad-hoc + * 零专属地址配置);目录未显式配置且提供 legacy 时回落 legacy 键 {@code svchall.flow.base-url}(ad-hoc * TargetSpec,可配带/不带 context-path 两种形态,构造时拆成 host+basePath,迁移期兼容)。 - * 两条路径的 per-target base-path 剥离语义一致(同 {@link CmsSiteDirectoryImpl})。 + * 无 legacy 时使用 SDK 可解析目标。两条路径的 per-target base-path 剥离语义一致(同 {@link CmsSiteDirectoryImpl})。 */ @Slf4j @Component @@ -38,7 +37,7 @@ public class FlowCenterClient implements FlowClient { private final ObjectMapper objectMapper; private final boolean enabled; private final TargetSpec flowSpec; - /** 服务目录显式配置了 flow-center(targets/services)则主路径走门面命名目标 */ + /** 显式目录或无 legacy 时 SDK 可解析 flow-center,则走门面命名目标 */ private final boolean directoryResolved; public FlowCenterClient(YschOpenCall openCall, ObjectMapper objectMapper, @@ -48,8 +47,11 @@ public class FlowCenterClient implements FlowClient { this.objectMapper = objectMapper; this.enabled = enabled; String base = trimTrailing(baseUrl == null ? "" : baseUrl.trim()); - this.directoryResolved = openCall != null && openCall.hasTarget( - com.yunhai.platform.openapi.core.PlatformService.FLOW_CENTER.key()); + // 保持 explicit > legacy;没有 legacy 时允许 SDK 枚举/标准网关解析。 + this.directoryResolved = openCall != null && (openCall.hasTarget( + com.yunhai.platform.openapi.core.PlatformService.FLOW_CENTER.key()) + || (base.isEmpty() && openCall.canResolve( + com.yunhai.platform.openapi.core.PlatformService.FLOW_CENTER.key()))); if (directoryResolved) { com.yunhai.platform.openapi.flow.YschFlowOpenApi.setCall(openCall); } @@ -58,12 +60,9 @@ public class FlowCenterClient implements FlowClient { // enabled=false(显式未接入)时不校验——地址本就不需要。 throw new IllegalStateException("流程中心调用地址未配置:请配置服务目录 yh.open.services." + com.yunhai.platform.openapi.core.PlatformService.FLOW_CENTER.key() - + "(推荐,容器直连完整 URL)或回落键 svchall.flow.base-url"); + + ",或平台网关 yh.open.base-host,或回落键 svchall.flow.base-url"); } - URI uri = URI.create(base); - String host = uri.getScheme() + "://" + uri.getRawAuthority(); - String path = uri.getRawPath() == null ? "" : uri.getRawPath(); - this.flowSpec = TargetSpec.of(host, path); + this.flowSpec = !enabled || directoryResolved || base.isEmpty() ? null : TargetSpec.fromUrl(base); } @Override diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 244b9f7..295fabb 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -86,23 +86,18 @@ yh: "100019": "flow-center-dev-secret-100019" open: # 身份密钥唯一来源 = yh.app-auth.app-key/app-secret(本文件 app-auth 段),open.* 副本已删 - base-host: http://base.yunhaixingtong.com - # 服务目录(子应用配置优化方案 §4.2):跨服务出站唯一地址来源,命名目标走 SDK 门面 / - # YschOpenCall。cms 未晋升 PlatformService 枚举(按名直调);flow-center 已入枚举。 - # 完整 URL 拆 host+ctx,per-target base-path 与提供方 context-path 对齐 → 签名天然一致。 - # 生产改容器直连:flow-center=http://flow-center-service:10008/flow-center、 - # cms=http://yschcms-service:10005/yschcms。 - # (base-path /userbase 已删 2026-10-05,对齐 Notice 形态:该键代码默认值即 /userbase,删键无行为变化, - # 回调前缀推导读的是 yh.flow-callback.target/base-path 三层回落,与本键无关) + base-host: ${YH_OPEN_BASE_HOST:http://open-gateway-service:10013} + # 标准后端按 appCode 走统一内部 open-gateway,无需逐应用 URL。 + # 本地直连只在运行时显式覆盖 yh.open.services.{appCode};不要写入出厂 jar。 services: # 生产零配置:不配则枚举兜底 = base-host(网关)+/flow-center;此处显式覆盖仅本地开发用 # (Notice 同款)。生产如仍被平台层注入容器直连值,属平台配置项(projman yh.open.services 段)。 - flow-center: http://localhost:8085 + # flow-center: http://localhost:8085 cms: http://base.yunhaixingtong.com:10005/yschcms # form-center 刻意不写本地默认值(TASK_1 交付说明第 2 条的同一取舍):本文件里写 # http://localhost:80xx 会被打进 jar,部署实例就去连容器内的 localhost(2026-09-18 事故)。 # 本机跨应用联调请运行时注入:YH_OPEN_SERVICES_FORMCENTER=http://localhost:; - # 生产不注入即走目录第 3 级兜底(base-host 网关 + PlatformService.FORM_CENTER 的 ctx)。 + # 不配条目则走标准 appCode fallback:base-host 网关 + /form-center(无需枚举)。 # form-center: ${YH_OPEN_SERVICES_FORMCENTER:} # 流程中心回调接收端 starter(2026-09-20 迁移;2026-10-05 收敛 Notice 形态): diff --git a/src/test/java/com/yunhai/svchall/service/impl/FlowCenterClientRoutingTest.java b/src/test/java/com/yunhai/svchall/service/impl/FlowCenterClientRoutingTest.java new file mode 100644 index 0000000..eeee89f --- /dev/null +++ b/src/test/java/com/yunhai/svchall/service/impl/FlowCenterClientRoutingTest.java @@ -0,0 +1,52 @@ +package com.yunhai.svchall.service.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.yunhai.platform.openapi.core.*; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; +import static org.junit.jupiter.api.Assertions.*; + +class FlowCenterClientRoutingTest { + private YschOpenCall call(String gateway) { + OpenApiProperties open = new OpenApiProperties(); + open.setBaseHost(gateway); + return new YschOpenCall(new YschCallProperties(), open, new ObjectMapper(), null); + } + + @Test + void enabledWithoutExplicitTargetUsesSdk() { + YschOpenCall call = call("http://gateway:10013"); + assertFalse(call.hasTarget("flow-center")); + FlowCenterClient client = new FlowCenterClient(call, new ObjectMapper(), true, ""); + assertTrue(client.enabled()); + assertEquals(true, ReflectionTestUtils.getField(client, "directoryResolved")); + assertNull(ReflectionTestUtils.getField(client, "flowSpec")); + assertEquals("http://gateway:10013/form-center", call.resolveTarget("form-center").resolveBaseUrl()); + assertDoesNotThrow(() -> new FormCenterCatalogClient(call)); + assertDoesNotThrow(() -> new FcSubmissionClient(call)); + assertDoesNotThrow(() -> new FormSyncClient(call)); + } + + @Test + void explicitThenLegacyThenSdkPriority() { + YschCallProperties props = new YschCallProperties(); + props.getTargets().put("flow-center", TargetSpec.of("http://explicit", "/flow-center")); + YschOpenCall explicit = new YschOpenCall(props, new ObjectMapper(), null); + FlowCenterClient client = new FlowCenterClient(explicit, new ObjectMapper(), true, "http://legacy/ctx"); + assertEquals(true, ReflectionTestUtils.getField(client, "directoryResolved")); + client = new FlowCenterClient(call("http://gateway"), new ObjectMapper(), true, "http://legacy/ctx"); + assertEquals(false, ReflectionTestUtils.getField(client, "directoryResolved")); + TargetSpec spec = (TargetSpec) ReflectionTestUtils.getField(client, "flowSpec"); + assertEquals("http://legacy/ctx", spec.resolveBaseUrl()); + } + + @Test + void enabledWithoutResolvableAddressFailsButDisabledDoesNot() { + assertThrows(IllegalStateException.class, + () -> new FlowCenterClient(call(null), new ObjectMapper(), true, "")); + assertThrows(IllegalStateException.class, + () -> new FlowCenterClient(call("http://gateway/open-gateway"), new ObjectMapper(), true, "")); + assertFalse(new FlowCenterClient(call(null), new ObjectMapper(), false, "").enabled()); + assertFalse(new FlowCenterClient(call(null), new ObjectMapper(), false, "unused-legacy-value").enabled()); + } +}