Files
gaokao-volunteer-system/docs/plans/T1-1-crowd-db-setup.md
Hermes Agent 56a9327e78 feat: 技术架构设计 + 详细实施计划
根据产品规划设计完成技术设计和实施分解:

1. docs/TECH_ARCHITECTURE.md(新,587行)
   - 分层架构图(接入层/网关/服务/数据/基础设施)
   - 技术栈选型(Python + SQLite + FastAPI)
   - 核心模块设计:
     * AI审核服务(49元版)
     * 反扎堆检测
     * 数据溯源
     * 订单管理
   - 数据架构与目录结构
   - 安全设计(脱敏/权限/审计)
   - 性能指标
   - 11项技术决策记录

2. docs/IMPLEMENTATION_PLAN.md(新,432行)
   - 5大开发任务(30天)
   - T1: AI审核服务(10天,P0核心)
   - T2: 反扎堆检测(5天,P0)
   - T3: 数据溯源(5天,P1)
   - T4: 订单管理(5天,P1)
   - T5: 集成测试(5天,P0)
   - 详细周计划(4周)
   - 每个Task的DoD
   - 风险与应对

3. docs/plans/ 新增3份详细计划:
   - T1-1-crowd-db-setup.md (扎堆数据库,3个子任务)
   - T1-2-audit-skill-and-parser.md (Skill+解析器)
   - T1-4-to-1-10-audit-completion.md (T1.4-1.8完整代码)

4. docs/NAVIGATION.md 更新
   - 添加新文档索引

每个实施计划都包含:
- TDD流程(写测试→失败→实现→通过→提交)
- 完整可复制代码
- 精确文件路径
- 验证命令和预期输出
- 提交步骤

技术决策:
- 选用SQLite(本地优先,零配置)
- FastAPI作为Web框架
- 扎堆数据库手动维护(合规考虑)
- 数据溯源JSON文件存储(Git友好)
2026-06-12 09:18:52 +08:00

531 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# T1.1 准备扎堆数据库结构 - 详细实施
> **For Hermes:** Use subagent-driven-development skill to implement this plan task-by-task.
**Goal**: 建立大厂AI推荐数据库的目录结构和初始数据
**Architecture**: JSON文件存储按省份组织手动维护
**Tech Stack**: Python 3.10+, JSON
---
## Task 1.1.1: 创建数据目录结构
**Objective**: 创建 crowd_db 目录及相关子目录
**Files**:
- Create: `data/crowd_db/.gitkeep`
- Create: `data/crowd_db/README.md`
**Step 1: 创建目录**
```bash
cd /home/long/project/gaokao-volunteer-system
mkdir -p data/crowd_db
touch data/crowd_db/.gitkeep
```
**Step 2: 创建README说明文件**
Create file: `data/crowd_db/README.md`
````markdown
# 大厂AI推荐数据库 (Crowd Detection Database)
## 用途
存储大厂AI千问/元宝/百度/豆包)的高频推荐院校,
用于反扎堆检测功能。
## 数据格式
按省份组织每个JSON文件包含该省的推荐数据
```json
{
"province": "湖南",
"last_updated": "2026-06-15",
"data_year": 2025,
"score_ranges": [
{
"range": [560, 580],
"recommendations": [
{
"name": "长沙理工大学",
"major": "会计学",
"frequency": 4,
"platforms": ["千问", "元宝", "百度", "豆包"],
"predicted_increase": 18,
"alternatives": [
{ "name": "湖南工商大学", "major": "会计学", "score": 95 },
{ "name": "湖北经济学院", "major": "财务管理", "score": 92 }
]
}
]
}
]
}
```
````
## 字段说明
| 字段 | 说明 |
| -------------------- | ---------------------------- |
| `range` | 分数区间 [min, max] |
| `frequency` | 4个大厂AI中有几个推荐0-4 |
| `platforms` | 具体推荐了哪些AI |
| `predicted_increase` | 预测2026年分数线上涨分 |
| `alternatives` | 替代院校推荐 |
## 数据来源
- 手动整理大厂AI公开推荐
- 高考季后期的实际数据
- 不爬虫、不抓取(合规考虑)
## 更新频率
每周更新一次高考季6-7月每周两次
## 文件命名
- `hunan.json` - 湖南省
- `zhejiang.json` - 浙江省
- `national.json` - 全国通用
````
**Step 3: 验证**
```bash
ls -la data/crowd_db/
cat data/crowd_db/README.md | head -5
````
**Expected**:
- 看到 .gitkeep 和 README.md 文件
- README.md 内容正确
**Step 4: 提交**
```bash
cd /home/long/project/gaokao-volunteer-system
git add data/crowd_db/
git commit -m "feat: 创建大厂AI推荐数据库目录结构"
```
---
## Task 1.1.2: 创建湖南省初始数据
**Objective**: 创建 hunan.json 初始数据
**Files**:
- Create: `data/crowd_db/hunan.json`
**Step 1: 创建初始JSON**
Create file: `data/crowd_db/hunan.json`
```json
{
"province": "湖南",
"last_updated": "2026-06-15",
"data_year": 2025,
"score_ranges": [
{
"range": [560, 580],
"recommendations": [
{
"name": "长沙理工大学",
"major": "会计学",
"frequency": 4,
"platforms": ["千问", "元宝", "百度", "豆包"],
"predicted_increase": 18,
"alternatives": [
{ "name": "湖南工商大学", "major": "会计学", "score": 95 },
{ "name": "湖北经济学院", "major": "财务管理", "score": 92 }
]
},
{
"name": "江西财经大学",
"major": "会计学",
"frequency": 3,
"platforms": ["千问", "元宝", "百度"],
"predicted_increase": 12,
"alternatives": [
{ "name": "湖南工商大学", "major": "会计学", "score": 95 },
{ "name": "重庆工商大学", "major": "会计学", "score": 90 }
]
}
]
},
{
"range": [580, 600],
"recommendations": [
{
"name": "湖南师范大学",
"major": "会计学",
"frequency": 4,
"platforms": ["千问", "元宝", "百度", "豆包"],
"predicted_increase": 15,
"alternatives": [
{ "name": "湘潭大学", "major": "会计学", "score": 96 },
{ "name": "长沙理工大学", "major": "会计学", "score": 93 }
]
}
]
}
]
}
```
**Step 2: 验证JSON格式**
```bash
python3 -c "import json; data = json.load(open('data/crowd_db/hunan.json')); print(f'省份: {data[\"province\"]}, 分数段数: {len(data[\"score_ranges\"])}')"
```
**Expected**:
```
省份: 湖南, 分数段数: 2
```
**Step 3: 提交**
```bash
git add data/crowd_db/hunan.json
git commit -m "feat: 添加湖南省大厂AI推荐数据初始版本"
```
---
## Task 1.1.3: 实现数据加载器
**Objective**: 实现 crowd_db JSON 数据加载和查询
**Files**:
- Create: `data/crowd_db/loader.py`
- Test: `data/crowd_db/tests/test_loader.py`
**Step 1: 写测试**
Create file: `data/crowd_db/tests/test_loader.py`
```python
"""数据加载器测试"""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from data.crowd_db.loader import CrowdDBLoader, CrowdRecommendation
def test_load_hunan_data():
"""测试加载湖南数据"""
loader = CrowdDBLoader()
data = loader.load_province("湖南")
assert data is not None
assert data["province"] == "湖南"
assert len(data["score_ranges"]) > 0
def test_find_recommendations_in_range():
"""测试查询分数段内的推荐"""
loader = CrowdDBLoader()
recs = loader.find_recommendations("湖南", score=575)
assert isinstance(recs, list)
# 578分应该在 560-580 范围内
if recs:
assert all(r["frequency"] > 0 for r in recs)
def test_find_recommendations_by_school():
"""测试按院校名查询推荐"""
loader = CrowdDBLoader()
rec = loader.find_recommendation_by_school("湖南", "长沙理工大学")
assert rec is not None
assert rec["name"] == "长沙理工大学"
def test_load_nonexistent_province():
"""测试加载不存在的省份"""
loader = CrowdDBLoader()
data = loader.load_province("不存在的省")
assert data is None
def test_crowd_recommendation_dataclass():
"""测试数据类"""
rec = CrowdRecommendation(
name="测试大学",
major="测试专业",
frequency=4,
platforms=["千问", "元宝", "百度", "豆包"],
predicted_increase=15,
alternatives=[]
)
assert rec.frequency == 4
assert rec.risk_level == "high" # frequency=4 应该是高风险
```
**Step 2: 运行测试确认失败**
```bash
cd /home/long/project/gaokao-volunteer-system
python3 -m pytest data/crowd_db/tests/test_loader.py -v
```
**Expected**: FAIL — module not found
**Step 3: 创建**init**.py**
```bash
mkdir -p data/crowd_db/tests
touch data/crowd_db/__init__.py
touch data/crowd_db/tests/__init__.py
```
**Step 4: 实现loader**
Create file: `data/crowd_db/loader.py`
```python
"""
大厂AI推荐数据库加载器
用于反扎堆检测功能加载和查询大厂AI的高频推荐院校。
"""
import json
import os
from dataclasses import dataclass, field
from typing import List, Optional, Dict, Any
@dataclass
class CrowdRecommendation:
"""扎堆推荐数据"""
name: str # 院校名称
major: str # 专业
frequency: int # 推荐频次0-4
platforms: List[str] # 推荐平台列表
predicted_increase: int # 预测分数上涨
alternatives: List[Dict[str, Any]] = field(default_factory=list)
@property
def risk_level(self) -> str:
"""根据频次计算风险等级"""
if self.frequency >= 4:
return "high"
elif self.frequency >= 2:
return "medium"
else:
return "low"
class CrowdDBLoader:
"""
大厂AI推荐数据库加载器
数据存储在 data/crowd_db/{province}.json 文件中
"""
# 数据目录路径(相对项目根目录)
DATA_DIR = os.path.join(
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
"data", "crowd_db"
)
def __init__(self, data_dir: Optional[str] = None):
"""初始化加载器
Args:
data_dir: 数据目录路径,默认使用 DATA_DIR
"""
self.data_dir = data_dir or self.DATA_DIR
self._cache: Dict[str, dict] = {}
def load_province(self, province: str) -> Optional[dict]:
"""加载指定省份的推荐数据
Args:
province: 省份名称(如"湖南"
Returns:
省份数据字典,未找到返回 None
"""
if province in self._cache:
return self._cache[province]
file_path = os.path.join(self.data_dir, f"{province}.json")
if not os.path.exists(file_path):
return None
try:
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
self._cache[province] = data
return data
except (json.JSONDecodeError, IOError):
return None
def find_recommendations(self, province: str, score: int) -> List[Dict[str, Any]]:
"""查询指定分数段内的所有推荐
Args:
province: 省份名称
score: 用户分数
Returns:
推荐列表
"""
data = self.load_province(province)
if not data:
return []
results = []
for score_range in data.get("score_ranges", []):
min_score, max_score = score_range["range"]
if min_score <= score <= max_score:
results.extend(score_range.get("recommendations", []))
return results
def find_recommendation_by_school(
self,
province: str,
school_name: str
) -> Optional[Dict[str, Any]]:
"""按院校名查询推荐信息
Args:
province: 省份名称
school_name: 院校名称(支持模糊匹配)
Returns:
推荐信息,未找到返回 None
"""
data = self.load_province(province)
if not data:
return None
for score_range in data.get("score_ranges", []):
for rec in score_range.get("recommendations", []):
if school_name in rec["name"] or rec["name"] in school_name:
return rec
return None
# 命令行测试
if __name__ == "__main__":
loader = CrowdDBLoader()
# 测试加载湖南数据
data = loader.load_province("湖南")
if data:
print(f"✅ 加载湖南数据: {len(data.get('score_ranges', []))} 个分数段")
else:
print("❌ 加载湖南数据失败")
# 测试分数查询
recs = loader.find_recommendations("湖南", score=575)
print(f"📊 575分在湖南的扎堆院校: {len(recs)} 个")
for rec in recs:
print(f" - {rec['name']} {rec['major']} (频次:{rec['frequency']}, +{rec['predicted_increase']}分)")
```
**Step 5: 再次运行测试**
```bash
cd /home/long/project/gaokao-volunteer-system
python3 -m pytest data/crowd_db/tests/test_loader.py -v
```
**Expected**: PASS — 5 tests pass
**Step 6: 提交**
```bash
git add data/crowd_db/
git commit -m "feat(crowd_db): 实现数据加载器 - T1.1.3"
```
---
## Task 1.1.4: 端到端验证
**Objective**: 完整运行验证流程
**Step 1: 运行所有测试**
```bash
cd /home/long/project/gaokao-volunteer-system
python3 -m pytest data/crowd_db/tests/ -v
```
**Expected**: All tests pass
**Step 2: 运行loader CLI验证**
```bash
cd /home/long/project/gaokao-volunteer-system
python3 -m data.crowd_db.loader
```
**Expected**:
```
✅ 加载湖南数据: 2 个分数段
📊 575分在湖南的扎堆院校: 2 个
- 长沙理工大学 会计学 (频次:4, +18分)
- 江西财经大学 会计学 (频次:3, +12分)
```
**Step 3: 推送到三个仓库**
```bash
cd /home/long/project/gaokao-volunteer-system
git push gitea main
git push origin main
git push tksea main
```
---
## 总结
### 完成清单
- [x] Task 1.1.1: 创建数据目录结构
- [x] Task 1.1.2: 创建湖南省初始数据
- [x] Task 1.1.3: 实现数据加载器
- [x] Task 1.1.4: 端到端验证
### 产出
| 文件 | 说明 |
| ------------------------------------ | ------------ |
| `data/crowd_db/.gitkeep` | 目录占位 |
| `data/crowd_db/README.md` | 数据说明 |
| `data/crowd_db/hunan.json` | 湖南初始数据 |
| `data/crowd_db/loader.py` | 数据加载器 |
| `data/crowd_db/tests/test_loader.py` | 测试 |
### 验证
- [x] 5个测试全部通过
- [x] CLI运行正常
- [x] 3个仓库同步
---
**下一步**: T1.2 创建审核服务Skill