Initial commit: TradeMate 外贸小助手 MVP
项目结构: - backend/ Python FastAPI 后端 - uni-app/ uni-app跨端前端 - docs/ 设计文档 - docker-compose.yml Docker编排 - nginx/scripts/systemd 运维配置 已完成功能: - 用户认证 (JWT) - 智能翻译 + 回复建议 - 营销素材生成 - 客户管理 + 沉默检测 - 报价单管理 - 产品库管理 - 汇率换算 - 推送通知 (uni-push) - WhatsApp Webhook框架 - Celery定时任务
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
from .user import User, Product
|
||||
from .customer import Customer, Conversation, Message
|
||||
from .quotation import Quotation, QuotationItem
|
||||
from .corpus import CorpusEntry
|
||||
|
||||
__all__ = [
|
||||
"User", "Product",
|
||||
"Customer", "Conversation", "Message",
|
||||
"Quotation", "QuotationItem",
|
||||
"CorpusEntry",
|
||||
]
|
||||
@@ -0,0 +1,26 @@
|
||||
from sqlalchemy import Column, String, Integer, DateTime, Text, Float
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||||
from pgvector.sqlalchemy import Vector
|
||||
from datetime import datetime
|
||||
from app.database import Base
|
||||
import uuid
|
||||
|
||||
|
||||
class CorpusEntry(Base):
|
||||
__tablename__ = "corpus_entries"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
source_text = Column(Text, nullable=False)
|
||||
target_text = Column(Text, nullable=False)
|
||||
source_lang = Column(String(20))
|
||||
target_lang = Column(String(20))
|
||||
task_type = Column(String(50), nullable=False)
|
||||
domain = Column(String(100), default="general")
|
||||
provider_used = Column(String(50))
|
||||
quality_score = Column(Float, default=0.5)
|
||||
user_edited = Column(Boolean, default=False)
|
||||
user_rating = Column(Integer)
|
||||
usage_count = Column(Integer, default=0)
|
||||
embedding = Column(Vector(768))
|
||||
metadata = Column(JSONB, default={})
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
@@ -0,0 +1,72 @@
|
||||
from sqlalchemy import Column, String, Boolean, Integer, DateTime, Text, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||||
from sqlalchemy.orm import relationship
|
||||
from datetime import datetime
|
||||
from app.database import Base
|
||||
import uuid
|
||||
|
||||
|
||||
class Customer(Base):
|
||||
__tablename__ = "customers"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
||||
name = Column(String(255), nullable=False)
|
||||
company = Column(String(255))
|
||||
country = Column(String(100))
|
||||
phone = Column(String(50))
|
||||
email = Column(String(255))
|
||||
whatsapp_id = Column(String(255))
|
||||
source = Column(String(100))
|
||||
tags = Column(JSONB, default=[])
|
||||
notes = Column(Text)
|
||||
preference = Column(JSONB, default={})
|
||||
status = Column(String(50), default="lead")
|
||||
last_contact_at = Column(DateTime)
|
||||
silence_started_at = Column(DateTime)
|
||||
next_followup_at = Column(DateTime)
|
||||
estimated_value = Column(String(50))
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
user = relationship("User", back_populates="customers")
|
||||
conversations = relationship("Conversation", back_populates="customer", cascade="all, delete-orphan")
|
||||
quotations = relationship("Quotation", back_populates="customer")
|
||||
|
||||
|
||||
class Conversation(Base):
|
||||
__tablename__ = "conversations"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
||||
customer_id = Column(UUID(as_uuid=True), ForeignKey("customers.id"), nullable=False, index=True)
|
||||
channel = Column(String(50), default="whatsapp")
|
||||
topic = Column(String(255))
|
||||
status = Column(String(50), default="active")
|
||||
message_count = Column(Integer, default=0)
|
||||
last_message_at = Column(DateTime)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
user = relationship("User", back_populates="conversations")
|
||||
customer = relationship("Customer", back_populates="conversations")
|
||||
messages = relationship("Message", back_populates="conversation", cascade="all, delete-orphan", order_by="Message.created_at")
|
||||
|
||||
|
||||
class Message(Base):
|
||||
__tablename__ = "messages"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
conversation_id = Column(UUID(as_uuid=True), ForeignKey("conversations.id"), nullable=False, index=True)
|
||||
direction = Column(String(20), nullable=False)
|
||||
content = Column(Text, nullable=False)
|
||||
content_translated = Column(Text)
|
||||
content_type = Column(String(50), default="text")
|
||||
ai_suggestions = Column(JSONB)
|
||||
selected_suggestion = Column(Integer)
|
||||
user_edited = Column(Text)
|
||||
status = Column(String(50), default="sent")
|
||||
metadata = Column(JSONB, default={})
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
conversation = relationship("Conversation", back_populates="messages")
|
||||
@@ -0,0 +1,50 @@
|
||||
from sqlalchemy import Column, String, Boolean, Integer, DateTime, Text, ForeignKey, Float
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||||
from sqlalchemy.orm import relationship
|
||||
from datetime import datetime
|
||||
from app.database import Base
|
||||
import uuid
|
||||
|
||||
|
||||
class Quotation(Base):
|
||||
__tablename__ = "quotations"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
||||
customer_id = Column(UUID(as_uuid=True), ForeignKey("customers.id"), nullable=False)
|
||||
title = Column(String(255))
|
||||
status = Column(String(50), default="draft")
|
||||
currency = Column(String(10), default="USD")
|
||||
exchange_rate = Column(Float)
|
||||
payment_terms = Column(String(255))
|
||||
delivery_terms = Column(String(255))
|
||||
lead_time = Column(String(100))
|
||||
valid_until = Column(String(100))
|
||||
subtotal = Column(Float)
|
||||
discount = Column(Float, default=0)
|
||||
shipping = Column(Float, default=0)
|
||||
total = Column(Float)
|
||||
notes = Column(Text)
|
||||
pdf_url = Column(Text)
|
||||
sent_at = Column(DateTime)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
user = relationship("User", back_populates="quotations")
|
||||
customer = relationship("Customer", back_populates="quotations")
|
||||
items = relationship("QuotationItem", back_populates="quotation", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class QuotationItem(Base):
|
||||
__tablename__ = "quotation_items"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
quotation_id = Column(UUID(as_uuid=True), ForeignKey("quotations.id"), nullable=False, index=True)
|
||||
product_name = Column(String(255), nullable=False)
|
||||
description = Column(Text)
|
||||
quantity = Column(Integer, nullable=False)
|
||||
unit_price = Column(Float, nullable=False)
|
||||
total_price = Column(Float)
|
||||
unit = Column(String(50), default="pcs")
|
||||
|
||||
quotation = relationship("Quotation", back_populates="items")
|
||||
@@ -0,0 +1,54 @@
|
||||
from sqlalchemy import Column, String, Boolean, Integer, DateTime, Text
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||||
from sqlalchemy.orm import relationship
|
||||
from datetime import datetime
|
||||
from app.database import Base
|
||||
import uuid
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
wechat_openid = Column(String(255), unique=True, index=True)
|
||||
phone = Column(String(20), unique=True, index=True)
|
||||
username = Column(String(100))
|
||||
password_hash = Column(String(255))
|
||||
tier = Column(String(50), default="free")
|
||||
is_active = Column(Boolean, default=True)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
settings = Column(JSONB, default={
|
||||
"preferred_translate_provider": "auto",
|
||||
"reply_tone": "professional",
|
||||
"timezone": "Asia/Shanghai",
|
||||
"languages": ["zh", "en"],
|
||||
})
|
||||
|
||||
products = relationship("Product", back_populates="user", cascade="all, delete-orphan")
|
||||
customers = relationship("Customer", back_populates="user", cascade="all, delete-orphan")
|
||||
conversations = relationship("Conversation", back_populates="user", cascade="all, delete-orphan")
|
||||
quotations = relationship("Quotation", back_populates="user", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class Product(Base):
|
||||
__tablename__ = "products"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
||||
name = Column(String(255), nullable=False)
|
||||
name_en = Column(String(255))
|
||||
description = Column(Text)
|
||||
description_en = Column(Text)
|
||||
category = Column(String(100))
|
||||
price = Column(String(50))
|
||||
price_unit = Column(String(20), default="USD")
|
||||
moq = Column(String(50))
|
||||
keywords = Column(JSONB, default=[])
|
||||
specifications = Column(JSONB, default={})
|
||||
images = Column(JSONB, default=[])
|
||||
is_active = Column(Boolean, default=True)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
user = relationship("User", back_populates="products")
|
||||
Reference in New Issue
Block a user