likes
comments
collection
share

从0搭建一个内部mock工具(支持sql转换,自带增删改查)

作者站长头像
站长
· 阅读数 46

一. 前言

作为程序员的我们,通常避免不了对需求任务的追赶。

而作为切图仔的我们,作为开发最后提测前最后一环的我们,对无法准时提测,有时候可能存在一些委屈,相信肯定遇到接口不及时而导致页面进度延期的情况。

此外,在日常迭代的规律性的迭代,前端很容易出现一种形象,就是出现"区间性"忙碌。你说非常忙,把工作日平摊下来,实际上也没多少产出,但就是在最后时刻特别忙碌。给人的一种感觉就是,联调前,没事做;联调后,事太多。

如果,有一款能生成虚拟的联调数据,是能分担任务的比重的。而前端mock就是在这种背景中产生。

二. mock的意义

说完mock存在的背景,这里再分析一下mock的意义。

1) 草图

如何解决前端任务不聚焦的问题,笔者这里简单梳理了一个草稿图:

从0搭建一个内部mock工具(支持sql转换,自带增删改查)

2) 分析

上图已经列出,我们面对的问题:

  • 1.当前只有业务逻辑(表结构),无法生成对应接口
  • 2.模拟数据成本高,无法快速生成
  • 3.有数据了,也无法进行真实增删改查

根据如上,笔者认为一个好用的mock工具,我觉得应该保证下边的功能:

  • 1.能自定义我们的数据格式,返回对应的数据结构以及相关的字段等。
  • 2.能快速自定义我们的数据源。
  • 3.能快速生成增删改查的接口,模拟真实数据的运转,否则就失去了意义
  • 4.如果能根据表结构直接生成,那就更完美

当然,博客上,也有好多博主有自己的见解,有些东西我觉得可以非必要追求:

  • 1.过于追求数据结构。假设没有技术评审,还未确认表结构,我们设计一个差不多就可以。这玩意再怎么优秀,都只是“模拟”数据而已,你永远猜不中后端最终给你的格式。
  • 2.实现复杂的逻辑。越复杂的逻辑,也表示花费的时间越多。只要接口有了,也意味着这玩意作废。所以,不适宜投入太高的成本。

这里也有几个前置条件:

  • BFF接口设计规范,我们知道对应的接口设计规范。
  • 对应业务的表结构

3) 结论

综上分析,我们需要一款,配置"简单"且能"快速"生成一个增删改查的mock工具。而"简单"与"快速",是mock的核心。

三. 市场的常用方案

此时我们已经有了需求,再来看看市场的解决方案。一起看看,市场是怎么设计这些mock工具的,或者是有什么解决方案。

1)手动虚拟数据

该方案,直接在代码中写死 Mock 数据,或者请求本地的 JSON 文件。相信也是日常大家使用最多的方案。

优势:

  • 上手快,无需学习成本。

劣势:

  • 做数据比较麻烦。
  • 无法自动生成增删改查等。
  • 会使项目太多冗余数据,解藕也较难。

2)接口管理工具

这里以swagger为例,相信后端是java的切图仔都看过这玩意。

优势:

  • 自带接口规范。配置功能强大。
  • 根据后端接口生成。

劣势:

  • 资源问题:毕竟是后端项目,启动一个后端项目也麻烦。还耗后端资源。
  • 人员问题:让后端帮忙处理,后端还不如直接加紧赶出接口给出来。
  • 配置问题:强大也意外着,学习成本高。熟悉一些配置相关的成本也不低。

3)node服务器

结合上边,如果说使用一个java后端比较麻烦,那么启动一个Node服务器的话呢?

常见的有我们的koa,或者是express框架等等。

优势:

  • 前端学习成本相对较低,毕竟都是js

劣势:

  • 增删改查,还是需要手动写

4)MOCKJS

优势:

可随机生成所需数据,可模拟对数据的增删改查

劣势:

  • 可以实现增删改查, 数据源的确比较灵活,支持随机数,正则等,但是模拟数据麻烦一些。

5)抓包工具

代表作Fiddler

优势:

脱离程序本身,可以直接映射,修改数据源。

劣势:

调试相对繁琐。定位并不是mock开发阶段,更适合偶尔模拟数据定位问题。

四.手动搭建自己的mock

介绍自己的Cb mock:

1)功能介绍

  • 能快速自定义我们的数据格式,能快速生成增删改查接口
  • 支持三种模式,自定义模式,json模式,sql模式
  • 支持接口校验,如必填,长度等校验
  • 能自定义返回接口格式,统一数据格式规范,项目也支持自定义规范
  • 与项目本身脱离解耦,可快速替换真实链接
  • 支持生成api文档说明

2)核心技术栈/源码

    1. koa

启动koa服务,作为服务提供者,代码如下:

const Koa = require('koa')
const app = new Koa();

app.use(json())
app.use(logger())
app.use(koaBody());
app.use(bodyParser({
    enableTypes: ['json', 'form', 'text'],
    multipart: true
}));
  • 2. mockjs

返回mock数据:

 const { mock } = require("mockjs");
 ctx.body = Result.info(mock(Object.assign(defaultResult, result)));

  • 3.commander指令

如启动commander指令:

const program = require('commander')
program
  .command("startMock")
  .description("启动mock服务")
  .action((appName, options) => {
    startMock()
  })
  • 4. 正则sql转换

利用正则切割sql:

const nameMatch = line.match(' `(.*)`');
const maxLengthMatch = line.match(' varchar\\((.*)\\) ');
const defaultMatch = line.match(' DEFAULT \'(.*)\' ');
const commentMatch = line.match(' COMMENT \'(.*)\',');
const typePattern = / (?:\w+)(.*?) /;
const typeMatch = line.match(typePattern, "$0");
const requiredReg = /NOT NULL/.test(line);
        
const key = nameMatch[1];
const maxLength = maxLengthMatch && maxLengthMatch.length > 0 ? Number(maxLengthMatch[1]) : undefined;
const required = !requiredReg;
const defaultValue = defaultMatch && defaultMatch.length > 0 ? defaultMatch[1] : undefined;
const comment = commentMatch && commentMatch.length > 0 ? commentMatch[1] : undefined;
const typeDb = typeMatch && typeMatch.length > 0 ? typeMatch[0] : [];
  • 5. fs生成文档

利用fs生成文档文件:

    // 生成文档
        const generateDoc = (table, port) => {
            let str = '';
            Object.keys(table).forEach(key => {
                str += `## ${key}模块
    |  参数名称  |  参数说明  |  数据类型  |  长度(字节)  |  是否必填  |  默认值  | 
    |  -------  |  -------  |  ------  |  -----  |  -------  |  ---------  |
        `
        const { column } = table[key]
        column.forEach(item => {
            const name = item.key;
            const type = item.type;
            const comment = item.comment ? item.comment : '-';
            const defaultValue = item.default ? item.default : '-';
            const isRequired = item.required ? "是" : "否"
            const maxLength = item.maxLength ? item.maxLength : "-"
            str += `| ${name} | ${comment} | ${type} | ${maxLength} | ${isRequired} | ${defaultValue} |\n`
        })

        // 获取接口名称
        const apiList = getApiList(key);
        for (let i = 0; i < apiList.length; i++) {
            const { url, name, type } = apiList[i]
            str += `###  http:/${url}
    接口说明:${name}
    请求类型: ${type}
                \n`

        }
    })

五.使用配置

1)安装本地

git clone https://github.com/zhuangweizhan/cb-cli
npm install
npm link

2)配置mock.config.js

module.exports = {
    common: {//支持公用配置
        port: 8000,
        timeout: 0,
        rate: 1,
    },
    sqlDataSource: [//支持sql数据源
        `
      CREATE TABLE \`jsh_depot\` (
        \`id\` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
        \`name\` varchar(20) DEFAULT NULL COMMENT '仓库名称',
        \`address\` varchar(50) DEFAULT NULL COMMENT '仓库地址',
        \`warehousing\` decimal(24,6) DEFAULT NULL COMMENT '仓储费',
        \`truckage\` decimal(24,6) DEFAULT NULL COMMENT '搬运费',
        \`type\` int DEFAULT NULL COMMENT '类型',
        \`sort\` varchar(10) DEFAULT NULL COMMENT '排序',
        \`remark\` varchar(100) DEFAULT NULL COMMENT '描述',
        \`principal\` bigint DEFAULT NULL COMMENT '负责人',
        \`tenant_id\` bigint DEFAULT NULL COMMENT '租户id',
        \`delete_Flag\` varchar(1) DEFAULT '0' COMMENT '删除标记,0未删除,1删除',
        \`is_default\` bit(1) DEFAULT NULL COMMENT '是否默认',
        PRIMARY KEY (\`id\`)
      ) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb3 COMMENT='仓库表';

      insert  into \`jsh_depot\`(\`id\`,\`name\`,\`address\`,\`warehousing\`,\`truckage\`,\`type\`,\`sort\`,\`remark\`,\`principal\`,\`tenant_id\`,\`delete_Flag\`,\`is_default\`) values (14,'仓库1','dizhi','12.000000','12.000000',0,'1','描述',131,63,'0','');
      insert  into \`jsh_depot\`(\`id\`,\`name\`,\`address\`,\`warehousing\`,\`truckage\`,\`type\`,\`sort\`,\`remark\`,\`principal\`,\`tenant_id\`,\`delete_Flag\`,\`is_default\`) values (15,'仓库2','地址100','555.000000','666.000000',0,'2','dfdf',131,63,'0','\0');
      insert  into \`jsh_depot\`(\`id\`,\`name\`,\`address\`,\`warehousing\`,\`truckage\`,\`type\`,\`sort\`,\`remark\`,\`principal\`,\`tenant_id\`,\`delete_Flag\`,\`is_default\`) values (17,'仓库3','123123','123.000000','123.000000',0,'3','123',131,63,'0','\0');


      CREATE TABLE \`jsh_depot22\` (
        \`id\` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
        \`name\` varchar(20) DEFAULT NULL COMMENT '仓库名称',
        \`address\` varchar(50) DEFAULT NULL COMMENT '仓库地址',
        \`warehousing\` decimal(24,6) DEFAULT NULL COMMENT '仓储费',
        \`truckage\` decimal(24,6) DEFAULT NULL COMMENT '搬运费',
        \`type\` int DEFAULT NULL COMMENT '类型',
        \`sort\` varchar(10) DEFAULT NULL COMMENT '排序',
        \`remark\` varchar(100) DEFAULT NULL COMMENT '描述',
        \`principal\` bigint DEFAULT NULL COMMENT '负责人',
        \`tenant_id\` bigint DEFAULT NULL COMMENT '租户id',
        \`delete_Flag\` varchar(1) DEFAULT '0' COMMENT '删除标记,0未删除,1删除',
        \`is_default\` bit(1) DEFAULT NULL COMMENT '是否默认',
        PRIMARY KEY (\`id\`)
      ) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb3 COMMENT='仓库表';
      insert  into \`jsh_depot\`(\`id\`,\`name\`,\`address\`,\`warehousing\`,\`truckage\`,\`type\`,\`sort\`,\`remark\`,\`principal\`,\`tenant_id\`,\`delete_Flag\`,\`is_default\`) values (14,'仓库1','dizhi','12.000000','12.000000',0,'1','描述',131,63,'0','');
      insert  into \`jsh_depot\`(\`id\`,\`name\`,\`address\`,\`warehousing\`,\`truckage\`,\`type\`,\`sort\`,\`remark\`,\`principal\`,\`tenant_id\`,\`delete_Flag\`,\`is_default\`) values (15,'仓库2','地址100','555.000000','666.000000',0,'2','dfdf',131,63,'0','\0');
      insert  into \`jsh_depot\`(\`id\`,\`name\`,\`address\`,\`warehousing\`,\`truckage\`,\`type\`,\`sort\`,\`remark\`,\`principal\`,\`tenant_id\`,\`delete_Flag\`,\`is_default\`) values (17,'仓库3','123123','123.000000','123.000000',0,'3','123',131,63,'0','\0');
      `
    ],
    jsonDataSource: [//支持json数据源
        {
            account_detail: {
                column: [
                    "id",
                    { key: 'username', required: true },
                    "auth_key",
                    "password_hash",
                    "password_reset_token",
                    "email",
                    "created_at",
                ],
                dataSource: [
                    {
                        "id": 13,
                        "email": "368938"
                    },
                    [14, 45457]
                ],
            },
        },
    ],
    customDataSource: [[//支持自定义数据源
        {
            url: "/login",
            returnFn: (req) => {
                const { name, password } = req.query;
                if (name === 'admin' || password === 'a123456') {
                    return { status: 1, msg: "登录成功" }
                }
                return { status: 0, msg: "账号或密码错误" }

            }
        },
        {
            url: "/addUser",
            type: "post",
            returnFn: (req) => {
                const { name } = req.request.body;
                if (name !== '' && name !== undefined) {
                    return { status: 1, msg: "新增账号成功" }
                }
                return { status: 0, msg: "请输入用户名" }

            }
        },
    ]
};

3)启动服务

cb startMock

4)启动结果

会生成mock.md接口文档说明:

五.源码

github.com/zhuangweizh…

六.结语

时间仓促,一些功能未完善。分享一下未来计划:

    1. 支持sql的left join生成
    1. 完成文档的生成
    1. 欢迎反馈bug
转载自:https://juejin.cn/post/7211147716893294653
评论
请登录