feat: 适配器解析引擎,用 CSS 选择器从提交的 HTML 中自动提取数据进行校验
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package com.par.api.controller;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.par.core.adapter.AdapterConfig;
|
||||
import com.par.core.adapter.AdapterEngine;
|
||||
import com.par.core.dto.ApiResponse;
|
||||
import com.par.core.entity.AccessResult;
|
||||
import com.par.core.entity.SiteConfig;
|
||||
import com.par.core.mapper.AccessResultMapper;
|
||||
import com.par.core.mapper.SiteConfigMapper;
|
||||
import com.par.core.storage.CachedStorageService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 适配器解析控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class AdapterController {
|
||||
|
||||
private final AdapterEngine adapterEngine;
|
||||
private final SiteConfigMapper siteConfigMapper;
|
||||
private final AccessResultMapper accessResultMapper;
|
||||
private final CachedStorageService storageService;
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 用指定适配配置解析指定访问结果
|
||||
*/
|
||||
@PostMapping("/api/v1/admin/adapter/parse")
|
||||
public ApiResponse<Map<String, Object>> parse(
|
||||
@RequestBody Map<String, Object> payload) {
|
||||
|
||||
Long configId = toLong(payload.get("configId"));
|
||||
Long accessResultId = toLong(payload.get("accessResultId"));
|
||||
|
||||
if (configId == null || accessResultId == null) {
|
||||
return ApiResponse.error(400, "configId and accessResultId are required");
|
||||
}
|
||||
|
||||
// 加载适配配置
|
||||
SiteConfig siteConfig = siteConfigMapper.selectById(configId);
|
||||
if (siteConfig == null) return ApiResponse.error(404, "配置不存在");
|
||||
|
||||
String configJson;
|
||||
try { configJson = storageService.read(siteConfig.getStoragePath()); }
|
||||
catch (Exception e) { return ApiResponse.error(500, "读取配置失败: " + e.getMessage()); }
|
||||
if (configJson == null) return ApiResponse.error(404, "配置内容为空");
|
||||
|
||||
AdapterConfig adapterConfig;
|
||||
try { adapterConfig = objectMapper.readValue(configJson, AdapterConfig.class); }
|
||||
catch (Exception e) { return ApiResponse.error(400, "配置格式无效: " + e.getMessage()); }
|
||||
|
||||
// 加载访问结果 HTML
|
||||
AccessResult accessResult = accessResultMapper.selectById(accessResultId);
|
||||
if (accessResult == null) return ApiResponse.error(404, "访问结果不存在");
|
||||
|
||||
String html;
|
||||
try { html = storageService.read(accessResult.getStoragePath()); }
|
||||
catch (Exception e) { return ApiResponse.error(500, "读取访问结果失败: " + e.getMessage()); }
|
||||
if (html == null) return ApiResponse.error(404, "访问结果内容为空");
|
||||
|
||||
// 执行解析
|
||||
Map<String, Object> result;
|
||||
try {
|
||||
result = adapterEngine.extract(adapterConfig, html, accessResult.getUrl());
|
||||
} catch (Exception e) {
|
||||
log.error("Adapter parse error", e);
|
||||
return ApiResponse.error(500, "解析异常: " + e.getMessage());
|
||||
}
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
private Long toLong(Object val) {
|
||||
if (val instanceof Number n) return n.longValue();
|
||||
if (val instanceof String s) {
|
||||
try { return Long.parseLong(s); } catch (NumberFormatException ignored) {}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user