UNPKG

whistle.mock-plugins

Version:

Whistle 插件,用于快速创建 API 模拟数据

696 lines (593 loc) 23.2 kB
/** * 接口路由处理程序 * 负责处理接口相关的 API 请求 */ const express = require('express'); const fs = require('fs-extra'); const path = require('path'); const { v4: uuidv4 } = require('uuid'); const Mock = require('mockjs'); const storage = require('../storage'); // 使用存储模块定义的数据目录 const DATA_DIR = storage.DATA_DIR; // 确保数据目录存在 fs.ensureDirSync(DATA_DIR); // 处理接口数据,确保兼容多响应格式 const processInterfaceData = (data) => { const result = { ...data }; // 调试日志 console.log('=== 开始处理接口数据 ==='); console.log('原始数据:', JSON.stringify({ name: result.name, group: result.group, // 添加分组信息的日志 hasResponses: !!result.responses, responsesType: typeof result.responses, responsesLength: result.responses ? result.responses.length : 0, responses: result.responses, responseContent: !!result.responseContent, activeResponseId: result.activeResponseId }, null, 2)); // 处理 responses 数组 if (result.responses && Array.isArray(result.responses) && result.responses.length > 0) { console.log('发现 responses 数组,直接处理多响应数据'); // 确保每个响应都有合法的 id, name 和 content result.responses = result.responses.map((resp, index) => { console.log(`处理响应 ${index}:`, resp); if (!resp.id) { resp.id = Math.random().toString(36).substring(2, 15); console.log(`为响应 ${index} 添加 ID:`, resp.id); } if (!resp.name) { resp.name = `响应 ${index + 1}`; console.log(`为响应 ${index} 添加默认名称:`, resp.name); } const processedResp = { id: resp.id, name: resp.name, description: resp.description || '', content: resp.content || '{}', paramMatchers: Array.isArray(resp.paramMatchers) ? resp.paramMatchers : [] }; console.log(`处理后的响应 ${index}:`, processedResp); return processedResp; }); console.log('处理后的响应数组长度:', result.responses.length); console.log('所有处理后的响应:', result.responses); // 如果有响应但没有设置 activeResponseId,默认使用第一个 if (!result.activeResponseId) { result.activeResponseId = result.responses[0].id; console.log('设置默认激活响应:', result.activeResponseId); } // 确保 responseContent 字段始终存在(向后兼容) const activeResponse = result.activeResponseId ? result.responses.find(r => r.id === result.activeResponseId) : result.responses[0]; if (activeResponse) { result.responseContent = activeResponse.content; console.log('设置 responseContent 为当前激活响应内容'); } } else if (result.responseContent) { // 如果没有提供 responses 但有 responseContent,创建默认响应(向后兼容) console.log('创建默认响应,因为没有 responses 数组但有 responseContent'); result.responses = [{ id: Math.random().toString(36).substring(2, 15), name: '默认响应', description: '', content: result.responseContent, paramMatchers: [] }]; result.activeResponseId = result.responses[0].id; console.log('创建的默认响应:', result.responses[0]); } else { // 如果既没有 responses 也没有 responseContent,创建一个空的默认响应 console.log('创建空的默认响应'); result.responses = [{ id: Math.random().toString(36).substring(2, 15), name: '默认响应', description: '', content: '{}', paramMatchers: [] }]; result.activeResponseId = result.responses[0].id; result.responseContent = '{}'; console.log('创建的空默认响应:', result.responses[0]); } // 最终日志 console.log('=== 处理后的最终接口数据 ==='); console.log('最终数据:', JSON.stringify({ name: result.name, group: result.group, // 添加分组信息的日志 responsesLength: result.responses.length, responses: result.responses, activeResponseId: result.activeResponseId, hasResponseContent: !!result.responseContent }, null, 2)); console.log('=== 接口数据处理完成 ===\n'); return result; }; // 初始化路由 const initRouter = () => { const router = express.Router(); // 解析 JSON 请求体 router.use(express.json({ limit: '100mb' })); // 获取所有接口 router.get('/interfaces', (req, res) => { try { const { featureId } = req.query; // 读取所有功能 const featuresFile = path.join(DATA_DIR, 'features.json'); if (!fs.existsSync(featuresFile)) { return res.json({ code: 0, data: [] }); } const features = fs.readJsonSync(featuresFile); let allInterfaces = []; if (featureId) { // 获取特定功能的接口 const interfacesFile = path.join(DATA_DIR, `interfaces-${featureId}.json`); if (fs.existsSync(interfacesFile)) { const interfaces = fs.readJsonSync(interfacesFile); console.log('=== 获取接口:从文件读取的原始数据 ==='); console.log(`文件路径: ${interfacesFile}`); console.log('原始接口数据:', JSON.stringify(interfaces, null, 2)); const processedInterfaces = interfaces.map((item, index) => { console.log(`=== 处理接口 ${index}: ${item.name} ===`); console.log('处理前的接口数据:', JSON.stringify(item, null, 2)); // 确保每个接口都经过数据处理 const processed = processInterfaceData(item); console.log('处理后的接口数据:', JSON.stringify(processed, null, 2)); const feature = features.find(f => f.id === featureId); const result = { ...processed, featureName: feature ? feature.name : '未知功能' }; console.log('最终接口数据:', JSON.stringify(result, null, 2)); return result; }); allInterfaces = processedInterfaces; } } else { // 读取每个功能的接口 features.forEach(feature => { const interfacesFile = path.join(DATA_DIR, `interfaces-${feature.id}.json`); if (fs.existsSync(interfacesFile)) { const interfaces = fs.readJsonSync(interfacesFile); // 确保每个接口都经过数据处理 const processedInterfaces = interfaces.map(item => { const processed = processInterfaceData(item); return { ...processed, featureName: feature.name }; }); allInterfaces = allInterfaces.concat(processedInterfaces); } }); } console.log(`获取接口列表成功,共 ${allInterfaces.length} 个接口`); res.json({ code: 0, data: allInterfaces }); } catch (err) { console.error('获取接口列表失败:', err); res.status(500).json({ code: 1, message: '获取接口列表失败: ' + err.message }); } }); // 获取单个接口 router.get('/interfaces/:id', (req, res) => { try { // 读取所有功能 const featuresFile = path.join(DATA_DIR, 'features.json'); if (!fs.existsSync(featuresFile)) { return res.status(404).json({ code: 1, message: '接口不存在' }); } const features = fs.readJsonSync(featuresFile); let foundInterface = null; // 在每个功能的接口中查找 for (const feature of features) { const interfacesFile = path.join(DATA_DIR, `interfaces-${feature.id}.json`); if (fs.existsSync(interfacesFile)) { const interfaces = fs.readJsonSync(interfacesFile); const found = interfaces.find(item => item.id === req.params.id); if (found) { // 确保数据经过处理 const processed = processInterfaceData(found); foundInterface = { ...processed, featureName: feature.name }; break; } } } if (!foundInterface) { return res.status(404).json({ code: 1, message: '接口不存在' }); } console.log(`获取接口详情成功: ${foundInterface.name},响应数量: ${foundInterface.responses ? foundInterface.responses.length : 0}`); res.json({ code: 0, data: foundInterface }); } catch (err) { console.error('获取接口详情失败:', err); res.status(500).json({ code: 1, message: '获取接口详情失败: ' + err.message }); } }); // 创建接口 router.post('/interfaces', (req, res) => { try { const interfaceData = req.body; if (!interfaceData.featureId || !interfaceData.urlPattern) { return res.status(400).json({ code: 1, message: '功能ID和URL匹配规则为必填项' }); } // 检查功能是否存在 const featuresFile = path.join(DATA_DIR, 'features.json'); if (!fs.existsSync(featuresFile)) { return res.status(404).json({ code: 1, message: '功能不存在' }); } const features = fs.readJsonSync(featuresFile); const feature = features.find(f => f.id === interfaceData.featureId); if (!feature) { return res.status(404).json({ code: 1, message: '功能不存在' }); } // 读取接口列表 const interfacesFile = path.join(DATA_DIR, `interfaces-${interfaceData.featureId}.json`); const interfaces = fs.existsSync(interfacesFile) ? fs.readJsonSync(interfacesFile) : []; // 处理接口数据,确保兼容多响应格式 console.log('=== 创建接口:开始处理数据 ==='); console.log('接收到的接口数据:', JSON.stringify(interfaceData, null, 2)); const processedData = processInterfaceData(interfaceData); console.log('处理后的接口数据:', JSON.stringify(processedData, null, 2)); // 创建新接口 const newInterface = { id: uuidv4(), ...processedData, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; console.log('=== 创建接口:准备保存的完整接口数据 ==='); console.log('完整的新接口数据:', JSON.stringify(newInterface, null, 2)); interfaces.push(newInterface); // 保存接口列表 fs.writeJsonSync(interfacesFile, interfaces, { spaces: 2 }); console.log('接口数据已保存到文件:', interfacesFile); // 验证保存后从文件读取的数据 const savedInterfaces = fs.readJsonSync(interfacesFile); const savedInterface = savedInterfaces.find(item => item.id === newInterface.id); console.log('=== 创建接口:验证保存后的数据 ==='); console.log('从文件读取的保存后数据:', JSON.stringify(savedInterface, null, 2)); // 更新功能模块的接口数量 const featureIndex = features.findIndex(f => f.id === interfaceData.featureId); features[featureIndex].interfaceCount = interfaces.length; features[featureIndex].updatedAt = new Date().toISOString(); fs.writeJsonSync(featuresFile, features, { spaces: 2 }); console.log('=== 创建接口:准备返回的数据 ==='); console.log('返回给前端的数据:', JSON.stringify(newInterface, null, 2)); res.json({ code: 0, message: '接口创建成功', data: newInterface }); } catch (err) { console.error('创建接口失败:', err); res.status(500).json({ code: 1, message: '创建接口失败: ' + err.message }); } }); // 更新接口 router.put('/interfaces', (req, res) => { try { const { id } = req.query; const interfaceData = req.body; if (!id) { return res.status(400).json({ code: 1, message: '接口ID为必填项' }); } if (!interfaceData.featureId) { return res.status(400).json({ code: 1, message: '功能ID为必填项' }); } // 检查功能是否存在 const featuresFile = path.join(DATA_DIR, 'features.json'); if (!fs.existsSync(featuresFile)) { return res.status(404).json({ code: 1, message: '功能不存在' }); } const features = fs.readJsonSync(featuresFile); const feature = features.find(f => f.id === interfaceData.featureId); if (!feature) { return res.status(404).json({ code: 1, message: '功能不存在' }); } // 读取接口列表 const interfacesFile = path.join(DATA_DIR, `interfaces-${interfaceData.featureId}.json`); if (!fs.existsSync(interfacesFile)) { return res.status(404).json({ code: 1, message: '接口不存在' }); } const interfaces = fs.readJsonSync(interfacesFile); const index = interfaces.findIndex(item => item.id === id); if (index === -1) { return res.status(404).json({ code: 1, message: '接口不存在' }); } // 处理接口数据,确保兼容多响应格式 console.log('=== 更新接口:开始处理数据 ==='); console.log('接收到的接口数据:', JSON.stringify(interfaceData, null, 2)); const processedData = processInterfaceData(interfaceData); console.log('处理后的接口数据:', JSON.stringify(processedData, null, 2)); // 更新接口 const updatedInterface = { ...interfaces[index], ...processedData, updatedAt: new Date().toISOString() }; console.log('=== 更新接口:准备保存的完整接口数据 ==='); console.log('完整的更新接口数据:', JSON.stringify(updatedInterface, null, 2)); interfaces[index] = updatedInterface; // 保存接口列表 fs.writeJsonSync(interfacesFile, interfaces, { spaces: 2 }); console.log('接口数据已保存到文件:', interfacesFile); // 验证保存后从文件读取的数据 const savedInterfaces = fs.readJsonSync(interfacesFile); const savedInterface = savedInterfaces.find(item => item.id === id); console.log('=== 更新接口:验证保存后的数据 ==='); console.log('从文件读取的保存后数据:', JSON.stringify(savedInterface, null, 2)); console.log('=== 更新接口:准备返回的数据 ==='); console.log('返回给前端的数据:', JSON.stringify(updatedInterface, null, 2)); res.json({ code: 0, message: '接口更新成功', data: updatedInterface }); } catch (err) { console.error('更新接口失败:', err); res.status(500).json({ code: 1, message: '更新接口失败: ' + err.message }); } }); // 删除接口 router.delete('/interfaces/:id', (req, res) => { try { // 读取所有功能 const featuresFile = path.join(DATA_DIR, 'features.json'); if (!fs.existsSync(featuresFile)) { return res.status(404).json({ error: '接口不存在' }); } const features = fs.readJsonSync(featuresFile); let found = false; // 在每个功能的接口中查找并删除 for (const feature of features) { const interfacesFile = path.join(DATA_DIR, `interfaces-${feature.id}.json`); if (fs.existsSync(interfacesFile)) { const interfaces = fs.readJsonSync(interfacesFile); const index = interfaces.findIndex(item => item.id === req.params.id); if (index !== -1) { interfaces.splice(index, 1); fs.writeJsonSync(interfacesFile, interfaces, { spaces: 2 }); found = true; break; } } } if (!found) { return res.status(404).json({ error: '接口不存在' }); } res.json({ success: true, message: '接口已删除' }); } catch (err) { console.error('删除接口失败:', err); res.status(500).json({ error: '删除接口失败' }); } }); // 测试接口 router.post('/interfaces/:id/test', (req, res) => { try { // 读取所有功能 const featuresFile = path.join(DATA_DIR, 'features.json'); if (!fs.existsSync(featuresFile)) { return res.status(404).json({ error: '接口不存在' }); } const features = fs.readJsonSync(featuresFile); let foundInterface = null; // 在每个功能的接口中查找 for (const feature of features) { const interfacesFile = path.join(DATA_DIR, `interfaces-${feature.id}.json`); if (fs.existsSync(interfacesFile)) { const interfaces = fs.readJsonSync(interfacesFile); const found = interfaces.find(item => item.id === req.params.id); if (found) { foundInterface = found; break; } } } if (!foundInterface) { return res.status(404).json({ error: '接口不存在' }); } // 使用 Mock.js 生成响应数据 let responseBody = foundInterface.responseBody; if (typeof responseBody === 'string') { try { responseBody = JSON.parse(responseBody); } catch (e) { return res.status(400).json({ error: '响应体JSON格式错误' }); } } const mockData = Mock.mock(responseBody); res.json({ original: foundInterface.responseBody, mocked: mockData }); } catch (err) { console.error('测试接口失败:', err); res.status(500).json({ error: '测试接口失败' }); } }); // 批量更新接口状态 router.patch('/interfaces', (req, res) => { try { const { active } = req.query; const { featureId, group } = req.body; if (active === undefined) { return res.status(400).json({ code: 1, message: '状态参数为必填项' }); } if (!featureId) { return res.status(400).json({ code: 1, message: '功能ID为必填项' }); } // 检查功能是否存在 const featuresFile = path.join(DATA_DIR, 'features.json'); if (!fs.existsSync(featuresFile)) { return res.status(404).json({ code: 1, message: '功能不存在' }); } const features = fs.readJsonSync(featuresFile); const feature = features.find(f => f.id === featureId); if (!feature) { return res.status(404).json({ code: 1, message: '功能不存在' }); } // 读取接口列表 const interfacesFile = path.join(DATA_DIR, `interfaces-${featureId}.json`); if (!fs.existsSync(interfacesFile)) { return res.status(404).json({ code: 1, message: '接口列表不存在' }); } const interfaces = fs.readJsonSync(interfacesFile); // 根据分组筛选要更新的接口 let updatedCount = 0; const updatedInterfaces = interfaces.map(item => { // 如果指定了分组,只更新该分组下的接口 if (group && item.group !== group) { return item; } // 更新接口状态 if (item.active !== (active === 'true')) { updatedCount++; return { ...item, active: active === 'true', updatedAt: new Date().toISOString() }; } return item; }); // 保存更新后的接口列表 fs.writeJsonSync(interfacesFile, updatedInterfaces, { spaces: 2 }); res.json({ code: 0, message: `成功${active === 'true' ? '启用' : '禁用'} ${updatedCount} 个接口`, data: { updatedCount } }); } catch (err) { console.error('批量更新接口状态失败:', err); res.status(500).json({ code: 1, message: '批量更新接口状态失败: ' + err.message }); } }); // 部分更新接口 router.patch('/interfaces/:id', (req, res) => { try { const { id } = req.params; const updateData = req.body; if (!id) { return res.status(400).json({ code: 1, message: '接口ID为必填项' }); } // 检查功能是否存在 const featuresFile = path.join(DATA_DIR, 'features.json'); if (!fs.existsSync(featuresFile)) { return res.status(404).json({ code: 1, message: '功能不存在' }); } const features = fs.readJsonSync(featuresFile); // 在所有功能模块中查找接口 let foundInterface = null; let foundFeature = null; let foundInterfaceIndex = -1; let interfacesFile = ''; for (const feature of features) { const featureInterfacesFile = path.join(DATA_DIR, `interfaces-${feature.id}.json`); if (fs.existsSync(featureInterfacesFile)) { const interfaces = fs.readJsonSync(featureInterfacesFile); const index = interfaces.findIndex(item => item.id === id); if (index !== -1) { foundInterface = interfaces[index]; foundFeature = feature; foundInterfaceIndex = index; interfacesFile = featureInterfacesFile; break; } } } if (!foundInterface) { return res.status(404).json({ code: 1, message: '接口不存在' }); } // 读取接口所在文件的所有接口 const interfaces = fs.readJsonSync(interfacesFile); // 更新接口 const updatedInterface = { ...foundInterface, ...updateData, updatedAt: new Date().toISOString() }; interfaces[foundInterfaceIndex] = updatedInterface; // 保存更新后的接口列表 fs.writeJsonSync(interfacesFile, interfaces, { spaces: 2 }); res.json({ code: 0, message: '接口更新成功', data: updatedInterface }); } catch (err) { console.error('更新接口失败:', err); res.status(500).json({ code: 1, message: '更新接口失败: ' + err.message }); } }); return router; }; module.exports = initRouter;