diff --git a/par-api/src/main/java/com/par/api/controller/AdminController.java b/par-api/src/main/java/com/par/api/controller/AdminController.java index bdb2614..623c61e 100644 --- a/par-api/src/main/java/com/par/api/controller/AdminController.java +++ b/par-api/src/main/java/com/par/api/controller/AdminController.java @@ -92,15 +92,7 @@ public class AdminController { } config.setVersion(String.valueOf(nextVersion)); - // 文件从 pending 移到 approved 路径 - String approvedPath = config.getStoragePath().replace("/pending/", "/approved/"); - String content = storageService.read(config.getStoragePath()); - if (content != null) { - storageService.store(approvedPath, content); - config.setStoragePath(approvedPath); - } - - // Groovy 脚本也移到 approved + // Groovy 脚本从 pending 移到 approved if (config.getScriptStoragePath() != null && !config.getScriptStoragePath().isBlank()) { String approvedScriptPath = config.getScriptStoragePath().replace("/pending/", "/approved/"); String script = storageService.read(config.getScriptStoragePath()); @@ -125,7 +117,6 @@ public class AdminController { var site = siteMapper.selectBySiteId(config.getSiteId()); if (site != null) { site.setCurrentConfigId(config.getId()); - site.setSchemaVersion(config.getSchemaVersion()); siteMapper.updateById(site); } @@ -152,9 +143,8 @@ public class AdminController { Long reviewerId = (Long) request.getAttribute("accountId"); // 删除 pending 文件 - storageService.delete(config.getStoragePath()); + if (config.getScriptStoragePath() != null) storageService.delete(config.getScriptStoragePath()); - config.setStoragePath(null); config.setStatus(ConfigStatus.REJECTED); config.setReviewerId(reviewerId); config.setReviewedAt(LocalDateTime.now()); @@ -181,54 +171,6 @@ public class AdminController { return ApiResponse.success(configs.stream().map(configService::toDTO).toList()); } - /** - * 查看指定版本配置内容 - */ - @GetMapping("/configs/{configId}/content") - public ApiResponse> getConfigContent( - @PathVariable Long configId, HttpServletRequest request) { - checkAdmin(request); - SiteConfig config = siteConfigMapper.selectById(configId); - if (config == null) return ApiResponse.error(404, "Config not found"); - String content = null; - try { content = storageService.read(config.getStoragePath()); } catch (Exception e) { - return ApiResponse.error(500, "读取配置失败: " + e.getMessage()); - } - return ApiResponse.success(Map.of("id", config.getId(), "content", content != null ? content : "")); - } - - /** - * 编辑待审核配置内容 - */ - @PutMapping("/configs/{configId}/content") - public ApiResponse updateConfigContent( - @PathVariable Long configId, - @RequestBody Map payload, - HttpServletRequest request) { - checkAdmin(request); - SiteConfig config = siteConfigMapper.selectById(configId); - if (config == null) return ApiResponse.error(404, "Config not found"); - if (config.getStatus() != ConfigStatus.PENDING) { - return ApiResponse.error(400, "只能编辑待审核的配置"); - } - - String content = payload.get("content"); - if (content == null || content.isBlank()) { - return ApiResponse.error(400, "content is required"); - } - - // 更新存储文件 - storageService.store(config.getStoragePath(), content); - - // 更新 hash 和文件大小 - config.setConfigHash(HmacUtil.sha256(content)); - config.setFileSize((long) content.getBytes(StandardCharsets.UTF_8).length); - siteConfigMapper.updateById(config); - - log.info("Config content updated: id={}, siteId={}", configId, config.getSiteId()); - return ApiResponse.success(configService.toDTO(config)); - } - /** * 查看指定配置的 Groovy 脚本 */ diff --git a/par-api/src/main/java/com/par/api/controller/ConfigController.java b/par-api/src/main/java/com/par/api/controller/ConfigController.java index 40cabd5..fef54d5 100644 --- a/par-api/src/main/java/com/par/api/controller/ConfigController.java +++ b/par-api/src/main/java/com/par/api/controller/ConfigController.java @@ -55,72 +55,6 @@ public class ConfigController { return ApiResponse.success(configService.listApprovedConfigs(siteId)); } - /** - * 查看配置内容(管理员用,返回 ApiResponse 包装) - */ - @GetMapping("/{siteId}/{version}/content") - public ApiResponse viewConfigContent( - @PathVariable("siteId") String siteId, - @PathVariable("version") String version) { - - SiteConfig config = configService.getConfig(siteId, version); - if (config == null) { - return ApiResponse.error(404, "配置不存在: " + siteId + " v" + version); - } - - String content; - try { - content = storageService.read(config.getStoragePath()); - } catch (Exception e) { - log.error("Failed to read config: siteId={}, version={}", siteId, version, e); - return ApiResponse.error(500, "读取配置文件失败: " + e.getMessage()); - } - if (content == null) { - return ApiResponse.error(404, "配置文件不存在: " + config.getStoragePath()); - } - - return ApiResponse.success(content); - } - - /** - * 下载指定版本配置(支持 ETag 条件请求,供 mediabot 客户端使用) - */ - @GetMapping("/{siteId}/{version}") - public ResponseEntity downloadConfig( - @PathVariable("siteId") String siteId, - @PathVariable("version") String version, - @RequestHeader(value = "If-None-Match", required = false) String ifNoneMatch) { - - SiteConfig config = configService.getConfig(siteId, version); - if (config == null) { - return ResponseEntity.notFound().build(); - } - - // ETag 匹配则返回 304 - if (ifNoneMatch != null && ifNoneMatch.equals(config.getConfigHash())) { - return ResponseEntity.status(304) - .eTag(config.getConfigHash()) - .build(); - } - - // 读取文件内容(通过存储抽象:本地缓存或七牛云) - String content; - try { - content = storageService.read(config.getStoragePath()); - } catch (Exception e) { - log.error("Failed to read config: siteId={}, version={}", siteId, version, e); - return ResponseEntity.status(500).body("Failed to read config"); - } - if (content == null) { - return ResponseEntity.notFound().build(); - } - - return ResponseEntity.ok() - .eTag(config.getConfigHash()) - .header(org.springframework.http.HttpHeaders.CONTENT_TYPE, "application/json") - .body(content); - } - /** * 下载最新版本 Groovy 脚本(供 mediabot 使用) * 优先自定义脚本,无自定义则返回站点类型默认模板 @@ -186,20 +120,14 @@ public class ConfigController { HttpServletRequest request) { String version = (String) payload.get("version"); - String configJson = (String) payload.get("config"); - String schemaVersion = (String) payload.get("schemaVersion"); String groovyScript = (String) payload.get("script"); - if (version == null) { - return ApiResponse.error(400, "version is required"); - } - if ((configJson == null || configJson.isBlank()) && (groovyScript == null || groovyScript.isBlank())) { - return ApiResponse.error(400, "config 或 script 至少需要一个"); - } + if (version == null) return ApiResponse.error(400, "version is required"); + if (groovyScript == null || groovyScript.isBlank()) return ApiResponse.error(400, "script is required"); Long submitterId = (Long) request.getAttribute("accountId"); - SiteConfig config = configService.submitConfig(siteId, version, configJson, schemaVersion, groovyScript, submitterId); + SiteConfig config = configService.submitConfig(siteId, version, groovyScript, submitterId); return ApiResponse.success(configService.toDTO(config)); } } diff --git a/par-api/src/main/java/com/par/api/controller/ManifestController.java b/par-api/src/main/java/com/par/api/controller/ManifestController.java index 801d208..b2b01e5 100644 --- a/par-api/src/main/java/com/par/api/controller/ManifestController.java +++ b/par-api/src/main/java/com/par/api/controller/ManifestController.java @@ -63,13 +63,11 @@ public class ManifestController { SiteConfig latest = siteConfigMapper.selectLatestBySiteId(site.getSiteId()); if (latest != null) { entry.put("latestVersion", latest.getVersion()); - entry.put("configHash", latest.getConfigHash()); entry.put("hasScript", latest.getScriptStoragePath() != null && !latest.getScriptStoragePath().isBlank()); entry.put("updatedAt", latest.getCreatedAt()); } // 参与 ETag 计算 etagBuilder.append(site.getSiteId()) - .append(latest != null ? latest.getConfigHash() : "none") .append(latest != null ? latest.getScriptStoragePath() : "none") .append(";"); diff --git a/par-core/src/main/java/com/par/core/adapter/AdapterConfig.java b/par-core/src/main/java/com/par/core/adapter/AdapterConfig.java deleted file mode 100644 index ae6f6c5..0000000 --- a/par-core/src/main/java/com/par/core/adapter/AdapterConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.par.core.adapter; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; - -import java.util.Map; - -/** - * 适配器配置根对象 - */ -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class AdapterConfig { - /** 站点 ID */ - private String siteId; - /** 站点类型 */ - private String siteType; - /** 域名列表 */ - private String[] domains; - /** 页面解析规则 */ - private Map pages; -} diff --git a/par-core/src/main/java/com/par/core/adapter/AdapterEngine.java b/par-core/src/main/java/com/par/core/adapter/AdapterEngine.java deleted file mode 100644 index b5d2089..0000000 --- a/par-core/src/main/java/com/par/core/adapter/AdapterEngine.java +++ /dev/null @@ -1,287 +0,0 @@ -package com.par.core.adapter; - -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.extern.slf4j.Slf4j; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; -import org.springframework.stereotype.Service; - -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * 适配器解析引擎:根据配置文件的 CSS 选择器(HTML)或点分路径(JSON)提取数据 - */ -@Slf4j -@Service -public class AdapterEngine { - - private static final ObjectMapper objectMapper = new ObjectMapper(); - - public Map 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 extract(AdapterConfig config, String content, String accessUrl) { - return extract(config, content, "html", accessUrl); - } - - // ==================== JSON ==================== - - private Map extractJson(AdapterConfig config, String jsonStr, String accessUrl) { - Map result = new LinkedHashMap<>(); - try { - Object parsed = objectMapper.readValue(jsonStr, Object.class); - result.put("contentType", "json"); - - Map.Entry matched = matchPage(config.getPages(), accessUrl); - - if (matched != null && matched.getValue().getFields() != null && !matched.getValue().getFields().isEmpty()) { - // 有页面规则 → 按 selector 提取 - PageConfig page = matched.getValue(); - result.put("matched", true); - result.put("pageName", matched.getKey()); - - List items = new ArrayList<>(); - if (page.getListContainer() != null && !page.getListContainer().isBlank()) { - Object container = resolveJsonPath(parsed, page.getListContainer()); - if (container instanceof List) items = (List) container; - else items.add(parsed); - } else { - items.add(parsed); - } - - List> rows = new ArrayList<>(); - int max = Math.min(items.size(), 50); - for (int i = 0; i < max; i++) { - Map row = new LinkedHashMap<>(); - boolean hasData = false; - for (Map.Entry field : page.getFields().entrySet()) { - String fieldName = field.getKey(); - FieldExtractor fe = field.getValue(); - if (fe.getSelector() == null) continue; - Object val = resolveJsonPath(items.get(i), fe.getSelector()); - if (val != null) hasData = true; - if (fe.getOptional() != null && fe.getOptional() && val == null) continue; - if (val instanceof String s) { - row.put(fieldName, applyTransform(s, fe.getTransform())); - } else if (val != null) { - row.put(fieldName, toDisplayString(val)); - } else { - row.put(fieldName, null); - } - } - if (hasData) rows.add(row); - } - result.put("extracted", rows); - result.put("rowCount", rows.size()); - result.put("fieldCount", page.getFields().size()); - result.put("message", rows.isEmpty() ? "selector 未匹配到数据" : "共 " + rows.size() + " 行"); - } else { - // 无页面规则 → 兜底展示原始结构 - String reason = matched == null ? "未匹配到页面规则" : "页面规则未定义字段"; - result.put("matched", true); - result.put("pageName", "JSON原始结构"); - result.put("message", reason + ",展示原始数据"); - List> rows = new ArrayList<>(); - if (parsed instanceof Map) { - Map row = new LinkedHashMap<>(); - for (String key : ((Map) parsed).keySet()) { - row.put(key, toDisplayString(((Map) parsed).get(key))); - } - rows.add(row); - } else if (parsed instanceof List) { - int m = Math.min(((List) parsed).size(), 10); - for (int i = 0; i < m; i++) { - Map row = new LinkedHashMap<>(); - row.put("value", ((List) parsed).get(i)); - rows.add(row); - } - } else { - Map row = new LinkedHashMap<>(); - row.put("value", parsed); - rows.add(row); - } - result.put("extracted", rows); - result.put("rowCount", rows.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; - } - - @SuppressWarnings("unchecked") - private Object resolveJsonPath(Object root, String path) { - if (root == null || path == null || path.isBlank()) return null; - Object current = root; - for (String part : path.split("\\.")) { - if (current == null) return null; - String key = part; - int bracket = key.indexOf('['); - int arrIdx = -1; - if (bracket > 0) { - try { - arrIdx = Integer.parseInt(key.substring(bracket + 1, key.indexOf(']'))); - key = key.substring(0, bracket); - } catch (Exception ignored) {} - } - if (current instanceof Map) { - current = ((Map) current).get(key); - } else if (current instanceof List && !key.isEmpty()) { - try { - int li = Integer.parseInt(key); - current = ((List) current).size() > li ? ((List) current).get(li) : null; - } catch (NumberFormatException e) { return null; } - } else { - return null; - } - if (arrIdx >= 0 && current instanceof List) { - current = ((List) current).size() > arrIdx ? ((List) current).get(arrIdx) : null; - } - } - return current; - } - - // ==================== HTML ==================== - - private Map extractHtml(AdapterConfig config, String html, String accessUrl) { - Map result = new LinkedHashMap<>(); - Document doc = Jsoup.parse(html); - - Map.Entry matched = matchPage(config.getPages(), accessUrl); - if (matched == null) { - result.put("matched", false); - result.put("contentType", "html"); - result.put("message", "未匹配到页面规则"); - result.put("availablePages", new ArrayList<>(config.getPages() != null ? config.getPages().keySet() : Set.of())); - return result; - } - - PageConfig page = matched.getValue(); - result.put("matched", true); - result.put("contentType", "html"); - result.put("pageName", matched.getKey()); - result.put("fieldCount", page.getFields() != null ? page.getFields().size() : 0); - - List> extracted = extractRows(doc, page); - result.put("extracted", extracted); - result.put("rowCount", extracted.size()); - return result; - } - - // ==================== 页面匹配 ==================== - - private Map.Entry matchPage(Map pages, String accessUrl) { - if (pages == null || pages.isEmpty() || accessUrl == null) return null; - for (Map.Entry entry : pages.entrySet()) { - PageConfig page = entry.getValue(); - if (page.getUrl() != null) { - String a = page.getUrl().replaceAll("\\?.+", ""); - String b = accessUrl.replaceAll("\\?.+", ""); - if (b.contains(a) || a.contains(b)) return entry; - } - if (page.getUrlPattern() != null) { - try { if (Pattern.compile(page.getUrlPattern()).matcher(accessUrl).find()) return entry; } - catch (Exception ignored) {} - } - if (accessUrl.contains("/" + entry.getKey())) return entry; - } - return null; - } - - // ==================== HTML 提取 ==================== - - private List> extractRows(Document doc, PageConfig page) { - if (page.getFields() == null || page.getFields().isEmpty()) return List.of(); - - Elements rows; - if (page.getListContainer() != null && !page.getListContainer().isBlank()) { - rows = doc.select(page.getListContainer()); - } else { - rows = new Elements(); rows.add(doc); - } - - List> results = new ArrayList<>(); - for (int i = Math.max(0, page.getSkipRows()); i < rows.size(); i++) { - Element row = rows.get(i); - Map rowData = new LinkedHashMap<>(); - boolean hasData = false; - for (Map.Entry f : page.getFields().entrySet()) { - FieldExtractor fe = f.getValue(); - if (fe.getSelector() == null) continue; - Element el = row.selectFirst(fe.getSelector()); - if (el == null) { - if (fe.getOptional() == null || !fe.getOptional()) rowData.put(f.getKey(), null); - continue; - } - String raw = switch (fe.getType() != null ? fe.getType() : "text") { - case "attribute" -> fe.getAttribute() != null ? el.attr(fe.getAttribute()) : el.text(); - case "html" -> el.html(); - default -> el.text(); - }; - rowData.put(f.getKey(), applyTransform(raw, fe.getTransform())); - hasData = true; - } - if (hasData) results.add(rowData); - } - return results; - } - - // ==================== 转换器 ==================== - - private Object applyTransform(String raw, String transform) { - if (raw == null || transform == null) return raw; - try { - return switch (transform.toLowerCase()) { - case "int", "parseint" -> { String c = raw.replaceAll("[^\\d-]", ""); yield c.isEmpty() ? null : Integer.parseInt(c); } - case "long" -> { String c = raw.replaceAll("[^\\d-]", ""); yield c.isEmpty() ? null : Long.parseLong(c); } - case "double", "float" -> { String c = raw.replaceAll("[^\\d.-]", ""); yield c.isEmpty() ? null : Double.parseDouble(c); } - case "filesize" -> parseFileSize(raw); - case "boolean", "bool" -> parseBoolean(raw); - default -> raw; - }; - } catch (Exception e) { return raw; } - } - - private Long parseFileSize(String raw) { - if (raw == null) return null; - Matcher m = Pattern.compile("([\\d.]+)\\s*(GB|MB|KB|TB|B|GiB|MiB|KiB|TiB)", Pattern.CASE_INSENSITIVE).matcher(raw); - if (!m.find()) { - String n = raw.replaceAll("[^\\d.]", ""); - return n.isEmpty() ? null : (long) Double.parseDouble(n); - } - double v = Double.parseDouble(m.group(1)); - return (long) (v * switch (m.group(2).toUpperCase()) { - case "TB","TIB" -> 1024L*1024*1024*1024; - case "GB","GIB" -> 1024L*1024*1024; - case "MB","MIB" -> 1024L*1024; - case "KB","KIB" -> 1024L; - default -> 1L; - }); - } - - private Boolean parseBoolean(String raw) { - if (raw == null) return null; - String s = raw.trim().toLowerCase(); - if (s.equals("true") || s.equals("yes") || s.equals("1") || s.equals("是")) return true; - if (s.equals("false") || s.equals("no") || s.equals("0") || s.equals("否")) return false; - return null; - } - - private String toDisplayString(Object val) { - if (val == null) return "null"; - if (val instanceof String s) return s; - if (val instanceof Number || val instanceof Boolean) return val.toString(); - try { return objectMapper.writeValueAsString(val); } catch (Exception e) { return val.toString(); } - } -} diff --git a/par-core/src/main/java/com/par/core/adapter/FieldExtractor.java b/par-core/src/main/java/com/par/core/adapter/FieldExtractor.java deleted file mode 100644 index 44481e4..0000000 --- a/par-core/src/main/java/com/par/core/adapter/FieldExtractor.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.par.core.adapter; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; - -/** - * 字段提取器定义 - */ -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class FieldExtractor { - /** CSS 选择器 */ - private String selector; - /** 提取类型: text | attribute | html */ - private String type = "text"; - /** type=attribute 时指定属性名 */ - private String attribute; - /** 转换器名称 */ - private String transform; - /** 是否可选 */ - private Boolean optional; -} diff --git a/par-core/src/main/java/com/par/core/adapter/PageConfig.java b/par-core/src/main/java/com/par/core/adapter/PageConfig.java deleted file mode 100644 index 82c7b45..0000000 --- a/par-core/src/main/java/com/par/core/adapter/PageConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.par.core.adapter; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; - -import java.util.Map; - -/** - * 页面解析配置 - */ -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class PageConfig { - /** 页面 URL 或 URL 模板 */ - private String url; - /** URL 正则匹配模式 */ - private String urlPattern; - /** 请求方法 */ - private String method = "GET"; - /** 列表容器 CSS 选择器 */ - private String listContainer; - /** 跳过行数 */ - private int skipRows; - /** 字段定义 map */ - private Map fields; -} diff --git a/par-core/src/main/java/com/par/core/dto/SiteConfigDTO.java b/par-core/src/main/java/com/par/core/dto/SiteConfigDTO.java index 9d19e0a..b22893f 100644 --- a/par-core/src/main/java/com/par/core/dto/SiteConfigDTO.java +++ b/par-core/src/main/java/com/par/core/dto/SiteConfigDTO.java @@ -14,10 +14,7 @@ public class SiteConfigDTO { private Long id; private String siteId; private String version; - private String configHash; private String scriptStoragePath; - private Long fileSize; - private String schemaVersion; private ConfigStatus status; private Boolean isLatest; private Long submitterId; diff --git a/par-core/src/main/java/com/par/core/entity/SiteConfig.java b/par-core/src/main/java/com/par/core/entity/SiteConfig.java index e065258..8c6eabf 100644 --- a/par-core/src/main/java/com/par/core/entity/SiteConfig.java +++ b/par-core/src/main/java/com/par/core/entity/SiteConfig.java @@ -22,21 +22,9 @@ public class SiteConfig { @TableField("version") private String version; - @TableField("config_hash") - private String configHash; - - @TableField("storage_path") - private String storagePath; - @TableField("script_storage_path") private String scriptStoragePath; - @TableField("file_size") - private Long fileSize; - - @TableField("schema_version") - private String schemaVersion; - @TableField("status") private ConfigStatus status; diff --git a/par-core/src/main/java/com/par/core/service/ConfigService.java b/par-core/src/main/java/com/par/core/service/ConfigService.java index 6a43744..8089f42 100644 --- a/par-core/src/main/java/com/par/core/service/ConfigService.java +++ b/par-core/src/main/java/com/par/core/service/ConfigService.java @@ -31,10 +31,9 @@ public interface ConfigService { SiteConfig getConfig(String siteId, String version); /** - * 提交新配置(需审核),可选 Groovy 脚本 + * 提交新适配脚本(需审核) */ - SiteConfig submitConfig(String siteId, String version, String configJson, - String schemaVersion, String groovyScript, Long submitterId); + SiteConfig submitConfig(String siteId, String version, String groovyScript, Long submitterId); /** * 转换为 DTO diff --git a/par-core/src/main/java/com/par/core/service/impl/ConfigServiceImpl.java b/par-core/src/main/java/com/par/core/service/impl/ConfigServiceImpl.java index 987f8e7..37ac685 100644 --- a/par-core/src/main/java/com/par/core/service/impl/ConfigServiceImpl.java +++ b/par-core/src/main/java/com/par/core/service/impl/ConfigServiceImpl.java @@ -10,7 +10,6 @@ import com.par.core.storage.CachedStorageService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import java.nio.charset.StandardCharsets; import java.util.List; import java.util.stream.Collectors; @@ -54,57 +53,25 @@ public class ConfigServiceImpl implements ConfigService { } @Override - public SiteConfig submitConfig(String siteId, String version, String configJson, - String schemaVersion, String groovyScript, Long submitterId) { - boolean hasConfig = configJson != null && !configJson.isBlank(); + public SiteConfig submitConfig(String siteId, String version, String groovyScript, Long submitterId) { boolean hasScript = groovyScript != null && !groovyScript.isBlank(); - String configHash = hasConfig ? HmacUtil.sha256(configJson) : HmacUtil.sha256(groovyScript); - String idPrefix = hasConfig ? configHash.substring(0, 8) : Long.toHexString(System.currentTimeMillis()); + if (!hasScript) throw new IllegalArgumentException("groovyScript is required"); - // 相同 hash 的提交 → 真正重复,跳过 - SiteConfig sameHash = siteConfigMapper.selectOne( - new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() - .eq(SiteConfig::getSiteId, siteId) - .eq(SiteConfig::getConfigHash, configHash) - .eq(SiteConfig::getDeleted, 0)); - if (sameHash != null) { - log.info("配置未变化,跳过: siteId={}, hash={}", siteId, configHash); - return sameHash; - } - - // 存储 JSON 配置(可选) - String storagePath = null; - if (hasConfig) { - storagePath = String.format("configs/%s/pending/%s.json", siteId, idPrefix); - try { storageService.store(storagePath, configJson); } - catch (Exception e) { - log.error("存储配置文件失败 siteId={} version={}: {}", siteId, version, e.getMessage()); - throw new RuntimeException("存储配置文件失败: " + e.getMessage(), e); - } - } - - // 存储 Groovy 脚本(用脚本自己的 hash,不和 config 共用) - String scriptPath = null; - if (hasScript) { - String scriptHash = HmacUtil.sha256(groovyScript); - scriptPath = String.format("configs/%s/pending/%s.groovy", siteId, scriptHash.substring(0, 8)); - storageService.store(scriptPath, groovyScript); - } + // 存储 Groovy 脚本 + String scriptHash = HmacUtil.sha256(groovyScript); + String scriptPath = String.format("configs/%s/pending/%s.groovy", siteId, scriptHash.substring(0, 8)); + storageService.store(scriptPath, groovyScript); SiteConfig config = new SiteConfig(); config.setSiteId(siteId); config.setVersion(version); - config.setConfigHash(configHash); - config.setStoragePath(storagePath); config.setScriptStoragePath(scriptPath); - config.setFileSize((long) configJson.getBytes(StandardCharsets.UTF_8).length); - config.setSchemaVersion(schemaVersion != null ? schemaVersion : "1.0.0"); config.setStatus(ConfigStatus.PENDING); config.setSubmitterId(submitterId); config.setIsLatest(false); siteConfigMapper.insert(config); - log.info("Config submitted: siteId={}, version={}, id={}, hash={}, hasScript={}", siteId, version, config.getId(), configHash, scriptPath != null); + log.info("Config submitted: siteId={}, version={}, id={}, scriptPath={}", siteId, version, config.getId(), scriptPath); return config; } @@ -115,10 +82,7 @@ public class ConfigServiceImpl implements ConfigService { dto.setId(config.getId()); dto.setSiteId(config.getSiteId()); dto.setVersion(config.getVersion()); - dto.setConfigHash(config.getConfigHash()); dto.setScriptStoragePath(config.getScriptStoragePath()); - dto.setFileSize(config.getFileSize()); - dto.setSchemaVersion(config.getSchemaVersion()); dto.setStatus(config.getStatus()); dto.setIsLatest(config.getIsLatest()); dto.setSubmitterId(config.getSubmitterId()); diff --git a/web/src/views/Configs.vue b/web/src/views/Configs.vue index 773b58d..8871852 100644 --- a/web/src/views/Configs.vue +++ b/web/src/views/Configs.vue @@ -8,7 +8,7 @@ - +