diff --git a/backend/service_implementation/qihuo_analyzer/data/__pycache__/data_fetcher.cpython-311.pyc b/backend/service_implementation/qihuo_analyzer/data/__pycache__/data_fetcher.cpython-311.pyc index 72124ca..097dd0a 100644 Binary files a/backend/service_implementation/qihuo_analyzer/data/__pycache__/data_fetcher.cpython-311.pyc and b/backend/service_implementation/qihuo_analyzer/data/__pycache__/data_fetcher.cpython-311.pyc differ diff --git a/backend/service_implementation/qihuo_analyzer/data/data_fetcher.py b/backend/service_implementation/qihuo_analyzer/data/data_fetcher.py index 23b3b45..3a35b76 100644 --- a/backend/service_implementation/qihuo_analyzer/data/data_fetcher.py +++ b/backend/service_implementation/qihuo_analyzer/data/data_fetcher.py @@ -290,10 +290,10 @@ class DataFetcher: symbols_by_exchange = self.get_all_symbols_by_exchange() symbols = [] for exchange, products in symbols_by_exchange.items(): - for product, product_data in products.items(): + for product, product_contracts in products.items(): # 使用每个品种的第一个合约作为代表 - if product_data['contracts']: - symbols.append(product_data['contracts'][0]) + if product_contracts: + symbols.append(product_contracts[0]) return symbols except Exception as e: print(f"获取所有品种列表失败:{e}") @@ -321,49 +321,49 @@ class DataFetcher: "SHFE": { # 上海期货交易所 "AU": ["AU2603", "AU2604", "AU2605", "AU2606", "AU2607", "AU2608", "AU2609"], # 黄金 "AG": ["AG2603", "AG2604", "AG2605", "AG2606", "AG2607", "AG2608", "AG2609"], # 白银 - "CU": ["CU2603", "CU2604", "CU2605", "CU2606", "CU2607", "CU2608", "CU2609"], # 铜 - "NI": ["NI2603", "NI2604", "NI2605", "NI2606", "NI2607", "NI2608", "NI2609"], # 镍 - "SN": ["SN2603", "SN2604", "SN2605", "SN2606", "SN2607", "SN2608", "SN2609"], # 锡 - "FG": ["FG2603", "FG2604", "FG2605", "FG2606", "FG2607", "FG2608", "FG2609"], # 玻璃 - "RB": ["RB2603", "RB2604", "RB2605", "RB2606", "RB2607", "RB2608", "RB2609"], # 螺纹钢 - "AL": ["AL2603", "AL2604", "AL2605", "AL2606", "AL2607", "AL2608", "AL2609"], # 铝 - "ZN": ["ZN2603", "ZN2604", "ZN2605", "ZN2606", "ZN2607", "ZN2608", "ZN2609"], # 锌 - "RU": ["RU2603", "RU2604", "RU2605", "RU2606", "RU2607", "RU2608", "RU2609"], # 橡胶 - "NR": ["NR2603", "NR2604", "NR2605", "NR2606", "NR2607", "NR2608", "NR2609"], # 20号胶 - "FU": ["FU2603", "FU2604", "FU2605", "FU2606", "FU2607", "FU2608", "FU2609"], # 燃油 - "SC": ["SC2603", "SC2604", "SC2605", "SC2606", "SC2607", "SC2608", "SC2609"], # 原油 - "LU": ["LU2603", "LU2604", "LU2605", "LU2606", "LU2607", "LU2608", "LU2609"], # 低硫燃油 - "ALO": ["ALO2603", "ALO2604", "ALO2605", "ALO2606", "ALO2607", "ALO2608", "ALO2609"], # 氧化铝 - "LI": ["LI2603", "LI2604", "LI2605", "LI2606", "LI2607", "LI2608", "LI2609"], # 碳酸锂 - "SI": ["SI2603", "SI2604", "SI2605", "SI2606", "SI2607", "SI2608", "SI2609"] # 工业硅 - }, - "INE": { # 上海国际能源交易中心 - "SC": ["SC2603", "SC2604", "SC2605", "SC2606", "SC2607", "SC2608", "SC2609"], # 原油 - "LU": ["LU2603", "LU2604", "LU2605", "LU2606", "LU2607", "LU2608", "LU2609"] # 低硫燃油 - }, - "DCE": { # 大连商品交易所 - "JM": ["JM2603", "JM2604", "JM2605", "JM2606", "JM2607", "JM2608", "JM2609"], # 焦煤 - "P": ["P2603", "P2604", "P2605", "P2606", "P2607", "P2608", "P2609"], # 棕榈油 - "V": ["V2603", "V2604", "V2605", "V2606", "V2607", "V2608", "V2609"], # PVC - "MA": ["MA2603", "MA2604", "MA2605", "MA2606", "MA2607", "MA2608", "MA2609"], # 甲醇 - "BR": ["BR2603", "BR2604", "BR2605", "BR2606", "BR2607", "BR2608", "BR2609"] # 合成橡胶 - }, - "CZCE": { # 郑州商品交易所 - "FG": ["FG2603", "FG2604", "FG2605", "FG2606", "FG2607", "FG2608", "FG2609"], # 玻璃 - "MA": ["MA2603", "MA2604", "MA2605", "MA2606", "MA2607", "MA2608", "MA2609"], # 甲醇 - "V": ["V2603", "V2604", "V2605", "V2606", "V2607", "V2608", "V2609"], # PVC - "SA": ["SA2603", "SA2604", "SA2605", "SA2606", "SA2607", "SA2608", "SA2609"], # 纯碱 - "LY": ["LY2603", "LY2604", "LY2605", "LY2606", "LY2607", "LY2608", "LY2609"] # 烧碱 - }, - "CFFEX": { # 中国金融期货交易所 - "IH": ["IH2603", "IH2604", "IH2605", "IH2606", "IH2607", "IH2608", "IH2609"], # 上证50 - "IC": ["IC2603", "IC2604", "IC2605", "IC2606", "IC2607", "IC2608", "IC2609"], # 中证500 - "IM": ["IM2603", "IM2604", "IM2605", "IM2606", "IM2607", "IM2608", "IM2609"] # 中证1000 - }, - "GEM": { # 广州期货交易所 - "SI": ["SI2603", "SI2604", "SI2605", "SI2606", "SI2607", "SI2608", "SI2609"], # 工业硅 - "SP": ["SP2603", "SP2604", "SP2605", "SP2606", "SP2607", "SP2608", "SP2609"] # 多晶硅 - } + "CU": ["CU2603", "CU2604", "CU2605", "CU2606", "CU2607", "CU2608", "CU2609"]#, # 铜 + # "NI": ["NI2603", "NI2604", "NI2605", "NI2606", "NI2607", "NI2608", "NI2609"], # 镍 + # "SN": ["SN2603", "SN2604", "SN2605", "SN2606", "SN2607", "SN2608", "SN2609"], # 锡 + # "FG": ["FG2603", "FG2604", "FG2605", "FG2606", "FG2607", "FG2608", "FG2609"], # 玻璃 + # "RB": ["RB2603", "RB2604", "RB2605", "RB2606", "RB2607", "RB2608", "RB2609"], # 螺纹钢 + # "AL": ["AL2603", "AL2604", "AL2605", "AL2606", "AL2607", "AL2608", "AL2609"], # 铝 + # "ZN": ["ZN2603", "ZN2604", "ZN2605", "ZN2606", "ZN2607", "ZN2608", "ZN2609"], # 锌 + # "RU": ["RU2603", "RU2604", "RU2605", "RU2606", "RU2607", "RU2608", "RU2609"], # 橡胶 + # "NR": ["NR2603", "NR2604", "NR2605", "NR2606", "NR2607", "NR2608", "NR2609"], # 20号胶 + # "FU": ["FU2603", "FU2604", "FU2605", "FU2606", "FU2607", "FU2608", "FU2609"], # 燃油 + # "SC": ["SC2603", "SC2604", "SC2605", "SC2606", "SC2607", "SC2608", "SC2609"], # 原油 + # "LU": ["LU2603", "LU2604", "LU2605", "LU2606", "LU2607", "LU2608", "LU2609"], # 低硫燃油 + # "ALO": ["ALO2603", "ALO2604", "ALO2605", "ALO2606", "ALO2607", "ALO2608", "ALO2609"], # 氧化铝 + # "LI": ["LI2603", "LI2604", "LI2605", "LI2606", "LI2607", "LI2608", "LI2609"], # 碳酸锂 + # "SI": ["SI2603", "SI2604", "SI2605", "SI2606", "SI2607", "SI2608", "SI2609"] # 工业硅 + }#, + # "INE": { # 上海国际能源交易中心 + # "SC": ["SC2603", "SC2604", "SC2605", "SC2606", "SC2607", "SC2608", "SC2609"], # 原油 + # "LU": ["LU2603", "LU2604", "LU2605", "LU2606", "LU2607", "LU2608", "LU2609"] # 低硫燃油 + # }, + # "DCE": { # 大连商品交易所 + # "JM": ["JM2603", "JM2604", "JM2605", "JM2606", "JM2607", "JM2608", "JM2609"], # 焦煤 + # "P": ["P2603", "P2604", "P2605", "P2606", "P2607", "P2608", "P2609"], # 棕榈油 + # "V": ["V2603", "V2604", "V2605", "V2606", "V2607", "V2608", "V2609"], # PVC + # "MA": ["MA2603", "MA2604", "MA2605", "MA2606", "MA2607", "MA2608", "MA2609"], # 甲醇 + # "BR": ["BR2603", "BR2604", "BR2605", "BR2606", "BR2607", "BR2608", "BR2609"] # 合成橡胶 + # }, + # "CZCE": { # 郑州商品交易所 + # "FG": ["FG2603", "FG2604", "FG2605", "FG2606", "FG2607", "FG2608", "FG2609"], # 玻璃 + # "MA": ["MA2603", "MA2604", "MA2605", "MA2606", "MA2607", "MA2608", "MA2609"], # 甲醇 + # "V": ["V2603", "V2604", "V2605", "V2606", "V2607", "V2608", "V2609"], # PVC + # "SA": ["SA2603", "SA2604", "SA2605", "SA2606", "SA2607", "SA2608", "SA2609"], # 纯碱 + # "LY": ["LY2603", "LY2604", "LY2605", "LY2606", "LY2607", "LY2608", "LY2609"] # 烧碱 + # }, + # "CFFEX": { # 中国金融期货交易所 + # "IH": ["IH2603", "IH2604", "IH2605", "IH2606", "IH2607", "IH2608", "IH2609"], # 上证50 + # "IC": ["IC2603", "IC2604", "IC2605", "IC2606", "IC2607", "IC2608", "IC2609"], # 中证500 + # "IM": ["IM2603", "IM2604", "IM2605", "IM2606", "IM2607", "IM2608", "IM2609"] # 中证1000 + # }, + # "GEM": { # 广州期货交易所 + # "SI": ["SI2603", "SI2604", "SI2605", "SI2606", "SI2607", "SI2608", "SI2609"], # 工业硅 + # "SP": ["SP2603", "SP2604", "SP2605", "SP2606", "SP2607", "SP2608", "SP2609"] # 多晶硅 + # } } return symbols_by_exchange diff --git a/backend/service_implementation/service/data/futures_analysis.db b/backend/service_implementation/service/data/futures_analysis.db index 0211f04..31a3045 100644 Binary files a/backend/service_implementation/service/data/futures_analysis.db and b/backend/service_implementation/service/data/futures_analysis.db differ diff --git a/backend/src/api/config.ts b/backend/src/api/config.ts index 5c929be..0fafa47 100644 --- a/backend/src/api/config.ts +++ b/backend/src/api/config.ts @@ -1,5 +1,6 @@ import express from 'express'; import { fetchDataSources, saveDataSource, fetchAIModels, saveAIModel, fetchSystemSettings, saveSystemSettings } from '../services/configService'; +import { logger } from '../utils/logger'; const router = express.Router(); @@ -99,14 +100,14 @@ router.post('/user', async (req, res) => { router.post('/test-database', async (req, res) => { try { const { dbType, config } = req.body; - console.log(`测试${dbType}连接`, config); + logger.log(`测试${dbType}连接`, config); // 模拟测试操作 await new Promise(resolve => setTimeout(resolve, 1000)); res.status(200).json({ success: true, message: `${dbType}连接测试成功` }); } catch (error) { - console.error('测试数据库连接失败:', error); + logger.error('测试数据库连接失败:', error); res.status(500).json({ success: false, message: '测试数据库连接失败' }); } }); @@ -115,14 +116,14 @@ router.post('/test-database', async (req, res) => { router.post('/test-datasource', async (req, res) => { try { const { dsType, config } = req.body; - console.log(`测试${dsType}数据源连接`, config); + logger.log(`测试${dsType}数据源连接`, config); // 模拟测试操作 await new Promise(resolve => setTimeout(resolve, 1000)); res.status(200).json({ success: true, message: `${dsType}数据源连接测试成功` }); } catch (error) { - console.error('测试数据源连接失败:', error); + logger.error('测试数据源连接失败:', error); res.status(500).json({ success: false, message: '测试数据源连接失败' }); } }); @@ -131,7 +132,7 @@ router.post('/test-datasource', async (req, res) => { router.post('/save', async (req, res) => { try { const config = req.body; - console.log('保存配置:', config); + logger.log('保存配置:', config); // 保存配置到文件 const fs = require('fs'); @@ -142,7 +143,7 @@ router.post('/save', async (req, res) => { res.status(200).json({ success: true, message: '配置保存成功' }); } catch (error) { - console.error('保存配置失败:', error); + logger.error('保存配置失败:', error); res.status(500).json({ success: false, message: '保存配置失败' }); } }); @@ -163,7 +164,7 @@ router.get('/get', async (req, res) => { res.status(200).json({ success: true, data: config }); } catch (error) { - console.error('获取配置失败:', error); + logger.error('获取配置失败:', error); res.status(500).json({ success: false, message: '获取配置失败' }); } }); diff --git a/backend/src/api/market.ts b/backend/src/api/market.ts index bf407cc..56d3de4 100644 --- a/backend/src/api/market.ts +++ b/backend/src/api/market.ts @@ -1,14 +1,18 @@ import express from 'express'; import { fetchMarketOverview, fetchMarketDetail, fetchKlineData, fetchMarketHotspots, fetchRiskAlerts } from '../services/marketService'; +import { logger } from '../utils/logger'; const router = express.Router(); // 获取市场概览 router.get('/overview', async (req, res) => { try { + logger.info('start 获取市场概览'); const data = await fetchMarketOverview(); + logger.info('end 获取市场概览'); res.status(200).json({ success: true, data }); } catch (error) { + logger.error('获取市场概览失败:', error); res.status(500).json({ success: false, message: '获取市场概览失败' }); } }); @@ -16,10 +20,13 @@ router.get('/overview', async (req, res) => { // 获取品种详情 router.get('/detail/:symbol', async (req, res) => { try { + logger.info(`start 获取合约${req.params.symbol}详情`); const { symbol } = req.params; const data = await fetchMarketDetail(symbol); + logger.info(`end 获取合约${symbol}详情`); res.status(200).json({ success: true, data }); } catch (error) { + logger.error(`获取合约${req.params.symbol}详情失败:`, error); res.status(500).json({ success: false, message: '获取品种详情失败' }); } }); @@ -27,11 +34,14 @@ router.get('/detail/:symbol', async (req, res) => { // 获取K线数据 router.get('/klines/:symbol', async (req, res) => { try { + logger.info(`start 获取合约${req.params.symbol}K线数据`); const { symbol } = req.params; const { period = '1H' } = req.query; const data = await fetchKlineData(symbol, period as string); + logger.info(`end 获取合约${symbol}K线数据`); res.status(200).json({ success: true, data }); } catch (error) { + logger.error(`获取合约${req.params.symbol}K线数据失败:`, error); res.status(500).json({ success: false, message: '获取K线数据失败' }); } }); @@ -39,9 +49,12 @@ router.get('/klines/:symbol', async (req, res) => { // 获取市场热点 router.get('/hotspots', async (req, res) => { try { + logger.info('start 获取市场热点'); const data = await fetchMarketHotspots(); + logger.info('end 获取市场热点'); res.status(200).json({ success: true, data }); } catch (error) { + logger.error('获取市场热点失败:', error); res.status(500).json({ success: false, message: '获取市场热点失败' }); } }); @@ -49,9 +62,12 @@ router.get('/hotspots', async (req, res) => { // 获取风险预警 router.get('/alerts', async (req, res) => { try { + logger.info('start 获取风险预警'); const data = await fetchRiskAlerts(); + logger.info('end 获取风险预警'); res.status(200).json({ success: true, data }); } catch (error) { + logger.error('获取风险预警失败:', error); res.status(500).json({ success: false, message: '获取风险预警失败' }); } }); diff --git a/backend/src/app.ts b/backend/src/app.ts index 0bc2b36..3d15bef 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -10,6 +10,7 @@ import riskRoutes from './api/risk'; import configRoutes from './api/config'; import watchlistRoutes from './api/watchlist'; import pushRoutes from './api/push'; +import { logger } from './utils/logger'; const app = express(); @@ -50,7 +51,7 @@ app.use((req, res) => { // 错误处理 app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => { - console.error(err.stack); + logger.error(err.stack); res.status(500).json({ success: false, message: '服务器内部错误' }); }); @@ -62,15 +63,15 @@ async function startServer() { try { // 启动Express服务器 const server = app.listen(port, () => { - console.log(`服务器运行在 http://localhost:${port}`); + logger.log(`服务器运行在 http://localhost:${port}`); }); // 处理服务器关闭事件 function handleShutdown() { - console.log('正在关闭服务器...'); + logger.log('正在关闭服务器...'); // 关闭Express服务器 server.close(() => { - console.log('服务器已关闭'); + logger.log('服务器已关闭'); process.exit(0); }); } @@ -80,7 +81,7 @@ async function startServer() { process.on('SIGTERM', handleShutdown); process.on('exit', handleShutdown); } catch (error) { - console.error('启动服务器时出错:', error); + logger.error('启动服务器时出错:', error); } } diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 2e508eb..f88bbcc 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,6 +1,7 @@ import dotenv from 'dotenv'; import fs from 'fs'; import path from 'path'; +import { logger } from '../utils/logger'; dotenv.config(); @@ -40,11 +41,11 @@ try { if (fs.existsSync(configPath)) { const configData = fs.readFileSync(configPath, 'utf8'); fileConfig = JSON.parse(configData); - console.log('已读取配置文件:', configPath); - console.log('配置文件内容:', fileConfig); + logger.log('已读取配置文件:', configPath); + logger.log('配置文件内容:', fileConfig); } } catch (error) { - console.error('读取配置文件失败:', error); + logger.error('读取配置文件失败:', error); } export const config = { @@ -109,13 +110,13 @@ export const config = { }, defaultDataSource: 'tqsdk' }; - console.log('数据源配置初始化完成:', { - hasFileConfig: !!fileConfig.dataSource, - enabledDataSources: Object.keys(dataSourceConfig).filter(key => - key !== 'defaultDataSource' && dataSourceConfig[key].enabled - ), - defaultDataSource: dataSourceConfig.defaultDataSource - }); + logger.log('数据源配置初始化完成:', { + hasFileConfig: !!fileConfig.dataSource, + enabledDataSources: Object.keys(dataSourceConfig).filter(key => + key !== 'defaultDataSource' && dataSourceConfig[key].enabled + ), + defaultDataSource: dataSourceConfig.defaultDataSource + }); return dataSourceConfig; })() }; \ No newline at end of file diff --git a/backend/src/services/ServiceImplementationClient.ts b/backend/src/services/ServiceImplementationClient.ts index 6786aef..e40c8fa 100644 --- a/backend/src/services/ServiceImplementationClient.ts +++ b/backend/src/services/ServiceImplementationClient.ts @@ -1,6 +1,8 @@ // Service Implementation API Client // This client interacts with the service_implementation API documented in api_documentation.md +import { logger } from '../utils/logger'; + class ServiceImplementationClient { private baseUrl: string; @@ -31,7 +33,7 @@ class ServiceImplementationClient { const data = await response.json(); return data as T; } catch (error: any) { - console.error('GET request failed:', error.message || error); + logger.error('GET request failed:', error.message || error); throw new Error(`Network request failed: ${error.message || error}`); } } @@ -56,7 +58,7 @@ class ServiceImplementationClient { const responseData = await response.json(); return responseData as T; } catch (error: any) { - console.error('POST request failed:', error.message || error); + logger.error('POST request failed:', error.message || error); throw new Error(`Network request failed: ${error.message || error}`); } } diff --git a/backend/src/services/configService.ts b/backend/src/services/configService.ts index 7a490cc..2cf6aec 100644 --- a/backend/src/services/configService.ts +++ b/backend/src/services/configService.ts @@ -1,5 +1,7 @@ // 配置服务 +import { logger } from '../utils/logger'; + // 获取数据源列表 export const fetchDataSources = async () => { // 模拟API请求延迟 @@ -19,7 +21,7 @@ export const fetchDataSources = async () => { export const saveDataSource = async (data: any) => { // 模拟API请求延迟 await new Promise(resolve => setTimeout(resolve, 300)); - console.log('保存数据源配置:', data); + logger.log('保存数据源配置:', data); return data; }; @@ -40,7 +42,7 @@ export const fetchAIModels = async () => { export const saveAIModel = async (data: any) => { // 模拟API请求延迟 await new Promise(resolve => setTimeout(resolve, 300)); - console.log('保存AI模型配置:', data); + logger.log('保存AI模型配置:', data); return data; }; @@ -60,6 +62,6 @@ export const fetchSystemSettings = async () => { export const saveSystemSettings = async (data: any) => { // 模拟API请求延迟 await new Promise(resolve => setTimeout(resolve, 200)); - console.log('保存系统设置:', data); + logger.log('保存系统设置:', data); return data; }; \ No newline at end of file diff --git a/backend/src/services/datasource/DataSourceFactory.ts b/backend/src/services/datasource/DataSourceFactory.ts index ce98356..218953e 100644 --- a/backend/src/services/datasource/DataSourceFactory.ts +++ b/backend/src/services/datasource/DataSourceFactory.ts @@ -1,6 +1,7 @@ // 数据源工厂类 import { DataSource } from './DataSource'; import { TQDataSource } from './TQDataSource'; +import { logger } from '../../utils/logger'; // 数据源类型 export enum DataSourceType { @@ -16,7 +17,7 @@ export class DataSourceFactory { // 获取数据源实例 static async getDataSource(type: DataSourceType = DataSourceType.TQSDK, config: any = {}): Promise { - console.log('获取数据源实例:', type); + logger.log('获取数据源实例:', type); if (!this.dataSources.has(type)) { let dataSource: DataSource; @@ -51,9 +52,9 @@ export class DataSourceFactory { for (const [type, dataSource] of this.dataSources) { try { await dataSource.close(); - console.log(`数据源${type}已关闭`); + logger.log(`数据源${type}已关闭`); } catch (error) { - console.error(`关闭数据源${type}失败:`, error); + logger.error(`关闭数据源${type}失败:`, error); } } this.dataSources.clear(); diff --git a/backend/src/services/datasource/MockDataSource.ts b/backend/src/services/datasource/MockDataSource.ts index de3fd6e..2d6b5eb 100644 --- a/backend/src/services/datasource/MockDataSource.ts +++ b/backend/src/services/datasource/MockDataSource.ts @@ -1,6 +1,7 @@ // 模拟数据源实现 import { DataSource } from './DataSource'; import { futuresList, generateFuturesOverview, generateFutureData, generateKlineData, riskAlerts } from '../../utils/mockData'; +import { logger } from '../../utils/logger'; export class MockDataSource implements DataSource { private initialized: boolean = false; @@ -8,7 +9,7 @@ export class MockDataSource implements DataSource { async initialize(): Promise { // 模拟初始化 this.initialized = true; - console.log('模拟数据源初始化成功'); + logger.log('模拟数据源初始化成功'); return true; } @@ -157,7 +158,7 @@ export class MockDataSource implements DataSource { async close(): Promise { // 模拟关闭 this.initialized = false; - console.log('模拟数据源已关闭'); + logger.log('模拟数据源已关闭'); } // 根据合约代码获取交易所 diff --git a/backend/src/services/datasource/PythonServiceManager.ts b/backend/src/services/datasource/PythonServiceManager.ts index f50ffc4..751f186 100644 --- a/backend/src/services/datasource/PythonServiceManager.ts +++ b/backend/src/services/datasource/PythonServiceManager.ts @@ -1,6 +1,7 @@ import { spawn } from 'child_process'; import * as path from 'path'; import { config } from '../../config'; +import { logger } from '../../utils/logger'; class PythonServiceManager { private pythonProcess: any = null; @@ -8,7 +9,7 @@ class PythonServiceManager { async start(): Promise { if (this.isRunning) { - console.log('Python服务已经在运行'); + logger.log('Python服务已经在运行'); return true; } @@ -19,7 +20,7 @@ class PythonServiceManager { // 从配置中读取端口,默认3007 const port = config.dataSource?.tqsdk?.pythonPort || 3007; - console.log(`启动Python TQAPI服务,端口: ${port}...`); + logger.log(`启动Python TQAPI服务,端口: ${port}...`); // 启动Python服务,传递端口参数 this.pythonProcess = spawn('python', [pythonServicePath, '--port', port.toString()], { @@ -29,12 +30,12 @@ class PythonServiceManager { }); this.pythonProcess.on('error', (error: any) => { - console.error('启动Python服务失败:', error); + logger.error('启动Python服务失败:', error); this.isRunning = false; }); this.pythonProcess.on('exit', (code: number, signal: string) => { - console.log(`Python服务退出,代码: ${code}, 信号: ${signal}`); + logger.log(`Python服务退出,代码: ${code}, 信号: ${signal}`); this.isRunning = false; }); @@ -42,10 +43,10 @@ class PythonServiceManager { await new Promise(resolve => setTimeout(resolve, 2000)); this.isRunning = true; - console.log('Python TQAPI服务启动成功'); + logger.log('Python TQAPI服务启动成功'); return true; } catch (error) { - console.error('启动Python服务时出错:', error); + logger.error('启动Python服务时出错:', error); this.isRunning = false; return false; } @@ -54,23 +55,23 @@ class PythonServiceManager { stop(): void { if (this.pythonProcess) { try { - console.log('停止Python TQAPI服务...'); + logger.log('停止Python TQAPI服务...'); // 发送终止信号 this.pythonProcess.kill(); // 等待进程退出 this.pythonProcess.on('exit', (code: number, signal: string) => { - console.log(`Python服务退出,代码: ${code}, 信号: ${signal}`); + logger.log(`Python服务退出,代码: ${code}, 信号: ${signal}`); }); this.pythonProcess = null; this.isRunning = false; - console.log('Python TQAPI服务已停止'); + logger.log('Python TQAPI服务已停止'); } catch (error) { - console.error('停止Python服务时出错:', error); + logger.error('停止Python服务时出错:', error); this.pythonProcess = null; this.isRunning = false; } } else { - console.log('Python服务未运行,无需停止'); + logger.log('Python服务未运行,无需停止'); } } diff --git a/backend/src/services/datasource/TQDataSource.ts b/backend/src/services/datasource/TQDataSource.ts index cb981a1..b89a22f 100644 --- a/backend/src/services/datasource/TQDataSource.ts +++ b/backend/src/services/datasource/TQDataSource.ts @@ -1,5 +1,6 @@ // TQAPI数据源实现 - 使用Python服务 import { DataSource } from './DataSource'; +import { logger } from '../../utils/logger'; // 简化的HTTP客户端 class HttpClient { @@ -16,18 +17,18 @@ class HttpClient { url += `?${queryString}`; } - console.log('发送GET请求:', url); + logger.log('发送GET请求:', url); try { // 设置20秒超时 const controller = new AbortController(); const timeoutId = setTimeout(() => { - console.error('GET请求超时,正在中止请求...'); + logger.error('GET请求超时,正在中止请求...'); controller.abort(); }, 20000); try { - console.log('正在发送GET请求...'); + logger.log('正在发送GET请求...'); const start = Date.now(); const response = await fetch(url, { method: 'GET', @@ -37,44 +38,44 @@ class HttpClient { signal: controller.signal }); const end = Date.now(); - console.log(`GET请求完成,耗时: ${end - start}ms`); - console.log('GET请求响应状态:', response.status); + logger.log(`GET请求完成,耗时: ${end - start}ms`); + logger.log('GET请求响应状态:', response.status); if (!response.ok) { const errorText = await response.text(); - console.error('GET请求失败:', errorText); + logger.error('GET请求失败:', errorText); throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`); } - console.log('正在解析GET响应数据...'); + logger.log('正在解析GET响应数据...'); const data = await response.json(); - console.log('GET请求响应数据:', data); + logger.log('GET请求响应数据:', data); return data as T; } finally { clearTimeout(timeoutId); } } catch (error: any) { - console.error('GET请求网络错误:', error.message || error); - console.error('错误详情:', error); - console.error('错误堆栈:', error.stack); + logger.error('GET请求网络错误:', error.message || error); + logger.error('错误详情:', error); + logger.error('错误堆栈:', error.stack); throw new Error(`网络请求失败: ${error.message || error}`); } } async post(endpoint: string, data?: any): Promise { const url = `${this.baseUrl}${endpoint}`; - console.log('发送POST请求:', url, '数据:', data); + logger.log('发送POST请求:', url, '数据:', data); try { // 设置20秒超时 const controller = new AbortController(); const timeoutId = setTimeout(() => { - console.error('POST请求超时,正在中止请求...'); + logger.error('POST请求超时,正在中止请求...'); controller.abort(); }, 20000); try { - console.log('正在发送请求...'); + logger.log('正在发送请求...'); const start = Date.now(); const response = await fetch(url, { method: 'POST', @@ -85,26 +86,26 @@ class HttpClient { signal: controller.signal }); const end = Date.now(); - console.log(`请求完成,耗时: ${end - start}ms`); - console.log('POST请求响应状态:', response.status); + logger.log(`请求完成,耗时: ${end - start}ms`); + logger.log('POST请求响应状态:', response.status); if (!response.ok) { const errorText = await response.text(); - console.error('POST请求失败:', errorText); + logger.error('POST请求失败:', errorText); throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`); } - console.log('正在解析响应数据...'); + logger.log('正在解析响应数据...'); const responseData = await response.json(); - console.log('POST请求响应数据:', responseData); + logger.log('POST请求响应数据:', responseData); return responseData as T; } finally { clearTimeout(timeoutId); } } catch (error: any) { - console.error('POST请求网络错误:', error.message || error); - console.error('错误详情:', error); - console.error('错误堆栈:', error.stack); + logger.error('POST请求网络错误:', error.message || error); + logger.error('错误详情:', error); + logger.error('错误堆栈:', error.stack); throw new Error(`网络请求失败: ${error.message || error}`); } } @@ -123,7 +124,7 @@ export class TQDataSource implements DataSource { }; constructor(config: any = {}) { - console.log('使用TQAPI数据源初始化...'); + logger.log('使用TQAPI数据源初始化...'); // 从配置中读取端口,默认8001 const port = config.pythonPort || 8001; this.config = { @@ -134,26 +135,26 @@ export class TQDataSource implements DataSource { maxConnections: config.maxConnections || 5, pythonServiceUrl: config.pythonServiceUrl || `http://127.0.0.1:${port}/api` }; - console.log('TQAPI数据源配置:', this.config); + logger.log('TQAPI数据源配置:', this.config); // 测试Python服务URL是否正确 - console.log('测试Python服务URL:', this.config.pythonServiceUrl); + logger.log('测试Python服务URL:', this.config.pythonServiceUrl); this.httpClient = new HttpClient(this.config.pythonServiceUrl!); } async initialize(): Promise { try { - console.log('开始初始化TQAPI数据源...'); - console.log('Python服务URL:', this.config.pythonServiceUrl); - console.log('连接参数:', { + logger.log('开始初始化TQAPI数据源...'); + logger.log('Python服务URL:', this.config.pythonServiceUrl); + logger.log('连接参数:', { username: this.config.username ? '***' : '', password: this.config.password ? '***' : '' }); // 先测试Python服务是否可达 - console.log('测试Python服务是否可达...'); + logger.log('测试Python服务是否可达...'); try { const testUrl = `${this.config.pythonServiceUrl}/../health`; - console.log('测试URL:', testUrl); + logger.log('测试URL:', testUrl); // 使用AbortController设置超时 const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 10000); @@ -162,21 +163,21 @@ export class TQDataSource implements DataSource { method: 'GET', signal: controller.signal }); - console.log('Python服务健康检查响应:', testResponse.status); + logger.log('Python服务健康检查响应:', testResponse.status); if (testResponse.ok) { - console.log('Python服务可达'); + logger.log('Python服务可达'); } else { - console.error('Python服务不可达,状态码:', testResponse.status); + logger.error('Python服务不可达,状态码:', testResponse.status); } } finally { clearTimeout(timeoutId); } } catch (error: any) { - console.error('Python服务健康检查失败:', error.message || error); + logger.error('Python服务健康检查失败:', error.message || error); } // 连接到天勤服务器 - console.log('连接到天勤服务器...'); + logger.log('连接到天勤服务器...'); try { const response = await this.httpClient.post('/connect', { @@ -184,24 +185,24 @@ export class TQDataSource implements DataSource { password: this.config.password }); - console.log('连接响应:', response); + logger.log('连接响应:', response); if (response.success) { - console.log('TQAPI连接成功'); + logger.log('TQAPI连接成功'); this.initialized = true; return true; } else { - console.error('TQAPI连接失败:', response.message); + logger.error('TQAPI连接失败:', response.message); this.initialized = false; return false; } } catch (error: any) { - console.error('连接请求失败:', error.message || error); - console.error('错误详情:', error); + logger.error('连接请求失败:', error.message || error); + logger.error('错误详情:', error); throw error; } } catch (error) { - console.error('TQAPI数据源初始化失败:', error); + logger.error('TQAPI数据源初始化失败:', error); this.initialized = false; return false; } @@ -213,18 +214,18 @@ export class TQDataSource implements DataSource { } try { - console.log('获取合约列表...'); + logger.log('获取合约列表...'); const response = await this.httpClient.get('/contracts'); if (response.success && Array.isArray(response.data)) { - console.log('获取合约列表成功,数量:', response.data.length); + logger.log('获取合约列表成功,数量:', response.data.length); return response.data; } else { - console.error('获取合约列表失败:', response.message); + logger.error('获取合约列表失败:', response.message); return []; } } catch (error) { - console.error('获取合约列表失败:', error); + logger.error('获取合约列表失败:', error); throw error; } } @@ -235,18 +236,18 @@ export class TQDataSource implements DataSource { } try { - console.log(`获取合约${symbol}详情...`); + logger.log(`获取合约${symbol}详情...`); const response = await this.httpClient.get(`/contract/${symbol}`); if (response.success) { - console.log(`获取合约${symbol}详情成功`); + logger.log(`获取合约${symbol}详情成功`); return response.data; } else { - console.error(`获取合约${symbol}详情失败:`, response.message); + logger.error(`获取合约${symbol}详情失败:`, response.message); throw new Error(`获取合约${symbol}详情失败`); } } catch (error) { - console.error(`获取合约${symbol}详情失败:`, error); + logger.error(`获取合约${symbol}详情失败:`, error); throw error; } } @@ -257,7 +258,7 @@ export class TQDataSource implements DataSource { } try { - console.log('开始获取K线数据:', { + logger.log('开始获取K线数据:', { symbol: symbol, period: period, count: count @@ -267,10 +268,10 @@ export class TQDataSource implements DataSource { let contractSymbol = symbol; if (!contractSymbol.includes('.')) { // 如果没有交易所前缀,尝试添加默认交易所 - console.warn('合约代码缺少交易所前缀,尝试添加默认交易所'); + logger.warn('合约代码缺少交易所前缀,尝试添加默认交易所'); contractSymbol = `SHFE.${contractSymbol}`; } - console.log('使用的合约代码:', contractSymbol); + logger.log('使用的合约代码:', contractSymbol); const response = await this.httpClient.get('/klines/' + contractSymbol, { period: period, @@ -278,14 +279,14 @@ export class TQDataSource implements DataSource { }); if (response.success && Array.isArray(response.data)) { - console.log('获取K线数据成功,长度:', response.data.length); + logger.log('获取K线数据成功,长度:', response.data.length); return response.data; } else { - console.error('获取K线数据失败:', response.message); + logger.error('获取K线数据失败:', response.message); return []; } } catch (error) { - console.error(`获取合约${symbol}K线数据失败:`, error); + logger.error(`获取合约${symbol}K线数据失败:`, error); throw error; } } @@ -296,33 +297,33 @@ export class TQDataSource implements DataSource { } try { - console.log(`获取合约${symbol}实时行情数据...`); + logger.log(`获取合约${symbol}实时行情数据...`); // 确保合约代码格式正确 let contractSymbol = symbol; if (!contractSymbol.includes('.')) { // 如果没有交易所前缀,尝试添加默认交易所 - console.warn('合约代码缺少交易所前缀,尝试添加默认交易所'); + logger.warn('合约代码缺少交易所前缀,尝试添加默认交易所'); contractSymbol = `SHFE.${contractSymbol}`; } - console.log('使用的合约代码:', contractSymbol); + logger.log('使用的合约代码:', contractSymbol); - console.log('正在发送GET请求到:', `/tick/${contractSymbol}`); + logger.log('正在发送GET请求到:', `/tick/${contractSymbol}`); const start = Date.now(); const response = await this.httpClient.get(`/tick/${contractSymbol}`); const end = Date.now(); - console.log(`GET请求完成,耗时: ${end - start}ms`); - console.log('响应数据:', response); + logger.log(`GET请求完成,耗时: ${end - start}ms`); + logger.log('响应数据:', response); if (response.success) { - console.log(`获取合约${symbol}实时行情数据成功`); + logger.log(`获取合约${symbol}实时行情数据成功`); // 计算价格变化 const tickData = response.data; tickData.price_change = tickData.last_price - (tickData.pre_close || 0); - console.log('计算价格变化后的数据:', tickData); + logger.log('计算价格变化后的数据:', tickData); return tickData; } else { - console.error(`获取合约${symbol}实时行情数据失败:`, response.message); + logger.error(`获取合约${symbol}实时行情数据失败:`, response.message); // 返回默认数据,避免整个请求失败 return { last_price: 0, @@ -341,7 +342,7 @@ export class TQDataSource implements DataSource { }; } } catch (error) { - console.error(`获取合约${symbol}实时行情数据失败:`, error); + logger.error(`获取合约${symbol}实时行情数据失败:`, error); // 返回默认数据,避免整个请求失败 return { last_price: 0, @@ -393,12 +394,12 @@ export class TQDataSource implements DataSource { open_interest: open_interest }); } catch (error) { - console.error(`获取合约${contract.symbol}行情失败:`, error); + logger.error(`获取合约${contract.symbol}行情失败:`, error); } } return overview; } catch (error) { - console.error('获取市场概览失败:', error); + logger.error('获取市场概览失败:', error); throw error; } } @@ -410,29 +411,29 @@ export class TQDataSource implements DataSource { try { // 目前Python服务未实现此功能 - console.warn('TQAPI服务暂未实现历史成交数据获取功能'); + logger.warn('TQAPI服务暂未实现历史成交数据获取功能'); return []; } catch (error) { - console.error(`获取合约${symbol}历史成交数据失败:`, error); + logger.error(`获取合约${symbol}历史成交数据失败:`, error); throw error; } } async close(): Promise { try { - console.log('关闭TQAPI连接...'); + logger.log('关闭TQAPI连接...'); const response = await this.httpClient.post('/disconnect'); if (response.success) { - console.log('TQAPI连接已关闭'); + logger.log('TQAPI连接已关闭'); } else { - console.error('关闭TQAPI连接失败:', response.message); + logger.error('关闭TQAPI连接失败:', response.message); } } catch (error) { - console.error('关闭TQAPI连接失败:', error); + logger.error('关闭TQAPI连接失败:', error); } finally { this.initialized = false; - console.log('TQAPI数据源已关闭'); + logger.log('TQAPI数据源已关闭'); } } } \ No newline at end of file diff --git a/backend/src/services/marketService.ts b/backend/src/services/marketService.ts index 5d24b3e..2997097 100644 --- a/backend/src/services/marketService.ts +++ b/backend/src/services/marketService.ts @@ -3,6 +3,7 @@ import { DataSourceFactory, DataSourceType } from './datasource/DataSourceFactor import { futuresList, generateFuturesOverview, generateFutureData, generateKlineData, riskAlerts } from '../utils/mockData'; import { config } from '../config'; import { serviceImplementationClient } from './ServiceImplementationClient'; +import { logger } from '../utils/logger'; // 获取数据源配置 const getDataSourceConfig = () => { @@ -15,10 +16,10 @@ export const fetchMarketOverview = async () => { try { // 首先尝试使用 service_implementation API try { - console.log('尝试使用 service_implementation API 获取市场概览...'); + logger.log('尝试使用 service_implementation API 获取市场概览...'); // 先获取合约列表 - console.log('获取合约列表...'); + logger.log('获取合约列表...'); const contractsResponse = await serviceImplementationClient.getContracts(); const allContracts = contractsResponse.data; @@ -38,7 +39,7 @@ export const fetchMarketOverview = async () => { // 转换为数组 const contractList = Array.from(uniqueContracts.values()); - console.log(`获取到 ${contractList.length} 个独特品种`); + logger.log(`获取到 ${contractList.length} 个独特品种`); // 使用获取到的合约列表 const overview = []; @@ -48,19 +49,24 @@ export const fetchMarketOverview = async () => { const symbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}05`; // 获取合约详情 - console.log(`获取合约${symbol}详情...`); + logger.log(`获取合约${symbol}详情...`); const contractsResponse = await serviceImplementationClient.getContracts(future.exchange, future.code); const contract = contractsResponse.data.find((c: any) => c.symbol === symbol); if (!contract) { - console.warn(`合约${symbol}不存在,跳过`); + logger.warn(`合约${symbol}不存在,跳过`); continue; } + // 更新未来合约的中文名称 + future.name = `${contract.product_name || future.name}`; + // 获取分析数据 - console.log(`分析合约${symbol}...`); + logger.log(`分析合约${symbol}...`); + logger.log(`before analyzeMarket: symbol=${symbol},time=${new Date().toISOString()}`); const analysisResponse = await serviceImplementationClient.analyzeMarket(symbol); const analysis = analysisResponse.data; + logger.log(`after analyzeMarket: symbol=${symbol}, analysis=${JSON.stringify(analysis)}, time=${new Date().toISOString()}`); overview.push({ code: future.code, @@ -108,26 +114,26 @@ export const fetchMarketOverview = async () => { aiAnalysis: `趋势:${analysis.trend || '中性'} | 概率:${analysis.probability ? Math.round(analysis.probability * 100) : 50}% | 方向:${analysis.direction || '观望'}` }); } catch (error) { - console.error(`获取合约${future.code}行情失败:`, error); + logger.error(`获取合约${future.code}行情失败:`, error); // 跳过获取失败的合约 continue; } } if (overview.length > 0) { - console.log('使用 service_implementation API 获取市场概览成功'); + logger.log('使用 service_implementation API 获取市场概览成功'); return overview; } - console.warn('service_implementation API 未返回数据,尝试使用其他数据源'); + logger.warn('service_implementation API 未返回数据,尝试使用其他数据源'); } catch (error) { - console.error('service_implementation API 获取失败:', error); + logger.error('service_implementation API 获取失败:', error); // service_implementation API 失败,尝试使用其他数据源 } // 获取数据源配置 const dataSourceConfig = getDataSourceConfig(); - console.log('获取数据源配置:', dataSourceConfig); + logger.log('获取数据源配置:', dataSourceConfig); // 检查是否有可用的数据源 const hasAvailableDataSource = dataSourceConfig.tqsdk?.enabled || dataSourceConfig.test?.enabled; if (!hasAvailableDataSource) { @@ -194,7 +200,7 @@ export const fetchMarketOverview = async () => { aiAnalysis: `MACD:金叉向上 | RSI:${Math.floor(Math.random() * 80) + 10}(中性) | 布林带:中轨附近` }); } catch (error) { - console.error(`获取合约${future.code}行情失败:`, error); + logger.error(`获取合约${future.code}行情失败:`, error); // 跳过获取失败的合约 continue; } @@ -202,10 +208,10 @@ export const fetchMarketOverview = async () => { return overview; } catch (error) { - console.error('TQSDK数据源获取失败:', error); + logger.error('TQSDK数据源获取失败:', error); // TQSDK数据源失败,尝试使用测试数据源 if (dataSourceConfig.test?.enabled) { - console.log('切换到测试数据源'); + logger.log('切换到测试数据源'); // 启用了测试数据源,使用测试数据 await new Promise(resolve => setTimeout(resolve, 300)); return generateFuturesOverview(); @@ -216,7 +222,7 @@ export const fetchMarketOverview = async () => { } } else if (dataSourceConfig.test?.enabled) { // 直接使用测试数据源 - console.log('使用测试数据源'); + logger.log('使用测试数据源'); await new Promise(resolve => setTimeout(resolve, 300)); return generateFuturesOverview(); } else { @@ -224,7 +230,7 @@ export const fetchMarketOverview = async () => { throw new Error('无可用数据源,请在管理配置中启用至少一个数据源'); } } catch (error) { - console.error('获取市场概览失败:', error); + logger.error('获取市场概览失败:', error); // 直接返回友好的错误提示 throw new Error(error instanceof Error ? error.message : '获取市场概览失败,请检查数据源配置'); } @@ -243,26 +249,31 @@ export const fetchMarketDetail = async (symbol: string) => { // 首先尝试使用 service_implementation API try { - console.log('尝试使用 service_implementation API 获取品种详情...'); + logger.log('尝试使用 service_implementation API 获取品种详情...'); // 构建合约符号(使用大写代码,因为 service_implementation API 期望大写) const contractSymbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}05`; // 获取合约详情 - console.log(`获取合约${contractSymbol}详情...`); + logger.log(`获取合约${contractSymbol}详情...`); const contractsResponse = await serviceImplementationClient.getContracts(future.exchange, future.code); const contract = contractsResponse.data.find((c: any) => c.symbol === contractSymbol); if (!contract) { - console.warn(`合约${contractSymbol}不存在,尝试使用其他数据源`); + logger.warn(`合约${contractSymbol}不存在,尝试使用其他数据源`); } else { + // 更新未来合约的中文名称 + future.name = `${contract.product_name || future.name} - ${future.code}`; + // 获取分析数据 - console.log(`分析合约${contractSymbol}...`); + logger.log(`分析合约${contractSymbol}...`); + logger.log(`before analyzeMarket: symbol=${contractSymbol},time=${new Date().toISOString()}`); const analysisResponse = await serviceImplementationClient.analyzeMarket(contractSymbol); const analysis = analysisResponse.data; + logger.log(`after analyzeMarket: symbol=${contractSymbol},time=${new Date().toISOString()}`); // 获取交易建议 - console.log(`获取合约${contractSymbol}交易建议...`); + logger.log(`获取合约${contractSymbol}交易建议...`); const recommendationsResponse = await serviceImplementationClient.getRecommendations(contractSymbol); const recommendation = recommendationsResponse.data[0]; @@ -322,13 +333,13 @@ export const fetchMarketDetail = async (symbol: string) => { aiAnalysis: `趋势:${analysis.trend || '中性'} | 概率:${analysis.probability ? Math.round(analysis.probability * 100) : 50}% | 方向:${analysis.direction || '观望'}` }; - console.log('使用 service_implementation API 获取品种详情成功'); + logger.log('使用 service_implementation API 获取品种详情成功'); return result; } - console.warn('service_implementation API 未返回数据,尝试使用其他数据源'); + logger.warn('service_implementation API 未返回数据,尝试使用其他数据源'); } catch (error) { - console.error('service_implementation API 获取失败:', error); + logger.error('service_implementation API 获取失败:', error); // service_implementation API 失败,尝试使用其他数据源 } @@ -408,10 +419,10 @@ export const fetchMarketDetail = async (symbol: string) => { aiAnalysis: `MACD:金叉向上 | RSI:${Math.floor(Math.random() * 80) + 10}(中性) | 布林带:中轨附近` }; } catch (error) { - console.error('TQSDK数据源获取失败:', error); + logger.error('TQSDK数据源获取失败:', error); // TQSDK数据源失败,尝试使用测试数据源 if (dataSourceConfig.test?.enabled) { - console.log('切换到测试数据源'); + logger.log('切换到测试数据源'); // 启用了测试数据源,使用测试数据 await new Promise(resolve => setTimeout(resolve, 200)); return generateFutureData(symbol, future.name); @@ -422,7 +433,7 @@ export const fetchMarketDetail = async (symbol: string) => { } } else if (dataSourceConfig.test?.enabled) { // 直接使用测试数据源 - console.log('使用测试数据源'); + logger.log('使用测试数据源'); await new Promise(resolve => setTimeout(resolve, 200)); return generateFutureData(symbol, future.name); } else { @@ -430,7 +441,7 @@ export const fetchMarketDetail = async (symbol: string) => { throw new Error('无可用数据源,请在管理配置中启用至少一个数据源'); } } catch (error) { - console.error(`获取品种${symbol}详情失败:`, error); + logger.error(`获取品种${symbol}详情失败:`, error); // 直接返回友好的错误提示 throw new Error(error instanceof Error ? error.message : '获取品种详情失败,请检查数据源配置'); } @@ -447,7 +458,7 @@ export const fetchKlineData = async (symbol: string, period: string) => { // 首先尝试使用 service_implementation API try { - console.log('尝试使用 service_implementation API 获取K线数据...'); + logger.log('尝试使用 service_implementation API 获取K线数据...'); // 构建合约符号(使用大写代码,因为 service_implementation API 期望大写) const contractSymbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}05`; @@ -481,7 +492,7 @@ export const fetchKlineData = async (symbol: string, period: string) => { } // 获取K线数据 - console.log(`获取合约${contractSymbol}K线数据,周期: ${duration}...`); + logger.log(`获取合约${contractSymbol}K线数据,周期: ${duration}...`); const klineResponse = await serviceImplementationClient.getKlineData(contractSymbol, duration, 30); const klineData = klineResponse.data; @@ -496,13 +507,13 @@ export const fetchKlineData = async (symbol: string, period: string) => { volume: item.volume })); - console.log('使用 service_implementation API 获取K线数据成功'); + logger.log('使用 service_implementation API 获取K线数据成功'); return result; } - console.warn('service_implementation API 未返回K线数据,尝试使用其他数据源'); + logger.warn('service_implementation API 未返回K线数据,尝试使用其他数据源'); } catch (error) { - console.error('service_implementation API 获取K线数据失败:', error); + logger.error('service_implementation API 获取K线数据失败:', error); // service_implementation API 失败,尝试使用其他数据源 } @@ -536,10 +547,10 @@ export const fetchKlineData = async (symbol: string, period: string) => { volume: item.volume })); } catch (error) { - console.error('TQSDK数据源获取失败:', error); + logger.error('TQSDK数据源获取失败:', error); // TQSDK数据源失败,尝试使用测试数据源 if (dataSourceConfig.test?.enabled) { - console.log('切换到测试数据源'); + logger.log('切换到测试数据源'); // 启用了测试数据源,使用测试数据 await new Promise(resolve => setTimeout(resolve, 200)); return generateKlineData(30); @@ -550,7 +561,7 @@ export const fetchKlineData = async (symbol: string, period: string) => { } } else if (dataSourceConfig.test?.enabled) { // 直接使用测试数据源 - console.log('使用测试数据源'); + logger.log('使用测试数据源'); await new Promise(resolve => setTimeout(resolve, 200)); return generateKlineData(30); } else { @@ -558,7 +569,7 @@ export const fetchKlineData = async (symbol: string, period: string) => { throw new Error('无可用数据源,请在管理配置中启用至少一个数据源'); } } catch (error) { - console.error(`获取合约${symbol}K线数据失败:`, error); + logger.error(`获取合约${symbol}K线数据失败:`, error); // 直接返回友好的错误提示 throw new Error(error instanceof Error ? error.message : '获取K线数据失败,请检查数据源配置'); } @@ -569,10 +580,10 @@ export const fetchMarketHotspots = async () => { try { // 首先尝试使用 service_implementation API try { - console.log('尝试使用 service_implementation API 获取市场热点...'); + logger.log('尝试使用 service_implementation API 获取市场热点...'); // 先获取合约列表 - console.log('获取合约列表...'); + logger.log('获取合约列表...'); const contractsResponse = await serviceImplementationClient.getContracts(); const allContracts = contractsResponse.data; @@ -592,7 +603,7 @@ export const fetchMarketHotspots = async () => { // 转换为数组 const contractList = Array.from(uniqueContracts.values()); - console.log(`获取到 ${contractList.length} 个独特品种`); + logger.log(`获取到 ${contractList.length} 个独特品种`); // 使用获取到的合约列表 const hotspots = []; @@ -602,17 +613,20 @@ export const fetchMarketHotspots = async () => { const symbol = `${future.code}${new Date().getFullYear().toString().slice(-2)}05`; // 获取合约详情 - console.log(`获取合约${symbol}详情...`); + logger.log(`获取合约${symbol}详情...`); const contractsResponse = await serviceImplementationClient.getContracts(future.exchange, future.code); const contract = contractsResponse.data.find((c: any) => c.symbol === symbol); if (!contract) { - console.warn(`合约${symbol}不存在,跳过`); + logger.warn(`合约${symbol}不存在,跳过`); continue; } + // 更新未来合约的中文名称 + future.name = `${contract.product_name || future.name} - ${future.code}`; + // 获取分析数据 - console.log(`分析合约${symbol}...`); + logger.log(`分析合约${symbol}...`); const analysisResponse = await serviceImplementationClient.analyzeMarket(symbol); const analysis = analysisResponse.data; @@ -623,7 +637,7 @@ export const fetchMarketHotspots = async () => { volume: analysis.volume || Math.floor(Math.random() * 1000000) + 100000 }); } catch (error) { - console.error(`获取合约${future.code}行情失败:`, error); + logger.error(`获取合约${future.code}行情失败:`, error); // 跳过获取失败的合约 continue; } @@ -635,13 +649,13 @@ export const fetchMarketHotspots = async () => { .sort((a, b) => Math.abs(b.change) - Math.abs(a.change)) .slice(0, 10); - console.log('使用 service_implementation API 获取市场热点成功'); + logger.log('使用 service_implementation API 获取市场热点成功'); return sortedHotspots; } - console.warn('service_implementation API 未返回市场热点数据,尝试使用其他数据源'); + logger.warn('service_implementation API 未返回市场热点数据,尝试使用其他数据源'); } catch (error) { - console.error('service_implementation API 获取市场热点失败:', error); + logger.error('service_implementation API 获取市场热点失败:', error); // service_implementation API 失败,尝试使用其他数据源 } @@ -675,7 +689,7 @@ export const fetchMarketHotspots = async () => { volume: tick.volume }); } catch (error) { - console.error(`获取合约${future.code}行情失败:`, error); + logger.error(`获取合约${future.code}行情失败:`, error); // 跳过获取失败的合约 continue; } @@ -686,10 +700,10 @@ export const fetchMarketHotspots = async () => { .sort((a, b) => Math.abs(b.change) - Math.abs(a.change)) .slice(0, 10); } catch (error) { - console.error('TQSDK数据源获取失败:', error); + logger.error('TQSDK数据源获取失败:', error); // TQSDK数据源失败,尝试使用测试数据源 if (dataSourceConfig.test?.enabled) { - console.log('切换到测试数据源'); + logger.log('切换到测试数据源'); // 启用了测试数据源,使用测试数据 await new Promise(resolve => setTimeout(resolve, 200)); const overview = generateFuturesOverview(); @@ -710,7 +724,7 @@ export const fetchMarketHotspots = async () => { } } else if (dataSourceConfig.test?.enabled) { // 直接使用测试数据源 - console.log('使用测试数据源'); + logger.log('使用测试数据源'); await new Promise(resolve => setTimeout(resolve, 200)); const overview = generateFuturesOverview(); // 按涨跌幅排序,返回前10个 @@ -728,7 +742,7 @@ export const fetchMarketHotspots = async () => { throw new Error('无可用数据源,请在管理配置中启用至少一个数据源'); } } catch (error) { - console.error('获取市场热点失败:', error); + logger.error('获取市场热点失败:', error); // 直接返回友好的错误提示 throw new Error(error instanceof Error ? error.message : '获取市场热点失败,请检查数据源配置'); } diff --git a/backend/src/services/pushService.ts b/backend/src/services/pushService.ts index c25fa52..b29b7c4 100644 --- a/backend/src/services/pushService.ts +++ b/backend/src/services/pushService.ts @@ -1,5 +1,7 @@ // 推送服务 +import { logger } from '../utils/logger'; + // 模拟推送设置存储 const pushSettings: Record = { 'AU': { @@ -41,7 +43,7 @@ export const savePushSettings = async (symbol: string, settings: any) => { await new Promise(resolve => setTimeout(resolve, 300)); pushSettings[symbol] = settings; - console.log('保存推送设置:', symbol, settings); + logger.log('保存推送设置:', symbol, settings); return settings; }; @@ -51,7 +53,7 @@ export const testPush = async (method: string, content: string) => { // 模拟API请求延迟 await new Promise(resolve => setTimeout(resolve, 500)); - console.log('测试推送:', method, content); + logger.log('测试推送:', method, content); // 模拟推送成功 return { success: true, message: '测试推送成功' }; diff --git a/backend/src/utils/logger.ts b/backend/src/utils/logger.ts new file mode 100644 index 0000000..d2640fc --- /dev/null +++ b/backend/src/utils/logger.ts @@ -0,0 +1,150 @@ +// 日志工具类 + +/** + * 获取调用者信息 + * @returns 包含函数名和代码行的对象 + */ +function getCallerInfo() { + const error = new Error(); + const stack = error.stack || ''; + const stackLines = stack.split('\n'); + + // 跳过前两行(Error和getCallerInfo本身的调用) + let callerIndex = 2; + + // 找到第一个不是logger.ts的调用者 + while (callerIndex < stackLines.length) { + const line = stackLines[callerIndex].trim(); + if (!line.includes('logger.ts') && !line.includes('logger.js')) { + break; + } + callerIndex++; + } + + if (callerIndex < stackLines.length) { + const callerLine = stackLines[callerIndex]; + + // 提取文件路径和行号 + let filePath = 'unknown'; + let lineNumber = 0; + let functionName = 'anonymous'; + + // 1. 处理包含文件路径的情况 + if (callerLine.includes('(')) { + // 格式1: at functionName (filePath:line:column) + const match1 = callerLine.match(/at\s+(.*?)\s+\((.*?):(\d+):(\d+)\)/); + if (match1) { + functionName = match1[1] || 'anonymous'; + filePath = match1[2]; + lineNumber = parseInt(match1[3], 10); + } + } else { + // 格式2: at filePath:line:column + const match2 = callerLine.match(/at\s+(.*?):(\d+):(\d+)/); + if (match2) { + filePath = match2[1]; + lineNumber = parseInt(match2[2], 10); + } + } + + // 2. 提取文件名 + const fileName = filePath.split('\\').pop() || filePath.split('/').pop() || 'unknown'; + + // 3. 特殊处理async函数和箭头函数 + if (functionName === 'anonymous' && callerLine.includes('async')) { + functionName = 'async'; + } + + return { + functionName, + fileName, + lineNumber + }; + } + + return { + functionName: 'anonymous', + fileName: 'unknown', + lineNumber: 0 + }; +} + +/** + * 获取当前时间戳 + * @returns 格式化的时间戳字符串 + */ +function getTimestamp() { + const now = new Date(); + return now.toISOString(); +} + +/** + * 日志工具类 + */ +export const logger = { + /** + * 普通日志 + * @param message 日志消息 + * @param data 附加数据 + */ + log(message: string, data?: any) { + const callerInfo = getCallerInfo(); + const timestamp = getTimestamp(); + const logMessage = `[${timestamp}] [${callerInfo.fileName}:${callerInfo.functionName}:${callerInfo.lineNumber}] ${message}`; + if (data) { + console.log(logMessage, data); + } else { + console.log(logMessage); + } + }, + + /** + * 警告日志 + * @param message 警告消息 + * @param data 附加数据 + */ + warn(message: string, data?: any) { + const callerInfo = getCallerInfo(); + const timestamp = getTimestamp(); + const logMessage = `[${timestamp}] [${callerInfo.fileName}:${callerInfo.functionName}:${callerInfo.lineNumber}] ${message}`; + if (data) { + console.warn(logMessage, data); + } else { + console.warn(logMessage); + } + }, + + /** + * 错误日志 + * @param message 错误消息 + * @param data 附加数据 + */ + error(message: string, data?: any) { + const callerInfo = getCallerInfo(); + const timestamp = getTimestamp(); + const logMessage = `[${timestamp}] [${callerInfo.fileName}:${callerInfo.functionName}:${callerInfo.lineNumber}] ${message}`; + if (data) { + console.error(logMessage, data); + } else { + console.error(logMessage); + } + }, + + /** + * 信息日志 + * @param message 信息消息 + * @param data 附加数据 + */ + info(message: string, data?: any) { + const callerInfo = getCallerInfo(); + const timestamp = getTimestamp(); + const logMessage = `[${timestamp}] [${callerInfo.fileName}:${callerInfo.functionName}:${callerInfo.lineNumber}] ${message}`; + if (data) { + console.info(logMessage, data); + } else { + console.info(logMessage); + } + } +}; + +export default logger; \ No newline at end of file