from flask import Blueprint, request, jsonify, render_template, session from app.models import db, Goal, StudentGoal, GoalRelation, StudentGoalEvaluation from app.routes.auth import login_required_json, admin_required, login_required from app.routes import main_bp goals_bp = Blueprint("goals", __name__) # 目标管理页面路由 @main_bp.route("/goals") @login_required def goals_page(): """目标管理页面""" return render_template("goals.html", active_nav="goals") @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", ""), level=data.get("level", "入门"), category=data.get("category", "综合") ) 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"] if "level" in data: goal.level = data["level"] if "category" in data: goal.category = data["category"] db.session.commit() return jsonify(goal.to_dict()) @goals_bp.route("/api/goals/", methods=["DELETE"]) @login_required_json def delete_goal(goal_id): """删除目标模板(仅管理员,需检查依赖)""" from app.models import User user = User.query.get(session.get("user_id")) if not user or user.role != "admin": return jsonify({"error": "权限不足,仅管理员可操作"}), 403 goal = Goal.query.get_or_404(goal_id) # 检查依赖关系 deps = [] # 1. 检查是否有学员分配了此目标 student_goal_count = StudentGoal.query.filter_by(goal_id=goal_id).count() if student_goal_count > 0: deps.append(f"已被 {student_goal_count} 名学员分配") # 2. 检查是否有目标关联(作为父目标或子目标) as_parent = GoalRelation.query.filter_by(parent_goal_id=goal_id).count() as_child = GoalRelation.query.filter_by(child_goal_id=goal_id).count() if as_parent > 0: deps.append(f"有 {as_parent} 个子目标关联") if as_child > 0: deps.append(f"作为子目标被 {as_child} 个目标引用") if deps: return jsonify({"error": f"无法删除:{', '.join(deps)}"}), 400 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": "移除成功"})