156 lines
8.0 KiB
Python
156 lines
8.0 KiB
Python
"""培训与技能认证体系:课程/章节/课时/报名/进度/证书
|
|
|
|
Revision ID: 0047
|
|
Revises: 0046
|
|
Create Date: 2026-09-02
|
|
"""
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
revision = "0047"
|
|
down_revision = "0046_credit_certification_full"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade():
|
|
# courses
|
|
op.create_table(
|
|
"training_courses",
|
|
sa.Column("id", sa.String(32), primary_key=True),
|
|
sa.Column("title", sa.String(200), default=""),
|
|
sa.Column("subtitle", sa.String(500), default=""),
|
|
sa.Column("description", sa.Text, default=""),
|
|
sa.Column("cover_url", sa.String(500), default=""),
|
|
sa.Column("category", sa.String(50), default="", index=True),
|
|
sa.Column("level", sa.String(20), default="beginner"),
|
|
sa.Column("price", sa.Integer, default=0),
|
|
sa.Column("duration_minutes", sa.Integer, default=0),
|
|
sa.Column("lesson_count", sa.Integer, default=0),
|
|
sa.Column("chapter_count", sa.Integer, default=0),
|
|
sa.Column("instructor_id", sa.String(32), default=""),
|
|
sa.Column("instructor_name", sa.String(100), default=""),
|
|
sa.Column("instructor_title", sa.String(100), default=""),
|
|
sa.Column("instructor_avatar", sa.String(500), default=""),
|
|
sa.Column("tags_json", sa.Text, default="[]"),
|
|
sa.Column("prerequisites", sa.Text, default=""),
|
|
sa.Column("learning_outcomes", sa.Text, default=""),
|
|
sa.Column("status", sa.String(20), default="draft"),
|
|
sa.Column("is_featured", sa.Integer, default=0),
|
|
sa.Column("sort_order", sa.Integer, default=0),
|
|
sa.Column("enroll_count", sa.Integer, default=0),
|
|
sa.Column("rating", sa.Float, default=0.0),
|
|
sa.Column("rating_count", sa.Integer, default=0),
|
|
sa.Column("created_at", sa.String(32), default=""),
|
|
sa.Column("updated_at", sa.String(32), default=""),
|
|
)
|
|
|
|
# course_chapters
|
|
op.create_table(
|
|
"training_chapters",
|
|
sa.Column("id", sa.String(32), primary_key=True),
|
|
sa.Column("course_id", sa.String(32), default="", index=True),
|
|
sa.Column("title", sa.String(200), default=""),
|
|
sa.Column("description", sa.String(500), default=""),
|
|
sa.Column("sort_order", sa.Integer, default=0),
|
|
sa.Column("created_at", sa.String(32), default=""),
|
|
)
|
|
|
|
# course_lessons
|
|
op.create_table(
|
|
"training_lessons",
|
|
sa.Column("id", sa.String(32), primary_key=True),
|
|
sa.Column("chapter_id", sa.String(32), default="", index=True),
|
|
sa.Column("course_id", sa.String(32), default="", index=True),
|
|
sa.Column("title", sa.String(200), default=""),
|
|
sa.Column("type", sa.String(20), default="video"),
|
|
sa.Column("content", sa.Text, default=""),
|
|
sa.Column("duration_minutes", sa.Integer, default=0),
|
|
sa.Column("sort_order", sa.Integer, default=0),
|
|
sa.Column("is_free", sa.Integer, default=0),
|
|
sa.Column("is_exam", sa.Integer, default=0),
|
|
sa.Column("created_at", sa.String(32), default=""),
|
|
)
|
|
|
|
# course_enrollments
|
|
op.create_table(
|
|
"training_course_enrollments",
|
|
sa.Column("id", sa.String(32), primary_key=True),
|
|
sa.Column("user_id", sa.String(32), default="", index=True),
|
|
sa.Column("course_id", sa.String(32), default="", index=True),
|
|
sa.Column("status", sa.String(20), default="enrolled"),
|
|
sa.Column("progress_percent", sa.Integer, default=0),
|
|
sa.Column("completed_lessons", sa.Integer, default=0),
|
|
sa.Column("total_lessons", sa.Integer, default=0),
|
|
sa.Column("last_lesson_id", sa.String(32), default=""),
|
|
sa.Column("last_studied_at", sa.String(32), default=""),
|
|
sa.Column("total_watch_seconds", sa.Integer, default=0),
|
|
sa.Column("enrolled_at", sa.String(32), default=""),
|
|
sa.Column("completed_at", sa.String(32), default=""),
|
|
sa.Column("price_paid", sa.Integer, default=0),
|
|
sa.Column("created_at", sa.String(32), default=""),
|
|
)
|
|
|
|
# course_lesson_progress
|
|
op.create_table(
|
|
"training_lesson_progress",
|
|
sa.Column("id", sa.String(32), primary_key=True),
|
|
sa.Column("enrollment_id", sa.String(32), default="", index=True),
|
|
sa.Column("user_id", sa.String(32), default="", index=True),
|
|
sa.Column("lesson_id", sa.String(32), default="", index=True),
|
|
sa.Column("course_id", sa.String(32), default="", index=True),
|
|
sa.Column("status", sa.String(20), default="not_started"),
|
|
sa.Column("watch_seconds", sa.Integer, default=0),
|
|
sa.Column("quiz_score", sa.Integer, default=0),
|
|
sa.Column("started_at", sa.String(32), default=""),
|
|
sa.Column("completed_at", sa.String(32), default=""),
|
|
sa.Column("updated_at", sa.String(32), default=""),
|
|
)
|
|
|
|
# course_certificates
|
|
op.create_table(
|
|
"training_certificates",
|
|
sa.Column("id", sa.String(32), primary_key=True),
|
|
sa.Column("certificate_no", sa.String(50), default="", index=True),
|
|
sa.Column("user_id", sa.String(32), default="", index=True),
|
|
sa.Column("course_id", sa.String(32), default="", index=True),
|
|
sa.Column("course_title", sa.String(200), default=""),
|
|
sa.Column("instructor_name", sa.String(100), default=""),
|
|
sa.Column("duration_minutes", sa.Integer, default=0),
|
|
sa.Column("final_score", sa.Integer, default=0),
|
|
sa.Column("issued_at", sa.String(32), default=""),
|
|
sa.Column("badge_id", sa.String(32), default=""),
|
|
sa.Column("is_verified", sa.Integer, default=1),
|
|
sa.Column("created_at", sa.String(32), default=""),
|
|
)
|
|
|
|
# 种子数据:8个技能领域的示例课程(draft状态,运营可编辑发布)
|
|
from datetime import datetime
|
|
now = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
seed_courses = [
|
|
("UI设计入门到精通", "ui_design", "beginner", 0, 120, 12, 3, "张设计", "资深UI设计师", "从基础设计原则到完整项目实战,涵盖Figma/Sketch工具使用"),
|
|
("平面设计实战课程", "graphic_design", "beginner", 0, 90, 10, 2, "李创意", "品牌设计专家", "海报/LOGO/品牌VI设计全流程教学"),
|
|
("前端开发工程师培养", "frontend_dev", "intermediate", 19900, 180, 20, 4, "王前端", "全栈开发工程师", "HTML/CSS/JavaScript/React/Vue全栈前端开发"),
|
|
("后端开发实战", "backend_dev", "intermediate", 19900, 200, 22, 5, "赵后端", "后端架构师", "Python/Node.js/数据库/API设计后端开发"),
|
|
("文案写作训练营", "copywriting", "beginner", 0, 60, 8, 2, "陈文案", "资深文案策划", "从标题到结尾,写出高转化率的营销文案"),
|
|
("翻译技能提升", "translation", "intermediate", 9900, 90, 10, 3, "刘翻译", "专业翻译师", "中英互译技巧、行业术语、CAT工具使用"),
|
|
("数据分析入门", "data_analysis", "beginner", 0, 100, 12, 3, "孙数据", "数据分析师", "Excel/SQL/Python数据分析全流程"),
|
|
("项目管理实战", "project_management", "intermediate", 14900, 120, 14, 3, "周项目", "PMP认证项目经理", "敏捷/瀑布项目管理方法与工具实战"),
|
|
]
|
|
for i, (title, cat, level, price, dur, lessons, chapters, instructor, inst_title, desc) in enumerate(seed_courses):
|
|
op.execute(
|
|
f"INSERT INTO training_courses (id, title, description, category, level, price, duration_minutes, "
|
|
f"lesson_count, chapter_count, instructor_name, instructor_title, status, sort_order, created_at, updated_at) "
|
|
f"VALUES ('course_seed_{i+1:03d}', '{title}', '{desc}', '{cat}', '{level}', {price}, {dur}, "
|
|
f"{lessons}, {chapters}, '{instructor}', '{inst_title}', 'draft', {i+1}, '{now}', '{now}')"
|
|
)
|
|
|
|
|
|
def downgrade():
|
|
op.drop_table("training_certificates")
|
|
op.drop_table("training_lesson_progress")
|
|
op.drop_table("training_course_enrollments")
|
|
op.drop_table("training_lessons")
|
|
op.drop_table("training_chapters")
|
|
op.drop_table("training_courses")
|