feat: 添加 Goal, GoalRelation, StudentGoal 三个数据模型

- Goal: 目标表,支持存储学习目标
- GoalRelation: 目标自关联多对多表,支持 DAG 结构
- StudentGoal: 学员目标记录表,关联学员和目标
This commit is contained in:
hmo
2026-04-23 20:10:08 +08:00
parent 285979ff70
commit b54b6c7aec
21 changed files with 3229 additions and 0 deletions
@@ -0,0 +1,143 @@
# 目标管理模块设计
> 日期:2026-04-23
> 状态:待评审
## 概述
目标管理模块用于管理系统化的钢琴学习目标,支持目标间的多对多关联(DAG结构),以及目标与学员的关联记录。
## 数据模型
### 1. Goal (目标)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Integer | 主键,自增 |
| name | String(100) | 目标名称,必填 |
| content | Text | Markdown 格式详细内容 |
| created_at | DateTime | 创建时间 |
| updated_at | DateTime | 更新时间 |
### 2. GoalRelation (目标关联 - 自关联多对多)
| 字段 | 类型 | 说明 |
|------|------|------|
| parent_goal_id | Integer | 外键 → goals.id |
| child_goal_id | Integer | 外键 → goals.id |
| PRIMARY KEY | (parent_goal_id, child_goal_id) | 联合主键 |
**约束**
- 禁止循环引用(A→B→C→A
- 自关联:goal 可以是自身的父/子节点
### 3. StudentGoal (学员目标记录)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Integer | 主键,自增 |
| student_id | Integer | 外键 → students.id |
| goal_id | Integer | 外键 → goals.id |
| status | String(20) | 状态:未开始/进行中/已完成 |
| mastery_level | Integer | 完成度:1-51最少,5最精通) |
| deadline | DateTime | 截止日期 |
| completed_at | DateTime | 完成日期 |
| created_at | DateTime | 创建时间 |
**显示规则**
- `mastery_level` 直接渲染为对应数量的五角星(★)
---
## API 接口
### 目标管理
```
GET /api/goals # 获取所有目标
POST /api/goals # 创建目标
GET /api/goals/<id> # 获取目标详情
PUT /api/goals/<id> # 更新目标
DELETE /api/goals/<id> # 删除目标
```
### 目标关联
```
GET /api/goals/<id>/parents # 获取父目标列表
GET /api/goals/<id>/children # 获取子目标列表
POST /api/goals/<id>/children # 添加子目标关联
DELETE /api/goals/<id>/children/<child_id> # 移除关联
```
### 学员目标
```
GET /api/students/<student_id>/goals # 获取学员的目标列表
POST /api/students/<student_id>/goals # 为学员添加目标
PUT /api/students/<student_id>/goals/<goal_id> # 更新学员目标状态
DELETE /api/students/<student_id>/goals/<goal_id> # 移除学员的目标
```
---
## 循环引用检测
在添加关联时必须检测:
```python
def has_cycle(goal_id, new_child_id):
"""检测添加 new_child_id 作为 goal_id 的子目标是否会形成循环"""
visited = set()
stack = [new_child_id]
while stack:
current = stack.pop()
if current == goal_id:
return True # 发现循环
if current in visited:
continue
visited.add(current)
# 获取 current 的所有子目标,继续检测
for child in get_children(current):
stack.append(child)
return False
```
---
## 前端页面
### 1. 目标管理页面 (`/goals`)
- 目标列表(可树状展示)
- 创建/编辑/删除目标
- 目标内容 Markdown 编辑器
- 关联管理(拖拽或选择器添加关联)
### 2. 学员详情页目标区块
在现有 `student.html` 中扩展:
- 显示学员的目标列表
- 每项目标显示:名称、状态、★完成度、截止日期
- 可添加/移除/编辑目标
- 状态变更触发刷新
---
## 实现顺序
1. **数据模型** - goals, goal_relations, student_goals 表
2. **目标 CRUD API** - 基础的增删改查
3. **目标关联 API** - 关联管理 + 循环检测
4. **学员目标 API** - 学员与目标的关联管理
5. **目标管理页面** - 目标列表 + 关联管理
6. **学员详情页扩展** - 目标区块
---
## 依赖关系
- 无外部依赖
- 复用现有的 Markdown 编辑器(EasyMDE)和星级组件