# 工学调剂动态·自动化更新系统

将调剂动态页（`news.html`）从"手动改 HTML"升级为**数据驱动 + 半自动抓取 + CI/CD 自动部署**的完整流水线。

## 架构总览

```
┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  官方公告源       │     │  GitHub Actions   │     │   Cloudflare    │
│  教育部 / 研招网  │ ──→ │  每日定时抓取     │ ──→ │   Pages 部署    │
└─────────────────┘     └──────────────────┘     └─────────────────┘
                               │ 生成草稿                ↑
                               ↓                        │ 构建
                        ┌──────────────────┐            │
                        │  news-draft.json  │            │
                        │  （人工审核）      │ ──合并──→ news.json
                        └──────────────────┘            │
                                                         ↓
                                                  ┌──────────────┐
                                                  │  build-news  │
                                                  │  生成HTML     │
                                                  └──────────────┘
```

## 文件清单

| 文件 | 作用 |
|------|------|
| `assets/data/news.json` | **正式数据源**，构建脚本读取此文件生成页面 |
| `assets/data/news-draft.json` | **草稿数据**，抓取脚本生成，待人工审核 |
| `assets/data/fetch-state.json` | 抓取状态记录（页面哈希，用于变更检测） |
| `scripts/build-news.py` | **构建脚本**：读 JSON → 生成 news.html |
| `scripts/fetch-news.py` | **抓取脚本**：监控官方源 → 生成草稿 |
| `scripts/merge-draft.py` | **合并脚本**：审核后的草稿合并到正式数据 |
| `.github/workflows/auto-update-news.yml` | **CI/CD**：定时抓取 → PR → 构建 → 部署 |

## 三种使用方式

### 方式一：纯手动（最简单，立即可用）

适合不想配置 CI/CD 的用户，本地完成全部操作。

```bash
# 1. 编辑数据源（添加/修改/删除条目）
#    直接编辑 assets/data/news.json

# 2. 构建页面
python scripts/build-news.py

# 3. 部署（按 deploy-guide.html 的任意方案部署整个站点）
```

### 方式二：半自动抓取 + 人工审核（推荐）

本地定时运行抓取脚本，审核草稿后构建。

```bash
# 1. 运行抓取脚本，检测官方源更新
python scripts/fetch-news.py

# 2. 查看生成的草稿
python scripts/merge-draft.py --list

# 3. 审核草稿：编辑 assets/data/news-draft.json
#    - 编辑每条草稿的 title/summary/category/tags
#    - 将 status 字段改为 "approved"
# 或直接命令行审核：
python scripts/merge-draft.py --approve <draft-id>

# 4. 合并已审核草稿到正式数据
python scripts/merge-draft.py

# 5. 重新构建页面
python scripts/build-news.py

# 6. 部署
```

### 方式三：全自动 CI/CD（需 GitHub 仓库）

把代码推到 GitHub，配置 Secrets 后全自动运行。

#### 配置步骤

1. **把项目推到 GitHub**（参见 `deploy-guide.html` 方案 A 的 Git 步骤）

2. **配置仓库 Secrets**（Settings → Secrets and variables → Actions）：

   | Secret 名 | 说明 | 必填 |
   |-----------|------|------|
   | `CLOUDFLARE_API_TOKEN` | Cloudflare API 令牌 | 部署到 CF 时必填 |
   | `CLOUDFLARE_ACCOUNT_ID` | Cloudflare 账户 ID | 部署到 CF 时必填 |
   | `CLOUDFLARE_PROJECT_NAME` | CF Pages 项目名 | 部署到 CF 时必填 |
   | `VERCEL_TOKEN` | Vercel 令牌（备选部署） | 用 Vercel 时填 |

   > 如不配置任何部署 Secret，工作流仍会构建并提交 news.html，你可手动拉取部署。

3. **启用 Actions**：GitHub 仓库 → Actions 标签页 → 启用 workflows

#### 自动运行流程

- **每日 08:00（北京时间）**：自动抓取官方源
  - 无变化 → 不做任何操作
  - 有变化 → 生成草稿 → 自动创建 PR（标题带 🤖 标识）
- **人工审核 PR**：在 PR 中编辑 `news-draft.json`，审核草稿内容
  - 编辑 `title/summary/category/tags`
  - 将 `status` 改为 `"approved"`
  - 合并 PR
- **PR 合并后**：自动运行 `merge-draft.py` + `build-news.py` → 部署到 Cloudflare Pages

#### 手动触发

GitHub → Actions → "自动更新调剂动态" → Run workflow

## 数据结构说明

### news.json 字段

```jsonc
{
  "site": {
    "name": "工学调剂信息网",
    "domain": "https://tiaojizhan.com",  // 部署后改为真实域名
    "updated": "2026-04-10"            // 自动更新
  },
  "categories": {                       // 分类定义
    "policy":  { "label": "政策", "color": "primary", "icon": "📋" },
    "school":  { "label": "院校", "color": "success", "icon": "🎓" },
    "operate": { "label": "操作", "color": "accent",  "icon": "⚙️" },
    "pitfall": { "label": "避坑", "color": "danger",  "icon": "⚠️" }
  },
  "items": [
    {
      "id": "n10",                      // 唯一ID，用作锚点 #n10
      "category": "pitfall",            // 对应 categories 的 key
      "title": "...",                   // 标题（支持纯文本）
      "date": "2026-04-10",             // 发布日期 YYYY-MM-DD
      "summary": "...",                 // 摘要（支持 <strong> 等 HTML 标签）
      "tags": ["截止前", "待录取"],      // 标签数组
      "tagLevel": "hot",                // 标签级别：hot/new/normal
      "source": "工学调剂信息网整理",     // 来源
      "sourceUrl": "faq.html#q-admit",  // 站内相关链接
      "sourceLabel": "查看待录取详解"     // 链接文字
    }
  ]
}
```

### 排序规则

`build-news.py` 按 JSON 中 `items` 数组的顺序渲染。建议保持**日期倒序**（最新在上）。`merge-draft.py` 合并时会自动按日期重排。

## 抓取源配置

在 `scripts/fetch-news.py` 的 `SOURCES` 列表中配置：

```python
SOURCES = [
    {
        "id": "moe_gaokao",           # 唯一标识
        "name": "教育部·政务资讯",     # 显示名
        "url": "https://www.moe.gov.cn/",  # 监控URL
        "keywords": ["调剂", "复试"],   # 关键词过滤
        "enabled": True                # 是否启用
    },
    # ... 增删其他源
]
```

### 抓取策略说明

- **页面哈希检测**：即使无法解析内容，只要页面变了就能发现
- **宽松链接提取**：提取所有含关键词的 `<a>` 链接
- **草稿机制**：新链接写入草稿，不直接发布，避免误报
- **编码兼容**：自动处理 UTF-8 / GBK 编码

### 已知限制

- 官方公告常以 **PDF/图片**发布，本脚本只能检测到"页面变了"，无法提取 PDF 内容
- 部分站点有反爬，可能返回 403/429，可在 `HEADERS` 中调整 User-Agent
- 这是**变更检测 + 草稿生成**工具，最终内容仍需人工审核编辑

## 本地调试

```bash
# 查看抓取状态
python scripts/fetch-news.py --status

# 仅检测不写草稿（CI模式）
python scripts/fetch-news.py --check

# 列出所有草稿
python scripts/merge-draft.py --list

# 审核单条草稿
python scripts/merge-draft.py --approve draft-20260416120001-1

# 重新构建页面
python scripts/build-news.py
```

## 与 SEO / AI 引用的关系

本系统的设计兼顾 SEO 与 AI 引用：

1. **构建时渲染**：`news.html` 是构建脚本生成的纯静态 HTML，爬虫与 AI 可直接解析，**不依赖 JS 执行**
2. **结构化数据自动生成**：`NewsArticle` / `ItemList` 从 JSON 派生，保证数据与结构化数据一致
3. **独立锚点**：每条动态有 `#nXX` 锚点，AI 可精确引用单条
4. **generator 标记**：页面 meta 标注 `auto-generated`，便于追踪

## 故障排查

| 问题 | 解决 |
|------|------|
| 构建脚本报错 JSON 解析失败 | 检查 `news.json` 格式（用 jsonlint.com 验证） |
| 抓取脚本报 403/429 | 调整 `HEADERS` 中的 User-Agent，或降低抓取频率 |
| GitHub Actions 不触发 | 检查仓库 Actions 是否启用；定时任务对未活动仓库可能暂停 |
| Cloudflare 部署失败 | 检查 Secrets 配置；项目需先在 CF Pages 手动创建 |
| 草稿重复合并 | `merge-draft.py` 已按标题去重，可安全重跑 |

## 升级建议

- 增加 RSS 输出：在 `build-news.py` 中额外生成 `news.xml`（RSS格式），供订阅器使用
- 增加邮件通知：在 workflow 中接入邮件服务，抓取到变化时发邮件提醒
- 接入更多源：在 `SOURCES` 增加目标院校研究生院官网
- 图片/PDF 解析：接入 OCR 服务，自动提取 PDF/图片中的公告内容（成本较高）
