智教助手平台:完整初始化
- 前端: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:
@@ -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 *
|
||||
@@ -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
|
||||
@@ -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] = []
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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)
|
||||
@@ -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="新密码")
|
||||
Reference in New Issue
Block a user