mirror of
https://github.com/handsomezhuzhu/QQuiz.git
synced 2026-02-20 12:00:14 +00:00
## 功能特性 ✅ **核心功能** - 多文件上传与智能去重(基于 content_hash) - 异步文档解析(支持 TXT/PDF/DOCX/XLSX) - AI 智能题目提取与评分(OpenAI/Anthropic/Qwen) - 断点续做与进度管理 - 自动错题本收集 ✅ **技术栈** - Backend: FastAPI + SQLAlchemy 2.0 + PostgreSQL - Frontend: React 18 + Vite + Tailwind CSS - Deployment: Docker Compose ✅ **项目结构** - 53 个文件 - 完整的前后端分离架构 - Docker/源码双模部署支持 🚀 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
161 lines
3.3 KiB
Python
161 lines
3.3 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
|
|
|
|
|
|
class SystemConfigResponse(BaseModel):
|
|
allow_registration: bool
|
|
max_upload_size_mb: int
|
|
max_daily_uploads: int
|
|
ai_provider: str
|
|
|
|
|
|
# ============ 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
|