168 lines
4.7 KiB
JavaScript
168 lines
4.7 KiB
JavaScript
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 app = express();
|
|
const PORT = 3002;
|
|
const CONFIG_PATH = './mock-config.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, query, swagger } = config;
|
|
const methodName = method.toLowerCase();
|
|
|
|
app[methodName](path, async (req, res, next) => {
|
|
try {
|
|
console.log(`[MOCK] Incoming: ${req.method} ${req.path}`, req.query);
|
|
|
|
// 默认 fallback
|
|
if (path === '/api/mall/recommend' && Object.keys(req.query).length === 0) {
|
|
const defaultConfig = global.mockConfigs.find(c =>
|
|
c.path === path && c.query?.type === 'home'
|
|
);
|
|
if (defaultConfig) {
|
|
console.log(`[MOCK] Fallback to default (home)`);
|
|
return res.json(formatResponse(defaultConfig.response));
|
|
}
|
|
}
|
|
if (path === '/api/nursing/hospital-companion/list' && Object.keys(req.query).length === 0) {
|
|
const defaultConfig = global.mockConfigs.find(c =>
|
|
c.path === path && c.query?.type === 'home'
|
|
);
|
|
if (defaultConfig) {
|
|
console.log(`[MOCK] Fallback to default (home)`);
|
|
return res.json(formatResponse(defaultConfig.response));
|
|
}
|
|
}
|
|
|
|
if (path === '/api/lesson/list' && Object.keys(req.query).length === 0) {
|
|
const defaultConfig = global.mockConfigs.find(c =>
|
|
c.path === path
|
|
);
|
|
if (defaultConfig) {
|
|
console.log(`[MOCK] Fallback to default (home)`);
|
|
return res.json(formatResponse(defaultConfig.response));
|
|
}
|
|
}
|
|
|
|
if (path === '/api/lesson/catalog' && Object.keys(req.query).length === 0) {
|
|
const defaultConfig = global.mockConfigs.find(c =>
|
|
c.path === path
|
|
);
|
|
if (defaultConfig) {
|
|
console.log(`[MOCK] Fallback to default (home)`);
|
|
return res.json(formatResponse(defaultConfig.response));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// 精确 query 匹配
|
|
if (query) {
|
|
const isMatch = Object.entries(query).every(([key, val]) => req.query[key] === val);
|
|
if (!isMatch) return next(); // 继续往下找
|
|
}
|
|
|
|
return res.json(formatResponse(response));
|
|
} catch (err) {
|
|
res.json(formatResponse(null, 500, err.message));
|
|
}
|
|
});
|
|
}
|
|
|
|
async function startServer() {
|
|
|
|
|
|
// 读取配置
|
|
let mockConfigs = fs.existsSync(CONFIG_PATH) ? fs.readJsonSync(CONFIG_PATH) : [];
|
|
global.mockConfigs = mockConfigs;
|
|
|
|
// 注册接口
|
|
mockConfigs.forEach(registerMockApi);
|
|
|
|
|
|
|
|
// swagger 文档支持
|
|
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,
|
|
parameters: swagger?.parameters || [],
|
|
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();
|