From 04f4af1f2c33d599f0783ec0c27fc21bbccd1b37 Mon Sep 17 00:00:00 2001 From: mediabot-pt <295750538+mediabot-pt@users.noreply.github.com> Date: Tue, 30 Jun 2026 13:47:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=AB=99=E7=82=B9=E6=8F=90=E4=BA=A4/?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E7=BB=9F=E4=B8=80=E6=94=AF=E6=8C=81=E5=A4=9A?= =?UTF-8?q?=E5=90=8D=E7=A7=B0=E5=A4=9A=E5=9C=B0=E5=9D=80=E6=95=B0=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - POST /sites/{id}/submit: names[]/urls[] 数组,兼容旧 name/baseUrl 单值 - 已有站点合并新名称/地址(去重+前置) - 站点列表返回 names[]/urls[] - API 文档和代码示例同步更新 --- docs/API.md | 27 +++++++------- .../par/api/controller/SiteController.java | 35 ++++++++++++------- .../com/par/core/service/SiteService.java | 2 +- .../core/service/impl/SiteServiceImpl.java | 24 +++++++------ 4 files changed, 51 insertions(+), 37 deletions(-) diff --git a/docs/API.md b/docs/API.md index 639846e..8245e62 100644 --- a/docs/API.md +++ b/docs/API.md @@ -164,13 +164,14 @@ curl -H "If-None-Match: a1b2c3d4..." http://par:9012/api/v1/manifest #### POST /api/v1/sites/{siteId}/submit ```json -// Request +// Request (多名称/多地址) { - "name": "PThome", // 必填 - "baseUrl": "https://pthome.net", // 必填 + "names": ["PThome", "PThome镜像"], // 必填 + "urls": ["https://pthome.net", "https://pthome.mirror"], // 必填 + // 或向后兼容单值: "name": "PThome", "baseUrl": "https://..." "description": "PT 资源站", "iconUrl": "https://...", - "siteType": "private" // private/public + "siteType": "private" } // Response 200 @@ -179,8 +180,8 @@ curl -H "If-None-Match: a1b2c3d4..." http://par:9012/api/v1/manifest "data": { "id": 1, "siteId": "pthome", - "name": "PThome", - "baseUrl": "https://pthome.net", + "names": ["PThome", "PThome镜像"], + "urls": ["https://pthome.net", "https://pthome.mirror"], "siteType": "private", "schemaVersion": "1.0.0", "status": "active", @@ -282,9 +283,9 @@ curl -H "If-None-Match: a1b2c3d4..." http://par:9012/api/v1/manifest |------|------|------| | id | Long | 内部 ID | | siteId | String | 站点唯一标识(如 `pthome`) | -| name | String | 显示名称 | +| names | String[] | 站点名称列表 | +| urls | String[] | 站点地址列表 | | description | String | 描述 | -| baseUrl | String | 站点 URL | | iconUrl | String | 图标 URL | | siteType | String | `private` / `public` | | schemaVersion | String | 当前 Schema 版本 | @@ -372,10 +373,10 @@ def hmac_headers(method, path): "X-Email": EMAIL } -def submit_site(site_id, name, base_url, site_type="private"): +def submit_site(site_id, names, urls, site_type="private"): path = f"/api/v1/sites/{site_id}/submit" return requests.post(f"{BASE}{path}", - json={"name": name, "baseUrl": base_url, "siteType": site_type}, + json={"names": names, "urls": urls, "siteType": site_type}, headers=hmac_headers("POST", path) ).json() @@ -396,7 +397,7 @@ def download_config(site_id, version): return resp.json() # ---- 使用 ---- -submit_site("pthome", "PThome", "https://pthome.net") +submit_site("pthome", ["PThome", "PThome镜像"], ["https://pthome.net", "https://pthome.mirror"]) submit_config("pthome", "1.0.0", '{"rules":[...]}') print(list_configs("pthome")) ``` @@ -429,12 +430,12 @@ async function hmacHeaders(method, path) { }; } -async function submitSite(siteId, name, baseUrl, siteType = 'private') { +async function submitSite(siteId, names, urls, siteType = 'private') { const path = `/api/v1/sites/${siteId}/submit`; const res = await fetch(`${BASE}${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...await hmacHeaders('POST', path) }, - body: JSON.stringify({ name, baseUrl, siteType }) + body: JSON.stringify({ names, urls, siteType }) }); return res.json(); } diff --git a/par-api/src/main/java/com/par/api/controller/SiteController.java b/par-api/src/main/java/com/par/api/controller/SiteController.java index fe1ed54..33884f6 100644 --- a/par-api/src/main/java/com/par/api/controller/SiteController.java +++ b/par-api/src/main/java/com/par/api/controller/SiteController.java @@ -66,8 +66,8 @@ public class SiteController { } /** - * 提交站点收录(需 HMAC 鉴权) - * 创建或更新站点信息,自动设置为活跃状态 + * 提交站点收录(需 PSK 鉴权) + * 支持 names[]/urls[] 数组或 name/baseUrl 单值(向后兼容) */ @PostMapping("/{siteId}/submit") public ApiResponse submitSite( @@ -75,22 +75,31 @@ public class SiteController { @RequestBody Map payload, HttpServletRequest request) { - String name = (String) payload.get("name"); + @SuppressWarnings("unchecked") + List names = (List) payload.get("names"); + @SuppressWarnings("unchecked") + List urls = (List) payload.get("urls"); + // 向后兼容单值 + if ((names == null || names.isEmpty()) && payload.containsKey("name")) { + names = List.of((String) payload.get("name")); + } + if ((urls == null || urls.isEmpty()) && payload.containsKey("baseUrl")) { + urls = List.of((String) payload.get("baseUrl")); + } + + if (names == null || names.isEmpty()) { + return ApiResponse.error(400, "names or name is required"); + } + if (urls == null || urls.isEmpty()) { + return ApiResponse.error(400, "urls or baseUrl is required"); + } + String description = (String) payload.get("description"); - String baseUrl = (String) payload.get("baseUrl"); String iconUrl = (String) payload.get("iconUrl"); String siteType = (String) payload.get("siteType"); - - if (name == null || name.isBlank()) { - return ApiResponse.error(400, "name is required"); - } - if (baseUrl == null || baseUrl.isBlank()) { - return ApiResponse.error(400, "baseUrl is required"); - } - Long submitterId = (Long) request.getAttribute("accountId"); - Site site = siteService.submitSite(siteId, name, description, baseUrl, iconUrl, siteType, submitterId); + Site site = siteService.submitSite(siteId, names, urls, description, iconUrl, siteType, submitterId); return ApiResponse.success(siteService.toDTO(site)); } diff --git a/par-core/src/main/java/com/par/core/service/SiteService.java b/par-core/src/main/java/com/par/core/service/SiteService.java index d212433..864b7e2 100644 --- a/par-core/src/main/java/com/par/core/service/SiteService.java +++ b/par-core/src/main/java/com/par/core/service/SiteService.java @@ -29,7 +29,7 @@ public interface SiteService { /** * 提交站点信息(创建 pending,更新保持原状态) */ - Site submitSite(String siteId, String name, String description, String baseUrl, + Site submitSite(String siteId, List names, List urls, String description, String iconUrl, String siteType, Long submitterId); /** diff --git a/par-core/src/main/java/com/par/core/service/impl/SiteServiceImpl.java b/par-core/src/main/java/com/par/core/service/impl/SiteServiceImpl.java index 496ac4c..baa8dae 100644 --- a/par-core/src/main/java/com/par/core/service/impl/SiteServiceImpl.java +++ b/par-core/src/main/java/com/par/core/service/impl/SiteServiceImpl.java @@ -90,18 +90,22 @@ public class SiteServiceImpl implements SiteService { } @Override - public Site submitSite(String siteId, String name, String description, String baseUrl, - String iconUrl, String siteType, Long submitterId) { + public Site submitSite(String siteId, List names, List urls, + String description, String iconUrl, String siteType, Long submitterId) { Site existing = siteMapper.selectBySiteId(siteId); if (existing != null) { - if (name != null && !name.isBlank()) { - List names = fromJsonArray(existing.getNamesJson()); - if (!names.contains(name)) { names.add(0, name); existing.setNamesJson(toJsonArray(names)); } + // 合并名称:新名称置前,去重 + List existingNames = fromJsonArray(existing.getNamesJson()); + for (String n : names) { + if (n != null && !n.isBlank() && !existingNames.contains(n)) existingNames.add(0, n); } - if (baseUrl != null && !baseUrl.isBlank()) { - List urls = fromJsonArray(existing.getUrlsJson()); - if (!urls.contains(baseUrl)) { urls.add(0, baseUrl); existing.setUrlsJson(toJsonArray(urls)); } + existing.setNamesJson(toJsonArray(existingNames)); + // 合并地址 + List existingUrls = fromJsonArray(existing.getUrlsJson()); + for (String u : urls) { + if (u != null && !u.isBlank() && !existingUrls.contains(u)) existingUrls.add(0, u); } + existing.setUrlsJson(toJsonArray(existingUrls)); if (description != null) existing.setDescription(description); if (iconUrl != null) existing.setIconUrl(iconUrl); if (siteType != null) existing.setSiteType(SiteType.fromValue(siteType)); @@ -111,8 +115,8 @@ public class SiteServiceImpl implements SiteService { Site site = new Site(); site.setSiteId(siteId); - site.setNamesJson(toJsonArray(name != null ? List.of(name) : List.of())); - site.setUrlsJson(toJsonArray(baseUrl != null ? List.of(baseUrl) : List.of())); + site.setNamesJson(toJsonArray(names.stream().filter(n -> n != null && !n.isBlank()).toList())); + site.setUrlsJson(toJsonArray(urls.stream().filter(u -> u != null && !u.isBlank()).toList())); site.setDescription(description); site.setIconUrl(iconUrl); site.setSiteType(siteType != null ? SiteType.fromValue(siteType) : SiteType.UNKNOWN);