feat: 添加目标管理CRUD API和关联蓝图

This commit is contained in:
hmo
2026-04-23 20:18:36 +08:00
parent 285979ff70
commit ab0a8f383d
3 changed files with 117 additions and 1 deletions
+2
View File
@@ -34,9 +34,11 @@ def create_app():
# 注册蓝图 # 注册蓝图
from app.routes import main_bp from app.routes import main_bp
from app.routes.templates import templates_bp from app.routes.templates import templates_bp
from app.routes.goals import goals_bp
app.register_blueprint(main_bp) app.register_blueprint(main_bp)
app.register_blueprint(templates_bp) app.register_blueprint(templates_bp)
app.register_blueprint(goals_bp)
# 创建数据库和目录 # 创建数据库和目录
with app.app_context(): with app.app_context():
+1 -1
View File
@@ -5,4 +5,4 @@ from flask import Blueprint
main_bp = Blueprint("main", __name__) main_bp = Blueprint("main", __name__)
# 导入各路由模块 # 导入各路由模块
from app.routes import students, plans, problems, settings, auth, classes, users, backup from app.routes import students, plans, problems, settings, auth, classes, users, backup, goals
+114
View File
@@ -0,0 +1,114 @@
from flask import Blueprint, request, jsonify
from app.models import db, Goal
from app.routes.auth import login_required_json
goals_bp = Blueprint("goals", __name__)
@goals_bp.route("/api/goals", methods=["GET"])
@login_required_json
def get_goals():
goals = Goal.query.order_by(Goal.created_at.desc()).all()
return jsonify([g.to_dict() for g in goals])
@goals_bp.route("/api/goals", methods=["POST"])
@login_required_json
def create_goal():
data = request.get_json()
goal = Goal(name=data["name"], content=data.get("content", ""))
db.session.add(goal)
db.session.commit()
return jsonify(goal.to_dict()), 201
@goals_bp.route("/api/goals/<int:goal_id>", methods=["GET"])
@login_required_json
def get_goal(goal_id):
goal = Goal.query.get_or_404(goal_id)
return jsonify(goal.to_dict())
@goals_bp.route("/api/goals/<int:goal_id>", methods=["PUT"])
@login_required_json
def update_goal(goal_id):
goal = Goal.query.get_or_404(goal_id)
data = request.get_json()
if "name" in data:
goal.name = data["name"]
if "content" in data:
goal.content = data["content"]
db.session.commit()
return jsonify(goal.to_dict())
@goals_bp.route("/api/goals/<int:goal_id>", methods=["DELETE"])
@login_required_json
def delete_goal(goal_id):
goal = Goal.query.get_or_404(goal_id)
db.session.delete(goal)
db.session.commit()
return jsonify({"message": "删除成功"})
# ===== 以下是 Task 3 的循环检测和关联 API =====
def _has_cycle(parent_id, child_id):
"""检测添加 child_id 作为 parent_id 的子目标是否会形成循环"""
visited = set()
stack = [child_id]
while stack:
current = stack.pop()
if current == parent_id:
return True
if current in visited:
continue
visited.add(current)
from app.models import GoalRelation
for rel in GoalRelation.query.filter_by(parent_goal_id=current).all():
stack.append(rel.child_goal_id)
return False
@goals_bp.route("/api/goals/<int:goal_id>/children", methods=["GET"])
@login_required_json
def get_goal_children(goal_id):
from app.models import GoalRelation
relations = GoalRelation.query.filter_by(parent_goal_id=goal_id).all()
child_ids = [r.child_goal_id for r in relations]
children = Goal.query.filter(Goal.id.in_(child_ids)).all() if child_ids else []
return jsonify([c.to_dict() for c in children])
@goals_bp.route("/api/goals/<int:goal_id>/parents", methods=["GET"])
@login_required_json
def get_goal_parents(goal_id):
from app.models import GoalRelation
relations = GoalRelation.query.filter_by(child_goal_id=goal_id).all()
parent_ids = [r.parent_goal_id for r in relations]
parents = Goal.query.filter(Goal.id.in_(parent_ids)).all() if parent_ids else []
return jsonify([p.to_dict() for p in parents])
@goals_bp.route("/api/goals/<int:goal_id>/children", methods=["POST"])
@login_required_json
def add_goal_child(goal_id):
from app.models import GoalRelation
data = request.get_json()
child_id = data["child_goal_id"]
if goal_id == child_id:
return jsonify({"error": "不能将目标关联到自身"}), 400
if _has_cycle(goal_id, child_id):
return jsonify({"error": "添加此关联会形成循环引用"}), 400
existing = GoalRelation.query.filter_by(parent_goal_id=goal_id, child_goal_id=child_id).first()
if existing:
return jsonify({"error": "关联已存在"}), 400
relation = GoalRelation(parent_goal_id=goal_id, child_goal_id=child_id)
db.session.add(relation)
db.session.commit()
return jsonify({"message": "添加成功"})
@goals_bp.route("/api/goals/<int:goal_id>/children/<int:child_id>", methods=["DELETE"])
@login_required_json
def remove_goal_child(goal_id, child_id):
from app.models import GoalRelation
relation = GoalRelation.query.filter_by(parent_goal_id=goal_id, child_goal_id=child_id).first()
if relation:
db.session.delete(relation)
db.session.commit()
return jsonify({"message": "移除成功"})