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:
TradeMate Dev
2026-05-08 18:17:12 +08:00
commit c6206787da
121 changed files with 11743 additions and 0 deletions
+11
View File
@@ -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",
]
+26
View File
@@ -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)
+72
View File
@@ -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")
+50
View File
@@ -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")
+54
View File
@@ -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")