智教助手平台:完整初始化

- 前端:Vue3 + TS + Element Plus,24 个页面路由(课件/组题/教案/动画/思维导图/作文批改/命题/课堂/资源/社区等)
- 后端:FastAPI + SQLAlchemy + SQLite,17 个路由模块,AI 服务层含降级模板
- AI:glm-5.x 推理模型已禁用思维链,确保输出真实内容
- 修复:ai_service 两处请求体注入 enable_thinking/thinking disabled
- 测试账号:13900999999 / test1234
This commit is contained in:
Zhang Jing Xuan
2026-07-29 16:29:10 +08:00
commit 0841b1a103
193 changed files with 44995 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
from .user import *
from .courseware import *
from .animation import *
from .exercise import *
from .lesson_plan import *
from .exam import *
from .resource import *
from .community import *
from .ai import *
+53
View File
@@ -0,0 +1,53 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field, field_validator
class AdminUserOut(BaseModel):
id: int
phone: str
name: str
avatar: str = ""
subject: str = ""
school: str = ""
grade: str = ""
role: str = "teacher"
is_active: bool = True
credits: int = 0
created_at: datetime | None = None
@field_validator("avatar", "subject", "school", "grade", "name", "role", mode="before")
@classmethod
def _none_to_empty(cls, v):
return "" if v is None else v
model_config = {"from_attributes": True}
class AdminUserUpdate(BaseModel):
role: Optional[str] = None
is_active: Optional[bool] = None
name: Optional[str] = None
class AdminGrantCredits(BaseModel):
amount: int = Field(..., gt=0, le=10000, description="积分数量")
class AdminDashboardOut(BaseModel):
total_users: int = 0
active_users: int = 0
admin_users: int = 0
total_resources: int = 0
public_resources: int = 0
total_coursewares: int = 0
total_animations: int = 0
total_exercises: int = 0
total_lesson_plans: int = 0
total_exams: int = 0
total_views: int = 0
total_downloads: int = 0
total_favorites: int = 0
total_credits_used: int = 0
new_users_today: int = 0
+38
View File
@@ -0,0 +1,38 @@
from pydantic import BaseModel, Field
from typing import Any
class AIRequest(BaseModel):
prompt: str = Field(..., min_length=1, description="AI请求内容")
context: dict[str, Any] = Field(default_factory=dict, description="上下文信息")
stream: bool = Field(default=False, description="是否流式返回")
class EssayGradeRequest(BaseModel):
essay_text: str = Field(..., min_length=10, description="作文文本")
grade_level: str = Field(default="初中", description="年级段")
essay_type: str = Field(default="记叙文", description="作文类型")
total_score: int = Field(default=50, description="满分")
class ExamGenerateRequest(BaseModel):
subject: str = Field(..., min_length=1)
grade: str = ""
knowledge_points: list[str] = []
difficulty: str = Field(default="medium", pattern=r"^(easy|medium|hard)$")
question_types: list[str] = Field(default=["choice"])
count: int = Field(default=10, ge=1, le=50)
total_score: int = Field(default=100)
class HtmlExportRequest(BaseModel):
title: str = Field(default="教学内容", max_length=200)
html: str = Field(..., min_length=1)
class ExamExportRequest(BaseModel):
title: str = Field(default="试卷", max_length=200)
subject: str = ""
grade: str = ""
questions: list[dict[str, Any]] = []
answers: list[Any] = []
+40
View File
@@ -0,0 +1,40 @@
from pydantic import BaseModel, Field
from datetime import datetime
from typing import Optional, Any
class AnimationGenerate(BaseModel):
prompt: str = Field(..., min_length=1, description="教学动画描述")
anim_type: str = Field(default="general", pattern=r"^(math|physics|chemistry|biology|geography|general)$")
subject: str = Field(default="", max_length=50)
class AnimationCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=200)
anim_type: str = "general"
description: str = ""
config: dict[str, Any] = {}
class AnimationUpdate(BaseModel):
title: Optional[str] = None
anim_type: Optional[str] = None
description: Optional[str] = None
config: Optional[dict[str, Any]] = None
thumbnail: Optional[str] = None
status: Optional[str] = None
class AnimationOut(BaseModel):
id: int
user_id: int
title: str
anim_type: str = "general"
description: str = ""
config: dict = {}
thumbnail: str = ""
status: str = "draft"
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = {"from_attributes": True}
+41
View File
@@ -0,0 +1,41 @@
from datetime import datetime
from pydantic import BaseModel, Field
class ConversationCreate(BaseModel):
title: str | None = Field(default=None, max_length=200)
subject: str = ""
grade: str = ""
class ConversationRename(BaseModel):
title: str = Field(..., min_length=1, max_length=200)
pinned: bool | None = None
class ChatSend(BaseModel):
content: str = Field(..., min_length=1, max_length=4000)
subject: str = ""
grade: str = ""
class MessageOut(BaseModel):
id: int
role: str
content: str
created_at: datetime | None = None
model_config = {"from_attributes": True}
class ConversationOut(BaseModel):
id: int
title: str
subject: str = ""
grade: str = ""
pinned: bool = False
created_at: datetime | None = None
updated_at: datetime | None = None
messages: list[MessageOut] = Field(default_factory=list)
model_config = {"from_attributes": True}
+71
View File
@@ -0,0 +1,71 @@
from datetime import datetime
from typing import Any, Optional
from pydantic import BaseModel, Field
class ClassroomActivityCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=200)
activity_type: str = Field(default="poll", max_length=50)
description: str = ""
config: dict[str, Any] = {}
status: str = "draft"
class ClassroomActivityUpdate(BaseModel):
title: Optional[str] = None
activity_type: Optional[str] = None
description: Optional[str] = None
config: Optional[dict[str, Any]] = None
responses: Optional[list[dict[str, Any]]] = None
status: Optional[str] = None
class ClassroomResponseCreate(BaseModel):
student_name: str = Field(default="", max_length=50)
answer: Any
meta: dict[str, Any] = {}
class ClassroomActivityOut(BaseModel):
id: int
user_id: int
title: str
activity_type: str = "poll"
description: str = ""
config: dict = {}
responses: list = []
status: str = "draft"
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = {"from_attributes": True}
class ClassroomOptionStat(BaseModel):
option: str
count: int = 0
percent: int = 0
class ClassroomKeywordStat(BaseModel):
keyword: str
count: int = 0
class ClassroomAnalysisOut(BaseModel):
activity_id: int
title: str
activity_type: str = "poll"
question: str = ""
response_count: int = 0
participant_count: int = 0
option_stats: list[ClassroomOptionStat] = Field(default_factory=list)
keywords: list[ClassroomKeywordStat] = Field(default_factory=list)
common_answers: list[str] = Field(default_factory=list)
mastery_level: str = "暂无数据"
accuracy: int | None = None
diagnosis: list[str] = Field(default_factory=list)
teaching_suggestions: list[str] = Field(default_factory=list)
followup_prompt: str = ""
generated_at: datetime | None = None
+67
View File
@@ -0,0 +1,67 @@
from pydantic import BaseModel, Field
from datetime import datetime
from typing import Any, Optional
class CommunityAuthorOut(BaseModel):
id: int
name: str = ""
avatar: str = ""
subject: str = ""
school: str = ""
model_config = {"from_attributes": True}
class PostCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=200)
content: str = Field(..., min_length=1)
post_type: str = "discussion"
tags: list[str] = Field(default_factory=list)
attachments: list[dict[str, Any]] = Field(default_factory=list)
class PostUpdate(BaseModel):
title: Optional[str] = Field(None, min_length=1, max_length=200)
content: Optional[str] = Field(None, min_length=1)
post_type: Optional[str] = None
tags: Optional[list[str]] = None
attachments: Optional[list[dict[str, Any]]] = None
class PostOut(BaseModel):
id: int
user_id: int
title: str
content: str = ""
post_type: str = "discussion"
tags: list = Field(default_factory=list)
attachments: list = Field(default_factory=list)
views: int = 0
likes: int = 0
comments_count: int = 0
is_pinned: int = 0
is_favorited: bool = False
author: CommunityAuthorOut | None = None
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = {"from_attributes": True}
class CommentCreate(BaseModel):
content: str = Field(..., min_length=1)
parent_id: Optional[int] = None
class CommentOut(BaseModel):
id: int
post_id: int
user_id: int
content: str
parent_id: Optional[int] = None
likes: int = 0
author: CommunityAuthorOut | None = None
created_at: datetime | None = None
model_config = {"from_attributes": True}
+69
View File
@@ -0,0 +1,69 @@
from pydantic import BaseModel, Field
from datetime import datetime
from typing import Optional, Any
class CoursewareCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=200)
subject: str = Field(default="", max_length=50)
grade: str = Field(default="", max_length=50)
description: str = ""
content: list[Any] = []
tags: list[str] = []
status: str = "draft"
class CoursewareAIGenerate(BaseModel):
prompt: str = Field(..., min_length=1, description="教学想法/描述")
subject: str = Field(default="", max_length=50)
grade: str = Field(default="", max_length=50)
page_count: int = Field(default=8, ge=3, le=30)
aspect_ratio: str = Field(default="16:9", description="画面比例: 16:9, 4:3, 1:1")
class CoursewareUpdate(BaseModel):
title: Optional[str] = None
subject: Optional[str] = None
grade: Optional[str] = None
description: Optional[str] = None
content: Optional[list[Any]] = None
cover_image: Optional[str] = None
status: Optional[str] = None
tags: Optional[list[str]] = None
class CoursewareOut(BaseModel):
id: int
user_id: int
title: str
subject: str = ""
grade: str = ""
description: str = ""
content: list = []
cover_image: str = ""
status: str = "draft"
version: int = 1
tags: list = []
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = {"from_attributes": True}
class CoursewareShareOut(BaseModel):
id: int
courseware_id: int
token: str
title: str
subject: str = ""
grade: str = ""
description: str = ""
content: list = []
tags: list = []
views: int = 0
status: str = "active"
share_url: str = ""
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = {"from_attributes": True}
+27
View File
@@ -0,0 +1,27 @@
from datetime import datetime
from typing import Any
from pydantic import BaseModel, Field
class EssayGradeCreate(BaseModel):
title: str = Field(default="作文批改报告", min_length=1, max_length=200)
essay_text: str = Field(..., min_length=1)
grade_level: str = "初中"
essay_type: str = "记叙文"
total_score: int = 50
result: dict[str, Any] = Field(default_factory=dict)
class EssayGradeOut(BaseModel):
id: int
user_id: int
title: str
essay_text: str = ""
grade_level: str = "初中"
essay_type: str = "记叙文"
total_score: int = 50
result: dict = Field(default_factory=dict)
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = {"from_attributes": True}
+46
View File
@@ -0,0 +1,46 @@
from datetime import datetime
from typing import Any, Optional
from pydantic import BaseModel, Field
class ExamCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=200)
subject: str = ""
grade: str = ""
questions: list[dict[str, Any]] = []
answers: list[Any] = []
duration: int = 90
total_score: int = 100
difficulty: str = "medium"
knowledge_points: list[str] = []
class ExamUpdate(BaseModel):
title: Optional[str] = None
subject: Optional[str] = None
grade: Optional[str] = None
questions: Optional[list[dict[str, Any]]] = None
answers: Optional[list[Any]] = None
duration: Optional[int] = None
total_score: Optional[int] = None
difficulty: Optional[str] = None
knowledge_points: Optional[list[str]] = None
class ExamOut(BaseModel):
id: int
user_id: int
title: str
subject: str = ""
grade: str = ""
questions: list = []
answers: list = []
duration: int = 90
total_score: int = 100
difficulty: str = "medium"
knowledge_points: list = []
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = {"from_attributes": True}
+65
View File
@@ -0,0 +1,65 @@
from pydantic import BaseModel, Field
from datetime import datetime
from typing import Optional, Any
class ExerciseAIGenerate(BaseModel):
prompt: str = Field(..., min_length=1, description="练习生成描述")
exercise_type: str = Field(default="choice")
subject: str = ""
knowledge_points: list[str] = []
count: int = Field(default=5, ge=1, le=50)
class ExerciseCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=200)
exercise_type: str = "choice"
subject: str = ""
knowledge_points: list[str] = []
questions: list[dict[str, Any]] = []
settings: dict[str, Any] = {}
class ExerciseUpdate(BaseModel):
title: Optional[str] = None
exercise_type: Optional[str] = None
subject: Optional[str] = None
knowledge_points: Optional[list[str]] = None
questions: Optional[list[dict[str, Any]]] = None
settings: Optional[dict[str, Any]] = None
class ExerciseAttemptCreate(BaseModel):
student_name: str = ""
answers: list[Any] = []
score: int = 0
total: int = 0
duration: int = 0
class ExerciseAttemptOut(BaseModel):
id: int
exercise_id: int
student_name: str = ""
answers: list[Any] = []
score: int = 0
total: int = 0
duration: int = 0
created_at: datetime | None = None
model_config = {"from_attributes": True}
class ExerciseOut(BaseModel):
id: int
user_id: int
title: str
exercise_type: str = "choice"
subject: str = ""
knowledge_points: list = []
questions: list = []
settings: dict = {}
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = {"from_attributes": True}
+57
View File
@@ -0,0 +1,57 @@
from pydantic import BaseModel, Field
from datetime import datetime
from typing import Optional, Any
class LessonPlanGenerate(BaseModel):
title: str = Field(..., min_length=1)
subject: str = ""
grade: str = ""
objectives: list[str] = []
duration: int = Field(default=45, ge=20, le=120)
extra_requirements: str = ""
class LessonPlanCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=200)
subject: str = ""
grade: str = ""
objectives: list[str] = []
key_points: list[str] = []
difficulties: list[str] = []
content: dict[str, Any] = {}
duration: int = 45
materials: list[str] = []
homework: list[str] = []
class LessonPlanUpdate(BaseModel):
title: Optional[str] = None
subject: Optional[str] = None
grade: Optional[str] = None
objectives: Optional[list[str]] = None
key_points: Optional[list[str]] = None
difficulties: Optional[list[str]] = None
content: Optional[dict[str, Any]] = None
duration: Optional[int] = None
materials: Optional[list[str]] = None
homework: Optional[list[str]] = None
class LessonPlanOut(BaseModel):
id: int
user_id: int
title: str
subject: str = ""
grade: str = ""
objectives: list = []
key_points: list = []
difficulties: list = []
content: dict = {}
duration: int = 45
materials: list = []
homework: list = []
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = {"from_attributes": True}
+44
View File
@@ -0,0 +1,44 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field
class MaterialCreate(BaseModel):
filename: str = Field(..., min_length=1, max_length=255)
title: str = Field(..., min_length=1, max_length=200)
material_type: str = "text"
subject: str = ""
grade: str = ""
tags: list[str] = Field(default_factory=list)
summary: str = ""
char_count: int = 0
size: int = 0
source: str = "upload"
class MaterialUpdate(BaseModel):
title: Optional[str] = None
subject: Optional[str] = None
grade: Optional[str] = None
tags: Optional[list[str]] = None
summary: Optional[str] = None
status: Optional[str] = None
class MaterialOut(BaseModel):
id: int
user_id: int
filename: str
title: str
material_type: str = "text"
subject: str = ""
grade: str = ""
tags: list = Field(default_factory=list)
summary: str = ""
char_count: int = 0
size: int = 0
source: str = "upload"
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = {"from_attributes": True}
+32
View File
@@ -0,0 +1,32 @@
from datetime import datetime
from typing import Any
from pydantic import BaseModel, Field
class MindMapGenerate(BaseModel):
topic: str = Field(..., min_length=1, max_length=200)
subject: str = "综合"
grade: str = ""
class MindMapCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=200)
subject: str = "综合"
nodes: list[Any] = Field(default_factory=list)
class MindMapUpdate(BaseModel):
title: str | None = None
nodes: list[Any] | None = None
class MindMapOut(BaseModel):
id: int
user_id: int
title: str
subject: str = "综合"
nodes: list[Any] = Field(default_factory=list)
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = {"from_attributes": True}
+16
View File
@@ -0,0 +1,16 @@
from datetime import datetime
from pydantic import BaseModel
class NotificationOut(BaseModel):
id: int
user_id: int
actor_id: int | None = None
ntype: str = "comment"
title: str
content: str = ""
link: str = ""
is_read: bool = False
created_at: datetime | None = None
model_config = {"from_attributes": True}
+63
View File
@@ -0,0 +1,63 @@
from pydantic import BaseModel, Field
from datetime import datetime
from typing import Optional, Any
class ResourceAuthorOut(BaseModel):
id: int
name: str = ""
avatar: str = ""
subject: str = ""
school: str = ""
model_config = {"from_attributes": True}
class ResourceCreate(BaseModel):
resource_type: str = "other"
title: str = Field(..., min_length=1, max_length=200)
description: str = ""
content_ref: str = ""
file_url: str = ""
tags: list[str] = Field(default_factory=list)
subject: str = ""
grade: str = ""
is_public: int = 1
class ResourceUpdate(BaseModel):
resource_type: Optional[str] = None
title: Optional[str] = None
description: Optional[str] = None
content_ref: Optional[str] = None
file_url: Optional[str] = None
cover_image: Optional[str] = None
tags: Optional[list[str]] = None
subject: Optional[str] = None
grade: Optional[str] = None
is_public: Optional[int] = None
status: Optional[str] = None
class ResourceOut(BaseModel):
id: int
user_id: int
resource_type: str
title: str
description: str = ""
content_ref: str = ""
file_url: str = ""
cover_image: str = ""
tags: list = Field(default_factory=list)
subject: str = ""
grade: str = ""
downloads: int = 0
likes: int = 0
views: int = 0
is_public: int = 1
is_favorited: bool = False
author: ResourceAuthorOut | None = None
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = {"from_attributes": True}
+28
View File
@@ -0,0 +1,28 @@
from datetime import datetime
from pydantic import BaseModel, Field
class SearchResultItem(BaseModel):
id: int | str
source: str
source_label: str
title: str
description: str = ""
subject: str = ""
grade: str = ""
tags: list[str] = Field(default_factory=list)
route: str
action_route: str = ""
is_public: bool = False
updated_at: datetime | None = None
class SearchResponse(BaseModel):
keyword: str = ""
total: int = 0
resources: list[SearchResultItem] = Field(default_factory=list)
community: list[SearchResultItem] = Field(default_factory=list)
works: list[SearchResultItem] = Field(default_factory=list)
materials: list[SearchResultItem] = Field(default_factory=list)
classroom: list[SearchResultItem] = Field(default_factory=list)
+130
View File
@@ -0,0 +1,130 @@
from pydantic import BaseModel, Field, field_validator
from datetime import datetime
from typing import Optional
class UserLogin(BaseModel):
phone: str = Field(..., pattern=r"^1[3-9]\d{9}$", description="手机号")
password: str = Field(..., min_length=6, max_length=50, description="密码")
class UserRegister(BaseModel):
phone: str = Field(..., pattern=r"^1[3-9]\d{9}$")
password: str = Field(..., min_length=6, max_length=50)
name: str = Field(..., min_length=2, max_length=50)
subject: str = Field(default="", max_length=50)
school: str = Field(default="", max_length=100)
grade: str = Field(default="", max_length=50)
class UserUpdate(BaseModel):
name: Optional[str] = None
avatar: Optional[str] = None
subject: Optional[str] = None
school: Optional[str] = None
grade: Optional[str] = None
class PasswordChange(BaseModel):
old_password: str = Field(..., min_length=6, max_length=50, description="当前密码")
new_password: str = Field(..., min_length=6, max_length=50, description="新密码")
class UserOut(BaseModel):
id: int
phone: str
name: str
avatar: str = ""
subject: str = ""
school: str = ""
grade: str = ""
role: str = "teacher"
credits: int = 0
total_credits_granted: int = 0
total_credits_used: int = 0
created_at: datetime | None = None
@field_validator("avatar", "subject", "school", "grade", "name", "role", mode="before")
@classmethod
def _none_to_empty(cls, v):
return "" if v is None else v
model_config = {"from_attributes": True}
class Token(BaseModel):
access_token: str
refresh_token: str
token_type: str = "bearer"
class TokenPayload(BaseModel):
sub: int
exp: datetime
class ProfileRecentItem(BaseModel):
id: int
item_type: str
title: str
subtitle: str = ""
status: str = ""
updated_at: datetime | None = None
class CreditTransactionOut(BaseModel):
id: int
amount: int
action: str = ""
description: str = ""
balance_after: int = 0
created_at: datetime | None = None
model_config = {"from_attributes": True}
class CreditBenefitOut(BaseModel):
credits: int = 0
daily_amount: int = 0
daily_claimed: bool = False
next_claim_text: str = ""
recent_checkins: list[CreditTransactionOut] = Field(default_factory=list)
class ProfileStatsOut(BaseModel):
credits: int = 0
total_credits_granted: int = 0
total_credits_used: int = 0
total_works: int = 0
draft_works: int = 0
published_works: int = 0
published_resources: int = 0
public_resources: int = 0
private_resources: int = 0
favorite_resources: int = 0
total_views: int = 0
total_downloads: int = 0
total_favorites: int = 0
classroom_activities: int = 0
active_classroom_activities: int = 0
classroom_responses: int = 0
community_posts: int = 0
community_favorites: int = 0
community_comments: int = 0
works_by_type: dict[str, int] = Field(default_factory=dict)
resources_by_type: dict[str, int] = Field(default_factory=dict)
recent_works: list[ProfileRecentItem] = Field(default_factory=list)
recent_resources: list[ProfileRecentItem] = Field(default_factory=list)
recent_interactions: list[ProfileRecentItem] = Field(default_factory=list)
recent_credit_transactions: list[CreditTransactionOut] = Field(default_factory=list)
class SendCodeRequest(BaseModel):
phone: str = Field(..., pattern=r"^1[3-9]\d{9}$", description="手机号")
class ResetPasswordRequest(BaseModel):
phone: str = Field(..., pattern=r"^1[3-9]\d{9}$")
code: str = Field(..., min_length=6, max_length=6, description="6位验证码")
new_password: str = Field(..., min_length=6, max_length=50, description="新密码")