fix: resolve 10 code review issues

High priority:
- Fix concurrent race condition for view_count/like_count (atomic update)
- Add route request ID tracking to prevent race conditions
- Filter get_joke by status=approved (no pending content leak)
- Add error feedback for like button

Performance:
- Optimize random joke query (avoid full table sort)
- Limit page_size max to 100 (DoS prevention)

Medium:
- Add localStorage quota error handling
- Handle empty AI response gracefully
- Fix generate content title extraction

Low:
- Add rejected_jokes to stats API
- Update dashboard to show rejected count
This commit is contained in:
bwstudio
2026-06-02 20:35:08 +08:00
parent 0b43973236
commit ceed63fcb0
144 changed files with 191660 additions and 270 deletions
+149
View File
@@ -0,0 +1,149 @@
# 采集工具操作文档
## 概述
采集工具分为两个独立程序:
| 程序 | 功能 | 输出 |
|------|------|------|
| `site_finder.py` | 搜索 Bing,发现笑话聚合网站 | `joke_sites.json`(站点编号列表) |
| `site_crawler.py` | 按编号或 URL,深度翻页采集笑话 | 直接提交到 API(数据库) |
---
## 一、程序 1:发现笑话站点
搜索 Bing 获取笑话聚合网站,保存到 JSON 文件。
```bash
# 搜索并保存站点列表(默认输出到 joke_sites.json
python crawler/site_finder.py
# 指定关键词
python crawler/site_finder.py --keywords "笑话大全,冷笑话,段子"
# 指定输出文件
python crawler/site_finder.py --output my_sites.json
# 限制最多 10 个站点
python crawler/site_finder.py --max-sites 10
# 显示浏览器窗口
python crawler/site_finder.py --no-headless
```
**参数说明:**
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `--keywords` | 笑话大全,搞笑段子,冷笑话,... | 搜索关键词,逗号分隔 |
| `--output` | `joke_sites.json` | 输出 JSON 文件路径 |
| `--max-sites` | 15 | 最多保留几个站点 |
| `--no-headless` | — | 显示浏览器窗口(不加则无头模式) |
**输出格式(joke_sites.json):**
```json
[
{
"id": 1,
"url": "https://xiaohua.example.com",
"domain": "xiaohua.example.com",
"title": "笑话大全 - 爆笑来袭",
"found_at": "2026-05-31T10:30:00"
},
{
"id": 2,
"url": "https://joke.example.net",
"domain": "joke.example.net",
"title": "每日一笑",
"found_at": "2026-05-31T10:30:05"
}
]
```
已有站点会自动保留编号,新站点追加,按域名去重。
---
## 二、程序 2:深度采集指定站点
从 JSON 文件按编号加载站点,或直接指定 URL,深度采集该站点所有笑话并提交到 API。
### 按编号采集(推荐)
```bash
# 采集 #1 站点
python crawler/site_crawler.py --id 1
# 批量采集多个站点
python crawler/site_crawler.py --id 1,2,3
# 指定站点文件
python crawler/site_crawler.py --id 1 --sites-file my_sites.json
```
### 直接指定 URL
```bash
# 直接采集任意 URL
python crawler/site_crawler.py --url https://xiaohua.example.com
```
python crawler/site_crawler.py --url https://www.52xiaohua.com --no-headless
### 其他选项
```bash
# 显示浏览器窗口
python crawler/site_crawler.py --id 1 --no-headless
# 指定 API 地址
python crawler/site_crawler.py --id 1 --api-base http://192.168.1.10:8001
# 指定管理员账号
python crawler/site_crawler.py --id 1 --username admin --password admin123
```
**参数说明:**
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `--id` | — | 站点编号,多个用逗号分隔 |
| `--url` | — | 直接指定 URL(与 --id 二选一) |
| `--sites-file` | `joke_sites.json` | 站点列表文件路径 |
| `--api-base` | `http://localhost:8001` | API 服务地址 |
| `--username` | `admin` | 管理员用户名 |
| `--password` | `admin123` | 管理员密码 |
| `--no-headless` | — | 显示浏览器窗口 |
---
## 三、配合使用流程
```bash
# 1. 先启动 API
cd api && uvicorn main:app --reload --port 8001
# 2. 发现笑话站点(建议第一次用 --no-headless 观察效果)
cd crawler && python site_finder.py --no-headless
# 3. 查看生成的站点列表
cat joke_sites.json
# 4. 选择站点编号,开始深度采集
python site_crawler.py --id 1 --no-headless
# 5. 继续采集其他站点
python site_crawler.py --id 2,3 --no-headless
```
---
## 四、采集说明
- **翻页采集**:自动发现首页的翻页链接(`?page=N``/page/N/``index_N.html``下一页` 等),逐页抓取所有笑话
- **去重**:基于内容 MD5 哈希去重,同一笑话不会重复入库
- **容错**:单个页面失败自动重试 2 次;同一站点连续失败 3 次自动跳过
- **翻页限制**:每个站点最多采 30 页,避免无限抓取
- **状态**:采集的笑话默认 `pending`(待审核),需在后台管理页面审核通过后才会在前台展示