pm-repair-api-test.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. const axios = require('axios');
  2. const CryptoJS = require('crypto-js');
  3. const NodeRSA = require('node-rsa');
  4. const colors = require('colors');
  5. // 配置信息
  6. const CONFIG = {
  7. baseURL: 'http://localhost/dev-api',
  8. loginURL: 'http://localhost/dev-api/auth/login',
  9. clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
  10. tenantId: '000000',
  11. referer: 'http://localhost/login?redirect=%252Findex',
  12. rsaKey: 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==',
  13. };
  14. /**
  15. * 生成32位随机AES密钥
  16. */
  17. function generateAESKey() {
  18. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  19. let result = '';
  20. for (let i = 0; i < 32; i++) {
  21. result += chars.charAt(Math.floor(Math.random() * chars.length));
  22. }
  23. return result;
  24. }
  25. /**
  26. * AES加密
  27. */
  28. function aesEncrypt(data, key) {
  29. const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
  30. return CryptoJS.AES.encrypt(data, keyUtf8, {
  31. mode: CryptoJS.mode.ECB,
  32. padding: CryptoJS.pad.Pkcs7,
  33. }).toString();
  34. }
  35. /**
  36. * RSA加密AES密钥
  37. */
  38. function rsaEncrypt(aesKey) {
  39. const pemKey = `-----BEGIN PUBLIC KEY-----\n${CONFIG.rsaKey}\n-----END PUBLIC KEY-----`;
  40. const rsaKey = new NodeRSA(pemKey);
  41. rsaKey.setOptions({ encryptionScheme: 'pkcs1' });
  42. const aesKeyBase64 = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(aesKey));
  43. return rsaKey.encrypt(aesKeyBase64, 'base64');
  44. }
  45. /**
  46. * 登录获取Token
  47. */
  48. async function login(username = 'admin', password = 'admin123') {
  49. try {
  50. console.log(colors.blue('🔐 开始登录获取Token...'));
  51. // 生成AES密钥并加密登录数据
  52. const aesKey = generateAESKey();
  53. const loginData = {
  54. tenantId: CONFIG.tenantId,
  55. username: username,
  56. password: password,
  57. clientId: CONFIG.clientId,
  58. grantType: 'password',
  59. };
  60. const encryptedData = aesEncrypt(JSON.stringify(loginData), aesKey);
  61. const encryptedKey = rsaEncrypt(aesKey);
  62. // 设置请求头
  63. const headers = {
  64. 'Content-Type': 'application/json;charset=utf-8',
  65. clientid: CONFIG.clientId,
  66. 'encrypt-key': encryptedKey,
  67. referer: CONFIG.referer,
  68. 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
  69. isToken: 'false',
  70. isEncrypt: 'true',
  71. repeatSubmit: 'false',
  72. };
  73. // 发送登录请求
  74. const response = await axios.post(CONFIG.loginURL, encryptedData, {
  75. headers: headers,
  76. timeout: 10000,
  77. });
  78. if (response.data.code === 200 && response.data.data?.access_token) {
  79. console.log(colors.green('✅ 登录成功!'));
  80. console.log(
  81. colors.gray(`Token: ${response.data.data.access_token.substring(0, 50)}...`),
  82. );
  83. return response.data.data.access_token;
  84. } else {
  85. throw new Error(response.data.msg || 'Login failed');
  86. }
  87. } catch (error) {
  88. console.error(colors.red('❌ 登录失败:'), error.message);
  89. throw error;
  90. }
  91. }
  92. /**
  93. * 通用API请求方法
  94. */
  95. async function apiRequest(token, method, endpoint, data = null, params = null) {
  96. try {
  97. const config = {
  98. method: method,
  99. url: `${CONFIG.baseURL}${endpoint}`,
  100. headers: {
  101. 'Content-Type': 'application/json;charset=utf-8',
  102. Authorization: `Bearer ${token}`,
  103. clientid: CONFIG.clientId,
  104. referer: CONFIG.referer,
  105. 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
  106. },
  107. timeout: 10000,
  108. };
  109. if (data) {
  110. config.data = data;
  111. }
  112. if (params) {
  113. config.params = params;
  114. }
  115. const response = await axios(config);
  116. return {
  117. success: true,
  118. data: response.data,
  119. status: response.status,
  120. };
  121. } catch (error) {
  122. return {
  123. success: false,
  124. error: error.response?.data || error.message,
  125. status: error.response?.status || 0,
  126. };
  127. }
  128. }
  129. /**
  130. * 测试物业报修分类接口
  131. */
  132. async function testRepairCategoryAPIs(token) {
  133. console.log(colors.yellow('\n📂 测试物业报修分类接口'));
  134. console.log(colors.gray('=' * 50));
  135. // 1. 查询维修类型列表
  136. console.log(colors.cyan('1. 查询维修类型列表 - GET /pm/repairCategory/list'));
  137. const listResult = await apiRequest(token, 'GET', '/pm/repairCategory/list', null, {
  138. pageNum: 1,
  139. pageSize: 10,
  140. });
  141. if (listResult.success) {
  142. console.log(colors.green('✅ 成功'), `状态码: ${listResult.status}`);
  143. console.log(
  144. colors.gray(`响应: ${JSON.stringify(listResult.data, null, 2).substring(0, 200)}...`),
  145. );
  146. } else {
  147. console.log(colors.red('❌ 失败'), `状态码: ${listResult.status}`);
  148. console.log(colors.gray(`错误: ${JSON.stringify(listResult.error, null, 2)}`));
  149. }
  150. // 2. 获取维修类型树形结构
  151. console.log(colors.cyan('\n2. 获取维修类型树形结构 - GET /pm/repairCategory/tree'));
  152. const treeResult = await apiRequest(token, 'GET', '/pm/repairCategory/tree');
  153. if (treeResult.success) {
  154. console.log(colors.green('✅ 成功'), `状态码: ${treeResult.status}`);
  155. console.log(
  156. colors.gray(`响应: ${JSON.stringify(treeResult.data, null, 2).substring(0, 200)}...`),
  157. );
  158. } else {
  159. console.log(colors.red('❌ 失败'), `状态码: ${treeResult.status}`);
  160. console.log(colors.gray(`错误: ${JSON.stringify(treeResult.error, null, 2)}`));
  161. }
  162. // 3. 新增维修类型
  163. console.log(colors.cyan('\n3. 新增维修类型 - POST /pm/repairCategory'));
  164. const addCategoryData = {
  165. categoryName: '测试维修类型',
  166. categoryCode: 'TEST_REPAIR',
  167. categoryDescription: '这是一个测试的维修类型',
  168. parentId: 0,
  169. skillLevel: '1',
  170. estimatedDuration: 60,
  171. standardCost: 100.0,
  172. colorCode: '#1890FF',
  173. sortOrder: 99,
  174. status: '1',
  175. remark: '测试数据',
  176. };
  177. const addResult = await apiRequest(token, 'POST', '/pm/repairCategory', addCategoryData);
  178. if (addResult.success) {
  179. console.log(colors.green('✅ 成功'), `状态码: ${addResult.status}`);
  180. console.log(colors.gray(`响应: ${JSON.stringify(addResult.data, null, 2)}`));
  181. } else {
  182. console.log(colors.red('❌ 失败'), `状态码: ${addResult.status}`);
  183. console.log(colors.gray(`错误: ${JSON.stringify(addResult.error, null, 2)}`));
  184. }
  185. }
  186. /**
  187. * 测试物业报修工单接口
  188. */
  189. async function testRepairOrderAPIs(token) {
  190. console.log(colors.yellow('\n🔧 测试物业报修工单接口'));
  191. console.log(colors.gray('=' * 50));
  192. // 1. 生成工单编号
  193. console.log(colors.cyan('1. 生成工单编号 - GET /pm/repairOrder/generateOrderNo'));
  194. const generateResult = await apiRequest(token, 'GET', '/pm/repairOrder/generateOrderNo');
  195. let orderNo = null;
  196. if (generateResult.success) {
  197. console.log(colors.green('✅ 成功'), `状态码: ${generateResult.status}`);
  198. console.log(colors.gray(`响应: ${JSON.stringify(generateResult.data, null, 2)}`));
  199. orderNo = generateResult.data.data;
  200. } else {
  201. console.log(colors.red('❌ 失败'), `状态码: ${generateResult.status}`);
  202. console.log(colors.gray(`错误: ${JSON.stringify(generateResult.error, null, 2)}`));
  203. orderNo = `RO${new Date().toISOString().slice(0, 10).replace(/-/g, '')}001`;
  204. }
  205. // 2. 查询报修工单列表
  206. console.log(colors.cyan('\n2. 查询报修工单列表 - GET /pm/repairOrder/list'));
  207. const listResult = await apiRequest(token, 'GET', '/pm/repairOrder/list', null, {
  208. pageNum: 1,
  209. pageSize: 10,
  210. });
  211. if (listResult.success) {
  212. console.log(colors.green('✅ 成功'), `状态码: ${listResult.status}`);
  213. console.log(
  214. colors.gray(`响应: ${JSON.stringify(listResult.data, null, 2).substring(0, 300)}...`),
  215. );
  216. } else {
  217. console.log(colors.red('❌ 失败'), `状态码: ${listResult.status}`);
  218. console.log(colors.gray(`错误: ${JSON.stringify(listResult.error, null, 2)}`));
  219. }
  220. // 3. 新增报修工单
  221. console.log(colors.cyan('\n3. 新增报修工单 - POST /pm/repairOrder'));
  222. const addOrderData = {
  223. orderNo: orderNo,
  224. title: '测试报修工单 - 办公室空调故障',
  225. description: '办公室空调不制冷,温度调节失效,影响正常办公。请尽快安排维修人员检查处理。',
  226. repairLocation: '行政楼305办公室',
  227. categoryId: 1,
  228. priorityLevel: 2,
  229. expectedFinishTime: new Date(Date.now() + 24 * 60 * 60 * 1000)
  230. .toISOString()
  231. .slice(0, 19)
  232. .replace('T', ' '),
  233. reporterName: '张三',
  234. reporterPhone: '13800138001',
  235. reporterDept: '行政办公室',
  236. remark: '测试工单数据',
  237. };
  238. const addResult = await apiRequest(token, 'POST', '/pm/repairOrder', addOrderData);
  239. let createdOrderId = null;
  240. if (addResult.success) {
  241. console.log(colors.green('✅ 成功'), `状态码: ${addResult.status}`);
  242. console.log(colors.gray(`响应: ${JSON.stringify(addResult.data, null, 2)}`));
  243. // 尝试从响应中获取创建的工单ID
  244. if (addResult.data.data && typeof addResult.data.data === 'object') {
  245. createdOrderId = addResult.data.data.id || Date.now();
  246. } else {
  247. createdOrderId = Date.now();
  248. }
  249. } else {
  250. console.log(colors.red('❌ 失败'), `状态码: ${addResult.status}`);
  251. console.log(colors.gray(`错误: ${JSON.stringify(addResult.error, null, 2)}`));
  252. }
  253. // 4. 如果有创建的工单ID,测试工单详情查询
  254. if (createdOrderId) {
  255. console.log(colors.cyan(`\n4. 查询工单详情 - GET /pm/repairOrder/${createdOrderId}`));
  256. const detailResult = await apiRequest(token, 'GET', `/pm/repairOrder/${createdOrderId}`);
  257. if (detailResult.success) {
  258. console.log(colors.green('✅ 成功'), `状态码: ${detailResult.status}`);
  259. console.log(
  260. colors.gray(
  261. `响应: ${JSON.stringify(detailResult.data, null, 2).substring(0, 300)}...`,
  262. ),
  263. );
  264. } else {
  265. console.log(colors.red('❌ 失败'), `状态码: ${detailResult.status}`);
  266. console.log(colors.gray(`错误: ${JSON.stringify(detailResult.error, null, 2)}`));
  267. }
  268. }
  269. return createdOrderId;
  270. }
  271. /**
  272. * 测试工单操作接口
  273. */
  274. async function testOrderOperationAPIs(token, orderId) {
  275. if (!orderId) {
  276. console.log(colors.yellow('\n⚠️ 跳过工单操作测试(没有可用的工单ID)'));
  277. return;
  278. }
  279. console.log(colors.yellow('\n⚙️ 测试工单操作接口'));
  280. console.log(colors.gray('=' * 50));
  281. // 1. 分派工单
  282. console.log(colors.cyan(`1. 分派工单 - PUT /pm/repairOrder/${orderId}/assign`));
  283. const assignResult = await apiRequest(token, 'PUT', `/pm/repairOrder/${orderId}/assign`, null, {
  284. assigneeId: 1001,
  285. assignRemark: '分派给李师傅处理,请尽快联系报修人',
  286. });
  287. if (assignResult.success) {
  288. console.log(colors.green('✅ 成功'), `状态码: ${assignResult.status}`);
  289. console.log(colors.gray(`响应: ${JSON.stringify(assignResult.data, null, 2)}`));
  290. } else {
  291. console.log(colors.red('❌ 失败'), `状态码: ${assignResult.status}`);
  292. console.log(colors.gray(`错误: ${JSON.stringify(assignResult.error, null, 2)}`));
  293. }
  294. // 2. 开始维修
  295. console.log(colors.cyan(`\n2. 开始维修 - PUT /pm/repairOrder/${orderId}/start`));
  296. const startResult = await apiRequest(token, 'PUT', `/pm/repairOrder/${orderId}/start`);
  297. if (startResult.success) {
  298. console.log(colors.green('✅ 成功'), `状态码: ${startResult.status}`);
  299. console.log(colors.gray(`响应: ${JSON.stringify(startResult.data, null, 2)}`));
  300. } else {
  301. console.log(colors.red('❌ 失败'), `状态码: ${startResult.status}`);
  302. console.log(colors.gray(`错误: ${JSON.stringify(startResult.error, null, 2)}`));
  303. }
  304. // 3. 完成维修
  305. console.log(colors.cyan(`\n3. 完成维修 - PUT /pm/repairOrder/${orderId}/finish`));
  306. const finishResult = await apiRequest(token, 'PUT', `/pm/repairOrder/${orderId}/finish`, null, {
  307. repairResult: '已更换空调滤网,清洁空调系统,制冷功能恢复正常',
  308. usedMaterials: '空调滤网 x1, 清洁剂 x1',
  309. });
  310. if (finishResult.success) {
  311. console.log(colors.green('✅ 成功'), `状态码: ${finishResult.status}`);
  312. console.log(colors.gray(`响应: ${JSON.stringify(finishResult.data, null, 2)}`));
  313. } else {
  314. console.log(colors.red('❌ 失败'), `状态码: ${finishResult.status}`);
  315. console.log(colors.gray(`错误: ${JSON.stringify(finishResult.error, null, 2)}`));
  316. }
  317. }
  318. /**
  319. * 主测试函数
  320. */
  321. async function main() {
  322. console.log(colors.rainbow('🏢 物业报修接口测试工具'));
  323. console.log(colors.gray('=' * 60));
  324. try {
  325. // 1. 登录获取Token
  326. const token = await login();
  327. // 2. 测试物业报修分类接口
  328. await testRepairCategoryAPIs(token);
  329. // 3. 测试物业报修工单接口
  330. const orderId = await testRepairOrderAPIs(token);
  331. // 4. 测试工单操作接口
  332. await testOrderOperationAPIs(token, orderId);
  333. // 5. 测试结果总结
  334. console.log(colors.rainbow('\n🎉 物业报修接口测试完成!'));
  335. console.log(colors.green('\n✅ 测试覆盖的接口:'));
  336. console.log(colors.gray(' - 维修类型列表查询'));
  337. console.log(colors.gray(' - 维修类型树形结构'));
  338. console.log(colors.gray(' - 新增维修类型'));
  339. console.log(colors.gray(' - 生成工单编号'));
  340. console.log(colors.gray(' - 工单列表查询'));
  341. console.log(colors.gray(' - 新增报修工单'));
  342. console.log(colors.gray(' - 工单详情查询'));
  343. console.log(colors.gray(' - 分派工单'));
  344. console.log(colors.gray(' - 开始维修'));
  345. console.log(colors.gray(' - 完成维修'));
  346. console.log(colors.blue('\n💡 提示:'));
  347. console.log(colors.gray(' - 所有接口都需要Token认证'));
  348. console.log(colors.gray(' - 接口路径基于后端Controller定义'));
  349. console.log(colors.gray(' - 部分操作可能需要特定的权限码'));
  350. console.log(colors.gray(' - 建议检查后端数据库是否有相应的表结构'));
  351. } catch (error) {
  352. console.error(colors.red('\n💥 测试过程中发生错误:'), error.message);
  353. process.exit(1);
  354. }
  355. }
  356. // 运行测试
  357. if (require.main === module) {
  358. main();
  359. }
  360. module.exports = {
  361. login,
  362. apiRequest,
  363. testRepairCategoryAPIs,
  364. testRepairOrderAPIs,
  365. testOrderOperationAPIs,
  366. };