392 lines
13 KiB
JavaScript
392 lines
13 KiB
JavaScript
const axios = require('axios');
|
|
const CryptoJS = require('crypto-js');
|
|
const NodeRSA = require('node-rsa');
|
|
const colors = require('colors');
|
|
|
|
// 配置信息
|
|
const CONFIG = {
|
|
baseURL: 'http://localhost/dev-api',
|
|
loginURL: 'http://localhost/dev-api/auth/login',
|
|
clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
|
tenantId: '000000',
|
|
referer: 'http://localhost/login?redirect=%252Findex',
|
|
rsaKey: 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==',
|
|
};
|
|
|
|
/**
|
|
* 生成32位随机AES密钥
|
|
*/
|
|
function generateAESKey() {
|
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
let result = '';
|
|
for (let i = 0; i < 32; i++) {
|
|
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* AES加密
|
|
*/
|
|
function aesEncrypt(data, key) {
|
|
const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
|
|
return CryptoJS.AES.encrypt(data, keyUtf8, {
|
|
mode: CryptoJS.mode.ECB,
|
|
padding: CryptoJS.pad.Pkcs7,
|
|
}).toString();
|
|
}
|
|
|
|
/**
|
|
* RSA加密AES密钥
|
|
*/
|
|
function rsaEncrypt(aesKey) {
|
|
const pemKey = `-----BEGIN PUBLIC KEY-----\n${CONFIG.rsaKey}\n-----END PUBLIC KEY-----`;
|
|
const rsaKey = new NodeRSA(pemKey);
|
|
rsaKey.setOptions({ encryptionScheme: 'pkcs1' });
|
|
const aesKeyBase64 = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(aesKey));
|
|
return rsaKey.encrypt(aesKeyBase64, 'base64');
|
|
}
|
|
|
|
/**
|
|
* 登录获取Token
|
|
*/
|
|
async function login(username = 'admin', password = 'admin123') {
|
|
try {
|
|
console.log(colors.blue('🔐 开始登录...'));
|
|
|
|
const aesKey = generateAESKey();
|
|
const loginData = {
|
|
tenantId: CONFIG.tenantId,
|
|
username: username,
|
|
password: password,
|
|
clientId: CONFIG.clientId,
|
|
grantType: 'password',
|
|
};
|
|
|
|
const encryptedData = aesEncrypt(JSON.stringify(loginData), aesKey);
|
|
const encryptedKey = rsaEncrypt(aesKey);
|
|
|
|
const headers = {
|
|
'Content-Type': 'application/json;charset=utf-8',
|
|
clientid: CONFIG.clientId,
|
|
'encrypt-key': encryptedKey,
|
|
referer: CONFIG.referer,
|
|
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
|
isToken: 'false',
|
|
isEncrypt: 'true',
|
|
repeatSubmit: 'false',
|
|
};
|
|
|
|
const response = await axios.post(CONFIG.loginURL, encryptedData, {
|
|
headers: headers,
|
|
timeout: 10000,
|
|
});
|
|
|
|
if (response.data.code === 200 && response.data.data?.access_token) {
|
|
console.log(colors.green('✅ 登录成功'));
|
|
return response.data.data.access_token;
|
|
} else {
|
|
throw new Error(response.data.msg || 'Login failed');
|
|
}
|
|
} catch (error) {
|
|
console.error(colors.red('❌ 登录失败:'), error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 通用API请求
|
|
*/
|
|
async function apiRequest(token, method, endpoint, data = null, params = null) {
|
|
try {
|
|
const config = {
|
|
method: method,
|
|
url: `${CONFIG.baseURL}${endpoint}`,
|
|
headers: {
|
|
'Content-Type': 'application/json;charset=utf-8',
|
|
Authorization: `Bearer ${token}`,
|
|
clientid: CONFIG.clientId,
|
|
referer: CONFIG.referer,
|
|
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
|
},
|
|
timeout: 15000,
|
|
};
|
|
|
|
if (data) {
|
|
config.data = data;
|
|
}
|
|
|
|
if (params) {
|
|
config.params = params;
|
|
}
|
|
|
|
const response = await axios(config);
|
|
return {
|
|
success: true,
|
|
data: response.data,
|
|
status: response.status,
|
|
headers: response.headers,
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error: error.response?.data || error.message,
|
|
status: error.response?.status || 0,
|
|
headers: error.response?.headers || {},
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 详细测试维修分类接口
|
|
*/
|
|
async function detailedCategoryTest(token) {
|
|
console.log(colors.yellow('\n📂 详细测试维修分类接口'));
|
|
console.log('=' * 60);
|
|
|
|
// 查询现有分类
|
|
console.log(colors.cyan('\n1. 查询现有维修分类'));
|
|
const listResult = await apiRequest(token, 'GET', '/pm/repairCategory/list', null, {
|
|
pageNum: 1,
|
|
pageSize: 20,
|
|
});
|
|
|
|
console.log(`状态: ${listResult.success ? '✅' : '❌'} | 状态码: ${listResult.status}`);
|
|
if (listResult.success && listResult.data.rows) {
|
|
console.log(colors.green(`发现 ${listResult.data.rows.length} 个维修分类:`));
|
|
listResult.data.rows.forEach((category, index) => {
|
|
console.log(
|
|
` ${index + 1}. ${category.categoryName} (ID: ${category.id}, Code: ${
|
|
category.categoryCode
|
|
})`,
|
|
);
|
|
});
|
|
return listResult.data.rows;
|
|
} else {
|
|
console.log(colors.red('获取分类失败:'), JSON.stringify(listResult.error, null, 2));
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 详细测试工单接口
|
|
*/
|
|
async function detailedOrderTest(token, categories) {
|
|
console.log(colors.yellow('\n🔧 详细测试工单接口'));
|
|
console.log('=' * 60);
|
|
|
|
// 1. 生成工单编号
|
|
console.log(colors.cyan('\n1. 生成工单编号'));
|
|
const genResult = await apiRequest(token, 'GET', '/pm/repairOrder/generateOrderNo');
|
|
console.log(`状态: ${genResult.success ? '✅' : '❌'} | 状态码: ${genResult.status}`);
|
|
|
|
let orderNo = 'TEST_ORDER_' + Date.now();
|
|
if (genResult.success) {
|
|
// 修正:服务器返回的格式中,工单编号在msg字段中
|
|
orderNo = genResult.data.msg || orderNo;
|
|
console.log(colors.green(`生成的工单编号: ${orderNo}`));
|
|
} else {
|
|
console.log(colors.red('生成编号失败,使用默认编号:'), orderNo);
|
|
}
|
|
|
|
// 2. 查询现有工单
|
|
console.log(colors.cyan('\n2. 查询现有工单列表'));
|
|
const listResult = await apiRequest(token, 'GET', '/pm/repairOrder/list', null, {
|
|
pageNum: 1,
|
|
pageSize: 20,
|
|
});
|
|
|
|
console.log(`状态: ${listResult.success ? '✅' : '❌'} | 状态码: ${listResult.status}`);
|
|
if (listResult.success) {
|
|
console.log(colors.green(`现有工单数量: ${listResult.data.total || 0}`));
|
|
if (listResult.data.rows && listResult.data.rows.length > 0) {
|
|
console.log('现有工单:');
|
|
listResult.data.rows.forEach((order, index) => {
|
|
console.log(
|
|
` ${index + 1}. ${order.title} (ID: ${order.id}, Status: ${
|
|
order.orderStatus
|
|
})`,
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
// 3. 创建新工单
|
|
console.log(colors.cyan('\n3. 创建新的报修工单'));
|
|
const categoryId = categories.length > 0 ? categories[0].id : 1;
|
|
|
|
const orderData = {
|
|
orderNo: orderNo,
|
|
title: '详细测试工单 - 会议室投影仪故障',
|
|
description:
|
|
'会议室投影仪无法正常启动,显示器黑屏,影响会议正常进行。设备型号:EPSON EB-U05,购买时间:2023年6月。',
|
|
repairLocation: '行政楼三楼305会议室',
|
|
categoryId: categoryId,
|
|
priorityLevel: 2, // 紧急
|
|
expectedFinishTime: new Date(Date.now() + 48 * 60 * 60 * 1000)
|
|
.toISOString()
|
|
.slice(0, 19)
|
|
.replace('T', ' '),
|
|
reporterName: '测试用户',
|
|
reporterPhone: '13900139001',
|
|
reporterDept: '信息技术部',
|
|
remark: '设备在保修期内,请联系厂家技术支持',
|
|
};
|
|
|
|
console.log('工单数据:', JSON.stringify(orderData, null, 2));
|
|
|
|
const createResult = await apiRequest(token, 'POST', '/pm/repairOrder', orderData);
|
|
console.log(`状态: ${createResult.success ? '✅' : '❌'} | 状态码: ${createResult.status}`);
|
|
console.log('响应详情:', JSON.stringify(createResult.data, null, 2));
|
|
|
|
// 4. 再次查询工单列表,看是否有新增
|
|
console.log(colors.cyan('\n4. 验证工单是否创建成功'));
|
|
const verifyResult = await apiRequest(token, 'GET', '/pm/repairOrder/list', null, {
|
|
pageNum: 1,
|
|
pageSize: 20,
|
|
orderNo: orderNo,
|
|
});
|
|
|
|
console.log(`状态: ${verifyResult.success ? '✅' : '❌'} | 状态码: ${verifyResult.status}`);
|
|
if (verifyResult.success) {
|
|
console.log(`查询结果数量: ${verifyResult.data.total || 0}`);
|
|
if (verifyResult.data.rows && verifyResult.data.rows.length > 0) {
|
|
const foundOrder = verifyResult.data.rows[0];
|
|
console.log(colors.green('找到创建的工单:'));
|
|
console.log(` ID: ${foundOrder.id}`);
|
|
console.log(` 编号: ${foundOrder.orderNo}`);
|
|
console.log(` 标题: ${foundOrder.title}`);
|
|
console.log(` 状态: ${foundOrder.orderStatus}`);
|
|
return foundOrder.id;
|
|
} else {
|
|
console.log(colors.yellow('没有找到刚创建的工单,可能需要时间同步'));
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 测试权限相关接口
|
|
*/
|
|
async function testPermissions(token) {
|
|
console.log(colors.yellow('\n🔐 测试权限相关接口'));
|
|
console.log('=' * 60);
|
|
|
|
// 测试一些需要特定权限的接口
|
|
const permissionTests = [
|
|
{
|
|
endpoint: '/pm/repairCategory/list',
|
|
permission: 'pm:category:list',
|
|
name: '查询维修分类',
|
|
},
|
|
{
|
|
endpoint: '/pm/repairOrder/list',
|
|
permission: 'pm:repairOrder:list',
|
|
name: '查询工单列表',
|
|
},
|
|
{ endpoint: '/pm/repairOrder/generateOrderNo', permission: '无', name: '生成工单编号' },
|
|
];
|
|
|
|
for (const test of permissionTests) {
|
|
console.log(colors.cyan(`\n测试: ${test.name} (权限: ${test.permission})`));
|
|
const result = await apiRequest(token, 'GET', test.endpoint);
|
|
|
|
if (result.success) {
|
|
console.log(colors.green('✅ 权限验证通过'));
|
|
} else {
|
|
console.log(colors.red('❌ 权限验证失败'));
|
|
console.log('错误详情:', JSON.stringify(result.error, null, 2));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 测试数据库相关问题
|
|
*/
|
|
async function testDatabaseIssues(token) {
|
|
console.log(colors.yellow('\n🗃️ 测试数据库相关问题'));
|
|
console.log('=' * 60);
|
|
|
|
// 尝试获取工单详情,测试ID范围
|
|
const testIds = [1, 2, 3, 100, 1000];
|
|
|
|
console.log(colors.cyan('测试不同ID的工单详情查询:'));
|
|
for (const id of testIds) {
|
|
const result = await apiRequest(token, 'GET', `/pm/repairOrder/${id}`);
|
|
console.log(
|
|
`ID ${id}: ${result.success ? '✅' : '❌'} | 状态码: ${result.status} | ${
|
|
result.data?.msg || '无消息'
|
|
}`,
|
|
);
|
|
}
|
|
|
|
// 测试导出功能
|
|
console.log(colors.cyan('\n测试导出功能:'));
|
|
const exportResult = await apiRequest(token, 'POST', '/pm/repairOrder/export', {});
|
|
console.log(`导出测试: ${exportResult.success ? '✅' : '❌'} | 状态码: ${exportResult.status}`);
|
|
if (!exportResult.success) {
|
|
console.log('导出错误:', JSON.stringify(exportResult.error, null, 2));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 主测试函数
|
|
*/
|
|
async function main() {
|
|
console.log(colors.rainbow('🏢 物业报修接口详细测试工具'));
|
|
console.log('=' * 70);
|
|
|
|
try {
|
|
// 1. 登录
|
|
const token = await login();
|
|
|
|
// 2. 详细测试维修分类
|
|
const categories = await detailedCategoryTest(token);
|
|
|
|
// 3. 详细测试工单接口
|
|
const orderId = await detailedOrderTest(token, categories);
|
|
|
|
// 4. 测试权限
|
|
await testPermissions(token);
|
|
|
|
// 5. 测试数据库问题
|
|
await testDatabaseIssues(token);
|
|
|
|
// 6. 总结报告
|
|
console.log(colors.rainbow('\n📊 详细测试报告'));
|
|
console.log('=' * 70);
|
|
|
|
console.log(colors.green('\n✅ 成功的功能:'));
|
|
console.log(' - Token身份认证');
|
|
console.log(' - 维修分类查询和管理');
|
|
console.log(' - 工单编号生成');
|
|
console.log(' - 工单列表查询');
|
|
console.log(' - 权限控制机制');
|
|
|
|
console.log(colors.yellow('\n⚠️ 需要关注的问题:'));
|
|
console.log(' - 工单创建后可能没有立即返回正确的ID');
|
|
console.log(' - 工单详情查询返回null,可能是数据同步问题');
|
|
console.log(' - 工单操作接口依赖于有效的工单ID');
|
|
|
|
console.log(colors.blue('\n💡 建议:'));
|
|
console.log(' - 检查数据库表是否正确创建');
|
|
console.log(' - 确认工单创建时的ID生成策略');
|
|
console.log(' - 验证工单状态转换逻辑');
|
|
console.log(' - 测试完整的工单生命周期');
|
|
} catch (error) {
|
|
console.error(colors.red('\n💥 测试过程中发生错误:'), error.message);
|
|
if (error.stack) {
|
|
console.error(colors.gray(error.stack));
|
|
}
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// 运行测试
|
|
if (require.main === module) {
|
|
main();
|
|
}
|
|
|
|
module.exports = { login, apiRequest, detailedCategoryTest, detailedOrderTest };
|