fix: 解决 ConfigServiceImpl 与 SiteSnapshotServiceImpl 循环依赖

- 移除 SiteSnapshotServiceImpl 对 ConfigService 的依赖,直接使用 SiteConfigMapper
- ConfigServiceImpl/SitePagesServiceImpl/RiskRuleServiceImpl 改用 ApplicationEvent 异步触发快照更新
- 新增 SnapshotUpdateEvent 和 SnapshotUpdateListener
- ParApplication 添加 @EnableAsync 支持异步事件处理
- AdminController 中 updateSnapshot 调用改为发布事件
This commit is contained in:
mediabot-pt
2026-07-07 11:17:22 +08:00
parent b25aa82c73
commit c823878ae0
9 changed files with 344 additions and 20 deletions

View File

@@ -0,0 +1,273 @@
# PAR Server 接口升级对比文档 (供 MediaBot 迁移)
> **版本**: V2.0(SiteSnapshot 重构)
> **日期**: 2026-07-07
> **面向对象**: MediaBot 开发团队
---
## 一、升级概览
本次重构将站点配置的存储和分发方式从 **"分散存储 + 运行时合并"** 升级为 **"统一快照 + 增量更新"**。核心变化:
| 维度 | 旧方案 (V1) | 新方案 (V2) |
|------|------------|------------|
| **存储** | 脚本/页面/规则分别存七牛云 | MySQL `site_snapshots` 表统一存储 + 七牛云 CDN 灾备 |
| **获取方式** | 多次请求分别获取脚本、页面、规则 | **一次请求获取完整 bundle** |
| **更新检测** | 无/各接口各自判断 | **统一 checksum + ETag/304** 增量更新 |
| **可用性** | 依赖七牛云 | 数据库为主 + 七牛云 CDN 自动灾备 |
---
## 二、新增接口(推荐 MediaBot 迁移到这些)
### 2.1 `GET /api/v1/snapshots/{siteId}` ⭐ 推荐
**用途**: 获取站点完整配置快照(一站式获取脚本 + 页面配置 + 风险规则)
**请求头**:
```
If-None-Match: {上次返回的 ETag checksum}
```
**响应 200 OK**(内容未变化时):
```json
{
"siteId": "example.com",
"generatedAt": "2026-07-07T10:30:00",
"script": "// Groovy 脚本完整源码...",
"scriptVersion": "3",
"scriptSource": "custom",
"pages": [
{
"pageKey": "INDEX",
"url": "https://example.com/torrents.php",
"method": "GET",
"...": "..."
}
],
"pagesVersion": "2",
"pagesSource": "custom",
"riskRules": [
{
"field": "title",
"operator": "contains",
"value": "xxx",
"level": "WARN"
}
],
"riskRulesVersion": 1
}
```
**响应 304 Not Modified**(内容未变化):
```
HTTP/1.1 304 Not Modified
ETag: "abc123def456..."
```
_(Body 为空,节省带宽)_
**可用性保障**:
- 优先从 MySQL 数据库读取
- 数据库不可用时自动回退到七牛云 CDN `snapshots/{siteId}.json`
- 两层级联失败返回 500
---
### 2.2 `GET /api/v1/snapshots/{siteId}/check` ⭐ 推荐
**用途**: 轻量级检查站点是否有配置更新(不传输配置内容)
**响应**:
```json
{
"code": 200,
"data": {
"checksum": "abc123def456...",
"version": 5,
"publishedAt": "2026-07-07T10:30:00"
}
}
```
**使用模式**:
1. MediaBot 先调 `/check` 获取最新 `checksum`
2. 与本地缓存的 checksum 比较
3. 不同时再调 `/snapshots/{siteId}` 拉取完整配置
---
## 三、旧接口兼容状态
以下接口**继续可用**,但推荐逐步迁移到新接口:
### 3.1 `GET /api/v1/configs/{siteId}/bundle` — ⚠️ 保持兼容
| 项目 | 说明 |
|------|------|
| **状态** | 继续可用,底层已委托给快照服务 |
| **变化** | 无破坏性变化,响应格式不变 |
| **建议** | 可继续使用,但建议迁移到 `GET /api/v1/snapshots/{siteId}` |
**数据流变化**:
```
旧: 七牛云 bundles/{siteId}.json → 返回
新: MySQL site_snapshots → 返回 (优先)
↓ 失败
七牛云 bundles/{siteId}.json / snapshots/{siteId}.json → 返回 (回退)
↓ 失败
实时构建 → 返回 (兜底)
```
### 3.2 `GET /api/v1/configs/{siteId}/script` — ⚠️ 保持兼容
| 项目 | 说明 |
|------|------|
| **状态** | 无变化,功能一致 |
| **建议** | 如需单独获取脚本,可继续使用;否则建议用 snapshot 一次性获取 |
### 3.3 `GET /api/v1/scripts/{siteId}` — ⚠️ 保持兼容
| 项目 | 说明 |
|------|------|
| **状态** | 无变化,功能一致 |
| **说明** | 仅返回 Groovy 脚本纯文本,无 JSON 包装 |
| **建议** | 如需单独获取脚本,可继续使用 |
### 3.4 `GET /api/v1/scripts/{siteId}/version` — ⚠️ 保持兼容
| 项目 | 说明 |
|------|------|
| **状态** | 无变化 |
| **响应** | `{ "version": "3", "source": "custom" }` |
| **建议** | 可用 `/snapshots/{siteId}/check` 替代,后者提供更全面的版本信息 |
### 3.5 `GET /api/v1/manifest` — ⚠️ 保持兼容
| 项目 | 说明 |
|------|------|
| **状态** | 无变化,仍支持 ETag |
| **说明** | 返回所有活跃站点配置索引(siteId、站点类型、是否有脚本/页面/规则等元信息) |
| **建议** | 继续用于获取全量站点列表和配置状态概览 |
### 3.6 `GET /api/v1/sites/{siteId}/pages` — ⚠️ 保持兼容
| 项目 | 说明 |
|------|------|
| **状态** | 无变化 |
| **建议** | 如只需页面配置,可继续使用;否则建议用 snapshot |
### 3.7 `GET /api/v1/risk-rules/{siteType}` — ⚠️ 保持兼容
| 项目 | 说明 |
|------|------|
| **状态** | 无变化,仍支持版本号增量判断 |
| **建议** | 如只需风险规则,可继续使用;否则建议用 snapshot |
---
## 四、推荐 MediaBot 迁移方案
### 方案 A:完全迁移到 Snapshot(推荐)
```
MediaBot 启动 / 定时任务:
1. GET /api/v1/manifest → 获取全量站点列表
2. 对每个站点:
a. GET /api/v1/snapshots/{siteId}/check → 获取最新 checksum
b. 与本地缓存的 checksum 比较
c. 不同时: GET /api/v1/snapshots/{siteId} → 拉取完整 bundle
d. 缓存 checksum 和完整 bundle
```
**优点**:
- 每次只需 1~2 次 HTTP 请求即可获取站点全部配置
- ETag/304 支持,配置未变时不传输内容
- 三层可用性保障(数据库 → CDN → 实时构建)
### 方案 B:渐进式迁移(兼容期)
```
1. 优先调用新接口 GET /api/v1/snapshots/{siteId}
2. 失败时 fallback 到旧接口组合:
- GET /api/v1/configs/{siteId}/script
- GET /api/v1/sites/{siteId}/pages
- GET /api/v1/risk-rules/{siteType}
```
---
## 五、响应格式对比
### 旧方式(分散获取)
需要 **3~4 次 HTTP 请求** 才能凑齐一个站点的完整配置:
```
请求 1: GET /api/v1/configs/{siteId}/script → 返回 Groovy 脚本纯文本
请求 2: GET /api/v1/sites/{siteId}/pages → 返回页面入口配置
请求 3: GET /api/v1/risk-rules/{siteType} → 返回风险规则
请求 4: (可选) GET /api/v1/manifest → 返回全局站点列表
```
### 新方式(统一快照)
**1~2 次 HTTP 请求** 获取站点完整配置:
```
请求 1: GET /api/v1/snapshots/{siteId} (带 If-None-Match) → 返回完整 bundle
- 配置未变化: 304 Not Modified (0 流量)
- 配置有变化: 200 OK + 完整 JSON
(可选) 请求 0: GET /api/v1/manifest → 获取全量站点列表
```
### Bundle JSON 响应字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `siteId` | string | 站点标识 |
| `generatedAt` | string | 快照生成时间 (ISO 8601) |
| `script` | string | Groovy 脚本完整源码 |
| `scriptVersion` | string | 脚本版本号 |
| `scriptSource` | string | `"custom"` 或 `"default"` |
| `pages` | array | 页面入口配置条目列表 |
| `pagesVersion` | string | 页面配置版本号 |
| `pagesSource` | string | `"custom"` 或 `"default"` |
| `riskRules` | array | 风险规则列表 |
| `riskRulesVersion` | integer | 风险规则版本号 |
---
## 六、鉴权说明
**无变化**。所有配置读取接口(snapshot、manifest、script、pages、risk-rules)均为**公开接口**,无需鉴权。提交类接口需 HMAC/PSK 或 JWT 认证。
---
## 七、迁移时间线建议
| 阶段 | 时间 | 操作 |
|------|------|------|
| **Phase 1** | 立即 | MediaBot 开始调用 `/snapshots/{siteId}` 作为主路径,旧接口作为 fallback |
| **Phase 2** | 稳定运行 2 周后 | 移除旧接口 fallback 逻辑 |
| **Phase 3** | 后续版本 | 视情况废弃分散获取接口(script/pages/rules 独立端点) |
---
## 八、FAQ
**Q: 旧接口会下线吗?**
A: 短期内不会。但建议尽快迁移到 snapshot 接口以获得更好的性能和可用性。
**Q: snapshot 接口响应时间会不会更长?**
A: 不会。数据库查询是单次 SQL(按 site_id + status 索引),且应用层有 Caffeine 缓存。304 场景下完全不传输内容。
**Q: 如果 PAR Server 完全挂了怎么办?**
A: Snapshot 在发布时会同步上传到七牛云 CDN (`snapshots/{siteId}.json`),MediaBot 可以直接从 CDN 读取作为终极灾备。
**Q: checksum 是用什么算法计算的?**
A: SHA-256,对完整 bundle JSON 内容计算。
**Q: bundle 和 snapshot 的区别?**
A: 同一份数据。`/configs/{siteId}/bundle` 是兼容旧路径,`/snapshots/{siteId}` 是新路径,底层数据源相同。