diff --git a/backend/Dockerfile b/backend/Dockerfile index e3216bf..2b40a1b 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,4 @@ -# 使用官方 Python 3.11 slim 镜像作为基础镜像 +# 使用官方 Python 3.11 slim 镜像 FROM python:3.11-slim # 设置工作目录 @@ -9,12 +9,22 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PYTHONPATH=/app -# 复制依赖文件(利用 Docker 层缓存) +# 安装系统依赖 +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# 复制依赖文件 COPY pyproject.toml ./ -# 安装 uv 和项目依赖到系统 Python(避免虚拟环境开销) -RUN pip install --no-cache-dir uv && \ - uv pip install --no-cache-dir --system -r pyproject.toml +# 安装 uv +RUN pip install --no-cache-dir uv + +# 创建虚拟环境并安装所有依赖(内置到镜像) +RUN uv venv && \ + . .venv/bin/activate && \ + uv pip install -r pyproject.toml # 复制应用代码 COPY . . @@ -25,6 +35,5 @@ RUN chmod +x docker-entrypoint.sh # 暴露端口 EXPOSE 8000 - # 启动命令 ENTRYPOINT ["./docker-entrypoint.sh"] \ No newline at end of file diff --git a/backend/alembic.ini b/backend/alembic.ini index 5ddbbbd..37df3ae 100644 --- a/backend/alembic.ini +++ b/backend/alembic.ini @@ -84,7 +84,6 @@ path_separator = os # database URL. This is consumed by the user-maintained env.py script only. # other means of configuring database URLs may be customized within the env.py # file. -# ʹûеݿURL sqlalchemy.url = %ENV DATABASE_URL diff --git a/backend/alembic/versions/2952aa9a98f3_initial_migration.py b/backend/alembic/versions/2952aa9a98f3_initial_migration.py new file mode 100644 index 0000000..c8376bd --- /dev/null +++ b/backend/alembic/versions/2952aa9a98f3_initial_migration.py @@ -0,0 +1,144 @@ +"""initial_migration + +Revision ID: 2952aa9a98f3 +Revises: +Create Date: 2025-10-04 17:23:05.644899 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '2952aa9a98f3' +down_revision: Union[str, Sequence[str], None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('system_config', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('is_allow_register', sa.Boolean(), nullable=False), + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_system_config_id'), 'system_config', ['id'], unique=False) + op.create_table('users', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('username', sa.String(length=50), nullable=False), + sa.Column('email', sa.String(length=100), nullable=False), + sa.Column('password_hash', sa.String(length=255), nullable=False), + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('is_active', sa.Boolean(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True) + op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False) + op.create_index(op.f('ix_users_username'), 'users', ['username'], unique=True) + op.create_table('work_flow_states', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('work_id', sa.String(length=50), nullable=False), + sa.Column('current_state', sa.String(length=50), nullable=False), + sa.Column('previous_state', sa.String(length=50), nullable=True), + sa.Column('state_data', sa.JSON(), nullable=True), + sa.Column('transition_reason', sa.Text(), nullable=True), + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_work_flow_states_id'), 'work_flow_states', ['id'], unique=False) + op.create_index(op.f('ix_work_flow_states_work_id'), 'work_flow_states', ['work_id'], unique=False) + op.create_table('chat_sessions', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('session_id', sa.String(length=100), nullable=False), + sa.Column('work_id', sa.String(length=50), nullable=False), + sa.Column('system_type', sa.String(length=20), nullable=False), + sa.Column('title', sa.String(length=200), nullable=True), + sa.Column('status', sa.String(length=20), nullable=True), + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('created_by', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['created_by'], ['users.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_chat_sessions_id'), 'chat_sessions', ['id'], unique=False) + op.create_index(op.f('ix_chat_sessions_session_id'), 'chat_sessions', ['session_id'], unique=True) + op.create_index(op.f('ix_chat_sessions_work_id'), 'chat_sessions', ['work_id'], unique=False) + op.create_table('model_configs', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('type', sa.String(length=50), nullable=False), + sa.Column('model_id', sa.String(length=50), nullable=False), + sa.Column('base_url', sa.String(length=100), nullable=False), + sa.Column('api_key', sa.String(length=255), nullable=False), + sa.Column('is_active', sa.Boolean(), nullable=True), + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('created_by', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['created_by'], ['users.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_model_configs_id'), 'model_configs', ['id'], unique=False) + op.create_table('paper_templates', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=100), nullable=False), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('category', sa.String(length=50), nullable=True), + sa.Column('file_path', sa.String(length=500), nullable=False), + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('is_public', sa.Boolean(), nullable=True), + sa.Column('created_by', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['created_by'], ['users.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_paper_templates_id'), 'paper_templates', ['id'], unique=False) + op.create_table('works', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('work_id', sa.String(length=50), nullable=False), + sa.Column('title', sa.String(length=200), nullable=False), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('status', sa.String(length=50), nullable=False), + sa.Column('progress', sa.Integer(), nullable=True), + sa.Column('tags', sa.Text(), nullable=True), + sa.Column('template_id', sa.Integer(), nullable=True), + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('created_by', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['created_by'], ['users.id'], ), + sa.ForeignKeyConstraint(['template_id'], ['paper_templates.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_works_id'), 'works', ['id'], unique=False) + op.create_index(op.f('ix_works_work_id'), 'works', ['work_id'], unique=True) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_works_work_id'), table_name='works') + op.drop_index(op.f('ix_works_id'), table_name='works') + op.drop_table('works') + op.drop_index(op.f('ix_paper_templates_id'), table_name='paper_templates') + op.drop_table('paper_templates') + op.drop_index(op.f('ix_model_configs_id'), table_name='model_configs') + op.drop_table('model_configs') + op.drop_index(op.f('ix_chat_sessions_work_id'), table_name='chat_sessions') + op.drop_index(op.f('ix_chat_sessions_session_id'), table_name='chat_sessions') + op.drop_index(op.f('ix_chat_sessions_id'), table_name='chat_sessions') + op.drop_table('chat_sessions') + op.drop_index(op.f('ix_work_flow_states_work_id'), table_name='work_flow_states') + op.drop_index(op.f('ix_work_flow_states_id'), table_name='work_flow_states') + op.drop_table('work_flow_states') + op.drop_index(op.f('ix_users_username'), table_name='users') + op.drop_index(op.f('ix_users_id'), table_name='users') + op.drop_index(op.f('ix_users_email'), table_name='users') + op.drop_table('users') + op.drop_index(op.f('ix_system_config_id'), table_name='system_config') + op.drop_table('system_config') + # ### end Alembic commands ### diff --git a/backend/alembic/versions/8fd0f91dc04a_initial_migration.py b/backend/alembic/versions/8fd0f91dc04a_initial_migration.py deleted file mode 100644 index 16acfe3..0000000 --- a/backend/alembic/versions/8fd0f91dc04a_initial_migration.py +++ /dev/null @@ -1,313 +0,0 @@ -"""Initial migration - -Revision ID: 8fd0f91dc04a -Revises: -Create Date: 2025-10-04 15:56:14.270751 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import postgresql - -# revision identifiers, used by Alembic. -revision: str = '8fd0f91dc04a' -down_revision: Union[str, Sequence[str], None] = None -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('chat_sessions', 'created_at', - existing_type=postgresql.TIMESTAMP(), - type_=sa.DateTime(timezone=True), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.alter_column('chat_sessions', 'updated_at', - existing_type=postgresql.TIMESTAMP(), - type_=sa.DateTime(timezone=True), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.drop_constraint(op.f('chat_sessions_session_id_key'), 'chat_sessions', type_='unique') - op.drop_index(op.f('idx_chat_sessions_created_by'), table_name='chat_sessions') - op.drop_index(op.f('idx_chat_sessions_session_id'), table_name='chat_sessions') - op.drop_index(op.f('idx_chat_sessions_work_id'), table_name='chat_sessions') - op.create_index(op.f('ix_chat_sessions_id'), 'chat_sessions', ['id'], unique=False) - op.create_index(op.f('ix_chat_sessions_session_id'), 'chat_sessions', ['session_id'], unique=True) - op.create_index(op.f('ix_chat_sessions_work_id'), 'chat_sessions', ['work_id'], unique=False) - op.drop_constraint(op.f('chat_sessions_created_by_fkey'), 'chat_sessions', type_='foreignkey') - op.create_foreign_key(None, 'chat_sessions', 'users', ['created_by'], ['id']) - op.drop_table_comment( - 'chat_sessions', - existing_comment='聊天会话表', - schema=None - ) - op.alter_column('model_configs', 'created_at', - existing_type=postgresql.TIMESTAMP(), - type_=sa.DateTime(timezone=True), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.drop_index(op.f('idx_model_configs_created_by'), table_name='model_configs') - op.drop_index(op.f('idx_model_configs_is_active'), table_name='model_configs') - op.drop_index(op.f('idx_model_configs_type'), table_name='model_configs') - op.create_index(op.f('ix_model_configs_id'), 'model_configs', ['id'], unique=False) - op.drop_constraint(op.f('fk_model_configs_created_by'), 'model_configs', type_='foreignkey') - op.create_foreign_key(None, 'model_configs', 'users', ['created_by'], ['id']) - op.drop_table_comment( - 'model_configs', - existing_comment='模型配置表', - schema=None - ) - op.alter_column('paper_templates', 'created_at', - existing_type=postgresql.TIMESTAMP(), - type_=sa.DateTime(timezone=True), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.alter_column('paper_templates', 'updated_at', - existing_type=postgresql.TIMESTAMP(), - type_=sa.DateTime(timezone=True), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.drop_index(op.f('idx_paper_templates_category'), table_name='paper_templates') - op.drop_index(op.f('idx_paper_templates_created_by'), table_name='paper_templates') - op.create_index(op.f('ix_paper_templates_id'), 'paper_templates', ['id'], unique=False) - op.drop_constraint(op.f('paper_templates_created_by_fkey'), 'paper_templates', type_='foreignkey') - op.create_foreign_key(None, 'paper_templates', 'users', ['created_by'], ['id']) - op.drop_table_comment( - 'paper_templates', - existing_comment='论文模板表', - schema=None - ) - op.alter_column('system_config', 'created_at', - existing_type=postgresql.TIMESTAMP(), - type_=sa.DateTime(timezone=True), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.alter_column('system_config', 'updated_at', - existing_type=postgresql.TIMESTAMP(), - type_=sa.DateTime(timezone=True), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.create_index(op.f('ix_system_config_id'), 'system_config', ['id'], unique=False) - op.drop_table_comment( - 'system_config', - existing_comment='系统基本配置表', - schema=None - ) - op.alter_column('users', 'created_at', - existing_type=postgresql.TIMESTAMP(), - type_=sa.DateTime(timezone=True), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.alter_column('users', 'updated_at', - existing_type=postgresql.TIMESTAMP(), - type_=sa.DateTime(timezone=True), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.drop_index(op.f('idx_users_email'), table_name='users') - op.drop_index(op.f('idx_users_username'), table_name='users') - op.drop_constraint(op.f('users_email_key'), 'users', type_='unique') - op.drop_constraint(op.f('users_username_key'), 'users', type_='unique') - op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True) - op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False) - op.create_index(op.f('ix_users_username'), 'users', ['username'], unique=True) - op.drop_table_comment( - 'users', - existing_comment='用户表', - schema=None - ) - op.alter_column('work_flow_states', 'created_at', - existing_type=postgresql.TIMESTAMP(), - type_=sa.DateTime(timezone=True), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.drop_index(op.f('idx_work_flow_states_work_id'), table_name='work_flow_states') - op.create_index(op.f('ix_work_flow_states_id'), 'work_flow_states', ['id'], unique=False) - op.create_index(op.f('ix_work_flow_states_work_id'), 'work_flow_states', ['work_id'], unique=False) - op.drop_table_comment( - 'work_flow_states', - existing_comment='工作流状态表', - schema=None - ) - op.alter_column('works', 'created_at', - existing_type=postgresql.TIMESTAMP(), - type_=sa.DateTime(timezone=True), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.alter_column('works', 'updated_at', - existing_type=postgresql.TIMESTAMP(), - type_=sa.DateTime(timezone=True), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.drop_index(op.f('idx_works_created_at'), table_name='works') - op.drop_index(op.f('idx_works_created_by'), table_name='works') - op.drop_index(op.f('idx_works_status'), table_name='works') - op.drop_index(op.f('idx_works_template_id'), table_name='works') - op.drop_index(op.f('idx_works_work_id'), table_name='works') - op.drop_constraint(op.f('works_work_id_key'), 'works', type_='unique') - op.create_index(op.f('ix_works_id'), 'works', ['id'], unique=False) - op.create_index(op.f('ix_works_work_id'), 'works', ['work_id'], unique=True) - op.drop_constraint(op.f('works_template_id_fkey'), 'works', type_='foreignkey') - op.drop_constraint(op.f('works_created_by_fkey'), 'works', type_='foreignkey') - op.create_foreign_key(None, 'works', 'paper_templates', ['template_id'], ['id']) - op.create_foreign_key(None, 'works', 'users', ['created_by'], ['id']) - op.drop_table_comment( - 'works', - existing_comment='工作历史表', - schema=None - ) - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.create_table_comment( - 'works', - '工作历史表', - existing_comment=None, - schema=None - ) - op.drop_constraint(None, 'works', type_='foreignkey') - op.drop_constraint(None, 'works', type_='foreignkey') - op.create_foreign_key(op.f('works_created_by_fkey'), 'works', 'users', ['created_by'], ['id'], ondelete='CASCADE') - op.create_foreign_key(op.f('works_template_id_fkey'), 'works', 'paper_templates', ['template_id'], ['id'], ondelete='SET NULL') - op.drop_index(op.f('ix_works_work_id'), table_name='works') - op.drop_index(op.f('ix_works_id'), table_name='works') - op.create_unique_constraint(op.f('works_work_id_key'), 'works', ['work_id'], postgresql_nulls_not_distinct=False) - op.create_index(op.f('idx_works_work_id'), 'works', ['work_id'], unique=False) - op.create_index(op.f('idx_works_template_id'), 'works', ['template_id'], unique=False) - op.create_index(op.f('idx_works_status'), 'works', ['status'], unique=False) - op.create_index(op.f('idx_works_created_by'), 'works', ['created_by'], unique=False) - op.create_index(op.f('idx_works_created_at'), 'works', ['created_at'], unique=False) - op.alter_column('works', 'updated_at', - existing_type=sa.DateTime(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.alter_column('works', 'created_at', - existing_type=sa.DateTime(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.create_table_comment( - 'work_flow_states', - '工作流状态表', - existing_comment=None, - schema=None - ) - op.drop_index(op.f('ix_work_flow_states_work_id'), table_name='work_flow_states') - op.drop_index(op.f('ix_work_flow_states_id'), table_name='work_flow_states') - op.create_index(op.f('idx_work_flow_states_work_id'), 'work_flow_states', ['work_id'], unique=False) - op.alter_column('work_flow_states', 'created_at', - existing_type=sa.DateTime(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.create_table_comment( - 'users', - '用户表', - existing_comment=None, - schema=None - ) - op.drop_index(op.f('ix_users_username'), table_name='users') - op.drop_index(op.f('ix_users_id'), table_name='users') - op.drop_index(op.f('ix_users_email'), table_name='users') - op.create_unique_constraint(op.f('users_username_key'), 'users', ['username'], postgresql_nulls_not_distinct=False) - op.create_unique_constraint(op.f('users_email_key'), 'users', ['email'], postgresql_nulls_not_distinct=False) - op.create_index(op.f('idx_users_username'), 'users', ['username'], unique=False) - op.create_index(op.f('idx_users_email'), 'users', ['email'], unique=False) - op.alter_column('users', 'updated_at', - existing_type=sa.DateTime(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.alter_column('users', 'created_at', - existing_type=sa.DateTime(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.create_table_comment( - 'system_config', - '系统基本配置表', - existing_comment=None, - schema=None - ) - op.drop_index(op.f('ix_system_config_id'), table_name='system_config') - op.alter_column('system_config', 'updated_at', - existing_type=sa.DateTime(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.alter_column('system_config', 'created_at', - existing_type=sa.DateTime(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.create_table_comment( - 'paper_templates', - '论文模板表', - existing_comment=None, - schema=None - ) - op.drop_constraint(None, 'paper_templates', type_='foreignkey') - op.create_foreign_key(op.f('paper_templates_created_by_fkey'), 'paper_templates', 'users', ['created_by'], ['id'], ondelete='SET NULL') - op.drop_index(op.f('ix_paper_templates_id'), table_name='paper_templates') - op.create_index(op.f('idx_paper_templates_created_by'), 'paper_templates', ['created_by'], unique=False) - op.create_index(op.f('idx_paper_templates_category'), 'paper_templates', ['category'], unique=False) - op.alter_column('paper_templates', 'updated_at', - existing_type=sa.DateTime(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.alter_column('paper_templates', 'created_at', - existing_type=sa.DateTime(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.create_table_comment( - 'model_configs', - '模型配置表', - existing_comment=None, - schema=None - ) - op.drop_constraint(None, 'model_configs', type_='foreignkey') - op.create_foreign_key(op.f('fk_model_configs_created_by'), 'model_configs', 'users', ['created_by'], ['id'], ondelete='CASCADE') - op.drop_index(op.f('ix_model_configs_id'), table_name='model_configs') - op.create_index(op.f('idx_model_configs_type'), 'model_configs', ['type'], unique=False) - op.create_index(op.f('idx_model_configs_is_active'), 'model_configs', ['is_active'], unique=False) - op.create_index(op.f('idx_model_configs_created_by'), 'model_configs', ['created_by'], unique=False) - op.alter_column('model_configs', 'created_at', - existing_type=sa.DateTime(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.create_table_comment( - 'chat_sessions', - '聊天会话表', - existing_comment=None, - schema=None - ) - op.drop_constraint(None, 'chat_sessions', type_='foreignkey') - op.create_foreign_key(op.f('chat_sessions_created_by_fkey'), 'chat_sessions', 'users', ['created_by'], ['id'], ondelete='CASCADE') - op.drop_index(op.f('ix_chat_sessions_work_id'), table_name='chat_sessions') - op.drop_index(op.f('ix_chat_sessions_session_id'), table_name='chat_sessions') - op.drop_index(op.f('ix_chat_sessions_id'), table_name='chat_sessions') - op.create_index(op.f('idx_chat_sessions_work_id'), 'chat_sessions', ['work_id'], unique=False) - op.create_index(op.f('idx_chat_sessions_session_id'), 'chat_sessions', ['session_id'], unique=False) - op.create_index(op.f('idx_chat_sessions_created_by'), 'chat_sessions', ['created_by'], unique=False) - op.create_unique_constraint(op.f('chat_sessions_session_id_key'), 'chat_sessions', ['session_id'], postgresql_nulls_not_distinct=False) - op.alter_column('chat_sessions', 'updated_at', - existing_type=sa.DateTime(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - op.alter_column('chat_sessions', 'created_at', - existing_type=sa.DateTime(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=True, - existing_server_default=sa.text('CURRENT_TIMESTAMP')) - # ### end Alembic commands ### diff --git a/backend/docker-entrypoint.sh b/backend/docker-entrypoint.sh index 40a6a80..2b7eeeb 100644 --- a/backend/docker-entrypoint.sh +++ b/backend/docker-entrypoint.sh @@ -2,7 +2,10 @@ set -e printf '%s\n' "Running database migrations..." -uv run alembic upgrade head +# 使用预装的虚拟环境(避免运行时下载) +. .venv/bin/activate +alembic upgrade head printf '%s\n' "Starting application server..." -exec uvicorn main:app --host 0.0.0.0 --port 8000 +# 使用虚拟环境中的 uvicorn +exec .venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000