Files
server-core/alembic/versions/0047_training_certification.py
T

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")