Files
piano-plan/app/routes/settings.py
T

340 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 问题配置路由 - 完整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/<provider_id>", 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/<provider_id>", 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/<int:problem_id>", 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/<int:problem_id>", 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/<int:problem_id>", 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)})