From 1e0f9f3fc5ad3c03e5af8676e72a17e862404132 Mon Sep 17 00:00:00 2001 From: wangyingjie3 Date: Fri, 12 Jul 2024 16:02:42 +0800 Subject: [PATCH] feat(codegen): support feature and test and table generation based on template or generator --- jcommon/codegen/pom.xml | 17 ++ .../run/mone/ai/codegen/FeatureGenerator.java | 149 ++++++++++++++++++ .../mone/ai/codegen/UnitTestGenerator.java | 12 ++ .../ai/codegen/bo/FeatureGeneratType.java | 39 +++++ .../mone/ai/codegen/bo/FeatureGenerateBo.java | 76 +++++++++ .../mone/ai/codegen/util/TemplateUtils.java | 73 +++++++++ .../src/main/resources/tlp/code_test.txt | 2 + .../src/main/resources/tlp/controller.java | 41 +++++ .../codegen/src/main/resources/tlp/pojo.java | 32 ++++ .../src/main/resources/tlp/service.java | 20 +++ .../codegen/src/main/resources/tlp/test.java | 32 ++++ .../src/main/resources/tlp/transfer.java | 24 +++ .../codegen/src/main/resources/tlp/vo.java | 21 +++ 13 files changed, 538 insertions(+) create mode 100644 jcommon/codegen/src/main/java/run/mone/ai/codegen/FeatureGenerator.java create mode 100644 jcommon/codegen/src/main/java/run/mone/ai/codegen/UnitTestGenerator.java create mode 100644 jcommon/codegen/src/main/java/run/mone/ai/codegen/bo/FeatureGeneratType.java create mode 100644 jcommon/codegen/src/main/java/run/mone/ai/codegen/bo/FeatureGenerateBo.java create mode 100644 jcommon/codegen/src/main/java/run/mone/ai/codegen/util/TemplateUtils.java create mode 100644 jcommon/codegen/src/main/resources/tlp/code_test.txt create mode 100644 jcommon/codegen/src/main/resources/tlp/controller.java create mode 100644 jcommon/codegen/src/main/resources/tlp/pojo.java create mode 100644 jcommon/codegen/src/main/resources/tlp/service.java create mode 100644 jcommon/codegen/src/main/resources/tlp/test.java create mode 100644 jcommon/codegen/src/main/resources/tlp/transfer.java create mode 100644 jcommon/codegen/src/main/resources/tlp/vo.java diff --git a/jcommon/codegen/pom.xml b/jcommon/codegen/pom.xml index 1ce33b9f6..a21ae573b 100644 --- a/jcommon/codegen/pom.xml +++ b/jcommon/codegen/pom.xml @@ -27,6 +27,23 @@ beetl 2.7.14 + + com.mybatis-flex + mybatis-flex-codegen + 1.7.8 + + + org.springframework + spring-jdbc + 5.3.29 + true + + + com.zaxxer + HikariCP + 4.0.3 + true + org.apache.commons commons-lang3 diff --git a/jcommon/codegen/src/main/java/run/mone/ai/codegen/FeatureGenerator.java b/jcommon/codegen/src/main/java/run/mone/ai/codegen/FeatureGenerator.java new file mode 100644 index 000000000..38812927d --- /dev/null +++ b/jcommon/codegen/src/main/java/run/mone/ai/codegen/FeatureGenerator.java @@ -0,0 +1,149 @@ +package run.mone.ai.codegen; + +import com.mybatisflex.codegen.Generator; +import com.mybatisflex.codegen.config.GlobalConfig; +import com.zaxxer.hikari.HikariDataSource; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.jdbc.core.JdbcTemplate; +import run.mone.ai.codegen.bo.FeatureGeneratType; +import run.mone.ai.codegen.bo.FeatureGenerateBo; +import run.mone.ai.codegen.util.TemplateUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @author goodjava@qq.com, HawickMason@xiaomi.com + * @date 7/12/24 9:44 AM + */ +@Slf4j +public class FeatureGenerator { + + public static void generateWithTemplate(FeatureGenerateBo featureGenerateBo) { + + // 类型检查 + if (Objects.isNull(featureGenerateBo.getType())) { + log.warn("Empty generation type, will do noting!"); + return; + } + FeatureGeneratType featureGenType = featureGenerateBo.getType(); + if (FeatureGeneratType.CODE_WITH_TEMPLATE != featureGenType + && FeatureGeneratType.TABLE != featureGenType) { + log.warn("generate type:{} is not match with current call", featureGenType); + return; + } + + //配置数据源 + HikariDataSource dataSource = new HikariDataSource(); + dataSource.setJdbcUrl(featureGenerateBo.getJdbcUrl()); + dataSource.setUsername(featureGenerateBo.getUserName()); + dataSource.setPassword(featureGenerateBo.getPassword()); + + //创建mapper相关代码 + if (FeatureGeneratType.CODE_WITH_TEMPLATE == featureGenType) { + GlobalConfig globalConfig = createGlobalConfigUseStyle(featureGenerateBo); + Generator generator = new Generator(dataSource, globalConfig); + generator.generate(); + return; + } + + //创建table + if (FeatureGeneratType.TABLE == featureGenType) { + JdbcTemplate jt = new JdbcTemplate(dataSource); + jt.update(featureGenerateBo.getSql()); + } + } + + public static GlobalConfig createGlobalConfigUseStyle(FeatureGenerateBo featureGenerateBo) { + GlobalConfig globalConfig = new GlobalConfig(); + //设置根包 + String basePackage = featureGenerateBo.getBasePackage(); + globalConfig.setBasePackage(basePackage); + //表的名字 + globalConfig.setGenerateTable(featureGenerateBo.getTableName()); + globalConfig.setTablePrefix(""); + //设置生成 entity 并启用 Lombok + globalConfig.setEntityGenerateEnable(true); + globalConfig.setEntityWithLombok(true); + globalConfig.setServiceGenerateEnable(true); + globalConfig.setServiceImplGenerateEnable(true); + globalConfig.enableController(); + + //生成entity mapper service controller + String dir = featureGenerateBo.getBasePath(); + globalConfig.setMapperGenerateEnable(true); + globalConfig.getEntityConfig().setSourceDir(dir); + globalConfig.getMapperConfig().setSourceDir(dir); + globalConfig.getServiceConfig().setSourceDir(dir); + globalConfig.getServiceImplConfig().setSourceDir(dir); + globalConfig.getControllerConfig().setSourceDir(dir); + + globalConfig.setEntityPackage(basePackage + ".dao.entity"); + globalConfig.setMapperPackage(basePackage + ".dao.mapper"); + globalConfig.setServicePackage(basePackage + ".service"); + globalConfig.setServiceImplPackage(basePackage + ".service"); + globalConfig.setControllerPackage(basePackage + ".controller"); + + return globalConfig; + } + + public static void generateByTemplate(FeatureGenerateBo featureGenerateBo) { + String className = featureGenerateBo.getClassName(); + if (StringUtils.isEmpty(className)) { + return; + } + Map data = new HashMap<>(); + data.put("className", className); + data.put("author", "goodjava@qq.com"); + + String basePath = featureGenerateBo.getBasePath(); + String basePackage = featureGenerateBo.getBasePackage(); + String midPath = replaceDotWithSlash(basePackage); + log.info("generate by template with base path:{}", basePath); + // 调用方法并获取结果 + if (featureGenerateBo.isCreatePojo()) { + String pojo = TemplateUtils.renderTemplateFromFile("tlp/pojo.java", data); + TemplateUtils.writeStringToFile(pojo, getFilePath(basePath, featureGenerateBo.getServiceModulePath(), midPath, "/model/po/", className, ".java")); + } + + if (featureGenerateBo.isCreateVo()) { + String vo = TemplateUtils.renderTemplateFromFile("tlp/vo.java", data); + TemplateUtils.writeStringToFile(vo, getFilePath(basePath, featureGenerateBo.getApiModulePath(), midPath, "/api/vo/", className, "VO.java")); + } + + if (featureGenerateBo.isCreateTransfer()) { + String transfer = TemplateUtils.renderTemplateFromFile("tlp/transfer.java", data); + TemplateUtils.writeStringToFile(transfer, getFilePath(basePath, featureGenerateBo.getServiceModulePath(), midPath, "/model/transfer/", className, "Transfer.java")); + } + + if (featureGenerateBo.isCreateService()) { + String service = TemplateUtils.renderTemplateFromFile("tlp/service.java", data); + TemplateUtils.writeStringToFile(service, getFilePath(basePath, featureGenerateBo.getServiceModulePath(), midPath, "/service/", className, "Service.java")); + } + + if (featureGenerateBo.isCreateTest()) { + String cn = className + "ServiceTest.java"; + String test = TemplateUtils.renderTemplateFromFile("tlp/test.java", data); + TemplateUtils.writeStringToFile(test, basePath + "/" + featureGenerateBo.getServerModulePath() + "/src/test/java/run/mone/test/service/" + cn); + } + + if (featureGenerateBo.isCreateController()) { + String controller = TemplateUtils.renderTemplateFromFile("tlp/controller.java", data); + TemplateUtils.writeStringToFile(controller, getFilePath(basePath, featureGenerateBo.getServerModulePath(), midPath, "/controller/", className, "Controller.java")); + } + } + + private static String getFilePath(String basePath, String modulePath, String midPath, String subPath, String className, String suffix) { + return basePath + "/" + modulePath + "/src/main/java/" + midPath + subPath + className + suffix; + } + + // 将字符串中的"."替换为"/" + public static String replaceDotWithSlash(String input) { + if (input == null) { + return null; + } + return input.replace(".", "/"); + } +} diff --git a/jcommon/codegen/src/main/java/run/mone/ai/codegen/UnitTestGenerator.java b/jcommon/codegen/src/main/java/run/mone/ai/codegen/UnitTestGenerator.java new file mode 100644 index 000000000..3c9cc8556 --- /dev/null +++ b/jcommon/codegen/src/main/java/run/mone/ai/codegen/UnitTestGenerator.java @@ -0,0 +1,12 @@ +package run.mone.ai.codegen; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author goodjava@qq.com, HawickMason@xiaomi.com + * @date 7/11/24 14:14 + */ +@Slf4j +public class UnitTestGenerator { + +} diff --git a/jcommon/codegen/src/main/java/run/mone/ai/codegen/bo/FeatureGeneratType.java b/jcommon/codegen/src/main/java/run/mone/ai/codegen/bo/FeatureGeneratType.java new file mode 100644 index 000000000..2773cc4e0 --- /dev/null +++ b/jcommon/codegen/src/main/java/run/mone/ai/codegen/bo/FeatureGeneratType.java @@ -0,0 +1,39 @@ +package run.mone.ai.codegen.bo; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author goodjava@qq.com, HawickMason@xiaomi.com + * @date 7/12/24 14:15 + */ +public enum FeatureGeneratType { + + CODE_WITH_GENERATOR(1, "使用mybatis-flex-generator生成"), + + CODE_WITH_TEMPLATE(2, "使用预制模板生成"), + + TABLE(3, "创建表"); + + private final int code; + + private final String desc; + + private static final Map valMap = Arrays.stream(values()).collect(Collectors.toMap(FeatureGeneratType::getCode, Function.identity())); + + FeatureGeneratType(int code, String desc) { + this.code = code; + this.desc = desc; + } + + public int getCode() { + return code; + } + + public String getDesc() { + return desc; + } + +} diff --git a/jcommon/codegen/src/main/java/run/mone/ai/codegen/bo/FeatureGenerateBo.java b/jcommon/codegen/src/main/java/run/mone/ai/codegen/bo/FeatureGenerateBo.java new file mode 100644 index 000000000..cce56c8e3 --- /dev/null +++ b/jcommon/codegen/src/main/java/run/mone/ai/codegen/bo/FeatureGenerateBo.java @@ -0,0 +1,76 @@ +package run.mone.ai.codegen.bo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author goodjava@qq.com, HawickMason@xiaomi.com + * @date 7/12/24 14:10 + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +@Builder +public class FeatureGenerateBo { + + @Builder.Default + private FeatureGeneratType type = FeatureGeneratType.CODE_WITH_GENERATOR; + + @Builder.Default + private String tableName = ""; + + @Builder.Default + private String sql = ""; + + @Builder.Default + private String jdbcUrl = ""; + + @Builder.Default + private String userName = ""; + + @Builder.Default + private String password = ""; + + + @Builder.Default + private String basePackage = ""; + + @Builder.Default + private String className = "Dummy"; + + @Builder.Default + private String auth = ""; + + @Builder.Default + private String basePath = ""; + + @Builder.Default + private String serverModulePath = ""; + + @Builder.Default + private String serviceModulePath = ""; + + @Builder.Default + private String apiModulePath = ""; + + @Builder.Default + private boolean createPojo = false; + + @Builder.Default + private boolean createVo = false; + + @Builder.Default + private boolean createTransfer = false; + + @Builder.Default + private boolean createService = false; + + @Builder.Default + private boolean createTest = true; + + @Builder.Default + private boolean createController = false; + +} diff --git a/jcommon/codegen/src/main/java/run/mone/ai/codegen/util/TemplateUtils.java b/jcommon/codegen/src/main/java/run/mone/ai/codegen/util/TemplateUtils.java new file mode 100644 index 000000000..4d2805cbe --- /dev/null +++ b/jcommon/codegen/src/main/java/run/mone/ai/codegen/util/TemplateUtils.java @@ -0,0 +1,73 @@ +package run.mone.ai.codegen.util; + +import com.google.common.collect.Lists; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; +import org.beetl.core.Configuration; +import org.beetl.core.Function; +import org.beetl.core.GroupTemplate; +import org.beetl.core.Template; +import org.beetl.core.resource.StringTemplateResourceLoader; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; + +/** + * @author goodjava@qq.com, HawickMason@xiaomi.com + * @date 7/12/24 11:05 AM + */ +@Slf4j +public class TemplateUtils { + + public static String renderTemplate(String template, Map m) { + return renderTemplate(template, m, Lists.newArrayList()); + } + + public static String renderTemplate(String template, Map m, List> functionList) { + try { + if (template == null || template.isEmpty()) { + return ""; + } + StringTemplateResourceLoader resourceLoader = new StringTemplateResourceLoader(); + Configuration cfg = Configuration.defaultConfiguration(); + GroupTemplate gt = new GroupTemplate(resourceLoader, cfg); + functionList.forEach(it -> gt.registerFunction(it.getKey(), it.getValue())); + Template t = gt.getTemplate(template); + m.forEach((k, v) -> t.binding(k, v)); + String str = t.render(); + return str; + } catch (Throwable ex) { + log.error("renderTemplate", ex); + } + return ""; + } + + //读取resources下的模板文件,然后渲染成String(class) + @SneakyThrows + public static String renderTemplateFromFile(String templateFileName, Map m) { + try { + InputStream is = TemplateUtils.class.getClassLoader().getResourceAsStream(templateFileName); + //读取is成String + String template = new String(is.readAllBytes(), StandardCharsets.UTF_8); + return renderTemplate(template, m); + } catch (IOException ex) { + log.error("Error reading template file", ex); + } + return ""; + } + + //把String写到指定文件中(class) + public static void writeStringToFile(String content, String filePath) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) { + writer.write(content); + } catch (IOException ex) { + log.error("Error writing to file", ex); + } + } +} diff --git a/jcommon/codegen/src/main/resources/tlp/code_test.txt b/jcommon/codegen/src/main/resources/tlp/code_test.txt new file mode 100644 index 000000000..bc3775302 --- /dev/null +++ b/jcommon/codegen/src/main/resources/tlp/code_test.txt @@ -0,0 +1,2 @@ +name:${name} +name:${strutil.toLowerCase(name)} diff --git a/jcommon/codegen/src/main/resources/tlp/controller.java b/jcommon/codegen/src/main/resources/tlp/controller.java new file mode 100644 index 000000000..93807abe2 --- /dev/null +++ b/jcommon/codegen/src/main/resources/tlp/controller.java @@ -0,0 +1,41 @@ +package run.mone.controller; + +import com.xiaomi.mone.http.docs.annotations.HttpApiDoc; +import com.xiaomi.mone.http.docs.annotations.HttpApiModule; +import com.xiaomi.mone.http.docs.annotations.MiApiRequestMethod; +import com.xiaomi.youpin.docean.anno.Controller; +import com.xiaomi.youpin.docean.anno.RequestMapping; +import com.xiaomi.youpin.infra.rpc.Result; +import lombok.extern.slf4j.Slf4j; +import run.mone.api.vo.${className}VO; +import run.mone.model.po.${className}; +import run.mone.model.transfer.${className}Transfer; +import run.mone.service.${className}Service; + +import javax.annotation.Resource; + +/** + * @author ${author} + */ +@HttpApiModule(value = "${className}Controller", apiController = ${className}Controller.class) +@Controller +@RequestMapping(path = "/api/${strutil.toLowerCase(className)}") +@Slf4j +public class ${className}Controller extends MongodbController<${className}> { + + @Resource + private ${className}Service ${strutil.toLowerCase(className)}Service; + + public ${className}Controller() { + super(${className}.class); + } + + + @RequestMapping(path = "/create", method = "post") + @HttpApiDoc(value = "/api/${strutil.toLowerCase(className)}/create", method = MiApiRequestMethod.POST, apiName = "创建") + public Result create${className}(${className}VO ${strutil.toLowerCase(className)}VO) { + ${className} ${strutil.toLowerCase(className)} = ${className}Transfer.vo2po(${strutil.toLowerCase(className)}VO); + boolean result = ${strutil.toLowerCase(className)}Service.save(${strutil.toLowerCase(className)}); + return Result.success(result); + } +} \ No newline at end of file diff --git a/jcommon/codegen/src/main/resources/tlp/pojo.java b/jcommon/codegen/src/main/resources/tlp/pojo.java new file mode 100644 index 000000000..188a8846f --- /dev/null +++ b/jcommon/codegen/src/main/resources/tlp/pojo.java @@ -0,0 +1,32 @@ +package run.mone.model.po; + +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import run.mone.bo.MongoBo; + +/** + * @author ${author} + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Entity +public class ${className} implements MongoBo { + + @Id + private String id; + + private long ctime; + + private long utime; + + private int state; + + private int version; + +} \ No newline at end of file diff --git a/jcommon/codegen/src/main/resources/tlp/service.java b/jcommon/codegen/src/main/resources/tlp/service.java new file mode 100644 index 000000000..910f52f3f --- /dev/null +++ b/jcommon/codegen/src/main/resources/tlp/service.java @@ -0,0 +1,20 @@ +package run.mone.service; + +import com.xiaomi.youpin.docean.anno.Service; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import run.mone.model.po.${className}; + + +/** + * @author ${author} + */ +@Data +@Service +@Slf4j +public class ${className}Service extends MongoService<${className}> { + + public ${className}Service() { + super(${className}.class); + } +} \ No newline at end of file diff --git a/jcommon/codegen/src/main/resources/tlp/test.java b/jcommon/codegen/src/main/resources/tlp/test.java new file mode 100644 index 000000000..2c5eb309e --- /dev/null +++ b/jcommon/codegen/src/main/resources/tlp/test.java @@ -0,0 +1,32 @@ +package run.mone.test.service; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import run.mone.junit.DoceanConfiguration; +import run.mone.junit.DoceanExtension; +import javax.annotation.Resource; +import run.mone.service.${className}Service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import run.mone.bo.User; +import run.mone.common.Result; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author ${author} + */ +@ExtendWith(DoceanExtension.class) +@DoceanConfiguration(basePackage = {"run.mone", "com.xiaomi.youpin.docean.plugin", "com.xiaomi.mone.http.docs.core"}) +public class ${className}ServiceTest { + + + @Resource + private ${className}Service ${strutil.toLowerCase(className)}Service; + + +} \ No newline at end of file diff --git a/jcommon/codegen/src/main/resources/tlp/transfer.java b/jcommon/codegen/src/main/resources/tlp/transfer.java new file mode 100644 index 000000000..ce469927c --- /dev/null +++ b/jcommon/codegen/src/main/resources/tlp/transfer.java @@ -0,0 +1,24 @@ +package run.mone.model.transfer; + +import run.mone.api.vo.${className}VO; +import run.mone.model.po.${className}; +import com.xiaomi.youpin.docean.common.BeanUtils; + + +/** + * @author ${author} + */ +public class ${className}Transfer { + + public static ${className}VO po2vo(${className} po) { + ${className}VO vo = new ${className}VO(); + BeanUtils.copy(po, vo); + return vo; + } + + public static ${className} vo2po(${className}VO vo) { + ${className} po = new ${className}(); + BeanUtils.copy(vo, po); + return po; + } +} \ No newline at end of file diff --git a/jcommon/codegen/src/main/resources/tlp/vo.java b/jcommon/codegen/src/main/resources/tlp/vo.java new file mode 100644 index 000000000..1ed22d2ab --- /dev/null +++ b/jcommon/codegen/src/main/resources/tlp/vo.java @@ -0,0 +1,21 @@ +package run.mone.api.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author ${author} + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ${className}VO implements Serializable { + + private String id; + +} \ No newline at end of file