100 lines
4.2 KiB
Python
100 lines
4.2 KiB
Python
"""
|
||
新闻文章数据库模型
|
||
"""
|
||
from datetime import datetime
|
||
from sqlalchemy import Column, Integer, String, Text, DateTime, JSON, Boolean, Float
|
||
from sqlalchemy.orm import relationship
|
||
|
||
from app.models.database import Base
|
||
|
||
|
||
class NewsArticle(Base):
|
||
"""新闻文章表"""
|
||
__tablename__ = "news_articles"
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
|
||
# 新闻基本信息
|
||
title = Column(String(500), nullable=False)
|
||
content = Column(Text, nullable=True) # 完整内容或摘要
|
||
content_hash = Column(String(64), nullable=False, index=True) # 内容哈希,用于去重
|
||
url = Column(String(1000), nullable=False, unique=True) # 原文链接
|
||
source = Column(String(100), nullable=False, index=True) # 来源网站
|
||
author = Column(String(200), nullable=True) # 作者
|
||
|
||
# 新闻分类
|
||
category = Column(String(50), nullable=False, index=True) # 'crypto', 'stock', 'forex', 'commodity'
|
||
tags = Column(JSON, nullable=True) # 标签列表
|
||
|
||
# 时间信息
|
||
published_at = Column(DateTime, nullable=True, index=True) # 发布时间
|
||
crawled_at = Column(DateTime, default=datetime.utcnow, index=True) # 爬取时间
|
||
|
||
# LLM 分析结果
|
||
llm_analyzed = Column(Boolean, default=False, index=True) # 是否已分析
|
||
market_impact = Column(String(20), nullable=True, index=True) # 'high', 'medium', 'low'
|
||
impact_type = Column(String(50), nullable=True) # 'bullish', 'bearish', 'neutral'
|
||
relevant_symbols = Column(JSON, nullable=True) # 相关的币种/股票代码
|
||
|
||
# LLM 分析详情
|
||
sentiment = Column(String(20), nullable=True) # 'positive', 'negative', 'neutral'
|
||
summary = Column(Text, nullable=True) # LLM 生成的摘要
|
||
key_points = Column(JSON, nullable=True) # 关键点列表
|
||
trading_advice = Column(Text, nullable=True) # 交易建议
|
||
|
||
# 优先级队列
|
||
priority = Column(Float, default=0.0, index=True) # 优先级分数
|
||
priority_reason = Column(Text, nullable=True) # 优先级原因
|
||
|
||
# 通知状态
|
||
notified = Column(Boolean, default=False, index=True) # 是否已发送通知
|
||
notification_sent_at = Column(DateTime, nullable=True)
|
||
notification_channel = Column(String(50), nullable=True) # 'feishu', 'telegram', etc.
|
||
|
||
# 质量控制
|
||
quality_score = Column(Float, nullable=True) # 质量分数 0-1
|
||
duplicate_of = Column(Integer, nullable=True) # 如果是重复,指向原始文章ID
|
||
|
||
# 状态
|
||
is_active = Column(Boolean, default=True, index=True) # 是否有效
|
||
|
||
# 时间戳
|
||
created_at = Column(DateTime, default=datetime.utcnow, index=True)
|
||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||
|
||
def __repr__(self):
|
||
return f"<NewsArticle({self.category} {self.source} {self.title[:50]}...)>"
|
||
|
||
def to_dict(self):
|
||
"""转换为字典"""
|
||
return {
|
||
'id': self.id,
|
||
'title': self.title,
|
||
'content': self.content,
|
||
'url': self.url,
|
||
'source': self.source,
|
||
'author': self.author,
|
||
'category': self.category,
|
||
'tags': self.tags,
|
||
'published_at': self.published_at.isoformat() if self.published_at else None,
|
||
'crawled_at': self.crawled_at.isoformat() if self.crawled_at else None,
|
||
'llm_analyzed': self.llm_analyzed,
|
||
'market_impact': self.market_impact,
|
||
'impact_type': self.impact_type,
|
||
'relevant_symbols': self.relevant_symbols,
|
||
'sentiment': self.sentiment,
|
||
'summary': self.summary,
|
||
'key_points': self.key_points,
|
||
'trading_advice': self.trading_advice,
|
||
'priority': self.priority,
|
||
'priority_reason': self.priority_reason,
|
||
'notified': self.notified,
|
||
'notification_sent_at': self.notification_sent_at.isoformat() if self.notification_sent_at else None,
|
||
'notification_channel': self.notification_channel,
|
||
'quality_score': self.quality_score,
|
||
'duplicate_of': self.duplicate_of,
|
||
'is_active': self.is_active,
|
||
'created_at': self.created_at.isoformat() if self.created_at else None,
|
||
'updated_at': self.updated_at.isoformat() if self.updated_at else None,
|
||
}
|