1.0版本完成

This commit is contained in:
hupeng
2019-11-06 21:34:55 +08:00
commit a7f03930ca
644 changed files with 40190 additions and 0 deletions

39
yshop-generator/pom.xml Normal file
View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>yshop</artifactId>
<groupId>co.yixiang</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yshop-generator</artifactId>
<name>代码生成模块</name>
<properties>
<configuration.version>1.9</configuration.version>
</properties>
<dependencies>
<dependency>
<groupId>co.yixiang</groupId>
<artifactId>yshop-common</artifactId>
<version>1.0</version>
</dependency>
<!--模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-configuration/commons-configuration -->
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>${configuration.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,41 @@
package co.yixiang.domain;
import lombok.Data;
import javax.persistence.*;
/**
* 代码生成配置
* @author Zheng Jie
* @date 2019-01-03
*/
@Data
@Entity
@Table(name = "gen_config")
public class GenConfig {
@Id
private Long id;
/** 包路径 **/
private String pack;
/** 模块名 **/
@Column(name = "module_name")
private String moduleName;
/** 前端文件路径 **/
private String path;
/** 前端文件路径 **/
@Column(name = "api_path")
private String apiPath;
/** 作者 **/
private String author;
/** 表前缀 **/
private String prefix;
/** 是否覆盖 **/
private Boolean cover;
}

View File

@ -0,0 +1,40 @@
package co.yixiang.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 列的数据信息
* @author Zheng Jie
* @date 2019-01-02
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ColumnInfo {
/** 数据库字段名称 **/
private Object columnName;
/** 允许空值 **/
private Object isNullable;
/** 数据库字段类型 **/
private Object columnType;
/** 数据库字段注释 **/
private Object columnComment;
/** 数据库字段键类型 **/
private Object columnKey;
/** 额外的参数 **/
private Object extra;
/** 查询 1:模糊 2精确 **/
private String columnQuery;
/** 是否在列表显示 **/
private String columnShow;
}

View File

@ -0,0 +1,33 @@
package co.yixiang.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 表的数据信息
* @author Zheng Jie
* @date 2019-01-02
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TableInfo {
/** 表名称 **/
private Object tableName;
/** 创建日期 **/
private Object createTime;
// 数据库引擎
private Object engine;
// 编码集
private Object coding;
// 备注
private Object remark;
}

View File

@ -0,0 +1,11 @@
package co.yixiang.repository;
import co.yixiang.domain.GenConfig;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @author Zheng Jie
* @date 2019-01-14
*/
public interface GenConfigRepository extends JpaRepository<GenConfig,Long> {
}

View File

@ -0,0 +1,35 @@
package co.yixiang.rest;
import co.yixiang.service.GenConfigService;
import co.yixiang.domain.GenConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* @author Zheng Jie
* @date 2019-01-14
*/
@RestController
@RequestMapping("api")
public class GenConfigController {
@Autowired
private GenConfigService genConfigService;
/**
* 查询生成器配置
* @return
*/
@GetMapping(value = "/genConfig")
public ResponseEntity get(){
return new ResponseEntity(genConfigService.find(), HttpStatus.OK);
}
@PutMapping(value = "/genConfig")
public ResponseEntity emailConfig(@Validated @RequestBody GenConfig genConfig){
return new ResponseEntity(genConfigService.update(genConfig),HttpStatus.OK);
}
}

View File

@ -0,0 +1,70 @@
package co.yixiang.rest;
import cn.hutool.core.util.PageUtil;
import co.yixiang.domain.vo.ColumnInfo;
import co.yixiang.service.GenConfigService;
import co.yixiang.service.GeneratorService;
import co.yixiang.exception.BadRequestException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author Zheng Jie
* @date 2019-01-02
*/
@RestController
@RequestMapping("api")
public class GeneratorController {
@Autowired
private GeneratorService generatorService;
@Autowired
private GenConfigService genConfigService;
@Value("${generator.enabled}")
private Boolean generatorEnabled;
/**
* 查询数据库元数据
* @param name
* @param page
* @param size
* @return
*/
@GetMapping(value = "/generator/tables")
public ResponseEntity getTables(@RequestParam(defaultValue = "") String name,
@RequestParam(defaultValue = "0")Integer page,
@RequestParam(defaultValue = "10")Integer size){
int[] startEnd = PageUtil.transToStartEnd(page+1, size);
return new ResponseEntity(generatorService.getTables(name,startEnd), HttpStatus.OK);
}
/**
* 查询表内元数据
* @param tableName
* @return
*/
@GetMapping(value = "/generator/columns")
public ResponseEntity getTables(@RequestParam String tableName){
return new ResponseEntity(generatorService.getColumns(tableName), HttpStatus.OK);
}
/**
* 生成代码
* @param columnInfos
* @return
*/
@PostMapping(value = "/generator")
public ResponseEntity generator(@RequestBody List<ColumnInfo> columnInfos, @RequestParam String tableName){
if(!generatorEnabled){
throw new BadRequestException("此环境不允许生成代码!");
}
generatorService.generator(columnInfos,genConfigService.find(),tableName);
return new ResponseEntity(HttpStatus.OK);
}
}

View File

@ -0,0 +1,29 @@
package co.yixiang.service;
import co.yixiang.domain.GenConfig;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
/**
* @author Zheng Jie
* @date 2019-01-14
*/
@CacheConfig(cacheNames = "genConfig")
public interface GenConfigService {
/**
* find
* @return
*/
@Cacheable(key = "'1'")
GenConfig find();
/**
* update
* @param genConfig
* @return
*/
@CacheEvict(allEntries = true)
GenConfig update(GenConfig genConfig);
}

View File

@ -0,0 +1,35 @@
package co.yixiang.service;
import co.yixiang.domain.GenConfig;
import co.yixiang.domain.vo.ColumnInfo;
import java.util.List;
/**
* @author Zheng Jie
* @date 2019-01-02
*/
public interface GeneratorService {
/**
* 查询数据库元数据
* @param name
* @param startEnd
* @return
*/
Object getTables(String name, int[] startEnd);
/**
* 得到数据表的元数据
* @param name
* @return
*/
Object getColumns(String name);
/**
* 生成代码
* @param columnInfos
* @param genConfig
* @param tableName
*/
void generator(List<ColumnInfo> columnInfos, GenConfig genConfig, String tableName);
}

View File

@ -0,0 +1,53 @@
package co.yixiang.service.impl;
import co.yixiang.repository.GenConfigRepository;
import co.yixiang.service.GenConfigService;
import co.yixiang.domain.GenConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.File;
import java.util.Optional;
/**
* @author Zheng Jie
* @date 2019-01-14
*/
@Service
public class GenConfigServiceImpl implements GenConfigService {
@Autowired
private GenConfigRepository genConfigRepository;
@Override
public GenConfig find() {
Optional<GenConfig> genConfig = genConfigRepository.findById(1L);
if(genConfig.isPresent()){
return genConfig.get();
} else {
return new GenConfig();
}
}
@Override
public GenConfig update(GenConfig genConfig) {
genConfig.setId(1L);
// 自动设置Api路径注释掉前需要同步取消前端的注释
String separator = File.separator;
String[] paths = null;
if (separator.equals("\\")) {
paths = genConfig.getPath().split("\\\\");
} else paths = genConfig.getPath().split(File.separator);
StringBuffer api = new StringBuffer();
for (int i = 0; i < paths.length; i++) {
api.append(paths[i]);
api.append(separator);
if(paths[i].equals("src")){
api.append("api");
break;
}
}
genConfig.setApiPath(api.toString());
return genConfigRepository.save(genConfig);
}
}

View File

@ -0,0 +1,77 @@
package co.yixiang.service.impl;
import cn.hutool.core.util.ObjectUtil;
import co.yixiang.domain.vo.ColumnInfo;
import co.yixiang.service.GeneratorService;
import co.yixiang.domain.GenConfig;
import co.yixiang.domain.vo.TableInfo;
import co.yixiang.exception.BadRequestException;
import co.yixiang.utils.GenUtil;
import co.yixiang.utils.PageUtil;
import co.yixiang.utils.StringUtils;
import org.springframework.stereotype.Service;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author Zheng Jie
* @date 2019-01-02
*/
@Service
public class GeneratorServiceImpl implements GeneratorService {
@PersistenceContext
private EntityManager em;
@Override
public Object getTables(String name, int[] startEnd) {
// 使用预编译防止sql注入
String sql = "select table_name ,create_time , engine, table_collation, table_comment from information_schema.tables " +
"where table_schema = (select database()) " +
"and table_name like ? order by create_time desc";
Query query = em.createNativeQuery(sql);
query.setFirstResult(startEnd[0]);
query.setMaxResults(startEnd[1]-startEnd[0]);
query.setParameter(1, StringUtils.isNotBlank(name) ? ("%" + name + "%") : "%%");
List<Object[]> result = query.getResultList();
List<TableInfo> tableInfos = new ArrayList<>();
for (Object[] obj : result) {
tableInfos.add(new TableInfo(obj[0],obj[1],obj[2],obj[3], ObjectUtil.isNotEmpty(obj[4])? obj[4] : "-"));
}
Query query1 = em.createNativeQuery("SELECT COUNT(*) from information_schema.tables where table_schema = (select database())");
Object totalElements = query1.getSingleResult();
return PageUtil.toPage(tableInfos,totalElements);
}
@Override
public Object getColumns(String name) {
// 使用预编译防止sql注入
String sql = "select column_name, is_nullable, data_type, column_comment, column_key, extra from information_schema.columns " +
"where table_name = ? and table_schema = (select database()) order by ordinal_position";
Query query = em.createNativeQuery(sql);
query.setParameter(1, StringUtils.isNotBlank(name) ? name : null);
List<Object[]> result = query.getResultList();
List<ColumnInfo> columnInfos = new ArrayList<>();
for (Object[] obj : result) {
columnInfos.add(new ColumnInfo(obj[0],obj[1],obj[2],obj[3],obj[4],obj[5],null,"true"));
}
return PageUtil.toPage(columnInfos,columnInfos.size());
}
@Override
public void generator(List<ColumnInfo> columnInfos, GenConfig genConfig, String tableName) {
if(genConfig.getId() == null){
throw new BadRequestException("请先配置生成器");
}
try {
GenUtil.generatorCode(columnInfos,genConfig,tableName);
} catch (IOException e) {
System.out.println(e.getMessage());
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,34 @@
package co.yixiang.utils;
import org.apache.commons.configuration.*;
/**
* sql字段转java
*
* @author Zheng Jie
* @date 2019-01-03
*/
public class ColUtil {
/**
* 转换mysql数据类型为java数据类型
* @param type
* @return
*/
public static String cloToJava(String type){
Configuration config = getConfig();
return config.getString(type,"unknowType");
}
/**
* 获取配置信息
*/
public static PropertiesConfiguration getConfig() {
try {
return new PropertiesConfiguration("generator.properties" );
} catch (ConfigurationException e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,248 @@
package co.yixiang.utils;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.template.*;
import co.yixiang.domain.GenConfig;
import co.yixiang.domain.vo.ColumnInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 代码生成
* @author Zheng Jie
* @date 2019-01-02
*/
@Slf4j
public class GenUtil {
private static final String TIMESTAMP = "Timestamp";
private static final String BIGDECIMAL = "BigDecimal";
private static final String PK = "PRI";
private static final String EXTRA = "auto_increment";
/**
* 获取后端代码模板名称
* @return
*/
public static List<String> getAdminTemplateNames() {
List<String> templateNames = new ArrayList<>();
templateNames.add("Entity");
templateNames.add("Dto");
templateNames.add("Mapper");
templateNames.add("Repository");
templateNames.add("Service");
templateNames.add("ServiceImpl");
templateNames.add("QueryCriteria");
templateNames.add("Controller");
return templateNames;
}
/**
* 获取前端代码模板名称
* @return
*/
public static List<String> getFrontTemplateNames() {
List<String> templateNames = new ArrayList<>();
templateNames.add("api");
templateNames.add("index");
templateNames.add("eForm");
return templateNames;
}
/**
* 生成代码
* @param columnInfos 表元数据
* @param genConfig 生成代码的参数配置,如包路径,作者
*/
public static void generatorCode(List<ColumnInfo> columnInfos, GenConfig genConfig, String tableName) throws IOException {
Map<String,Object> map = new HashMap();
map.put("package",genConfig.getPack());
map.put("moduleName",genConfig.getModuleName());
map.put("author",genConfig.getAuthor());
map.put("date", LocalDate.now().toString());
map.put("tableName",tableName);
String className = StringUtils.toCapitalizeCamelCase(tableName);
String changeClassName = StringUtils.toCamelCase(tableName);
// 判断是否去除表前缀
if (StringUtils.isNotEmpty(genConfig.getPrefix())) {
className = StringUtils.toCapitalizeCamelCase(StrUtil.removePrefix(tableName,genConfig.getPrefix()));
changeClassName = StringUtils.toCamelCase(StrUtil.removePrefix(tableName,genConfig.getPrefix()));
}
map.put("className", className);
map.put("upperCaseClassName", className.toUpperCase());
map.put("changeClassName", changeClassName);
map.put("hasTimestamp",false);
map.put("hasBigDecimal",false);
map.put("hasQuery",false);
map.put("auto",false);
List<Map<String,Object>> columns = new ArrayList<>();
List<Map<String,Object>> queryColumns = new ArrayList<>();
for (ColumnInfo column : columnInfos) {
Map<String,Object> listMap = new HashMap();
listMap.put("columnComment",column.getColumnComment());
listMap.put("columnKey",column.getColumnKey());
String colType = ColUtil.cloToJava(column.getColumnType().toString());
String changeColumnName = StringUtils.toCamelCase(column.getColumnName().toString());
String capitalColumnName = StringUtils.toCapitalizeCamelCase(column.getColumnName().toString());
if(PK.equals(column.getColumnKey())){
map.put("pkColumnType",colType);
map.put("pkChangeColName",changeColumnName);
map.put("pkCapitalColName",capitalColumnName);
}
if(TIMESTAMP.equals(colType)){
map.put("hasTimestamp",true);
}
if(BIGDECIMAL.equals(colType)){
map.put("hasBigDecimal",true);
}
if(EXTRA.equals(column.getExtra())){
map.put("auto",true);
}
listMap.put("columnType",colType);
listMap.put("columnName",column.getColumnName());
listMap.put("isNullable",column.getIsNullable());
listMap.put("columnShow",column.getColumnShow());
listMap.put("changeColumnName",changeColumnName);
listMap.put("capitalColumnName",capitalColumnName);
// 判断是否有查询如有则把查询的字段set进columnQuery
if(!StringUtils.isBlank(column.getColumnQuery())){
listMap.put("columnQuery",column.getColumnQuery());
map.put("hasQuery",true);
queryColumns.add(listMap);
}
columns.add(listMap);
}
map.put("columns",columns);
map.put("queryColumns",queryColumns);
TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
// 生成后端代码
List<String> templates = getAdminTemplateNames();
for (String templateName : templates) {
Template template = engine.getTemplate("generator/admin/"+templateName+".ftl");
String filePath = getAdminFilePath(templateName,genConfig,className);
File file = new File(filePath);
// 如果非覆盖生成
if(!genConfig.getCover() && FileUtil.exist(file)){
continue;
}
// 生成代码
genFile(file, template, map);
}
// 生成前端代码
templates = getFrontTemplateNames();
for (String templateName : templates) {
Template template = engine.getTemplate("generator/front/"+templateName+".ftl");
String filePath = getFrontFilePath(templateName,genConfig,map.get("changeClassName").toString());
File file = new File(filePath);
// 如果非覆盖生成
if(!genConfig.getCover() && FileUtil.exist(file)){
continue;
}
// 生成代码
genFile(file, template, map);
}
}
/**
* 定义后端文件路径以及名称
*/
public static String getAdminFilePath(String templateName, GenConfig genConfig, String className) {
String projectPath = System.getProperty("user.dir") + File.separator + genConfig.getModuleName();
String packagePath = projectPath + File.separator + "src" +File.separator+ "main" + File.separator + "java" + File.separator;
if (!ObjectUtils.isEmpty(genConfig.getPack())) {
packagePath += genConfig.getPack().replace(".", File.separator) + File.separator;
}
if ("Entity".equals(templateName)) {
return packagePath + "domain" + File.separator + className + ".java";
}
if ("Controller".equals(templateName)) {
return packagePath + "rest" + File.separator + className + "Controller.java";
}
if ("Service".equals(templateName)) {
return packagePath + "service" + File.separator + className + "Service.java";
}
if ("ServiceImpl".equals(templateName)) {
return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
}
if ("Dto".equals(templateName)) {
return packagePath + "service" + File.separator + "dto" + File.separator + className + "DTO.java";
}
if ("QueryCriteria".equals(templateName)) {
return packagePath + "service" + File.separator + "dto" + File.separator + className + "QueryCriteria.java";
}
if ("Mapper".equals(templateName)) {
return packagePath + "service" + File.separator + "mapper" + File.separator + className + "Mapper.java";
}
if ("Repository".equals(templateName)) {
return packagePath + "repository" + File.separator + className + "Repository.java";
}
return null;
}
/**
* 定义前端文件路径以及名称
*/
public static String getFrontFilePath(String templateName, GenConfig genConfig, String apiName) {
String path = genConfig.getPath();
if ("api".equals(templateName)) {
return genConfig.getApiPath() + File.separator + apiName + ".js";
}
if ("index".equals(templateName)) {
return path + File.separator + "index.vue";
}
if ("eForm".equals(templateName)) {
return path + File.separator + File.separator + "form.vue";
}
return null;
}
public static void genFile(File file,Template template,Map<String,Object> map) throws IOException {
// 生成目标文件
Writer writer = null;
try {
FileUtil.touch(file);
writer = new FileWriter(file);
template.render(map, writer);
} catch (TemplateException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
writer.close();
}
}
}