mirror of
https://github.com/handsomezhuzhu/QQuiz.git
synced 2026-02-20 20:10:14 +00:00
## 新功能 - 实现管理后台API配置管理(OpenAI/Anthropic/Qwen) - API配置保存到数据库,实时生效无需重启 - API密钥遮罩显示(前10位+后4位) - 完整endpoint URL自动显示 ## 后端改进 - 新增 config_service.py 用于加载数据库配置 - LLMService 支持动态配置注入,回退到环境变量 - 更新 exam.py 和 question.py 使用数据库配置 - 扩展 schemas.py 支持所有API配置字段 ## 前端改进 - 重写 AdminSettings.jsx 增强UI体验 - API密钥显示/隐藏切换 - 当前使用的提供商可视化标识 - 移除"需要重启"的误导性提示 ## 项目结构重组 - 移动所有脚本到 scripts/ 目录 - 移动所有文档到 docs/ 目录 - 清理 Python 缓存文件 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
179 lines
4.0 KiB
Python
179 lines
4.0 KiB
Python
"""
|
|
Pydantic Schemas for Request/Response Validation
|
|
"""
|
|
from pydantic import BaseModel, Field, validator
|
|
from typing import Optional, List
|
|
from datetime import datetime
|
|
from models import ExamStatus, QuestionType
|
|
|
|
|
|
# ============ Auth Schemas ============
|
|
class UserCreate(BaseModel):
|
|
username: str = Field(..., min_length=3, max_length=50)
|
|
password: str = Field(..., min_length=6)
|
|
|
|
@validator('username')
|
|
def username_alphanumeric(cls, v):
|
|
if not v.replace('_', '').replace('-', '').isalnum():
|
|
raise ValueError('Username must be alphanumeric (allows _ and -)')
|
|
return v
|
|
|
|
|
|
class UserLogin(BaseModel):
|
|
username: str
|
|
password: str
|
|
|
|
|
|
class Token(BaseModel):
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
|
|
|
|
class UserResponse(BaseModel):
|
|
id: int
|
|
username: str
|
|
is_admin: bool
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# ============ System Config Schemas ============
|
|
class SystemConfigUpdate(BaseModel):
|
|
allow_registration: Optional[bool] = None
|
|
max_upload_size_mb: Optional[int] = None
|
|
max_daily_uploads: Optional[int] = None
|
|
ai_provider: Optional[str] = None
|
|
# API Configuration
|
|
openai_api_key: Optional[str] = None
|
|
openai_base_url: Optional[str] = None
|
|
openai_model: Optional[str] = None
|
|
anthropic_api_key: Optional[str] = None
|
|
anthropic_model: Optional[str] = None
|
|
qwen_api_key: Optional[str] = None
|
|
qwen_base_url: Optional[str] = None
|
|
qwen_model: Optional[str] = None
|
|
|
|
|
|
class SystemConfigResponse(BaseModel):
|
|
allow_registration: bool
|
|
max_upload_size_mb: int
|
|
max_daily_uploads: int
|
|
ai_provider: str
|
|
# API Configuration
|
|
openai_api_key: Optional[str] = None
|
|
openai_base_url: Optional[str] = None
|
|
openai_model: Optional[str] = None
|
|
anthropic_api_key: Optional[str] = None
|
|
anthropic_model: Optional[str] = None
|
|
qwen_api_key: Optional[str] = None
|
|
qwen_base_url: Optional[str] = None
|
|
qwen_model: Optional[str] = None
|
|
|
|
|
|
# ============ Exam Schemas ============
|
|
class ExamCreate(BaseModel):
|
|
title: str = Field(..., min_length=1, max_length=200)
|
|
|
|
|
|
class ExamResponse(BaseModel):
|
|
id: int
|
|
user_id: int
|
|
title: str
|
|
status: ExamStatus
|
|
current_index: int
|
|
total_questions: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class ExamListResponse(BaseModel):
|
|
exams: List[ExamResponse]
|
|
total: int
|
|
|
|
|
|
class ExamUploadResponse(BaseModel):
|
|
exam_id: int
|
|
title: str
|
|
status: str
|
|
message: str
|
|
|
|
|
|
class ParseResult(BaseModel):
|
|
"""Result from file parsing"""
|
|
total_parsed: int
|
|
duplicates_removed: int
|
|
new_added: int
|
|
message: str
|
|
|
|
|
|
# ============ Question Schemas ============
|
|
class QuestionBase(BaseModel):
|
|
content: str
|
|
type: QuestionType
|
|
options: Optional[List[str]] = None
|
|
answer: str
|
|
analysis: Optional[str] = None
|
|
|
|
|
|
class QuestionCreate(QuestionBase):
|
|
exam_id: int
|
|
|
|
|
|
class QuestionResponse(QuestionBase):
|
|
id: int
|
|
exam_id: int
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class QuestionListResponse(BaseModel):
|
|
questions: List[QuestionResponse]
|
|
total: int
|
|
|
|
|
|
# ============ Quiz Schemas ============
|
|
class AnswerSubmit(BaseModel):
|
|
question_id: int
|
|
user_answer: str
|
|
|
|
|
|
class AnswerCheckResponse(BaseModel):
|
|
correct: bool
|
|
user_answer: str
|
|
correct_answer: str
|
|
analysis: Optional[str] = None
|
|
ai_score: Optional[float] = None # For short answer questions
|
|
ai_feedback: Optional[str] = None # For short answer questions
|
|
|
|
|
|
class QuizProgressUpdate(BaseModel):
|
|
current_index: int
|
|
|
|
|
|
# ============ Mistake Schemas ============
|
|
class MistakeAdd(BaseModel):
|
|
question_id: int
|
|
|
|
|
|
class MistakeResponse(BaseModel):
|
|
id: int
|
|
user_id: int
|
|
question_id: int
|
|
question: QuestionResponse
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class MistakeListResponse(BaseModel):
|
|
mistakes: List[MistakeResponse]
|
|
total: int
|