refactor: 适配校验页重构为顶栏选配置+自动批量解析+URL卡片内展示提取数据

This commit is contained in:
mediabot-pt
2026-07-01 08:50:06 +08:00
parent e56f1d4b92
commit b3667d7b7d
3 changed files with 291 additions and 374 deletions

View File

@@ -13,7 +13,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
* 适配器解析控制器
@@ -77,6 +78,80 @@ public class AdapterController {
return ApiResponse.success(result);
}
/**
* 批量解析:用指定配置解析站点所有URL的最新访问结果
*/
@PostMapping("/api/v1/admin/adapter/parse-batch")
public ApiResponse<Map<String, Object>> parseBatch(@RequestBody Map<String, Object> payload) {
Long configId = toLong(payload.get("configId"));
String siteId = (String) payload.get("siteId");
if (configId == null || siteId == null) {
return ApiResponse.error(400, "configId and siteId 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()); }
// 获取该站点所有结果,按URL分组取最新
List<AccessResult> allResults = accessResultMapper.selectBySiteId(siteId);
Map<String, AccessResult> latestByUrl = allResults.stream()
.filter(r -> r.getUrl() != null && !r.getUrl().isBlank())
.collect(Collectors.toMap(
AccessResult::getUrl,
r -> r,
(a, b) -> a.getCreatedAt().isAfter(b.getCreatedAt()) ? a : b));
// 批量解析
Map<String, Object> response = new LinkedHashMap<>();
response.put("configId", configId);
response.put("configVersion", siteConfig.getVersion());
response.put("totalUrls", latestByUrl.size());
response.put("totalResults", allResults.size());
List<Map<String, Object>> pages = new ArrayList<>();
for (Map.Entry<String, AccessResult> entry : latestByUrl.entrySet()) {
String url = entry.getKey();
AccessResult ar = entry.getValue();
Map<String, Object> item = new LinkedHashMap<>();
item.put("url", url);
item.put("accessResultId", ar.getId());
item.put("statusCode", ar.getStatusCode());
item.put("responseTimeMs", ar.getResponseTimeMs());
item.put("createdAt", ar.getCreatedAt());
item.put("totalCount", allResults.stream().filter(r -> url.equals(r.getUrl())).count());
item.put("validCount", allResults.stream().filter(r -> url.equals(r.getUrl()) && Boolean.TRUE.equals(r.getIsValid())).count());
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);
item.put("parsed", parsed);
}
} catch (Exception e) {
item.put("parseError", e.getMessage());
}
pages.add(item);
}
response.put("pages", pages);
return ApiResponse.success(response);
}
private Long toLong(Object val) {
if (val instanceof Number n) return n.longValue();
if (val instanceof String s) {