egg代码生成器

今天给大家分享一下egg的代码生成器,这个其实原理很简单,说白了就是用到了nodejs的一个文件io的操作,通过一系列配置参数解析然后生成一个很长的字符串,写入到文件中,最后导出到我们指定的文件夹。

前提概要

为什么我要写这个代码生成器呢?原因主要还是为了提高自己平时开发的效率和时间,比如说什么呢,其实现在单表单业务的管理系统页面完全是可以使用代码生成来做的,可以极大程度的去简化我们开发的一个效率,也是我们一个摸鱼神器。话不多说,讲讲我的大致的一个思路把

大致思路

这里我希望每次生成代码都是根据配置进行生成,我希望我每次在egg-sequelize设计好一个实体之后,然后自动关联到数据表,自动获取数据表中的字段,这个字段我可以进行配置也可以不进行配置,然后这些字段最终都是要加入到我后端的crud接口中的。

我这里使用的是egg-swagger-doc框架,还需要根据它那边的规则去生成对应的swagger文档,所以我写了一个大致的配置

在这里插入图片描述

这样的话我就可以根据具体的配置去生成对应的controller,对应的service,对应的router以及vue文件,生成完成之后的str,我就根据工具类的创建文件夹,创建对应的controller,service,router,vue等文件夹,再把文件内容写入到文件,放入到对应文件夹里面,这就是我分享的代码生成器的大致的一个实现思路

工具类

// 移除文件夹,移除output目录
function clearOutputFolder() {
  fs.readdirSync(outputPath).forEach((file) => {
    if (file !== 'index.js') {
      const filePath = path.join(outputPath, file);
      if (fs.lstatSync(filePath).isDirectory()) {
        deleteFolderRecursive(filePath);
      } else {
        fs.unlinkSync(filePath);
      }
    }
  });
}

// 创建文件夹
function createFolder(folderName) {
  return new Promise((resolve, reject) => {
    fs.mkdir(folderName, (err) => {
      if (err) {
        reject(err);
      } else {
        resolve();
      }
    });
  });
}

// 删除文件夹
function deleteFolderRecursive(folderPath) {
  // 文件夹存在
  if (fs.existsSync(folderPath)) {
    // 读取文件夹里面所有文件
    fs.readdirSync(folderPath).forEach((file) => {
      const currentFilePath = path.join(folderPath, file);
      // 如果是文件夹,递归删除
      if (fs.lstatSync(currentFilePath).isDirectory()) {
        deleteFolderRecursive(currentFilePath); // 递归删除子文件夹
      } else {
        // 如果是文件删除文件
        fs.unlinkSync(currentFilePath); // 删除文件
      }
    });
    fs.rmdirSync(folderPath); // 删除空文件夹
  }
}

//生成controller代码
function generateController(){
    //...
}

//生成service代码
function generateService(){
    //...
}

//生成swagger的contract代码,用于放dto和vo的对象
function generateContract(){
    //...
}

//生成路由代码
function generateRouter(){
    //...
}

//生成swagger api字符串
function generateSwaggerApiStr(){
    
}

入口

  const str = `请选择要使用的功能
  1.代码生成器
  2.根据表格生成所有字段基本信息
  请输入对应的数字序号`;

  let number = await getInput(str);

  if (Number(number) === 1) {
    const config = require('./config');
    let isUseFileConfig = await getInput(
      '你是否使用文件配置(yes/no)',
      'select'
    );

    let author,
      parentMenu,
      moduleEName,
      moduleName,
      moduleDescription,
      isGenerateAnnotaion;

    // 是否使用文件配置
    if (!isUseFileConfig) {
      author = await getInput('请输入作者');
      parentMenu = await getInput('请输入模块所在上层目录');
      moduleName = await getInput('请输入模块中文名称');
      moduleEName = await getInput('请输入模块英文名称');
      moduleDescription = await getInput('请输入模块描述信息');
      isGenerateAnnotaion = await getInput(
        '你是否想要生成注释信息(yes/no)',
        'select'
      );
    } else {
      author = config.author;
      parentMenu = config.parentMenu;
      moduleName = config.moduleName;
      moduleEName = config.moduleEName;
      moduleDescription = config.moduleDescription;
      isGenerateAnnotaion = config.isGenerateAnnotaion;
    }

    clearOutputFolder();

    const date = timeFormat(new Date());

    const upperCaseModuleEName = capitalizeFirstLetter(moduleEName);
    const upperCaseParentMenu = capitalizeFirstLetter(parentMenu);

    let generateConfig = {
      author,
      parentMenu,
      moduleName,
      moduleEName,
      moduleDescription,
      isGenerateAnnotaion,
      date,
      upperCaseModuleEName,
      upperCaseParentMenu
    };

    if (isUseFileConfig) {
      generateConfig = {
        ...generateConfig,
        ...config
      };
    }

    // 生产控制器代码
    generateController(generateConfig);

    // 生产路由代码
    generateRouter(generateConfig);

    // 生产服务端代码
    generateService(generateConfig);

    // 生成swagger的contract
    generateContract(generateConfig);

    // 生成前端的文件
    generateVueFolder(generateConfig);

    console.log('代码生成完成,生成的文件在codeGenerator/output文件夹下');
    rl.close();
  } else if (Number(number) === 2) {
    let table = await getInput('请输入表名称');
    const str = await getVoByTb(table);

    const folderPath = path.join(__dirname, 'output');
    // console.log(folderPath);
    // await createFolder(folderPath);

    const filePath = path.join(folderPath, 'dto.js');
    fs.writeFileSync(filePath, str);

    console.log(
      'vo生成完成,生成的文件在codeGenerator/output文件夹下,名字为dto.js'
    );
    rl.close();
  } else {
    console.log('输入的值出错!!');
    rl.close();
  }

// 控制台输入
async function getInput(question, type) {
  return new Promise((resolve) => {
    rl.question(question, (input) => {
      input = input.trim();
      if (!input) {
        console.log('输入不能为空,请再次输入');
        resolve(getInput(question));
      } else {
        if (type === 'select') {
          if (input.toLocaleLowerCase() === 'yes') {
            resolve(true);
          } else if (input.toLocaleLowerCase() === 'no') {
            resolve(false);
          } else {
            console.log('请输入yes或者no');
            resolve(getInput(question));
          }
        }
        resolve(input);
      }
    });
  });
}

我这里还使用了一个控制台输入的,做了两种配置,可以通过配置指定,也可以通过控制台输入,这里控制台输入的有一些还没有完善好,后面觉得配置形式的好用一点,就是每次需要重新改。

生成controller

我这里随机用一个来展示,代码有点多,主要就是来展示一下,大概的一个实现,本质上其实就是模版 + 配置的动态数据

const {
    author,
    moduleName,
    moduleEName,
    moduleDescription,
    date,
    groupName,
    parentMenu
  } = opts;

  const listAnnotationStr = generateSwaggerApiStr({ ...opts, type: 'list' });
  const detailAnnotationStr = generateSwaggerApiStr({
    ...opts,
    type: 'detail'
  });
  const pageAnnotationStr = generateSwaggerApiStr({ ...opts, type: 'page' });
  const addAnnotationStr = generateSwaggerApiStr({ ...opts, type: 'add' });
  const editAnnotationStr = generateSwaggerApiStr({ ...opts, type: 'edit' });
  const deleteAnnotationStr = generateSwaggerApiStr({
    ...opts,
    type: 'delete'
  });
  const exportAnnotationStr = generateSwaggerApiStr({
    ...opts,
    type: 'export'
  });

  const controllerStr = `/**
* @description ${moduleDescription}
* @controller ${moduleName}管理 ${moduleName}管理 ${groupName ? groupName : ''}
* @author ${author}
* @date : ${date}
*/`;

  const upperCaseModuleEname = capitalizeFirstLetter(moduleEName);

  const parentModuleGroupStr = parentMenu ? `${parentMenu}.${moduleEName}` : moduleEName;

  const content = `
const Controller = ${parentMenu ? "require('../../core/baseController')" : "require('../core/baseController')"};

${controllerStr}
class ${moduleEName}Controller extends Controller {
    ${listAnnotationStr}
    async list() {
        const { ctx, service } = this;
        ctx.body = await service.${parentModuleGroupStr}.find${upperCaseModuleEname}List();
    }

    ${detailAnnotationStr}
    async detail() {
        const { ctx, service } = this;

        const {
            id
        } = ctx.params;

        ctx.body = await service.${parentModuleGroupStr}.find${upperCaseModuleEname}Detail(id);
    }

    ${pageAnnotationStr}
    async page(){
        const { ctx, service } = this;
        const opts = ctx.params;
        ctx.body = await service.${parentModuleGroupStr}.find${upperCaseModuleEname}Page(opts);
    }

    ${addAnnotationStr}
    async create() {
        const { ctx, service } = this;
        await service.${parentModuleGroupStr}.add${upperCaseModuleEname}(ctx.request.body);
    }

    ${editAnnotationStr}
    async update() {
        const { ctx, service } = this;
        await service.${parentModuleGroupStr}.edit${upperCaseModuleEname}(ctx.request.body);
    }

    ${deleteAnnotationStr}
    async destroy() {
        const { ctx, service } = this;

        ctx.validate({ ids: 'string' }, ctx.request.query);

        await service.${parentModuleGroupStr}.delete${upperCaseModuleEname}(ctx.request.query.ids);
    }

    ${exportAnnotationStr}
    async export() {
        await this.exportExcel('${parentModuleGroupStr}', 'export${upperCaseModuleEname}', '${moduleName}');
    }
}
module.exports = ${moduleEName}Controller;

最终效果

运行这个index,在控制台可以进行输入,输入完成回车

在这里插入图片描述

最后根据配置生成的一个结果

在这里插入图片描述

可以直接进入引入,引入到自己项目中,重新编译就可以看到一个效果

可拓展

我目前这个代码生成器还有可拓展的空间

  • 比如关于一些字段的配置,我这里最好可以传递一个数组,然后在后台进行接收,数组里面包含每一个属性对象,属性对象里面有各种属性来区分这些字段是否要展示,是否要加入查询,是否要加入添加,是否要加入编辑,等等
  • 我这个代码生成器可以做成可视化的形式,把配置文件通过在前端界面进行配置,配置完成之后生成文件出来,通过浏览器去下载对应的资源压缩包,然后一键导入到项目中
  • 等之后有时间我会去做一个可视化的代码生成器的版本,功能更加细致,更加方便快捷进行开发

结语

每天进步一点点,每天进行学习以及进行技术分享,加油,只要一直坚持总能越来越好

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/746761.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网工内推 | 国企信息工程师,信息系统项目管理师优先,最高14薪

01 上海浦东软件园股份有限公司 🔷招聘岗位:信息化管理工程师 🔷岗位职责: 1. 根据公司战略、数字化总体架构规划和IT 技术趋势,制定信息化系统的规划与设计,并制定实施计划。 2. 统筹公司信息化系统管理…

【Linux详解】进程的状态 | 运行 阻塞 挂起 | 僵尸和孤儿状态

目录 操作系统中 运行状态 阻塞状态 进程状态转换 Linux系统中 查看进程状态 深度睡眠状态 T 暂停状态 Z 僵尸状态 孤儿状态 文章手稿 xmind: 引言 介绍系统中的进程状态及其管理方式。将通过结合操作系统原理和实际代码示例,详细说明进程的各种状态、转换…

Java Stream API揭秘:掌握List流操作,打造高效数据处理流程

序言 Java Stream API是Java 8中引入的一个非常重要的功能组成部分,它提供了一种声明式的处理数据集合的方法。它主要特点是基于函数式编程的理念,允许我们以更加简洁、高效的方式进行集合的处理、转换和过滤。通过Stream API,我们可以灵活地…

暗黑4PTR怎么参与测试 暗黑4第五赛季怎么参加PTR测试教程

暗黑破坏神4作为暗黑破坏神系列的最新作品,自从2023年上线就受到了一众好评。游戏是动作冒险类角色扮演游戏,游戏的背景设定在一个腐化的圣休瑞亚大陆上,玩家们可以五种职业中选择自己喜爱的游戏进行游戏。 暗黑破坏神4第五赛季现在已经开启P…

机器学习数学原理专题——线性分类模型:损失函数推导新视角——交叉熵

目录 二、从回归到线性分类模型:分类 3.分类模型损失函数推导——极大似然估计法 (1)二分类损失函数——极大似然估计 (2)多分类损失函数——极大似然估计 4.模型损失函数推导新视角——交叉熵 (1&#x…

SpringCloud_GateWay服务网关

网关作用 Gateway网关是我们服务的守门神,所有微服务的统一入口。 网关的核心功能特性: 请求路由和负载均衡:一切请求都必须先经过gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务&a…

Python数据分析-糖尿病数据集数据分析

一、研究背景介绍 糖尿病是美国最普遍的慢性病之一,每年影响数百万美国人,并对经济造成重大的经济负担。糖尿病是一种严重的慢性疾病,其中个体失去有效调节血液中葡萄糖水平的能力,并可能导致生活质量和预期寿命下降。。。。糖尿…

如何借助物联网实现土壤监测与保护

如何借助物联网实现土壤监测与保护 高标准农田信息化是指利用现代信息技术,如物联网、大数据、云计算等,对农田进行数字化、智能化的管理,以提高农田的生产效率和可持续发展能力。其中,土壤监测与保护是农田信息化的重要内容之一…

解决SD卡被写保护问题

存储卡在使用过程中,有时会遇到写保护问题,导致无法写入或删除数据。这可能会对用户的正常使用造成困扰。MK米客方德将为您介绍几种常见的解决方法,帮助用户解除存储卡的写保护。 一、检查物理写保护开关 许多存储卡,如SD卡&…

JavaScript的学习之dom的查询(一)

一、获得元素 通过document对象调用&#xff1a; getElementById()&#xff1a;通过id属性获取一个元素节点对象getElementsByTagName()&#xff1a;通过标签名获取一组元素节点对象getElementsByName()&#xff1a;通过name属性来获取一组元素节点对象 核心学习代码 <scrip…

【UE5.3】笔记3-静态网格体,BSP

静态网格体组件 主要有两个属性 一个是静态网格体&#xff1a;对应的也就是模型&#xff0c;比如fbx&#xff0c;maya&#xff0c;obj等格式 一个是材质&#xff1a;由各种贴图、渲染设置等&#xff0c;比如unity里的shader BSP画刷&#xff1a; 打开放置Actor选项卡&#…

QT中子工程的创建,以及如何在含有库的子工程项目中引用主项目中的qt资源

1、背景 在qt中创建多项目类型,如下: CustomDll表示其中的一个动态库子项目; CustomLib表示其中的一个静态库子项目; MyWidget表示主项目窗口(main函数所在项目); 2、qrc资源的共享 如何在CustomDll和CustomLib等子项目中也同样使用 MyWidget项目中的qrc资源呢??? 直…

MySQL 7种Join的定义图解示范结果(所有join类型)

文章目录 MySQL 7种Join的定义&图解&示范&结果&#xff08;所有join类型&#xff09;基本知识笛卡尔积 建表&填充数据1-Join不带条件account筛选 1-Inner Join 内连接不带条件account相同where筛选玩点特殊的 2-Left Join 左连接不带条件account筛选 3-Right J…

神经网络学习8-反向传播

back propagation 拿到前面传回来的L对z的偏导&#xff0c;再分别算损失值对x和w的偏导 反向传播 前馈过程求局部梯度 反向传播 这里的loss&#xff08;wxb-y)^2,第一个关于b的偏导为2(wxb-y),第二个关于w的为2w(wxb-y)

业绩尚可但股价不振,浙商银行陆建强闯“3元大关”

&#xff08;题图&#xff09; 文&#xff5c;新熔财经 作者&#xff5c;宏一 本来做着钱生钱的“美梦”&#xff0c;现在倒好&#xff0c;本金都不一定拿得回来。 因为不想把“鸡蛋都放在一个笼子里”&#xff0c;所以前几年在理财的时候一部分放在银行定存&#xff0c;一…

Sum of Single Effects Linear Regression (susieR):多个因果变异位点的鉴定

使用susieR鉴定多个因果变异位点只需要两个输入文件&#xff0c;一个输入文件是包含Zscore值的SNP位点&#xff08;zscore.txt&#xff09;&#xff0c;另一个文件是LD matrix&#xff08;LD.matrix.ld&#xff09;。 zscore.txt 文件如下所示&#xff1a; LD.matrix.ld 文件…

Vue DevTools

介绍 什么是 Vue DevTools&#xff1f; Vue DevTools 是一款旨在增强 Vue 开发者体验的工具&#xff0c;它是一款功能强大且用途广泛的工具&#xff0c;可以在使用 Vue 应用程序时显着提高您的生产力和调试能力。它的实时编辑、时间旅行调试和全面检查功能使其成为任何Vue.js开…

程序员如何用ChatGPT解决常见编程问题:实例解析

引言 在现代编程的世界中&#xff0c;技术进步日新月异&#xff0c;程序员们面临着各种各样的挑战和问题。解决这些问题的过程中&#xff0c;找到合适的工具至关重要。ChatGPT作为一种先进的人工智能语言模型&#xff0c;能够帮助程序员迅速、高效地解决常见的编程问题。本文将…

基于SpringBoot的学生综合测评系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot框架 工具&#xff1a;MyEclipse、Tomcat 系统展示 首页 系统首页&#xff0c;提供综合…

【ajax07基础】回调函数地狱

一&#xff1a;什么是回调函数地狱 在一个回调函数中嵌套另一个回调函数&#xff08;甚至一直嵌套下去&#xff09;&#xff0c;形成回调函数地狱 回调函数地狱存在问题&#xff1a; 可读性差异常捕获严重耦合性严重 // 1. 获取默认第一个省份的名字axios({url: http://hmaj…