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

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

708 lines
37 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.
"""add training models
Revision ID: 9245f8845fe1
Revises: 001
Create Date: 2025-09-21 22:11:03.319902
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision: str = '9245f8845fe1'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('training_scenes',
sa.Column('name', sa.String(length=100), nullable=False, comment='场景名称'),
sa.Column('description', sa.Text(), nullable=True, comment='场景描述'),
sa.Column('category', sa.String(length=50), nullable=False, comment='场景分类'),
sa.Column('ai_config', sa.JSON(), nullable=True, comment='AI配置如Coze Bot ID等'),
sa.Column('prompt_template', sa.Text(), nullable=True, comment='提示词模板'),
sa.Column('evaluation_criteria', sa.JSON(), nullable=True, comment='评估标准'),
sa.Column('status', sa.Enum('DRAFT', 'ACTIVE', 'INACTIVE', name='trainingscenestatus'), nullable=False, comment='场景状态'),
sa.Column('is_public', sa.Boolean(), nullable=False, comment='是否公开'),
sa.Column('required_level', sa.Integer(), nullable=True, comment='所需用户等级'),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.Column('is_deleted', sa.Boolean(), nullable=False),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('created_by', sa.Integer(), nullable=True),
sa.Column('updated_by', sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_training_scenes_id'), 'training_scenes', ['id'], unique=False)
op.create_table('training_sessions',
sa.Column('user_id', sa.Integer(), nullable=False, comment='用户ID'),
sa.Column('scene_id', sa.Integer(), nullable=False, comment='场景ID'),
sa.Column('coze_conversation_id', sa.String(length=100), nullable=True, comment='Coze会话ID'),
sa.Column('start_time', sa.DateTime(), nullable=False, comment='开始时间'),
sa.Column('end_time', sa.DateTime(), nullable=True, comment='结束时间'),
sa.Column('duration_seconds', sa.Integer(), nullable=True, comment='持续时长(秒)'),
sa.Column('status', sa.Enum('CREATED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED', 'ERROR', name='trainingsessionstatus'), nullable=False, comment='会话状态'),
sa.Column('session_config', sa.JSON(), nullable=True, comment='会话配置'),
sa.Column('total_score', sa.Float(), nullable=True, comment='总分'),
sa.Column('evaluation_result', sa.JSON(), nullable=True, comment='评估结果详情'),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.Column('created_by', sa.Integer(), nullable=True),
sa.Column('updated_by', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['scene_id'], ['training_scenes.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_training_sessions_id'), 'training_sessions', ['id'], unique=False)
op.create_index(op.f('ix_training_sessions_user_id'), 'training_sessions', ['user_id'], unique=False)
op.create_table('training_messages',
sa.Column('session_id', sa.Integer(), nullable=False, comment='会话ID'),
sa.Column('role', sa.Enum('USER', 'ASSISTANT', 'SYSTEM', name='messagerole'), nullable=False, comment='消息角色'),
sa.Column('type', sa.Enum('TEXT', 'VOICE', 'SYSTEM', name='messagetype'), nullable=False, comment='消息类型'),
sa.Column('content', sa.Text(), nullable=False, comment='消息内容'),
sa.Column('voice_url', sa.String(length=500), nullable=True, comment='语音文件URL'),
sa.Column('voice_duration', sa.Float(), nullable=True, comment='语音时长(秒)'),
sa.Column('message_metadata', sa.JSON(), nullable=True, comment='消息元数据'),
sa.Column('coze_message_id', sa.String(length=100), nullable=True, comment='Coze消息ID'),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['session_id'], ['training_sessions.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_training_messages_id'), 'training_messages', ['id'], unique=False)
op.create_table('training_reports',
sa.Column('session_id', sa.Integer(), nullable=False, comment='会话ID'),
sa.Column('user_id', sa.Integer(), nullable=False, comment='用户ID'),
sa.Column('overall_score', sa.Float(), nullable=False, comment='总体得分'),
sa.Column('dimension_scores', sa.JSON(), nullable=False, comment='各维度得分'),
sa.Column('strengths', sa.JSON(), nullable=False, comment='优势点'),
sa.Column('weaknesses', sa.JSON(), nullable=False, comment='待改进点'),
sa.Column('suggestions', sa.JSON(), nullable=False, comment='改进建议'),
sa.Column('detailed_analysis', sa.Text(), nullable=True, comment='详细分析'),
sa.Column('transcript', sa.Text(), nullable=True, comment='对话文本记录'),
sa.Column('statistics', sa.JSON(), nullable=True, comment='统计数据'),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.Column('created_by', sa.Integer(), nullable=True),
sa.Column('updated_by', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['session_id'], ['training_sessions.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('session_id')
)
op.create_index(op.f('ix_training_reports_id'), 'training_reports', ['id'], unique=False)
op.create_index(op.f('ix_training_reports_user_id'), 'training_reports', ['user_id'], unique=False)
op.create_table('user_teams',
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('team_id', sa.Integer(), nullable=False),
sa.Column('role', sa.String(length=50), nullable=False),
sa.Column('joined_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['team_id'], ['teams.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('user_id', 'team_id'),
sa.UniqueConstraint('user_id', 'team_id', name='uq_user_team')
)
op.alter_column('course_materials', 'course_id',
existing_type=mysql.INTEGER(),
comment='课程ID',
existing_comment='课ç¨ID',
existing_nullable=False)
op.alter_column('course_materials', 'name',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=200),
comment='资料名称',
existing_comment='资料å\x90\x8dç§°',
existing_nullable=False)
op.alter_column('course_materials', 'description',
existing_type=mysql.TEXT(collation='utf8mb4_unicode_ci'),
comment='资料描述',
existing_comment='资料æ\x8f\x8fè¿°',
existing_nullable=True)
op.alter_column('course_materials', 'file_url',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=500),
comment='文件URL',
existing_comment='æ‡ä»¶URL',
existing_nullable=False)
op.alter_column('course_materials', 'file_type',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=50),
comment='文件类型',
existing_comment='文件类型',
existing_nullable=False)
op.alter_column('course_materials', 'file_size',
existing_type=mysql.INTEGER(),
comment='文件大小(字节)',
existing_comment='文件大å°\x8f\xad—节)',
existing_nullable=False)
op.alter_column('course_materials', 'sort_order',
existing_type=mysql.INTEGER(),
comment='排序顺序',
existing_comment='排åº\x8f顺åº\x8f',
existing_nullable=False,
existing_server_default=sa.text("'0'"))
op.drop_index('idx_course_materials_course_id', table_name='course_materials')
op.drop_index('idx_course_materials_is_deleted', table_name='course_materials')
op.create_index(op.f('ix_course_materials_id'), 'course_materials', ['id'], unique=False)
op.drop_table_comment(
'course_materials',
existing_comment='课程资料表',
schema=None
)
op.alter_column('courses', 'name',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=200),
comment='课程名称',
existing_comment='课程å\x90\x8dç§°',
existing_nullable=False)
op.alter_column('courses', 'description',
existing_type=mysql.TEXT(collation='utf8mb4_unicode_ci'),
comment='课程描述',
existing_comment='课程æ\x8f\x8fè¿°',
existing_nullable=True)
op.alter_column('courses', 'category',
existing_type=mysql.ENUM('technology', 'management', 'business', 'general', collation='utf8mb4_unicode_ci'),
comment='课程分类',
existing_comment='课程分类',
existing_nullable=False,
existing_server_default=sa.text("'general'"))
op.alter_column('courses', 'status',
existing_type=mysql.ENUM('draft', 'published', 'archived', collation='utf8mb4_unicode_ci'),
comment='课程状态',
existing_comment='课程状æ€\x81',
existing_nullable=False,
existing_server_default=sa.text("'draft'"))
op.alter_column('courses', 'cover_image',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=500),
comment='封面图片URL',
existing_comment='å°\x81é\x9d¢å¾ç‰‡URL',
existing_nullable=True)
op.alter_column('courses', 'duration_hours',
existing_type=mysql.FLOAT(),
comment='课程时长(小时)',
existing_comment='课程时长(å°\x8fæ—¶)',
existing_nullable=True)
op.alter_column('courses', 'difficulty_level',
existing_type=mysql.INTEGER(),
comment='难度等级(1-5)',
existing_comment='难度ç\xad‰çº§(1-5)',
existing_nullable=True)
op.alter_column('courses', 'tags',
existing_type=mysql.JSON(),
comment='标签列表',
existing_comment='æ\xa0‡ç\xad¾åˆ—表',
existing_nullable=True)
op.alter_column('courses', 'published_at',
existing_type=mysql.DATETIME(),
comment='发布时间',
existing_comment='å\x8f‘布时间',
existing_nullable=True)
op.alter_column('courses', 'publisher_id',
existing_type=mysql.INTEGER(),
comment='发布人ID',
existing_comment='å\x8f布人ID',
existing_nullable=True)
op.alter_column('courses', 'sort_order',
existing_type=mysql.INTEGER(),
comment='排序顺序',
existing_comment='排åº\x8f顺åº\x8f',
existing_nullable=False,
existing_server_default=sa.text("'0'"))
op.alter_column('courses', 'is_featured',
existing_type=mysql.TINYINT(display_width=1),
comment='是否推荐',
existing_comment='是å\x90¦æŽ¨è\x8d\x90',
existing_nullable=False,
existing_server_default=sa.text("'0'"))
op.drop_index('idx_courses_category', table_name='courses')
op.drop_index('idx_courses_is_deleted', table_name='courses')
op.drop_index('idx_courses_status', table_name='courses')
op.create_index(op.f('ix_courses_id'), 'courses', ['id'], unique=False)
op.drop_table_comment(
'courses',
existing_comment='课程表',
schema=None
)
op.alter_column('growth_paths', 'name',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=200),
comment='路径名称',
existing_comment='路径å\x90\x8dç§°',
existing_nullable=False)
op.alter_column('growth_paths', 'description',
existing_type=mysql.TEXT(collation='utf8mb4_unicode_ci'),
comment='路径描述',
existing_comment='路径æ\x8f\x8fè¿°',
existing_nullable=True)
op.alter_column('growth_paths', 'target_role',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=100),
comment='目标角色',
existing_comment='ç›®æ\xa0‡è§’色',
existing_nullable=True)
op.alter_column('growth_paths', 'courses',
existing_type=mysql.JSON(),
comment='课程列表[{course_id, order, is_required}]',
existing_comment='课程列表[{course_id, order, is_required}]',
existing_nullable=True)
op.alter_column('growth_paths', 'estimated_duration_days',
existing_type=mysql.INTEGER(),
comment='预计完成天数',
existing_comment='预计完æˆ\x90天数',
existing_nullable=True)
op.alter_column('growth_paths', 'is_active',
existing_type=mysql.TINYINT(display_width=1),
comment='是否启用',
existing_comment='是å\x90¦å\x90¯ç”¨',
existing_nullable=False,
existing_server_default=sa.text("'1'"))
op.alter_column('growth_paths', 'sort_order',
existing_type=mysql.INTEGER(),
comment='排序顺序',
existing_comment='排åº\x8f顺åº\x8f',
existing_nullable=False,
existing_server_default=sa.text("'0'"))
op.drop_index('idx_growth_paths_is_active', table_name='growth_paths')
op.drop_index('idx_growth_paths_is_deleted', table_name='growth_paths')
op.create_index(op.f('ix_growth_paths_id'), 'growth_paths', ['id'], unique=False)
op.drop_table_comment(
'growth_paths',
existing_comment='æˆ\x90长路径表',
schema=None
)
op.alter_column('knowledge_points', 'course_id',
existing_type=mysql.INTEGER(),
comment='课程ID',
existing_comment='课ç¨ID',
existing_nullable=False)
op.alter_column('knowledge_points', 'name',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=200),
comment='知识点名称',
existing_comment='知识点å\x90\x8dç§°',
existing_nullable=False)
op.alter_column('knowledge_points', 'description',
existing_type=mysql.TEXT(collation='utf8mb4_unicode_ci'),
comment='知识点描述',
existing_comment='知识点æ\x8f\x8fè¿°',
existing_nullable=True)
op.alter_column('knowledge_points', 'parent_id',
existing_type=mysql.INTEGER(),
comment='父知识点ID',
existing_comment='父知识ç¹ID',
existing_nullable=True)
op.alter_column('knowledge_points', 'level',
existing_type=mysql.INTEGER(),
comment='层级深度',
existing_comment='层级深度',
existing_nullable=False,
existing_server_default=sa.text("'1'"))
op.alter_column('knowledge_points', 'path',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=500),
comment='路径(如: 1.2.3)',
existing_comment='路径(如: 1.2.3)',
existing_nullable=True)
op.alter_column('knowledge_points', 'sort_order',
existing_type=mysql.INTEGER(),
comment='排序顺序',
existing_comment='排åº\x8f顺åº\x8f',
existing_nullable=False,
existing_server_default=sa.text("'0'"))
op.alter_column('knowledge_points', 'weight',
existing_type=mysql.FLOAT(),
comment='权重',
existing_comment='æ\x9dƒé‡\x8d',
existing_nullable=False,
existing_server_default=sa.text("'1'"))
op.alter_column('knowledge_points', 'is_required',
existing_type=mysql.TINYINT(display_width=1),
comment='是否必修',
existing_comment='是å\x90¦å¿…ä¿®',
existing_nullable=False,
existing_server_default=sa.text("'1'"))
op.alter_column('knowledge_points', 'estimated_hours',
existing_type=mysql.FLOAT(),
comment='预计学习时间(小时)',
existing_comment='预计å\xad¦ä¹\xa0æ—¶é—´(å°\x8fæ—¶)',
existing_nullable=True)
op.drop_index('idx_knowledge_points_course_id', table_name='knowledge_points')
op.drop_index('idx_knowledge_points_is_deleted', table_name='knowledge_points')
op.drop_index('idx_knowledge_points_parent_id', table_name='knowledge_points')
op.create_index(op.f('ix_knowledge_points_id'), 'knowledge_points', ['id'], unique=False)
op.drop_table_comment(
'knowledge_points',
existing_comment='知识点表',
schema=None
)
op.drop_index('ix_teams_is_deleted', table_name='teams')
op.drop_index('ix_teams_parent_id', table_name='teams')
op.create_foreign_key(None, 'teams', 'users', ['leader_id'], ['id'], ondelete='SET NULL')
op.create_foreign_key(None, 'teams', 'teams', ['parent_id'], ['id'], ondelete='CASCADE')
op.drop_column('teams', 'deleted_at')
op.drop_column('teams', 'created_by')
op.drop_column('teams', 'updated_by')
op.drop_column('teams', 'is_deleted')
op.add_column('users', sa.Column('bio', sa.Text(), nullable=True))
op.add_column('users', sa.Column('is_verified', sa.Boolean(), nullable=False))
op.add_column('users', sa.Column('last_login_at', sa.DateTime(), nullable=True))
op.add_column('users', sa.Column('password_changed_at', sa.DateTime(), nullable=True))
op.alter_column('users', 'username',
existing_type=mysql.VARCHAR(length=50),
comment=None,
existing_comment='用户名',
existing_nullable=False)
op.alter_column('users', 'email',
existing_type=mysql.VARCHAR(length=100),
comment=None,
existing_comment='邮箱',
existing_nullable=False)
op.alter_column('users', 'phone',
existing_type=mysql.VARCHAR(length=20),
comment=None,
existing_comment='手机号',
existing_nullable=True)
op.alter_column('users', 'password_hash',
existing_type=mysql.VARCHAR(length=200),
comment=None,
existing_comment='密码哈希',
existing_nullable=False)
op.alter_column('users', 'full_name',
existing_type=mysql.VARCHAR(length=100),
comment=None,
existing_comment='全名',
existing_nullable=True)
op.alter_column('users', 'avatar_url',
existing_type=mysql.VARCHAR(length=500),
comment=None,
existing_comment='头像URL',
existing_nullable=True)
op.alter_column('users', 'role',
existing_type=mysql.VARCHAR(length=20),
nullable=False,
comment=None,
existing_comment='角色: trainee(学员), manager(管理者), admin(管理员)')
op.alter_column('users', 'is_active',
existing_type=mysql.TINYINT(display_width=1),
nullable=False,
comment=None,
existing_comment='是否激活')
op.alter_column('users', 'id',
existing_type=mysql.BIGINT(),
type_=sa.Integer(),
existing_nullable=False,
autoincrement=True)
op.create_unique_constraint(None, 'users', ['phone'])
op.drop_column('users', 'position')
op.drop_column('users', 'is_superuser')
op.drop_column('users', 'locked_until')
op.drop_column('users', 'login_count')
op.drop_column('users', 'failed_login_count')
op.drop_column('users', 'department')
op.drop_column('users', 'created_by')
op.drop_column('users', 'updated_by')
op.drop_column('users', 'last_login')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('users', sa.Column('last_login', mysql.VARCHAR(length=100), nullable=True, comment='最后登录时间'))
op.add_column('users', sa.Column('updated_by', mysql.BIGINT(), autoincrement=False, nullable=True))
op.add_column('users', sa.Column('created_by', mysql.BIGINT(), autoincrement=False, nullable=True))
op.add_column('users', sa.Column('department', mysql.VARCHAR(length=100), nullable=True, comment='部门'))
op.add_column('users', sa.Column('failed_login_count', mysql.VARCHAR(length=100), nullable=True, comment='失败登录次数'))
op.add_column('users', sa.Column('login_count', mysql.VARCHAR(length=100), nullable=True, comment='登录次数'))
op.add_column('users', sa.Column('locked_until', mysql.VARCHAR(length=100), nullable=True, comment='锁定到期时间'))
op.add_column('users', sa.Column('is_superuser', mysql.TINYINT(display_width=1), autoincrement=False, nullable=True, comment='是否超级管理员'))
op.add_column('users', sa.Column('position', mysql.VARCHAR(length=100), nullable=True, comment='职位'))
op.drop_constraint(None, 'users', type_='unique')
op.alter_column('users', 'id',
existing_type=sa.Integer(),
type_=mysql.BIGINT(),
existing_nullable=False,
autoincrement=True)
op.alter_column('users', 'is_active',
existing_type=mysql.TINYINT(display_width=1),
nullable=True,
comment='是否激活')
op.alter_column('users', 'role',
existing_type=mysql.VARCHAR(length=20),
nullable=True,
comment='角色: trainee(学员), manager(管理者), admin(管理员)')
op.alter_column('users', 'avatar_url',
existing_type=mysql.VARCHAR(length=500),
comment='头像URL',
existing_nullable=True)
op.alter_column('users', 'full_name',
existing_type=mysql.VARCHAR(length=100),
comment='全名',
existing_nullable=True)
op.alter_column('users', 'password_hash',
existing_type=mysql.VARCHAR(length=200),
comment='密码哈希',
existing_nullable=False)
op.alter_column('users', 'phone',
existing_type=mysql.VARCHAR(length=20),
comment='手机号',
existing_nullable=True)
op.alter_column('users', 'email',
existing_type=mysql.VARCHAR(length=100),
comment='邮箱',
existing_nullable=False)
op.alter_column('users', 'username',
existing_type=mysql.VARCHAR(length=50),
comment='用户名',
existing_nullable=False)
op.drop_column('users', 'password_changed_at')
op.drop_column('users', 'last_login_at')
op.drop_column('users', 'is_verified')
op.drop_column('users', 'bio')
op.add_column('teams', sa.Column('is_deleted', mysql.TINYINT(display_width=1), server_default=sa.text("'0'"), autoincrement=False, nullable=False))
op.add_column('teams', sa.Column('updated_by', mysql.INTEGER(), autoincrement=False, nullable=True))
op.add_column('teams', sa.Column('created_by', mysql.INTEGER(), autoincrement=False, nullable=True))
op.add_column('teams', sa.Column('deleted_at', mysql.DATETIME(), nullable=True))
op.drop_constraint(None, 'teams', type_='foreignkey')
op.drop_constraint(None, 'teams', type_='foreignkey')
op.create_index('ix_teams_parent_id', 'teams', ['parent_id'], unique=False)
op.create_index('ix_teams_is_deleted', 'teams', ['is_deleted'], unique=False)
op.create_table_comment(
'knowledge_points',
'知识点表',
existing_comment=None,
schema=None
)
op.drop_index(op.f('ix_knowledge_points_id'), table_name='knowledge_points')
op.create_index('idx_knowledge_points_parent_id', 'knowledge_points', ['parent_id'], unique=False)
op.create_index('idx_knowledge_points_is_deleted', 'knowledge_points', ['is_deleted'], unique=False)
op.create_index('idx_knowledge_points_course_id', 'knowledge_points', ['course_id'], unique=False)
op.alter_column('knowledge_points', 'estimated_hours',
existing_type=mysql.FLOAT(),
comment='预计å\xad¦ä¹\xa0æ—¶é—´(å°\x8fæ—¶)',
existing_comment='预计学习时间(小时)',
existing_nullable=True)
op.alter_column('knowledge_points', 'is_required',
existing_type=mysql.TINYINT(display_width=1),
comment='是å\x90¦å¿…ä¿®',
existing_comment='是否必修',
existing_nullable=False,
existing_server_default=sa.text("'1'"))
op.alter_column('knowledge_points', 'weight',
existing_type=mysql.FLOAT(),
comment='æ\x9dƒé‡\x8d',
existing_comment='权重',
existing_nullable=False,
existing_server_default=sa.text("'1'"))
op.alter_column('knowledge_points', 'sort_order',
existing_type=mysql.INTEGER(),
comment='排åº\x8f顺åº\x8f',
existing_comment='排序顺序',
existing_nullable=False,
existing_server_default=sa.text("'0'"))
op.alter_column('knowledge_points', 'path',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=500),
comment='路径(如: 1.2.3)',
existing_comment='路径(如: 1.2.3)',
existing_nullable=True)
op.alter_column('knowledge_points', 'level',
existing_type=mysql.INTEGER(),
comment='层级深度',
existing_comment='层级深度',
existing_nullable=False,
existing_server_default=sa.text("'1'"))
op.alter_column('knowledge_points', 'parent_id',
existing_type=mysql.INTEGER(),
comment='父知识ç¹ID',
existing_comment='父知识点ID',
existing_nullable=True)
op.alter_column('knowledge_points', 'description',
existing_type=mysql.TEXT(collation='utf8mb4_unicode_ci'),
comment='知识点æ\x8f\x8fè¿°',
existing_comment='知识点描述',
existing_nullable=True)
op.alter_column('knowledge_points', 'name',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=200),
comment='知识点å\x90\x8dç§°',
existing_comment='知识点名称',
existing_nullable=False)
op.alter_column('knowledge_points', 'course_id',
existing_type=mysql.INTEGER(),
comment='课ç¨ID',
existing_comment='课程ID',
existing_nullable=False)
op.create_table_comment(
'growth_paths',
'æˆ\x90长路径表',
existing_comment=None,
schema=None
)
op.drop_index(op.f('ix_growth_paths_id'), table_name='growth_paths')
op.create_index('idx_growth_paths_is_deleted', 'growth_paths', ['is_deleted'], unique=False)
op.create_index('idx_growth_paths_is_active', 'growth_paths', ['is_active'], unique=False)
op.alter_column('growth_paths', 'sort_order',
existing_type=mysql.INTEGER(),
comment='排åº\x8f顺åº\x8f',
existing_comment='排序顺序',
existing_nullable=False,
existing_server_default=sa.text("'0'"))
op.alter_column('growth_paths', 'is_active',
existing_type=mysql.TINYINT(display_width=1),
comment='是å\x90¦å\x90¯ç”¨',
existing_comment='是否启用',
existing_nullable=False,
existing_server_default=sa.text("'1'"))
op.alter_column('growth_paths', 'estimated_duration_days',
existing_type=mysql.INTEGER(),
comment='预计完æˆ\x90天数',
existing_comment='预计完成天数',
existing_nullable=True)
op.alter_column('growth_paths', 'courses',
existing_type=mysql.JSON(),
comment='课程列表[{course_id, order, is_required}]',
existing_comment='课程列表[{course_id, order, is_required}]',
existing_nullable=True)
op.alter_column('growth_paths', 'target_role',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=100),
comment='ç›®æ\xa0‡è§’色',
existing_comment='目标角色',
existing_nullable=True)
op.alter_column('growth_paths', 'description',
existing_type=mysql.TEXT(collation='utf8mb4_unicode_ci'),
comment='路径æ\x8f\x8fè¿°',
existing_comment='路径描述',
existing_nullable=True)
op.alter_column('growth_paths', 'name',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=200),
comment='路径å\x90\x8dç§°',
existing_comment='路径名称',
existing_nullable=False)
op.create_table_comment(
'courses',
'课程表',
existing_comment=None,
schema=None
)
op.drop_index(op.f('ix_courses_id'), table_name='courses')
op.create_index('idx_courses_status', 'courses', ['status'], unique=False)
op.create_index('idx_courses_is_deleted', 'courses', ['is_deleted'], unique=False)
op.create_index('idx_courses_category', 'courses', ['category'], unique=False)
op.alter_column('courses', 'is_featured',
existing_type=mysql.TINYINT(display_width=1),
comment='是å\x90¦æŽ¨è\x8d\x90',
existing_comment='是否推荐',
existing_nullable=False,
existing_server_default=sa.text("'0'"))
op.alter_column('courses', 'sort_order',
existing_type=mysql.INTEGER(),
comment='排åº\x8f顺åº\x8f',
existing_comment='排序顺序',
existing_nullable=False,
existing_server_default=sa.text("'0'"))
op.alter_column('courses', 'publisher_id',
existing_type=mysql.INTEGER(),
comment='å\x8f布人ID',
existing_comment='发布人ID',
existing_nullable=True)
op.alter_column('courses', 'published_at',
existing_type=mysql.DATETIME(),
comment='å\x8f‘布时间',
existing_comment='发布时间',
existing_nullable=True)
op.alter_column('courses', 'tags',
existing_type=mysql.JSON(),
comment='æ\xa0‡ç\xad¾åˆ—表',
existing_comment='标签列表',
existing_nullable=True)
op.alter_column('courses', 'difficulty_level',
existing_type=mysql.INTEGER(),
comment='难度ç\xad‰çº§(1-5)',
existing_comment='难度等级(1-5)',
existing_nullable=True)
op.alter_column('courses', 'duration_hours',
existing_type=mysql.FLOAT(),
comment='课程时长(å°\x8fæ—¶)',
existing_comment='课程时长(小时)',
existing_nullable=True)
op.alter_column('courses', 'cover_image',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=500),
comment='å°\x81é\x9d¢å¾ç‰‡URL',
existing_comment='封面图片URL',
existing_nullable=True)
op.alter_column('courses', 'status',
existing_type=mysql.ENUM('draft', 'published', 'archived', collation='utf8mb4_unicode_ci'),
comment='课程状æ€\x81',
existing_comment='课程状态',
existing_nullable=False,
existing_server_default=sa.text("'draft'"))
op.alter_column('courses', 'category',
existing_type=mysql.ENUM('technology', 'management', 'business', 'general', collation='utf8mb4_unicode_ci'),
comment='课程分类',
existing_comment='课程分类',
existing_nullable=False,
existing_server_default=sa.text("'general'"))
op.alter_column('courses', 'description',
existing_type=mysql.TEXT(collation='utf8mb4_unicode_ci'),
comment='课程æ\x8f\x8fè¿°',
existing_comment='课程描述',
existing_nullable=True)
op.alter_column('courses', 'name',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=200),
comment='课程å\x90\x8dç§°',
existing_comment='课程名称',
existing_nullable=False)
op.create_table_comment(
'course_materials',
'课程资料表',
existing_comment=None,
schema=None
)
op.drop_index(op.f('ix_course_materials_id'), table_name='course_materials')
op.create_index('idx_course_materials_is_deleted', 'course_materials', ['is_deleted'], unique=False)
op.create_index('idx_course_materials_course_id', 'course_materials', ['course_id'], unique=False)
op.alter_column('course_materials', 'sort_order',
existing_type=mysql.INTEGER(),
comment='排åº\x8f顺åº\x8f',
existing_comment='排序顺序',
existing_nullable=False,
existing_server_default=sa.text("'0'"))
op.alter_column('course_materials', 'file_size',
existing_type=mysql.INTEGER(),
comment='文件大å°\x8f\xad—节)',
existing_comment='文件大小(字节)',
existing_nullable=False)
op.alter_column('course_materials', 'file_type',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=50),
comment='文件类型',
existing_comment='文件类型',
existing_nullable=False)
op.alter_column('course_materials', 'file_url',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=500),
comment='æ‡ä»¶URL',
existing_comment='文件URL',
existing_nullable=False)
op.alter_column('course_materials', 'description',
existing_type=mysql.TEXT(collation='utf8mb4_unicode_ci'),
comment='资料æ\x8f\x8fè¿°',
existing_comment='资料描述',
existing_nullable=True)
op.alter_column('course_materials', 'name',
existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=200),
comment='资料å\x90\x8dç§°',
existing_comment='资料名称',
existing_nullable=False)
op.alter_column('course_materials', 'course_id',
existing_type=mysql.INTEGER(),
comment='课ç¨ID',
existing_comment='课程ID',
existing_nullable=False)
op.drop_table('user_teams')
op.drop_index(op.f('ix_training_reports_user_id'), table_name='training_reports')
op.drop_index(op.f('ix_training_reports_id'), table_name='training_reports')
op.drop_table('training_reports')
op.drop_index(op.f('ix_training_messages_id'), table_name='training_messages')
op.drop_table('training_messages')
op.drop_index(op.f('ix_training_sessions_user_id'), table_name='training_sessions')
op.drop_index(op.f('ix_training_sessions_id'), table_name='training_sessions')
op.drop_table('training_sessions')
op.drop_index(op.f('ix_training_scenes_id'), table_name='training_scenes')
op.drop_table('training_scenes')
# ### end Alembic commands ###