This commit is contained in:
Ubuntu
2026-07-03 16:58:28 +08:00
commit 32b43f85bd
6240 changed files with 1987678 additions and 0 deletions
+303
View File
@@ -0,0 +1,303 @@
"""
数据库模型
"""
from sqlalchemy import Column, Integer, String, DateTime, Text, JSON, Float, Boolean, ForeignKey, UniqueConstraint
from sqlalchemy.sql import func
from database import Base
class User(Base):
"""管理员用户"""
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
hashed_password = Column(String)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
class Customer(Base):
"""本地客户"""
__tablename__ = "customers"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True) # 公司名称
country = Column(String) # 国家
source_country = Column(String) # 数据来源国家
import_data = Column(JSON, default=dict) # 进口数据
export_data = Column(JSON, default=dict) # 出口数据
raw_data = Column(JSON, default=dict) # 原始数据
notes = Column(Text) # 备注
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
class Country(Base):
"""国家信息表"""
__tablename__ = "countries"
id = Column(Integer, primary_key=True, index=True)
country_id = Column(Integer, unique=True, index=True) # API中的countryid
continent_id = Column(Integer, index=True) # 大洲ID
sort_order = Column(Integer, default=0) # 排序
cn_name = Column(String(100), index=True) # 中文名
en_name = Column(String(100)) # 英文名
en_name_ali = Column(String(100)) # 英文别名
short_name = Column(String(10)) # 简称(如CN)
short_name_ali = Column(String(10)) # 简称别名
pinyin = Column(String(10)) # 拼音首字母
del_state = Column(Integer, default=0) # 删除状态 0=正常 1=删除
def to_dict(self):
return {
"id": self.id,
"country_id": self.country_id,
"continent_id": self.continent_id,
"cn_name": self.cn_name,
"en_name": self.en_name,
"short_name": self.short_name,
"pinyin": self.pinyin,
}
class ImportTask(Base):
"""潜在客户导入任务"""
__tablename__ = "import_tasks"
id = Column(Integer, primary_key=True, index=True)
keyword = Column(String(200), index=True) # 搜索关键词
status = Column(String(20), default="pending", index=True) # pending/running/completed/failed/cancelled
# 分页信息
total_pages = Column(Integer, default=0) # 总页数
current_page = Column(Integer, default=0) # 当前处理页码
page_size = Column(Integer, default=10) # 每页数量
# 统计信息
total_count = Column(Integer, default=0) # 预计总企业数
processed_count = Column(Integer, default=0) # 已处理企业数
success_count = Column(Integer, default=0) # 成功导入企业数
failed_count = Column(Integer, default=0) # 失败企业数
duplicate_count = Column(Integer, default=0) # 重复跳过数
# 错误信息
error_msg = Column(Text) # 错误信息
# 时间
started_at = Column(DateTime(timezone=True)) # 开始时间
completed_at = Column(DateTime(timezone=True)) # 完成时间
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
def to_dict(self):
return {
"id": self.id,
"keyword": self.keyword,
"status": self.status,
"total_pages": self.total_pages,
"current_page": self.current_page,
"page_size": self.page_size,
"total_count": self.total_count,
"processed_count": self.processed_count,
"success_count": self.success_count,
"failed_count": self.failed_count,
"duplicate_count": self.duplicate_count,
"error_msg": self.error_msg,
"started_at": self.started_at.isoformat() if self.started_at else None,
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
"created_at": self.created_at.isoformat() if self.created_at else None,
"progress_percent": round((self.processed_count / self.total_count * 100), 1) if self.total_count > 0 else 0,
}
class PotentialCustomer(Base):
"""潜在客户企业"""
__tablename__ = "potential_customers"
id = Column(Integer, primary_key=True, index=True)
external_id = Column(String(64), index=True) # fumamx API 返回的 id
domain = Column(String(200), index=True) # 域名
name = Column(String(500)) # 公司名称
country = Column(String(10)) # 国家代码
country_en = Column(String(100)) # 英文国家名
country_cn = Column(String(100)) # 中文国家名
# 企业信息
industry = Column(String(200)) # 行业
employees = Column(String(50)) # 员工数
founded = Column(String(20)) # 成立时间
address = Column(String(500)) # 地址
phone = Column(String(100)) # 主电话
description = Column(Text) # 描述
# 在线资料
website = Column(String(300)) # 网站
linkedin_url = Column(String(500)) # LinkedIn
# 贸易数据
procurement_frequency = Column(Integer, default=0) # 采购频次
annual_purchase_amount = Column(Float, default=0) # 年采购额
hs_codes = Column(JSON, default=list) # HS编码列表
hs_entities = Column(JSON, default=list) # HS商品名列表
# 来源信息
keyword = Column(String(200), index=True) # 搜索关键词
source_score = Column(Float, default=0) # 匹配分数
source_db = Column(Integer) # 数据源
import_task_id = Column(Integer, ForeignKey("import_tasks.id")) # 导入任务ID
# 关联的联系人数量(冗余统计)
contact_count = Column(Integer, default=0)
# 深度分析状态(冗余字段,避免级联查询)
analysis_status = Column(String(20), default="none", index=True) # none/pending/running/completed/failed
analysis_count = Column(Integer, default=0) # 分析次数
last_analyzed_at = Column(DateTime(timezone=True)) # 最后分析时间
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# 联合唯一索引:domain + external_id
__table_args__ = (
UniqueConstraint('domain', 'external_id', name='uix_customer_domain_external'),
)
def to_dict(self):
return {
"id": self.id,
"external_id": self.external_id,
"domain": self.domain,
"name": self.name,
"country": self.country,
"country_en": self.country_en,
"country_cn": self.country_cn,
"industry": self.industry,
"employees": self.employees,
"address": self.address,
"phone": self.phone,
"description": self.description,
"website": self.website,
"linkedin_url": self.linkedin_url,
"procurement_frequency": self.procurement_frequency,
"annual_purchase_amount": self.annual_purchase_amount,
"hs_codes": self.hs_codes,
"hs_entities": self.hs_entities,
"keyword": self.keyword,
"source_score": self.source_score,
"contact_count": self.contact_count,
"analysis_status": self.analysis_status or "none", # 深度分析状态(反范式设计)
"analysis_count": self.analysis_count or 0,
"last_analyzed_at": self.last_analyzed_at.isoformat() if self.last_analyzed_at else None,
"created_at": self.created_at.isoformat() if self.created_at else None,
}
class PotentialContact(Base):
"""潜在客户联系人"""
__tablename__ = "potential_contacts"
id = Column(Integer, primary_key=True, index=True)
customer_id = Column(Integer, ForeignKey("potential_customers.id"), index=True)
# 联系人信息
name = Column(String(200)) # 姓名
position = Column(String(300)) # 职位
email = Column(String(300), index=True) # 邮箱
phone = Column(String(100)) # 电话
# 社交资料
linkedin = Column(String(500)) # LinkedIn URL
facebook = Column(String(500)) # Facebook
twitter = Column(String(500)) # Twitter
whatsapp = Column(String(100)) # WhatsApp
# 评分标签
score = Column(Integer, default=0) # 匹配分数
is_executive = Column(Boolean, default=False) # 是否高管
is_kp = Column(Boolean, default=False) # 是否关键人
is_purchase = Column(Boolean, default=False) # 是否采购负责人
# 验证状态
email_check_status = Column(String(50)) # 邮箱验证状态
whatsapp_status = Column(String(50)) # WhatsApp状态
created_at = Column(DateTime(timezone=True), server_default=func.now())
def to_dict(self):
return {
"id": self.id,
"customer_id": self.customer_id,
"name": self.name,
"position": self.position,
"email": self.email,
"phone": self.phone,
"linkedin": self.linkedin,
"score": self.score,
"is_executive": self.is_executive,
"is_kp": self.is_kp,
"created_at": self.created_at.isoformat() if self.created_at else None,
}
class DeepAnalysis(Base):
"""深度分析记录"""
__tablename__ = "deep_analyses"
id = Column(Integer, primary_key=True, index=True)
customer_id = Column(Integer, ForeignKey("potential_customers.id"), index=True)
company_name = Column(String(500)) # 企业名称(冗余)
# 分析状态
status = Column(String(20), default="pending", index=True) # pending/processing/completed/failed
# 报告文件
report_file_path = Column(String(500)) # 报告文件路径
# 使用的模型
model_used = Column(String(50))
# 商业画布9个维度的摘要(可选,用于快速预览)
canvas_summary = Column(JSON, default=dict)
# 错误信息
error_message = Column(Text)
# 时间
created_at = Column(DateTime(timezone=True), server_default=func.now())
completed_at = Column(DateTime(timezone=True))
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
def to_dict(self):
return {
"id": self.id,
"customer_id": self.customer_id,
"company_name": self.company_name,
"status": self.status,
"report_file_path": self.report_file_path,
"model_used": self.model_used,
"canvas_summary": self.canvas_summary,
"error_message": self.error_message,
"created_at": self.created_at.isoformat() if self.created_at else None,
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
}
class SystemSetting(Base):
"""系统设置"""
__tablename__ = "system_settings"
id = Column(Integer, primary_key=True, index=True)
key = Column(String(100), unique=True, index=True) # 设置项key
value = Column(Text) # 设置项value(加密存储敏感信息)
description = Column(String(500)) # 描述
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
def to_dict(self):
return {
"id": self.id,
"key": self.key,
"value": self.value,
"description": self.description,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}