diff --git a/app/__init__.py b/app/__init__.py index fd92061..ffeea74 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -34,9 +34,11 @@ def create_app(): # 注册蓝图 from app.routes import main_bp from app.routes.templates import templates_bp + from app.routes.goals import goals_bp app.register_blueprint(main_bp) app.register_blueprint(templates_bp) + app.register_blueprint(goals_bp) # 创建数据库和目录 with app.app_context(): diff --git a/app/routes/__init__.py b/app/routes/__init__.py index a3be51b..3d933f0 100644 --- a/app/routes/__init__.py +++ b/app/routes/__init__.py @@ -5,4 +5,4 @@ from flask import Blueprint 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 diff --git a/app/routes/goals.py b/app/routes/goals.py new file mode 100644 index 0000000..1863e68 --- /dev/null +++ b/app/routes/goals.py @@ -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/", 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/", 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/", 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//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//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//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//children/", 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": "移除成功"}) \ No newline at end of file