package com.yunhai.flowcenter.engine; import com.fasterxml.jackson.databind.ObjectMapper; import com.yunhai.platform.openapi.core.TargetSpec; import com.yunhai.platform.openapi.core.YschOpenCall; import com.yunhai.flowcenter.entity.FlwRouter; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 条件路由判断调用客户端(2026-09-24 §4.3):POST {目录解析 host}{basePath}{path}?schId=, * 身份即本应用 appKey(100019)——签名拦截器由 {@link YschOpenCall} 装配链注入,与回调推送同构。 * 寻址:service_key 走服务目录三级解析(targets > services > 枚举兜底); * flw_router.base_path 非空时覆盖目录条目的 context-path(网关形态目录无前缀、实际服务有的场景), * 漏填必 401(规范 §4.2 同款坑)。direct_host 非空的自注册行跳过目录解析直连(2026-10-12 收口)。 * 信封解析复用 SDK({code,msg,data})。 * *
重试纪律:同步 3s×3 次退避重试(500ms/1500ms),业务方义务=幂等纯读轻实现。
* 全部失败抛 {@link JudgeUnavailableException}(由调用方落 outlet=NULL 留痕并挂起)。
*/
@Slf4j
@Service
public class RouteJudgeClient {
/** 判断接口单次超时(毫秒)与重试次数/退避(设计 §4.4:总预算 3s×3 退避重试) */
static final int TIMEOUT_MS = 3_000;
static final int MAX_ATTEMPTS = 3;
private static final long[] BACKOFF_MS = {500L, 1500L};
private final YschOpenCall openCall;
private final ObjectMapper objectMapper;
public RouteJudgeClient(YschOpenCall openCall, ObjectMapper objectMapper) {
this.openCall = openCall;
this.objectMapper = objectMapper;
}
/** 判断请求体(契约冻结:query 带 schId,业务方按 outlets 返回 {outlet};submitterId 2026-10-10 增补,老实现忽略即可) */
public static class JudgeBody {
public final Long schId;
public final Long instanceId;
public final Integer instanceVersion;
public final String bizType;
public final String bizId;
public final Integer submitterId;
public final String nodeId;
public final String nodeName;
public final String routerKey;
public final List
*
* 两分支同设判断专用 3s 超时。
*/
private TargetSpec resolveTarget(FlwRouter router) {
String directHost = router.getDirectHost();
if (directHost != null && !directHost.trim().isEmpty()) {
return withJudgeTimeout(TargetSpec.fromUrl(directHost.trim()));
}
TargetSpec resolved = openCall.resolveTarget(router.getServiceKey());
String basePath = resolved.getBasePath();
if (router.getBasePath() != null && !router.getBasePath().trim().isEmpty()) {
basePath = router.getBasePath().trim();
}
return withJudgeTimeout(TargetSpec.of(resolved.getBaseHost(), basePath));
}
private static TargetSpec withJudgeTimeout(TargetSpec spec) {
spec.setConnectTimeoutMs((long) TIMEOUT_MS);
spec.setReadTimeoutMs((long) TIMEOUT_MS);
return spec;
}
private static void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}