453 lines
17 KiB
Markdown
453 lines
17 KiB
Markdown
# 学生管理系统 — 优化文档 v1.0
|
||||
|
|
|
|||
|
|
> 本文档针对当前 V1.0 代码实现,梳理存在的问题、提出优化方案,按优先级排序,供后续迭代参考。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 一、当前问题清单
|
|||
|
|
|
|||
|
|
### 1. 数据库层
|
|||
|
|
|
|||
|
|
#### [P0] database.py 硬编码 SQLite,未支持多数据源
|
|||
|
|
**现状:**
|
|||
|
|
```python
|
|||
|
|
DATABASE_URL = "sqlite:///./sms.db"
|
|||
|
|
```
|
|||
|
|
**问题:** 开发/测试/生产环境共用同一配置,切换数据库需改代码重启。
|
|||
|
|
**影响:** README 中明确规划了 dev→prod 的多数据源适配,当前未实现。
|
|||
|
|
**优化方案:**
|
|||
|
|
- 引入 `pydantic-settings` 或 `python-dotenv` 读取 `.env` 环境变量
|
|||
|
|
- 根据 `ENV` 环境变量自动切换连接串
|
|||
|
|
- 支持 MySQL / PostgreSQL 驱动切换
|
|||
|
|
|
|||
|
|
#### [P0] 软删除未更新 delete_time
|
|||
|
|
**现状:** `students.py`、`scores.py`、`employment.py` 中软删除只设 `is_deleted=True`,`delete_time` 始终为 NULL;仅 `teachers.py` 正确设置了 `delete_time`。
|
|||
|
|
**问题:** 审计字段不一致,删除追溯能力缺失。
|
|||
|
|
**优化方案:** 统一在 BaseModel 或基类方法中处理 `delete_time` 赋值,所有删除接口复用同一逻辑。
|
|||
|
|
|
|||
|
|
#### [P1] 缺少 Alembic 迁移工具
|
|||
|
|
**现状:** 建表依赖 `Base.metadata.create_all()`,每次启动都会执行,生产环境不安全。
|
|||
|
|
**问题:** 无法做版本控制、灰度发布、回滚。
|
|||
|
|
**优化方案:** 引入 Alembic,将 `init_db()` 改为迁移脚本,seed 数据单独管理。
|
|||
|
|
|
|||
|
|
#### [P1] 索引设计有冗余和缺失
|
|||
|
|
**现状:**
|
|||
|
|
- `teachers.num` 同时有 `unique=True`(自带索引)和代码中手动加 `index=True`,重复
|
|||
|
|
- `students` 表缺少 `(is_deleted, class_id)` 联合索引的显式定义(虽然代码中写了但 ORM 未强制)
|
|||
|
|
- `employment` 表缺少 `(class_id, is_deleted)` 复合索引的实际生效确认
|
|||
|
|
**优化方案:** 清理重复索引,在模型 `__table_args__` 中统一声明复合索引,与 DDL 文档对齐。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. API 层
|
|||
|
|
|
|||
|
|
#### [P0] 缺少统一异常处理和响应格式
|
|||
|
|
**现状:** 每个 router 独立 `raise HTTPException`,错误响应格式不一致(有的返回 `{"detail": "..."}`,有的返回 plain string)。
|
|||
|
|
**问题:** 前端难以统一处理错误,移动端/第三方接入体验差。
|
|||
|
|
**优化方案:**
|
|||
|
|
```python
|
|||
|
|
# 新增 utils/exceptions.py
|
|||
|
|
class SMSException(Exception):
|
|||
|
|
def __init__(self, code: int, message: str, detail: Any = None):
|
|||
|
|
self.code = code
|
|||
|
|
self.message = message
|
|||
|
|
self.detail = detail
|
|||
|
|
|
|||
|
|
@app.exception_handler(SMSException)
|
|||
|
|
async def sms_exception_handler(request, exc):
|
|||
|
|
return JSONResponse(
|
|||
|
|
status_code=exc.code,
|
|||
|
|
content={"code": exc.code, "message": exc.message, "detail": exc.detail},
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### [P0] 分页参数 skip/limit 未做边界校验
|
|||
|
|
**现状:** `limit` 最大无上限,攻击者可传 `?limit=999999` 导致内存溢出。
|
|||
|
|
**问题:** 缺乏防护,存在 DoS 风险。
|
|||
|
|
**优化方案:**
|
|||
|
|
```python
|
|||
|
|
skip: int = Query(0, ge=0),
|
|||
|
|
limit: int = Query(50, ge=1, le=200), # 默认50,最大200
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### [P1] 列表接口无总数返回
|
|||
|
|
**现状:** GET 列表只返回 `List[Model]`,前端无法实现完整分页(无 total count)。
|
|||
|
|
**问题:** 需要额外发一次 `COUNT` 请求,增加网络开销。
|
|||
|
|
**优化方案:** 返回 `{ "data": [...], "total": int, "skip": int, "limit": int }` 结构。
|
|||
|
|
|
|||
|
|
#### [P1] CORS 全开放,生产不安全
|
|||
|
|
**现状:**
|
|||
|
|
```python
|
|||
|
|
allow_origins=["*"], allow_credentials=True
|
|||
|
|
```
|
|||
|
|
**问题:** `*` 与 `allow_credentials=True` 冲突(浏览器会拒绝),且生产环境不应放行所有来源。
|
|||
|
|
**优化方案:** 通过环境变量配置允许的来源列表,生产环境严格限制。
|
|||
|
|
|
|||
|
|
#### [P1] 统计接口存在 N+1 查询问题
|
|||
|
|
**现状:** `stats.py` 的 `employment_stats` 中,对每个班级单独再发一次查询计算 `avg_duration_days`:
|
|||
|
|
```python
|
|||
|
|
for r in results:
|
|||
|
|
durations = db.query(...).filter(Employment.class_id == r.class_id, ...).all()
|
|||
|
|
```
|
|||
|
|
**问题:** N 个班级触发 N+1 次查询,数据量增长后性能急剧下降。
|
|||
|
|
**优化方案:** 一次查询获取所有班级的周期数据,在内存中分组聚合。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3. 业务逻辑层
|
|||
|
|
|
|||
|
|
#### [P0] 缺少用户认证与权限控制
|
|||
|
|
**现状:** 所有接口无需任何凭证即可访问和修改数据。
|
|||
|
|
**问题:** 任何知道 URL 的人都可以增删改查全部数据,存在严重安全隐患。
|
|||
|
|
**优化方案(V2.0):**
|
|||
|
|
- 引入 JWT OAuth2 认证(FastAPI 内置 `OAuth2PasswordBearer`)
|
|||
|
|
- 基于角色的访问控制(RBAC):教务管理员 / 老师 / 顾问
|
|||
|
|
- 敏感接口(写操作)强制鉴权,统计接口可匿名访问
|
|||
|
|
|
|||
|
|
#### [P1] 缺少数据校验规则
|
|||
|
|
**现状:** Pydantic Schema 未对关键字段做格式约束。
|
|||
|
|
**问题示例:**
|
|||
|
|
- `age` 可以为负数或超大值
|
|||
|
|
- `score` 可以为负数或超过合理范围(如 200 分)
|
|||
|
|
- `num`(编号)无格式校验,可能输入非法字符
|
|||
|
|
**优化方案:**
|
|||
|
|
```python
|
|||
|
|
from pydantic import Field, field_validator
|
|||
|
|
|
|||
|
|
class StudentCreate(BaseModel):
|
|||
|
|
age: int = Field(6, ge=6, le=100)
|
|||
|
|
sex: int = Field(0, ge=0, le=2)
|
|||
|
|
score: Decimal = Field(..., ge=0, le=100)
|
|||
|
|
|
|||
|
|
@field_validator('num')
|
|||
|
|
@classmethod
|
|||
|
|
def validate_num(cls, v):
|
|||
|
|
if not re.match(r'^[A-Za-z0-9_-]{1,30}$', v):
|
|||
|
|
raise ValueError('编号只能包含字母、数字、下划线和短横线')
|
|||
|
|
return v
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### [P1] 删除操作无二次确认(业务层)
|
|||
|
|
**现状:** 删除学生时,级联删除其所有成绩和就业记录,无前置检查。
|
|||
|
|
**问题:** 一旦误删,数据不可恢复(软删除也无法区分级联删除和手动删除)。
|
|||
|
|
**优化方案:**
|
|||
|
|
- 删除学生前检查是否有关联成绩/就业记录,给出警告
|
|||
|
|
- 提供"彻底删除"和"软删除"两种模式供管理员选择
|
|||
|
|
|
|||
|
|
#### [P2] seed 数据硬编码在 Python 中,不可维护
|
|||
|
|
**现状:** `seed.py` 包含大量内联数据,与 DDL 文档中的造数 SQL 分离。
|
|||
|
|
**问题:** 两份数据容易不同步,新成员难以快速理解测试数据结构。
|
|||
|
|
**优化方案:** 将种子数据统一迁移到 SQL 文件(`seed/data.sql`),由 Alembic 迁移或独立脚本导入。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4. 前端层
|
|||
|
|
|
|||
|
|
#### [P1] 静态页面与后端耦合度高
|
|||
|
|
**现状:** `static/index.html` 中 `API = 'http://localhost:8000/api'` 硬编码。
|
|||
|
|
**问题:** 部署到测试/生产环境需改代码重新构建。
|
|||
|
|
**优化方案:**
|
|||
|
|
- 通过环境变量注入 API 地址:`const API_BASE = window.API_BASE || 'http://localhost:8000/api';`
|
|||
|
|
- 或在 Nginx 层做代理转发,前端直接请求相对路径 `/api/...`
|
|||
|
|
|
|||
|
|
#### [P1] 缺少加载状态和错误提示
|
|||
|
|
**现状:** 所有接口调用无 loading 状态,失败时无用户提示。
|
|||
|
|
**问题:** 用户体验差,网络异常时页面卡死无反馈。
|
|||
|
|
**优化方案:** 添加统一的请求拦截器,显示 loading spinner 和错误 toast。
|
|||
|
|
|
|||
|
|
#### [P2] 前端无路由,单页切换靠 display 控制
|
|||
|
|
**现状:** 所有模块在一个 HTML 文件中,用 `style.display` 切换。
|
|||
|
|
**问题:** 无法书签共享特定页面,SEO 不支持,体积随功能增长而膨胀。
|
|||
|
|
**优化方案:** V2.0 迁移至 Vue3 + Vue Router,组件化开发。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 5. 运维与工程化
|
|||
|
|
|
|||
|
|
#### [P0] 缺少健康检查端点
|
|||
|
|
**现状:** 无 `/health` 接口,负载均衡器无法检测服务状态。
|
|||
|
|
**问题:** 容器编排(K8s/Docker Swarm)无法做存活探针,故障实例无法自动剔除。
|
|||
|
|
**优化方案:**
|
|||
|
|
```python
|
|||
|
|
from sqlalchemy import text
|
|||
|
|
|
|||
|
|
@app.get("/health")
|
|||
|
|
async def health_check(db: Session = Depends(get_db)):
|
|||
|
|
db.execute(text("SELECT 1"))
|
|||
|
|
return {"status": "healthy", "timestamp": datetime.utcnow().isoformat()}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### [P1] 缺少日志系统
|
|||
|
|
**现状:** 无日志配置,生产环境无法追踪请求链路。
|
|||
|
|
**问题:** 故障排查只能靠打印到终端,线上无法定位问题。
|
|||
|
|
**优化方案:**
|
|||
|
|
- 引入 `loguru`,配置请求日志中间件
|
|||
|
|
- 记录:请求方法、路径、状态码、耗时、客户端 IP
|
|||
|
|
- 日志按天轮转,保留 30 天
|
|||
|
|
|
|||
|
|
#### [P1] 缺少单元测试
|
|||
|
|
**现状:** 无任何测试代码。
|
|||
|
|
**问题:** 修改功能无法验证回归,重构风险高。
|
|||
|
|
**优化方案:**
|
|||
|
|
- 引入 `pytest` + `httpx`(异步测试客户端)
|
|||
|
|
- 覆盖核心 CRUD 接口和统计接口
|
|||
|
|
- CI 流水线中自动运行测试
|
|||
|
|
|
|||
|
|
#### [P2] 缺少 API 文档自动化
|
|||
|
|
**现状:** FastAPI 自动生成 Swagger,但无 Postman/OpenAPI 导出。
|
|||
|
|
**问题:** 前端联调时无法快速导入接口列表。
|
|||
|
|
**优化方案:**
|
|||
|
|
- 启动时导出 OpenAPI JSON 到 `docs/openapi.json`
|
|||
|
|
- 提供 Postman Collection 下载端点
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 二、优化实施优先级矩阵
|
|||
|
|
|
|||
|
|
| 优先级 | 类别 | 优化项 | 预计工作量 | 建议阶段 |
|
|||
|
|
|--------|------|--------|-----------|----------|
|
|||
|
|
| P0 | 安全 | 用户认证(JWT/OAuth2) | 3-5天 | V2.0 |
|
|||
|
|
| P0 | 安全 | 统一异常处理 | 0.5天 | V2.0 第1周 |
|
|||
|
|
| P0 | 数据 | 多数据源配置(.env) | 1天 | V2.0 第1周 |
|
|||
|
|
| P0 | 数据 | 统一软删除 delete_time | 0.5天 | V2.0 第1周 |
|
|||
|
|
| P0 | 运维 | 健康检查端点 | 0.5天 | V2.0 第1周 |
|
|||
|
|
| P0 | 性能 | 统计接口 N+1 查询修复 | 1天 | V2.0 第2周 |
|
|||
|
|
| P1 | API | 分页参数边界校验 | 0.5天 | V2.0 第1周 |
|
|||
|
|
| P1 | API | 列表接口返回总数 | 1天 | V2.0 第2周 |
|
|||
|
|
| P1 | API | CORS 生产环境收紧 | 0.5天 | V2.0 第1周 |
|
|||
|
|
| P1 | 业务 | 数据校验规则完善 | 2天 | V2.0 第2周 |
|
|||
|
|
| P1 | 业务 | 删除前关联检查 | 1天 | V2.0 第2周 |
|
|||
|
|
| P1 | 数据库 | Alembic 迁移工具 | 2天 | V2.0 第3周 |
|
|||
|
|
| P1 | 数据库 | 索引清理与对齐 | 0.5天 | V2.0 第1周 |
|
|||
|
|
| P1 | 运维 | 日志系统(loguru) | 1天 | V2.0 第2周 |
|
|||
|
|
| P1 | 运维 | 单元测试框架 | 2天 | V2.0 第3周 |
|
|||
|
|
| P2 | 前端 | API 地址环境变量化 | 0.5天 | V2.0 第1周 |
|
|||
|
|
| P2 | 前端 | 加载状态与错误提示 | 1天 | V2.0 第2周 |
|
|||
|
|
| P2 | 前端 | Vue3 重构(可选) | 10-15天 | V3.0 |
|
|||
|
|
| P2 | 运维 | 种子数据 SQL 化 | 1天 | V2.0 第2周 |
|
|||
|
|
| P2 | 运维 | API 文档导出 | 0.5天 | V2.0 第1周 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 三、V2.0 版本规划(特色需求)
|
|||
|
|
|
|||
|
|
### 3.1 模块新增
|
|||
|
|
|
|||
|
|
| 模块 | 说明 | 接口前缀 |
|
|||
|
|
|------|------|----------|
|
|||
|
|
| 用户认证 | JWT Token 登录/刷新/注销 | `/api/auth` |
|
|||
|
|
| 部门管理 | 组织架构树,老师归属部门 | `/api/departments` |
|
|||
|
|
| 顾问管理 | 顾问老师与学生的绑定关系(当前 advisor_id 已存在,需独立模块化管理) | `/api/advisors` |
|
|||
|
|
| 数据源配置 | 多环境数据库配置管理(仅管理员可见) | `/api/config` |
|
|||
|
|
|
|||
|
|
### 3.2 统计增强
|
|||
|
|
|
|||
|
|
| 功能 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| 趋势分析 | 成绩/就业率按月/季度趋势折线图数据接口 |
|
|||
|
|
| 导出功能 | 统计结果导出为 Excel/CSV |
|
|||
|
|
| 不及格预警 | 连续两次不及格学生自动标记,推送消息 |
|
|||
|
|
|
|||
|
|
### 3.3 AI 能力(V3.0 预留)
|
|||
|
|
|
|||
|
|
| 功能 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| 自然语言查询 | 输入"三班平均分多少",自动转为 SQL 查询 |
|
|||
|
|
| 智能报告 | 定期自动生成班级学情报告(PDF) |
|
|||
|
|
| 知识库 | 教培行业常见问题 FAQ 智能问答 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 四、数据库优化专项
|
|||
|
|
|
|||
|
|
### 4.1 字典表落地(V2.0)
|
|||
|
|
|
|||
|
|
当前 `sex`、`education`、`coach_area` 等字段存的是裸值,需升级为字典表驱动:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 迁移脚本(在 Alembic migration 中执行)
|
|||
|
|
ALTER TABLE students ADD COLUMN sex_code VARCHAR(10) AFTER sex;
|
|||
|
|
ALTER TABLE students ADD COLUMN education_code VARCHAR(20) AFTER education;
|
|||
|
|
UPDATE students SET sex_code = CAST(sex AS CHAR) WHERE sex IS NOT NULL;
|
|||
|
|
UPDATE students SET education_code =
|
|||
|
|
CASE education
|
|||
|
|
WHEN '本科' THEN 'bachelor'
|
|||
|
|
WHEN '硕士' THEN 'master'
|
|||
|
|
WHEN '博士' THEN 'phd'
|
|||
|
|
ELSE education_code
|
|||
|
|
END;
|
|||
|
|
-- 后续版本再移除裸值字段
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.2 大表归档策略(V3.0)
|
|||
|
|
|
|||
|
|
当学生表超过 100 万行时启用:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 创建归档表
|
|||
|
|
CREATE TABLE students_archive LIKE students;
|
|||
|
|
CREATE TABLE scores_archive LIKE scores;
|
|||
|
|
|
|||
|
|
-- 归档毕业超过 2 年的学生
|
|||
|
|
INSERT INTO students_archive
|
|||
|
|
SELECT * FROM students
|
|||
|
|
WHERE is_deleted = 0
|
|||
|
|
AND graduate_time IS NOT NULL
|
|||
|
|
AND graduate_time < DATE_SUB(NOW(), INTERVAL 2 YEAR);
|
|||
|
|
|
|||
|
|
-- 归档后从主表删除(谨慎操作,先备份)
|
|||
|
|
DELETE FROM students
|
|||
|
|
WHERE id IN (SELECT id FROM students_archive);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 五、运维优化专项
|
|||
|
|
|
|||
|
|
### 5.1 项目目录结构调整
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
student_manage_system/
|
|||
|
|
├── app/ # 应用主体(重构后)
|
|||
|
|
│ ├── __init__.py
|
|||
|
|
│ ├── main.py # 应用入口
|
|||
|
|
│ ├── config.py # 配置管理(.env 读取)
|
|||
|
|
│ ├── database.py # DB 连接
|
|||
|
|
│ ├── models/ # ORM 模型(按模块分包)
|
|||
|
|
│ │ ├── __init__.py
|
|||
|
|
│ │ ├── teacher.py
|
|||
|
|
│ │ ├── student.py
|
|||
|
|
│ │ ├── class.py
|
|||
|
|
│ │ ├── score.py
|
|||
|
|
│ │ └── employment.py
|
|||
|
|
│ ├── schemas/ # Pydantic Schema
|
|||
|
|
│ │ ├── __init__.py
|
|||
|
|
│ │ └── ...
|
|||
|
|
│ ├── routers/ # API 路由
|
|||
|
|
│ │ ├── __init__.py
|
|||
|
|
│ │ ├── auth.py # 新增
|
|||
|
|
│ │ └── ...
|
|||
|
|
│ ├── services/ # 业务逻辑层(新增)
|
|||
|
|
│ │ └── ...
|
|||
|
|
│ └── utils/ # 工具函数
|
|||
|
|
│ ├── exceptions.py # 新增:统一异常
|
|||
|
|
│ └── logging.py # 新增:日志配置
|
|||
|
|
├── tests/ # 新增:测试目录
|
|||
|
|
│ ├── __init__.py
|
|||
|
|
│ ├── test_teachers.py
|
|||
|
|
│ ├── test_students.py
|
|||
|
|
│ └── conftest.py
|
|||
|
|
├── alembic/ # 新增:数据库迁移
|
|||
|
|
│ ├── versions/
|
|||
|
|
│ └── env.py
|
|||
|
|
├── seed/ # 新增:种子数据 SQL
|
|||
|
|
│ └── data.sql
|
|||
|
|
├── static/ # 保持现有
|
|||
|
|
├── docs/ # 保持现有
|
|||
|
|
├── .env.example # 新增:环境变量模板
|
|||
|
|
├── .env.local # 新增:本地开发配置(.gitignore)
|
|||
|
|
├── Dockerfile
|
|||
|
|
├── docker-compose.yml
|
|||
|
|
├── pyproject.toml
|
|||
|
|
└── README.md
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.2 .env 配置模板
|
|||
|
|
|
|||
|
|
```env
|
|||
|
|
# 环境:development / staging / production
|
|||
|
|
APP_ENV=development
|
|||
|
|
|
|||
|
|
# 数据库
|
|||
|
|
DATABASE_URL=mysql+pymysql://sms_user:SMS_pass@db-host:3306/sms
|
|||
|
|
# 或开发环境
|
|||
|
|
# DATABASE_URL=sqlite:///./sms.db
|
|||
|
|
|
|||
|
|
# 应用
|
|||
|
|
APP_HOST=0.0.0.0
|
|||
|
|
APP_PORT=8000
|
|||
|
|
DEBUG=true
|
|||
|
|
|
|||
|
|
# JWT(生产必须配置)
|
|||
|
|
SECRET_KEY=your-random-secret-key-here
|
|||
|
|
ALGORITHM=HS256
|
|||
|
|
ACCESS_TOKEN_EXPIRE_MINUTES=1440
|
|||
|
|
|
|||
|
|
# CORS(生产填写具体域名)
|
|||
|
|
ALLOWED_ORIGINS=https://sms.example.com,https://admin.sms.example.com
|
|||
|
|
|
|||
|
|
# 日志
|
|||
|
|
LOG_LEVEL=INFO
|
|||
|
|
LOG_FILE=/var/log/sms/app.log
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.3 Docker Compose 生产版
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
version: "3.9"
|
|||
|
|
|
|||
|
|
services:
|
|||
|
|
app:
|
|||
|
|
build: .
|
|||
|
|
environment:
|
|||
|
|
- DATABASE_URL=${DATABASE_URL}
|
|||
|
|
- SECRET_KEY=${SECRET_KEY}
|
|||
|
|
- ALLOWED_ORIGINS=${ALLOWED_ORIGINS}
|
|||
|
|
volumes:
|
|||
|
|
- ./logs:/app/logs
|
|||
|
|
depends_on:
|
|||
|
|
db:
|
|||
|
|
condition: service_healthy
|
|||
|
|
restart: unless-stopped
|
|||
|
|
|
|||
|
|
db:
|
|||
|
|
image: mysql:8.0
|
|||
|
|
environment:
|
|||
|
|
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
|
|||
|
|
MYSQL_DATABASE: sms
|
|||
|
|
MYSQL_USER: sms_user
|
|||
|
|
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
|||
|
|
volumes:
|
|||
|
|
- sms_mysql_data:/var/lib/mysql
|
|||
|
|
- ./seed/data.sql:/docker-entrypoint-initdb.d/init.sql
|
|||
|
|
ports:
|
|||
|
|
- "127.0.0.1:3306:3306" # 仅本地可访问
|
|||
|
|
restart: unless-stopped
|
|||
|
|
healthcheck:
|
|||
|
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "sms_user", "-p${MYSQL_PASSWORD}"]
|
|||
|
|
interval: 10s
|
|||
|
|
timeout: 5s
|
|||
|
|
retries: 5
|
|||
|
|
|
|||
|
|
volumes:
|
|||
|
|
sms_mysql_data:
|
|||
|
|
driver: local
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 六、接口变更清单(V2.0)
|
|||
|
|
|
|||
|
|
| 接口 | 变更类型 | 变更内容 |
|
|||
|
|
|------|----------|----------|
|
|||
|
|
| `POST /api/auth/login` | **新增** | 用户名密码登录,返回 JWT |
|
|||
|
|
| `POST /api/auth/refresh` | **新增** | 刷新 Token |
|
|||
|
|
| `POST /api/auth/logout` | **新增** | 注销(黑名单 Token) |
|
|||
|
|
| `GET /api/teachers/` | **修改** | 需要 Token,返回加上 `total` 字段 |
|
|||
|
|
| `GET /api/students/` | **修改** | 需要 Token,分页参数加 `ge/le` 校验 |
|
|||
|
|
| `GET /api/stats/classes` | **修改** | 修复 N+1 查询,性能优化 |
|
|||
|
|
| `GET /health` | **新增** | 健康检查端点 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 七、总结
|
|||
|
|
|
|||
|
|
| 维度 | 当前状态 | V2.0 目标 |
|
|||
|
|
|------|----------|-----------|
|
|||
|
|
| 安全性 | 无认证,CORS 全开放 | JWT 认证 + RBAC + 严格 CORS |
|
|||
|
|
| 数据一致性 | 软删除字段不完整 | 全量统一审计字段 |
|
|||
|
|
| 性能 | 统计接口 N+1 查询 | 单次聚合查询 |
|
|||
|
|
| 可维护性 | 单体结构,无测试 | 分层架构 + 单元测试覆盖 |
|
|||
|
|
| 可观测性 | 无日志无监控 | loguru 日志 + health endpoint |
|
|||
|
|
| 部署 | 手动启动 | Docker + .env 配置化管理 |
|