Files
012-kaopeilian/backend/app/schemas/course.py
111 998211c483 feat: 初始化考培练系统项目
- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
2026-01-24 19:33:28 +08:00

365 lines
14 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.
"""
课程相关的数据验证模型
"""
from typing import Optional, List
from datetime import datetime
from enum import Enum
from pydantic import BaseModel, Field, ConfigDict, field_validator
from app.models.course import CourseStatus, CourseCategory
class CourseBase(BaseModel):
"""
课程基础模型
"""
name: str = Field(..., min_length=1, max_length=200, description="课程名称")
description: Optional[str] = Field(None, description="课程描述")
category: CourseCategory = Field(default=CourseCategory.GENERAL, description="课程分类")
cover_image: Optional[str] = Field(None, max_length=500, description="封面图片URL")
duration_hours: Optional[float] = Field(None, ge=0, description="课程时长(小时)")
difficulty_level: Optional[int] = Field(None, ge=1, le=5, description="难度等级(1-5)")
tags: Optional[List[str]] = Field(default_factory=list, description="标签列表")
sort_order: int = Field(default=0, description="排序顺序")
is_featured: bool = Field(default=False, description="是否推荐")
allow_download: bool = Field(default=False, description="是否允许下载资料")
@field_validator("category", mode="before")
@classmethod
def normalize_category(cls, v):
"""允许使用枚举的名称或值(忽略大小写)。空字符串使用默认值。"""
if isinstance(v, CourseCategory):
return v
if isinstance(v, str):
s = v.strip()
# 空字符串使用默认值
if not s:
return CourseCategory.GENERAL
# 优先按值匹配technology 等)
try:
return CourseCategory(s.lower())
except Exception:
pass
# 再按名称匹配TECHNOLOGY 等)
try:
return CourseCategory[s.upper()]
except Exception:
pass
return v
class CourseCreate(CourseBase):
"""
创建课程模型
"""
status: CourseStatus = Field(default=CourseStatus.DRAFT, description="课程状态")
class CourseUpdate(BaseModel):
"""
更新课程模型
"""
name: Optional[str] = Field(None, min_length=1, max_length=200, description="课程名称")
description: Optional[str] = Field(None, description="课程描述")
category: Optional[CourseCategory] = Field(None, description="课程分类")
status: Optional[CourseStatus] = Field(None, description="课程状态")
cover_image: Optional[str] = Field(None, max_length=500, description="封面图片URL")
duration_hours: Optional[float] = Field(None, ge=0, description="课程时长(小时)")
difficulty_level: Optional[int] = Field(None, ge=1, le=5, description="难度等级(1-5)")
tags: Optional[List[str]] = Field(None, description="标签列表")
sort_order: Optional[int] = Field(None, description="排序顺序")
is_featured: Optional[bool] = Field(None, description="是否推荐")
allow_download: Optional[bool] = Field(None, description="是否允许下载资料")
@field_validator("category", mode="before")
@classmethod
def normalize_category_update(cls, v):
if v is None:
return v
if isinstance(v, CourseCategory):
return v
if isinstance(v, str):
s = v.strip()
if not s: # 空字符串视为None不更新
return None
try:
return CourseCategory(s.lower())
except Exception:
pass
try:
return CourseCategory[s.upper()]
except Exception:
pass
return v
class CourseInDB(CourseBase):
"""
数据库中的课程模型
"""
model_config = ConfigDict(from_attributes=True)
id: int = Field(..., description="课程ID")
status: CourseStatus = Field(..., description="课程状态")
created_at: datetime = Field(..., description="创建时间")
updated_at: datetime = Field(..., description="更新时间")
published_at: Optional[datetime] = Field(None, description="发布时间")
publisher_id: Optional[int] = Field(None, description="发布人ID")
created_by: Optional[int] = Field(None, description="创建人ID")
updated_by: Optional[int] = Field(None, description="更新人ID")
# 用户岗位相关的课程类型(必修/选修非数据库字段由API动态计算
course_type: Optional[str] = Field(None, description="课程类型required=必修, optional=选修")
class CourseList(BaseModel):
"""
课程列表查询参数
"""
status: Optional[CourseStatus] = Field(None, description="课程状态")
category: Optional[CourseCategory] = Field(None, description="课程分类")
is_featured: Optional[bool] = Field(None, description="是否推荐")
keyword: Optional[str] = Field(None, description="搜索关键词")
# 课程资料相关模型
class CourseMaterialBase(BaseModel):
"""
课程资料基础模型
"""
name: str = Field(..., min_length=1, max_length=200, description="资料名称")
description: Optional[str] = Field(None, description="资料描述")
sort_order: int = Field(default=0, description="排序顺序")
class CourseMaterialCreate(CourseMaterialBase):
"""
创建课程资料模型
"""
file_url: str = Field(..., max_length=500, description="文件URL")
file_type: str = Field(..., max_length=50, description="文件类型")
file_size: int = Field(..., gt=0, description="文件大小(字节)")
@field_validator("file_type")
def validate_file_type(cls, v):
"""验证文件类型
支持格式TXT、Markdown、MDX、PDF、HTML、Excel、Word、CSV、VTT、Properties
"""
allowed_types = [
"txt", "md", "mdx", "pdf", "html", "htm",
"xlsx", "xls", "docx", "doc", "csv", "vtt", "properties"
]
file_ext = v.lower()
if file_ext not in allowed_types:
raise ValueError(f"不支持的文件类型: {v}。允许的类型: TXT、Markdown、MDX、PDF、HTML、Excel、Word、CSV、VTT、Properties")
return file_ext
class CourseMaterialInDB(CourseMaterialBase):
"""
数据库中的课程资料模型
"""
model_config = ConfigDict(from_attributes=True)
id: int = Field(..., description="资料ID")
course_id: int = Field(..., description="课程ID")
file_url: str = Field(..., description="文件URL")
file_type: str = Field(..., description="文件类型")
file_size: int = Field(..., description="文件大小(字节)")
created_at: datetime = Field(..., description="创建时间")
updated_at: datetime = Field(..., description="更新时间")
# 知识点相关模型
class KnowledgePointBase(BaseModel):
"""
知识点基础模型
"""
name: str = Field(..., min_length=1, max_length=200, description="知识点名称")
description: Optional[str] = Field(None, description="知识点描述")
type: str = Field(default="理论知识", description="知识点类型")
source: int = Field(default=0, description="来源0=手动1=AI分析")
topic_relation: Optional[str] = Field(None, description="与主题的关系描述")
class KnowledgePointCreate(KnowledgePointBase):
"""
创建知识点模型
"""
material_id: int = Field(..., description="关联资料ID必填")
class KnowledgePointUpdate(BaseModel):
"""
更新知识点模型
"""
name: Optional[str] = Field(None, min_length=1, max_length=200, description="知识点名称")
description: Optional[str] = Field(None, description="知识点描述")
type: Optional[str] = Field(None, description="知识点类型")
source: Optional[int] = Field(None, description="来源0=手动1=AI分析")
topic_relation: Optional[str] = Field(None, description="与主题的关系描述")
material_id: int = Field(..., description="关联资料ID必填")
class KnowledgePointInDB(KnowledgePointBase):
"""
数据库中的知识点模型
"""
model_config = ConfigDict(from_attributes=True)
id: int = Field(..., description="知识点ID")
course_id: int = Field(..., description="课程ID")
material_id: int = Field(..., description="关联资料ID")
created_at: datetime = Field(..., description="创建时间")
updated_at: datetime = Field(..., description="更新时间")
class KnowledgePointTree(KnowledgePointInDB):
"""
知识点树形结构
"""
children: List["KnowledgePointTree"] = Field(
default_factory=list, description="子知识点"
)
# 成长路径相关模型
class GrowthPathCourse(BaseModel):
"""
成长路径中的课程
"""
course_id: int = Field(..., description="课程ID")
order: int = Field(..., ge=0, description="排序")
is_required: bool = Field(default=True, description="是否必修")
class GrowthPathBase(BaseModel):
"""
成长路径基础模型
"""
name: str = Field(..., min_length=1, max_length=200, description="路径名称")
description: Optional[str] = Field(None, description="路径描述")
target_role: Optional[str] = Field(None, max_length=100, description="目标角色")
courses: List[GrowthPathCourse] = Field(default_factory=list, description="课程列表")
estimated_duration_days: Optional[int] = Field(None, ge=1, description="预计完成天数")
is_active: bool = Field(default=True, description="是否启用")
sort_order: int = Field(default=0, description="排序顺序")
class GrowthPathCreate(GrowthPathBase):
"""
创建成长路径模型
"""
pass
class GrowthPathInDB(GrowthPathBase):
"""
数据库中的成长路径模型
"""
model_config = ConfigDict(from_attributes=True)
id: int = Field(..., description="路径ID")
created_at: datetime = Field(..., description="创建时间")
updated_at: datetime = Field(..., description="更新时间")
# 课程考试设置相关Schema
class CourseExamSettingsBase(BaseModel):
"""
课程考试设置基础模型
"""
single_choice_count: int = Field(default=4, ge=0, le=50, description="单选题数量")
multiple_choice_count: int = Field(default=2, ge=0, le=30, description="多选题数量")
true_false_count: int = Field(default=1, ge=0, le=20, description="判断题数量")
fill_blank_count: int = Field(default=2, ge=0, le=10, description="填空题数量")
essay_count: int = Field(default=1, ge=0, le=10, description="问答题数量")
duration_minutes: int = Field(default=10, ge=10, le=180, description="考试时长(分钟)")
difficulty_level: int = Field(default=3, ge=1, le=5, description="难度系数(1-5)")
passing_score: int = Field(default=60, ge=0, le=100, description="及格分数")
is_enabled: bool = Field(default=True, description="是否启用")
show_answer_immediately: bool = Field(default=False, description="是否立即显示答案")
allow_retake: bool = Field(default=True, description="是否允许重考")
max_retake_times: Optional[int] = Field(None, ge=1, le=10, description="最大重考次数")
class CourseExamSettingsCreate(CourseExamSettingsBase):
"""
创建课程考试设置模型
"""
pass
class CourseExamSettingsUpdate(BaseModel):
"""
更新课程考试设置模型
"""
single_choice_count: Optional[int] = Field(None, ge=0, le=50, description="单选题数量")
multiple_choice_count: Optional[int] = Field(None, ge=0, le=30, description="多选题数量")
true_false_count: Optional[int] = Field(None, ge=0, le=20, description="判断题数量")
fill_blank_count: Optional[int] = Field(None, ge=0, le=10, description="填空题数量")
essay_count: Optional[int] = Field(None, ge=0, le=10, description="问答题数量")
duration_minutes: Optional[int] = Field(None, ge=10, le=180, description="考试时长(分钟)")
difficulty_level: Optional[int] = Field(None, ge=1, le=5, description="难度系数(1-5)")
passing_score: Optional[int] = Field(None, ge=0, le=100, description="及格分数")
is_enabled: Optional[bool] = Field(None, description="是否启用")
show_answer_immediately: Optional[bool] = Field(None, description="是否立即显示答案")
allow_retake: Optional[bool] = Field(None, description="是否允许重考")
max_retake_times: Optional[int] = Field(None, ge=1, le=10, description="最大重考次数")
class CourseExamSettingsInDB(CourseExamSettingsBase):
"""
数据库中的课程考试设置模型
"""
model_config = ConfigDict(from_attributes=True)
id: int = Field(..., description="设置ID")
course_id: int = Field(..., description="课程ID")
created_at: datetime = Field(..., description="创建时间")
updated_at: datetime = Field(..., description="更新时间")
# 岗位分配相关Schema
class CoursePositionAssignment(BaseModel):
"""
课程岗位分配模型
"""
position_id: int = Field(..., description="岗位ID")
course_type: str = Field(default="required", pattern="^(required|optional)$", description="课程类型required必修/optional选修")
priority: int = Field(default=0, description="优先级/排序")
class CoursePositionAssignmentInDB(CoursePositionAssignment):
"""
数据库中的课程岗位分配模型
"""
model_config = ConfigDict(from_attributes=True)
id: int = Field(..., description="分配ID")
course_id: int = Field(..., description="课程ID")
position_name: Optional[str] = Field(None, description="岗位名称")
position_description: Optional[str] = Field(None, description="岗位描述")
member_count: Optional[int] = Field(None, description="岗位成员数")