- 重构数据访问层:引入DAO模式,支持MySQL/SQLite双数据库 - 新增数据库架构:完整的股票数据、AI分析、自选股管理表结构 - 升级AI分析服务:集成豆包大模型,支持多维度分析 - 优化API路由:分离市场数据API,提供更清晰的接口设计 - 完善项目文档:添加数据库迁移指南、新功能指南等 - 清理冗余文件:删除旧的缓存文件和无用配置 - 新增调度器:支持定时任务和数据自动更新 - 改进前端模板:简化的股票展示页面 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
171 lines
6.2 KiB
Python
171 lines
6.2 KiB
Python
"""
|
|
系统配置数据访问对象
|
|
"""
|
|
from typing import Dict, List, Optional, Any
|
|
import json
|
|
from datetime import datetime, date
|
|
|
|
from .base_dao import BaseDAO
|
|
|
|
|
|
class ConfigDAO(BaseDAO):
|
|
"""系统配置数据访问对象"""
|
|
|
|
def get_config(self, key: str, default_value: Any = None) -> Any:
|
|
"""获取配置值"""
|
|
query = "SELECT config_value, config_type FROM system_config WHERE config_key = %s"
|
|
result = self._execute_single_query(query, (key,))
|
|
|
|
if not result:
|
|
return default_value
|
|
|
|
config_value = result['config_value']
|
|
config_type = result['config_type']
|
|
|
|
# 根据类型转换值
|
|
if config_type == 'integer':
|
|
try:
|
|
return int(config_value) if config_value else default_value
|
|
except (ValueError, TypeError):
|
|
return default_value
|
|
elif config_type == 'float':
|
|
try:
|
|
return float(config_value) if config_value else default_value
|
|
except (ValueError, TypeError):
|
|
return default_value
|
|
elif config_type == 'boolean':
|
|
return config_value.lower() in ('true', '1', 'yes', 'on') if config_value else default_value
|
|
elif config_type == 'json':
|
|
try:
|
|
return json.loads(config_value) if config_value else default_value
|
|
except json.JSONDecodeError:
|
|
return default_value
|
|
else: # string
|
|
return config_value if config_value else default_value
|
|
|
|
def set_config(self, key: str, value: Any, config_type: str = 'string') -> bool:
|
|
"""设置配置值"""
|
|
try:
|
|
# 转换值为字符串存储
|
|
if config_type == 'json':
|
|
str_value = json.dumps(value, ensure_ascii=False)
|
|
elif config_type == 'boolean':
|
|
str_value = str(value).lower()
|
|
else:
|
|
str_value = str(value)
|
|
|
|
# 检查配置是否存在
|
|
existing = self._execute_single_query(
|
|
"SELECT id FROM system_config WHERE config_key = %s", (key,)
|
|
)
|
|
|
|
if existing:
|
|
# 更新现有配置
|
|
query = """
|
|
UPDATE system_config
|
|
SET config_value = %s, config_type = %s, updated_at = CURRENT_TIMESTAMP
|
|
WHERE config_key = %s
|
|
"""
|
|
self._execute_update(query, (str_value, config_type, key))
|
|
else:
|
|
# 插入新配置
|
|
query = """
|
|
INSERT INTO system_config (config_key, config_value, config_type)
|
|
VALUES (%s, %s, %s)
|
|
"""
|
|
self._execute_insert(query, (key, str_value, config_type))
|
|
|
|
self.log_data_update('config', key, 'success', f'Config updated: {key}={value}')
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"设置配置失败: {key}={value}, 错误: {e}")
|
|
self.log_data_update('config', key, 'failed', str(e))
|
|
return False
|
|
|
|
def get_all_configs(self) -> Dict[str, Dict]:
|
|
"""获取所有配置"""
|
|
query = "SELECT * FROM system_config ORDER BY config_key"
|
|
results = self._execute_query(query)
|
|
|
|
configs = {}
|
|
for result in results:
|
|
key = result['config_key']
|
|
configs[key] = {
|
|
'value': self.get_config(key),
|
|
'type': result['config_type'],
|
|
'created_at': result['created_at'],
|
|
'updated_at': result['updated_at']
|
|
}
|
|
|
|
return configs
|
|
|
|
def delete_config(self, key: str) -> bool:
|
|
"""删除配置"""
|
|
try:
|
|
query = "DELETE FROM system_config WHERE config_key = %s"
|
|
affected_rows = self._execute_update(query, (key,))
|
|
success = affected_rows > 0
|
|
|
|
if success:
|
|
self.log_data_update('config', key, 'success', 'Config deleted')
|
|
else:
|
|
self.log_data_update('config', key, 'failed', 'Config not found')
|
|
|
|
return success
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"删除配置失败: {key}, 错误: {e}")
|
|
self.log_data_update('config', key, 'failed', str(e))
|
|
return False
|
|
|
|
def increment_counter(self, key: str, increment: int = 1) -> int:
|
|
"""递增计数器配置"""
|
|
try:
|
|
current_value = self.get_config(key, 0)
|
|
new_value = current_value + increment
|
|
self.set_config(key, new_value, 'integer')
|
|
return new_value
|
|
except Exception as e:
|
|
self.logger.error(f"递增计数器失败: {key}, 错误: {e}")
|
|
return 0
|
|
|
|
def reset_daily_counters(self) -> None:
|
|
"""重置每日计数器"""
|
|
daily_counters = [
|
|
'tushare_api_calls_today',
|
|
]
|
|
|
|
for counter in daily_counters:
|
|
self.set_config(counter, 0, 'integer')
|
|
|
|
# 更新最后重置日期
|
|
self.set_config('last_counter_reset_date', self.get_today_date(), 'date')
|
|
|
|
def get_last_data_update_date(self) -> Optional[str]:
|
|
"""获取最后数据更新日期"""
|
|
return self.get_config('last_data_update_date')
|
|
|
|
def set_last_data_update_date(self, date_str: str) -> bool:
|
|
"""设置最后数据更新日期"""
|
|
return self.set_config('last_data_update_date', date_str, 'date')
|
|
|
|
def get_cache_expiration_hours(self) -> int:
|
|
"""获取缓存过期时间(小时)"""
|
|
return self.get_config('cache_expiration_hours', 24)
|
|
|
|
def get_max_watchlist_size(self) -> int:
|
|
"""获取最大监控列表大小"""
|
|
return self.get_config('max_watchlist_size', 50)
|
|
|
|
def is_cache_expired(self, data_date: str) -> bool:
|
|
"""检查缓存是否过期"""
|
|
try:
|
|
cache_hours = self.get_cache_expiration_hours()
|
|
current_date = date.today()
|
|
data_date_obj = datetime.strptime(data_date, '%Y-%m-%d').date()
|
|
|
|
days_diff = (current_date - data_date_obj).days
|
|
return days_diff > 0 # 如果不是今天的数据,就算过期
|
|
except Exception:
|
|
return True # 如果无法解析日期,认为过期 |