项目6 · Python 学习记录 API
用 FastAPI 把课程记录、笔记和统计做成一个真实可调用的本机服务。
约 10 小时
学习重点
- 用 FastAPI 设计资源化接口:记录、笔记、统计
- 用 Pydantic 模型完成请求/响应校验与统一错误结构
- SQLite 持久化:迁移、唯一约束、事务与分页
- 用 pytest 覆盖成功、参数错误、缺失资源与重复提交
- 与前端联调:CORS、数据契约、服务不可达的处理
页面 / 接口清单
- 01GET /records?page=&size=&course= 分页查询学习记录
- 02POST /records 新增记录(校验 course/status/duration)
- 03GET /records/{id} 与 DELETE /records/{id}
- 04GET /records/{id}/notes 与 POST /records/{id}/notes 笔记
- 05GET /stats 学习统计(总课时、按状态计数、最近 7 天)
- 06GET /health 健康检查
验收清单
0 / 8(0%)
uvicorn app.main:app 启动,/docs 可打开,/health 返回 ok
缺字段、空课程名、非法状态、负时长都返回 422 且字段定位清楚
page/size 限制上限,course 筛选与总数正确,排序稳定
不存在的记录返回 404 与统一错误结构
同 id 重复创建返回 409,不产生重复行
笔记挂到记录下,删除记录级联删除笔记
总课时/按状态计数/最近 7 天来自真实记录聚合
pytest 全绿,测试使用临时数据库不碰真实数据
README(项目目录)
# 项目 6:Python 学习记录 API
用 FastAPI 做一个本机可调用的学习记录服务:课程记录、笔记与统计。
## 学习目标
- 资源化接口设计(记录 / 笔记 / 统计)
- Pydantic 请求校验与统一错误结构 `{detail: {category, message}}`
- SQLite 持久化:迁移、唯一约束、级联删除、事务
- pytest 覆盖成功、参数错误、缺失资源、重复提交
## 启动(参考实现)
```bash
cd examples/projects/p6-python-learning-api/solution
python -m pip install -r requirements.txt
uvicorn app.main:app --reload --port 8100
# 打开 http://127.0.0.1:8100/docs 调接口
```
## 测试
```bash
cd examples/projects/p6-python-learning-api/solution
python -m pytest tests -q
# 8 passed
```
测试使用临时数据库(`LEARNING_API_DB` 环境变量指向临时目录),不会碰真实数据。
## 目录
```
starter/ 可运行的起点(TODO 缺口在 app/main.py)
solution/ 完整参考实现(app/main.py + tests/test_api.py)
tests/ 本项目的验收脚本(run-checks.py:复制 solution 到临时目录跑 pytest)
docs/ api.md(接口契约)· data-model.md(表结构)
fixtures/ sample-records.json(示例数据)
```
## 验收清单
见 `content/projects/p6-python-learning-api.json` 的 8 项任务(m1-m8)。逐项对照接口行为验证:
1. 启动 + `/docs` + `/health`
2. 非法请求 422(空课程名/非法状态/负时长)
3. 分页与筛选(size 上限 50,总数正确)
4. 缺失资源 404
5. 重复 id 409
6. 笔记关联与级联删除
7. `/stats` 聚合正确
8. pytest 全绿
## 与前端联调
- 服务地址:`http://127.0.0.1:8100`
- CORS 已允许 `http://localhost:3000`
- 前端调用示例:
```js
const res = await fetch("http://127.0.0.1:8100/records?page=1&size=10");
const data = await res.json(); // { items, total, page, size }
```
服务不可达时前端应显示「服务未启动」并给出启动命令,而不是空白表格。
数据模型
records(id TEXT PK, course TEXT, status TEXT, duration_min INTEGER, note_count INTEGER, created_at TEXT)
notes(id TEXT PK, record_id TEXT FK, content TEXT, created_at TEXT)
迁移用 PRAGMA user_version 管理;records.status ∈ {planned, learning, done};外键级联删除。