Merge remote-tracking branch 'origin/development'

This commit is contained in:
Kevin
2026-05-19 16:42:22 +08:00
11 changed files with 384 additions and 4 deletions

View File

@@ -0,0 +1,34 @@
"""Alembic 迁移共享工具(仅用于 versions/ 下的迁移脚本)。"""
from __future__ import annotations
import sqlalchemy as sa
from alembic import op
def table_exists(table_name: str) -> bool:
bind = op.get_bind()
return table_name in sa.inspect(bind).get_table_names()
def has_column(table_name: str, column_name: str) -> bool:
if not table_exists(table_name):
return False
bind = op.get_bind()
columns = sa.inspect(bind).get_columns(table_name)
return any(column["name"] == column_name for column in columns)
def add_column_if_missing(table_name: str, column: sa.Column) -> bool:
"""若列不存在则 add_column返回是否执行了添加。"""
if has_column(table_name, column.name):
return False
op.add_column(table_name, column)
return True
def drop_column_if_exists(table_name: str, column_name: str) -> bool:
if not has_column(table_name, column_name):
return False
op.drop_column(table_name, column_name)
return True

View File

@@ -6,9 +6,10 @@ chapters 含 story 物化字段markdown_compose_dirty、markdown_composed_at
(阅读片段快照,随 ORM 一并 create_all
已并入原 0002stories-first无 chapter_sections / memoir_images.section_id与原 0003segments.tts_audio_urls
的语义:新库仅由当前 ORM 建表即可,无需后续 ALTER。
conversation_messages会话轮次 durable log由 app.features.conversation.models.ConversationMessage 一并 create_all。
segments.audio_duration_seconds语音条时长秒数历史 API / Redis 回填)由 ORM 一并 create_all无独立迁移
的语义:仅对「全新库」由 create_all 建出;**已有库不会 ALTER**
老库缺列见 0019_align_legacy_schemasegments.audio_duration_seconds、tts_audio_urls
conversations.deleted_atconversation_messages.tts_audio_urls 等)
conversation_messages 表由 ORM 在 0001 create_all 中创建(新库);老库若缺表须单独处理。
story_image_intents 无 source_span主图回填在正文末尾意图仅存 caption / prompt_brief 等)。
新库 / 删库重来:`alembic upgrade head`。

View File

@@ -0,0 +1,64 @@
"""补齐 0001 create_all 未覆盖的老库列(显式清单,禁止内省全库)
0001 对「已存在的表」不会 ALTER。下列列在 ORM / 0001 注释中视为新库默认字段,
但 staging/production 等自 squash 前就存在的库需要本迁移显式 add_column。
新增 ORM 字段时:禁止改已部署 revision id在本文件追加列定义或新建 0020_* 迁移。
Revision ID: 0019_align_legacy_schema
Revises: 0018_users_language_preference
"""
from __future__ import annotations
import sys
from pathlib import Path
from typing import Sequence, Union
import sqlalchemy as sa
# versions/ -> alembic/
_ALEMBIC_DIR = Path(__file__).resolve().parents[1]
if str(_ALEMBIC_DIR) not in sys.path:
sys.path.insert(0, str(_ALEMBIC_DIR))
from script_helpers import add_column_if_missing, drop_column_if_exists, table_exists
revision: str = "0019_align_legacy_schema"
down_revision: Union[str, None] = "0018_users_language_preference"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
# (table, column) — 仅列「00020018 未 add_column 且老库常缺」的字段
_LEGACY_COLUMNS: tuple[tuple[str, sa.Column], ...] = (
(
"segments",
sa.Column("audio_duration_seconds", sa.Integer(), nullable=True),
),
(
"conversations",
sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
),
(
"segments",
sa.Column("tts_audio_urls", sa.JSON(), nullable=True),
),
(
"conversation_messages",
sa.Column("tts_audio_urls", sa.JSON(), nullable=True),
),
)
def upgrade() -> None:
for table_name, column in _LEGACY_COLUMNS:
if not table_exists(table_name):
continue
add_column_if_missing(table_name, column)
def downgrade() -> None:
for table_name, column in reversed(_LEGACY_COLUMNS):
if not table_exists(table_name):
continue
drop_column_if_exists(table_name, column.name)