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,388 @@
|
||||
# 外贸小助手 (TradeMate) — 数据库设计文档
|
||||
|
||||
> 版本: v1.0
|
||||
> 创建日期: 2026-05-08
|
||||
|
||||
---
|
||||
|
||||
## 一、数据库概述
|
||||
|
||||
- **数据库类型**: PostgreSQL 15 + pgvector
|
||||
- **字符集**: UTF-8
|
||||
- **时区**: UTC (存储), 应用层转换
|
||||
|
||||
---
|
||||
|
||||
## 二、实体关系图
|
||||
|
||||
```
|
||||
┌────────────────┐ ┌────────────────┐
|
||||
│ users │ │ products │
|
||||
├────────────────┤ ├────────────────┤
|
||||
│ id (PK) │◄──────│ user_id (FK) │
|
||||
│ wechat_openid │ │ id (PK) │
|
||||
│ phone │ │ name │
|
||||
│ username │ │ name_en │
|
||||
│ password_hash │ │ description │
|
||||
│ tier │ │ category │
|
||||
│ is_active │ │ price │
|
||||
│ settings (JSON)│ │ keywords (JSON)│
|
||||
│ created_at │ │ specifications │
|
||||
└───────┬────────┘ └────────────────┘
|
||||
│
|
||||
│ 1:N
|
||||
▼
|
||||
┌────────────────┐ ┌────────────────┐
|
||||
│ customers │ │ quotations │
|
||||
├────────────────┤ ├────────────────┤
|
||||
│ id (PK) │ │ id (PK) │
|
||||
│ user_id (FK) │ │ user_id (FK) │
|
||||
│ name │ │ customer_id(FK)│
|
||||
│ company │ │ title │
|
||||
│ country │ │ status │
|
||||
│ phone │ │ currency │
|
||||
│ whatsapp_id │ │ subtotal │
|
||||
│ status │ │ total │
|
||||
│ last_contact_at│ │ ... │
|
||||
└───────┬────────┘ └────────────────┘
|
||||
│
|
||||
│ 1:N
|
||||
▼
|
||||
┌────────────────┐ ┌────────────────┐
|
||||
│ conversations │ │ quotation_items│
|
||||
├────────────────┤ ├────────────────┤
|
||||
│ id (PK) │ │ id (PK) │
|
||||
│ user_id (FK) │ │ quotation_id │
|
||||
│ customer_id(FK)│ │ product_name │
|
||||
│ channel │ │ quantity │
|
||||
│ status │ │ unit_price │
|
||||
│ message_count │ │ total_price │
|
||||
└───────┬────────┘ └────────────────┘
|
||||
│
|
||||
│ 1:N
|
||||
▼
|
||||
┌────────────────┐
|
||||
│ messages │
|
||||
├────────────────┤
|
||||
│ id (PK) │
|
||||
│ conversation_id│
|
||||
│ direction │
|
||||
│ content │
|
||||
│ content_translt│
|
||||
│ ai_suggestions │
|
||||
└────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、表结构定义
|
||||
|
||||
### 3.1 用户表 (users)
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
wechat_openid VARCHAR(255) UNIQUE,
|
||||
phone VARCHAR(20) UNIQUE,
|
||||
username VARCHAR(100),
|
||||
password_hash VARCHAR(255),
|
||||
tier VARCHAR(50) DEFAULT 'free',
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
settings JSONB DEFAULT '{"preferred_translate_provider": "auto", "reply_tone": "professional"}',
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_users_wechat ON users(wechat_openid);
|
||||
CREATE INDEX idx_users_phone ON users(phone);
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | UUID | 主键 |
|
||||
| wechat_openid | VARCHAR | 微信 OpenID (唯一) |
|
||||
| phone | VARCHAR | 手机号 (唯一) |
|
||||
| username | VARCHAR | 用户名 |
|
||||
| password_hash | VARCHAR | 密码 bcrypt 哈希 |
|
||||
| tier | VARCHAR | 订阅等级: free/pro/enterprise |
|
||||
| is_active | BOOLEAN | 账户状态 |
|
||||
| settings | JSONB | 用户偏好设置 |
|
||||
|
||||
---
|
||||
|
||||
### 3.2 产品表 (products)
|
||||
|
||||
```sql
|
||||
CREATE TABLE products (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
name_en VARCHAR(255),
|
||||
description TEXT,
|
||||
description_en TEXT,
|
||||
category VARCHAR(100),
|
||||
price VARCHAR(50),
|
||||
price_unit VARCHAR(20) DEFAULT 'USD',
|
||||
moq VARCHAR(50),
|
||||
keywords JSONB DEFAULT '[]',
|
||||
specifications JSONB DEFAULT '{}',
|
||||
images JSONB DEFAULT '[]',
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_products_user ON products(user_id);
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | UUID | 主键 |
|
||||
| user_id | UUID | 所属用户 |
|
||||
| name | VARCHAR | 产品名称 (中文) |
|
||||
| name_en | VARCHAR | 产品英文名 |
|
||||
| description | TEXT | 产品描述 |
|
||||
| price | VARCHAR | 价格 |
|
||||
| moq | VARCHAR | 最小起订量 |
|
||||
| keywords | JSONB | 关键词列表 |
|
||||
|
||||
---
|
||||
|
||||
### 3.3 客户表 (customers)
|
||||
|
||||
```sql
|
||||
CREATE TABLE customers (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
company VARCHAR(255),
|
||||
country VARCHAR(100),
|
||||
phone VARCHAR(50),
|
||||
email VARCHAR(255),
|
||||
whatsapp_id VARCHAR(255),
|
||||
source VARCHAR(100),
|
||||
tags JSONB DEFAULT '[]',
|
||||
notes TEXT,
|
||||
preference JSONB DEFAULT '{}',
|
||||
status VARCHAR(50) DEFAULT 'lead',
|
||||
last_contact_at TIMESTAMP,
|
||||
silence_started_at TIMESTAMP,
|
||||
next_followup_at TIMESTAMP,
|
||||
estimated_value VARCHAR(50),
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_customers_user ON customers(user_id);
|
||||
CREATE INDEX idx_customers_status ON customers(status);
|
||||
CREATE INDEX idx_customers_last_contact ON customers(last_contact_at);
|
||||
```
|
||||
|
||||
**status 枚举值**:
|
||||
- `lead`: 潜在客户
|
||||
- `negotiating`: 谈判中
|
||||
- `customer`: 已成交客户
|
||||
- `lost`: 丢失客户
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | UUID | 主键 |
|
||||
| user_id | UUID | 所属用户 |
|
||||
| name | VARCHAR | 客户姓名 |
|
||||
| whatsapp_id | VARCHAR | WhatsApp ID |
|
||||
| status | VARCHAR | 客户状态 |
|
||||
| last_contact_at | TIMESTAMP | 最后联系时间 |
|
||||
| silence_started_at | TIMESTAMP | 沉默开始时间 |
|
||||
| next_followup_at | TIMESTAMP | 下次跟进时间 |
|
||||
|
||||
---
|
||||
|
||||
### 3.4 对话表 (conversations)
|
||||
|
||||
```sql
|
||||
CREATE TABLE conversations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
|
||||
channel VARCHAR(50) DEFAULT 'whatsapp',
|
||||
topic VARCHAR(255),
|
||||
status VARCHAR(50) DEFAULT 'active',
|
||||
message_count INTEGER DEFAULT 0,
|
||||
last_message_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_conversations_user ON conversations(user_id);
|
||||
CREATE INDEX idx_conversations_customer ON conversations(customer_id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.5 消息表 (messages)
|
||||
|
||||
```sql
|
||||
CREATE TABLE messages (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
conversation_id UUID NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
|
||||
direction VARCHAR(20) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
content_translated TEXT,
|
||||
content_type VARCHAR(50) DEFAULT 'text',
|
||||
ai_suggestions JSONB,
|
||||
selected_suggestion INTEGER,
|
||||
user_edited TEXT,
|
||||
status VARCHAR(50) DEFAULT 'sent',
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_messages_conversation ON messages(conversation_id);
|
||||
```
|
||||
|
||||
**direction 枚举值**:
|
||||
- `inbound`: 客户发来
|
||||
- `outbound`: 用户发出
|
||||
|
||||
---
|
||||
|
||||
### 3.6 报价单表 (quotations)
|
||||
|
||||
```sql
|
||||
CREATE TABLE quotations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
customer_id UUID REFERENCES customers(id),
|
||||
title VARCHAR(255),
|
||||
status VARCHAR(50) DEFAULT 'draft',
|
||||
currency VARCHAR(10) DEFAULT 'USD',
|
||||
exchange_rate FLOAT,
|
||||
payment_terms VARCHAR(255),
|
||||
delivery_terms VARCHAR(255),
|
||||
lead_time VARCHAR(100),
|
||||
valid_until VARCHAR(100),
|
||||
subtotal FLOAT,
|
||||
discount FLOAT DEFAULT 0,
|
||||
shipping FLOAT DEFAULT 0,
|
||||
total FLOAT,
|
||||
notes TEXT,
|
||||
pdf_url TEXT,
|
||||
sent_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_quotations_user ON quotations(user_id);
|
||||
CREATE INDEX idx_quotations_customer ON quotations(customer_id);
|
||||
CREATE INDEX idx_quotations_status ON quotations(status);
|
||||
```
|
||||
|
||||
**status 枚举值**:
|
||||
- `draft`: 草稿
|
||||
- `sent`: 已发送
|
||||
- `accepted`: 已接受
|
||||
- `rejected`: 已拒绝
|
||||
- `expired`: 已过期
|
||||
|
||||
---
|
||||
|
||||
### 3.7 报价单项表 (quotation_items)
|
||||
|
||||
```sql
|
||||
CREATE TABLE quotation_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
quotation_id UUID NOT NULL REFERENCES quotations(id) ON DELETE CASCADE,
|
||||
product_name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
quantity INTEGER NOT NULL,
|
||||
unit_price FLOAT NOT NULL,
|
||||
total_price FLOAT,
|
||||
unit VARCHAR(50) DEFAULT 'pcs'
|
||||
);
|
||||
|
||||
CREATE INDEX idx_quotation_items_quotation ON quotation_items(quotation_id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.8 语料库表 (corpus_entries)
|
||||
|
||||
```sql
|
||||
CREATE TABLE corpus_entries (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
source_text TEXT NOT NULL,
|
||||
target_text TEXT NOT NULL,
|
||||
source_lang VARCHAR(20),
|
||||
target_lang VARCHAR(20),
|
||||
task_type VARCHAR(50) NOT NULL,
|
||||
domain VARCHAR(100) DEFAULT 'general',
|
||||
provider_used VARCHAR(50),
|
||||
quality_score FLOAT DEFAULT 0.5,
|
||||
user_edited BOOLEAN DEFAULT false,
|
||||
user_rating INTEGER,
|
||||
usage_count INTEGER DEFAULT 0,
|
||||
embedding VECTOR(768),
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_corpus_task ON corpus_entries(task_type);
|
||||
CREATE INDEX idx_corpus_domain ON corpus_entries(domain);
|
||||
CREATE INDEX idx_corpus_embedding ON corpus_entries USING ivfflat (embedding vector_cosine_ops);
|
||||
```
|
||||
|
||||
**task_type 枚举值**:
|
||||
- `translation`: 翻译
|
||||
- `reply_suggestion`: 回复建议
|
||||
- `marketing_copy`: 营销文案
|
||||
|
||||
---
|
||||
|
||||
## 四、pgvector 扩展
|
||||
|
||||
```sql
|
||||
CREATE EXTENSION IF NOT EXISTS vector;
|
||||
```
|
||||
|
||||
语料库表使用向量存储,支持语义相似度搜索:
|
||||
|
||||
```sql
|
||||
-- 查找相似翻译
|
||||
SELECT * FROM corpus_entries
|
||||
WHERE task_type = 'translation'
|
||||
ORDER BY embedding <=> query_embedding
|
||||
LIMIT 5;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、索引策略
|
||||
|
||||
| 表 | 索引 | 用途 |
|
||||
|---|------|------|
|
||||
| users | wechat_openid, phone | 登录查询 |
|
||||
| products | user_id | 用户产品列表 |
|
||||
| customers | user_id, status, last_contact_at | 客户列表、沉默检测 |
|
||||
| conversations | user_id, customer_id | 对话查询 |
|
||||
| messages | conversation_id | 消息历史 |
|
||||
| quotations | user_id, customer_id, status | 报价单管理 |
|
||||
| corpus_entries | task_type, domain, embedding | 语料检索 |
|
||||
|
||||
---
|
||||
|
||||
## 六、数据保留策略
|
||||
|
||||
| 数据类型 | 保留期限 | 原因 |
|
||||
|---------|---------|------|
|
||||
| 用户数据 | 永久 | 业务核心 |
|
||||
| 客户数据 | 永久 | 业务核心 |
|
||||
| 消息数据 | 2年 | 对话历史 |
|
||||
| 报价单数据 | 3年 | 订单追溯 |
|
||||
| 语料数据 | 永久 | AI训练 |
|
||||
| 日志数据 | 90天 | 调试审计 |
|
||||
|
||||
---
|
||||
|
||||
## 七、迁移脚本
|
||||
|
||||
使用 Alembic 进行数据库迁移,初始迁移见 `backend/alembic/versions/001_initial.py`。
|
||||
Reference in New Issue
Block a user