diff --git a/backend/api/main.py b/backend/api/main.py index 874cf69..fcbcfc6 100644 --- a/backend/api/main.py +++ b/backend/api/main.py @@ -97,13 +97,18 @@ app = FastAPI( ) # CORS配置(允许React前端访问) -cors_origins = os.getenv('CORS_ORIGINS', 'http://localhost:3000,http://localhost:5173,http://as.deepx1.com').split(',') +cors_origins_str = os.getenv('CORS_ORIGINS', 'http://localhost:3000,http://localhost:5173,http://as.deepx1.com,http://asapi.deepx1.com') +cors_origins = [origin.strip() for origin in cors_origins_str.split(',') if origin.strip()] + +logger.info(f"CORS允许的源: {cors_origins}") + app.add_middleware( CORSMiddleware, allow_origins=cors_origins, allow_credentials=True, - allow_methods=["*"], + allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"], allow_headers=["*"], + expose_headers=["*"], ) # 注册路由 diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js index d37cb58..08ff8f5 100644 --- a/frontend/src/services/api.js +++ b/frontend/src/services/api.js @@ -1,19 +1,27 @@ -const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'; +// 如果设置了VITE_API_URL环境变量,使用它;否则在开发环境使用相对路径(通过vite代理),生产环境使用默认值 +const API_BASE_URL = import.meta.env.VITE_API_URL || (import.meta.env.DEV ? '' : 'http://localhost:8000'); + +// 构建API URL的辅助函数,避免双斜杠和格式问题 +const buildUrl = (path) => { + const baseUrl = API_BASE_URL.endsWith('/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL; + const cleanPath = path.startsWith('/') ? path : `/${path}`; + return baseUrl ? `${baseUrl}${cleanPath}` : cleanPath; +}; export const api = { // 配置管理 getConfigs: async () => { - const response = await fetch(`${API_BASE_URL}/api/config/`); + const response = await fetch(buildUrl('/api/config/')); return response.json(); }, getConfig: async (key) => { - const response = await fetch(`${API_BASE_URL}/api/config/${key}`); + const response = await fetch(buildUrl(`/api/config/${key}`)); return response.json(); }, updateConfig: async (key, data) => { - const response = await fetch(`${API_BASE_URL}/api/config/${key}`, { + const response = await fetch(buildUrl(`/api/config/${key}`), { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(data) @@ -26,41 +34,59 @@ export const api = { }, updateConfigsBatch: async (configs) => { - const response = await fetch(`${API_BASE_URL}/api/config/batch`, { + const response = await fetch(buildUrl('/api/config/batch'), { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(configs) }); + if (!response.ok) { + const error = await response.json().catch(() => ({ detail: '批量更新配置失败' })); + throw new Error(error.detail || '批量更新配置失败'); + } return response.json(); }, // 交易记录 getTrades: async (params = {}) => { const query = new URLSearchParams(params).toString(); - const response = await fetch(`${API_BASE_URL}/api/trades?${query}`); + const url = query ? `${buildUrl('/api/trades')}?${query}` : buildUrl('/api/trades'); + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); if (!response.ok) { - throw new Error('获取交易记录失败'); + const error = await response.json().catch(() => ({ detail: '获取交易记录失败' })); + throw new Error(error.detail || '获取交易记录失败'); } return response.json(); }, getTradeStats: async (params = {}) => { const query = new URLSearchParams(params).toString(); - const response = await fetch(`${API_BASE_URL}/api/trades/stats?${query}`); + const url = query ? `${buildUrl('/api/trades/stats')}?${query}` : buildUrl('/api/trades/stats'); + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); if (!response.ok) { - throw new Error('获取交易统计失败'); + const error = await response.json().catch(() => ({ detail: '获取交易统计失败' })); + throw new Error(error.detail || '获取交易统计失败'); } return response.json(); }, // 统计 getPerformance: async (days = 7) => { - const response = await fetch(`${API_BASE_URL}/api/stats/performance?days=${days}`); + const response = await fetch(buildUrl(`/api/stats/performance?days=${days}`)); return response.json(); }, getDashboard: async () => { - const response = await fetch(`${API_BASE_URL}/api/dashboard/`); + const response = await fetch(buildUrl('/api/dashboard/')); return response.json(); }, }; diff --git a/trading_system/config.py b/trading_system/config.py index 31d1e1e..a5bbb85 100644 --- a/trading_system/config.py +++ b/trading_system/config.py @@ -18,20 +18,41 @@ def _init_config_manager(): try: import sys + import logging from pathlib import Path + + logger = logging.getLogger(__name__) + # 从trading_system目录向上两级到项目根目录,然后找backend project_root = Path(__file__).parent.parent backend_path = project_root / 'backend' + + logger.debug(f"尝试初始化配置管理器,项目根目录: {project_root}, backend路径: {backend_path}") + if backend_path.exists(): sys.path.insert(0, str(backend_path)) - from config_manager import config_manager - _config_manager = config_manager - USE_DB_CONFIG = True - return config_manager + try: + from config_manager import config_manager + _config_manager = config_manager + USE_DB_CONFIG = True + logger.info("✓ 配置管理器初始化成功,将从数据库读取配置") + return config_manager + except ImportError as e: + logger.warning(f"无法导入config_manager: {e},将使用环境变量和默认配置") + USE_DB_CONFIG = False + return None + except Exception as e: + logger.warning(f"配置管理器初始化失败: {e},将使用环境变量和默认配置") + USE_DB_CONFIG = False + return None else: + logger.warning(f"backend目录不存在: {backend_path},将使用环境变量和默认配置") USE_DB_CONFIG = False return None except Exception as e: + import logging + logger = logging.getLogger(__name__) + logger.warning(f"配置管理器初始化异常: {e},将使用环境变量和默认配置") USE_DB_CONFIG = False return None @@ -41,9 +62,20 @@ _init_config_manager() def _get_config_value(key, default=None): """获取配置值(支持动态重载)""" if _config_manager: - _config_manager.reload() # 每次获取配置时重新加载 - return _config_manager.get(key, default) or os.getenv(key, default) - return os.getenv(key, default) + try: + _config_manager.reload() # 每次获取配置时重新加载 + value = _config_manager.get(key, default) + # 如果从数据库获取到值,直接返回(即使是空字符串也返回) + if value is not None: + return value + except Exception as e: + import logging + logger = logging.getLogger(__name__) + logger.debug(f"从配置管理器获取{key}失败: {e},尝试环境变量") + + # 回退到环境变量 + env_value = os.getenv(key, default) + return env_value if env_value is not None else default def _get_trading_config(): """获取交易配置(支持动态重载)""" diff --git a/trading_system/main.py b/trading_system/main.py index d02872d..ad852f9 100644 --- a/trading_system/main.py +++ b/trading_system/main.py @@ -49,25 +49,73 @@ async def main(): logger.info("币安自动交易系统启动") logger.info("=" * 60) - # 检查API密钥 - if not config.BINANCE_API_KEY or not config.BINANCE_API_SECRET: - logger.error("请设置 BINANCE_API_KEY 和 BINANCE_API_SECRET 环境变量") - logger.error("或在 config.py 中直接配置") + # 检查配置管理器状态 + logger.info("检查配置管理器状态...") + if config.USE_DB_CONFIG: + logger.info("✓ 使用数据库配置") + # 尝试重新加载配置(确保获取最新值) + try: + config.reload_config() + logger.info("配置已重新加载") + except Exception as e: + logger.warning(f"重新加载配置失败: {e}") + else: + logger.warning("⚠ 未使用数据库配置,将使用环境变量和默认配置") + logger.warning("如果前端已配置API密钥,请检查:") + logger.warning("1. backend目录是否存在") + logger.warning("2. 数据库连接是否正常") + logger.warning("3. config_manager模块是否可以正常导入") + + # 检查API密钥(重新获取,确保是最新值) + api_key = config._get_config_value('BINANCE_API_KEY', '') + api_secret = config._get_config_value('BINANCE_API_SECRET', '') + + logger.info(f"API密钥检查: KEY存在={bool(api_key)}, SECRET存在={bool(api_secret)}") + + if not api_key or not api_secret: + logger.error("=" * 60) + logger.error("API密钥未配置!") + logger.error("=" * 60) + if config.USE_DB_CONFIG: + logger.error("配置管理器已启用,但未从数据库读取到API密钥") + logger.error("请检查:") + logger.error("1. 前端配置界面是否已设置BINANCE_API_KEY和BINANCE_API_SECRET") + logger.error("2. 数据库trading_config表中是否有这些配置项") + logger.error("3. 数据库连接是否正常") + else: + logger.error("请设置 BINANCE_API_KEY 和 BINANCE_API_SECRET 环境变量") + logger.error("或在 config.py 中直接配置") + logger.error("或确保backend目录存在,以便从数据库读取配置") + logger.error("=" * 60) return + # 更新config模块的API密钥(确保使用最新值) + config.BINANCE_API_KEY = api_key + config.BINANCE_API_SECRET = api_secret + # 初始化组件 client = None try: # 1. 初始化币安客户端 logger.info("初始化币安客户端...") - logger.info(f"测试网模式: {config.USE_TESTNET}") + + # 再次确认API密钥(使用最新值) + api_key = config._get_config_value('BINANCE_API_KEY', config.BINANCE_API_KEY) + api_secret = config._get_config_value('BINANCE_API_SECRET', config.BINANCE_API_SECRET) + use_testnet = config._get_config_value('USE_TESTNET', config.USE_TESTNET) + if isinstance(use_testnet, str): + use_testnet = use_testnet.lower() in ('true', '1', 'yes', 'on') + elif not isinstance(use_testnet, bool): + use_testnet = bool(use_testnet) + + logger.info(f"测试网模式: {use_testnet}") logger.info(f"连接超时: {config.CONNECTION_TIMEOUT}秒") logger.info(f"重试次数: {config.CONNECTION_RETRIES}次") client = BinanceClient( - api_key=config.BINANCE_API_KEY, - api_secret=config.BINANCE_API_SECRET, - testnet=config.USE_TESTNET + api_key=api_key, + api_secret=api_secret, + testnet=use_testnet ) await client.connect()