抽象出 ToolCodegenBuilder 类,负责 table 和 column 的构建,让 ToolCodegenService 专注于逻辑的组合

This commit is contained in:
YunaiV 2021-02-02 00:06:38 +08:00
parent 1bea2ea7f8
commit 6c5c32c845
2 changed files with 210 additions and 168 deletions

View File

@ -0,0 +1,192 @@
package cn.iocoder.dashboard.modules.tool.service.codegen.impl;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.dashboard.modules.tool.convert.codegen.CodegenConvert;
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.enums.codegen.ToolCodegenTemplateTypeEnum;
import com.google.common.collect.Sets;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.*;
/**
* 代码生成器的 Builder负责
* 1. 将数据库的表 {@link ToolInformationSchemaTableDO} 定义构建成 {@link ToolCodegenTableDO}
* 2. 将数据库的列 {@link ToolInformationSchemaColumnDO} 构定义建成 {@link ToolCodegenColumnDO}
*/
@Component
public class ToolCodegenBuilder {
/**
* 字段名与 {@link ToolCodegenColumnListConditionEnum} 的默认映射
* 注意字段的匹配以后缀的方式
*/
private static final Map<String, ToolCodegenColumnListConditionEnum> columnListOperationConditionMappings =
MapUtil.<String, ToolCodegenColumnListConditionEnum>builder()
.put("name", ToolCodegenColumnListConditionEnum.LIKE)
.put("time", ToolCodegenColumnListConditionEnum.BETWEEN)
.put("date", ToolCodegenColumnListConditionEnum.BETWEEN)
.build();
/**
* 字段名与 {@link ToolCodegenColumnHtmlTypeEnum} 的默认映射
* 注意字段的匹配以后缀的方式
*/
private static final Map<String, ToolCodegenColumnHtmlTypeEnum> columnHtmlTypeMappings =
MapUtil.<String, ToolCodegenColumnHtmlTypeEnum>builder()
.put("status", ToolCodegenColumnHtmlTypeEnum.RADIO)
.put("sex", ToolCodegenColumnHtmlTypeEnum.RADIO)
.put("type", ToolCodegenColumnHtmlTypeEnum.SELECT)
.put("image", ToolCodegenColumnHtmlTypeEnum.UPLOAD_IMAGE)
.put("file", ToolCodegenColumnHtmlTypeEnum.UPLOAD_FILE)
.put("content", ToolCodegenColumnHtmlTypeEnum.EDITOR)
.build();
/**
* 新增操作不需要传递的字段
*/
private static final Set<String> CREATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id");
/**
* 修改操作不需要传递的字段
*/
private static final Set<String> UPDATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet();
/**
* 列表操作的条件不需要传递的字段
*/
private static final Set<String> LIST_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id");
/**
* 列表操作的结果不需要返回的字段
*/
private static final Set<String> LIST_OPERATION_RESULT_EXCLUDE_COLUMN = Sets.newHashSet();
/**
* Java 类型与 MySQL 类型的映射关系
*/
private static final Map<String, Set<String>> javaTypeMappings = MapUtil.<String, Set<String>>builder()
.put(Boolean.class.getSimpleName(), Sets.newHashSet("bit"))
.put(Integer.class.getSimpleName(), Sets.newHashSet("tinyint", "smallint", "mediumint", "int"))
.put(Long.class.getSimpleName(), Collections.singleton("bigint"))
.put(Double.class.getSimpleName(), Sets.newHashSet("float", "double"))
.put(BigDecimal.class.getSimpleName(), Sets.newHashSet("decimal", "numeric"))
.put(String.class.getSimpleName(), Sets.newHashSet("tinytext", "text", "mediumtext", "longtext", // 长文本
"char", "varchar", "nvarchar", "varchar2")) // 短文本
.put(Date.class.getSimpleName(), Sets.newHashSet("datetime", "time", "date", "timestamp"))
.build();
static {
// 处理 OPERATION 相关的字段
Arrays.stream(BaseDO.class.getDeclaredFields()).forEach(field -> {
CREATE_OPERATION_EXCLUDE_COLUMN.add(field.getName());
UPDATE_OPERATION_EXCLUDE_COLUMN.add(field.getName());
LIST_OPERATION_EXCLUDE_COLUMN.add(field.getName());
LIST_OPERATION_RESULT_EXCLUDE_COLUMN.add(field.getName());
});
LIST_OPERATION_EXCLUDE_COLUMN.remove("create_time"); // 创建时间还是可能需要传递的
LIST_OPERATION_RESULT_EXCLUDE_COLUMN.remove("create_time"); // 创建时间还是需要返回的
}
public ToolCodegenTableDO buildTable(ToolInformationSchemaTableDO schemaTable) {
ToolCodegenTableDO table = CodegenConvert.INSTANCE.convert(schemaTable);
initTableDefault(table);
return table;
}
/**
* 初始化 Table 表的默认字段
*
* @param table 表定义
*/
private void initTableDefault(ToolCodegenTableDO table) {
table.setModuleName(StrUtil.subBefore(table.getTableName(),
'_', false)); // 第一个 _ 前缀的前面作为 module 名字
table.setBusinessName(StrUtil.subAfter(table.getTableName(),
'_', false)); // 第一个 _ 前缀的后面作为 module 名字
table.setBusinessName(StrUtil.toCamelCase(table.getBusinessName())); // 可能存在多个 _ 的情况转换成驼峰
table.setClassName(StrUtil.upperFirst(StrUtil.toCamelCase(table.getTableName()))); // 驼峰 + 首字母大写
table.setClassComment(StrUtil.subBefore(table.getTableComment(), // 去除结尾的表作为类描述
'表', true));
table.setAuthor("芋艿"); // TODO 稍后改成创建人
table.setTemplateType(ToolCodegenTemplateTypeEnum.CRUD.getType());
}
public List<ToolCodegenColumnDO> buildColumns(List<ToolInformationSchemaColumnDO> schemaColumns) {
List<ToolCodegenColumnDO> columns = CodegenConvert.INSTANCE.convertList(schemaColumns);
columns.forEach(this::initColumnDefault);
return columns;
}
/**
* 初始化 Column 列的默认字段
*
* @param column 列定义
*/
private void initColumnDefault(ToolCodegenColumnDO column) {
// 处理 Java 相关的字段的默认值
processColumnJava(column);
// 处理 CRUD 相关的字段的默认值
processColumnOperation(column);
// 处理 UI 相关的字段的默认值
processColumnUI(column);
}
private void processColumnJava(ToolCodegenColumnDO column) {
// 处理 javaField 字段
column.setJavaField(StrUtil.toCamelCase(column.getColumnName()));
// 处理 dictType 字段暂无
// 处理 javaType 字段
String dbType = StrUtil.subBefore(column.getColumnType(), '(', false);
javaTypeMappings.entrySet().stream()
.filter(entry -> entry.getValue().contains(dbType))
.findFirst().ifPresent(entry -> column.setJavaType(entry.getKey()));
if (column.getJavaType() == null) {
throw new IllegalStateException(String.format("column(%s) 的数据库类型(%s) 找不到匹配的 Java 类型",
column.getColumnName(), column.getColumnType()));
}
}
private void processColumnOperation(ToolCodegenColumnDO column) {
// 处理 createOperation 字段
column.setCreateOperation(!CREATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
&& !column.getPrimaryKey()); // 对于主键创建时无需传递
// 处理 updateOperation 字段
column.setUpdateOperation(!UPDATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
|| column.getPrimaryKey()); // 对于主键更新时需要传递
// 处理 listOperation 字段
column.setListOperation(!LIST_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
&& !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());
}
// 处理 listOperationResult 字段
column.setListOperationResult(!LIST_OPERATION_RESULT_EXCLUDE_COLUMN.contains(column.getJavaField()));
}
private void processColumnUI(ToolCodegenColumnDO column) {
// 基于后缀进行匹配
columnHtmlTypeMappings.entrySet().stream()
.filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey()))
.findFirst().ifPresent(entry -> column.setHtmlType(entry.getValue().getType()));
// 如果是 Boolean 类型时设置为 radio 类型.
// 其它类型因为字段名可以相对保障所以不进行处理例如说 date 对应 datetime 类型.
if (Boolean.class.getSimpleName().equals(column.getJavaType())) {
column.setHtmlType(ToolCodegenColumnHtmlTypeEnum.RADIO.getType());
}
// 兜底设置默认为 input 类型
if (column.getHtmlType() == null) {
column.setHtmlType(ToolCodegenColumnHtmlTypeEnum.INPUT.getType());
}
}
}

View File

@ -1,21 +1,20 @@
package cn.iocoder.dashboard.modules.tool.service.codegen.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.dashboard.modules.tool.convert.codegen.CodegenConvert;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dao.coegen.*;
import cn.iocoder.dashboard.modules.tool.dal.mysql.dataobject.codegen.*;
import cn.iocoder.dashboard.modules.tool.enums.codegen.*;
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.dao.coegen.ToolInformationSchemaColumnMapper;
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.service.codegen.ToolCodegenService;
import com.google.common.collect.Sets;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.List;
/**
* 代码生成 Service 实现类
@ -25,74 +24,6 @@ import java.util.*;
@Service
public class ToolCodegenServiceImpl implements ToolCodegenService {
/**
* 字段名与 {@link ToolCodegenColumnListConditionEnum} 的默认映射
* 注意字段的匹配以后缀的方式
*/
private static final Map<String, ToolCodegenColumnListConditionEnum> columnListOperationConditionMappings =
MapUtil.<String, ToolCodegenColumnListConditionEnum>builder()
.put("name", ToolCodegenColumnListConditionEnum.LIKE)
.put("time", ToolCodegenColumnListConditionEnum.BETWEEN)
.put("date", ToolCodegenColumnListConditionEnum.BETWEEN)
.build();
/**
* 字段名与 {@link ToolCodegenColumnHtmlTypeEnum} 的默认映射
* 注意字段的匹配以后缀的方式
*/
private static final Map<String, ToolCodegenColumnHtmlTypeEnum> columnHtmlTypeMappings =
MapUtil.<String, ToolCodegenColumnHtmlTypeEnum>builder()
.put("status", ToolCodegenColumnHtmlTypeEnum.RADIO)
.put("sex", ToolCodegenColumnHtmlTypeEnum.RADIO)
.put("type", ToolCodegenColumnHtmlTypeEnum.SELECT)
.put("image", ToolCodegenColumnHtmlTypeEnum.UPLOAD_IMAGE)
.put("file", ToolCodegenColumnHtmlTypeEnum.UPLOAD_FILE)
.put("content", ToolCodegenColumnHtmlTypeEnum.EDITOR)
.build();
/**
* 新增操作不需要传递的字段
*/
private static final Set<String> CREATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id");
/**
* 修改操作不需要传递的字段
*/
private static final Set<String> UPDATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet();
/**
* 列表操作的条件不需要传递的字段
*/
private static final Set<String> LIST_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id");
/**
* 列表操作的结果不需要返回的字段
*/
private static final Set<String> LIST_OPERATION_RESULT_EXCLUDE_COLUMN = Sets.newHashSet();
/**
* Java 类型与 MySQL 类型的映射关系
*/
private static final Map<String, Set<String>> javaTypeMappings = MapUtil.<String, Set<String>>builder()
.put(Boolean.class.getSimpleName(), Sets.newHashSet("bit"))
.put(Integer.class.getSimpleName(), Sets.newHashSet("tinyint", "smallint", "mediumint", "int"))
.put(Long.class.getSimpleName(), Collections.singleton("bigint"))
.put(Double.class.getSimpleName(), Sets.newHashSet("float", "double"))
.put(BigDecimal.class.getSimpleName(), Sets.newHashSet("decimal", "numeric"))
.put(String.class.getSimpleName(), Sets.newHashSet("tinytext", "text", "mediumtext", "longtext", // 长文本
"char", "varchar", "nvarchar", "varchar2")) // 短文本
.put(Date.class.getSimpleName(), Sets.newHashSet("datetime", "time", "date", "timestamp"))
.build();
static {
// 处理 OPERATION 相关的字段
Arrays.stream(BaseDO.class.getDeclaredFields()).forEach(field -> {
CREATE_OPERATION_EXCLUDE_COLUMN.add(field.getName());
UPDATE_OPERATION_EXCLUDE_COLUMN.add(field.getName());
LIST_OPERATION_EXCLUDE_COLUMN.add(field.getName());
LIST_OPERATION_RESULT_EXCLUDE_COLUMN.add(field.getName());
});
LIST_OPERATION_EXCLUDE_COLUMN.remove("create_time"); // 创建时间还是可能需要传递的
LIST_OPERATION_RESULT_EXCLUDE_COLUMN.remove("create_time"); // 创建时间还是需要返回的
}
@Resource
private ToolInformationSchemaTableMapper informationSchemaTableMapper;
@Resource
@ -102,6 +33,11 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
@Resource
private ToolCodegenColumnMapper codegenColumnMapper;
@Resource
private ToolCodegenBuilder codegenBuilder;
@Resource
private ToolCodegenEngine codegenEngine;
@Override
@Transactional
public Long createCodegenTable(String tableName) {
@ -119,102 +55,16 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
throw new RuntimeException(""); // TODO
}
// table 插入到数据库
ToolCodegenTableDO table = CodegenConvert.INSTANCE.convert(schemaTable);
initTableDefault(table);
// 构建 ToolCodegenTableDO 对象插入到 DB
ToolCodegenTableDO table = codegenBuilder.buildTable(schemaTable);
codegenTableMapper.insert(table);
// column 插入到数据库
List<ToolCodegenColumnDO> columns = CodegenConvert.INSTANCE.convertList(schemaColumns);
// 构建 ToolCodegenColumnDO 数组插入到 DB
List<ToolCodegenColumnDO> columns = codegenBuilder.buildColumns(schemaColumns);
columns.forEach(column -> {
initColumnDefault(column);
column.setTableId(table.getId());
codegenColumnMapper.insert(column); // TODO 批量插入
});
return table.getId();
}
/**
* 初始化 Table 表的默认字段
*
* @param table 表定义
*/
private void initTableDefault(ToolCodegenTableDO table) {
table.setModuleName(StrUtil.subBefore(table.getTableName(),
'_', false)); // 第一个 _ 前缀的前面作为 module 名字
table.setBusinessName(StrUtil.subAfter(table.getTableName(),
'_', false)); // 第一个 _ 前缀的后面作为 module 名字
table.setBusinessName(StrUtil.toCamelCase(table.getBusinessName())); // 可能存在多个 _ 的情况转换成驼峰
table.setClassName(StrUtil.upperFirst(StrUtil.toCamelCase(table.getTableName()))); // 驼峰 + 首字母大写
table.setClassComment(StrUtil.subBefore(table.getTableComment(), // 去除结尾的表作为类描述
'表', true));
table.setAuthor("芋艿"); // TODO 稍后改成创建人
table.setTemplateType(ToolCodegenTemplateTypeEnum.CRUD.getType());
}
/**
* 初始化 Column 列的默认字段
*
* @param column 列定义
*/
private void initColumnDefault(ToolCodegenColumnDO column) {
// 处理 Java 相关的字段的默认值
processColumnJava(column);
// 处理 CRUD 相关的字段的默认值
processColumnOperation(column);
// 处理 UI 相关的字段的默认值
processColumnUI(column);
}
private void processColumnJava(ToolCodegenColumnDO column) {
// 处理 javaField 字段
column.setJavaField(StrUtil.toCamelCase(column.getColumnName()));
// 处理 dictType 字段暂无
// 处理 javaType 字段
String dbType = StrUtil.subBefore(column.getColumnType(), '(', false);
javaTypeMappings.entrySet().stream()
.filter(entry -> entry.getValue().contains(dbType))
.findFirst().ifPresent(entry -> column.setJavaType(entry.getKey()));
if (column.getJavaType() == null) {
throw new IllegalStateException(String.format("column(%s) 的数据库类型(%s) 找不到匹配的 Java 类型",
column.getColumnName(), column.getColumnType()));
}
}
private void processColumnOperation(ToolCodegenColumnDO column) {
// 处理 createOperation 字段
column.setCreateOperation(!CREATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
&& !column.getPrimaryKey()); // 对于主键创建时无需传递
// 处理 updateOperation 字段
column.setUpdateOperation(!UPDATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
|| column.getPrimaryKey()); // 对于主键更新时需要传递
// 处理 listOperation 字段
column.setListOperation(!LIST_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
&& !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());
}
// 处理 listOperationResult 字段
column.setListOperationResult(!LIST_OPERATION_RESULT_EXCLUDE_COLUMN.contains(column.getJavaField()));
}
private void processColumnUI(ToolCodegenColumnDO column) {
// 基于后缀进行匹配
columnHtmlTypeMappings.entrySet().stream()
.filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey()))
.findFirst().ifPresent(entry -> column.setHtmlType(entry.getValue().getType()));
// 如果是 Boolean 类型时设置为 radio 类型.
// 其它类型因为字段名可以相对保障所以不进行处理例如说 date 对应 datetime 类型.
if (Boolean.class.getSimpleName().equals(column.getJavaType())) {
column.setHtmlType(ToolCodegenColumnHtmlTypeEnum.RADIO.getType());
}
// 兜底设置默认为 input 类型
if (column.getHtmlType() == null) {
column.setHtmlType(ToolCodegenColumnHtmlTypeEnum.INPUT.getType());
}
}
}