增加构建通知
This commit is contained in:
parent
97f6e8c8e2
commit
e409101fbf
@ -0,0 +1,243 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.delegate;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.qqchen.deploy.backend.framework.utils.JsonUtils;
|
||||||
|
import com.qqchen.deploy.backend.workflow.dto.inputmapping.HttpRequestInputMapping;
|
||||||
|
import com.qqchen.deploy.backend.workflow.dto.outputs.HttpRequestOutputs;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
|
import org.springframework.http.*;
|
||||||
|
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.client.HttpClientErrorException;
|
||||||
|
import org.springframework.web.client.HttpServerErrorException;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP请求节点委托
|
||||||
|
*
|
||||||
|
* @author qqchen
|
||||||
|
* @since 2025-11-20
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component("httpRequestDelegate")
|
||||||
|
public class HttpRequestNodeDelegate extends BaseNodeDelegate<HttpRequestInputMapping, HttpRequestOutputs> {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void executeInternal(DelegateExecution execution,
|
||||||
|
Map<String, Object> configs,
|
||||||
|
HttpRequestInputMapping input) {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
try {
|
||||||
|
logInfo(String.format("开始HTTP请求: %s %s (超时: %dms)",
|
||||||
|
input.getMethod(), input.getUrl(), input.getTimeout()));
|
||||||
|
|
||||||
|
// 1. 创建专属的RestTemplate,使用节点配置的超时时间
|
||||||
|
RestTemplate restTemplate = createRestTemplate(input.getTimeout());
|
||||||
|
|
||||||
|
// 2. 构建请求
|
||||||
|
HttpHeaders headers = buildHeaders(input.getHeadersAsMap());
|
||||||
|
HttpEntity<?> entity = new HttpEntity<>(input.getBody(), headers);
|
||||||
|
|
||||||
|
// 3. 构建完整URL(带查询参数)
|
||||||
|
String fullUrl = buildUrl(input.getUrl(), input.getQueryParamsAsMap());
|
||||||
|
|
||||||
|
logInfo(String.format("请求URL: %s", fullUrl));
|
||||||
|
if (input.getBody() != null) {
|
||||||
|
logInfo(String.format("请求体: %s", JsonUtils.toJson(input.getBody())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 发送HTTP请求
|
||||||
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
|
fullUrl,
|
||||||
|
input.getMethod(),
|
||||||
|
entity,
|
||||||
|
String.class
|
||||||
|
);
|
||||||
|
|
||||||
|
// 4. 解析响应
|
||||||
|
long responseTime = System.currentTimeMillis() - startTime;
|
||||||
|
parseResponse(response, responseTime, input.getResponseBodyType());
|
||||||
|
|
||||||
|
logInfo(String.format("HTTP请求成功: 状态码=%d, 耗时=%dms, 响应大小=%d bytes",
|
||||||
|
output.getStatusCode(), output.getResponseTime(), output.getResponseSize()));
|
||||||
|
|
||||||
|
} catch (HttpClientErrorException | HttpServerErrorException e) {
|
||||||
|
// HTTP错误(4xx, 5xx)
|
||||||
|
handleHttpError(e, System.currentTimeMillis() - startTime);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 其他错误(网络超时、连接失败等)
|
||||||
|
handleGeneralError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析响应
|
||||||
|
*/
|
||||||
|
private void parseResponse(ResponseEntity<String> response, long responseTime,
|
||||||
|
HttpRequestInputMapping.ResponseBodyType type) {
|
||||||
|
output.setStatusCode(response.getStatusCode().value());
|
||||||
|
output.setResponseTime(responseTime);
|
||||||
|
output.setIsSuccess(response.getStatusCode().is2xxSuccessful());
|
||||||
|
|
||||||
|
// 解析响应头
|
||||||
|
Map<String, String> headerMap = new HashMap<>();
|
||||||
|
response.getHeaders().forEach((k, v) ->
|
||||||
|
headerMap.put(k, String.join(", ", v))
|
||||||
|
);
|
||||||
|
output.setResponseHeaders(headerMap);
|
||||||
|
|
||||||
|
// 解析响应体
|
||||||
|
String body = response.getBody();
|
||||||
|
if (body != null) {
|
||||||
|
output.setResponseSize((long) body.length());
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case JSON -> {
|
||||||
|
try {
|
||||||
|
// 尝试解析为JSON对象或数组
|
||||||
|
Object jsonBody = parseJsonBody(body);
|
||||||
|
output.setResponseBody(jsonBody);
|
||||||
|
logInfo(String.format("响应体(JSON): %s", body.length() > 500 ?
|
||||||
|
body.substring(0, 500) + "..." : body));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("JSON解析失败,返回原始文本: {}", e.getMessage());
|
||||||
|
output.setResponseBody(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case TEXT -> {
|
||||||
|
output.setResponseBody(body);
|
||||||
|
logInfo(String.format("响应体(TEXT): %s", body.length() > 200 ?
|
||||||
|
body.substring(0, 200) + "..." : body));
|
||||||
|
}
|
||||||
|
case XML -> output.setResponseBody(body); // 简化实现,直接返回文本
|
||||||
|
case BINARY -> output.setResponseBody(body.getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析JSON响应体
|
||||||
|
*/
|
||||||
|
private Object parseJsonBody(String body) throws Exception {
|
||||||
|
// 尝试解析为Map或List
|
||||||
|
if (body.trim().startsWith("{")) {
|
||||||
|
return objectMapper.readValue(body, Map.class);
|
||||||
|
} else if (body.trim().startsWith("[")) {
|
||||||
|
return objectMapper.readValue(body, List.class);
|
||||||
|
} else {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理HTTP错误
|
||||||
|
*/
|
||||||
|
private void handleHttpError(Exception e, long responseTime) {
|
||||||
|
HttpStatusCode statusCode = null;
|
||||||
|
String responseBody = null;
|
||||||
|
|
||||||
|
if (e instanceof HttpClientErrorException) {
|
||||||
|
HttpClientErrorException clientError = (HttpClientErrorException) e;
|
||||||
|
statusCode = clientError.getStatusCode();
|
||||||
|
responseBody = clientError.getResponseBodyAsString();
|
||||||
|
} else if (e instanceof HttpServerErrorException) {
|
||||||
|
HttpServerErrorException serverError = (HttpServerErrorException) e;
|
||||||
|
statusCode = serverError.getStatusCode();
|
||||||
|
responseBody = serverError.getResponseBodyAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
output.setStatusCode(statusCode != null ? statusCode.value() : 0);
|
||||||
|
output.setResponseTime(responseTime);
|
||||||
|
output.setIsSuccess(false);
|
||||||
|
output.setErrorMessage(e.getMessage());
|
||||||
|
|
||||||
|
if (responseBody != null && !responseBody.isEmpty()) {
|
||||||
|
output.setResponseBody(responseBody);
|
||||||
|
output.setResponseSize((long) responseBody.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
String errorMsg = String.format("HTTP请求失败: 状态码=%d, 错误=%s",
|
||||||
|
output.getStatusCode(), e.getMessage());
|
||||||
|
|
||||||
|
// 不在这里记录日志,由BaseNodeDelegate统一处理(避免重复记录)
|
||||||
|
// 直接抛出异常,由BaseNodeDelegate统一处理continueOnFailure逻辑
|
||||||
|
throw new RuntimeException(errorMsg, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理一般错误(网络超时、连接失败等)
|
||||||
|
*/
|
||||||
|
private void handleGeneralError(Exception e) {
|
||||||
|
output.setIsSuccess(false);
|
||||||
|
output.setErrorMessage(e.getMessage());
|
||||||
|
|
||||||
|
String errorMsg = String.format("HTTP请求异常: %s - %s",
|
||||||
|
e.getClass().getSimpleName(), e.getMessage());
|
||||||
|
|
||||||
|
// 不在这里记录日志,由BaseNodeDelegate统一处理(避免重复记录)
|
||||||
|
// 直接抛出异常,由BaseNodeDelegate统一处理continueOnFailure逻辑
|
||||||
|
throw new RuntimeException(errorMsg, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建RestTemplate(使用节点配置的超时时间)
|
||||||
|
*/
|
||||||
|
private RestTemplate createRestTemplate(Integer timeout) {
|
||||||
|
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||||
|
|
||||||
|
// 使用节点配置的超时时间
|
||||||
|
int timeoutMs = (timeout != null && timeout > 0) ? timeout : 30000;
|
||||||
|
|
||||||
|
// 连接超时:建立TCP连接的超时时间
|
||||||
|
factory.setConnectTimeout(timeoutMs);
|
||||||
|
|
||||||
|
// 读取超时:等待服务器响应的超时时间
|
||||||
|
factory.setReadTimeout(timeoutMs);
|
||||||
|
|
||||||
|
RestTemplate restTemplate = new RestTemplate(factory);
|
||||||
|
return restTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建请求头
|
||||||
|
*/
|
||||||
|
private HttpHeaders buildHeaders(Map<String, String> headersMap) {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
|
||||||
|
// 默认设置 Content-Type 为 JSON(如果有body且未指定)
|
||||||
|
if (currentInputMapping.getBody() != null &&
|
||||||
|
(headersMap == null || !headersMap.containsKey("Content-Type"))) {
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headersMap != null) {
|
||||||
|
headersMap.forEach(headers::set);
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建完整URL(带查询参数)
|
||||||
|
*/
|
||||||
|
private String buildUrl(String baseUrl, Map<String, String> queryParams) {
|
||||||
|
if (queryParams == null || queryParams.isEmpty()) {
|
||||||
|
return baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUrl);
|
||||||
|
queryParams.forEach(builder::queryParam);
|
||||||
|
return builder.toUriString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,116 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.inputmapping;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import lombok.Data;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP请求节点输入映射
|
||||||
|
*
|
||||||
|
* @author qqchen
|
||||||
|
* @since 2025-11-20
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class HttpRequestInputMapping extends BaseNodeInputMapping {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求URL(支持表达式)
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "请求URL不能为空")
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP方法
|
||||||
|
*/
|
||||||
|
private HttpMethod method = HttpMethod.GET;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求头(数组格式,前端 KeyValueEditor 传入)
|
||||||
|
*/
|
||||||
|
private List<KeyValue> headers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求体(自动序列化为JSON)
|
||||||
|
*/
|
||||||
|
private Object body;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询参数(数组格式,前端 KeyValueEditor 传入)
|
||||||
|
*/
|
||||||
|
private List<KeyValue> queryParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超时时间(毫秒),默认30秒
|
||||||
|
*/
|
||||||
|
private Integer timeout = 30000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应体解析类型
|
||||||
|
*/
|
||||||
|
private ResponseBodyType responseBodyType = ResponseBodyType.JSON;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否跟随重定向
|
||||||
|
*/
|
||||||
|
private Boolean followRedirects = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSL证书验证(生产环境建议true)
|
||||||
|
*/
|
||||||
|
private Boolean verifySsl = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 键值对内部类(用于前端 KeyValueEditor 组件)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public static class KeyValue {
|
||||||
|
private String key;
|
||||||
|
private String value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将请求头数组转换为Map(供HTTP客户端使用)
|
||||||
|
*/
|
||||||
|
public Map<String, String> getHeadersAsMap() {
|
||||||
|
if (headers == null || headers.isEmpty()) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
return headers.stream()
|
||||||
|
.filter(kv -> kv.getKey() != null && !kv.getKey().isEmpty())
|
||||||
|
.collect(Collectors.toMap(KeyValue::getKey, KeyValue::getValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将查询参数数组转换为Map(供URL构建使用)
|
||||||
|
*/
|
||||||
|
public Map<String, String> getQueryParamsAsMap() {
|
||||||
|
if (queryParams == null || queryParams.isEmpty()) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
return queryParams.stream()
|
||||||
|
.filter(kv -> kv.getKey() != null && !kv.getKey().isEmpty())
|
||||||
|
.collect(Collectors.toMap(KeyValue::getKey, KeyValue::getValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应体解析类型枚举
|
||||||
|
*/
|
||||||
|
public enum ResponseBodyType {
|
||||||
|
/** JSON格式(自动解析为Map/List) */
|
||||||
|
JSON,
|
||||||
|
/** 纯文本 */
|
||||||
|
TEXT,
|
||||||
|
/** XML格式 */
|
||||||
|
XML,
|
||||||
|
/** 二进制数据 */
|
||||||
|
BINARY
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.outputs;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP请求节点输出
|
||||||
|
*
|
||||||
|
* @author qqchen
|
||||||
|
* @since 2025-11-20
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class HttpRequestOutputs extends BaseNodeOutputs {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP状态码
|
||||||
|
*/
|
||||||
|
private Integer statusCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应体(自动解析,JSON格式解析为Map/List)
|
||||||
|
*/
|
||||||
|
private Object responseBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应头
|
||||||
|
*/
|
||||||
|
private Map<String, String> responseHeaders;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求耗时(毫秒)
|
||||||
|
*/
|
||||||
|
private Long responseTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应大小(字节)
|
||||||
|
*/
|
||||||
|
private Long responseSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否成功(2xx状态码)
|
||||||
|
*/
|
||||||
|
private Boolean isSuccess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误信息
|
||||||
|
*/
|
||||||
|
private String errorMessage;
|
||||||
|
}
|
||||||
@ -74,6 +74,13 @@ public enum NodeTypeEnums {
|
|||||||
NodeCategoryEnums.TASK,
|
NodeCategoryEnums.TASK,
|
||||||
"通知节点"
|
"通知节点"
|
||||||
),
|
),
|
||||||
|
HTTP_REQUEST(
|
||||||
|
"HTTP_REQUEST",
|
||||||
|
"HTTP节点",
|
||||||
|
BpmnNodeTypeEnums.SERVICE_TASK,
|
||||||
|
NodeCategoryEnums.TASK,
|
||||||
|
"HTTP节点"
|
||||||
|
),
|
||||||
APPROVAL(
|
APPROVAL(
|
||||||
"APPROVAL",
|
"APPROVAL",
|
||||||
"审批节点",
|
"审批节点",
|
||||||
|
|||||||
@ -429,6 +429,8 @@ public class BpmnConverter {
|
|||||||
return "${approvalDelegate}";
|
return "${approvalDelegate}";
|
||||||
case "DEPLOY_NODE":
|
case "DEPLOY_NODE":
|
||||||
return "${deployDelegate}";
|
return "${deployDelegate}";
|
||||||
|
case "HTTP_REQUEST":
|
||||||
|
return "${httpRequestDelegate}";
|
||||||
default:
|
default:
|
||||||
log.warn("未知的节点类型: {}, 将不设置 delegateExpression", nodeCode);
|
log.warn("未知的节点类型: {}, 将不设置 delegateExpression", nodeCode);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
205
frontend/src/pages/Workflow/Design/nodes/HttpRequestNode.tsx
Normal file
205
frontend/src/pages/Workflow/Design/nodes/HttpRequestNode.tsx
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
import {ConfigurableNodeDefinition, NodeType, NodeCategory, defineNodeOutputs} from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP请求节点定义
|
||||||
|
* 用于发送HTTP/HTTPS请求并解析响应,支持REST API调用、Webhook触发等场景
|
||||||
|
*/
|
||||||
|
export const HttpRequestNodeDefinition: ConfigurableNodeDefinition = {
|
||||||
|
nodeCode: "HTTP_REQUEST",
|
||||||
|
nodeName: "HTTP请求",
|
||||||
|
nodeType: NodeType.HTTP_REQUEST,
|
||||||
|
category: NodeCategory.TASK,
|
||||||
|
description: "发送HTTP/HTTPS请求并解析响应",
|
||||||
|
|
||||||
|
// 渲染配置
|
||||||
|
renderConfig: {
|
||||||
|
shape: 'rounded-rect',
|
||||||
|
size: {width: 140, height: 48},
|
||||||
|
icon: {
|
||||||
|
type: 'emoji',
|
||||||
|
content: '🌐',
|
||||||
|
size: 32
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
primary: '#10b981',
|
||||||
|
secondary: '#059669',
|
||||||
|
selectedBorder: '#3b82f6',
|
||||||
|
hoverBorder: '#10b981',
|
||||||
|
gradient: ['#ffffff', '#d1fae5']
|
||||||
|
},
|
||||||
|
handles: {
|
||||||
|
input: true,
|
||||||
|
output: true
|
||||||
|
},
|
||||||
|
features: {
|
||||||
|
showBadge: true,
|
||||||
|
showHoverMenu: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 输入配置Schema
|
||||||
|
inputMappingSchema: {
|
||||||
|
type: "object",
|
||||||
|
title: "HTTP请求配置",
|
||||||
|
description: "配置HTTP请求的URL、方法、参数等",
|
||||||
|
properties: {
|
||||||
|
continueOnFailure: {
|
||||||
|
type: "boolean",
|
||||||
|
title: "失败后继续",
|
||||||
|
description: "当节点执行失败时,是否继续执行后续节点。true: 节点失败时标记为 FAILURE,但流程继续执行后续节点;false: 节点失败时抛出 BpmnError,终止流程",
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: "string",
|
||||||
|
title: "请求URL",
|
||||||
|
description: "HTTP请求的完整URL地址,支持变量表达式,如:${apiUrl}/status",
|
||||||
|
"x-allow-variable": true,
|
||||||
|
pattern: "^https?://.*",
|
||||||
|
minLength: 1
|
||||||
|
},
|
||||||
|
method: {
|
||||||
|
type: "string",
|
||||||
|
title: "HTTP方法",
|
||||||
|
description: "选择HTTP请求方法",
|
||||||
|
enum: ["GET", "POST", "PUT", "DELETE", "PATCH"],
|
||||||
|
enumNames: ["GET", "POST", "PUT", "DELETE", "PATCH"],
|
||||||
|
default: "GET"
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
type: "array",
|
||||||
|
title: "请求头",
|
||||||
|
description: "自定义HTTP请求头,例如:Authorization, Content-Type",
|
||||||
|
"x-component": "KeyValueEditor",
|
||||||
|
items: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
key: {
|
||||||
|
type: "string",
|
||||||
|
title: "Header名称"
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: "string",
|
||||||
|
title: "Header值"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ["key", "value"]
|
||||||
|
},
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
queryParams: {
|
||||||
|
type: "array",
|
||||||
|
title: "查询参数",
|
||||||
|
description: "URL查询参数,会自动拼接到URL后面",
|
||||||
|
"x-component": "KeyValueEditor",
|
||||||
|
items: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
key: {
|
||||||
|
type: "string",
|
||||||
|
title: "参数名"
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: "string",
|
||||||
|
title: "参数值"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ["key", "value"]
|
||||||
|
},
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
type: "string",
|
||||||
|
title: "请求体(JSON)",
|
||||||
|
description: "HTTP请求体内容,支持变量表达式",
|
||||||
|
format: "textarea",
|
||||||
|
"x-allow-variable": true,
|
||||||
|
"x-condition": {
|
||||||
|
field: "method",
|
||||||
|
operator: "in",
|
||||||
|
value: ["POST", "PUT", "PATCH"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
timeout: {
|
||||||
|
type: "number",
|
||||||
|
title: "超时时间(毫秒)",
|
||||||
|
description: "请求超时时间,0表示不限制",
|
||||||
|
minimum: 1000,
|
||||||
|
maximum: 300000,
|
||||||
|
default: 30000
|
||||||
|
},
|
||||||
|
responseBodyType: {
|
||||||
|
type: "string",
|
||||||
|
title: "响应体类型",
|
||||||
|
description: "响应内容的解析方式",
|
||||||
|
enum: ["JSON", "TEXT", "XML", "BINARY"],
|
||||||
|
enumNames: ["JSON", "纯文本", "XML", "二进制"],
|
||||||
|
default: "JSON"
|
||||||
|
},
|
||||||
|
verifySsl: {
|
||||||
|
type: "boolean",
|
||||||
|
title: "SSL证书验证",
|
||||||
|
description: "是否验证HTTPS证书,开发环境可关闭",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
followRedirects: {
|
||||||
|
type: "boolean",
|
||||||
|
title: "跟随重定向",
|
||||||
|
description: "是否自动跟随HTTP重定向(3xx状态码)",
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ["url", "method"]
|
||||||
|
},
|
||||||
|
|
||||||
|
// 输出字段定义
|
||||||
|
outputs: defineNodeOutputs(
|
||||||
|
{
|
||||||
|
name: "statusCode",
|
||||||
|
title: "HTTP状态码",
|
||||||
|
type: "number",
|
||||||
|
description: "响应的HTTP状态码(如 200, 404, 500)",
|
||||||
|
example: 200,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "isSuccess",
|
||||||
|
title: "是否成功",
|
||||||
|
type: "boolean",
|
||||||
|
description: "状态码为2xx时返回true",
|
||||||
|
example: true,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "responseBody",
|
||||||
|
title: "响应体",
|
||||||
|
type: "object",
|
||||||
|
description: "响应内容,JSON格式会自动解析为对象",
|
||||||
|
example: { status: 'success', data: {} },
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "responseTime",
|
||||||
|
title: "响应时间(毫秒)",
|
||||||
|
type: "number",
|
||||||
|
description: "请求耗时(毫秒)",
|
||||||
|
example: 150,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "errorMessage",
|
||||||
|
title: "错误信息",
|
||||||
|
type: "string",
|
||||||
|
description: "失败时的错误描述",
|
||||||
|
example: "Connection timeout",
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "responseHeaders",
|
||||||
|
title: "响应头",
|
||||||
|
type: "object",
|
||||||
|
description: "HTTP响应头信息",
|
||||||
|
example: { "content-type": "application/json" },
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user