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, };