- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
271 lines
8.3 KiB
Python
271 lines
8.3 KiB
Python
"""
|
||
课程相关数据库模型
|
||
"""
|
||
from enum import Enum
|
||
from typing import List, Optional
|
||
from datetime import datetime
|
||
|
||
from sqlalchemy import (
|
||
String,
|
||
Text,
|
||
Integer,
|
||
Boolean,
|
||
ForeignKey,
|
||
Enum as SQLEnum,
|
||
Float,
|
||
JSON,
|
||
DateTime,
|
||
)
|
||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||
|
||
from app.models.base import BaseModel, SoftDeleteMixin, AuditMixin
|
||
|
||
|
||
class CourseStatus(str, Enum):
|
||
"""课程状态枚举"""
|
||
|
||
DRAFT = "draft" # 草稿
|
||
PUBLISHED = "published" # 已发布
|
||
ARCHIVED = "archived" # 已归档
|
||
|
||
|
||
class CourseCategory(str, Enum):
|
||
"""课程分类枚举"""
|
||
|
||
TECHNOLOGY = "technology" # 技术
|
||
MANAGEMENT = "management" # 管理
|
||
BUSINESS = "business" # 业务
|
||
GENERAL = "general" # 通用
|
||
|
||
|
||
class Course(BaseModel, SoftDeleteMixin, AuditMixin):
|
||
"""
|
||
课程表
|
||
"""
|
||
|
||
__tablename__ = "courses"
|
||
|
||
# 基本信息
|
||
name: Mapped[str] = mapped_column(String(200), nullable=False, comment="课程名称")
|
||
description: Mapped[Optional[str]] = mapped_column(
|
||
Text, nullable=True, comment="课程描述"
|
||
)
|
||
category: Mapped[CourseCategory] = mapped_column(
|
||
SQLEnum(
|
||
CourseCategory,
|
||
values_callable=lambda enum_cls: [e.value for e in enum_cls],
|
||
validate_strings=True,
|
||
),
|
||
default=CourseCategory.GENERAL,
|
||
nullable=False,
|
||
comment="课程分类",
|
||
)
|
||
status: Mapped[CourseStatus] = mapped_column(
|
||
SQLEnum(
|
||
CourseStatus,
|
||
values_callable=lambda enum_cls: [e.value for e in enum_cls],
|
||
validate_strings=True,
|
||
),
|
||
default=CourseStatus.DRAFT,
|
||
nullable=False,
|
||
comment="课程状态",
|
||
)
|
||
|
||
# 课程详情
|
||
cover_image: Mapped[Optional[str]] = mapped_column(
|
||
String(500), nullable=True, comment="封面图片URL"
|
||
)
|
||
duration_hours: Mapped[Optional[float]] = mapped_column(
|
||
Float, nullable=True, comment="课程时长(小时)"
|
||
)
|
||
difficulty_level: Mapped[Optional[int]] = mapped_column(
|
||
Integer, nullable=True, comment="难度等级(1-5)"
|
||
)
|
||
tags: Mapped[Optional[List[str]]] = mapped_column(
|
||
JSON, nullable=True, comment="标签列表"
|
||
)
|
||
|
||
# 发布信息
|
||
published_at: Mapped[Optional[datetime]] = mapped_column(
|
||
DateTime(timezone=True), nullable=True, comment="发布时间"
|
||
)
|
||
publisher_id: Mapped[Optional[int]] = mapped_column(
|
||
Integer, nullable=True, comment="发布人ID"
|
||
)
|
||
|
||
# 播课信息
|
||
# 播课功能(Coze工作流直接写数据库)
|
||
broadcast_audio_url: Mapped[Optional[str]] = mapped_column(
|
||
String(500), nullable=True, comment="播课音频URL"
|
||
)
|
||
broadcast_generated_at: Mapped[Optional[datetime]] = mapped_column(
|
||
DateTime(timezone=True), nullable=True, comment="播课生成时间"
|
||
)
|
||
|
||
# 排序和权重
|
||
sort_order: Mapped[int] = mapped_column(
|
||
Integer, default=0, nullable=False, comment="排序顺序"
|
||
)
|
||
is_featured: Mapped[bool] = mapped_column(
|
||
Boolean, default=False, nullable=False, comment="是否推荐"
|
||
)
|
||
|
||
# 统计信息
|
||
student_count: Mapped[int] = mapped_column(
|
||
Integer, default=0, nullable=False, comment="学习人数"
|
||
)
|
||
is_new: Mapped[bool] = mapped_column(
|
||
Boolean, default=True, nullable=False, comment="是否新课程"
|
||
)
|
||
|
||
# 资料下载设置
|
||
allow_download: Mapped[bool] = mapped_column(
|
||
Boolean, default=False, nullable=False, comment="是否允许下载资料"
|
||
)
|
||
|
||
# 关联关系
|
||
materials: Mapped[List["CourseMaterial"]] = relationship(
|
||
"CourseMaterial", back_populates="course"
|
||
)
|
||
knowledge_points: Mapped[List["KnowledgePoint"]] = relationship(
|
||
"KnowledgePoint", back_populates="course"
|
||
)
|
||
|
||
# 岗位分配关系(通过关联表)
|
||
position_assignments = relationship("PositionCourse", back_populates="course", cascade="all, delete-orphan")
|
||
exams = relationship("Exam", back_populates="course")
|
||
questions = relationship("Question", back_populates="course")
|
||
|
||
|
||
class CourseMaterial(BaseModel, SoftDeleteMixin, AuditMixin):
|
||
"""
|
||
课程资料表
|
||
"""
|
||
|
||
__tablename__ = "course_materials"
|
||
|
||
course_id: Mapped[int] = mapped_column(
|
||
Integer,
|
||
ForeignKey("courses.id", ondelete="CASCADE"),
|
||
nullable=False,
|
||
comment="课程ID",
|
||
)
|
||
name: Mapped[str] = mapped_column(String(200), nullable=False, comment="资料名称")
|
||
description: Mapped[Optional[str]] = mapped_column(
|
||
Text, nullable=True, comment="资料描述"
|
||
)
|
||
file_url: Mapped[str] = mapped_column(String(500), nullable=False, comment="文件URL")
|
||
file_type: Mapped[str] = mapped_column(String(50), nullable=False, comment="文件类型")
|
||
file_size: Mapped[int] = mapped_column(Integer, nullable=False, comment="文件大小(字节)")
|
||
|
||
# 排序
|
||
sort_order: Mapped[int] = mapped_column(
|
||
Integer, default=0, nullable=False, comment="排序顺序"
|
||
)
|
||
|
||
# 关联关系
|
||
course: Mapped["Course"] = relationship("Course", back_populates="materials")
|
||
# 关联的知识点(直接关联)
|
||
knowledge_points: Mapped[List["KnowledgePoint"]] = relationship(
|
||
"KnowledgePoint", back_populates="material"
|
||
)
|
||
|
||
|
||
class KnowledgePoint(BaseModel, SoftDeleteMixin, AuditMixin):
|
||
"""
|
||
知识点表
|
||
"""
|
||
|
||
__tablename__ = "knowledge_points"
|
||
|
||
course_id: Mapped[int] = mapped_column(
|
||
Integer,
|
||
ForeignKey("courses.id", ondelete="CASCADE"),
|
||
nullable=False,
|
||
comment="课程ID",
|
||
)
|
||
material_id: Mapped[int] = mapped_column(
|
||
Integer,
|
||
ForeignKey("course_materials.id", ondelete="CASCADE"),
|
||
nullable=False,
|
||
comment="关联资料ID",
|
||
)
|
||
name: Mapped[str] = mapped_column(String(200), nullable=False, comment="知识点名称")
|
||
description: Mapped[Optional[str]] = mapped_column(
|
||
Text, nullable=True, comment="知识点描述"
|
||
)
|
||
type: Mapped[str] = mapped_column(
|
||
String(50), default="概念定义", nullable=False, comment="知识点类型"
|
||
)
|
||
source: Mapped[int] = mapped_column(
|
||
Integer, default=0, nullable=False, comment="来源:0=手动,1=AI分析"
|
||
)
|
||
topic_relation: Mapped[Optional[str]] = mapped_column(
|
||
String(200), nullable=True, comment="与主题的关系描述"
|
||
)
|
||
|
||
# 关联关系
|
||
course: Mapped["Course"] = relationship("Course", back_populates="knowledge_points")
|
||
material: Mapped["CourseMaterial"] = relationship("CourseMaterial")
|
||
|
||
|
||
class GrowthPath(BaseModel, SoftDeleteMixin):
|
||
"""
|
||
成长路径表
|
||
"""
|
||
|
||
__tablename__ = "growth_paths"
|
||
|
||
name: Mapped[str] = mapped_column(String(200), nullable=False, comment="路径名称")
|
||
description: Mapped[Optional[str]] = mapped_column(
|
||
Text, nullable=True, comment="路径描述"
|
||
)
|
||
target_role: Mapped[Optional[str]] = mapped_column(
|
||
String(100), nullable=True, comment="目标角色"
|
||
)
|
||
|
||
# 路径配置
|
||
courses: Mapped[Optional[List[dict]]] = mapped_column(
|
||
JSON, nullable=True, comment="课程列表[{course_id, order, is_required}]"
|
||
)
|
||
|
||
# 预计时长
|
||
estimated_duration_days: Mapped[Optional[int]] = mapped_column(
|
||
Integer, nullable=True, comment="预计完成天数"
|
||
)
|
||
|
||
# 状态
|
||
is_active: Mapped[bool] = mapped_column(
|
||
Boolean, default=True, nullable=False, comment="是否启用"
|
||
)
|
||
sort_order: Mapped[int] = mapped_column(
|
||
Integer, default=0, nullable=False, comment="排序顺序"
|
||
)
|
||
|
||
|
||
class MaterialKnowledgePoint(BaseModel, SoftDeleteMixin):
|
||
"""
|
||
资料知识点关联表
|
||
"""
|
||
|
||
__tablename__ = "material_knowledge_points"
|
||
|
||
material_id: Mapped[int] = mapped_column(
|
||
Integer,
|
||
ForeignKey("course_materials.id", ondelete="CASCADE"),
|
||
nullable=False,
|
||
comment="资料ID",
|
||
)
|
||
knowledge_point_id: Mapped[int] = mapped_column(
|
||
Integer,
|
||
ForeignKey("knowledge_points.id", ondelete="CASCADE"),
|
||
nullable=False,
|
||
comment="知识点ID",
|
||
)
|
||
sort_order: Mapped[int] = mapped_column(
|
||
Integer, default=0, nullable=False, comment="排序顺序"
|
||
)
|
||
is_ai_generated: Mapped[bool] = mapped_column(
|
||
Boolean, default=False, nullable=False, comment="是否AI生成"
|
||
)
|