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

This commit is contained in:
chenxing
2026-10-11 22:40:44 +08:00
parent cd046782d9
commit f07ac6da93
5 changed files with 142 additions and 0 deletions
@@ -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");