You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
6.6 KiB

#!/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);