update 重构 将代码生成vm模板替换为fm模板 语法更清晰明了

This commit is contained in:
疯狂的狮子Li
2026-06-18 14:59:06 +08:00
parent 06d4e6d7f1
commit f04197b401
34 changed files with 1671 additions and 1689 deletions
@@ -81,7 +81,7 @@ public interface GenConstants {
String SORT_FIELD = "sortField";
/**
* 默认前端模板类型,对应 vm/vue 目录。
* 默认前端模板类型,对应模板根目录下的 vue 目录。
*/
String FRONTEND_TYPE_VUE = "vue";
@@ -262,26 +262,34 @@ public interface GenConstants {
*/
String REQUIRE = "1";
// 模板根路径与后缀
String TEMPLATE_ROOT_PATH = "fm";
String TEMPLATE_FILE_SUFFIX = ".ftl";
String TEMPLATE_RESOURCE_PREFIX = "classpath*:";
String TEMPLATE_PAGE_PATTERN_SUFFIX = ".*" + TEMPLATE_FILE_SUFFIX;
// 后端源码模板
String JAVA_DOMAIN_TEMPLATE_PATH = "vm/java/domain.java.vm";
String JAVA_VO_TEMPLATE_PATH = "vm/java/vo.java.vm";
String JAVA_BO_TEMPLATE_PATH = "vm/java/bo.java.vm";
String JAVA_MAPPER_TEMPLATE_PATH = "vm/java/mapper.java.vm";
String JAVA_SERVICE_TEMPLATE_PATH = "vm/java/service.java.vm";
String JAVA_SERVICE_IMPL_TEMPLATE_PATH = "vm/java/serviceImpl.java.vm";
String JAVA_CONTROLLER_TEMPLATE_PATH = "vm/java/controller.java.vm";
String JAVA_TEMPLATE_ROOT_PATH = TEMPLATE_ROOT_PATH + "/java";
String JAVA_DOMAIN_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/domain.java" + TEMPLATE_FILE_SUFFIX;
String JAVA_VO_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/vo.java" + TEMPLATE_FILE_SUFFIX;
String JAVA_BO_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/bo.java" + TEMPLATE_FILE_SUFFIX;
String JAVA_MAPPER_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/mapper.java" + TEMPLATE_FILE_SUFFIX;
String JAVA_SERVICE_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/service.java" + TEMPLATE_FILE_SUFFIX;
String JAVA_SERVICE_IMPL_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/serviceImpl.java" + TEMPLATE_FILE_SUFFIX;
String JAVA_CONTROLLER_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/controller.java" + TEMPLATE_FILE_SUFFIX;
// MyBatis MapperXML 模板
String XML_MAPPER_TEMPLATE_PATH = "vm/xml/mapper.xml.vm";
String XML_TEMPLATE_ROOT_PATH = TEMPLATE_ROOT_PATH + "/xml";
String XML_MAPPER_TEMPLATE_PATH = XML_TEMPLATE_ROOT_PATH + "/mapper.xml" + TEMPLATE_FILE_SUFFIX;
// 前端源码模板
String FRONTEND_API_TEMPLATE_NAME = "api.ts.vm";
String FRONTEND_TYPES_TEMPLATE_NAME = "types.ts.vm";
String FRONTEND_API_TEMPLATE_NAME = "api.ts" + TEMPLATE_FILE_SUFFIX;
String FRONTEND_TYPES_TEMPLATE_NAME = "types.ts" + TEMPLATE_FILE_SUFFIX;
String FRONTEND_INDEX_TEMPLATE_PREFIX = "index";
String FRONTEND_INDEX_TREE_TEMPLATE_PREFIX = "index-tree";
// 数据库SQL模板
String SQL_ORACLE_TEMPLATE_PATH = "vm/sql/oracle.sql.vm";
String SQL_POSTGRES_TEMPLATE_PATH = "vm/sql/postgres.sql.vm";
String SQL_SQLSERVER_TEMPLATE_PATH = "vm/sql/sqlserver.sql.vm";
String SQL_MYSQL_TEMPLATE_PATH = "vm/sql/mysql.sql.vm";
String SQL_TEMPLATE_ROOT_PATH = TEMPLATE_ROOT_PATH + "/sql";
String SQL_ORACLE_TEMPLATE_PATH = SQL_TEMPLATE_ROOT_PATH + "/oracle.sql" + TEMPLATE_FILE_SUFFIX;
String SQL_POSTGRES_TEMPLATE_PATH = SQL_TEMPLATE_ROOT_PATH + "/postgres.sql" + TEMPLATE_FILE_SUFFIX;
String SQL_SQLSERVER_TEMPLATE_PATH = SQL_TEMPLATE_ROOT_PATH + "/sqlserver.sql" + TEMPLATE_FILE_SUFFIX;
String SQL_MYSQL_TEMPLATE_PATH = SQL_TEMPLATE_ROOT_PATH + "/mysql.sql" + TEMPLATE_FILE_SUFFIX;
/**
* 固定模板路径集合,前端模板按 frontendType 动态解析。
@@ -62,7 +62,7 @@ public class GenTable extends BaseEntity {
private String tplCategory;
/**
* 前端模板类型,对应 vm 下的模板目录
* 前端模板类型,对应 fm 下的模板目录
*/
private String frontendType;
@@ -132,6 +132,80 @@ public class GenTableColumn extends BaseEntity {
return StringUtils.capitalize(javaField);
}
/**
* 获取适合界面展示的字段注释,去除括号内的枚举说明。
*
* @return 字段展示名称
*/
public String getColumnLabel() {
int index = StringUtils.indexOf(this.columnComment, "");
if (index != StringUtils.INDEX_NOT_FOUND) {
return StringUtils.substring(this.columnComment, 0, index);
}
return this.columnComment;
}
/**
* 获取 TypeScript 字段类型。
*
* @return TypeScript 类型
*/
public String getTsType() {
if (StringUtils.containsAny(this.javaField, "id", "Id")) {
return "string | number";
}
if (StringUtils.equalsAny(this.javaType,
GenConstants.TYPE_LONG, GenConstants.TYPE_INTEGER, GenConstants.TYPE_DOUBLE,
"Float", GenConstants.TYPE_BIGDECIMAL)) {
return "number";
}
if (StringUtils.equals(this.javaType, GenConstants.TYPE_BOOLEAN)) {
return "boolean";
}
return "string";
}
/**
* 获取开关启用值的前端字面量。
*
* @return 前端字面量
*/
public String getSwitchActiveValue() {
if (StringUtils.equals(this.javaType, GenConstants.TYPE_BOOLEAN)) {
return "true";
}
if (StringUtils.equalsAny(this.javaType, GenConstants.TYPE_INTEGER, GenConstants.TYPE_LONG)) {
return "0";
}
return "'0'";
}
/**
* 获取开关停用值的前端字面量。
*
* @return 前端字面量
*/
public String getSwitchInactiveValue() {
if (StringUtils.equals(this.javaType, GenConstants.TYPE_BOOLEAN)) {
return "false";
}
if (StringUtils.equalsAny(this.javaType, GenConstants.TYPE_INTEGER, GenConstants.TYPE_LONG)) {
return "1";
}
return "'1'";
}
/**
* 是否为日期范围查询字段。
*
* @return 日期范围查询字段返回 {@code true}
*/
public boolean isDateRangeQuery() {
return isQuery()
&& StringUtils.equals(this.htmlType, GenConstants.HTML_DATETIME)
&& StringUtils.equals(this.queryType, GenConstants.QUERY_BETWEEN);
}
/**
* 判断当前列是否为主键列。
*
@@ -6,6 +6,7 @@ import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateUtil;
import cn.hutool.extra.template.engine.freemarker.FreemarkerEngine;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -59,6 +60,7 @@ public class TemplateEngineUtils {
static {
// 模板引擎初始化
GenProperties properties = SpringUtils.getBean(GenProperties.class);
properties.getTemplateConfig().setCustomEngine(FreemarkerEngine.class);
TEMPLATE_ENGINE = TemplateUtil.createEngine(properties.getTemplateConfig());
TEMPLATE_MAPPER = PathNamedTemplate.form(TEMPLATE_ENGINE, GenConstants.TEMPLATE_PATHS);
}
@@ -104,11 +106,14 @@ public class TemplateEngineUtils {
context.put("author", genTable.getFunctionAuthor());
context.put("datetime", DateUtils.now());
context.put("pkColumn", genTable.getPkColumn());
String dicts = getDicts(genTable);
context.put("importList", getImportList(genTable));
context.put("permissionPrefix", getPermissionPrefix(moduleName, businessName));
context.put("columns", genTable.getColumns());
context.put("table", genTable);
context.put("dicts", getDicts(genTable));
context.put("dicts", dicts);
context.put("dictsNoSymbol", StringUtils.replace(dicts, "'", StringUtils.EMPTY));
setColumnFeatureContext(context, genTable, dicts);
// 向模板上下文写入菜单相关变量
String options = genTable.getOptions();
Dict paramsObj = JsonUtils.parseMap(options);
@@ -133,6 +138,8 @@ public class TemplateEngineUtils {
context.put("enableSort", enableSort && ObjectUtil.isNotNull(sortColumn));
context.put("sortColumn", sortColumn);
context.put("sortField", ObjectUtil.isNotNull(sortColumn) ? sortColumn.getJavaField() : StringUtils.EMPTY);
context.put("statusActiveValue", ObjectUtil.isNotNull(statusColumn) ? statusColumn.getSwitchActiveValue() : StringUtils.EMPTY);
context.put("statusInactiveValue", ObjectUtil.isNotNull(statusColumn) ? statusColumn.getSwitchInactiveValue() : StringUtils.EMPTY);
// 向树形模板上下文写入树字段相关变量
if (GenConstants.TPL_TREE.equals(tplCategory)) {
@@ -142,6 +149,71 @@ public class TemplateEngineUtils {
return context;
}
/**
* 写入模板中常用的列派生开关,避免在模板内反复扫描和处理。
*
* @param context 模板上下文
* @param genTable 代码生成业务表对象
* @param dicts 字典类型表达式
*/
private static void setColumnFeatureContext(Dict context, GenTable genTable, String dicts) {
boolean hasBetween = false;
boolean needImagePreview = false;
boolean needImageUpload = false;
boolean needFileUpload = false;
boolean needEditor = false;
boolean needCheckbox = false;
boolean needSelect = false;
boolean needTextArea = false;
boolean needDigit = false;
boolean needDateField = false;
boolean needSwitchField = false;
String firstTreeListField = StringUtils.EMPTY;
for (GenTableColumn column : genTable.getColumns()) {
boolean writable = column.isInsert() || column.isEdit();
hasBetween = hasBetween || column.isQuery() && StringUtils.equals(column.getQueryType(), GenConstants.QUERY_BETWEEN);
needImagePreview = needImagePreview || column.isList() && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_IMAGE_UPLOAD);
needImageUpload = needImageUpload || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_IMAGE_UPLOAD);
needFileUpload = needFileUpload || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_FILE_UPLOAD);
needEditor = needEditor || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_EDITOR);
needCheckbox = needCheckbox || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_CHECKBOX);
needSelect = needSelect || (writable || column.isQuery()) && StringUtils.equalsAny(column.getHtmlType(),
GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_SWITCH);
needTextArea = needTextArea || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_TEXTAREA);
needDigit = needDigit || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_INPUT_NUMBER);
needDateField = needDateField || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_DATETIME);
needSwitchField = needSwitchField || (writable || column.isList()) && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_SWITCH);
if (StringUtils.isBlank(firstTreeListField) && column.isList()) {
firstTreeListField = column.getJavaField();
}
}
boolean needDateRange = hasDateRangeQuery(genTable);
context.put("hasBetween", hasBetween);
context.put("needAddDateRange", needDateRange);
context.put("needDateRange", needDateRange);
context.put("needDict", StringUtils.isNotBlank(dicts));
context.put("needImagePreview", needImagePreview);
context.put("needImageUpload", needImageUpload);
context.put("needFileUpload", needFileUpload);
context.put("needEditor", needEditor);
context.put("needCheckbox", needCheckbox);
context.put("needSelect", needSelect);
context.put("needTextArea", needTextArea);
context.put("needDigit", needDigit);
context.put("needDateField", needDateField);
context.put("needSwitchField", needSwitchField);
context.put("firstTreeListField", firstTreeListField);
}
private static boolean hasDateRangeQuery(GenTable genTable) {
for (GenTableColumn column : genTable.getColumns()) {
if (column.isDateRangeQuery()) {
return true;
}
}
return false;
}
/**
* 向树形模板上下文写入树字段相关变量。
*
@@ -168,6 +240,9 @@ public class TemplateEngineUtils {
context.put("treeRootValue", treeRootValue);
context.put("treeRootValueJavaLiteral", getJavaLiteral(treeParentColumn, treeRootValue));
context.put("treeRootValueTsLiteral", getTsLiteral(treeParentColumn, treeRootValue));
context.put("treeParentCap", StringUtils.capitalize(treeParentCode));
context.put("treeAncestorsCap", StringUtils.capitalize(ObjectUtil.isNotNull(treeAncestorsColumn) ? treeAncestorsColumn.getJavaField() : StringUtils.EMPTY));
context.put("treeOrderCap", StringUtils.capitalize(ObjectUtil.isNotNull(treeOrderColumn) ? treeOrderColumn.getJavaField() : StringUtils.EMPTY));
String expandTreeName = paramsObj.getStr(GenConstants.TREE_NAME);
int expandColumn = 0;
for (GenTableColumn column : genTable.getColumns()) {
@@ -241,7 +316,7 @@ public class TemplateEngineUtils {
}
/**
* 获取模板,支持按前端模板类型动态加载 vm/{frontendType} 下的模板。
* 获取模板,支持按前端模板类型动态加载模板根目录下的模板。
*
* @param templatePath 模板路径
* @return 带路径名的模板
@@ -278,27 +353,27 @@ public class TemplateEngineUtils {
String frontendPath = getFrontendPath(genTable.getFrontendType());
// templatePath
// genFilePathFormat
if (template.contains("domain.java.vm")) {
if (template.contains("domain.java.")) {
fileName = StringUtils.format("{}/domain/{}.java", javaPath, className);
} else if (template.contains("vo.java.vm")) {
} else if (template.contains("vo.java.")) {
fileName = StringUtils.format("{}/domain/vo/{}Vo.java", javaPath, className);
} else if (template.contains("bo.java.vm")) {
} else if (template.contains("bo.java.")) {
fileName = StringUtils.format("{}/domain/bo/{}Bo.java", javaPath, className);
} else if (template.contains("mapper.java.vm")) {
} else if (template.contains("mapper.java.")) {
fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className);
} else if (template.contains("service.java.vm")) {
} else if (template.contains("service.java.")) {
fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className);
} else if (template.contains("serviceImpl.java.vm")) {
} else if (template.contains("serviceImpl.java.")) {
fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className);
} else if (template.contains("controller.java.vm")) {
} else if (template.contains("controller.java.")) {
fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className);
} else if (template.contains("mapper.xml.vm")) {
} else if (template.contains("mapper.xml.")) {
fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className);
} else if (template.contains("sql.vm")) {
} else if (template.contains("sql.")) {
fileName = businessName + "Menu.sql";
} else if (template.contains("api.ts.vm")) {
} else if (template.contains("api.ts.")) {
fileName = StringUtils.format("{}/api/{}/{}/index.ts", frontendPath, moduleName, businessName);
} else if (template.contains("types.ts.vm")) {
} else if (template.contains("types.ts.")) {
fileName = StringUtils.format("{}/api/{}/{}/types.ts", frontendPath, moduleName, businessName);
} else if (isFrontendPageTemplate(template, GenConstants.FRONTEND_INDEX_TEMPLATE_PREFIX)) {
fileName = StringUtils.format("{}/views/{}/{}/index.{}", frontendPath, moduleName, businessName,
@@ -329,7 +404,9 @@ public class TemplateEngineUtils {
*/
private static String getFrontendPageExtension(String template, String templatePrefix) {
String fileName = getTemplateFileName(template);
return fileName.substring((templatePrefix + ".").length(), fileName.length() - ".vm".length());
int suffixIndex = fileName.lastIndexOf('.');
String withoutTemplateSuffix = suffixIndex >= 0 ? fileName.substring(0, suffixIndex) : fileName;
return withoutTemplateSuffix.substring((templatePrefix + ".").length());
}
private static String getFrontendApiTemplatePath(String frontendType) {
@@ -363,7 +440,7 @@ public class TemplateEngineUtils {
}
private static String getFrontendTemplatePath(String frontendType, String templateName) {
return StringUtils.format("vm/{}/{}", resolveFrontendType(frontendType), templateName);
return StringUtils.format("{}/{}/{}", GenConstants.TEMPLATE_ROOT_PATH, resolveFrontendType(frontendType), templateName);
}
/**
@@ -375,27 +452,33 @@ public class TemplateEngineUtils {
*/
private static String getFrontendPageTemplatePath(String frontendType, String templatePrefix) {
String type = resolveFrontendType(frontendType);
String pattern = StringUtils.format("classpath*:vm/{}/{}.*.vm", type, templatePrefix);
String templatePathPattern = getFrontendPageTemplatePathPattern(type, templatePrefix);
String resourcePattern = GenConstants.TEMPLATE_RESOURCE_PREFIX + templatePathPattern;
try {
Resource[] resources = RESOURCE_PATTERN_RESOLVER.getResources(pattern);
Resource[] resources = RESOURCE_PATTERN_RESOLVER.getResources(resourcePattern);
if (resources.length == 0) {
throw new ServiceException(StringUtils.format("未找到前端模板: vm/{}/{}.*.vm", type, templatePrefix));
throw new ServiceException(StringUtils.format("未找到前端模板: {}", templatePathPattern));
}
return Arrays.stream(resources)
.map(Resource::getFilename)
.filter(StringUtils::isNotBlank)
.sorted()
.findFirst()
.map(fileName -> StringUtils.format("vm/{}/{}", type, fileName))
.orElseThrow(() -> new ServiceException(StringUtils.format("未找到前端模板: vm/{}/{}.*.vm", type, templatePrefix)));
.map(fileName -> StringUtils.format("{}/{}/{}", GenConstants.TEMPLATE_ROOT_PATH, type, fileName))
.orElseThrow(() -> new ServiceException(StringUtils.format("未找到前端模板: {}", templatePathPattern)));
} catch (IOException e) {
throw new ServiceException(StringUtils.format("读取前端模板失败: vm/{}/{}.*.vm", type, templatePrefix), e);
throw new ServiceException(StringUtils.format("读取前端模板失败: {}", templatePathPattern), e);
}
}
private static String getFrontendPageTemplatePathPattern(String frontendType, String templatePrefix) {
return StringUtils.format("{}/{}/{}{}", GenConstants.TEMPLATE_ROOT_PATH, frontendType,
templatePrefix, GenConstants.TEMPLATE_PAGE_PATTERN_SUFFIX);
}
private static boolean isFrontendPageTemplate(String template, String templatePrefix) {
String fileName = getTemplateFileName(template);
return fileName.startsWith(templatePrefix + ".") && fileName.endsWith(".vm");
return fileName.startsWith(templatePrefix + ".") && fileName.endsWith(GenConstants.TEMPLATE_FILE_SUFFIX);
}
private static String getTemplateFileName(String template) {
@@ -430,10 +513,6 @@ public class TemplateEngineUtils {
} else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) {
importList.add("java.math.BigDecimal");
}
if (!column.isSuperColumn() && GenConstants.QUERY_BETWEEN.equals(column.getQueryType())) {
importList.add("java.util.HashMap");
importList.add("java.util.Map");
}
}
return importList;
}