Files
nanocode/fumamxCRM/backend/models.py
T
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

304 lines
12 KiB
Python

"""
数据库模型
"""
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,
}