feat(callback/app): 回调探测端点 /admin/callback/probe/{id}——签 flow.ping 未知事件码走真实管道同步一发(信封同构 CallbackDispatcher,结果落 flw_callback_log 一次性行+resp_body 响应摘要列);应用辅助统计 /admin/app/stats(appKey→本校回调注册数,应用设置页对接深度列)
build / build-push (push) Failing after 14s
build / build-push (push) Failing after 14s
This commit is contained in:
@@ -211,6 +211,105 @@ public class CallbackPushService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回调探测(2026-10-11 管理端增强):以该注册的 secret 按现行信封签一条未知事件码 flow.ping,
|
||||
* 走真实推送管道同步一发——接收方按规范 §6.4 对未知事件码"记日志后 200",不破坏业务数据。
|
||||
* 探测是一次性行为:结果落 flw_callback_log(成功=1 / 失败=2,永不进重试队列),
|
||||
* 响应体摘要落 resp_body。返回 {success, httpStatus, elapsedMs, respSnippet, error}。
|
||||
*/
|
||||
public java.util.Map<String, Object> probe(Long schId, FlwCallbackReg reg) {
|
||||
String eventId = UUID.randomUUID().toString().replace("-", "");
|
||||
String body = probePayload(schId, reg, eventId);
|
||||
long start = System.currentTimeMillis();
|
||||
String ts = String.valueOf(System.currentTimeMillis());
|
||||
String nonce = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
|
||||
Integer httpStatus = null;
|
||||
String respSnippet = null;
|
||||
String error = null;
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.set("X-Flw-EventId", eventId);
|
||||
headers.set("X-Flw-Timestamp", ts);
|
||||
headers.set("X-Flw-Nonce", nonce);
|
||||
headers.set("X-Flw-Signature", sign(reg.getSecret(), ts, nonce, body));
|
||||
RestTemplate template = CallbackAppSigner.signedTemplate(
|
||||
appIdentityProvider.getIfAvailable(), reg.getBasePath(), 5000, 10000);
|
||||
ResponseEntity<String> resp = template.exchange(reg.getCallbackUrl(), HttpMethod.POST,
|
||||
new HttpEntity<>(body, headers), String.class);
|
||||
httpStatus = resp.getStatusCodeValue();
|
||||
respSnippet = resp.getBody() == null ? null : truncate(resp.getBody());
|
||||
} catch (Exception e) {
|
||||
error = rootMessage(e);
|
||||
}
|
||||
long elapsed = System.currentTimeMillis() - start;
|
||||
boolean ok = httpStatus != null && httpStatus >= 200 && httpStatus < 300;
|
||||
persistProbeLog(schId, reg, eventId, body, ok, httpStatus, respSnippet, error);
|
||||
log.info("[flow-center] 回调探测:reg={} url={} ok={} http={} elapsed={}ms",
|
||||
reg.getId(), reg.getCallbackUrl(), ok, httpStatus, elapsed);
|
||||
java.util.Map<String, Object> out = new java.util.HashMap<>();
|
||||
out.put("success", ok);
|
||||
out.put("httpStatus", httpStatus);
|
||||
out.put("elapsedMs", elapsed);
|
||||
out.put("respSnippet", respSnippet);
|
||||
out.put("error", error);
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 探测信封:与 CallbackDispatcher 现行事件同构,event=flow.ping(未知码),instanceId=0 哨兵 */
|
||||
private String probePayload(Long schId, FlwCallbackReg reg, String eventId) {
|
||||
java.util.Map<String, Object> payload = new java.util.HashMap<>();
|
||||
payload.put("eventId", eventId);
|
||||
payload.put("event", "flow.ping");
|
||||
payload.put("schId", schId);
|
||||
payload.put("flowGuid", reg.getFlowGuid());
|
||||
payload.put("instanceId", 0L);
|
||||
payload.put("instanceVersion", 0);
|
||||
payload.put("bizType", "probe");
|
||||
payload.put("bizId", String.valueOf(reg.getId()));
|
||||
payload.put("bizTitle", "回调探测 flow.ping");
|
||||
payload.put("occurredTime", LocalDateTime.now().format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME));
|
||||
payload.put("taskId", null);
|
||||
payload.put("operatorUserId", null);
|
||||
payload.put("operatorName", null);
|
||||
java.util.Map<String, Object> data = new java.util.HashMap<>();
|
||||
data.put("probe", true);
|
||||
data.put("message", "flow-center 回调连通性探测(未知事件码,按规范 §6.4 记日志后 200 即可)");
|
||||
payload.put("data", data);
|
||||
try {
|
||||
return new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(payload);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("探测信封序列化失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 探测结果落日志行(一次性行为:成功=1 / 失败=2,status≠PENDING 永不被重试扫描捞起) */
|
||||
private void persistProbeLog(Long schId, FlwCallbackReg reg, String eventId, String body,
|
||||
boolean ok, Integer httpStatus, String respSnippet, String error) {
|
||||
try {
|
||||
FlwCallbackLog row = new FlwCallbackLog();
|
||||
row.setSchId(schId);
|
||||
row.setRegId(reg.getId());
|
||||
row.setEventId(eventId);
|
||||
row.setEvent("flow.ping");
|
||||
row.setInstanceId(0L);
|
||||
row.setBizType("probe");
|
||||
row.setBizId(String.valueOf(reg.getId()));
|
||||
row.setPayloadJson(body);
|
||||
row.setRespBody(respSnippet);
|
||||
row.setStatus(ok ? FlwEnums.CallbackLogStatus.SUCCESS.getCode()
|
||||
: FlwEnums.CallbackLogStatus.DEAD.getCode());
|
||||
row.setRetryCount(0);
|
||||
row.setNextRetryTime(LocalDateTime.now());
|
||||
row.setLastHttpStatus(httpStatus == null ? null : String.valueOf(httpStatus));
|
||||
row.setLastError(error == null ? null : truncate(error));
|
||||
row.setPushTime(LocalDateTime.now());
|
||||
logRepository.save(row);
|
||||
} catch (Exception e) {
|
||||
log.warn("[flow-center] 探测日志落库失败:reg={} {}", reg.getId(), e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static String sign(String secret, String timestamp, String nonce, String body) {
|
||||
try {
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
|
||||
Reference in New Issue
Block a user