const express = require('express'); const bodyParser = require('body-parser'); const Mock = require('mockjs'); const fs = require('fs-extra'); const dayjs = require('dayjs'); const swaggerUi = require('swagger-ui-express'); const swaggerJSDoc = require('swagger-jsdoc'); const { Low, JSONFile } = require('lowdb'); const app = express(); const PORT = 3000; const CONFIG_PATH = './mock-config.json'; const DB_PATH = './mock-db.json'; app.use(bodyParser.json()); function formatResponse(data, code = 200, message = 'success') { return { code, type: code === 200 ? 'success' : 'error', message, result: data, extras: null, time: dayjs().format('YYYY-MM-DD HH:mm:ss') }; } function registerMockApi(config) { const { method, path, response } = config; const methodName = method.toLowerCase(); app[methodName](path, async (req, res) => { try { // 地址模块逻辑 if (path === '/api/user/address/list') { await global.db.read(); return res.json(formatResponse({ list: global.db.data.addresses })); } if (path === '/api/user/address/add') { const newItem = { id: Date.now(), ...req.body }; await global.db.read(); global.db.data.addresses.push(newItem); await global.db.write(); return res.json(formatResponse({ id: newItem.id }, 200, '地址新增成功')); } if (path === '/api/user/address/update') { const { id, ...rest } = req.body; await global.db.read(); const item = global.db.data.addresses.find(i => i.id == id); if (item) Object.assign(item, rest); await global.db.write(); return res.json(formatResponse(null, 200, '地址已更新')); } if (path === '/api/user/address/delete') { const { id } = req.body; await global.db.read(); global.db.data.addresses = global.db.data.addresses.filter(i => i.id != id); await global.db.write(); return res.json(formatResponse(null, 200, '地址已删除')); } if (path === '/api/user/address/set-default') { const { id } = req.body; await global.db.read(); global.db.data.addresses.forEach(i => i.isDefault = i.id == id); await global.db.write(); return res.json(formatResponse(null, 200, '已设置为默认地址')); } if (path === '/api/user/address/detail') { const { id } = req.query; await global.db.read(); const item = global.db.data.addresses.find(i => i.id == id); return res.json(formatResponse(item || null)); } // 其他静态 mock 响应 const data = Mock.mock(response); res.json(formatResponse(data)); } catch (err) { res.json(formatResponse(null, 500, err.message)); } }); } async function startServer() { // 初始化 lowdb const adapter = new JSONFile(DB_PATH); const db = new Low(adapter); await db.read(); db.data ||= { addresses: [], products: [], orders: [] }; await db.write(); global.db = db; // 加载配置文件 let mockConfigs = fs.existsSync(CONFIG_PATH) ? fs.readJsonSync(CONFIG_PATH) : []; global.mockConfigs = mockConfigs; mockConfigs.forEach(registerMockApi); // 添加 mock 接口 app.post('/mock-api/add', async (req, res) => { const config = req.body; if (!config.method || !config.path || !config.response) { return res.json(formatResponse(null, 400, '参数不完整')); } registerMockApi(config); global.mockConfigs.push(config); await fs.writeJson(CONFIG_PATH, global.mockConfigs, { spaces: 2 }); res.json(formatResponse({ path: config.path }, 200, '接口已添加')); }); app.get('/mock-api/list', (req, res) => { res.json(formatResponse(global.mockConfigs)); }); app.get('/mock-api/routes', (req, res) => { const list = global.mockConfigs.map(({ method, path }) => ({ method, path })); res.json(list); }); const swaggerDefinition = { openapi: '3.0.0', info: { title: 'Mock API Docs', version: '1.0.0', description: 'Dynamically registered mock APIs' }, servers: [] }; function buildSwaggerSpecFromMocks(configs) { const paths = {}; configs.forEach(({ method, path, swagger }) => { const cleanPath = path.replace(/:([^/]+)/g, '{$1}'); const methodName = method.toLowerCase(); const summary = swagger?.summary || `Mocked ${method} ${path}`; const description = swagger?.description || ''; const responses = swagger?.responses || { 200: { description: 'Mock response', content: { 'application/json': { schema: { type: 'object' } } } } }; paths[cleanPath] = paths[cleanPath] || {}; paths[cleanPath][methodName] = { summary, description, responses }; }); return { ...swaggerDefinition, paths }; } app.use('/swagger', (req, res, next) => { res.setHeader('Cache-Control', 'no-store'); next(); }); app.use('/swagger', swaggerUi.serve, (req, res, next) => { const protocol = req.headers['x-forwarded-proto'] || req.protocol; const host = req.get('host'); const dynamicSwaggerDefinition = { ...swaggerDefinition, servers: [ { url: `${protocol}://${host}`, description: 'Current host' } ] }; const spec = buildSwaggerSpecFromMocks(global.mockConfigs); const dynamicSpec = { ...spec, servers: dynamicSwaggerDefinition.servers }; swaggerUi.setup(dynamicSpec)(req, res, next); }); app.listen(PORT, () => { console.log(`✅ Mock Server is running at http://localhost:${PORT}`); console.log(`📚 Swagger UI available at http://localhost:${PORT}/swagger`); }); } startServer();