#!/usr/bin/env node /** * Docker 部署验证脚本 * 检查所有服务是否正常运行 */ const http = require('http'); const { exec } = require('child_process'); const util = require('util'); const execPromise = util.promisify(exec); const DELAY = ms => new Promise(resolve => setTimeout(resolve, ms)); async function checkDocker() { console.log('[1] 检查 Docker 服务...'); try { await execPromise('docker ps'); console.log(' ✓ Docker 运行正常'); return true; } catch (error) { console.log(' ✗ Docker 未运行'); return false; } } async function checkContainers() { console.log('[2] 检查容器状态...'); try { const { stdout } = await execPromise('docker-compose ps'); console.log(stdout); if (stdout.includes('aguzhitou-mysql') && stdout.includes('aguzhitou-redis') && stdout.includes('aguzhitou-app')) { console.log(' ✓ 所有容器已启动'); return true; } else { console.log(' ✗ 部分容器未启动'); return false; } } catch (error) { console.log(' ✗ 无法获取容器状态'); return false; } } async function checkAPI() { console.log('[3] 检查 API 服务...'); return new Promise((resolve) => { const req = http.get('http://localhost:3000/api/v1/health', (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { try { const result = JSON.parse(data); if (result.code === 200) { console.log(' ✓ API 服务正常'); console.log(` ✓ 服务时间: ${result.data.timestamp}`); resolve(true); } else { console.log(' ✗ API 返回异常'); resolve(false); } } catch (e) { console.log(' ✗ API 响应解析失败'); resolve(false); } }); }); req.on('error', (err) => { console.log(' ✗ 无法连接 API'); resolve(false); }); req.setTimeout(5000, () => { console.log(' ✗ API 连接超时'); req.destroy(); resolve(false); }); }); } async function checkMySQL() { console.log('[4] 检查 MySQL 数据库...'); try { const { stdout } = await execPromise( 'docker-compose exec -T mysql mysql -u root -p1qazse42W3 -e "SELECT COUNT(*) as tables FROM information_schema.tables WHERE table_schema=\'aguzhitou\';"' ); const match = stdout.match(/(\d+)/); if (match && parseInt(match[1]) >= 12) { console.log(` ✓ MySQL 数据库正常 (${match[1]} 张表)`); return true; } else { console.log(' ✗ 数据库表数量异常'); return false; } } catch (error) { console.log(' ✗ 无法连接 MySQL'); return false; } } async function checkPartitions() { console.log('[5] 检查数据库分区...'); try { const { stdout } = await execPromise( 'docker-compose exec -T mysql mysql -u root -p1qazse42W3 -e "SELECT COUNT(DISTINCT table_name) as tables, COUNT(*) as partitions FROM information_schema.partitions WHERE table_schema=\'aguzhitou\' AND partition_name IS NOT NULL;"' ); const match = stdout.match(/(\d+)\s*\|\s*(\d+)/); if (match) { console.log(` ✓ 分区表: ${match[1]} 张`); console.log(` ✓ 总分区: ${match[2]} 个`); return true; } return false; } catch (error) { console.log(' ✗ 无法获取分区信息'); return false; } } async function checkRedis() { console.log('[6] 检查 Redis 缓存...'); try { const { stdout } = await execPromise('docker-compose exec -T redis redis-cli ping'); if (stdout.includes('PONG')) { console.log(' ✓ Redis 运行正常'); return true; } console.log(' ✗ Redis 响应异常'); return false; } catch (error) { console.log(' ✗ 无法连接 Redis'); return false; } } async function testAPIEndpoints() { console.log('[7] 测试 API 端点...'); const endpoints = [ { path: '/api/v1/market/indices', name: '市场指数' }, { path: '/api/v1/sectors', name: '版块列表' }, { path: '/api/v1/stocks/search?keyword=茅台', name: '股票搜索' } ]; let success = 0; for (const endpoint of endpoints) { try { const result = await new Promise((resolve) => { http.get(`http://localhost:3000${endpoint.path}`, (res) => { resolve(res.statusCode === 200); }).on('error', () => resolve(false)); }); if (result) { console.log(` ✓ ${endpoint.name}`); success++; } else { console.log(` ✗ ${endpoint.name}`); } } catch (e) { console.log(` ✗ ${endpoint.name}`); } } return success === endpoints.length; } async function main() { console.log('='.repeat(60)); console.log('A股智投分析平台 - Docker 部署验证'); console.log('='.repeat(60)); console.log(); const results = { docker: await checkDocker(), containers: await checkContainers(), api: await checkAPI(), mysql: await checkMySQL(), partitions: await checkPartitions(), redis: await checkRedis(), endpoints: await testAPIEndpoints() }; console.log(); console.log('='.repeat(60)); const allPassed = Object.values(results).every(r => r === true); if (allPassed) { console.log('✅ 所有检查通过!部署成功!'); console.log('='.repeat(60)); console.log(); console.log('访问地址:'); console.log(' • API 文档: http://localhost:3000/api/v1/health'); console.log(' • MySQL: localhost:3306 (root/1qazse42W3)'); console.log(' • Redis: localhost:6379'); console.log(); } else { console.log('❌ 部分检查未通过,请查看日志:'); console.log(' docker-compose logs'); console.log('='.repeat(60)); process.exit(1); } } main().catch(console.error);