Merge remote-tracking branch 'base-version/test' into dev

This commit is contained in:
chenbowen
2026-01-22 17:22:02 +08:00
7 changed files with 312 additions and 31 deletions

View File

@@ -40,4 +40,23 @@ public class ApiDefinitionAggregate {
return credentialBindings == null ? Collections.emptyList() : credentialBindings;
}
/**
* 获取最后一个 HTTP 步骤的 IDorder 最大的 HTTP 步骤)
* 用于判断是否需要设置 responseBody
*/
public Long getLastHttpStepId() {
return getSteps().stream()
.filter(stepDef -> "HTTP".equalsIgnoreCase(stepDef.getStep().getType()))
.max((s1, s2) -> {
Integer order1 = s1.getStep().getStepOrder();
Integer order2 = s2.getStep().getStepOrder();
if (order1 == null && order2 == null) return 0;
if (order1 == null) return -1;
if (order2 == null) return 1;
return order1.compareTo(order2);
})
.map(stepDef -> stepDef.getStep().getId())
.orElse(null);
}
}

View File

@@ -96,18 +96,50 @@ public final class GatewayExpressionHelper {
}
private static void applyRequestMutations(ApiInvocationContext context, Map<?, ?> map) {
Object body = firstNonNull(map.get("requestBody"), map.get("body"));
if (body != null) {
context.setRequestBody(body);
}
Object headers = map.get("requestHeaders");
if (headers instanceof Map<?, ?> headerMap) {
context.getRequestHeaders().putAll(toStringMap(headerMap));
}
// 处理查询参数
Object query = firstNonNull(map.get("requestQuery"), map.get("requestQueryParams"), map.get("query"));
if (query instanceof Map<?, ?> queryMap) {
context.getRequestQueryParams().putAll(toObjectMap(queryMap));
}
// 处理请求头
Object headers = map.get("requestHeaders");
if (headers instanceof Map<?, ?> headerMap) {
context.getRequestHeaders().putAll(toStringMap(headerMap));
}
// 处理 body - 使用和 HTTP 步骤一致的逻辑
// 检查是否显式指定了 body 字段
boolean explicitBody = containsKeyIgnoreCase(map, "body", "payload", "requestBody");
if (explicitBody) {
// 如果显式指定了 body提取并与原有 body 合并
Object body = firstNonNull(map.get("requestBody"), map.get("body"), map.get("payload"));
Object existingBody = context.getRequestBody();
if (body instanceof Map && existingBody instanceof Map) {
// 两者都是 Map合并以保留未映射的字段
Map<String, Object> mergedBody = new LinkedHashMap<>(toObjectMap((Map<?, ?>) existingBody));
mergedBody.putAll(toObjectMap((Map<?, ?>) body));
context.setRequestBody(mergedBody);
} else if (body != null) {
// 直接替换
context.setRequestBody(body);
}
} else if (query == null) {
// 如果没有显式 body 字段,也没有 query 字段
// 说明整个 map 是新的 body与原有 body 合并
Object existingBody = context.getRequestBody();
if (existingBody instanceof Map) {
Map<String, Object> mergedBody = new LinkedHashMap<>(toObjectMap((Map<?, ?>) existingBody));
mergedBody.putAll(toObjectMap(map));
context.setRequestBody(mergedBody);
} else {
// 原有 body 不是 Map直接替换
context.setRequestBody(toObjectMap(map));
}
}
// 如果有 query 字段但没有显式 body 字段,保留原有 body不做任何操作
}
private static void applyResponseMutations(ApiInvocationContext context, Map<?, ?> map) {
@@ -165,4 +197,18 @@ public final class GatewayExpressionHelper {
}
return null;
}
private static boolean containsKeyIgnoreCase(Map<?, ?> source, String... keys) {
if (source == null || source.isEmpty()) {
return false;
}
for (String key : keys) {
for (Object entryKey : source.keySet()) {
if (key.equalsIgnoreCase(String.valueOf(entryKey))) {
return true;
}
}
}
return false;
}
}

View File

@@ -43,6 +43,13 @@ public class JsonataExpressionEvaluator implements ExpressionEvaluator {
environment.setVariable("vars", objectMapper.valueToTree(context.getVariables() == null ? java.util.Collections.emptyMap() : context.getVariables()));
environment.setVariable("headers", objectMapper.valueToTree(context.getHeaders() == null ? java.util.Collections.emptyMap() : context.getHeaders()));
environment.setVariable("ctx", objectMapper.valueToTree(context.getInvocation()));
// 直接绑定查询参数,方便访问
if (context.getInvocation() != null && context.getInvocation().getRequestQueryParams() != null) {
environment.setVariable("query", objectMapper.valueToTree(context.getInvocation().getRequestQueryParams()));
} else {
environment.setVariable("query", objectMapper.valueToTree(java.util.Collections.emptyMap()));
}
} catch (EvaluateRuntimeException e) {
throw ServiceExceptionUtil.exception(API_JSONATA_BIND_FAILED);
}

View File

@@ -18,10 +18,19 @@ import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.Map;
import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_STEP_START_EXECUTION_FAILED;
/**
* Handler for START orchestration nodes.
*
* <p>增强功能START 节点的映射结果会存入 context.requestBody
* 使得后续 HTTP 步骤可以自动继承这些公共参数,避免重复配置。
*
* <p>注意:不将参数存入 context.variables避免与后续步骤的响应结果冲突。
* variables 应该保留给步骤执行结果使用。
*/
@Component
@RequiredArgsConstructor
@@ -40,11 +49,22 @@ public class StartStepHandler implements ApiStepHandler {
Instant start = Instant.now();
Object snapshotBefore = GatewayExpressionHelper.snapshotRequest(payload);
try {
ExpressionSpec spec = ExpressionSpecParser.parse(stepDefinition.getStep().getRequestMappingExpr(), ExpressionTypeEnum.JSON);
ExpressionSpec spec = ExpressionSpecParser.parse(
stepDefinition.getStep().getRequestMappingExpr(),
ExpressionTypeEnum.JSON
);
if (spec != null) {
Object evaluated = expressionExecutor.evaluate(spec, payload, payload.getRequestBody(), headers);
// 将查询参数合并到 payload 中,使表达式可以通过 $.query.k 访问
// 这样和 HTTP 步骤的映射逻辑保持一致
Object enrichedPayload = enrichPayloadWithQuery(payload.getRequestBody(), payload.getRequestQueryParams());
Object evaluated = expressionExecutor.evaluate(spec, payload, enrichedPayload, headers);
// 应用上下文变更(包括 requestBody、requestQuery 等)
// 支持字段级映射,将外部字段名转换为内部字段名
GatewayExpressionHelper.applyContextMutations(payload, evaluated, true, false);
}
payload.addStepResult(ApiStepResult.builder()
.stepId(stepDefinition.getStep().getId())
.stepType(stepDefinition.getStep().getType())
@@ -70,4 +90,26 @@ public class StartStepHandler implements ApiStepHandler {
return payload;
};
}
/**
* 将查询参数合并到 payload 中,使表达式可以通过 $.query.k 访问
* 这样可以避免 JSONata 环境变量访问的限制
*/
private Object enrichPayloadWithQuery(Object originalPayload, Map<String, Object> queryParams) {
if (queryParams == null || queryParams.isEmpty()) {
return originalPayload;
}
Map<String, Object> enriched = new LinkedHashMap<>();
// 如果原始 payload 是 Map复制所有字段
if (originalPayload instanceof Map) {
enriched.putAll((Map<String, Object>) originalPayload);
}
// 添加查询参数到 query 字段
enriched.put("query", new LinkedHashMap<>(queryParams));
return enriched;
}
}