a
This commit is contained in:
@@ -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,
|
||||
}
|
||||
Reference in New Issue
Block a user