egg代码生成器

news2024/11/20 9:37:51

今天给大家分享一下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.coloradmin.cn/o/1866948.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入探索:大型语言模型消除幻觉的解决之道

随着人工智能技术的飞速发展,大型语言模型(LLMs)已经成为自然语言处理领域的明星。它们以其庞大的知识库和生成连贯、上下文相关文本的能力,极大地推动了研究、工业和社会的进步。然而,这些模型在生成文本时可能会产生…

Java基础概念

注释 概念 注释是在程序指定位置添加的说明性信息。 简单理解,就是对代码的一种解释。 注释分类 单行注释: //注释信息 多行注释: /*注释信息*/ 文档注释: /**注释信息*/ 注:文档注释暂时用不上 注释的使用细节 注释内容不会参与编译和运行&#xff0…

西安国际医学中心医院 多学科联合创新白癜风治疗法取得进展

近日,西安国际医学中心医院“自体头皮毛囊裂解物混悬液移植治疗白癜风”项目,备受瞩目。据悉,在白癜风和白发的研究及治疗上,均有望取得显著进展。 卢涛主任高分通过医院新技术新业务立项 “白癜风”——是由于皮肤黑素细胞被破坏…

Depth Anything V1,V2论文解读

Depth Anything 引言Depth Anything V1标注方法学习标注图像发挥未标注图像的潜力语义辅助感知 Depth Anything V2总体框架流程 引言 在深度估计领域,单目深度估计(Monocular Depth Estimation,MDE)是指利用单个摄像头拍摄的图像…

OpenGL-ES 学习(6)---- 立方体绘制

目录 立方体绘制基本原理立方体的顶点坐标和绘制顺序立方体颜色和着色器实现效果和参考代码 立方体绘制基本原理 一个立方体是由8个顶点组成,共6个面,所以绘制立方体本质上就是绘制这6个面共12个三角形 顶点的坐标体系如下图所示,三维坐标…

还没选好智能猫砂盆?今年最受好评的好用智能猫砂盆都在这了!

随着现代养猫文化的演进,智能化宠物用品逐渐走入千家万户,其中智能猫砂盆作为能帮助大家实现双手自由的自动铲屎神器,受到了广大铲屎官的喜欢。然而,市面上的智能猫砂盆种类繁多,品质参差不齐。一款优质的智能猫砂盆能…

从0到1构建自己的短链接系统

1. 短链系统简介 1.1 短链系统的定义与用途 短链系统是指将一个较长的URL地址,通过特定的算法生成一个较短的、具备唯一性的URL地址。这种系统广泛应用于社交网络、短信、邮件营销等场景,它能帮助用户在字数受限的情况下分享链接,并且还具有…

【干货】【全网最全】【全网最详细】 javaWeb关于Thymeleaf+SpringBoot 的学习教程,看这一篇就够了。

大家好,我是DX3906 第一部分:介绍 Thymeleaf 简介 1.什么是Thymeleaf Thymeleaf是一个用于Java和Java EE平台的服务器端模板引擎,它可以用来在服务端生成HTML、XML、JavaScript、CSS甚至纯文本的输出。Thymeleaf适用于需要快速开发和维护Web…

设备智能化:中国星坤线缆组件的解决方案!

在当今快速发展的电子行业中,产品小型化和成本效益是制造商追求的两大目标。中国星坤端子电缆组件以其灵活性和高效性,为电子设备制造商提供了一种理想的解决方案。本文将探讨星坤端子电缆组件的优势以及其在不同电子设备中的应用。 端子线:小…

Linux命令学习2

一.文件基础命令 1.alias-给某个命令取别名 使用方式:alias cl ls -la 说明:将ls -la命令取别名为cl,使用这种方式只是临时将命令取别名,重启中断后,就会失效。 问题1:如何永久性的设置命令的别名? 答…

简过网:专科生可以考的编制岗位有哪些?这5个铁饭碗要抓住了!

专科生可以考的编制岗位有哪些?以下这几种可以考的,尤其是应届毕业生,一定要抓住机会哦! ​ 一、三支一扶:专科生可报考,期满可转编。 三支一扶:支农、支医生、支教、扶贫 工作时间一般为2年&…

车载双向认证框架设计

最近工作需要,手写了一个双向认证库,可以用在Java、Android上,不限于PC/手机、车载平台。首先我们来看看双向认证的原理机框架设计思路,最后会给出下载链接大家可以体验或者源码参考。 因为可以和FlexNet网络库(参考我…

实现高效写入:Schemaless 写入性能优化指南

物联网应用常常需要收集大量的数据,用以支持智能控制、业务分析和设备监控等功能。然而,应用逻辑的更新或硬件的调整可能会导致数据采集项频繁变化,这是时序数据库(Time Series Database,TSDB)面临的一大挑…

Mybatis-映射文件中select标签resultType属性的使用

数据库的最最基本操作“增删改查”,“查”是最复杂的,有各种各样的查询,所以对应到Mybatis中的select标签也是这四个操作中最复杂的 resultType属性的使用 1.返回的结果是List集合的类型 select标签里的resultType类型设置为List集合里的元…

CCAA:认证通用基础 7(认证的基本概念)

7认证的基本概念 7.1认证类型(产品认证、管理体系认证、服务认证)及基本特征 第一节 认证 1.认证的定义和本质 1.1认证的定义 (1)认证:与产品、过程、体系或人员有关的第三方证明。 ①”产品,过程,体系或人员”是认证的对象,认证是对“产…

Rust: duckdb和polars读csv文件比较

一、文件准备 样本内容,N行9列的csv标准格式,有字符串,有浮点数,有整型。 有两个csv文件,一个大约是2.1万行;一个是64万行。 二、toml文件 [package] name "my_duckdb" version "0.1.0&…

Linux文件IO深入剖析

目录 一、文件IO引发的项目血案 1、分析 一、Linux文件系统基本概念 1、文件系统接口 2、文件系统缓存 二、文件IO 访问方式概述 1、标准文件访问方式 2、直接IO 3、实现方式 4、缓存同步 5、Linux 文件IO流程图 6、血案解决 一、文件IO引发的项目血案 事件经过&am…

甲骨文(Oracle)云AI专家级证书免费获取攻略

这次分享的是甲骨文云(Oracle)2024年最新最热门的AI专家级证书,活动截止日期7/31。 考试为闭卷监考形式,但小李哥已经把题库准备好,分享给大家。 甲骨文Oracle☁️云计算凭借其Oracle原生产品(数据库、ERP等)在云计算市场中具有一定地位。目前…

Kafka入门-分区及压缩

一、生产者消息分区 Kafka的消息组织方式实际上是三级结构:主题-分区-消息。主题下的每条消息只会保存在某一个分区中,而不会在多个分区中被保存多份。 分区的作用就是提供负载均衡的能力,或者说对数据进行分区的主要原因,就是为…

【已解决】Pycharm:卡顿解决方案汇总

可能原因: 1、内存少 2、加载慢 3、文件多 4、硬件老 解决方案: 本机测试在 MAC,Windows、Linux也有相应的设置,请自行查询。 一、调整Pycharm使用内存 Help - Change Memory Settings 二、取消勾选 重复打开上次项目 Pych…