/* * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: Chill 庄骞 (smallchill@163.com) */ package org.springblade.develop.support; import com.baomidou.mybatisplus.generator.FastAutoGenerator; import com.baomidou.mybatisplus.generator.config.TemplateType; import com.baomidou.mybatisplus.generator.config.rules.DateType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.annotations.Mapper; import org.springblade.core.tool.utils.Func; import org.springblade.core.tool.utils.StringUtil; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PropertiesLoaderUtils; import java.io.IOException; import java.util.*; import java.util.stream.Collectors; import static org.springblade.develop.constant.DevelopConstant.*; /** * 代码生成器配置类 * * @author Chill */ @Data @Slf4j public class BladeCodeGenerator { /** * 代码风格 */ private String codeStyle = SABER_NAME; /** * 代码模块名称 */ private String codeName; /** * 模型编号 */ private String modelCode; /** * 模型实体类 */ private String modelClass; /** * 代码所在服务名 */ private String serviceName = "blade-service"; /** * 代码生成的包名 */ private String packageName = "org.springblade.test"; /** * 模版类型 */ private String templateType; /** * 作者信息 */ private String author; /** * 子表模型主键 */ private String subModelId; /** * 子表绑定外键 */ private String subFkId; /** * 树主键字段 */ private String treeId; /** * 树父主键字段 */ private String treePid; /** * 树名称字段 */ private String treeName; /** * 代码后端生成的地址 */ private String packageDir; /** * 代码前端生成的地址 */ private String packageWebDir; /** * 需要去掉的表前缀 */ private String[] tablePrefix = {"blade_"}; /** * 需要生成的表名(两者只能取其一) */ private String[] includeTables = {"blade_test"}; /** * 需要排除的表名(两者只能取其一) */ private String[] excludeTables = {}; /** * 是否包含基础业务字段 */ private Boolean hasSuperEntity = Boolean.TRUE; /** * 是否包含包装器 */ private Boolean hasWrapper = Boolean.TRUE; /** * 是否包含远程调用 */ private Boolean hasFeign = Boolean.FALSE; /** * 是否包含服务名 */ private Boolean hasServiceName = Boolean.FALSE; /** * 基础业务字段 */ private String[] superEntityColumns = {"create_time", "create_user", "create_dept", "update_time", "update_user", "status", "is_deleted"}; /** * 租户字段 */ private String tenantColumn = "tenant_id"; /** * 数据库驱动名 */ private String driverName; /** * 数据库链接地址 */ private String url; /** * 数据库用户名 */ private String username; /** * 数据库密码 */ private String password; /** * 数据模型 */ private Map model; /** * 数据原型 */ private List> prototypes; /** * 子数据模型 */ private Map subModel; /** * 子数据原型 */ private List> subPrototypes; /** * 代码生成执行 */ public void run() { // 主模块代码生成 getAutoGenerator(getCustomMap(TEMPLATE_MAIN), getCustomFile(TEMPLATE_MAIN)).templateEngine(new BladeTemplateEngine(getOutputDir(), getOutputWebDir())).execute(); // 子模块代码生成 if (Func.equals(templateType, TEMPLATE_SUB) && StringUtil.isNotBlank(subModelId)) { getAutoGenerator(getCustomMap(TEMPLATE_SUB), getCustomFile(TEMPLATE_SUB)).templateEngine(new BladeTemplateEngine(getOutputDir(), getOutputWebDir())).execute(); } } /** * 设置 customMap */ private Map getCustomMap(String generateType) { List> prototypeList; String[] split = packageName.split("\\."); String serviceCode = split[split.length - 1]; Map customMap = new HashMap<>(11); customMap.put("generateType", generateType); customMap.put("codeName", codeName); customMap.put("serviceName", serviceName); customMap.put("serviceCode", serviceCode); customMap.put("packageName", packageName); customMap.put("tenantColumn", tenantColumn); customMap.put("hasWrapper", hasWrapper); customMap.put("hasServiceName", hasServiceName); customMap.put("templateType", templateType); customMap.put("author", author); customMap.put("subModelId", subModelId); customMap.put("subFkId", subFkId); customMap.put("treeId", treeId); customMap.put("treePid", treePid); customMap.put("treeName", treeName); customMap.put("subFkIdHump", StringUtil.underlineToHump(subFkId)); customMap.put("treeIdHump", StringUtil.underlineToHump(treeId)); customMap.put("treePidHump", StringUtil.underlineToHump(treePid)); if (Func.equals(generateType, TEMPLATE_SUB)) { prototypeList = subPrototypes; customMap.put("model", subModel); customMap.put("prototypes", subPrototypes); customMap.put("modelCode", subModel.get("modelCode")); customMap.put("modelClass", subModel.get("modelClass")); customMap.put("modelTable", subModel.get("modelTable")); } else { prototypeList = prototypes; customMap.put("model", model); customMap.put("prototypes", prototypes); customMap.put("subModel", subModel); customMap.put("subPrototypes", subPrototypes); customMap.put("modelCode", model.get("modelCode")); customMap.put("modelClass", model.get("modelClass")); customMap.put("modelTable", model.get("modelTable")); } List propertyImport = prototypeList.stream().filter(prototype -> { String propertyType = String.valueOf(prototype.get("propertyType")); return !"String".equals(propertyType) && !"Integer".equals(propertyType) && !"Long".equals(propertyType); }).map(prototype -> String.valueOf(prototype.get("propertyEntity"))).distinct().collect(Collectors.toList()); customMap.put("propertyImport", propertyImport); return customMap; } /** * 设置 customFile */ private Map getCustomFile(String type) { Map customFile = new HashMap<>(15); if (!Func.equals(type, TEMPLATE_SUB)) { customFile.put("menu.sql", "/templates/sql/menu.sql.btl"); } customFile.put("entityVO.java", "/templates/api/entityVO.java.btl"); customFile.put("entityDTO.java", "/templates/api/entityDTO.java.btl"); if (hasWrapper) { customFile.put("wrapper.java", "/templates/api/wrapper.java.btl"); } if (hasFeign) { customFile.put("feign.java", "/templates/api/feign.java.btl"); customFile.put("feignclient.java", "/templates/api/feignclient.java.btl"); } if (Func.isNotBlank(packageWebDir)) { if (Func.equals(codeStyle, SWORD_NAME)) { customFile.put("action.js", "/templates/sword/action.js.btl"); customFile.put("model.js", "/templates/sword/model.js.btl"); customFile.put("service.js", "/templates/sword/service.js.btl"); customFile.put("list.js", "/templates/sword/list.js.btl"); customFile.put("add.js", "/templates/sword/add.js.btl"); customFile.put("edit.js", "/templates/sword/edit.js.btl"); customFile.put("view.js", "/templates/sword/view.js.btl"); } else if (Func.equals(codeStyle, SABER_NAME)) { customFile.put("api.js", "/templates/saber/" + templateType + "/api.js.btl"); customFile.put("const.js", "/templates/saber/" + templateType + "/const.js.btl"); if (!Func.equals(type, TEMPLATE_SUB)) { customFile.put("crud.vue", "/templates/saber/" + templateType + "/crud.vue.btl"); } } else if (Func.equals(codeStyle, ELEMENT_NAME)) { customFile.put("api.js", "/templates/element/" + templateType + "/api.js.btl"); customFile.put("const.js", "/templates/element/" + templateType + "/const.js.btl"); if (!Func.equals(type, TEMPLATE_SUB)) { customFile.put("crud.vue", "/templates/element/" + templateType + "/crud.vue.btl"); } else { customFile.put("sub.vue", "/templates/element/" + templateType + "/sub.vue.btl"); } } else if (Func.equals(codeStyle, LEMON_NAME)) { customFile.put("data.ts", "/templates/lemon/" + templateType + "/data.ts.btl"); customFile.put("Modal.vue", "/templates/lemon/" + templateType + "/Modal.vue.btl"); customFile.put("data.data.ts", "/templates/lemon/" + templateType + "/data.data.ts.btl"); if (!Func.equals(type, TEMPLATE_SUB)) { customFile.put("index.vue", "/templates/lemon/" + templateType + "/index.vue.btl"); } else { customFile.put("lemonSub.vue", "/templates/lemon/" + templateType + "/sub.vue.btl"); } } } return customFile; } private FastAutoGenerator getAutoGenerator(Map customMap, Map customFile) { Properties props = getProperties(); String url = Func.toStr(this.url, props.getProperty("spring.datasource.url")); String username = Func.toStr(this.username, props.getProperty("spring.datasource.username")); String password = Func.toStr(this.password, props.getProperty("spring.datasource.password")); return FastAutoGenerator.create(url, username, password) .globalConfig(builder -> builder.author(StringUtil.isBlank(author) ? props.getProperty("author") : author).dateType(DateType.TIME_PACK).enableSwagger().outputDir(getOutputDir()).disableOpenDir()) .packageConfig(builder -> builder.parent(packageName).controller("controller").entity("entity").service("service").serviceImpl("service.impl").mapper("mapper").xml("mapper")) .strategyConfig(builder -> builder.addTablePrefix(tablePrefix).addInclude(Func.toStrArray(String.valueOf(customMap.get("modelTable")))).addExclude(excludeTables) .entityBuilder().naming(NamingStrategy.underline_to_camel).columnNaming(NamingStrategy.underline_to_camel).enableLombok().superClass("org.springblade.core.mp.base.BaseEntity").formatFileName("%sEntity").addSuperEntityColumns(superEntityColumns).enableFileOverride() .serviceBuilder().superServiceClass("org.springblade.core.mp.base.BaseService").superServiceImplClass("org.springblade.core.mp.base.BaseServiceImpl").formatServiceFileName("I%sService").formatServiceImplFileName("%sServiceImpl").enableFileOverride() .mapperBuilder().mapperAnnotation(Mapper.class).enableBaseResultMap().enableBaseColumnList().formatMapperFileName("%sMapper").formatXmlFileName("%sMapper").enableFileOverride() .controllerBuilder().superClass("org.springblade.core.boot.ctrl.BladeController").formatFileName("%sController").enableRestStyle().enableHyphenStyle().enableFileOverride() ) .templateConfig(builder -> builder.disable(TemplateType.ENTITY) .entity("/templates/api/entity.java") .service("/templates/api/service.java") .serviceImpl("/templates/api/serviceImpl.java") .mapper("/templates/api/mapper.java") .xml("/templates/api/mapper.xml") .controller("/templates/api/controller.java")) .injectionConfig(builder -> builder.beforeOutputFile( (tableInfo, objectMap) -> System.out.println("tableInfo: " + tableInfo.getEntityName() + " objectMap: " + objectMap.size()) ).customMap(customMap).customFile(customFile) ); } /** * 获取配置文件 * * @return 配置Props */ private Properties getProperties() { // 读取配置文件 Resource resource = new ClassPathResource("/templates/code.properties"); Properties props = new Properties(); try { props = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException e) { e.printStackTrace(); } return props; } /** * 生成到项目中 * * @return outputDir */ public String getOutputDir() { return (Func.isBlank(packageDir) ? System.getProperty("user.dir") + "/blade-ops/blade-develop" : packageDir) + "/src/main/java"; } /** * 生成到Web项目中 * * @return outputDir */ public String getOutputWebDir() { return (Func.isBlank(packageWebDir) ? System.getProperty("user.dir") : packageWebDir) + "/src"; } }