Files
hzdyjs/test/pm-repair-api-test.js
T
2025-06-30 20:33:58 +08:00

417 lines
15 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('🔐 开始登录获取Token...'));
// 生成AES密钥并加密登录数据
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('✅ 登录成功!'));
console.log(
colors.gray(`Token: ${response.data.data.access_token.substring(0, 50)}...`),
);
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: 10000,
};
if (data) {
config.data = data;
}
if (params) {
config.params = params;
}
const response = await axios(config);
return {
success: true,
data: response.data,
status: response.status,
};
} catch (error) {
return {
success: false,
error: error.response?.data || error.message,
status: error.response?.status || 0,
};
}
}
/**
* 测试物业报修分类接口
*/
async function testRepairCategoryAPIs(token) {
console.log(colors.yellow('\n📂 测试物业报修分类接口'));
console.log(colors.gray('=' * 50));
// 1. 查询维修类型列表
console.log(colors.cyan('1. 查询维修类型列表 - GET /pm/repairCategory/list'));
const listResult = await apiRequest(token, 'GET', '/pm/repairCategory/list', null, {
pageNum: 1,
pageSize: 10,
});
if (listResult.success) {
console.log(colors.green('✅ 成功'), `状态码: ${listResult.status}`);
console.log(
colors.gray(`响应: ${JSON.stringify(listResult.data, null, 2).substring(0, 200)}...`),
);
} else {
console.log(colors.red('❌ 失败'), `状态码: ${listResult.status}`);
console.log(colors.gray(`错误: ${JSON.stringify(listResult.error, null, 2)}`));
}
// 2. 获取维修类型树形结构
console.log(colors.cyan('\n2. 获取维修类型树形结构 - GET /pm/repairCategory/tree'));
const treeResult = await apiRequest(token, 'GET', '/pm/repairCategory/tree');
if (treeResult.success) {
console.log(colors.green('✅ 成功'), `状态码: ${treeResult.status}`);
console.log(
colors.gray(`响应: ${JSON.stringify(treeResult.data, null, 2).substring(0, 200)}...`),
);
} else {
console.log(colors.red('❌ 失败'), `状态码: ${treeResult.status}`);
console.log(colors.gray(`错误: ${JSON.stringify(treeResult.error, null, 2)}`));
}
// 3. 新增维修类型
console.log(colors.cyan('\n3. 新增维修类型 - POST /pm/repairCategory'));
const addCategoryData = {
categoryName: '测试维修类型',
categoryCode: 'TEST_REPAIR',
categoryDescription: '这是一个测试的维修类型',
parentId: 0,
skillLevel: '1',
estimatedDuration: 60,
standardCost: 100.0,
colorCode: '#1890FF',
sortOrder: 99,
status: '1',
remark: '测试数据',
};
const addResult = await apiRequest(token, 'POST', '/pm/repairCategory', addCategoryData);
if (addResult.success) {
console.log(colors.green('✅ 成功'), `状态码: ${addResult.status}`);
console.log(colors.gray(`响应: ${JSON.stringify(addResult.data, null, 2)}`));
} else {
console.log(colors.red('❌ 失败'), `状态码: ${addResult.status}`);
console.log(colors.gray(`错误: ${JSON.stringify(addResult.error, null, 2)}`));
}
}
/**
* 测试物业报修工单接口
*/
async function testRepairOrderAPIs(token) {
console.log(colors.yellow('\n🔧 测试物业报修工单接口'));
console.log(colors.gray('=' * 50));
// 1. 生成工单编号
console.log(colors.cyan('1. 生成工单编号 - GET /pm/repairOrder/generateOrderNo'));
const generateResult = await apiRequest(token, 'GET', '/pm/repairOrder/generateOrderNo');
let orderNo = null;
if (generateResult.success) {
console.log(colors.green('✅ 成功'), `状态码: ${generateResult.status}`);
console.log(colors.gray(`响应: ${JSON.stringify(generateResult.data, null, 2)}`));
orderNo = generateResult.data.data;
} else {
console.log(colors.red('❌ 失败'), `状态码: ${generateResult.status}`);
console.log(colors.gray(`错误: ${JSON.stringify(generateResult.error, null, 2)}`));
orderNo = `RO${new Date().toISOString().slice(0, 10).replace(/-/g, '')}001`;
}
// 2. 查询报修工单列表
console.log(colors.cyan('\n2. 查询报修工单列表 - GET /pm/repairOrder/list'));
const listResult = await apiRequest(token, 'GET', '/pm/repairOrder/list', null, {
pageNum: 1,
pageSize: 10,
});
if (listResult.success) {
console.log(colors.green('✅ 成功'), `状态码: ${listResult.status}`);
console.log(
colors.gray(`响应: ${JSON.stringify(listResult.data, null, 2).substring(0, 300)}...`),
);
} else {
console.log(colors.red('❌ 失败'), `状态码: ${listResult.status}`);
console.log(colors.gray(`错误: ${JSON.stringify(listResult.error, null, 2)}`));
}
// 3. 新增报修工单
console.log(colors.cyan('\n3. 新增报修工单 - POST /pm/repairOrder'));
const addOrderData = {
orderNo: orderNo,
title: '测试报修工单 - 办公室空调故障',
description: '办公室空调不制冷,温度调节失效,影响正常办公。请尽快安排维修人员检查处理。',
repairLocation: '行政楼305办公室',
categoryId: 1,
priorityLevel: 2,
expectedFinishTime: new Date(Date.now() + 24 * 60 * 60 * 1000)
.toISOString()
.slice(0, 19)
.replace('T', ' '),
reporterName: '张三',
reporterPhone: '13800138001',
reporterDept: '行政办公室',
remark: '测试工单数据',
};
const addResult = await apiRequest(token, 'POST', '/pm/repairOrder', addOrderData);
let createdOrderId = null;
if (addResult.success) {
console.log(colors.green('✅ 成功'), `状态码: ${addResult.status}`);
console.log(colors.gray(`响应: ${JSON.stringify(addResult.data, null, 2)}`));
// 尝试从响应中获取创建的工单ID
if (addResult.data.data && typeof addResult.data.data === 'object') {
createdOrderId = addResult.data.data.id || Date.now();
} else {
createdOrderId = Date.now();
}
} else {
console.log(colors.red('❌ 失败'), `状态码: ${addResult.status}`);
console.log(colors.gray(`错误: ${JSON.stringify(addResult.error, null, 2)}`));
}
// 4. 如果有创建的工单ID,测试工单详情查询
if (createdOrderId) {
console.log(colors.cyan(`\n4. 查询工单详情 - GET /pm/repairOrder/${createdOrderId}`));
const detailResult = await apiRequest(token, 'GET', `/pm/repairOrder/${createdOrderId}`);
if (detailResult.success) {
console.log(colors.green('✅ 成功'), `状态码: ${detailResult.status}`);
console.log(
colors.gray(
`响应: ${JSON.stringify(detailResult.data, null, 2).substring(0, 300)}...`,
),
);
} else {
console.log(colors.red('❌ 失败'), `状态码: ${detailResult.status}`);
console.log(colors.gray(`错误: ${JSON.stringify(detailResult.error, null, 2)}`));
}
}
return createdOrderId;
}
/**
* 测试工单操作接口
*/
async function testOrderOperationAPIs(token, orderId) {
if (!orderId) {
console.log(colors.yellow('\n⚠️ 跳过工单操作测试(没有可用的工单ID)'));
return;
}
console.log(colors.yellow('\n⚙️ 测试工单操作接口'));
console.log(colors.gray('=' * 50));
// 1. 分派工单
console.log(colors.cyan(`1. 分派工单 - PUT /pm/repairOrder/${orderId}/assign`));
const assignResult = await apiRequest(token, 'PUT', `/pm/repairOrder/${orderId}/assign`, null, {
assigneeId: 1001,
assignRemark: '分派给李师傅处理,请尽快联系报修人',
});
if (assignResult.success) {
console.log(colors.green('✅ 成功'), `状态码: ${assignResult.status}`);
console.log(colors.gray(`响应: ${JSON.stringify(assignResult.data, null, 2)}`));
} else {
console.log(colors.red('❌ 失败'), `状态码: ${assignResult.status}`);
console.log(colors.gray(`错误: ${JSON.stringify(assignResult.error, null, 2)}`));
}
// 2. 开始维修
console.log(colors.cyan(`\n2. 开始维修 - PUT /pm/repairOrder/${orderId}/start`));
const startResult = await apiRequest(token, 'PUT', `/pm/repairOrder/${orderId}/start`);
if (startResult.success) {
console.log(colors.green('✅ 成功'), `状态码: ${startResult.status}`);
console.log(colors.gray(`响应: ${JSON.stringify(startResult.data, null, 2)}`));
} else {
console.log(colors.red('❌ 失败'), `状态码: ${startResult.status}`);
console.log(colors.gray(`错误: ${JSON.stringify(startResult.error, null, 2)}`));
}
// 3. 完成维修
console.log(colors.cyan(`\n3. 完成维修 - PUT /pm/repairOrder/${orderId}/finish`));
const finishResult = await apiRequest(token, 'PUT', `/pm/repairOrder/${orderId}/finish`, null, {
repairResult: '已更换空调滤网,清洁空调系统,制冷功能恢复正常',
usedMaterials: '空调滤网 x1, 清洁剂 x1',
});
if (finishResult.success) {
console.log(colors.green('✅ 成功'), `状态码: ${finishResult.status}`);
console.log(colors.gray(`响应: ${JSON.stringify(finishResult.data, null, 2)}`));
} else {
console.log(colors.red('❌ 失败'), `状态码: ${finishResult.status}`);
console.log(colors.gray(`错误: ${JSON.stringify(finishResult.error, null, 2)}`));
}
}
/**
* 主测试函数
*/
async function main() {
console.log(colors.rainbow('🏢 物业报修接口测试工具'));
console.log(colors.gray('=' * 60));
try {
// 1. 登录获取Token
const token = await login();
// 2. 测试物业报修分类接口
await testRepairCategoryAPIs(token);
// 3. 测试物业报修工单接口
const orderId = await testRepairOrderAPIs(token);
// 4. 测试工单操作接口
await testOrderOperationAPIs(token, orderId);
// 5. 测试结果总结
console.log(colors.rainbow('\n🎉 物业报修接口测试完成!'));
console.log(colors.green('\n✅ 测试覆盖的接口:'));
console.log(colors.gray(' - 维修类型列表查询'));
console.log(colors.gray(' - 维修类型树形结构'));
console.log(colors.gray(' - 新增维修类型'));
console.log(colors.gray(' - 生成工单编号'));
console.log(colors.gray(' - 工单列表查询'));
console.log(colors.gray(' - 新增报修工单'));
console.log(colors.gray(' - 工单详情查询'));
console.log(colors.gray(' - 分派工单'));
console.log(colors.gray(' - 开始维修'));
console.log(colors.gray(' - 完成维修'));
console.log(colors.blue('\n💡 提示:'));
console.log(colors.gray(' - 所有接口都需要Token认证'));
console.log(colors.gray(' - 接口路径基于后端Controller定义'));
console.log(colors.gray(' - 部分操作可能需要特定的权限码'));
console.log(colors.gray(' - 建议检查后端数据库是否有相应的表结构'));
} catch (error) {
console.error(colors.red('\n💥 测试过程中发生错误:'), error.message);
process.exit(1);
}
}
// 运行测试
if (require.main === module) {
main();
}
module.exports = {
login,
apiRequest,
testRepairCategoryAPIs,
testRepairOrderAPIs,
testOrderOperationAPIs,
};