whistle.mock-plugins
Version:
Whistle 插件,用于快速创建 API 模拟数据
533 lines (467 loc) • 15.4 kB
JavaScript
/**
* 数据管理模块,负责功能和接口数据的存储和检索
*/
const fs = require('fs-extra');
const path = require('path');
const storage = require('./storage');
// 缓存启用的接口列表
let enabledInterfacesCache = null;
let lastCacheTime = 0;
// 缓存过期时间(毫秒)
const CACHE_TTL = 5000; // 5秒
const dataManager = {
/**
* 初始化数据管理器
* @param {object} options 配置选项,包含 baseDir 和 log
*/
init(options = {}) {
this.baseDir = options.baseDir || storage.DATA_DIR;
this.featuresFile = path.join(this.baseDir, 'features.json');
this.interfacesFile = path.join(this.baseDir, 'interfaces.json');
this.logsFile = path.join(this.baseDir, 'logs.json');
this.log = options.log || console.log;
this.log(`数据管理器初始化, 基础目录: ${this.baseDir}`);
this.log(`功能配置文件: ${this.featuresFile}`);
this.log(`接口配置文件: ${this.interfacesFile}`);
this.log(`日志文件: ${this.logsFile}`);
// 确保目录存在
try {
fs.ensureDirSync(this.baseDir);
this.log(`确保目录存在: ${this.baseDir}`);
} catch (err) {
this.log(`创建目录失败: ${err.message}`);
console.error('创建目录失败:', err);
}
// 初始化文件
this.ensureFilesExist();
},
// 确保配置文件存在
ensureFilesExist() {
try {
if (!fs.existsSync(this.featuresFile)) {
fs.writeJsonSync(this.featuresFile, { features: [] }, { spaces: 2 });
this.log(`创建功能配置文件: ${this.featuresFile}`);
}
if (!fs.existsSync(this.interfacesFile)) {
fs.writeJsonSync(this.interfacesFile, { interfaces: [] }, { spaces: 2 });
this.log(`创建接口配置文件: ${this.interfacesFile}`);
}
if (!fs.existsSync(this.logsFile)) {
fs.writeJsonSync(this.logsFile, { logs: [] }, { spaces: 2 });
this.log(`创建日志文件: ${this.logsFile}`);
}
} catch (err) {
this.log(`确保文件存在失败: ${err.message}`);
console.error('确保文件存在失败:', err);
}
},
/**
* 记录请求日志
* @param {object} logData 日志数据
*/
logRequest(logData) {
try {
// 首先检查参数是否有效,避免不必要的文件操作
if (!logData || typeof logData !== 'object') {
this.log(`记录请求日志失败: 无效的日志数据`);
return false;
}
// 确保日志文件存在
if (!fs.existsSync(this.logsFile)) {
fs.writeJsonSync(this.logsFile, { logs: [] }, { spaces: 2 });
}
// 读取现有日志
const logsData = fs.readJsonSync(this.logsFile);
// 确保日志结构正确
if (!logsData.logs) {
logsData.logs = [];
}
// 标准化日志数据,确保同时包含type和eventType字段
const standardizedLogData = {
...logData,
// 确保type和eventType都存在,前端可能使用eventType进行过滤
eventType: logData.eventType || logData.type || 'unknown',
type: logData.type || logData.eventType || 'unknown',
// 如果缺少必要字段,提供默认值,确保前端过滤不出错
url: logData.url || '',
method: logData.method || '',
message: logData.message || '',
status: logData.status || '',
pattern: logData.pattern || ''
};
// 添加ID和时间戳
const logEntry = {
...standardizedLogData,
id: Date.now().toString(),
timestamp: logData.timestamp || new Date().toISOString()
};
// 将新日志添加到前面
logsData.logs.unshift(logEntry);
// 限制日志数量,保留最新的5000条
if (logsData.logs.length > 5000) {
logsData.logs = logsData.logs.slice(0, 5000);
}
// 保存日志
fs.writeJsonSync(this.logsFile, logsData, { spaces: 2 });
this.log(`记录请求日志: ${logEntry.method} ${logEntry.url} (${logEntry.type})`);
return true;
} catch (err) {
this.log(`记录请求日志失败: ${err.message}`);
console.error('记录请求日志失败:', err);
return false;
}
},
/**
* 获取请求日志
* @param {object} options 查询选项
* @param {number} options.limit 返回日志的最大数量
* @param {string} options.type 日志类型
* @returns {Promise<Array>} 日志列表
*/
async getLogs(options = {}) {
try {
// 确保日志文件存在
if (!fs.existsSync(this.logsFile)) {
fs.writeJsonSync(this.logsFile, { logs: [] }, { spaces: 2 });
return [];
}
// 读取日志
const logsData = await fs.readJson(this.logsFile);
if (!logsData.logs) {
return [];
}
let logs = logsData.logs;
// 根据类型过滤
if (options.type) {
logs = logs.filter(log => log.type === options.type);
}
// 限制返回数量
if (options.limit && options.limit > 0) {
logs = logs.slice(0, options.limit);
}
return logs;
} catch (err) {
this.log(`获取日志失败: ${err.message}`);
console.error('获取日志失败:', err);
return [];
}
},
/**
* 清空日志
* @returns {Promise<boolean>} 是否成功
*/
async clearLogs() {
try {
fs.writeJsonSync(this.logsFile, { logs: [] }, { spaces: 2 });
this.log('日志已清空');
return true;
} catch (err) {
this.log(`清空日志失败: ${err.message}`);
console.error('清空日志失败:', err);
return false;
}
},
/**
* 获取所有功能列表
* @returns {Promise<Array>} 功能列表
*/
async getFeatures() {
try {
this.log('尝试读取功能配置...');
// 如果文件不存在,创建
if (!fs.existsSync(this.featuresFile)) {
this.log(`功能配置文件不存在,创建空文件`);
fs.writeJsonSync(this.featuresFile, { features: [] }, { spaces: 2 });
}
const data = await fs.readJson(this.featuresFile);
this.log(`成功读取功能配置, 发现 ${data.features ? data.features.length : 0} 个功能`);
return data.features || [];
} catch (err) {
this.log(`读取功能配置失败: ${err.message}`);
console.error('读取功能配置失败:', err);
return [];
}
},
/**
* 获取单个功能
* @param {string} id 功能ID
* @returns {Promise<object>} 功能对象
*/
async getFeature(id) {
return new Promise((resolve, reject) => {
this.db.features.findOne({ _id: id }, (err, doc) => {
if (err) return reject(err);
resolve(doc);
});
});
},
/**
* 创建新功能
* @param {object} feature 功能对象
* @returns {Promise<object>} 创建后的功能对象
*/
async createFeature(feature) {
const now = new Date();
const newFeature = {
...feature,
createdAt: now,
updatedAt: now,
};
return new Promise((resolve, reject) => {
this.db.features.insert(newFeature, (err, doc) => {
if (err) return reject(err);
resolve(doc);
});
});
},
/**
* 更新功能
* @param {string} id 功能ID
* @param {object} update 要更新的字段
* @returns {Promise<object>} 更新后的功能对象
*/
async updateFeature(id, update) {
const updateObj = {
...update,
updatedAt: new Date(),
};
return new Promise((resolve, reject) => {
this.db.features.update(
{ _id: id },
{ $set: updateObj },
{ returnUpdatedDocs: true },
(err, numAffected, affectedDocuments) => {
if (err) return reject(err);
resolve(affectedDocuments);
}
);
});
},
/**
* 删除功能
* @param {string} id 功能ID
* @returns {Promise<number>} 影响的行数
*/
async deleteFeature(id) {
// 首先删除该功能下的所有接口
await this.deleteInterfacesByFeature(id);
return new Promise((resolve, reject) => {
this.db.features.remove({ _id: id }, {}, (err, numRemoved) => {
if (err) return reject(err);
resolve(numRemoved);
});
});
},
/**
* 获取功能下的所有接口
* @param {string} featureId 功能ID
* @returns {Promise<Array>} 接口列表
*/
async getInterfaces(featureId) {
return new Promise((resolve, reject) => {
this.db.interfaces.find({ featureId }).sort({ updatedAt: -1 }).exec((err, docs) => {
if (err) return reject(err);
resolve(docs);
});
});
},
/**
* 获取所有启用的接口
* @returns {Promise<Array>} 启用状态的接口列表
*/
async getEnabledInterfaces() {
const now = Date.now();
// 如果缓存存在且未过期,直接返回缓存
if (enabledInterfacesCache && (now - lastCacheTime < CACHE_TTL)) {
this.log('使用已启用接口的缓存');
return enabledInterfacesCache;
}
try {
// 1. 获取所有启用的功能模块ID
const features = await this.getFeatures();
const enabledFeatureIds = features
.filter(feature => feature && feature.active === true)
.map(feature => feature.id);
this.log(`找到 ${enabledFeatureIds.length} 个启用的功能模块`);
// 2. 读取所有接口数据
const data = await this.getInterfaces();
// 3. 过滤出启用状态的接口,同时检查功能模块是否启用
const enabledInterfaces = data.filter(item => {
// 首先检查接口本身是否启用
const isInterfaceEnabled = item.enabled === true || item.active === true;
if (!isInterfaceEnabled) {
return false;
}
// 如果接口有关联功能,检查功能是否启用
if (item.featureId) {
return enabledFeatureIds.includes(item.featureId);
}
// 如果接口没有关联功能,只要接口本身启用就可以
return true;
});
this.log(`找到 ${enabledInterfaces.length} 个启用的接口(已过滤功能模块状态)`);
// 更新缓存
enabledInterfacesCache = enabledInterfaces;
lastCacheTime = now;
return enabledInterfaces;
} catch (err) {
this.log(`获取启用接口失败: ${err.message}`);
console.error('获取启用接口失败:', err);
return [];
}
},
/**
* 使缓存失效,强制下次请求重新加载
*/
invalidateCache() {
enabledInterfacesCache = null;
lastCacheTime = 0;
},
/**
* 获取单个接口
* @param {string} id 接口ID
* @returns {Promise<object>} 接口对象
*/
async getInterface(id) {
return new Promise((resolve, reject) => {
this.db.interfaces.findOne({ _id: id }, (err, doc) => {
if (err) return reject(err);
resolve(doc);
});
});
},
/**
* 创建新接口
* @param {object} interfaceObj 接口对象
* @returns {Promise<object>} 创建后的接口对象
*/
async createInterface(interfaceObj) {
const now = new Date();
const newInterface = {
...interfaceObj,
createdAt: now,
updatedAt: now,
};
return new Promise((resolve, reject) => {
this.db.interfaces.insert(newInterface, (err, doc) => {
if (err) return reject(err);
resolve(doc);
});
});
},
/**
* 更新接口
* @param {string} id 接口ID
* @param {object} update 要更新的字段
* @returns {Promise<object>} 更新后的接口对象
*/
async updateInterface(id, update) {
const updateObj = {
...update,
updatedAt: new Date(),
};
return new Promise((resolve, reject) => {
this.db.interfaces.update(
{ _id: id },
{ $set: updateObj },
{ returnUpdatedDocs: true },
(err, numAffected, affectedDocuments) => {
if (err) return reject(err);
resolve(affectedDocuments);
}
);
});
},
/**
* 删除接口
* @param {string} id 接口ID
* @returns {Promise<number>} 影响的行数
*/
async deleteInterface(id) {
return new Promise((resolve, reject) => {
this.db.interfaces.remove({ _id: id }, {}, (err, numRemoved) => {
if (err) return reject(err);
resolve(numRemoved);
});
});
},
/**
* 删除功能下的所有接口
* @param {string} featureId 功能ID
* @returns {Promise<number>} 影响的行数
*/
async deleteInterfacesByFeature(featureId) {
return new Promise((resolve, reject) => {
this.db.interfaces.remove({ featureId }, { multi: true }, (err, numRemoved) => {
if (err) return reject(err);
resolve(numRemoved);
});
});
},
// 保存所有特性
async saveFeatures(features) {
try {
this.log(`保存功能配置, 共 ${features.length} 个功能`);
await fs.writeJson(this.featuresFile, { features }, { spaces: 2 });
this.log('功能配置保存成功');
return true;
} catch (err) {
this.log(`保存功能配置失败: ${err.message}`);
console.error('保存功能配置失败:', err);
return false;
}
},
// 获取所有接口
async getInterfaces() {
try {
this.log('尝试读取接口配置...');
// 如果文件不存在,创建
if (!fs.existsSync(this.interfacesFile)) {
this.log(`接口配置文件不存在,创建空文件`);
fs.writeJsonSync(this.interfacesFile, { interfaces: [] }, { spaces: 2 });
}
const data = await fs.readJson(this.interfacesFile);
this.log(`成功读取接口配置, 发现 ${data.interfaces ? data.interfaces.length : 0} 个接口`);
return data.interfaces || [];
} catch (err) {
this.log(`读取接口配置失败: ${err.message}`);
console.error('读取接口配置失败:', err);
return [];
}
},
// 保存所有接口
async saveInterfaces(interfaces) {
try {
this.log(`保存接口配置, 共 ${interfaces.length} 个接口`);
await fs.writeJson(this.interfacesFile, { interfaces }, { spaces: 2 });
this.log('接口配置保存成功');
return true;
} catch (err) {
this.log(`保存接口配置失败: ${err.message}`);
console.error('保存接口配置失败:', err);
return false;
}
},
/**
* 获取所有启用的特性
* @returns {Promise<Array>} 启用的特性列表
*/
async getEnabledFeatures() {
try {
const features = await this.getFeatures();
// 确保features是数组
if (!Array.isArray(features)) {
this.log(`获取启用的功能失败: features不是数组`);
return [];
}
const enabledFeatures = features.filter(feature => feature && typeof feature === 'object' && feature.active);
this.log(`获取启用的功能, 共 ${enabledFeatures.length} 个`);
return enabledFeatures;
} catch (err) {
this.log(`获取启用的功能失败: ${err.message}`);
console.error('获取启用的功能失败:', err);
return [];
}
}
};
module.exports = dataManager;