Files
trade-assistant/backend/alembic/versions/001_initial.py
T
TradeMate Dev 7b62c2f8b4 feat: 修复 H5 底部导航覆盖 + 更新项目进度文档
## H5 底部导航修复 (Bug #10)
- 精简 App.vue,移除重复 tabbar,仅保留全局样式
- uni-page 设置 height: calc(100% - 50px) + overflow-y: auto
- 内容区域精确停在底部导航上方,独立滚动不再叠加
- 恢复 custom-tab-bar 组件

## 项目进度文档
- PROGRESS.md 更新至 10 个 Bug 修复
- 新增 H5 底部导航修复记录
- 新增历史变更条目
2026-05-12 20:24:42 +08:00

190 lines
10 KiB
Python

"""initial schema
Revision ID: 001
Revises:
Create Date: 2026-05-08
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
revision: str = '001'
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:
op.create_table('users',
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('wechat_openid', sa.String(length=255), nullable=True),
sa.Column('phone', sa.String(length=20), nullable=True),
sa.Column('username', sa.String(length=100), nullable=True),
sa.Column('password_hash', sa.String(length=255), nullable=True),
sa.Column('tier', sa.String(length=50), nullable=True),
sa.Column('role', sa.String(length=20), nullable=True),
sa.Column('is_active', sa.Boolean(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('settings', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_users_phone'), 'users', ['phone'], unique=True)
op.create_index(op.f('ix_users_wechat_openid'), 'users', ['wechat_openid'], unique=True)
op.create_table('products',
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('name_en', sa.String(length=255), nullable=True),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('description_en', sa.Text(), nullable=True),
sa.Column('category', sa.String(length=100), nullable=True),
sa.Column('price', sa.String(length=50), nullable=True),
sa.Column('price_unit', sa.String(length=20), nullable=True),
sa.Column('moq', sa.String(length=50), nullable=True),
sa.Column('keywords', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column('specifications', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column('images', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column('is_active', sa.Boolean(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_products_user_id'), 'products', ['user_id'], unique=False)
op.create_table('customers',
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('company', sa.String(length=255), nullable=True),
sa.Column('country', sa.String(length=100), nullable=True),
sa.Column('phone', sa.String(length=50), nullable=True),
sa.Column('email', sa.String(length=255), nullable=True),
sa.Column('whatsapp_id', sa.String(length=255), nullable=True),
sa.Column('source', sa.String(length=100), nullable=True),
sa.Column('tags', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column('notes', sa.Text(), nullable=True),
sa.Column('preference', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column('status', sa.String(length=50), nullable=True),
sa.Column('last_contact_at', sa.DateTime(), nullable=True),
sa.Column('silence_started_at', sa.DateTime(), nullable=True),
sa.Column('next_followup_at', sa.DateTime(), nullable=True),
sa.Column('estimated_value', sa.String(length=50), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_customers_user_id'), 'customers', ['user_id'], unique=False)
op.create_table('conversations',
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('customer_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('channel', sa.String(length=50), nullable=True),
sa.Column('topic', sa.String(length=255), nullable=True),
sa.Column('status', sa.String(length=50), nullable=True),
sa.Column('message_count', sa.Integer(), nullable=True),
sa.Column('last_message_at', sa.DateTime(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['customer_id'], ['customers.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_conversations_customer_id'), 'conversations', ['customer_id'], unique=False)
op.create_index(op.f('ix_conversations_user_id'), 'conversations', ['user_id'], unique=False)
op.create_table('messages',
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('conversation_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('direction', sa.String(length=20), nullable=False),
sa.Column('content', sa.Text(), nullable=False),
sa.Column('content_translated', sa.Text(), nullable=True),
sa.Column('content_type', sa.String(length=50), nullable=True),
sa.Column('ai_suggestions', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column('selected_suggestion', sa.Integer(), nullable=True),
sa.Column('user_edited', sa.Text(), nullable=True),
sa.Column('status', sa.String(length=50), nullable=True),
sa.Column('metadata', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['conversation_id'], ['conversations.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_messages_conversation_id'), 'messages', ['conversation_id'], unique=False)
op.create_table('quotations',
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('customer_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('title', sa.String(length=255), nullable=True),
sa.Column('status', sa.String(length=50), nullable=True),
sa.Column('currency', sa.String(length=10), nullable=True),
sa.Column('exchange_rate', sa.Float(), nullable=True),
sa.Column('payment_terms', sa.String(length=255), nullable=True),
sa.Column('delivery_terms', sa.String(length=255), nullable=True),
sa.Column('lead_time', sa.String(length=100), nullable=True),
sa.Column('valid_until', sa.String(length=100), nullable=True),
sa.Column('subtotal', sa.Float(), nullable=True),
sa.Column('discount', sa.Float(), nullable=True),
sa.Column('shipping', sa.Float(), nullable=True),
sa.Column('total', sa.Float(), nullable=True),
sa.Column('notes', sa.Text(), nullable=True),
sa.Column('pdf_url', sa.Text(), nullable=True),
sa.Column('sent_at', sa.DateTime(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['customer_id'], ['customers.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_quotations_user_id'), 'quotations', ['user_id'], unique=False)
op.create_table('quotation_items',
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('quotation_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('product_name', sa.String(length=255), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('quantity', sa.Integer(), nullable=False),
sa.Column('unit_price', sa.Float(), nullable=False),
sa.Column('total_price', sa.Float(), nullable=True),
sa.Column('unit', sa.String(length=50), nullable=True),
sa.ForeignKeyConstraint(['quotation_id'], ['quotations.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_quotation_items_quotation_id'), 'quotation_items', ['quotation_id'], unique=False)
op.create_table('corpus_entries',
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('source_text', sa.Text(), nullable=False),
sa.Column('target_text', sa.Text(), nullable=False),
sa.Column('source_lang', sa.String(length=20), nullable=True),
sa.Column('target_lang', sa.String(length=20), nullable=True),
sa.Column('task_type', sa.String(length=50), nullable=False),
sa.Column('domain', sa.String(length=100), nullable=True),
sa.Column('provider_used', sa.String(length=50), nullable=True),
sa.Column('quality_score', sa.Float(), nullable=True),
sa.Column('user_edited', sa.Boolean(), nullable=True),
sa.Column('user_rating', sa.Integer(), nullable=True),
sa.Column('usage_count', sa.Integer(), nullable=True),
sa.Column('embedding', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column('metadata', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
def downgrade() -> None:
op.drop_table('corpus_entries')
op.drop_table('quotation_items')
op.drop_table('quotations')
op.drop_table('messages')
op.drop_table('conversations')
op.drop_table('customers')
op.drop_table('products')
op.drop_table('users')