- 重构数据访问层:引入DAO模式,支持MySQL/SQLite双数据库 - 新增数据库架构:完整的股票数据、AI分析、自选股管理表结构 - 升级AI分析服务:集成豆包大模型,支持多维度分析 - 优化API路由:分离市场数据API,提供更清晰的接口设计 - 完善项目文档:添加数据库迁移指南、新功能指南等 - 清理冗余文件:删除旧的缓存文件和无用配置 - 新增调度器:支持定时任务和数据自动更新 - 改进前端模板:简化的股票展示页面 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
172 lines
6.2 KiB
Python
172 lines
6.2 KiB
Python
"""
|
|
监控列表数据访问对象
|
|
"""
|
|
from typing import Dict, List, Optional, Tuple
|
|
from datetime import datetime
|
|
|
|
from .base_dao import BaseDAO
|
|
|
|
|
|
class WatchlistDAO(BaseDAO):
|
|
"""监控列表数据访问对象"""
|
|
|
|
def get_watchlist(self) -> List[Dict]:
|
|
"""获取完整的监控列表,包含股票信息"""
|
|
query = """
|
|
SELECT
|
|
w.stock_code,
|
|
s.stock_name,
|
|
s.market,
|
|
w.target_market_value_min,
|
|
w.target_market_value_max,
|
|
w.created_at,
|
|
w.updated_at
|
|
FROM watchlist w
|
|
JOIN stocks s ON w.stock_code = s.stock_code
|
|
ORDER BY w.created_at DESC
|
|
"""
|
|
return self._execute_query(query)
|
|
|
|
def add_to_watchlist(self, stock_code: str, target_min: float = None,
|
|
target_max: float = None) -> bool:
|
|
"""添加股票到监控列表"""
|
|
try:
|
|
# 检查是否已在监控列表中
|
|
existing = self.get_watchlist_item(stock_code)
|
|
if existing:
|
|
# 更新现有的目标市值
|
|
return self.update_watchlist_item(stock_code, target_min, target_max)
|
|
|
|
# 添加新项到监控列表
|
|
query = """
|
|
INSERT INTO watchlist (stock_code, target_market_value_min, target_market_value_max)
|
|
VALUES (%s, %s, %s)
|
|
"""
|
|
self._execute_insert(query, (stock_code, target_min, target_max))
|
|
|
|
self.log_data_update('watchlist', stock_code, 'success', 'Added to watchlist')
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"添加到监控列表失败: {stock_code}, 错误: {e}")
|
|
self.log_data_update('watchlist', stock_code, 'failed', str(e))
|
|
return False
|
|
|
|
def remove_from_watchlist(self, stock_code: str) -> bool:
|
|
"""从监控列表移除股票"""
|
|
try:
|
|
query = "DELETE FROM watchlist WHERE stock_code = %s"
|
|
affected_rows = self._execute_update(query, (stock_code,))
|
|
success = affected_rows > 0
|
|
|
|
if success:
|
|
self.log_data_update('watchlist', stock_code, 'success', 'Removed from watchlist')
|
|
else:
|
|
self.log_data_update('watchlist', stock_code, 'failed', 'Stock not found in watchlist')
|
|
|
|
return success
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"从监控列表移除失败: {stock_code}, 错误: {e}")
|
|
self.log_data_update('watchlist', stock_code, 'failed', str(e))
|
|
return False
|
|
|
|
def get_watchlist_item(self, stock_code: str) -> Optional[Dict]:
|
|
"""获取监控列表中的单个项目"""
|
|
query = """
|
|
SELECT
|
|
w.stock_code,
|
|
s.stock_name,
|
|
s.market,
|
|
w.target_market_value_min,
|
|
w.target_market_value_max,
|
|
w.created_at,
|
|
w.updated_at
|
|
FROM watchlist w
|
|
JOIN stocks s ON w.stock_code = s.stock_code
|
|
WHERE w.stock_code = %s
|
|
"""
|
|
return self._execute_single_query(query, (stock_code,))
|
|
|
|
def update_watchlist_item(self, stock_code: str, target_min: float = None,
|
|
target_max: float = None) -> bool:
|
|
"""更新监控列表项目"""
|
|
try:
|
|
query = """
|
|
UPDATE watchlist
|
|
SET target_market_value_min = %s,
|
|
target_market_value_max = %s,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE stock_code = %s
|
|
"""
|
|
affected_rows = self._execute_update(query, (target_min, target_max, stock_code))
|
|
success = affected_rows > 0
|
|
|
|
if success:
|
|
self.log_data_update('watchlist', stock_code, 'success', 'Updated watchlist item')
|
|
else:
|
|
self.log_data_update('watchlist', stock_code, 'failed', 'Stock not found in watchlist')
|
|
|
|
return success
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"更新监控列表失败: {stock_code}, 错误: {e}")
|
|
self.log_data_update('watchlist', stock_code, 'failed', str(e))
|
|
return False
|
|
|
|
def get_watchlist_with_data(self, data_date: str = None) -> List[Dict]:
|
|
"""获取监控列表及其股票数据"""
|
|
if data_date is None:
|
|
data_date = self.get_today_date()
|
|
|
|
query = """
|
|
SELECT
|
|
w.stock_code,
|
|
s.stock_name,
|
|
s.market,
|
|
w.target_market_value_min,
|
|
w.target_market_value_max,
|
|
sd.price,
|
|
sd.change_percent,
|
|
sd.market_value as current_market_value,
|
|
sd.pe_ratio,
|
|
sd.pb_ratio,
|
|
sd.from_cache
|
|
FROM watchlist w
|
|
JOIN stocks s ON w.stock_code = s.stock_code
|
|
LEFT JOIN stock_data sd ON w.stock_code = sd.stock_code AND sd.data_date = %s
|
|
ORDER BY w.created_at DESC
|
|
"""
|
|
return self._execute_query(query, (data_date,))
|
|
|
|
def clear_watchlist(self) -> bool:
|
|
"""清空监控列表"""
|
|
try:
|
|
query = "DELETE FROM watchlist"
|
|
self._execute_update(query)
|
|
self.log_data_update('watchlist', 'all', 'success', 'Cleared watchlist')
|
|
return True
|
|
except Exception as e:
|
|
self.logger.error(f"清空监控列表失败: {e}")
|
|
self.log_data_update('watchlist', 'all', 'failed', str(e))
|
|
return False
|
|
|
|
def get_watchlist_count(self) -> int:
|
|
"""获取监控列表股票数量"""
|
|
query = "SELECT COUNT(*) as count FROM watchlist"
|
|
result = self._execute_single_query(query)
|
|
return result['count'] if result else 0
|
|
|
|
def get_stocks_needing_update(self, data_date: str = None) -> List[str]:
|
|
"""获取需要更新数据的股票代码列表"""
|
|
if data_date is None:
|
|
data_date = self.get_today_date()
|
|
|
|
query = """
|
|
SELECT DISTINCT w.stock_code
|
|
FROM watchlist w
|
|
LEFT JOIN stock_data sd ON w.stock_code = sd.stock_code AND sd.data_date = %s
|
|
WHERE sd.stock_code IS NULL OR sd.data_date < %s
|
|
"""
|
|
results = self._execute_query(query, (data_date, data_date))
|
|
return [item['stock_code'] for item in results] |