7b62c2f8b4
## H5 底部导航修复 (Bug #10) - 精简 App.vue,移除重复 tabbar,仅保留全局样式 - uni-page 设置 height: calc(100% - 50px) + overflow-y: auto - 内容区域精确停在底部导航上方,独立滚动不再叠加 - 恢复 custom-tab-bar 组件 ## 项目进度文档 - PROGRESS.md 更新至 10 个 Bug 修复 - 新增 H5 底部导航修复记录 - 新增历史变更条目
190 lines
10 KiB
Python
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') |