增加 mock json
parent
8240a52a98
commit
2554bf8441
|
|
@ -13,6 +13,8 @@ RUN npm install
|
|||
# 拷贝项目文件
|
||||
COPY . .
|
||||
|
||||
# 启动服务(默认端口为 3000)
|
||||
# 暴露端口
|
||||
EXPOSE 3000
|
||||
|
||||
# 启动服务
|
||||
CMD ["node", "server.js"]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
mock-server:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: mock-server
|
||||
ports:
|
||||
- "3002:3000"
|
||||
volumes:
|
||||
- .:/app
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
restart: unless-stopped
|
||||
150
mock-config.json
150
mock-config.json
|
|
@ -33,17 +33,153 @@
|
|||
{
|
||||
"method": "GET",
|
||||
"path": "/api/mall/recommend",
|
||||
"query": { "type": "1" },
|
||||
"response": {
|
||||
"products|4": [
|
||||
"products": [
|
||||
{
|
||||
"id|+1": 1001,
|
||||
"name": "@pick(['老年精纺防尿湿纸尿裤 医用护理','防摔防滑地垫 卫生间适老化改造专用','多功能床边扶手 起夜支撑辅助器','老人专用加厚棉拖鞋'])",
|
||||
"price": "@float(39, 9999, 2, 2)",
|
||||
"image": "@image('200x200', '#f4f4f4', '商品图')",
|
||||
"soldCount": "@natural(50, 999)"
|
||||
"id": 1,
|
||||
"name": "老年精纺防尿湿纸尿裤 医用护理",
|
||||
"price": 120.0,
|
||||
"soldCount": 73,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/1.png"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "防摔防滑地垫 卫生间适老化改造专用",
|
||||
"price": 999.0,
|
||||
"soldCount": 999,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/2.png"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "多功能床边扶手 起夜支撑辅助器",
|
||||
"price": 888.9,
|
||||
"soldCount": 73,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/3.png"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "老人专用加厚棉拖鞋 老人专用加厚棉拖...",
|
||||
"price": 129.0,
|
||||
"soldCount": 73,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/4.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/api/mall/recommend",
|
||||
"query": { "type": "2" },
|
||||
"response": {
|
||||
"products": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "防脱育发洗发水增密何发首乌生姜洗发液",
|
||||
"price": 120.0,
|
||||
"soldCount": 73,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/safe/1.png"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "护理老人卧床病人用洗头神器",
|
||||
"price": 9999.0,
|
||||
"soldCount": 999,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/safe/2.png"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "硫磺液体香皂洗脸洗澡洗头祛痘止痒",
|
||||
"price": 888.9,
|
||||
"soldCount": 73,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/safe/3.png"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "防脱控油蓬松腺苷育发赋活洗发水",
|
||||
"price": 120.0,
|
||||
"soldCount": 73,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/safe/4.png"
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/api/mall/recommend",
|
||||
"query": { "type": "3" },
|
||||
"response": {
|
||||
"products": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "轻奢碳纤维轻便登山拐杖老人防滑拐",
|
||||
"price": 120.00,
|
||||
"soldCount": 73,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/travel/1.png"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "雅德助行器坐板累了可坐 可洗澡防水",
|
||||
"price": 9999.00,
|
||||
"soldCount": 999,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/travel/2.png"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "买菜小拉车快递车带盖轻便可折叠",
|
||||
"price": 888.90,
|
||||
"soldCount": 73,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/travel/3.png"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "老人拐杖凳子折叠便携多功能",
|
||||
"price": 120.00,
|
||||
"soldCount": 73,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/travel/4.png"
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/api/mall/recommend",
|
||||
"query": { "type": "4" },
|
||||
"response": {
|
||||
"products": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "防脱育发洗发水增密何发首乌生姜洗发液",
|
||||
"price": 120.00,
|
||||
"soldCount": 73,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/care/1.png"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "护理老人卧床病人用洗头神器",
|
||||
"price": 9999.00,
|
||||
"soldCount": 999,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/care/2.png"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "硫磺液体香皂洗脸洗澡洗头祛痘止痒",
|
||||
"price": 888.90,
|
||||
"soldCount": 73,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/care/3.png"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "防脱控油蓬松腺苷育发赋活洗发水",
|
||||
"price": 120.00,
|
||||
"soldCount": 73,
|
||||
"image": "https://res.klyj.jinzejk.com/mall/care/4.png"
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
|
|
@ -81,7 +217,7 @@
|
|||
"method": "GET",
|
||||
"path": "/api/product/detail",
|
||||
"response": {
|
||||
"id": "1",
|
||||
"id": 1,
|
||||
"title": "防摔防滑地垫 卫生间适老化改造专用",
|
||||
"subtitle": "600g专用 600g专用 600g专用简介",
|
||||
"price": 120.0,
|
||||
|
|
|
|||
106
server.js
106
server.js
|
|
@ -5,12 +5,11 @@ 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());
|
||||
|
||||
|
|
@ -26,96 +25,50 @@ function formatResponse(data, code = 200, message = 'success') {
|
|||
}
|
||||
|
||||
function registerMockApi(config) {
|
||||
const { method, path, response } = config;
|
||||
const { method, path, response, query, swagger } = config;
|
||||
const methodName = method.toLowerCase();
|
||||
|
||||
app[methodName](path, async (req, res) => {
|
||||
app[methodName](path, async (req, res, next) => {
|
||||
try {
|
||||
// 地址模块逻辑
|
||||
if (path === '/api/user/address/list') {
|
||||
await global.db.read();
|
||||
return res.json(formatResponse({ list: global.db.data.addresses }));
|
||||
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/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));
|
||||
// 精确 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() {
|
||||
// 初始化 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);
|
||||
});
|
||||
|
||||
// swagger 文档支持
|
||||
const swaggerDefinition = {
|
||||
openapi: '3.0.0',
|
||||
info: {
|
||||
|
|
@ -144,7 +97,12 @@ async function startServer() {
|
|||
}
|
||||
};
|
||||
paths[cleanPath] = paths[cleanPath] || {};
|
||||
paths[cleanPath][methodName] = { summary, description, responses };
|
||||
paths[cleanPath][methodName] = {
|
||||
summary,
|
||||
description,
|
||||
parameters: swagger?.parameters || [],
|
||||
responses
|
||||
};
|
||||
});
|
||||
return { ...swaggerDefinition, paths };
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue