提交物业报修模块

This commit is contained in:
zlwu committed 2025-06-30 20:33:58 +08:00
1 parent 60cb1b09f8
commit 2371080245
597 files changed
+100361 -4145

No files matched your search

+331
View File
@@ -0,0 +1,331 @@
const axios = require('axios');
const colors = require('colors');
// API格式验证测试脚本
// 用于验证所有接口是否符合统一响应格式规范
// 获取Token
async function getToken() {
try {
const { getToken } = require('./quick-token-getter');
return await getToken();
} catch (error) {
console.log('❌ 获取Token失败'.red);
return null;
}
}
// 验证响应格式
function validateResponseFormat(response, apiName, expectedType) {
const issues = [];
// 检查基础字段
if (typeof response.code !== 'number') {
issues.push('❌ code字段缺失或类型错误(应为number)');
}
if (typeof response.msg !== 'string') {
issues.push('❌ msg字段缺失或类型错误(应为string)');
}
if (!response.hasOwnProperty('data')) {
issues.push('❌ data字段缺失');
}
// 检查成功状态码
if (response.code === 200) {
// 根据期望类型验证字段
switch (expectedType) {
case 'paginated':
// 分页接口直接在顶层包含total和rows字段
if (typeof response.total !== 'number') {
issues.push('❌ 分页接口缺少total字段(应为number)');
}
if (!Array.isArray(response.rows)) {
issues.push('❌ 分页接口缺少rows字段(应为array)');
}
break;
case 'array':
if (!Array.isArray(response.data)) {
issues.push('❌ 列表接口data应为数组');
}
break;
case 'object':
if (
response.data === null ||
typeof response.data !== 'object' ||
Array.isArray(response.data)
) {
issues.push('❌ 单条数据接口data应为对象(非null)');
}
break;
case 'null':
if (response.data !== null) {
issues.push('❌ 操作类接口data应为null');
}
break;
}
}
return issues;
}
// API测试用例
const apiTestCases = [
{
name: '报修工单列表(分页)',
url: '/pm/repairOrder/list',
method: 'GET',
data: { pageNum: 1, pageSize: 10 },
expectedType: 'paginated',
permission: 'pm:repairOrder:list',
},
{
name: '维修分类下拉选项',
url: '/pm/repairCategory/options',
method: 'GET',
data: null,
expectedType: 'array',
permission: 'pm:category:options',
},
{
name: '报修统计数据',
url: '/pm/repairOrder/statistics',
method: 'GET',
data: null,
expectedType: 'object',
permission: 'pm:repairOrder:statistics',
},
{
name: '系统用户信息',
url: '/system/user/getInfo',
method: 'GET',
data: null,
expectedType: 'object',
permission: 'system:user:query',
},
{
name: '系统字典数据',
url: '/system/dict/data/list',
method: 'GET',
data: { pageNum: 1, pageSize: 10 },
expectedType: 'paginated',
permission: 'system:dict:list',
},
];
// 执行API格式验证测试
async function runApiFormatValidation() {
console.log('🔍 API格式验证测试'.rainbow.bold);
console.log('='.repeat(80).rainbow);
console.log('📋 验证规范:统一响应格式 {code, msg, data}');
console.log('🎯 测试目标:确保所有接口符合新的格式规范');
console.log('');
// 获取Token
console.log('🔐 获取认证Token...'.magenta);
const token = await getToken();
if (!token) {
console.log('❌ 无法获取Token,测试终止'.red);
return;
}
console.log('✅ Token获取成功'.green);
console.log('');
const results = [];
let totalTests = 0;
let passedTests = 0;
// 逐个测试API
for (const testCase of apiTestCases) {
totalTests++;
console.log(`📝 测试 ${totalTests}: ${testCase.name}`.cyan.bold);
console.log(` 📍 接口: ${testCase.method} ${testCase.url}`);
console.log(` 🔑 权限: ${testCase.permission}`);
console.log(` 📊 期望格式: ${testCase.expectedType}`);
try {
// 构建请求配置
const config = {
method: testCase.method.toLowerCase(),
url: `http://localhost/dev-api${testCase.url}`,
headers: {
'Content-Type': 'application/json;charset=utf-8',
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
referer: 'http://localhost/login?redirect=%252Findex',
Authorization: `Bearer ${token}`,
},
timeout: 10000,
};
if (testCase.data) {
if (testCase.method === 'GET') {
config.params = testCase.data;
} else {
config.data = testCase.data;
}
}
// 发送请求
const response = await axios(config);
// 验证响应格式
const issues = validateResponseFormat(
response.data,
testCase.name,
testCase.expectedType,
);
if (issues.length === 0) {
console.log(' ✅ 格式验证通过'.green);
passedTests++;
// 显示响应数据统计
if (response.data.code === 200) {
if (testCase.expectedType === 'paginated') {
console.log(
` 📊 数据统计: total=${response.data.total}, rows=${
response.data.rows?.length || 0
}`,
);
} else if (
testCase.expectedType === 'array' &&
Array.isArray(response.data.data)
) {
console.log(` 📊 数据统计: 数组长度=${response.data.data.length}`);
} else if (testCase.expectedType === 'object' && response.data.data) {
const keys = Object.keys(response.data.data);
console.log(` 📊 数据统计: 对象字段=${keys.length}个`);
}
}
} else {
console.log(' ❌ 格式验证失败'.red);
issues.forEach((issue) => {
console.log(` ${issue}`.red);
});
}
results.push({
name: testCase.name,
url: testCase.url,
method: testCase.method,
success: issues.length === 0,
issues: issues,
response: {
code: response.data.code,
msg: response.data.msg,
hasData: response.data.hasOwnProperty('data'),
dataType:
testCase.expectedType === 'paginated'
? 'paginated'
: Array.isArray(response.data.data)
? 'array'
: response.data.data === null
? 'null'
: typeof response.data.data,
total: response.data.total,
rowsCount: response.data.rows?.length,
},
});
} catch (error) {
console.log(' ❌ 请求失败'.red);
console.log(` 错误信息: ${error.response?.data?.msg || error.message}`.red);
results.push({
name: testCase.name,
url: testCase.url,
method: testCase.method,
success: false,
issues: [`请求失败: ${error.response?.data?.msg || error.message}`],
response: null,
});
}
console.log('');
}
// 输出测试总结
console.log('📊 测试结果汇总'.rainbow.bold);
console.log('='.repeat(80).rainbow);
console.log(`🎯 总测试数: ${totalTests}`);
console.log(`✅ 通过测试: ${passedTests}`.green);
console.log(`❌ 失败测试: ${totalTests - passedTests}`.red);
console.log(`📈 通过率: ${((passedTests / totalTests) * 100).toFixed(1)}%`);
console.log('');
// 详细结果表格
console.log('📋 详细测试结果'.blue.bold);
console.log('─'.repeat(100));
results.forEach((result, index) => {
const status = result.success ? '✅ 通过'.green : '❌ 失败'.red;
console.log(
`${(index + 1).toString().padStart(2)}. ${result.name.padEnd(
20,
)} ${result.method.padEnd(6)} ${status}`,
);
if (!result.success && result.issues.length > 0) {
result.issues.forEach((issue) => {
console.log(` ${issue}`.red);
});
}
if (result.response) {
console.log(
` 响应: code=${result.response.code}, msg="${result.response.msg}", data类型=${result.response.dataType}`,
);
}
console.log('');
});
// 规范建议
if (passedTests < totalTests) {
console.log('💡 规范建议'.yellow.bold);
console.log('─'.repeat(50));
console.log('1. 检查请求头是否包含Bearer Token格式的Authorization');
console.log('2. 分页接口必须直接返回TableDataInfo格式(total、rows、code、msg)');
console.log('3. 非分页接口必须使用R<T>类型包装返回结果');
console.log('4. 确保包含clientid、Content-Type等必要请求头');
console.log('5. 验证响应格式是否符合新的统一规范');
console.log('');
}
// 成功提示
if (passedTests === totalTests) {
console.log('🎉 恭喜!所有接口都符合统一响应格式规范!'.green.bold);
}
return {
total: totalTests,
passed: passedTests,
failed: totalTests - passedTests,
successRate: ((passedTests / totalTests) * 100).toFixed(1),
};
}
// 主函数
async function main() {
try {
const result = await runApiFormatValidation();
console.log('\n🏁 API格式验证测试完成'.rainbow.bold);
// 返回测试结果供其他脚本使用
return result;
} catch (error) {
console.log('❌ 测试执行失败:'.red, error.message);
process.exit(1);
}
}
// 如果直接运行此脚本
if (require.main === module) {
main().then((result) => {
if (result.failed > 0) {
process.exit(1); // 有失败用例时退出码为1
}
});
}
module.exports = { runApiFormatValidation };