"""Phase 0 - 扩展性基础设施:metadata JSONB + title_libraries + voice_libraries Revision ID: 010 Revises: 009 Create Date: 2026-06-28 This migration: 1. Adds metadata JSONB column to 5 tables: - projects, asset_libraries, assets, edit_templates, generation_tasks 2. Creates title_libraries table (独立标题库,支持跨项目复用) 3. Creates voice_libraries table (配音库,支持 AI 配音管理) """ import sqlalchemy as sa from alembic import op # revision identifiers revision = "010" down_revision = "009" branch_labels = None depends_on = None def upgrade() -> None: conn = op.get_bind() # ── 1. Add metadata JSONB to existing tables ── conn.execute(sa.text("ALTER TABLE projects ADD COLUMN IF NOT EXISTS metadata JSONB NOT NULL DEFAULT '{}'")) conn.execute(sa.text("ALTER TABLE asset_libraries ADD COLUMN IF NOT EXISTS metadata JSONB NOT NULL DEFAULT '{}'")) conn.execute(sa.text("ALTER TABLE assets ADD COLUMN IF NOT EXISTS metadata JSONB NOT NULL DEFAULT '{}'")) conn.execute(sa.text("ALTER TABLE edit_templates ADD COLUMN IF NOT EXISTS metadata JSONB NOT NULL DEFAULT '{}'")) conn.execute(sa.text("ALTER TABLE generation_tasks ADD COLUMN IF NOT EXISTS metadata JSONB NOT NULL DEFAULT '{}'")) # ── 2. Create title_libraries table ── conn.execute(sa.text(""" CREATE TABLE IF NOT EXISTS title_libraries ( id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36) NOT NULL, name VARCHAR(255) NOT NULL, description TEXT NOT NULL DEFAULT '', category VARCHAR(50) NOT NULL DEFAULT 'default', text VARCHAR(500) NOT NULL, tags JSONB NOT NULL DEFAULT '[]', usage_count INTEGER NOT NULL DEFAULT 0, is_active BOOLEAN NOT NULL DEFAULT TRUE, metadata JSONB NOT NULL DEFAULT '{}', created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW() ) """)) conn.execute(sa.text("CREATE INDEX IF NOT EXISTS ix_title_libraries_user_id ON title_libraries(user_id)")) conn.execute(sa.text("CREATE INDEX IF NOT EXISTS ix_title_libraries_category ON title_libraries(category)")) conn.execute(sa.text("CREATE INDEX IF NOT EXISTS ix_title_libraries_is_active ON title_libraries(is_active)")) # ── 3. Create voice_libraries table ── conn.execute(sa.text(""" CREATE TABLE IF NOT EXISTS voice_libraries ( id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36) NOT NULL, project_id VARCHAR(36), name VARCHAR(255) NOT NULL, text TEXT NOT NULL DEFAULT '', voice_provider VARCHAR(50) NOT NULL DEFAULT '', voice_id VARCHAR(100) NOT NULL DEFAULT '', voice_name VARCHAR(100) NOT NULL DEFAULT '', audio_url VARCHAR(1000) NOT NULL DEFAULT '', duration FLOAT NOT NULL DEFAULT 0, file_size INTEGER NOT NULL DEFAULT 0, status VARCHAR(20) NOT NULL DEFAULT 'completed', tags JSONB NOT NULL DEFAULT '[]', metadata JSONB NOT NULL DEFAULT '{}', created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW() ) """)) conn.execute(sa.text("CREATE INDEX IF NOT EXISTS ix_voice_libraries_user_id ON voice_libraries(user_id)")) conn.execute(sa.text("CREATE INDEX IF NOT EXISTS ix_voice_libraries_project_id ON voice_libraries(project_id)")) conn.execute(sa.text("CREATE INDEX IF NOT EXISTS ix_voice_libraries_status ON voice_libraries(status)")) def downgrade() -> None: conn = op.get_bind() # Drop new tables conn.execute(sa.text("DROP TABLE IF EXISTS voice_libraries")) conn.execute(sa.text("DROP TABLE IF EXISTS title_libraries")) # Remove metadata columns conn.execute(sa.text("ALTER TABLE generation_tasks DROP COLUMN IF EXISTS metadata")) conn.execute(sa.text("ALTER TABLE edit_templates DROP COLUMN IF EXISTS metadata")) conn.execute(sa.text("ALTER TABLE assets DROP COLUMN IF EXISTS metadata")) conn.execute(sa.text("ALTER TABLE asset_libraries DROP COLUMN IF EXISTS metadata")) conn.execute(sa.text("ALTER TABLE projects DROP COLUMN IF EXISTS metadata"))