ExternalResponseUtil.java
package com.yumu.noveltranslator.util;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yumu.noveltranslator.enums.ErrorCodeEnum;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class ExternalResponseUtil {
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 从原始后端JSON响应中提取主要翻译文本。
* 后端格式通常为:
* {“code”:200,“data”:“<h1>...</h1>”,“engine”:“google”,...}
* 该方法尝试共用键(data、translatedContent、translation)和
* 在解析失败时,会退回返回原始的原始字符串。
*/
/**
* 从原始后端 JSON 响应中提取主要翻译文本
* 支持的格式:
* 1. 标准格式:{"success": true, "engine": "google", "translatedContent": "{...}"}
* 2. 通用格式:{"code": 200, "data": "<h1>...</h1>", "engine": "google",...}
*
* @param rawJson 原始 JSON 响应
* @return 翻译文本,如果失败返回 null
*/
public static String extractDataField(String rawJson) {
if (rawJson == null) return null;
try {
JsonNode root = MAPPER.readTree(rawJson);
if (root == null) return null;
// 1. 优先提取 translatedContent 字段(标准格式)
if (root.has("translatedContent")) {
JsonNode translatedContent = root.get("translatedContent");
if (translatedContent.isTextual()) {
return translatedContent.asText();
}
return translatedContent.toString();
}
// 2. 验证状态码(仅对有 code 字段的格式)
StatusCodeResult statusResult = validateStatusCode(root);
if (!statusResult.isValid()) {
// 状态码不是 200,返回 null 表示失败
return null;
}
// 3. 提取 data 字段
if (root.has("data")) {
JsonNode data = root.get("data");
if (data.isTextual()) return data.asText();
return data.toString();
}
// 4. 如果找不到已知键,尝试返回文本形式
if (root.isTextual()) return root.asText();
// 5. 返回原始 JSON 作为兜底
return rawJson;
} catch (Exception e) {
// 解析失败——返回 null 表示失败
return null;
}
}
/**
* 验证响应状态码
* @param root JSON 根节点
* @return 状态码验证结果
*/
public static StatusCodeResult validateStatusCode(JsonNode root) {
if (root.has("code")) {
JsonNode code = root.get("code");
int statusCode = code.asInt();
return new StatusCodeResult(statusCode == 200, statusCode);
}
// 没有 code 字段时,假设响应有效(兼容旧格式)
return new StatusCodeResult(true, 200);
}
/**
* 状态码验证结果类
*/
public static class StatusCodeResult {
private final boolean valid;
private final int statusCode;
public StatusCodeResult(boolean valid, int statusCode) {
this.valid = valid;
this.statusCode = statusCode;
}
public boolean isValid() {
return valid;
}
public int getStatusCode() {
return statusCode;
}
}
public static List<String> processingReader(String content){
Document document = Jsoup.parse(content);
List<String> orderedText = new ArrayList<>();
traverseAllTags(document, orderedText);
return orderedText;
}
private static void traverseAllTags(Node parentNode, List<String> resultList) {
for (Node childNode : parentNode.childNodes()) {
String tagContent = childNode.outerHtml();
resultList.add(tagContent);
traverseAllTags(childNode, resultList);
}
}
/**
* 统一错误响应格式:
* {
* "success": false,
* "error": "Error message description",
* "code": "ERROR_CODE",
* "details": { ... }
* }
*/
public static String buildErrorResponse(String error, String code, Map<String, Object> details) {
try {
ObjectNode root = MAPPER.createObjectNode();
root.put("success", false);
root.put("error", error);
root.put("code", code);
ObjectNode detailsNode = root.putObject("details");
if (details != null) {
details.forEach((k, v) -> detailsNode.putPOJO(k, v));
}
return MAPPER.writeValueAsString(root);
} catch (Exception e) {
// 最后兜底,构造极简错误字符串
return String.format("{\"success\":false,\"error\":\"%s\",\"code\":\"%s\"}", "Serialization failure", "FATAL_ERROR");
}
}
/**
* 使用枚举构建错误响应
*/
public static String buildErrorResponse(ErrorCodeEnum errorCodeEnum, Map<String, Object> details) {
return buildErrorResponse(errorCodeEnum.getMessage(), errorCodeEnum.getCode(), details);
}
/**
* 构建翻译文件路径(在扩展名前插入 _translated)
* @param originalPath 原始文件路径
* @return 翻译后的文件路径
*/
public static String buildTranslatedPath(String originalPath) {
if (originalPath == null) return null;
int lastDot = originalPath.lastIndexOf('.');
if (lastDot > 0) {
return originalPath.substring(0, lastDot) + "_translated" + originalPath.substring(lastDot);
}
return originalPath + "_translated";
}
/**
* 使用枚举构建简单错误响应
*/
public static String buildSimpleErrorResponse(ErrorCodeEnum errorCodeEnum) {
return buildErrorResponse(errorCodeEnum.getMessage(), errorCodeEnum.getCode(), null);
}
}