根据产品规划设计完成技术设计和实施分解:
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友好)
531 lines
13 KiB
Markdown
531 lines
13 KiB
Markdown
# 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
|