package com.doumee.service.business.snapshot; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Slf4j @Component public class SnapshotInferClient { @Autowired private SnapshotInferProperties properties; public boolean healthCheck() { if (StringUtils.isBlank(properties.getBaseUrl())) { return false; } String url = normalizeBaseUrl() + "/health"; try { HttpResponse response = HttpRequest.get(url) .timeout(properties.getConnectTimeoutMs()) .execute(); if (response.getStatus() != 200) { return false; } JSONObject json = JSON.parseObject(response.body()); return json != null && "ok".equalsIgnoreCase(json.getString("status")); } catch (Exception e) { log.warn("snapshot-infer health 失败: {}", e.getMessage()); return false; } } public SnapshotAnalyzeResponse analyze(SnapshotAnalyzeRequest request) { if (StringUtils.isBlank(properties.getBaseUrl())) { return resolveFailure(request, "snapshot.infer.base-url 未配置"); } String url = normalizeBaseUrl() + "/analyze"; String payload = JSON.toJSONString(request); try { HttpResponse response = HttpRequest.post(url) .body(payload) .contentType("application/json") .setConnectionTimeout(properties.getConnectTimeoutMs()) .timeout(properties.getReadTimeoutMs()) .execute(); String body = response.body(); if (response.getStatus() != 200) { log.warn("snapshot-infer analyze HTTP {} body={}", response.getStatus(), body); return resolveFailure(request, "推理服务 HTTP " + response.getStatus()); } SnapshotAnalyzeResponse parsed = JSON.parseObject(body, SnapshotAnalyzeResponse.class); if (parsed == null) { return resolveFailure(request, "推理响应为空"); } if (Boolean.FALSE.equals(parsed.getSuccess())) { String msg = StringUtils.defaultIfBlank(parsed.getMessage(), "推理未检测到有效时刻"); log.warn("snapshot-infer analyze 失败: {}", msg); return resolveFailure(request, msg); } if (parsed.getStorefront() == null || parsed.getHandover() == null) { return resolveFailure(request, "推理响应缺少门头或交付时刻"); } if (parsed.getSuccess() == null) { parsed.setSuccess(true); } if (parsed.getAsrHits() == null) { parsed.setAsrHits(new java.util.ArrayList<>()); } return parsed; } catch (Exception e) { log.warn("snapshot-infer analyze 异常: {}", e.getMessage()); return resolveFailure(request, e.getMessage()); } } private SnapshotAnalyzeResponse resolveFailure(SnapshotAnalyzeRequest request, String message) { if (properties.isFailOpenMock()) { log.warn("fail-open-mock 启用,使用本地 mock: {}", message); return buildLocalMock(request); } SnapshotAnalyzeResponse fail = new SnapshotAnalyzeResponse(); fail.setSuccess(false); fail.setMessage(message); fail.setModelVersion("unavailable"); return fail; } private SnapshotAnalyzeResponse buildLocalMock(SnapshotAnalyzeRequest request) { double duration = request.getDurationSec() != null && request.getDurationSec() > 0 ? request.getDurationSec() : 1200.0; double t1 = Math.round(duration * 0.25 * 100.0) / 100.0; double t2 = Math.round(duration * 0.75 * 100.0) / 100.0; if (t2 <= t1) { t2 = t1 + 60.0; } SnapshotAnalyzeResponse response = new SnapshotAnalyzeResponse(); response.setSuccess(true); response.setModelVersion("local-mock"); response.setDurationSec(duration); SnapshotAnalyzeResponse.SnapshotHit storefront = new SnapshotAnalyzeResponse.SnapshotHit(); storefront.setTimeSec(t1); storefront.setConfidence(0.5); storefront.setSource("local-mock"); SnapshotAnalyzeResponse.SnapshotHit handover = new SnapshotAnalyzeResponse.SnapshotHit(); handover.setTimeSec(t2); handover.setConfidence(0.5); handover.setSource("local-mock"); response.setStorefront(storefront); response.setHandover(handover); return response; } private String normalizeBaseUrl() { return properties.getBaseUrl().trim().replaceAll("/+$", ""); } }