diff --git a/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenBuilder.java b/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenBuilder.java new file mode 100644 index 000000000..4abd7349b --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenBuilder.java @@ -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 columnListOperationConditionMappings = + MapUtil.builder() + .put("name", ToolCodegenColumnListConditionEnum.LIKE) + .put("time", ToolCodegenColumnListConditionEnum.BETWEEN) + .put("date", ToolCodegenColumnListConditionEnum.BETWEEN) + .build(); + + /** + * 字段名与 {@link ToolCodegenColumnHtmlTypeEnum} 的默认映射 + * 注意,字段的匹配以后缀的方式 + */ + private static final Map columnHtmlTypeMappings = + MapUtil.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 CREATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id"); + /** + * 修改操作,不需要传递的字段 + */ + private static final Set UPDATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet(); + /** + * 列表操作的条件,不需要传递的字段 + */ + private static final Set LIST_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id"); + /** + * 列表操作的结果,不需要返回的字段 + */ + private static final Set LIST_OPERATION_RESULT_EXCLUDE_COLUMN = Sets.newHashSet(); + + /** + * Java 类型与 MySQL 类型的映射关系 + */ + private static final Map> javaTypeMappings = MapUtil.>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 buildColumns(List schemaColumns) { + List 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()); + } + } + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java index d8b5b2421..d23ecee2b 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java @@ -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 columnListOperationConditionMappings = - MapUtil.builder() - .put("name", ToolCodegenColumnListConditionEnum.LIKE) - .put("time", ToolCodegenColumnListConditionEnum.BETWEEN) - .put("date", ToolCodegenColumnListConditionEnum.BETWEEN) - .build(); - - /** - * 字段名与 {@link ToolCodegenColumnHtmlTypeEnum} 的默认映射 - * 注意,字段的匹配以后缀的方式 - */ - private static final Map columnHtmlTypeMappings = - MapUtil.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 CREATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id"); - /** - * 修改操作,不需要传递的字段 - */ - private static final Set UPDATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet(); - /** - * 列表操作的条件,不需要传递的字段 - */ - private static final Set LIST_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id"); - /** - * 列表操作的结果,不需要返回的字段 - */ - private static final Set LIST_OPERATION_RESULT_EXCLUDE_COLUMN = Sets.newHashSet(); - - /** - * Java 类型与 MySQL 类型的映射关系 - */ - private static final Map> javaTypeMappings = MapUtil.>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 columns = CodegenConvert.INSTANCE.convertList(schemaColumns); + // 构建 ToolCodegenColumnDO 数组,插入到 DB 中 + List 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()); - } - } - }