Skip to content

Commit

Permalink
feat: 新增生成代码接口(后端代码)
Browse files Browse the repository at this point in the history
  • Loading branch information
Charles7c committed Aug 12, 2023
1 parent c67a7b6 commit 72399d9
Show file tree
Hide file tree
Showing 20 changed files with 693 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@

package top.charles7c.cnadmin.tool.config.properties;

import java.util.Map;

import lombok.Data;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import cn.hutool.core.map.MapUtil;

/**
* 代码生成器配置属性
*
Expand All @@ -36,4 +40,26 @@ public class GeneratorProperties {
* 排除数据表(哪些数据表不展示在代码生成中)
*/
private String[] excludeTables;

/**
* 模板配置
*/
private Map<String, TemplateConfig> templateConfigs = MapUtil.newHashMap(true);

/**
* 模板配置
*/
@Data
public static class TemplateConfig {

/**
* 模板路径
*/
private String templatePath;

/**
* 包名称
*/
private String packageName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,25 @@

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;

import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Setter;

import io.swagger.v3.oas.annotations.media.Schema;

import org.hibernate.validator.constraints.Length;

import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnore;

import cn.hutool.core.util.StrUtil;

import top.charles7c.cnadmin.common.constant.RegexConsts;

Expand Down Expand Up @@ -78,6 +84,7 @@ public class GenConfigDO implements Serializable {
*/
@Schema(description = "前端路径")
@Length(max = 255, message = "前端路径不能超过 {max} 个字符")
@Pattern(regexp = "^(?=.*src\\/views)(?!.*\\/views\\/?$).*", message = "前端路径配置错误")
private String frontendPath;

/**
Expand Down Expand Up @@ -123,7 +130,28 @@ public class GenConfigDO implements Serializable {
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;

/**
* 类名前缀
*/
@Setter(AccessLevel.NONE)
@JsonIgnore
@TableField(exist = false)
private String classNamePrefix;

/**
* 字段配置信息
*/
@JsonIgnore
@TableField(exist = false)
private List<FieldConfigDO> fieldConfigs;

public GenConfigDO(String tableName) {
this.tableName = tableName;
}

public String getClassNamePrefix() {
String rawClassName = StrUtil.isNotBlank(this.tablePrefix)
? StrUtil.removePrefix(this.tableName, this.tablePrefix) : this.tableName;
return StrUtil.upperFirst(StrUtil.toCamelCase(rawClassName));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,16 @@ public class GenConfigRequest implements Serializable {
private static final long serialVersionUID = 1L;

/**
* 字段配置
* 字段配置信息
*/
@Schema(description = "字段配置")
@Schema(description = "字段配置信息")
@NotEmpty(message = "字段配置不能为空")
private List<FieldConfigDO> fieldConfigs = new ArrayList<>();

/**
* 生成配置
* 生成配置信息
*/
@Schema(description = "生成配置")
@Schema(description = "生成配置信息")
@NotNull(message = "生成配置不能为空")
private GenConfigDO genConfig;
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,12 @@ public interface GeneratorService {
* 表名称
*/
void saveConfig(GenConfigRequest request, String tableName);

/**
* 生成代码
*
* @param tableName
* 表名称
*/
void generate(String tableName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package top.charles7c.cnadmin.tool.service.impl;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
Expand All @@ -35,15 +37,24 @@

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.meta.Column;
import cn.hutool.system.SystemUtil;

import top.charles7c.cnadmin.common.constant.StringConsts;
import top.charles7c.cnadmin.common.enums.QueryTypeEnum;
import top.charles7c.cnadmin.common.exception.ServiceException;
import top.charles7c.cnadmin.common.model.query.PageQuery;
import top.charles7c.cnadmin.common.model.vo.PageDataVO;
import top.charles7c.cnadmin.common.util.TemplateUtils;
import top.charles7c.cnadmin.common.util.validate.CheckUtils;
import top.charles7c.cnadmin.tool.config.properties.GeneratorProperties;
import top.charles7c.cnadmin.tool.config.properties.GeneratorProperties.TemplateConfig;
import top.charles7c.cnadmin.tool.mapper.FieldConfigMapper;
import top.charles7c.cnadmin.tool.mapper.GenConfigMapper;
import top.charles7c.cnadmin.tool.model.entity.FieldConfigDO;
Expand Down Expand Up @@ -182,6 +193,10 @@ public void saveConfig(GenConfigRequest request, String tableName) {

// 保存或更新生成配置信息
GenConfigDO newGenConfig = request.getGenConfig();
String frontendPath = newGenConfig.getFrontendPath();
if (StrUtil.isNotBlank(frontendPath)) {
CheckUtils.throwIf(!StrUtil.containsAll(frontendPath, "src", "views"), "前端路径配置错误");
}
GenConfigDO oldGenConfig = genConfigMapper.selectById(tableName);
if (null != oldGenConfig) {
BeanUtil.copyProperties(newGenConfig, oldGenConfig);
Expand All @@ -190,4 +205,122 @@ public void saveConfig(GenConfigRequest request, String tableName) {
genConfigMapper.insert(newGenConfig);
}
}

@Override
public void generate(String tableName) {
GenConfigDO genConfig = genConfigMapper.selectById(tableName);
CheckUtils.throwIfNull(genConfig, "请先进行数据表 [{}] 生成配置", tableName);
List<FieldConfigDO> fieldConfigList = fieldConfigMapper.selectListByTableName(tableName);
CheckUtils.throwIfEmpty(fieldConfigList, "请先进行数据表 [{}] 字段配置", tableName);
Map<String, Object> genConfigMap = this.pretreatment(genConfig, fieldConfigList);

try {
String classNamePrefix = genConfig.getClassNamePrefix();
Boolean isOverride = genConfig.getIsOverride();
// 生成后端代码
// 1、确定后端代码基础路径
// 例如:D:/continew-admin
String projectPath = SystemUtil.getUserInfo().getCurrentDir();
// 例如:D:/continew-admin/continew-admin-tool
File backendModuleFile = new File(projectPath, genConfig.getModuleName());
// 例如:D:/continew-admin/continew-admin-tool/src/main/java/top/charles7c/cnadmin/tool
List<String> backendModuleChildPathList = CollUtil.newArrayList("src", "main", "java");
backendModuleChildPathList.addAll(StrUtil.split(genConfig.getPackageName(), StringConsts.DOT));
File backendParentFile =
FileUtil.file(backendModuleFile, backendModuleChildPathList.toArray(new String[0]));
// 2、生成代码
Map<String, TemplateConfig> templateConfigMap = generatorProperties.getTemplateConfigs();
for (Map.Entry<String, TemplateConfig> templateConfigEntry : templateConfigMap.entrySet()) {
// 例如:D:/continew-admin/continew-admin-tool/src/main/java/top/charles7c/cnadmin/tool/service/impl/XxxServiceImpl.java
TemplateConfig templateConfig = templateConfigEntry.getValue();
String subPackageName = templateConfig.getPackageName();
genConfigMap.put("subPackageName", subPackageName);
File classParentFile =
FileUtil.file(backendParentFile, StrUtil.splitToArray(subPackageName, StringConsts.DOT));
String className = classNamePrefix + StrUtil.nullToEmpty(templateConfigEntry.getKey());
genConfigMap.put("className", className);
File classFile = new File(classParentFile, className + FileNameUtil.EXT_JAVA);
// 如果已经存在,且不允许覆盖,则跳过
if (classFile.exists() && !isOverride) {
continue;
}
String content = TemplateUtils.render(templateConfig.getTemplatePath(), genConfigMap);
FileUtil.writeString(content, classFile, StandardCharsets.UTF_8);
}

// 生成前端代码
String frontendPath = genConfig.getFrontendPath();
if (StrUtil.isBlank(frontendPath)) {
return;
}
// 1、生成 api 代码
// 例如:D:/continew-admin/continew-admin-ui
List<String> frontendSubPathList = StrUtil.split(frontendPath, "src");
String frontendModulePath = frontendSubPathList.get(0);
// 例如:D:/continew-admin/continew-admin-ui/src/api/tool/xxx.ts
String moduleSimpleName = new File(frontendPath).getName();
File apiParentFile = FileUtil.file(frontendModulePath, "src", "api", moduleSimpleName);
String apiFileName = classNamePrefix.toLowerCase() + ".ts";
File apiFile = new File(apiParentFile, apiFileName);
if (apiFile.exists() && !isOverride) {
return;
}
String apiContent = TemplateUtils.render("generator/api.ftl", genConfigMap);
FileUtil.writeString(apiContent, apiFile, StandardCharsets.UTF_8);
// 2、生成 view 代码
// 例如:D:/continew-admin/continew-admin-ui/src/views/tool/xxx/index.vue
File indexFile = FileUtil.file(frontendPath, classNamePrefix, "index.vue");
if (indexFile.exists() && !isOverride) {
return;
}
String indexContent = TemplateUtils.render("generator/index.ftl", genConfigMap);
FileUtil.writeString(indexContent, indexFile, StandardCharsets.UTF_8);
} catch (Exception e) {
log.error("Generate code occurred an error: {}. tableName: {}.", e.getMessage(), tableName, e);
throw new ServiceException("代码生成失败,请手动清理生成文件");
}
}

/**
* 预处理生成配置
*
* @param genConfig
* 生成配置
* @param fieldConfigList
* 字段配置列表
* @return 处理后的生成配置
*/
private Map<String, Object> pretreatment(GenConfigDO genConfig, List<FieldConfigDO> fieldConfigList) {
Map<String, Object> genConfigMap = MapUtil.newHashMap();
genConfigMap.put("date", DateUtil.date().toString("yyyy/MM/dd HH:mm"));
genConfigMap.put("hasLocalDateTime", false);
genConfigMap.put("hasBigDecimal", false);
genConfigMap.put("hasRequiredField", false);
genConfigMap.put("hasListQueryField", false);
for (FieldConfigDO fieldConfig : fieldConfigList) {
String fieldType = fieldConfig.getFieldType();
if ("LocalDateTime".equals(fieldType)) {
genConfigMap.put("hasLocalDateTime", true);
}
if ("BigDecimal".equals(fieldType)) {
genConfigMap.put("hasLocalDateTime", true);
}
if (Boolean.TRUE.equals(fieldConfig.getIsRequired())) {
genConfigMap.put("hasRequiredField", true);
}
QueryTypeEnum queryType = fieldConfig.getQueryType();
if (null != queryType && StrUtil.equalsAny(queryType.name(), QueryTypeEnum.IN.name(),
QueryTypeEnum.NOT_IN.name(), QueryTypeEnum.BETWEEN.name())) {
genConfigMap.put("hasListQueryField", true);
}
}
genConfig.setFieldConfigs(fieldConfigList);
genConfigMap.putAll(BeanUtil.beanToMap(genConfig));
String packageName = genConfig.getPackageName();
String moduleName =
StrUtil.subSuf(packageName, StrUtil.lastIndexOfIgnoreCase(packageName, StringConsts.DOT) + 1);
genConfigMap.put("moduleName", moduleName);
genConfigMap.put("apiName", StrUtil.lowerFirst(genConfig.getClassNamePrefix()));
return genConfigMap;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ${packageName}.${subPackageName};

import static top.charles7c.cnadmin.common.annotation.CrudRequestMapping.Api;

import io.swagger.v3.oas.annotations.tags.Tag;

import org.springframework.web.bind.annotation.*;

import top.charles7c.cnadmin.common.annotation.CrudRequestMapping;
import top.charles7c.cnadmin.common.base.BaseController;
import ${packageName}.model.query.${classNamePrefix}Query;
import ${packageName}.model.request.${classNamePrefix}Request;
import ${packageName}.model.vo.${classNamePrefix}DetailVO;
import ${packageName}.model.vo.${classNamePrefix}VO;
import ${packageName}.service.${classNamePrefix}Service;

/**
* ${businessName}管理 API
*
* @author ${author}
* @since ${date}
*/
@Tag(name = "${businessName}管理 API")
@RestController
@CrudRequestMapping(value = "/${moduleName}/${apiName}", api = {Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT})
public class ${className} extends BaseController<${classNamePrefix}Service, ${classNamePrefix}VO, ${classNamePrefix}DetailVO, ${classNamePrefix}Query, ${classNamePrefix}Request> {}
Loading

0 comments on commit 72399d9

Please sign in to comment.