# 问题配置路由 - 完整CRUD import os import shutil from datetime import datetime from flask import request, jsonify, render_template, current_app, session, redirect from app.routes import main_bp from app.models import db, Problem, StudentProblem from app.config import load_api_config, save_api_config, DEFAULT_PROVIDERS from app.routes.auth import login_required_json, admin_required @main_bp.route("/problems") @login_required_json def settings(): """问题配置页面 - 所有登录用户可访问""" return render_template("problems.html", active_nav="problems") @main_bp.route("/api-settings") @admin_required def api_settings_page(): """API设置页面 - 仅管理员""" return render_template("api_settings.html", active_nav="api-settings") # ==================== API配置接口 ==================== @main_bp.route("/api/config", methods=["GET"]) @admin_required def get_api_config(): """获取API配置""" config = load_api_config(current_app.config) # 不返回完整的api_key,只返回前5位和后5位 if config.get("api_key"): api_key = config["api_key"] if len(api_key) > 10: config["api_key_preview"] = api_key[:5] + "..." + api_key[-5:] else: config["api_key_preview"] = "****" return jsonify(config) @main_bp.route("/api/config/providers", methods=["GET"]) @admin_required def list_providers(): """列出所有API提供商""" config = load_api_config(current_app.config) providers = config.get("providers", {}) result = {} for pid, pdata in providers.items(): result[pid] = { "name": pdata.get("name", pid), "endpoint": pdata.get("endpoint", ""), "models": pdata.get("models", []), } return jsonify(result) @main_bp.route("/api/config/providers", methods=["POST"]) @admin_required def add_provider(): """新增API提供商""" data = request.get_json() provider_id = data.get("id", "").strip().lower() name = data.get("name", "").strip() endpoint = data.get("endpoint", "").strip() models = data.get("models", []) if not isinstance(models, list): models = [m.strip() for m in str(models).split(",") if m.strip()] if not provider_id or not name: return jsonify({"error": "提供商ID和名称不能为空"}), 400 if not endpoint: return jsonify({"error": "Endpoint不能为空"}), 400 config = load_api_config(current_app.config) providers = config.get("providers", {}) if provider_id in providers: return jsonify({"error": f"提供商 '{provider_id}' 已存在"}), 400 providers[provider_id] = { "name": name, "endpoint": endpoint, "models": models, } config["providers"] = providers save_api_config(config, current_app.config) return jsonify({"message": "添加成功", "id": provider_id}) @main_bp.route("/api/config/providers/", methods=["DELETE"]) @admin_required def delete_provider(provider_id): """删除API提供商""" provider_id = provider_id.strip().lower() config = load_api_config(current_app.config) providers = config.get("providers", {}) if provider_id not in providers: return jsonify({"error": f"提供商 '{provider_id}' 不存在"}), 404 # 不允许删除当前选中的 provider if provider_id == config.get("provider"): return jsonify({"error": "不能删除当前正在使用的提供商,请先切换到其他提供商"}), 400 # 不允许删除到只剩 0 个 if len(providers) <= 1: return jsonify({"error": "至少保留一个提供商"}), 400 del providers[provider_id] # 同时删除 api_keys 中对应的 key if "api_keys" in config and provider_id in config["api_keys"]: del config["api_keys"][provider_id] config["providers"] = providers save_api_config(config, current_app.config) return jsonify({"message": "删除成功"}) @main_bp.route("/api/config/providers/", methods=["PUT"]) @admin_required def update_provider(provider_id): """更新API提供商(名称、endpoint、模型列表)""" provider_id = provider_id.strip().lower() data = request.get_json() config = load_api_config(current_app.config) providers = config.get("providers", {}) if provider_id not in providers: return jsonify({"error": f"提供商 '{provider_id}' 不存在"}), 404 pdata = providers[provider_id] if "name" in data: pdata["name"] = data["name"].strip() if "endpoint" in data: pdata["endpoint"] = data["endpoint"].strip() if "models" in data: if isinstance(data["models"], list): pdata["models"] = [m.strip() for m in data["models"] if m.strip()] else: pdata["models"] = [m.strip() for m in str(data["models"]).split(",") if m.strip()] config["providers"] = providers save_api_config(config, current_app.config) return jsonify({"message": "更新成功"}) @main_bp.route("/api/config", methods=["POST"]) @admin_required def update_api_config(): """更新API配置""" data = request.get_json() config = load_api_config(current_app.config) provider = data.get("provider", config.get("provider", "volcengine")) # api_key 留空时,尝试使用已有 key(当前 provider 的 key) api_key = data.get("api_key", "") if not api_key: existing_key = config.get("api_keys", {}).get(provider, "") if not existing_key: return jsonify({"error": "API Key不能为空"}), 400 api_key = existing_key providers = config.get("providers", DEFAULT_PROVIDERS) # 从 providers 列表获取默认 endpoint 和 model if provider in providers: default_endpoint = providers[provider].get("endpoint", "") models = providers[provider].get("models", []) default_model = models[0] if models else "" else: default_endpoint = "" default_model = "" new_config = { "provider": provider, "api_key": api_key, "base_url": data.get("base_url", default_endpoint), "model": data.get("model", default_model), "temperature": float(data.get("temperature", 0.7)), "prompt_template": data.get("prompt_template", ""), "watermark_text": data.get("watermark_text", ""), } save_api_config(new_config, current_app.config) return jsonify({"message": "配置保存成功"}) @main_bp.route("/api/problems", methods=["GET"]) @login_required_json def get_problems(): """获取问题列表(从数据库读取)""" from app.models import Problem problems = Problem.query.order_by(Problem.no).all() return jsonify([p.to_dict() for p in problems]) @main_bp.route("/api/problems/", methods=["GET"]) @login_required_json def get_problem_detail(problem_id): """获取单个问题的详细信息""" from app.models import Problem problem = Problem.query.get(problem_id) if not problem: return jsonify({"error": "问题不存在"}), 404 return jsonify({ "id": problem.id, "no": problem.no, "name": problem.name, "category": problem.category, "content": problem.content, }) @main_bp.route("/api/problems", methods=["POST"]) @admin_required def create_problem(): """创建新问题 - 仅管理员""" from app.models import Problem data = request.get_json() no = data.get("no", "").strip() name = data.get("name", "").strip() category = data.get("category", "技术类") content = data.get("content", "") if not no or not name: return jsonify({"error": "编号和名称不能为空"}), 400 # 检查编号是否已存在 existing = Problem.query.filter_by(no=no).first() if existing: return jsonify({"error": "该编号已存在"}), 400 # 创建问题 problem = Problem(no=no, name=name, category=category, content=content) db.session.add(problem) db.session.commit() return jsonify({"message": "创建成功", "id": problem.id, "no": problem.no, "name": problem.name}) @main_bp.route("/api/problems/", methods=["PUT"]) @login_required_json def update_problem(problem_id): """更新问题""" from app.models import Problem problem = Problem.query.get(problem_id) if not problem: return jsonify({"error": "问题不存在"}), 404 data = request.get_json() if "name" in data: problem.name = data["name"].strip() if "category" in data: problem.category = data["category"] if "content" in data: problem.content = data["content"] db.session.commit() return jsonify({"message": "更新成功"}) @main_bp.route("/api/problems/", methods=["DELETE"]) @admin_required def delete_problem(problem_id): """删除问题""" from app.models import Problem problem = Problem.query.get(problem_id) if not problem: return jsonify({"error": "问题不存在"}), 404 # 检查是否有关联数据 from app.models import StudentProblem if StudentProblem.query.filter_by(problem_id=problem_id).first(): return jsonify({"error": "该问题已被学员使用,无法删除"}), 400 db.session.delete(problem) db.session.commit() return jsonify({"message": "删除成功"}) # ==================== AI测试接口 ==================== @main_bp.route("/api/config/test", methods=["POST"]) @admin_required def test_api_connection(): """测试API连接""" import requests config = load_api_config(current_app.config) provider = config.get("provider", "volcengine") headers = { "Authorization": f"Bearer {config.get('api_key', '')}", "Content-Type": "application/json", } payload = { "model": config.get("model", "doubao-seed-2.0-pro"), "messages": [{"role": "user", "content": "你好"}], "max_tokens": 50, } try: # MiniMax 使用 Anthropic 格式的 API if provider == "minimax": endpoint = f"{config.get('base_url')}/messages" payload["max_tokens"] = 50 else: endpoint = f"{config.get('base_url')}/chat/completions" response = requests.post( endpoint, headers=headers, json=payload, timeout=30, ) if response.status_code == 200: return jsonify({"success": True, "message": "连接成功"}) else: return jsonify( {"success": False, "error": f"API返回错误: {response.status_code}"} ) except Exception as e: return jsonify({"success": False, "error": str(e)})