Summary: - Fix methodology score display issue in task list (show score instead of 'warn') - Add methodology_score field to database schema - Fix report display when only methodology agent is selected - Implement Word document export using docx library - Update documentation to v3.0/v3.1 Backend changes: - Add methodologyScore to Prisma schema and TaskSummary type - Update reviewWorker to save methodologyScore - Update getTaskList to return methodologyScore Frontend changes: - Install docx and file-saver libraries - Implement handleExportReport with Word generation - Fix activeTab auto-selection based on available data - Add proper imports for docx components Documentation: - Update RVW module status to 90% (Phase 1-5 complete) - Update system status document to v3.0 Tested: All review workflows verified, Word export functional
430 lines
11 KiB
JavaScript
430 lines
11 KiB
JavaScript
/**
|
||
* Tool C Day 2 API测试脚本
|
||
*
|
||
* 测试内容:
|
||
* 1. 创建测试Excel文件
|
||
* 2. 上传文件创建Session
|
||
* 3. 获取Session信息
|
||
* 4. 获取预览数据
|
||
* 5. 获取完整数据
|
||
* 6. 更新心跳
|
||
* 7. 删除Session
|
||
*
|
||
* 执行方式:node test-tool-c-day2.mjs
|
||
*/
|
||
|
||
import FormData from 'form-data';
|
||
import axios from 'axios';
|
||
import * as XLSX from 'xlsx';
|
||
import { Buffer } from 'buffer';
|
||
|
||
const BASE_URL = 'http://localhost:3000';
|
||
const API_PREFIX = '/api/v1/dc/tool-c';
|
||
|
||
// ==================== 辅助函数 ====================
|
||
|
||
function printSection(title) {
|
||
console.log('\n' + '='.repeat(70));
|
||
console.log(` ${title}`);
|
||
console.log('='.repeat(70) + '\n');
|
||
}
|
||
|
||
function printSuccess(message) {
|
||
console.log('✅ ' + message);
|
||
}
|
||
|
||
function printError(message) {
|
||
console.log('❌ ' + message);
|
||
}
|
||
|
||
function printInfo(message) {
|
||
console.log('ℹ️ ' + message);
|
||
}
|
||
|
||
// ==================== 创建测试Excel文件 ====================
|
||
|
||
function createTestExcelFile() {
|
||
printSection('创建测试Excel文件');
|
||
|
||
// 创建医疗数据
|
||
const testData = [
|
||
{ patient_id: 'P001', name: '张三', age: 25, gender: '男', diagnosis: '感冒', sbp: 120, dbp: 80 },
|
||
{ patient_id: 'P002', name: '李四', age: 65, gender: '女', diagnosis: '高血压', sbp: 150, dbp: 95 },
|
||
{ patient_id: 'P003', name: '王五', age: 45, gender: '男', diagnosis: '糖尿病', sbp: 135, dbp: 85 },
|
||
{ patient_id: 'P004', name: '赵六', age: 70, gender: '女', diagnosis: '冠心病', sbp: 160, dbp: 100 },
|
||
{ patient_id: 'P005', name: '钱七', age: 35, gender: '男', diagnosis: '胃炎', sbp: 110, dbp: 70 },
|
||
{ patient_id: 'P006', name: '孙八', age: 55, gender: '女', diagnosis: '肺炎', sbp: 125, dbp: 82 },
|
||
{ patient_id: 'P007', name: '周九', age: 48, gender: '男', diagnosis: '肝炎', sbp: 130, dbp: 88 },
|
||
{ patient_id: 'P008', name: '吴十', age: 62, gender: '女', diagnosis: '关节炎', sbp: 145, dbp: 92 },
|
||
];
|
||
|
||
// 创建工作簿
|
||
const ws = XLSX.utils.json_to_sheet(testData);
|
||
const wb = XLSX.utils.book_new();
|
||
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
|
||
|
||
// 生成Buffer
|
||
const excelBuffer = XLSX.write(wb, { type: 'buffer', bookType: 'xlsx' });
|
||
|
||
printSuccess(`测试文件创建成功: ${testData.length}行 x ${Object.keys(testData[0]).length}列`);
|
||
printInfo(`文件大小: ${(excelBuffer.length / 1024).toFixed(2)} KB`);
|
||
|
||
return {
|
||
buffer: excelBuffer,
|
||
fileName: 'test-medical-data.xlsx',
|
||
expectedRows: testData.length,
|
||
expectedCols: Object.keys(testData[0]).length,
|
||
};
|
||
}
|
||
|
||
// ==================== API测试函数 ====================
|
||
|
||
async function testUploadFile(excelData) {
|
||
printSection('测试1: 上传Excel文件创建Session');
|
||
|
||
try {
|
||
const form = new FormData();
|
||
form.append('file', excelData.buffer, {
|
||
filename: excelData.fileName,
|
||
contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||
});
|
||
|
||
printInfo(`上传文件: ${excelData.fileName}`);
|
||
|
||
const response = await axios.post(
|
||
`${BASE_URL}${API_PREFIX}/sessions/upload`,
|
||
form,
|
||
{
|
||
headers: form.getHeaders(),
|
||
timeout: 10000,
|
||
}
|
||
);
|
||
|
||
if (response.status === 201 && response.data.success) {
|
||
printSuccess('文件上传成功');
|
||
console.log('响应数据:', JSON.stringify(response.data.data, null, 2));
|
||
|
||
const { sessionId, totalRows, totalCols, columns } = response.data.data;
|
||
|
||
// 验证数据
|
||
if (totalRows === excelData.expectedRows && totalCols === excelData.expectedCols) {
|
||
printSuccess(`数据验证通过: ${totalRows}行 x ${totalCols}列`);
|
||
} else {
|
||
printError(`数据不匹配: 预期${excelData.expectedRows}行x${excelData.expectedCols}列, 实际${totalRows}行x${totalCols}列`);
|
||
}
|
||
|
||
printInfo(`Session ID: ${sessionId}`);
|
||
printInfo(`列名: ${columns.join(', ')}`);
|
||
|
||
return sessionId;
|
||
} else {
|
||
printError('上传失败: ' + JSON.stringify(response.data));
|
||
return null;
|
||
}
|
||
} catch (error) {
|
||
printError('上传异常: ' + (error.response?.data?.error || error.message));
|
||
if (error.response) {
|
||
console.log('错误详情:', JSON.stringify(error.response.data, null, 2));
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
|
||
async function testGetSession(sessionId) {
|
||
printSection('测试2: 获取Session信息');
|
||
|
||
try {
|
||
printInfo(`Session ID: ${sessionId}`);
|
||
|
||
const response = await axios.get(
|
||
`${BASE_URL}${API_PREFIX}/sessions/${sessionId}`,
|
||
{ timeout: 5000 }
|
||
);
|
||
|
||
if (response.status === 200 && response.data.success) {
|
||
printSuccess('Session信息获取成功');
|
||
console.log('Session信息:', JSON.stringify(response.data.data, null, 2));
|
||
return true;
|
||
} else {
|
||
printError('获取失败');
|
||
return false;
|
||
}
|
||
} catch (error) {
|
||
printError('获取异常: ' + (error.response?.data?.error || error.message));
|
||
return false;
|
||
}
|
||
}
|
||
|
||
async function testGetPreviewData(sessionId) {
|
||
printSection('测试3: 获取预览数据(前100行)');
|
||
|
||
try {
|
||
printInfo(`Session ID: ${sessionId}`);
|
||
|
||
const response = await axios.get(
|
||
`${BASE_URL}${API_PREFIX}/sessions/${sessionId}/preview`,
|
||
{ timeout: 10000 }
|
||
);
|
||
|
||
if (response.status === 200 && response.data.success) {
|
||
printSuccess('预览数据获取成功');
|
||
|
||
const { totalRows, previewRows, previewData } = response.data.data;
|
||
printInfo(`总行数: ${totalRows}, 预览行数: ${previewRows}`);
|
||
printInfo(`预览数据前3行:`);
|
||
console.log(JSON.stringify(previewData.slice(0, 3), null, 2));
|
||
|
||
return true;
|
||
} else {
|
||
printError('获取预览数据失败');
|
||
return false;
|
||
}
|
||
} catch (error) {
|
||
printError('获取预览数据异常: ' + (error.response?.data?.error || error.message));
|
||
return false;
|
||
}
|
||
}
|
||
|
||
async function testGetFullData(sessionId) {
|
||
printSection('测试4: 获取完整数据');
|
||
|
||
try {
|
||
printInfo(`Session ID: ${sessionId}`);
|
||
|
||
const response = await axios.get(
|
||
`${BASE_URL}${API_PREFIX}/sessions/${sessionId}/full`,
|
||
{ timeout: 10000 }
|
||
);
|
||
|
||
if (response.status === 200 && response.data.success) {
|
||
printSuccess('完整数据获取成功');
|
||
|
||
const { totalRows, data } = response.data.data;
|
||
printInfo(`总行数: ${totalRows}`);
|
||
printInfo(`完整数据前2行:`);
|
||
console.log(JSON.stringify(data.slice(0, 2), null, 2));
|
||
|
||
return true;
|
||
} else {
|
||
printError('获取完整数据失败');
|
||
return false;
|
||
}
|
||
} catch (error) {
|
||
printError('获取完整数据异常: ' + (error.response?.data?.error || error.message));
|
||
return false;
|
||
}
|
||
}
|
||
|
||
async function testUpdateHeartbeat(sessionId) {
|
||
printSection('测试5: 更新心跳');
|
||
|
||
try {
|
||
printInfo(`Session ID: ${sessionId}`);
|
||
|
||
const response = await axios.post(
|
||
`${BASE_URL}${API_PREFIX}/sessions/${sessionId}/heartbeat`,
|
||
{},
|
||
{ timeout: 5000 }
|
||
);
|
||
|
||
if (response.status === 200 && response.data.success) {
|
||
printSuccess('心跳更新成功');
|
||
console.log('新过期时间:', response.data.data.expiresAt);
|
||
return true;
|
||
} else {
|
||
printError('心跳更新失败');
|
||
return false;
|
||
}
|
||
} catch (error) {
|
||
printError('心跳更新异常: ' + (error.response?.data?.error || error.message));
|
||
return false;
|
||
}
|
||
}
|
||
|
||
async function testDeleteSession(sessionId) {
|
||
printSection('测试6: 删除Session');
|
||
|
||
try {
|
||
printInfo(`Session ID: ${sessionId}`);
|
||
|
||
const response = await axios.delete(
|
||
`${BASE_URL}${API_PREFIX}/sessions/${sessionId}`,
|
||
{ timeout: 5000 }
|
||
);
|
||
|
||
if (response.status === 200 && response.data.success) {
|
||
printSuccess('Session删除成功');
|
||
return true;
|
||
} else {
|
||
printError('Session删除失败');
|
||
return false;
|
||
}
|
||
} catch (error) {
|
||
printError('Session删除异常: ' + (error.response?.data?.error || error.message));
|
||
return false;
|
||
}
|
||
}
|
||
|
||
async function testGetDeletedSession(sessionId) {
|
||
printSection('测试7: 验证Session已删除');
|
||
|
||
try {
|
||
printInfo(`尝试获取已删除的Session: ${sessionId}`);
|
||
|
||
const response = await axios.get(
|
||
`${BASE_URL}${API_PREFIX}/sessions/${sessionId}`,
|
||
{ timeout: 5000 }
|
||
);
|
||
|
||
printError('Session仍然存在(不应该)');
|
||
return false;
|
||
} catch (error) {
|
||
if (error.response?.status === 404) {
|
||
printSuccess('Session已正确删除(返回404)');
|
||
return true;
|
||
} else {
|
||
printError('未预期的错误: ' + error.message);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
// ==================== 主测试函数 ====================
|
||
|
||
async function runAllTests() {
|
||
console.log('\n' + '🚀'.repeat(35));
|
||
console.log(' Tool C Day 2 API测试');
|
||
console.log('🚀'.repeat(35));
|
||
|
||
const results = {};
|
||
|
||
try {
|
||
// 创建测试文件
|
||
const excelData = createTestExcelFile();
|
||
|
||
// 测试1: 上传文件
|
||
const sessionId = await testUploadFile(excelData);
|
||
results['上传文件'] = !!sessionId;
|
||
|
||
if (!sessionId) {
|
||
printError('上传失败,后续测试无法继续');
|
||
return results;
|
||
}
|
||
|
||
// 等待1秒
|
||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||
|
||
// 测试2: 获取Session信息
|
||
results['获取Session'] = await testGetSession(sessionId);
|
||
await new Promise(resolve => setTimeout(resolve, 500));
|
||
|
||
// 测试3: 获取预览数据
|
||
results['获取预览数据'] = await testGetPreviewData(sessionId);
|
||
await new Promise(resolve => setTimeout(resolve, 500));
|
||
|
||
// 测试4: 获取完整数据
|
||
results['获取完整数据'] = await testGetFullData(sessionId);
|
||
await new Promise(resolve => setTimeout(resolve, 500));
|
||
|
||
// 测试5: 更新心跳
|
||
results['更新心跳'] = await testUpdateHeartbeat(sessionId);
|
||
await new Promise(resolve => setTimeout(resolve, 500));
|
||
|
||
// 测试6: 删除Session
|
||
results['删除Session'] = await testDeleteSession(sessionId);
|
||
await new Promise(resolve => setTimeout(resolve, 500));
|
||
|
||
// 测试7: 验证删除
|
||
results['验证删除'] = await testGetDeletedSession(sessionId);
|
||
|
||
} catch (error) {
|
||
printError('测试过程中发生异常: ' + error.message);
|
||
console.error(error);
|
||
}
|
||
|
||
// 汇总结果
|
||
printSection('测试结果汇总');
|
||
|
||
let passed = 0;
|
||
let total = 0;
|
||
|
||
for (const [testName, result] of Object.entries(results)) {
|
||
total++;
|
||
if (result) {
|
||
passed++;
|
||
console.log(`${testName.padEnd(20)}: ✅ 通过`);
|
||
} else {
|
||
console.log(`${testName.padEnd(20)}: ❌ 失败`);
|
||
}
|
||
}
|
||
|
||
console.log('\n' + '-'.repeat(70));
|
||
console.log(`总计: ${passed}/${total} 通过 (${((passed/total)*100).toFixed(1)}%)`);
|
||
console.log('-'.repeat(70));
|
||
|
||
if (passed === total) {
|
||
console.log('\n🎉 所有测试通过!Day 2 Session管理功能完成!\n');
|
||
} else {
|
||
console.log(`\n⚠️ 有 ${total - passed} 个测试失败,请检查\n`);
|
||
}
|
||
}
|
||
|
||
// 执行测试
|
||
runAllTests()
|
||
.then(() => {
|
||
console.log('测试完成');
|
||
process.exit(0);
|
||
})
|
||
.catch((error) => {
|
||
console.error('测试失败:', error);
|
||
process.exit(1);
|
||
});
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|