引入 Velocity 模板引擎,生成代码~

This commit is contained in:
YunaiV 2021-02-01 00:48:19 +08:00
parent 0be7138eef
commit 1bea2ea7f8
9 changed files with 187 additions and 38 deletions

View File

@ -54,6 +54,7 @@
<mapstruct.version>1.4.1.Final</mapstruct.version> <mapstruct.version>1.4.1.Final</mapstruct.version>
<hutool.version>5.5.6</hutool.version> <hutool.version>5.5.6</hutool.version>
<easyexcel.verion>2.2.7</easyexcel.verion> <easyexcel.verion>2.2.7</easyexcel.verion>
<velocity.version>2.2</velocity.version>
</properties> </properties>
<!-- 依赖声明 --> <!-- 依赖声明 -->
@ -221,6 +222,12 @@
<version>${easyexcel.verion}</version> <version>${easyexcel.verion}</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
</dependencies> </dependencies>

View File

@ -5,8 +5,7 @@ import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.*;
import lombok.EqualsAndHashCode;
/** /**
* 字典类型表 * 字典类型表
@ -16,6 +15,10 @@ import lombok.EqualsAndHashCode;
@TableName("sys_dict_type") @TableName("sys_dict_type")
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SysDictTypeDO extends BaseDO { public class SysDictTypeDO extends BaseDO {
/** /**

View File

@ -2,8 +2,18 @@ package cn.iocoder.dashboard.modules.tool.dal.mysql.dao.coegen;
import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dataobject.codegen.ToolCodegenColumnDO; import cn.iocoder.dashboard.modules.tool.dal.mysql.dataobject.codegen.ToolCodegenColumnDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper @Mapper
public interface ToolCodegenColumnMapper extends BaseMapperX<ToolCodegenColumnDO> { public interface ToolCodegenColumnMapper extends BaseMapperX<ToolCodegenColumnDO> {
default List<ToolCodegenColumnDO> selectByTableId(Long tableId) {
return selectList(new QueryWrapper<ToolCodegenColumnDO>()
.eq("table_id", tableId)
.orderByAsc("ordinal_position"));
}
} }

View File

@ -95,16 +95,19 @@ public class ToolCodegenColumnDO extends BaseDO {
*/ */
private Boolean updateOperation; private Boolean updateOperation;
/** /**
* 是否为 List 查询操作的返回字段 * 是否为 List 查询操作的字段
*/ */
private Boolean listOperationResult; private Boolean listOperation;
/** /**
* List 查询操作的条件类型 * List 查询操作的条件类型
* 如果为空则说明不是查询字段
* *
* 枚举 {@link ToolCodegenColumnListConditionEnum} * 枚举 {@link ToolCodegenColumnListConditionEnum}
*/ */
private String listOperationCondition; private String listOperationCondition;
/**
* 是否为 List 查询操作的返回字段
*/
private Boolean listOperationResult;
// ========== UI 相关字段 ========== // ========== UI 相关字段 ==========

View File

@ -0,0 +1,44 @@
package cn.iocoder.dashboard.modules.tool.service.codegen.impl;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateUtil;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dataobject.codegen.ToolCodegenColumnDO;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dataobject.codegen.ToolCodegenTableDO;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 代码生成的引擎用于具体生成代码
* 目前基于 {@link org.apache.velocity.app.Velocity} 模板引擎实现
*
* 考虑到 Java 模板引擎的框架非常多FreemarkerVelocityThymeleaf 等等所以我们采用 hutool 封装的 {@link cn.hutool.extra.template.Template} 抽象
*
* @author 芋道源码
*/
@Component
public class ToolCodegenEngine {
/**
* 模板引擎 hutool 实现
*/
private final TemplateEngine templateEngine;
public ToolCodegenEngine() {
TemplateConfig config = new TemplateConfig();
config.setResourceMode(TemplateConfig.ResourceMode.CLASSPATH);
this.templateEngine = TemplateUtil.createEngine(config);
}
public void execute(ToolCodegenTableDO table, List<ToolCodegenColumnDO> columns) {
Map<String, Object> bindingMap = new HashMap<>();
bindingMap.put("table", table);
bindingMap.put("columns", columns);
String result = templateEngine.getTemplate("codegen/dal/do.vm").render(bindingMap);
System.out.println(result);
}
}

View File

@ -5,16 +5,9 @@ import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.dashboard.modules.tool.convert.codegen.CodegenConvert; import cn.iocoder.dashboard.modules.tool.convert.codegen.CodegenConvert;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dao.coegen.ToolCodegenColumnMapper; import cn.iocoder.dashboard.modules.tool.dal.mysql.dao.coegen.*;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dao.coegen.ToolCodegenTableMapper; import cn.iocoder.dashboard.modules.tool.dal.mysql.dataobject.codegen.*;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dao.coegen.ToolInformationSchemaColumnMapper; import cn.iocoder.dashboard.modules.tool.enums.codegen.*;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dao.coegen.ToolInformationSchemaTableMapper;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dataobject.codegen.ToolCodegenColumnDO;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dataobject.codegen.ToolCodegenTableDO;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dataobject.codegen.ToolInformationSchemaColumnDO;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dataobject.codegen.ToolInformationSchemaTableDO;
import cn.iocoder.dashboard.modules.tool.enums.codegen.ToolCodegenColumnHtmlTypeEnum;
import cn.iocoder.dashboard.modules.tool.enums.codegen.ToolCodegenColumnListConditionEnum;
import cn.iocoder.dashboard.modules.tool.service.codegen.ToolCodegenService; import cn.iocoder.dashboard.modules.tool.service.codegen.ToolCodegenService;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -60,8 +53,7 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
/** /**
* 新增操作不需要传递的字段 * 新增操作不需要传递的字段
*/ */
private static final Set<String> CREATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet( private static final Set<String> CREATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id");
"id");
/** /**
* 修改操作不需要传递的字段 * 修改操作不需要传递的字段
*/ */
@ -69,11 +61,11 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
/** /**
* 列表操作的条件不需要传递的字段 * 列表操作的条件不需要传递的字段
*/ */
private static final Set<String> LIST_OPERATION_CONDITION_COLUMN = Sets.newHashSet(); private static final Set<String> LIST_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id");
/** /**
* 列表操作的结果不需要返回的字段 * 列表操作的结果不需要返回的字段
*/ */
private static final Set<String> LIST_OPERATION_RESULT_COLUMN = Sets.newHashSet(); private static final Set<String> LIST_OPERATION_RESULT_EXCLUDE_COLUMN = Sets.newHashSet();
/** /**
* Java 类型与 MySQL 类型的映射关系 * Java 类型与 MySQL 类型的映射关系
@ -90,13 +82,15 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
.build(); .build();
static { static {
// 处理 OPERATION 相关的字段
Arrays.stream(BaseDO.class.getDeclaredFields()).forEach(field -> { Arrays.stream(BaseDO.class.getDeclaredFields()).forEach(field -> {
CREATE_OPERATION_EXCLUDE_COLUMN.add(field.getName()); CREATE_OPERATION_EXCLUDE_COLUMN.add(field.getName());
UPDATE_OPERATION_EXCLUDE_COLUMN.add(field.getName()); UPDATE_OPERATION_EXCLUDE_COLUMN.add(field.getName());
LIST_OPERATION_CONDITION_COLUMN.add(field.getName()); LIST_OPERATION_EXCLUDE_COLUMN.add(field.getName());
LIST_OPERATION_RESULT_COLUMN.add(field.getName()); LIST_OPERATION_RESULT_EXCLUDE_COLUMN.add(field.getName());
}); });
LIST_OPERATION_RESULT_COLUMN.remove("create_time"); // 创建时间还是需要返回的 LIST_OPERATION_EXCLUDE_COLUMN.remove("create_time"); // 创建时间还是可能需要传递的
LIST_OPERATION_RESULT_EXCLUDE_COLUMN.remove("create_time"); // 创建时间还是需要返回的
} }
@Resource @Resource
@ -134,7 +128,7 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
columns.forEach(column -> { columns.forEach(column -> {
initColumnDefault(column); initColumnDefault(column);
column.setTableId(table.getId()); column.setTableId(table.getId());
codegenColumnMapper.insert(column); codegenColumnMapper.insert(column); // TODO 批量插入
}); });
return table.getId(); return table.getId();
} }
@ -150,10 +144,11 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
table.setBusinessName(StrUtil.subAfter(table.getTableName(), table.setBusinessName(StrUtil.subAfter(table.getTableName(),
'_', false)); // 第一个 _ 前缀的后面作为 module 名字 '_', false)); // 第一个 _ 前缀的后面作为 module 名字
table.setBusinessName(StrUtil.toCamelCase(table.getBusinessName())); // 可能存在多个 _ 的情况转换成驼峰 table.setBusinessName(StrUtil.toCamelCase(table.getBusinessName())); // 可能存在多个 _ 的情况转换成驼峰
table.setClassName(StrUtil.toCamelCase(table.getClassName())); // 驼峰 table.setClassName(StrUtil.upperFirst(StrUtil.toCamelCase(table.getTableName()))); // 驼峰 + 首字母大写
table.setClassComment(StrUtil.subBefore(table.getClassComment(), // 去除结尾的表作为类描述 table.setClassComment(StrUtil.subBefore(table.getTableComment(), // 去除结尾的表作为类描述
'表', true)); '表', true));
table.setAuthor("芋艿"); // TODO 稍后改成创建人 table.setAuthor("芋艿"); // TODO 稍后改成创建人
table.setTemplateType(ToolCodegenTemplateTypeEnum.CRUD.getType());
} }
/** /**
@ -175,7 +170,7 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
column.setJavaField(StrUtil.toCamelCase(column.getColumnName())); column.setJavaField(StrUtil.toCamelCase(column.getColumnName()));
// 处理 dictType 字段暂无 // 处理 dictType 字段暂无
// 处理 javaType 字段 // 处理 javaType 字段
String dbType = StrUtil.subBefore(column.getColumnName(), ')', false); String dbType = StrUtil.subBefore(column.getColumnType(), '(', false);
javaTypeMappings.entrySet().stream() javaTypeMappings.entrySet().stream()
.filter(entry -> entry.getValue().contains(dbType)) .filter(entry -> entry.getValue().contains(dbType))
.findFirst().ifPresent(entry -> column.setJavaType(entry.getKey())); .findFirst().ifPresent(entry -> column.setJavaType(entry.getKey()));
@ -187,26 +182,29 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
private void processColumnOperation(ToolCodegenColumnDO column) { private void processColumnOperation(ToolCodegenColumnDO column) {
// 处理 createOperation 字段 // 处理 createOperation 字段
column.setCreateOperation(!CREATE_OPERATION_EXCLUDE_COLUMN.contains(column.getColumnName()) column.setCreateOperation(!CREATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
&& !column.getPrimaryKey()); // 非主键 && !column.getPrimaryKey()); // 对于主键创建时无需传递
// 处理 updateOperation 字段 // 处理 updateOperation 字段
column.setUpdateOperation(!UPDATE_OPERATION_EXCLUDE_COLUMN.contains(column.getColumnName())); column.setUpdateOperation(!UPDATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
// 处理 listOperationResult 字段 || column.getPrimaryKey()); // 对于主键更新时需要传递
column.setListOperationResult(!LIST_OPERATION_RESULT_COLUMN.contains(column.getColumnName())); // 处理 listOperation 字段
// 处理 listOperationCondition 字段默认设置为需要过滤的条件手动进行取消 column.setListOperation(!LIST_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
if (!LIST_OPERATION_CONDITION_COLUMN.contains(column.getColumnName()) && !column.getPrimaryKey()); // 对于主键列表过滤不需要传递
&& !column.getPrimaryKey()) { // 非主键 // 处理 listOperationCondition 字段
columnListOperationConditionMappings.entrySet().stream()
.filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey()))
.findFirst().ifPresent(entry -> column.setListOperationCondition(entry.getValue().getCondition()));
if (column.getListOperationCondition() == null) {
column.setListOperationCondition(ToolCodegenColumnListConditionEnum.EQ.getCondition()); column.setListOperationCondition(ToolCodegenColumnListConditionEnum.EQ.getCondition());
} }
columnListOperationConditionMappings.entrySet().stream() // 处理 listOperationResult 字段
.filter(entry -> StrUtil.endWithIgnoreCase(column.getColumnName(), entry.getKey())) column.setListOperationResult(!LIST_OPERATION_RESULT_EXCLUDE_COLUMN.contains(column.getJavaField()));
.findFirst().ifPresent(entry -> column.setListOperationCondition(entry.getValue().getCondition()));
} }
private void processColumnUI(ToolCodegenColumnDO column) { private void processColumnUI(ToolCodegenColumnDO column) {
// 基于后缀进行匹配 // 基于后缀进行匹配
columnHtmlTypeMappings.entrySet().stream() columnHtmlTypeMappings.entrySet().stream()
.filter(entry -> StrUtil.endWithIgnoreCase(column.getColumnName(), entry.getKey())) .filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey()))
.findFirst().ifPresent(entry -> column.setHtmlType(entry.getValue().getType())); .findFirst().ifPresent(entry -> column.setHtmlType(entry.getValue().getType()));
// 如果是 Boolean 类型时设置为 radio 类型. // 如果是 Boolean 类型时设置为 radio 类型.
// 其它类型因为字段名可以相对保障所以不进行处理例如说 date 对应 datetime 类型. // 其它类型因为字段名可以相对保障所以不进行处理例如说 date 对应 datetime 类型.

View File

@ -0,0 +1,30 @@
import com.baomidou.mybatisplus.annotation.*;
import lombok.*;
import java.util.*;
/**
* ${table.description}
*/
@TableName("${table.tableName}")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ${table.tableName}DO extends BaseDO {
#foreach ($column in $columns)
/**
* ${column.columnComment}
*/
#if(${column.primaryKey} && ${column.javaType} != 'String')
@TableId
#end
#if(${column.primaryKey} && ${column.javaType} == 'String')
@TableId(type = IdType.INPUT)
#end
private ${column.javaType} ${column.javaField};
#end
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.dashboard.modules.tool.service.codegen.impl;
import cn.iocoder.dashboard.TestApplication;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dao.coegen.ToolCodegenColumnMapper;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dao.coegen.ToolCodegenTableMapper;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dataobject.codegen.ToolCodegenColumnDO;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dataobject.codegen.ToolCodegenTableDO;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.List;
@SpringBootTest(classes = TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ToolCodegenEngineTest {
@Resource
private ToolCodegenTableMapper codegenTableMapper;
@Resource
private ToolCodegenColumnMapper codegenColumnMapper;
@Resource
private ToolCodegenEngine codegenEngine;
@Test
public void testExecute() {
ToolCodegenTableDO table = codegenTableMapper.selectById(8);
List<ToolCodegenColumnDO> columns = codegenColumnMapper.selectByTableId(table.getId());
codegenEngine.execute(table, columns);
}
}

View File

@ -0,0 +1,22 @@
package cn.iocoder.dashboard.modules.tool.service.codegen.impl;
import cn.iocoder.dashboard.TestApplication;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest(classes = TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ToolCodegenServiceImplTest {
@Resource
private ToolCodegenServiceImpl toolCodegenService;
@Test
public void tetCreateCodegenTable() {
toolCodegenService.createCodegenTable("sys_dict_type");
}
}