feat: 适配引擎支持 JSON 内容直接展示结构,批量解析传入 contentType

This commit is contained in:
mediabot-pt
2026-07-01 09:21:33 +08:00
parent 138559b3dc
commit 71fbf90ad7
2 changed files with 84 additions and 10 deletions

View File

@@ -133,11 +133,11 @@ public class AdapterController {
item.put("invalidCount", allResults.stream().filter(r -> url.equals(r.getUrl()) && Boolean.FALSE.equals(r.getIsValid())).count());
item.put("uncheckedCount", allResults.stream().filter(r -> url.equals(r.getUrl()) && r.getIsValid() == null).count());
// 解析 HTML
// 解析内容
try {
String html = storageService.read(ar.getStoragePath());
if (html != null) {
Map<String, Object> parsed = adapterEngine.extract(adapterConfig, html, url);
String content = storageService.read(ar.getStoragePath());
if (content != null) {
Map<String, Object> parsed = adapterEngine.extract(adapterConfig, content, ar.getContentType(), url);
item.put("parsed", parsed);
}
} catch (Exception e) {

View File

@@ -23,29 +23,103 @@ public class AdapterEngine {
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* 解析单个 HTML 页面,匹配配置中的页面规则并提取数据
* @return { pageName, pageUrl, extracted: [{field: value, ...}, ...] }
* 解析页面内容(兼容 HTML 和 JSON)
*/
public Map<String, Object> extract(AdapterConfig config, String html, String accessUrl) {
public Map<String, Object> extract(AdapterConfig config, String content, String contentType, String accessUrl) {
if ("json".equalsIgnoreCase(contentType)) {
return extractJson(config, content, accessUrl);
}
return extractHtml(config, content, accessUrl);
}
/** 保留旧签名兼容单条解析 */
public Map<String, Object> extract(AdapterConfig config, String content, String accessUrl) {
return extract(config, content, "html", accessUrl);
}
/**
* 解析 JSON 内容:直接展示 JSON 结构
*/
private Map<String, Object> extractJson(AdapterConfig config, String jsonStr, String accessUrl) {
Map<String, Object> result = new LinkedHashMap<>();
try {
Object parsed = objectMapper.readValue(jsonStr, Object.class);
result.put("contentType", "json");
result.put("matched", true);
result.put("pageName", "JSON响应");
if (parsed instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) parsed;
result.put("message", "JSON 对象,共 " + map.size() + " 个顶层字段");
// 展示顶层字段
List<Map<String, Object>> rows = new ArrayList<>();
Map<String, Object> row = new LinkedHashMap<>();
for (String key : map.keySet()) {
Object val = map.get(key);
row.put(key, val instanceof String ? val : objectMapper.writeValueAsString(val));
}
rows.add(row);
result.put("extracted", rows);
result.put("rowCount", 1);
result.put("fieldCount", map.size());
} else if (parsed instanceof List) {
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) parsed;
result.put("message", "JSON 数组,共 " + list.size() + " 条记录");
List<Map<String, Object>> rows = new ArrayList<>();
int max = Math.min(list.size(), 20);
for (int i = 0; i < max; i++) {
Object item = list.get(i);
Map<String, Object> row = new LinkedHashMap<>();
if (item instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> m = (Map<String, Object>) item;
for (String key : m.keySet()) {
Object val = m.get(key);
row.put(key, val instanceof String ? val : objectMapper.writeValueAsString(val));
}
} else {
row.put("value", item);
}
rows.add(row);
}
result.put("extracted", rows);
result.put("rowCount", list.size());
result.put("fieldCount", rows.isEmpty() ? 0 : rows.get(0).size());
}
} catch (Exception e) {
result.put("contentType", "json");
result.put("matched", false);
result.put("message", "JSON 解析失败: " + e.getMessage());
}
return result;
}
/**
* 解析 HTML 内容:Jsoup + CSS 选择器
*/
private Map<String, Object> extractHtml(AdapterConfig config, String html, String accessUrl) {
Map<String, Object> result = new LinkedHashMap<>();
Document doc = Jsoup.parse(html);
// 匹配页面
Map.Entry<String, PageConfig> matched = matchPage(config.getPages(), accessUrl);
if (matched == null) {
result.put("matched", false);
result.put("contentType", "html");
result.put("message", "未匹配到页面规则");
result.put("availablePages", config.getPages().keySet());
result.put("availablePages", new ArrayList<>(config.getPages() != null ? config.getPages().keySet() : Set.of()));
result.put("accessUrl", accessUrl);
return result;
}
String pageName = matched.getKey();
PageConfig page = matched.getValue();
result.put("matched", true);
result.put("contentType", "html");
result.put("pageName", pageName);
result.put("pageUrl", accessUrl);
// 提取数据
List<Map<String, Object>> extracted = extractRows(doc, page);
result.put("extracted", extracted);
result.put("rowCount", extracted.size());