feat: 刷新字典缓存.

This commit is contained in:
lijiahang
2023-10-27 19:15:13 +08:00
parent 706492f54a
commit d219b7f88a
70 changed files with 147 additions and 119 deletions

View File

@@ -0,0 +1,440 @@
package com.orion.ops.framework.mybatis.core.generator;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.orion.lang.constant.Const;
import com.orion.lang.utils.ansi.AnsiAppender;
import com.orion.lang.utils.ansi.style.AnsiFont;
import com.orion.lang.utils.ansi.style.color.AnsiForeground;
import com.orion.lang.utils.ext.yml.YmlExt;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.mybatis.core.domain.BaseDO;
import com.orion.ops.framework.mybatis.core.generator.engine.VelocityTemplateEngine;
import com.orion.ops.framework.mybatis.core.generator.template.Table;
import com.orion.ops.framework.mybatis.core.generator.template.Template;
import com.orion.ops.framework.mybatis.core.mapper.IMapper;
import org.apache.ibatis.annotations.Mapper;
import java.io.File;
import java.sql.Types;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* 代码生成器
*
* @author Jiahang Li
* @version 1.0.0
* @since 2022/4/20 10:33
*/
public class CodeGenerator {
public static void main(String[] args) {
// 输出路径
String outputDir = "D:/MP/";
// 作者
String author = Const.ORION_AUTHOR;
// 模块
String module = "infra";
// 生成的表
Table[] tables = {
// Template.create("preference", "用户偏好", "preference")
// .enableProviderApi()
// .cache("user:preference:{}:{}", "用户偏好 ${type} ${userId}")
// .expire(1, TimeUnit.HOURS)
// .vue("user", "preference")
// .enums("type")
// .names("APP", "HOST")
// // 同 .value(1, 2)
// .values("value", 1, 2)
// // 同 .label("应用", "主机")
// .values("label", "应用", "主机")
// .color("blue", "green")
// .build(),
Template.create("history_value", "历史归档", "history")
.enableProviderApi()
.vue("meta", "history-value")
.build(),
Template.create("dict_key", "字典配置项", "dict")
.cache("dict:keys", "字典配置项")
.expire(1, TimeUnit.DAYS)
.vue("system", "dict-key")
.enums("value_type")
.names("STRING", "INTEGER", "DECIMAL", "BOOLEAN", "COLOR")
.label("字符串", "整数", "小数", "布尔值", "颜色")
.build(),
Template.create("dict_value", "字典配置值", "dict")
.cache("dict:value:{}", "字典配置值 ${key}")
.expire(1, TimeUnit.DAYS)
.vue("system", "dict-value")
.enableRowSelection()
.build(),
};
// jdbc 配置 - 使用配置文件
File yamlFile = new File("orion-ops-launch/src/main/resources/application-dev.yaml");
YmlExt yaml = YmlExt.load(yamlFile);
String url = yaml.getValue("spring.datasource.druid.url");
String username = yaml.getValue("spring.datasource.druid.username");
String password = yaml.getValue("spring.datasource.druid.password");
// 执行
runGenerator(outputDir, author,
url, username, password,
tables, module);
}
/**
* 代码生成
*/
private static void runGenerator(String outputDir,
String author,
String url,
String username,
String password,
Table[] tables,
String module) {
Valid.notEmpty(tables, "请先配置需要生成的表");
// 创建引擎
VelocityTemplateEngine engine = getEngine(tables);
// 获取全局配置
GlobalConfig globalConfig = getGlobalConfig(outputDir, author);
// 数据源配置
DataSourceConfig dataSourceConfig = getDataSourceConfig(url, username, password);
// 策略配置
StrategyConfig strategyConfig = getStrategyConfig(tables);
// 包名配置
PackageConfig packageConfig = getPackageConfig(module);
// 模板配置
TemplateConfig templateConfig = getTemplateConfig();
// 注入配置
InjectionConfig injectionConfig = getInjectionConfig();
// 整合配置
AutoGenerator ag = new AutoGenerator(dataSourceConfig)
// 整合全局配置
.global(globalConfig)
// 整合表名配置
.strategy(strategyConfig)
// 整合包名配置
.packageInfo(packageConfig)
// 整合模板配置
.template(templateConfig)
// 整合注入配置
.injection(injectionConfig);
// 执行
ag.execute(engine);
// 打印提示信息
printTips();
}
/**
* 获取渲染引擎
*
* @param tables 表
* @return 渲染引擎
*/
private static VelocityTemplateEngine getEngine(Table[] tables) {
return new VelocityTemplateEngine(tables);
}
/**
* 获取全局配置
*
* @param outputDir 输出地址
* @param author 作者
* @return config
*/
private static GlobalConfig getGlobalConfig(String outputDir, String author) {
return new GlobalConfig.Builder()
// 设置作者
.author(author)
// 生成路径
.outputDir(outputDir)
// 生成 spring doc 注解
.enableSpringdoc()
// date类型
.dateType(DateType.ONLY_DATE)
// 注释时间
.commentDate("yyyy-M-d HH:mm")
// 构建
.build();
}
/**
* 获取数据源配置
*
* @param url url
* @param username username
* @param password password
* @return 数据源配置
*/
private static DataSourceConfig getDataSourceConfig(String url, String username, String password) {
return new DataSourceConfig.Builder(url, username, password)
// 转换器
.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
switch (metaInfo.getJdbcType().TYPE_CODE) {
case Types.BIT:
case Types.TINYINT:
return DbColumnType.INTEGER;
default:
return typeRegistry.getColumnType(metaInfo);
}
})
// 查询器
.dbQuery(new MySqlQuery())
// 构建
.build();
}
/**
* 获取策略配置
*
* @param tables 生成的表名
* @return 策略配置
*/
private static StrategyConfig getStrategyConfig(Table[] tables) {
String[] tableNames = Arrays.stream(tables)
.map(Table::getTableName)
.toArray(String[]::new);
// 策略配置
return new StrategyConfig.Builder()
// 生成的表
.addInclude(tableNames)
// 全局大写命名
.enableCapitalMode()
// 实体配置
.entityBuilder()
// 下滑线转驼峰命名策略
.naming(NamingStrategy.underline_to_camel)
// 实体父类
.superClass(BaseDO.class)
// 实体父类字段
.addSuperEntityColumns("create_time", "update_time", "creator", "updater", "deleted")
// 启用lombok
.enableLombok()
// 主键策略
.idType(IdType.AUTO)
// 添加字段注解
.enableTableFieldAnnotation()
// 实体文件名称
.formatFileName("%sDO")
// 覆盖实体文件
.enableFileOverride()
// mapper 配置
.mapperBuilder()
// dao 添加 @Mapper
.mapperAnnotation(Mapper.class)
// mapper 父类
.superClass(IMapper.class)
// 生成 BaseResultMap
.enableBaseResultMap()
// 生成 BaseColumnList
.enableBaseColumnList()
// mapper 文件名称
.formatMapperFileName("%sDAO")
// xml 文件名称
.formatXmlFileName("%sMapper")
// 覆盖 mapper 文件
.enableFileOverride()
// controller 配置
.controllerBuilder()
// controller 文件名称
.formatFileName("%sController")
// 脊柱命名法
.enableHyphenStyle()
// @RestController
.enableRestStyle()
// 覆盖 controller 文件
.enableFileOverride()
// service 配置
.serviceBuilder()
// 覆盖 service 文件
.enableFileOverride()
// service 名称
.formatServiceFileName("%sService")
// service impl 名称
.formatServiceImplFileName("%sServiceImpl")
// 构建
.build();
}
/**
* 获取包名配置
*
* @param module 模块
* @return 包名配置
*/
private static PackageConfig getPackageConfig(String module) {
return new PackageConfig.Builder()
// 声明父包
.parent("com.orion.ops.module")
// 模块名称
.moduleName(module)
// 实体类的包
.entity("entity.domain")
// 映射接口的包
.mapper("dao")
// 映射文件的包
.xml("mapper")
// service接口的包
.service("service")
// serviceImpl接口的包
.serviceImpl("service.impl")
// controller接口的包
.controller("controller")
// 构建
.build();
}
/**
* 获取模板配置
*
* @return 模板配置
*/
private static TemplateConfig getTemplateConfig() {
return new TemplateConfig.Builder()
.controller("/templates/orion-server-module-controller.java.vm")
.entity("/templates/orion-server-module-entity-do.java.vm")
.service("/templates/orion-server-module-service.java.vm")
.serviceImpl("/templates/orion-server-module-service-impl.java.vm")
.mapper("/templates/orion-server-module-mapper.java.vm")
.xml("/templates/orion-server-module-mapper.xml.vm")
.build();
}
/**
* 获取注入配置
*
* @return 注入配置
*/
private static InjectionConfig getInjectionConfig() {
String[][] customFileDefineArr = new String[][]{
// -------------------- 后端 - module --------------------
// http 文件
new String[]{"/templates/orion-server-module-controller.http.vm", "${type}Controller.http", "controller"},
// vo 文件
new String[]{"/templates/orion-server-module-entity-vo.java.vm", "${type}VO.java", "entity.vo"},
// create request 文件
new String[]{"/templates/orion-server-module-entity-request-create.java.vm", "${type}CreateRequest.java", "entity.request.${bizPackage}"},
// update request 文件
new String[]{"/templates/orion-server-module-entity-request-update.java.vm", "${type}UpdateRequest.java", "entity.request.${bizPackage}"},
// query request 文件
new String[]{"/templates/orion-server-module-entity-request-query.java.vm", "${type}QueryRequest.java", "entity.request.${bizPackage}"},
// export 文件
new String[]{"/templates/orion-server-module-entity-export.java.vm", "${type}Export.java", "entity.export"},
// convert 文件
new String[]{"/templates/orion-server-module-convert.java.vm", "${type}Convert.java", "convert"},
// cache dto 文件
new String[]{"/templates/orion-server-module-cache-dto.java.vm", "${type}CacheDTO.java", "entity.dto"},
// cache key define 文件
new String[]{"/templates/orion-server-module-cache-key-define.java.vm", "${type}CacheKeyDefine.java", "define.cache"},
// operator log define 文件
new String[]{"/templates/orion-server-module-operator-key-define.java.vm", "${type}OperatorType.java", "define.operator"},
// -------------------- 后端 - provider --------------------
// api 文件
new String[]{"/templates/orion-server-provider-api.java.vm", "${type}Api.java", "api"},
// api impl 文件
new String[]{"/templates/orion-server-provider-api-impl.java.vm", "${type}ApiImpl.java", "api.impl"},
// dto 文件
new String[]{"/templates/orion-server-provider-entity-dto.java.vm", "${type}DTO.java", "entity.dto.${bizPackage}"},
// create dto 文件
new String[]{"/templates/orion-server-provider-entity-dto-create.java.vm", "${type}CreateDTO.java", "entity.dto.${bizPackage}"},
// update dto 文件
new String[]{"/templates/orion-server-provider-entity-dto-update.java.vm", "${type}UpdateDTO.java", "entity.dto.${bizPackage}"},
// query dto 文件
new String[]{"/templates/orion-server-provider-entity-dto-query.java.vm", "${type}QueryDTO.java", "entity.dto.${bizPackage}"},
// convert 文件
new String[]{"/templates/orion-server-provider-convert.java.vm", "${type}ProviderConvert.java", "convert"},
// -------------------- 后端 - test --------------------
// service unit test 文件
new String[]{"/templates/orion-server-test-service-impl-tests.java.vm", "${type}ServiceImplTests.java", "service.impl"},
// api unit test 文件
new String[]{"/templates/orion-server-test-api-impl-tests.java.vm", "${type}ApiImplTests.java", "api.impl"},
// create table sql 文件
new String[]{"/templates/orion-server-test-create-table.sql.vm", "create-table-h2-${tableName}.sql", "sql"},
// -------------------- 前端 --------------------
// api 文件
new String[]{"/templates/orion-vue-api.ts.vm", "${feature}.ts", "vue/api/${module}"},
// router 文件
new String[]{"/templates/orion-vue-router.ts.vm", "${module}.${feature}.ts", "vue/router/routes/modules"},
// views index.ts 文件
new String[]{"/templates/orion-vue-views-index.vue.vm", "index.vue", "vue/views/${module}/${feature}"},
// form-modal.vue 文件
new String[]{"/templates/orion-vue-views-components-form-modal.vue.vm", "${feature}-form-modal.vue", "vue/views/${module}/${feature}/components"},
// form-drawer.vue 文件
new String[]{"/templates/orion-vue-views-components-form-drawer.vue.vm", "${feature}-form-drawer.vue", "vue/views/${module}/${feature}/components"},
// table.vue 文件
new String[]{"/templates/orion-vue-views-components-table.vue.vm", "${feature}-table.vue", "vue/views/${module}/${feature}/components"},
// card-list.vue 文件
new String[]{"/templates/orion-vue-views-components-card-list.vue.vm", "${feature}-card-list.vue", "vue/views/${module}/${feature}/components"},
// enum.types.ts 文件
new String[]{"/templates/orion-vue-views-types-enum.types.ts.vm", "enum.types.ts", "vue/views/${module}/${feature}/types"},
// const.ts 文件
new String[]{"/templates/orion-vue-views-types-const.ts.vm", "const.ts", "vue/views/${module}/${feature}/types"},
// form.rules.ts 文件
new String[]{"/templates/orion-vue-views-types-form.rules.ts.vm", "form.rules.ts", "vue/views/${module}/${feature}/types"},
// table.columns.ts 文件
new String[]{"/templates/orion-vue-views-types-table.columns.ts.vm", "table.columns.ts", "vue/views/${module}/${feature}/types"},
// card.fields.ts 文件
new String[]{"/templates/orion-vue-views-types-card.fields.ts.vm", "card.fields.ts", "vue/views/${module}/${feature}/types"},
// menu.sql 文件
new String[]{"/templates/orion-sql-menu.sql.vm", "${feature}-menu.sql", "sql"},
};
// 构建文件
List<CustomFile> customerFiles = Arrays.stream(customFileDefineArr)
.map(s -> new CustomFile.Builder()
// 覆盖文件
.enableFileOverride()
// 模板路径
.templatePath(s[0])
// 文件名
.fileName(s[1])
// 包名
.packageName(s[2])
.build())
.collect(Collectors.toList());
// 注入配置
return new InjectionConfig.Builder()
// 自定义 文件
.customFile(customerFiles)
// 构建
.build();
}
/**
* 打印提示信息
*/
private static void printTips() {
String line = AnsiAppender.create()
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "\n:: 代码生成完毕 ^_^ ::\n")
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "- 后端代码复制后请先 clean 父工程\n")
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "- 后端代码需要自行修改缓存逻辑\n")
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "- 后端代码修改完成后请先执行单元测试检测是否正常\n")
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "- vue 代码需要注意同一模块的 router 需要自行合并\n")
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "- vue 枚举需要自行更改数据类型\n")
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "- 菜单 sql 执行完成后 需要在系统菜单页面刷新缓存\n")
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "- 字典 sql 执行完成后 需要在字典配置项页面刷新缓存\n")
.toString();
System.out.print(line);
}
}

View File

@@ -0,0 +1,35 @@
package com.orion.ops.framework.mybatis.core.generator.engine;
import com.orion.lang.define.collect.MultiLinkedHashMap;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* vue 枚举
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/9/26 16:50
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EnumMeta {
/**
* 类名称
*/
private String className;
/**
* 备注
*/
private String comment;
/**
* 配置
*/
private MultiLinkedHashMap<String, String, Object> info;
}

View File

@@ -0,0 +1,499 @@
/*
* Copyright (c) 2011-2022, baomidou (jobob@qq.com).
*
* 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 com.orion.ops.framework.mybatis.core.generator.engine;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.config.ConstVal;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine;
import com.orion.lang.define.collect.MultiLinkedHashMap;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.VariableStyles;
import com.orion.lang.utils.io.Files1;
import com.orion.lang.utils.reflect.BeanMap;
import com.orion.lang.utils.reflect.Fields;
import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.constant.OrionOpsProConst;
import com.orion.ops.framework.mybatis.core.generator.template.Table;
import com.orion.ops.framework.mybatis.core.generator.template.VueEnum;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* 代码生成器 Velocity 引擎
*
* @author Jiahang Li
* @version 1.0.0
* @since 2022/4/20 10:33
*/
public class VelocityTemplateEngine extends AbstractTemplateEngine {
private final Map<String, Table> tables;
private VelocityEngine velocityEngine;
public VelocityTemplateEngine(Table[] tables) {
this.tables = Arrays.stream(tables)
.collect(Collectors.toMap(Table::getTableName, Function.identity()));
}
{
try {
Class.forName("org.apache.velocity.util.DuckType");
} catch (ClassNotFoundException e) {
LOGGER.warn("Velocity 1.x is outdated, please upgrade to 2.x or later.");
}
}
@Override
@NotNull
public VelocityTemplateEngine init(@NotNull ConfigBuilder configBuilder) {
if (velocityEngine == null) {
Properties p = new Properties();
p.setProperty(ConstVal.VM_LOAD_PATH_KEY, ConstVal.VM_LOAD_PATH_VALUE);
p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, StringPool.EMPTY);
p.setProperty(Velocity.ENCODING_DEFAULT, ConstVal.UTF8);
p.setProperty(Velocity.INPUT_ENCODING, ConstVal.UTF8);
p.setProperty("file.resource.loader.unicode", StringPool.TRUE);
this.velocityEngine = new VelocityEngine(p);
}
return this;
}
@Override
public void writer(@NotNull Map<String, Object> objectMap, @NotNull String templatePath, @NotNull File outputFile) throws Exception {
Template template = velocityEngine.getTemplate(templatePath, ConstVal.UTF8);
try (FileOutputStream fos = new FileOutputStream(outputFile);
OutputStreamWriter ow = new OutputStreamWriter(fos, ConstVal.UTF8);
BufferedWriter writer = new BufferedWriter(ow)) {
template.merge(new VelocityContext(objectMap), writer);
}
LOGGER.debug("模板: " + templatePath + "; 文件: " + outputFile);
}
@Override
@NotNull
public String templateFilePath(@NotNull String filePath) {
final String dotVm = ".vm";
return filePath.endsWith(dotVm) ? filePath : filePath + dotVm;
}
@Override
protected void outputCustomFile(@NotNull List<CustomFile> customFiles, @NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap) {
// 创建自定义文件副本文件
customFiles = this.createCustomFilesBackup(customFiles, tableInfo);
// 添加表元数据
this.addTableMeta(tableInfo, objectMap);
// 替换自定义包名
this.replacePackageName(customFiles, tableInfo, objectMap);
// 添加注释元数据
this.addApiCommentMeta(tableInfo, objectMap);
// 生成后端文件
this.generatorServerFile(customFiles, tableInfo, objectMap);
// 生成前端文件
this.generatorVueFile(customFiles, tableInfo, objectMap);
}
/**
* 创建自定义文件副本对象
* <p>
* - 根据类型进行移除不需要生成的模板
*
* @param originCustomerFile originCustomerFile
* @param tableInfo tableInfo
* @return backup
*/
private List<CustomFile> createCustomFilesBackup(@NotNull List<CustomFile> originCustomerFile,
@NotNull TableInfo tableInfo) {
// 生成文件副本
List<CustomFile> files = originCustomerFile.stream().map(s ->
new CustomFile.Builder()
.enableFileOverride()
.templatePath(s.getTemplatePath())
.filePath(s.getFilePath())
.fileName(s.getFileName())
.packageName(s.getPackageName())
.build())
.collect(Collectors.toList());
// 获取 table
Table table = tables.get(tableInfo.getName());
// 不生成对外 api 文件
if (!table.isEnableProviderApi()) {
files.removeIf(file -> this.isServerProviderFile(file.getTemplatePath()));
// 不生成对外 api 单元测试文件
if (table.isEnableUnitTest()) {
files.removeIf(file -> this.isServerProviderTestFile(file.getTemplatePath()));
}
}
// 不生成单元测试文件
if (!table.isEnableUnitTest()) {
files.removeIf(file -> this.isServerUnitTestFile(file.getTemplatePath()));
}
// 不生成缓存文件
if (!table.isEnableCache()) {
files.removeIf(file -> this.isServerCacheFile(file.getTemplatePath()));
}
// 不生成 vue 文件
if (!table.isEnableVue()) {
files.removeIf(file -> this.isVueFile(file.getTemplatePath()));
}
return files;
}
/**
* 插入表元数据
*
* @param tableInfo tableInfo
* @param objectMap objectMap
*/
private void addTableMeta(@NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap) {
// http 注释标识
objectMap.put("httpComment", "###");
// 版本
objectMap.put("since", OrionOpsProConst.VERSION);
// 替换业务注释
tableInfo.setComment(tables.get(tableInfo.getName()).getComment());
Table table = tables.get(tableInfo.getName());
// 缓存元数据
Map<String, Object> cacheMeta = this.pickTableMeta(table,
"enableCache", "cacheKey", "cacheDesc",
"cacheExpired", "cacheExpireTime", "cacheExpireUnit");
objectMap.put("cacheMeta", cacheMeta);
// 实体名称
String domainName = tableInfo.getEntityName();
String mappingHyphen = objectMap.get("controllerMappingHyphen").toString();
String entityName = domainName.substring(0, domainName.length() - 2);
// 类型
objectMap.put("type", entityName);
// 类型首字母小写
objectMap.put("typeLower", Strings.firstLower(entityName));
// 类型脊柱名称
objectMap.put("typeHyphen", mappingHyphen.substring(0, mappingHyphen.length() - 3));
// 类型常量
objectMap.put("typeConst", VariableStyles.BIG_HUMP.toSerpentine(entityName).toUpperCase());
}
/**
* 替换自定义文件包
*
* @param customFiles 自定义文件
* @param tableInfo tableInfo
* @param objectMap objectMap
*/
private void replacePackageName(@NotNull List<CustomFile> customFiles, @NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap) {
// 替换包名
customFiles.forEach(s -> {
// 反射调用 setter 方法
BiConsumer<String, Object> callSetter = (field, value) -> Fields.setFieldValue(s, field, value);
String packageName = s.getPackageName();
// 替换文件业务包名
if (packageName.contains(Const.DOLLAR)) {
Map<String, Object> meta = new HashMap<>(4);
meta.put("bizPackage", tables.get(tableInfo.getName()).getBizPackage());
// 调用 setter
callSetter.accept("packageName", Strings.format(packageName, meta));
}
});
// 包转换器
Function<Predicate<String>, List<String>> packageConverter = conv ->
customFiles.stream()
.filter(s -> conv.test(s.getTemplatePath()))
.map(CustomFile::getPackageName)
.map(s -> getConfigBuilder().getPackageConfig().getParent() + "." + s)
.distinct()
.collect(Collectors.toList());
// 自定义 module 文件的包 (导入用)
List<String> customModuleFilePackages = packageConverter.apply(s -> s.contains(".java.vm") && s.contains("orion-server-module"));
objectMap.put("customModuleFilePackages", customModuleFilePackages);
// 自定义 provider entity 文件的包 (导入用)
List<String> customProviderEntityFilePackages = packageConverter.apply(s -> s.contains(".java.vm") && s.contains("orion-server-provider-entity"));
objectMap.put("customProviderEntityFilePackages", customProviderEntityFilePackages);
// 自定义 provider interface 文件的包 (导入用)
List<String> customProviderFilePackages = packageConverter.apply(s -> s.contains(".java.vm") && s.contains("orion-server-provider"));
objectMap.put("customProviderFilePackages", customProviderFilePackages);
}
/**
* 插入 api 注释
*
* @param tableInfo tableInfo
* @param objectMap objectMap
*/
private void addApiCommentMeta(@NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap) {
Map<String, String> map = new HashMap<>(12);
objectMap.put("apiComment", map);
String comment = tableInfo.getComment();
map.put("create", "创建" + comment);
map.put("updateAll", "根据条件更新" + comment);
map.put("updateById", "更新" + comment);
map.put("getById", "查询" + comment);
map.put("getByIdList", "批量查询" + comment);
map.put("queryList", "查询全部" + comment);
map.put("queryListByCache", "通过缓存查询" + comment);
map.put("queryPage", "分页查询" + comment);
map.put("queryCount", "查询" + comment + "数量");
map.put("deleteById", "删除" + comment);
map.put("deleteAll", "根据条件删除" + comment);
map.put("batchDelete", "批量删除" + comment);
map.put("export", "导出" + comment);
}
/**
* 生成后端文件
*
* @param customFiles customFiles
* @param tableInfo tableInfo
* @param objectMap objectMap
*/
private void generatorServerFile(@NotNull List<CustomFile> customFiles, @NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap) {
// 过滤文件
customFiles = customFiles.stream()
.filter(s -> this.isServerFile(s.getTemplatePath()))
.collect(Collectors.toList());
// 生成文件
customFiles.forEach(file -> {
// 获取 parent package
String currentPackage = getConfigBuilder().getPackageConfig().getParent() + "." + file.getPackageName();
// 设置当前包
objectMap.put("currentPackage", currentPackage);
// 文件路径
String filePath = this.getPathInfo(OutputFile.parent) + File.separator + file.getPackageName()
.replaceAll("\\.", StringPool.BACK_SLASH + File.separator);
// 文件名称
Map<String, Object> fileNameMeta = new HashMap<>(4);
fileNameMeta.put("type", objectMap.get("type"));
fileNameMeta.put("tableName", tableInfo.getName());
String fileName = filePath + File.separator + Strings.format(file.getFileName(), fileNameMeta);
// 渲染文件
this.outputFile(Files1.newFile(fileName), objectMap, file.getTemplatePath(), file.isFileOverride());
});
}
/**
* 生成前端文件
*
* @param customFiles customFiles
* @param tableInfo tableInfo
* @param objectMap objectMap
*/
private void generatorVueFile(@NotNull List<CustomFile> customFiles, @NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap) {
// 不生成 vue 文件
if (!tables.get(tableInfo.getName()).isEnableVue()) {
return;
}
// 过滤文件
customFiles = customFiles.stream()
.filter(s -> this.isVueFile(s.getTemplatePath()))
.collect(Collectors.toList());
// 设置前端元数据
Table table = tables.get(tableInfo.getName());
Map<String, Object> vueMeta = this.pickTableMeta(table, "module", "feature", "enableDrawerForm", "enableRowSelection", "enableCardView");
// 模块名称实体
vueMeta.put("moduleEntity", VariableStyles.SPINE.toBigHump(table.getModule()));
// 模块名称实体
vueMeta.put("moduleEntityFirstLower", Strings.firstLower(vueMeta.get("moduleEntity")));
// 模块名称常量
vueMeta.put("moduleConst", VariableStyles.SPINE.toSerpentine(table.getModule()).toUpperCase());
// 功能名称实体
vueMeta.put("featureEntity", VariableStyles.SPINE.toBigHump(table.getFeature()));
// 功能名称实体
vueMeta.put("featureEntityFirstLower", Strings.firstLower(vueMeta.get("featureEntity")));
// 功能名称常量
vueMeta.put("featureConst", VariableStyles.SPINE.toSerpentine(table.getFeature()).toUpperCase());
// 枚举
vueMeta.put("enums", this.getEnumMap(tableInfo, table));
objectMap.put("vue", vueMeta);
// 生成文件
customFiles.forEach(file -> {
// 文件路径
String filePath = getConfigBuilder().getGlobalConfig().getOutputDir()
+ "/" + Strings.format(file.getPackageName(), vueMeta)
+ "/" + Strings.format(file.getFileName(), vueMeta);
// 渲染文件
this.outputFile(Files1.newFile(filePath), objectMap, file.getTemplatePath(), file.isFileOverride());
});
}
/**
* 是否为后端文件
*
* @param templatePath templatePath
* @return 是否为后端文件
*/
private boolean isServerFile(String templatePath) {
return templatePath.contains("orion-server");
}
/**
* 是否为后端 provider 文件
*
* @param templatePath templatePath
* @return 是否为后端 provider 文件
*/
private boolean isServerProviderFile(String templatePath) {
return templatePath.contains("orion-server-provider");
}
/**
* 是否为后端 provider 单元测试文件
*
* @param templatePath templatePath
* @return 是否为后端 provider 单元测试文件
*/
private boolean isServerProviderTestFile(String templatePath) {
return templatePath.contains("orion-server-test-api");
}
/**
* 是否为后端单元测试文件
*
* @param templatePath templatePath
* @return 是否为后端单元测试文件
*/
private boolean isServerUnitTestFile(String templatePath) {
return templatePath.contains("orion-server-test");
}
/**
* 是否为后端缓存文件
*
* @param templatePath templatePath
* @return 是否为后端缓存文件
*/
private boolean isServerCacheFile(String templatePath) {
return templatePath.contains("orion-server-module-cache");
}
/**
* 是否为 vue 文件
*
* @param templatePath templatePath
* @return 是否为 vue 文件
*/
private boolean isVueFile(String templatePath) {
return templatePath.contains("orion-vue-") ||
templatePath.contains("orion-sql-menu.sql");
}
/**
* 获取表元数据
*
* @param table table
* @param keys keys
* @return meta
*/
private Map<String, Object> pickTableMeta(Table table, String... keys) {
BeanMap beanMap = BeanMap.create(table);
Map<String, Object> tableMeta = new HashMap<>();
for (String key : keys) {
tableMeta.put(key, beanMap.get(key));
}
return tableMeta;
}
/**
* 获取枚举
*
* @param tableInfo tableInfo
* @param table table
* @return enumMap
*/
private Map<String, EnumMeta> getEnumMap(TableInfo tableInfo, Table table) {
// 枚举值
Map<String, EnumMeta> enumMap = new LinkedHashMap<>();
for (VueEnum meta : table.getEnums()) {
// 检查字段是否存在
String variable = meta.getVariable();
TableField tableField = tableInfo.getFields()
.stream()
.filter(s -> variable.equals(s.getName()) || variable.equals(s.getPropertyName()))
.findFirst()
.orElseThrow(() -> new RuntimeException("未查询到枚举映射字段 " + variable));
// 设置枚举名称
if (meta.getClassName() == null) {
meta.setClassName(Strings.firstUpper(tableField.getPropertyName()) + "Enum");
}
// 设置枚举注释
if (meta.getComment() == null) {
meta.setComment(Strings.def(tableField.getComment(), meta.getClassName()));
}
// 设置枚举
MultiLinkedHashMap<String, String, Object> enumInfo = new MultiLinkedHashMap<>();
for (int i = 0; i < meta.getNames().size(); i++) {
// 枚举名称
String name = meta.getNames().get(i);
// 设置枚举值
for (int j = 0; j < meta.getFields().size(); j++) {
String field = meta.getFields().get(j);
Object value = safeGet(safeGet(meta.getValues(), j), i);
enumInfo.put(name, field, value);
}
// 检查是否有 value
if (!meta.getFields().contains(Const.VALUE)) {
// 没有 value 用 name
enumInfo.put(name, Const.VALUE, name);
}
}
enumMap.put(tableField.getPropertyName(), new EnumMeta(meta.getClassName(), meta.getComment(), enumInfo));
}
return enumMap;
}
/**
* 获取值
*
* @param list list
* @param index index
* @param <T> T
* @return value
*/
private <T> T safeGet(List<T> list, int index) {
if (list == null) {
return null;
}
if (list.size() > index) {
return list.get(index);
} else {
return null;
}
}
}

View File

@@ -0,0 +1,103 @@
package com.orion.ops.framework.mybatis.core.generator.template;
import java.util.concurrent.TimeUnit;
/**
* 后端代码缓存模板
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/9/26 1:14
*/
public class CacheTemplate extends ServerTemplate {
public CacheTemplate(Table table) {
this(table, table.cacheKey, table.cacheDesc);
}
public CacheTemplate(Table table, String key) {
this(table, key, table.cacheDesc);
}
public CacheTemplate(Table table, String key, String desc) {
super(table);
table.enableCache = true;
table.cacheExpireTime = 1;
table.cacheExpireUnit = TimeUnit.HOURS;
table.cacheKey = key;
table.cacheDesc = desc;
}
/**
* 设置缓存 key
*
* @param key key
* @return this
*/
public CacheTemplate key(String key) {
table.cacheKey = key;
return this;
}
/**
* 设置缓存 key
*
* @param key key
* @return this
*/
public CacheTemplate key(String key, String desc) {
table.cacheKey = key;
table.cacheDesc = desc;
return this;
}
/**
* 设置缓存描述
*
* @param desc desc
* @return this
*/
public CacheTemplate desc(String desc) {
table.cacheDesc = desc;
return this;
}
/**
* 设置缓存过期时间
*
* @param time time
* @return this
*/
public CacheTemplate expireTime(Integer time) {
table.cacheExpired = true;
table.cacheExpireTime = time;
return this;
}
/**
* 设置缓存过期时间单位
*
* @param unit unit
* @return this
*/
public CacheTemplate expireUnit(TimeUnit unit) {
table.cacheExpired = true;
table.cacheExpireUnit = unit;
return this;
}
/**
* 设置缓存过期时间
*
* @param time time
* @param unit unit
* @return this
*/
public CacheTemplate expire(Integer time, TimeUnit unit) {
table.cacheExpired = true;
table.cacheExpireTime = time;
table.cacheExpireUnit = unit;
return this;
}
}

View File

@@ -0,0 +1,151 @@
package com.orion.ops.framework.mybatis.core.generator.template;
import com.orion.lang.utils.Enums;
import com.orion.lang.utils.collect.Lists;
import com.orion.lang.utils.reflect.Fields;
import com.orion.ops.framework.common.constant.Const;
import java.util.List;
import java.util.stream.Collectors;
/**
* 前端代码枚举模板
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/9/26 1:14
*/
public class EnumsTemplate extends VueTemplate {
private final VueEnum vueEnum;
public EnumsTemplate(Table table, String variable) {
this(table, variable, (String) null);
}
public EnumsTemplate(Table table, String variable, String className) {
super(table);
this.vueEnum = new VueEnum(variable, className);
table.enums.add(vueEnum);
}
public EnumsTemplate(Table table, String variable, Class<? extends Enum<?>> enumClass) {
super(table);
this.vueEnum = new VueEnum(variable);
table.enums.add(vueEnum);
this.parseEnumMeta(enumClass);
}
/**
* 解析枚举
*
* @param enumClass enumClass
*/
private void parseEnumMeta(Class<? extends Enum<?>> enumClass) {
// 获取枚举
List<? extends Enum<?>> enumList = Lists.of(enumClass.getEnumConstants());
// 枚举名称
List<String> names = enumList.stream()
.map(Enum::name)
.collect(Collectors.toList());
// 枚举字段
List<String> fields = Enums.getFields(enumClass);
// 枚举值
List<List<Object>> values = fields.stream()
.map(field -> enumList.stream()
.map(enumItem -> Fields.getFieldValue(enumItem, field))
.collect(Collectors.toList()))
.collect(Collectors.toList());
vueEnum.className = enumClass.getSimpleName();
vueEnum.names.addAll(names);
vueEnum.fields.addAll(fields);
vueEnum.values.addAll(values);
}
/**
* 设置类名
*
* @param className className
* @return this
*/
public EnumsTemplate className(String className) {
vueEnum.className = className;
return this;
}
/**
* 设置注释
*
* @param comment comment
* @return this
*/
public EnumsTemplate comment(String comment) {
vueEnum.comment = comment;
return this;
}
/**
* 设置枚举名称
*
* @param names names
* @return this
*/
public EnumsTemplate names(String... names) {
vueEnum.names.addAll(Lists.of(names));
return this;
}
/**
* 设置字段值
*
* @param values values
* @return this
*/
public EnumsTemplate values(String field, Object... values) {
vueEnum.fields.add(field);
vueEnum.values.add(Lists.of(values));
return this;
}
/**
* 添加 label
*
* @param labels labels
* @return this
*/
public EnumsTemplate label(Object... labels) {
return this.values(Const.LABEL, labels);
}
/**
* 添加 value
* 如果 value 和 name 相同可以省略 (无 value 自动使用 name)
*
* @param values values
* @return this
*/
public EnumsTemplate value(Object... values) {
return this.values(Const.VALUE, values);
}
/**
* 添加 color
*
* @param colors colors
* @return this
*/
public EnumsTemplate color(Object... colors) {
return this.values(Const.COLOR, colors);
}
/**
* 添加 status
*
* @param status status
* @return this
*/
public EnumsTemplate status(Object... status) {
return this.values(Const.STATUS, status);
}
}

View File

@@ -0,0 +1,137 @@
package com.orion.ops.framework.mybatis.core.generator.template;
/**
* 后端代码模板
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/9/26 1:14
*/
public class ServerTemplate extends Template {
public ServerTemplate(Table table) {
super(table);
table.enableUnitTest = true;
}
public ServerTemplate(Table table, String tableName) {
super(table);
table.tableName = tableName;
table.enableUnitTest = true;
}
public ServerTemplate(Table table, String tableName, String comment, String bizPackage) {
super(table);
table.tableName = tableName;
table.comment = comment;
table.bizPackage = bizPackage;
table.enableUnitTest = true;
table.enableOperatorLog = true;
}
/**
* 设置表名称
*
* @param tableName tableName
* @return this
*/
public ServerTemplate tableName(String tableName) {
table.tableName = tableName;
return this;
}
/**
* 设置业务注释
*
* @param comment comment
* @return this
*/
public ServerTemplate comment(String comment) {
table.comment = comment;
return this;
}
/**
* 设置业务实体包名
*
* @param bizPackage bizPackage
* @return this
*/
public ServerTemplate bizPackage(String bizPackage) {
table.bizPackage = bizPackage;
return this;
}
/**
* 是否生成对外 api
*
* @return this
*/
public ServerTemplate enableProviderApi() {
table.enableProviderApi = true;
return this;
}
// fixme
/**
* 生成导出
*
* @return this
*/
public ServerTemplate enableExport() {
table.enableExport = false;
return this;
}
/**
* 不生成单元测试
*
* @return this
*/
public ServerTemplate disableUnitTest() {
table.enableUnitTest = false;
return this;
}
// fixme
/**
* 不生成操作日志
*
* @return this
*/
public ServerTemplate disableOperatorLog() {
table.enableUnitTest = false;
return this;
}
/**
* 设置 cache
*
* @return cache
*/
public CacheTemplate cache() {
return new CacheTemplate(table);
}
/**
* 设置 cache
*
* @param key key
* @return cache
*/
public CacheTemplate cache(String key) {
return new CacheTemplate(table, key);
}
/**
* 设置 cache
*
* @param key key
* @param desc desc
* @return cache
*/
public CacheTemplate cache(String key, String desc) {
return new CacheTemplate(table, key, desc);
}
}

View File

@@ -0,0 +1,127 @@
package com.orion.ops.framework.mybatis.core.generator.template;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author Jiahang Li
* @version 1.0.0
* @since 2023/7/17 10:44
*/
@Data
public class Table {
// -------------------- 后端 --------------------
/**
* 表名称
*/
protected String tableName;
/**
* 业务注释
*/
protected String comment;
/**
* 业务实体包名
* <p>
* request dto 包
*/
protected String bizPackage;
/**
* 是否生成对外 api
*/
protected boolean enableProviderApi;
/**
* 是否生成单元测试
*/
protected boolean enableUnitTest;
/**
* 是否生成导出
*/
protected boolean enableExport;
/**
* 是否可缓存
*/
protected boolean enableCache;
/**
* 是否生成操作日志
*/
protected boolean enableOperatorLog;
/**
* 缓存的 key
*/
protected String cacheKey;
/**
* 缓存描述
*/
protected String cacheDesc;
/**
* 缓存是否会过期
*/
protected boolean cacheExpired;
/**
* 缓存过期时间
*/
protected Integer cacheExpireTime;
/**
* 缓存过期时间单位
*/
protected TimeUnit cacheExpireUnit;
// -------------------- 前端 --------------------
/**
* 是否生成 vue 代码
*/
protected boolean enableVue;
/**
* 模块 用于文件名称生成
*/
protected String module;
/**
* 功能 用于文件名称生成
*/
protected String feature;
/**
* 使用抽屉表单
*/
protected boolean enableDrawerForm;
/**
* 列表可多选
*/
protected boolean enableRowSelection;
/**
* 使用卡片视图
*/
protected boolean enableCardView;
/**
* 生成的枚举文件
*/
protected List<VueEnum> enums;
protected Table() {
this.enums = new ArrayList<>();
}
}

View File

@@ -0,0 +1,81 @@
package com.orion.ops.framework.mybatis.core.generator.template;
import lombok.Data;
/**
* 代码生成模板
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/9/26 0:51
*/
@Data
public class Template {
protected final Table table;
protected Template(Table table) {
this.table = table;
}
/**
* 创建模板
*
* @param tableName 表名称
* @return Template
*/
public static ServerTemplate create(String tableName) {
return new ServerTemplate(new Table(), tableName);
}
/**
* 创建模板
*
* @param tableName 表名称
* @param comment 业务注释
* @param bizPackage 业务包名
* @return Template
*/
public static ServerTemplate create(String tableName, String comment, String bizPackage) {
return new ServerTemplate(new Table(), tableName, comment, bizPackage);
}
/**
* 设置 server
*
* @return ServerTemplate
*/
public ServerTemplate server() {
return new ServerTemplate(table);
}
/**
* 设置 vue
*
* @return vue
*/
public VueTemplate vue() {
return new VueTemplate(table);
}
/**
* 设置 vue
*
* @param module 模块
* @param feature 功能
* @return vue
*/
public VueTemplate vue(String module, String feature) {
return new VueTemplate(table, module, feature);
}
/**
* 构建
*
* @return table
*/
public Table build() {
return table;
}
}

View File

@@ -0,0 +1,69 @@
package com.orion.ops.framework.mybatis.core.generator.template;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 枚举元数据
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/9/26 13:53
*/
@Data
public class VueEnum {
/**
* 替换的枚举字段 数据库/小驼峰
*/
protected String variable;
/**
* 枚举类名 如果为空使用 field.propertyName + Enum
*/
protected String className;
/**
* 枚举注释 如果为空使用 field.comment || className
*/
protected String comment;
/**
* 枚举名称
*/
protected List<String> names;
/**
* 枚举字段
*/
protected List<String> fields;
/**
* 枚举值
*/
protected List<List<Object>> values;
public VueEnum(String variable) {
this(variable, null);
}
public VueEnum(String variable, String className) {
this.className = className;
this.variable = variable;
this.names = new ArrayList<>();
this.fields = new ArrayList<>();
this.values = new ArrayList<>();
}
public VueEnum(String variable, String className, String comment, List<String> names, List<String> fields, List<List<Object>> values) {
this.variable = variable;
this.className = className;
this.comment = comment;
this.names = names;
this.fields = fields;
this.values = values;
}
}

View File

@@ -0,0 +1,108 @@
package com.orion.ops.framework.mybatis.core.generator.template;
/**
* 前端代码模板
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/9/26 1:17
*/
public class VueTemplate extends Template {
public VueTemplate(Table table) {
super(table);
table.enableVue = true;
}
public VueTemplate(Table table, String module, String feature) {
super(table);
table.enableVue = true;
table.module = module;
table.feature = feature;
}
/**
* 设置模块 用于文件名称生成
*
* @param module module
* @return this
*/
public VueTemplate module(String module) {
table.module = module;
return this;
}
/**
* 设置功能 用于文件名称生成
*
* @param feature feature
* @return this
*/
public VueTemplate feature(String feature) {
table.feature = feature;
return this;
}
/**
* 使用抽屉表单
*
* @return this
*/
public VueTemplate enableDrawerForm() {
table.enableDrawerForm = true;
return this;
}
/**
* 列表可多选
*
* @return this
*/
public VueTemplate enableRowSelection() {
table.enableRowSelection = true;
return this;
}
/**
* 启用卡片列表
*
* @return this
*/
public VueTemplate enableCardView() {
table.enableCardView = true;
return this;
}
/**
* 设置枚举
*
* @param variable 枚举字段 数据库/小驼峰
* @return enums
*/
public EnumsTemplate enums(String variable) {
return new EnumsTemplate(table, variable);
}
/**
* 设置枚举
*
* @param variable 枚举字段 数据库/小驼峰
* @param className className
* @return enums
*/
public EnumsTemplate enums(String variable, String className) {
return new EnumsTemplate(table, variable, className);
}
/**
* 设置枚举
*
* @param variable 枚举字段 数据库/小驼峰
* @param enumClass 枚举类
* @return enums
*/
public EnumsTemplate enums(String variable, Class<? extends Enum<?>> enumClass) {
return new EnumsTemplate(table, variable, enumClass);
}
}

View File

@@ -0,0 +1,45 @@
package ${currentPackage};
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import lombok.*;
import java.util.*;
import java.math.*;
/**
* $!{table.comment} 缓存对象
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "${type}CacheDTO", description = "$!{table.comment} 缓存对象")
public class ${type}CacheDTO implements Serializable {
private static final long serialVersionUID = 1L;
#foreach($field in ${table.fields})
#if("$!field.comment" != "")
@Schema(description = "${field.comment}")
#end
private ${field.propertyType} ${field.propertyName};
#end
@Schema(description = "创建时间")
private Date createTime;
@Schema(description = "修改时间")
private Date updateTime;
@Schema(description = "创建人")
private String creator;
@Schema(description = "修改人")
private String updater;
}

View File

@@ -0,0 +1,29 @@
package ${currentPackage};
import com.orion.lang.define.cache.CacheKeyBuilder;
import com.orion.lang.define.cache.CacheKeyDefine;
#foreach($pkg in ${customModuleFilePackages})
import ${pkg}.*;
#end
import java.util.concurrent.TimeUnit;
/**
* $!{table.comment}缓存 key
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
public interface ${type}CacheKeyDefine {
CacheKeyDefine $typeConst = new CacheKeyBuilder()
.key("$cacheMeta.cacheKey")
.desc("$cacheMeta.cacheDesc")
.type(${type}CacheDTO.class)
#if($cacheMeta.cacheExpired)
.timeout($cacheMeta.cacheExpireTime, TimeUnit.$cacheMeta.cacheExpireUnit.name())
#end
.build();
}

View File

@@ -0,0 +1,85 @@
${httpComment} ${apiComment.create}
POST {{baseUrl}}/${package.ModuleName}/${typeHyphen}/create
Content-Type: application/json
Authorization: {{token}}
{
#foreach($field in ${table.fields})
#if("$!field.propertyName" != "id")
"${field.propertyName}": ""#if($foreach.hasNext),#end
#end
#end
}
${httpComment} ${apiComment.updateById}
PUT {{baseUrl}}/${package.ModuleName}/${typeHyphen}/update
Content-Type: application/json
Authorization: {{token}}
{
#foreach($field in ${table.fields})
"${field.propertyName}": ""#if($foreach.hasNext),#end
#end
}
${httpComment} ${apiComment.getById}
GET {{baseUrl}}/${package.ModuleName}/${typeHyphen}/get?id=1
Authorization: {{token}}
${httpComment} ${apiComment.getByIdList}
GET {{baseUrl}}/${package.ModuleName}/${typeHyphen}/batch-get?idList=1,2,3
Authorization: {{token}}
${httpComment} ${apiComment.queryList}
POST {{baseUrl}}/${package.ModuleName}/${typeHyphen}/list
Content-Type: application/json
Authorization: {{token}}
{
#foreach($field in ${table.fields})
"${field.propertyName}": ""#if($foreach.hasNext),#end
#end
}
${httpComment} ${apiComment.queryPage}
POST {{baseUrl}}/${package.ModuleName}/${typeHyphen}/query
Content-Type: application/json
Authorization: {{token}}
{
"page": 1,
"limit": 10,
#foreach($field in ${table.fields})
"${field.propertyName}": ""#if($foreach.hasNext),#end
#end
}
${httpComment} ${apiComment.deleteById}
DELETE {{baseUrl}}/${package.ModuleName}/${typeHyphen}/delete?id=1
Authorization: {{token}}
${httpComment} ${apiComment.batchDelete}
DELETE {{baseUrl}}/${package.ModuleName}/${typeHyphen}/batch-delete?idList=1,2,3
Authorization: {{token}}
${httpComment} ${apiComment.export}
POST {{baseUrl}}/${package.ModuleName}/${typeHyphen}/export
Content-Type: application/json
Authorization: {{token}}
{
#foreach($field in ${table.fields})
"${field.propertyName}": ""#if($foreach.hasNext),#end
#end
}
${httpComment}

View File

@@ -0,0 +1,130 @@
package ${package.Controller};
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.ops.framework.biz.operator.log.core.annotation.OperatorLog;
import com.orion.ops.framework.common.validator.group.Page;
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
import com.orion.ops.framework.web.core.annotation.RestWrapper;
import ${package.Service}.*;
#foreach($pkg in ${customModuleFilePackages})
import ${pkg}.*;
#end
#if(${superControllerClassPackage})
import ${superControllerClassPackage};
#end
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* $!{table.comment} api
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Tag(name = "${package.ModuleName} - $!{table.comment}服务")
@Slf4j
@Validated
@RestWrapper
@RestController
@RequestMapping("/${package.ModuleName}/${typeHyphen}")
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
#if(${superControllerClass})
public class ${table.controllerName} extends ${superControllerClass} {
#else
public class ${table.controllerName} {
#end
@Resource
private ${type}Service ${typeLower}Service;
@OperatorLog(${type}OperatorType.CREATE)
@PostMapping("/create")
@Operation(summary = "${apiComment.create}")
@PreAuthorize("@ss.hasPermission('${package.ModuleName}:${typeHyphen}:create')")
public Long create${type}(@Validated @RequestBody ${type}CreateRequest request) {
return ${typeLower}Service.create${type}(request);
}
@OperatorLog(${type}OperatorType.UPDATE)
@PutMapping("/update")
@Operation(summary = "${apiComment.updateById}")
@PreAuthorize("@ss.hasPermission('${package.ModuleName}:${typeHyphen}:update')")
public Integer update${type}(@Validated @RequestBody ${type}UpdateRequest request) {
return ${typeLower}Service.update${type}ById(request);
}
@IgnoreLog(IgnoreLogMode.RET)
@GetMapping("/get")
@Operation(summary = "${apiComment.getById}")
@Parameter(name = "id", description = "id", required = true)
@PreAuthorize("@ss.hasPermission('${package.ModuleName}:${typeHyphen}:query')")
public ${type}VO get${type}(@RequestParam("id") Long id) {
return ${typeLower}Service.get${type}ById(id);
}
@IgnoreLog(IgnoreLogMode.RET)
@GetMapping("/batch-get")
@Operation(summary = "${apiComment.getByIdList}")
@Parameter(name = "idList", description = "idList", required = true)
@PreAuthorize("@ss.hasPermission('${package.ModuleName}:${typeHyphen}:query')")
public List<${type}VO> get${type}Batch(@RequestParam("idList") List<Long> idList) {
return ${typeLower}Service.get${type}ByIdList(idList);
}
@IgnoreLog(IgnoreLogMode.RET)
@PostMapping("/list")
@Operation(summary = "${apiComment.queryList}")
@PreAuthorize("@ss.hasPermission('${package.ModuleName}:${typeHyphen}:query')")
public List<${type}VO> get${type}List(@Validated @RequestBody ${type}QueryRequest request) {
return ${typeLower}Service.get${type}List(request);
}
@IgnoreLog(IgnoreLogMode.RET)
@PostMapping("/query")
@Operation(summary = "${apiComment.queryPage}")
@PreAuthorize("@ss.hasPermission('${package.ModuleName}:${typeHyphen}:query')")
public DataGrid<${type}VO> get${type}Page(@Validated(Page.class) @RequestBody ${type}QueryRequest request) {
return ${typeLower}Service.get${type}Page(request);
}
@OperatorLog(${type}OperatorType.DELETE)
@DeleteMapping("/delete")
@Operation(summary = "${apiComment.deleteById}")
@Parameter(name = "id", description = "id", required = true)
@PreAuthorize("@ss.hasPermission('${package.ModuleName}:${typeHyphen}:delete')")
public Integer delete${type}(@RequestParam("id") Long id) {
return ${typeLower}Service.delete${type}ById(id);
}
@OperatorLog(${type}OperatorType.DELETE)
@DeleteMapping("/batch-delete")
@Operation(summary = "${apiComment.batchDelete}")
@Parameter(name = "idList", description = "idList", required = true)
@PreAuthorize("@ss.hasPermission('${package.ModuleName}:${typeHyphen}:delete')")
public Integer batchDelete${type}(@RequestParam("idList") List<Long> idList) {
return ${typeLower}Service.delete${type}ByIdList(idList);
}
@OperatorLog(${type}OperatorType.EXPORT)
@PostMapping("/export")
@Operation(summary = "${apiComment.export}")
@PreAuthorize("@ss.hasPermission('${package.ModuleName}:${typeHyphen}:export')")
public void export${type}(@Validated @RequestBody ${type}QueryRequest request,
HttpServletResponse response) throws IOException {
${typeLower}Service.export${type}(request, response);
}
}

View File

@@ -0,0 +1,42 @@
package ${currentPackage};
import ${package.Entity}.*;
#foreach($pkg in ${customModuleFilePackages})
import ${pkg}.*;
#end
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* $!{table.comment} 内部对象转换器
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Mapper
public interface ${type}Convert {
${type}Convert MAPPER = Mappers.getMapper(${type}Convert.class);
${type}DO to(${type}CreateRequest request);
${type}DO to(${type}UpdateRequest request);
${type}DO to(${type}QueryRequest request);
${type}VO to(${type}DO domain);
${type}Export toExport(${type}DO domain);
List<${type}VO> to(List<${type}DO> list);
#if($cacheMeta.enableCache)
${type}VO to(${type}CacheDTO cache);
${type}CacheDTO toCache(${type}DO domain);
#end
}

View File

@@ -0,0 +1,78 @@
package ${package.Entity};
import com.baomidou.mybatisplus.annotation.*;
import com.orion.ops.framework.mybatis.core.domain.BaseDO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.math.*;
/**
* $!{table.comment} 实体对象
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
#if(${table.convert})
@TableName(value = "${schemaName}${table.name}", autoResultMap = true)
#end
@Schema(name = "${entity}", description = "$!{table.comment} 实体对象")
#if(${superEntityClass})
public class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {
#elseif(${entitySerialVersionUID})
public class ${entity} implements Serializable {
#else
public class ${entity} {
#end
#if(${entitySerialVersionUID})
private static final long serialVersionUID = 1L;
#end
## ---------- BEGIN 字段循环遍历 ----------
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
#if("$!field.comment" != "")
@Schema(description = "${field.comment}")
#end
#if(${field.keyFlag})
## 主键
#if(${field.keyIdentityFlag})
@TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
#elseif(!$null.isNull(${idType}) && "$!idType" != "")
@TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
#elseif(${field.convert})
@TableId("${field.annotationColumnName}")
#end
## 普通字段
#elseif(${field.fill})
## ----- 存在字段填充设置 -----
#if(${field.convert})
@TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
#else
@TableField(fill = FieldFill.${field.fill})
#end
#elseif(${field.convert})
@TableField("${field.annotationColumnName}")
#end
## 乐观锁注解
#if(${field.versionField})
@Version
#end
## 逻辑删除注解
#if(${field.logicDeleteField})
@TableLogic
#end
private ${field.propertyType} ${field.propertyName};
#end
}

View File

@@ -0,0 +1,63 @@
package ${currentPackage};
import com.orion.lang.utils.time.Dates;
import com.orion.office.excel.annotation.ExportField;
import com.orion.office.excel.annotation.ExportSheet;
import com.orion.office.excel.annotation.ExportTitle;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.io.Serializable;
import java.util.*;
import java.math.*;
/**
* $!{table.comment} 导出对象
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ExportTitle(title = ${type}Export.TITLE)
@ExportSheet(name = "$!{table.comment}", filterHeader = true, freezeHeader = true, indexToSort = true)
@Schema(name = "${type}Export", description = "$!{table.comment}导出对象")
public class ${type}Export implements Serializable {
public static final String TITLE = "$!{table.comment}导出";
#foreach($field in ${table.fields})
#if("$!field.comment" != "")
@Schema(description = "${field.comment}")
#end
#if("$field.propertyType" == "Date")
@ExportField(index = ${foreach.index}, header = "${field.comment}", width = 16, format = Dates.YMD_HMS)
#else
@ExportField(index = ${foreach.index}, header = "${field.comment}", width = 16)
#end
private ${field.propertyType} ${field.propertyName};
#end
@ExportField(index = $table.fields.size(), header = "创建时间", width = 16, format = Dates.YMD_HMS)
@Schema(description = "创建时间")
private Date createTime;
#set($updateTimeIndex=$table.fields.size() + 1)
@Schema(description = "修改时间")
@ExportField(index = $updateTimeIndex, header = "修改时间", width = 16, format = Dates.YMD_HMS)
private Date updateTime;
#set($creatorIndex=$table.fields.size() + 2)
@Schema(description = "创建人")
@ExportField(index = $creatorIndex, header = "创建人", width = 16)
private String creator;
#set($updaterIndex=$table.fields.size() + 3)
@Schema(description = "修改人")
@ExportField(index = $updaterIndex, header = "修改人", width = 16)
private String updater;
}

View File

@@ -0,0 +1,51 @@
package ${currentPackage};
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.*;
import java.math.*;
/**
* $!{table.comment} 创建请求对象
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "${type}CreateRequest", description = "$!{table.comment} 创建请求对象")
public class ${type}CreateRequest implements Serializable {
#foreach($field in ${table.fields})
#if("$!field.propertyName" != "id")
#if("$field.propertyType" == "String")
@NotBlank
#if("$field.metaInfo.jdbcType" != "LONGVARCHAR")
@Size(max = $field.metaInfo.length)
#end
#else
@NotNull
#end
#if("$!field.comment" != "")
@Schema(description = "${field.comment}")
#end
#if("$field.propertyType" == "Date")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
#end
private ${field.propertyType} ${field.propertyName};
#end
#end
}

View File

@@ -0,0 +1,43 @@
package ${currentPackage};
import com.fasterxml.jackson.annotation.JsonFormat;
import com.orion.ops.framework.common.entity.PageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import javax.validation.constraints.Size;
import java.util.*;
import java.math.*;
/**
* $!{table.comment} 查询请求对象
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "${type}QueryRequest", description = "$!{table.comment} 查询请求对象")
public class ${type}QueryRequest extends PageRequest {
@Schema(description = "搜索")
private String searchValue;
#foreach($field in ${table.fields})
#if("$field.propertyType" == "String" && "$field.metaInfo.jdbcType" != "LONGVARCHAR")
@Size(max = $field.metaInfo.length)
#end
#if("$!field.comment" != "")
@Schema(description = "${field.comment}")
#end
#if("$field.propertyType" == "Date")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
#end
private ${field.propertyType} ${field.propertyName};
#end
}

View File

@@ -0,0 +1,49 @@
package ${currentPackage};
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.*;
import java.math.*;
/**
* $!{table.comment} 更新请求对象
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "${type}UpdateRequest", description = "$!{table.comment} 更新请求对象")
public class ${type}UpdateRequest implements Serializable {
#foreach($field in ${table.fields})
#if("$field.propertyType" == "String")
@NotBlank
#if("$field.metaInfo.jdbcType" != "LONGVARCHAR")
@Size(max = $field.metaInfo.length)
#end
#else
@NotNull
#end
#if("$!field.comment" != "")
@Schema(description = "${field.comment}")
#end
#if("$field.propertyType" == "Date")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
#end
private ${field.propertyType} ${field.propertyName};
#end
}

View File

@@ -0,0 +1,45 @@
package ${currentPackage};
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.io.Serializable;
import java.util.*;
import java.math.*;
/**
* $!{table.comment} 视图响应对象
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "${type}VO", description = "$!{table.comment} 视图响应对象")
public class ${type}VO implements Serializable {
private static final long serialVersionUID = 1L;
#foreach($field in ${table.fields})
#if("$!field.comment" != "")
@Schema(description = "${field.comment}")
#end
private ${field.propertyType} ${field.propertyName};
#end
@Schema(description = "创建时间")
private Date createTime;
@Schema(description = "修改时间")
private Date updateTime;
@Schema(description = "创建人")
private String creator;
@Schema(description = "修改人")
private String updater;
}

View File

@@ -0,0 +1,35 @@
package ${package.Mapper};
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import ${superMapperClassPackage};
import ${package.Entity}.${entity};
#if(${mapperAnnotationClass})
import ${mapperAnnotationClass.name};
#end
/**
* $!{table.comment} Mapper 接口
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
#if(${mapperAnnotationClass})
@${mapperAnnotationClass.simpleName}
#end
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {
/**
* 获取查询条件
*
* @param entity entity
* @return 查询条件
*/
default LambdaQueryWrapper<${entity}> queryCondition(${entity} entity) {
return this.wrapper()
#foreach($field in ${table.fields})
.eq(${type}DO::get${field.capitalName}, entity.get${field.capitalName}())#if(!$foreach.hasNext);#end
#end
}
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${package.Mapper}.${table.mapperName}">
#if(${enableCache})
<!-- 开启二级缓存 -->
<cache type="${cacheClassName}"/>
#end
#if(${baseResultMap})
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="${package.Entity}.${entity}">
#foreach($field in ${table.fields})
#if(${field.keyFlag})##生成主键排在第一位
<id column="${field.name}" property="${field.propertyName}"/>
#end
#end
#foreach($field in ${table.fields})
#if(!${field.keyFlag})##生成普通字段
<result column="${field.name}" property="${field.propertyName}"/>
#end
#end
#foreach($field in ${table.commonFields})##生成公共字段
<result column="${field.name}" property="${field.propertyName}"/>
#end
</resultMap>
#end
#if(${baseColumnList})
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
${table.fieldNames}, #foreach($field in ${table.commonFields})${field.columnName}#if($foreach.hasNext), #end#end
</sql>
#end
</mapper>

View File

@@ -0,0 +1,37 @@
package ${currentPackage};
import com.orion.ops.framework.biz.operator.log.core.annotation.Module;
import com.orion.ops.framework.biz.operator.log.core.factory.InitializingOperatorTypes;
import com.orion.ops.framework.biz.operator.log.core.model.OperatorType;
import static com.orion.ops.framework.biz.operator.log.core.enums.OperatorRiskLevel.*;
/**
* $!{table.comment} 操作日志类型
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Module("${package.ModuleName}:${typeHyphen}")
public class ${type}OperatorType extends InitializingOperatorTypes {
public static final String CREATE = "${typeHyphen}:create";
public static final String UPDATE = "${typeHyphen}:update";
public static final String DELETE = "${typeHyphen}:delete";
public static final String EXPORT = "${typeHyphen}:export";
@Override
public OperatorType[] types() {
return new OperatorType[]{
new OperatorType(L, CREATE, "创建$!{table.comment}"),
new OperatorType(M, UPDATE, "更新$!{table.comment}"),
new OperatorType(H, DELETE, "删除$!{table.comment}"),
new OperatorType(M, EXPORT, "导出$!{table.comment}"),
};
}
}

View File

@@ -0,0 +1,278 @@
package ${package.ServiceImpl};
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.collect.Lists;
import com.orion.office.excel.writer.exporting.ExcelExport;
#if($cacheMeta.enableCache)
import com.orion.ops.framework.common.constant.Const;
#end
import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.utils.FileNames;
import com.orion.ops.framework.common.utils.Valid;
#if($cacheMeta.enableCache)
import com.orion.ops.framework.redis.core.utils.RedisMaps;
#end
#foreach($pkg in ${customModuleFilePackages})
import ${pkg}.*;
#end
import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${package.Service}.${table.serviceName};
import com.orion.web.servlet.web.Servlets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
/**
* $!{table.comment} 服务实现类
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Slf4j
@Service
public class ${table.serviceImplName} implements ${table.serviceName} {
@Resource
private ${type}DAO ${typeLower}DAO;
@Override
public Long create${type}(${type}CreateRequest request) {
log.info("${type}Service-create${type} request: {}", JSON.toJSONString(request));
// 转换
${type}DO record = ${type}Convert.MAPPER.to(request);
// 查询数据是否冲突
this.check${type}Present(record);
// 插入
int effect = ${typeLower}DAO.insert(record);
Long id = record.getId();
log.info("${type}Service-create${type} id: {}, effect: {}", id, effect);
#if($cacheMeta.enableCache)
// 删除缓存
RedisMaps.delete(${type}CacheKeyDefine.${typeConst});
#end
return id;
}
@Override
public Integer update${type}ById(${type}UpdateRequest request) {
Long id = Valid.notNull(request.getId(), ErrorMessage.ID_MISSING);
log.info("${type}Service-update${type}ById id: {}, request: {}", id, JSON.toJSONString(request));
// 查询
${type}DO record = ${typeLower}DAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 转换
${type}DO updateRecord = ${type}Convert.MAPPER.to(request);
// 查询数据是否冲突
this.check${type}Present(updateRecord);
// 更新
int effect = ${typeLower}DAO.updateById(updateRecord);
log.info("${type}Service-update${type}ById effect: {}", effect);
#if($cacheMeta.enableCache)
// 删除缓存
RedisMaps.delete(${type}CacheKeyDefine.${typeConst});
#end
return effect;
}
@Override
public Integer update${type}(${type}QueryRequest query, ${type}UpdateRequest update) {
log.info("${type}Service.update${type} query: {}, update: {}", JSON.toJSONString(query), JSON.toJSONString(update));
// 条件
LambdaQueryWrapper<${type}DO> wrapper = this.buildQueryWrapper(query);
// 转换
${type}DO updateRecord = ${type}Convert.MAPPER.to(update);
// 更新
int effect = ${typeLower}DAO.update(updateRecord, wrapper);
log.info("${type}Service.update${type} effect: {}", effect);
#if($cacheMeta.enableCache)
// 删除缓存
RedisMaps.delete(${type}CacheKeyDefine.${typeConst});
#end
return effect;
}
@Override
public ${type}VO get${type}ById(Long id) {
// 查询
${type}DO record = ${typeLower}DAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 转换
return ${type}Convert.MAPPER.to(record);
}
@Override
public List<${type}VO> get${type}ByIdList(List<Long> idList) {
// 查询
List<${type}DO> records = ${typeLower}DAO.selectBatchIds(idList);
if (records.isEmpty()) {
return Lists.empty();
}
// 转换
return ${type}Convert.MAPPER.to(records);
}
@Override
public List<${type}VO> get${type}List(${type}QueryRequest request) {
// 条件
LambdaQueryWrapper<${type}DO> wrapper = this.buildQueryWrapper(request);
// 查询
return ${typeLower}DAO.of(wrapper).list(${type}Convert.MAPPER::to);
}
#if($cacheMeta.enableCache)
@Override
public List<${type}VO> get${type}ListByCache() {
// 查询缓存
List<${type}CacheDTO> list = RedisMaps.valuesJson(${type}CacheKeyDefine.${typeConst});
if (list.isEmpty()) {
// 查询数据库
list = ${typeLower}DAO.of().list(${type}Convert.MAPPER::toCache);
// 添加默认值 防止穿透
if (list.isEmpty()) {
list.add(${type}CacheDTO.builder()
.id(Const.NONE_ID)
.build());
}
// 设置缓存
RedisMaps.putAllJson(${type}CacheKeyDefine.${typeConst}.getKey(), s -> s.getId().toString(), list);
RedisMaps.setExpire(${type}CacheKeyDefine.${typeConst});
}
// 删除默认值
return list.stream()
.filter(s -> !s.getId().equals(Const.NONE_ID))
.map(${type}Convert.MAPPER::to)
.collect(Collectors.toList());
}
#end
@Override
public Long get${type}Count(${type}QueryRequest request) {
// 条件
LambdaQueryWrapper<${type}DO> wrapper = this.buildQueryWrapper(request);
// 查询
return ${typeLower}DAO.selectCount(wrapper);
}
@Override
public DataGrid<${type}VO> get${type}Page(${type}QueryRequest request) {
// 条件
LambdaQueryWrapper<${type}DO> wrapper = this.buildQueryWrapper(request);
// 查询
return ${typeLower}DAO.of(wrapper)
.page(request)
.dataGrid(${type}Convert.MAPPER::to);
}
@Override
public Integer delete${type}ById(Long id) {
log.info("${type}Service-delete${type}ById id: {}", id);
// 检查数据是否存在
${type}DO record = ${typeLower}DAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 删除
int effect = ${typeLower}DAO.deleteById(id);
log.info("${type}Service-delete${type}ById id: {}, effect: {}", id, effect);
#if($cacheMeta.enableCache)
// 删除缓存
RedisMaps.delete(${type}CacheKeyDefine.${typeConst}, id);
#end
return effect;
}
@Override
public Integer delete${type}ByIdList(List<Long> idList) {
log.info("${type}Service-delete${type}ByIdList idList: {}", idList);
int effect = ${typeLower}DAO.deleteBatchIds(idList);
log.info("${type}Service-delete${type}ByIdList effect: {}", effect);
#if($cacheMeta.enableCache)
// 删除缓存
RedisMaps.delete(${type}CacheKeyDefine.${typeConst}, idList);
#end
return effect;
}
@Override
public Integer delete${type}(${type}QueryRequest request) {
log.info("${type}Service.delete${type} request: {}", JSON.toJSONString(request));
// 条件
LambdaQueryWrapper<${type}DO> wrapper = this.buildQueryWrapper(request);
// 删除
int effect = ${typeLower}DAO.delete(wrapper);
log.info("${type}Service.delete${type} effect: {}", effect);
#if($cacheMeta.enableCache)
// 删除缓存
RedisMaps.delete(${type}CacheKeyDefine.${typeConst});
#end
return effect;
}
@Override
public void export${type}(${type}QueryRequest request, HttpServletResponse response) throws IOException {
log.info("${type}Service.export${type} request: {}", JSON.toJSONString(request));
// 条件
LambdaQueryWrapper<${type}DO> wrapper = this.buildQueryWrapper(request);
// 查询
List<${type}Export> rows = ${typeLower}DAO.of(wrapper).list(${type}Convert.MAPPER::toExport);
log.info("${type}Service.export${type} size: {}", rows.size());
// 导出
ByteArrayOutputStream out = new ByteArrayOutputStream();
ExcelExport.create(${type}Export.class)
.addRows(rows)
.write(out)
.close();
// 传输
Servlets.transfer(response, out.toByteArray(), FileNames.exportName(${type}Export.TITLE));
}
/**
* 检查对象是否存在
*
* @param domain domain
*/
private void check${type}Present(${type}DO domain) {
// 构造条件
LambdaQueryWrapper<${type}DO> wrapper = ${typeLower}DAO.wrapper()
// 更新时忽略当前记录
.ne(${type}DO::getId, domain.getId())
// 用其他字段做重复校验
#foreach($field in ${table.fields})
#if("$!field.propertyName" != "id")
.eq(${type}DO::get${field.capitalName}, domain.get${field.capitalName}())#if(!$foreach.hasNext);#end
#end
#end
// 检查是否存在
boolean present = ${typeLower}DAO.of(wrapper).present();
Valid.isFalse(present, ErrorMessage.DATA_PRESENT);
}
/**
* 构建查询 wrapper
*
* @param request request
* @return wrapper
*/
private LambdaQueryWrapper<${type}DO> buildQueryWrapper(${type}QueryRequest request) {
String searchValue = request.getSearchValue();
return ${typeLower}DAO.wrapper()
#foreach($field in ${table.fields})
.eq(${type}DO::get${field.capitalName}, request.get${field.capitalName}())
#end
.and(Strings.isNotEmpty(searchValue), c -> c
#foreach($field in ${table.fields})
.eq(${type}DO::get${field.capitalName}, searchValue)#if($foreach.hasNext).or()#end
#end
);
}
}

View File

@@ -0,0 +1,128 @@
package ${package.Service};
import com.orion.lang.define.wrapper.DataGrid;
#foreach($pkg in ${customModuleFilePackages})
import ${pkg}.*;
#end
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* $!{table.comment} 服务类
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
public interface ${table.serviceName} {
/**
* ${apiComment.create}
*
* @param request request
* @return id
*/
Long create${type}(${type}CreateRequest request);
/**
* ${apiComment.updateById}
*
* @param request request
* @return effect
*/
Integer update${type}ById(${type}UpdateRequest request);
/**
* ${apiComment.updateAll}
*
* @param query query
* @param update update
* @return effect
*/
Integer update${type}(${type}QueryRequest query, ${type}UpdateRequest update);
/**
* ${apiComment.getById}
*
* @param id id
* @return row
*/
${type}VO get${type}ById(Long id);
/**
* ${apiComment.getByIdList}
*
* @param idList idList
* @return rows
*/
List<${type}VO> get${type}ByIdList(List<Long> idList);
/**
* ${apiComment.queryList}
*
* @param request request
* @return rows
*/
List<${type}VO> get${type}List(${type}QueryRequest request);
#if($cacheMeta.enableCache)
/**
* ${apiComment.queryListByCache}
*
* @return rows
*/
List<${type}VO> get${type}ListByCache();
#end
/**
* ${apiComment.queryCount}
*
* @param request request
* @return count
*/
Long get${type}Count(${type}QueryRequest request);
/**
* ${apiComment.queryPage}
*
* @param request request
* @return rows
*/
DataGrid<${type}VO> get${type}Page(${type}QueryRequest request);
/**
* ${apiComment.deleteById}
*
* @param id id
* @return effect
*/
Integer delete${type}ById(Long id);
/**
* ${apiComment.batchDelete}
*
* @param idList idList
* @return effect
*/
Integer delete${type}ByIdList(List<Long> idList);
/**
* ${apiComment.deleteAll}
*
* @param request request
* @return effect
*/
Integer delete${type}(${type}QueryRequest request);
/**
* ${apiComment.export}
*
* @param request request
* @param response response
* @throws IOException IOException
*/
void export${type}(${type}QueryRequest request, HttpServletResponse response) throws IOException;
}

View File

@@ -0,0 +1,172 @@
package ${currentPackage};
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.utils.collect.Lists;
import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.utils.Valid;
#foreach($pkg in ${customModuleFilePackages})
import ${pkg}.*;
#end
#foreach($pkg in ${customProviderFilePackages})
import ${pkg}.*;
#end
import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${package.Service}.${table.serviceName};
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* $!{table.comment} 对外服务实现类
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Slf4j
@Service
public class ${type}ApiImpl implements ${type}Api {
@Resource
private ${type}Service ${typeLower}Service;
@Resource
private ${type}DAO ${typeLower}DAO;
@Override
public Long create${type}(${type}CreateDTO dto) {
log.info("${type}Api.create${type} dto: {}", JSON.toJSONString(dto));
Valid.valid(dto);
// 转换
${type}CreateRequest request = ${type}ProviderConvert.MAPPER.toRequest(dto);
// 创建
return ${typeLower}Service.create${type}(request);
}
@Override
public Integer update${type}ById(${type}UpdateDTO dto) {
log.info("${type}Api.update${type}ById dto: {}", JSON.toJSONString(dto));
Valid.valid(dto);
// 转换
${type}UpdateRequest request = ${type}ProviderConvert.MAPPER.toRequest(dto);
// 修改
return ${typeLower}Service.update${type}ById(request);
}
@Override
public Integer update${type}(${type}QueryDTO query, ${type}UpdateDTO update) {
log.info("${type}Api.update${type} query: {}, update: {}", JSON.toJSONString(query), JSON.toJSONString(update));
Valid.valid(query);
Valid.valid(update);
// 更新
int effect = ${typeLower}Service.update${type}(${type}ProviderConvert.MAPPER.toRequest(query),
${type}ProviderConvert.MAPPER.toRequest(update));
log.info("${type}Api.update${type} effect: {}", effect);
return effect;
}
@Override
public ${type}DTO get${type}ById(Long id) {
log.info("${type}Api.get${type}ById id: {}", id);
Valid.notNull(id, ErrorMessage.ID_MISSING);
// 修改
${type}DO record = ${typeLower}DAO.selectById(id);
if (record == null) {
return null;
}
// 转换
return ${type}ProviderConvert.MAPPER.to(record);
}
@Override
public List<${type}DTO> get${type}ByIdList(List<Long> idList) {
log.info("${type}Api.get${type}ByIdList idList: {}", idList);
if (Lists.isEmpty(idList)) {
return new ArrayList<>();
}
// 查询
List<${type}DO> rows = ${typeLower}DAO.selectBatchIds(idList);
// 转换
return ${type}ProviderConvert.MAPPER.toList(rows);
}
@Override
public List<${type}DTO> get${type}List(${type}QueryDTO dto) {
log.info("${type}Api.get${type}List dto: {}", JSON.toJSONString(dto));
Valid.valid(dto);
// 条件
LambdaQueryWrapper<${type}DO> wrapper = this.buildQueryWrapper(dto);
// 查询
return ${typeLower}DAO.of(wrapper).list(${type}ProviderConvert.MAPPER::to);
}
#if($cacheMeta.enableCache)
@Override
public List<${type}DTO> get${type}ListByCache() {
return ${typeLower}Service.get${type}ListByCache()
.stream()
.map(${type}ProviderConvert.MAPPER::to)
.collect(Collectors.toList());
}
#end
@Override
public Long get${type}Count(${type}QueryDTO dto) {
log.info("${type}Api.get${type}Count dto: {}", JSON.toJSONString(dto));
Valid.valid(dto);
// 条件
LambdaQueryWrapper<${type}DO> wrapper = this.buildQueryWrapper(dto);
// 查询
return ${typeLower}DAO.selectCount(wrapper);
}
@Override
public Integer delete${type}ById(Long id) {
log.info("${type}Api.delete${type}ById id: {}", id);
Valid.notNull(id, ErrorMessage.ID_MISSING);
// 删除
Integer effect = ${typeLower}Service.delete${type}ById(id);
log.info("${type}Api.delete${type}ById id: {}, effect: {}", id, effect);
return effect;
}
@Override
public Integer delete${type}ByIdList(List<Long> idList) {
log.info("${type}Api.delete${type}ByIdList idList: {}", idList);
Valid.notEmpty(idList, ErrorMessage.ID_MISSING);
// 删除
Integer effect = ${typeLower}Service.delete${type}ByIdList(idList);
log.info("${type}Api.delete${type}ByIdList effect: {}", effect);
return effect;
}
@Override
public Integer delete${type}(${type}QueryDTO dto) {
log.info("${type}Api.delete${type} dto: {}", JSON.toJSONString(dto));
Valid.valid(dto);
// 删除
Integer effect = ${typeLower}Service.delete${type}(${type}ProviderConvert.MAPPER.toRequest(dto));
log.info("${type}Api.delete${type} effect: {}", effect);
return effect;
}
/**
* 构建查询 wrapper
*
* @param dto dto
* @return wrapper
*/
private LambdaQueryWrapper<${type}DO> buildQueryWrapper(${type}QueryDTO dto) {
return ${typeLower}DAO.wrapper()
#foreach($field in ${table.fields})
.eq(${type}DO::get${field.capitalName}, dto.get${field.capitalName}())#if(!$foreach.hasNext);#end
#end
}
}

View File

@@ -0,0 +1,108 @@
package ${currentPackage};
#foreach($pkg in ${customProviderEntityFilePackages})
import ${pkg}.*;
#end
import java.util.List;
/**
* $!{table.comment} 对外服务类
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
public interface ${type}Api {
/**
* ${apiComment.create}
*
* @param dto dto
* @return id
*/
Long create${type}(${type}CreateDTO dto);
/**
* ${apiComment.updateById}
*
* @param dto dto
* @return effect
*/
Integer update${type}ById(${type}UpdateDTO dto);
/**
* ${apiComment.updateAll}
*
* @param query query
* @param update update
* @return effect
*/
Integer update${type}(${type}QueryDTO query, ${type}UpdateDTO update);
/**
* ${apiComment.getById}
*
* @param id id
* @return row
*/
${type}DTO get${type}ById(Long id);
/**
* ${apiComment.getByIdList}
*
* @param idList idList
* @return rows
*/
List<${type}DTO> get${type}ByIdList(List<Long> idList);
/**
* ${apiComment.queryList}
*
* @param dto dto
* @return rows
*/
List<${type}DTO> get${type}List(${type}QueryDTO dto);
#if($cacheMeta.enableCache)
/**
* ${apiComment.queryListByCache}
*
* @return rows
*/
List<${type}DTO> get${type}ListByCache();
#end
/**
* ${apiComment.queryCount}
*
* @param dto dto
* @return count
*/
Long get${type}Count(${type}QueryDTO dto);
/**
* ${apiComment.deleteById}
*
* @param id id
* @return effect
*/
Integer delete${type}ById(Long id);
/**
* ${apiComment.batchDelete}
*
* @param idList idList
* @return effect
*/
Integer delete${type}ByIdList(List<Long> idList);
/**
* ${apiComment.deleteAll}
*
* @param dto dto
* @return effect
*/
Integer delete${type}(${type}QueryDTO dto);
}

View File

@@ -0,0 +1,45 @@
package ${currentPackage};
import ${package.Entity}.*;
#foreach($pkg in ${customModuleFilePackages})
import ${pkg}.*;
#end
#foreach($pkg in ${customProviderEntityFilePackages})
import ${pkg}.*;
#end
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* $!{table.comment} 对外服务对象转换器
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Mapper
public interface ${type}ProviderConvert {
${type}ProviderConvert MAPPER = Mappers.getMapper(${type}ProviderConvert.class);
${type}DTO to(${type}VO dto);
${type}DO to(${type}DTO dto);
${type}DTO to(${type}DO domain);
${type}DO to(${type}QueryDTO domain);
${type}DO to(${type}UpdateDTO update);
${type}QueryRequest toRequest(${type}QueryDTO request);
${type}CreateRequest toRequest(${type}CreateDTO request);
${type}UpdateRequest toRequest(${type}UpdateDTO request);
List<${type}DTO> toList(List<${type}DO> list);
}

View File

@@ -0,0 +1,47 @@
package ${currentPackage};
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.*;
import java.math.*;
/**
* $!{table.comment} 创建请求业务对象
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "${type}CreateDTO", description = "$!{table.comment} 创建请求业务对象")
public class ${type}CreateDTO implements Serializable {
#foreach($field in ${table.fields})
#if("$!field.propertyName" != "id")
#if("$field.propertyType" == "String")
@NotBlank
#if("$field.metaInfo.jdbcType" != "LONGVARCHAR")
@Size(max = $field.metaInfo.length)
#end
#else
@NotNull
#end
#if("$!field.comment" != "")
@Schema(description = "${field.comment}")
#end
private ${field.propertyType} ${field.propertyName};
#end
#end
}

View File

@@ -0,0 +1,38 @@
package ${currentPackage};
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.*;
import java.math.*;
/**
* $!{table.comment} 查询请求业务对象
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "${type}QueryDTO", description = "$!{table.comment} 查询请求业务对象")
public class ${type}QueryDTO implements Serializable {
#foreach($field in ${table.fields})
#if("$field.propertyType" == "String" && "$field.metaInfo.jdbcType" != "LONGVARCHAR")
@Size(max = $field.metaInfo.length)
#end
#if("$!field.comment" != "")
@Schema(description = "${field.comment}")
#end
private ${field.propertyType} ${field.propertyName};
#end
}

View File

@@ -0,0 +1,45 @@
package ${currentPackage};
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.*;
import java.math.*;
/**
* $!{table.comment} 更新请求业务对象
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "${type}UpdateDTO", description = "$!{table.comment} 更新请求业务对象")
public class ${type}UpdateDTO implements Serializable {
#foreach($field in ${table.fields})
#if("$field.propertyType" == "String")
@NotBlank
#if("$field.metaInfo.jdbcType" != "LONGVARCHAR")
@Size(max = $field.metaInfo.length)
#end
#else
@NotNull
#end
#if("$!field.comment" != "")
@Schema(description = "${field.comment}")
#end
private ${field.propertyType} ${field.propertyName};
#end
}

View File

@@ -0,0 +1,45 @@
package ${currentPackage};
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import lombok.*;
import java.util.*;
import java.math.*;
/**
* $!{table.comment} 业务对象
*
* @author ${author}
* @version ${since}
* @since ${date}
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "${type}DTO", description = "$!{table.comment} 业务对象")
public class ${type}DTO implements Serializable {
private static final long serialVersionUID = 1L;
#foreach($field in ${table.fields})
#if("$!field.comment" != "")
@Schema(description = "${field.comment}")
#end
private ${field.propertyType} ${field.propertyName};
#end
@Schema(description = "创建时间")
private Date createTime;
@Schema(description = "修改时间")
private Date updateTime;
@Schema(description = "创建人")
private String creator;
@Schema(description = "修改人")
private String updater;
}

View File

@@ -0,0 +1,126 @@
package ${currentPackage};
import com.orion.lang.utils.collect.Lists;
import com.orion.ops.framework.test.core.base.BaseUnitTest;
import com.orion.ops.framework.test.core.utils.EntityRandoms;
import ${package.ServiceImpl}.*;
#foreach($pkg in ${customProviderFilePackages})
import ${pkg}.*;
#end
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* $!{table.comment} 对外服务单元测试
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/8/23 10:36
*/
@Slf4j
@Import({${type}ApiImpl.class, ${type}ServiceImpl.class})
public class ${type}ApiImplTests extends BaseUnitTest {
@Resource
private ${type}Api ${typeLower}Api;
private static Long lastId;
@Test
@Order(1)
public void create${type}Test() {
${type}CreateDTO req = EntityRandoms.random(${type}CreateDTO.class);
lastId = ${typeLower}Api.create${type}(req);
assertNotNull(lastId);
}
@Test
@Order(2)
public void update${type}ByIdTest() {
${type}UpdateDTO req = EntityRandoms.random(${type}UpdateDTO.class);
req.setId(lastId);
Integer effect = ${typeLower}Api.update${type}ById(req);
assertEquals(effect, 1);
}
@Test
@Order(3)
public void update${type}Test() {
${type}QueryDTO query = new ${type}QueryDTO();
query.setId(lastId);
${type}UpdateDTO req = EntityRandoms.random(${type}UpdateDTO.class);
req.setId(null);
Integer effect = ${typeLower}Api.update${type}(query, req);
assertEquals(effect, 1);
}
@Test
@Order(4)
public void get${type}ByIdTest() {
${type}DTO row = ${typeLower}Api.get${type}ById(lastId);
assertNotNull(row);
}
@Test
@Order(5)
public void get${type}ByIdListTest() {
List<${type}DTO> rows = ${typeLower}Api.get${type}ByIdList(Lists.of(lastId));
assertFalse(rows.isEmpty());
}
@Test
@Order(6)
public void get${type}ListTest() {
List<${type}DTO> rows = ${typeLower}Api.get${type}List(new ${type}QueryDTO());
assertFalse(rows.isEmpty());
}
@Test
@Order(7)
public void get${type}CountTest() {
Long count = ${typeLower}Api.get${type}Count(new ${type}QueryDTO());
assertEquals(count, 1L);
}
@Test
@Order(8)
public void delete${type}ByIdTest() {
Integer effect = ${typeLower}Api.delete${type}ById(lastId);
assertEquals(effect, 1);
}
@Test
@Order(9)
public void delete${type}ByIdListTest() {
Integer effect = ${typeLower}Api.delete${type}ByIdList(Lists.of(lastId));
assertEquals(effect, 0);
}
@Test
@Order(10)
public void delete${type}Test() {
${type}QueryDTO dto = new ${type}QueryDTO();
Integer effect = ${typeLower}Api.delete${type}(dto);
assertEquals(effect, 0);
}
#if($cacheMeta.enableCache)
// -------------------- cache --------------------
@Test
@Order(11)
public void get${type}ListByCacheTest() {
this.create${type}Test();
List<${type}DTO> list = ${typeLower}Api.get${type}ListByCache();
assertEquals(list.size(), 1);
}
#end
}

View File

@@ -0,0 +1,21 @@
-- $!{table.comment}
DROP TABLE IF EXISTS `${table.name}`;
CREATE TABLE `${table.name}`
(
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'id',
#foreach($field in ${table.fields})
#if("$!field.propertyName" != "id")
#if("$field.metaInfo.jdbcType" == "TINYINT" || "$field.metaInfo.jdbcType" == "SMALLINT" || "$field.metaInfo.jdbcType" == "INTEGER" || "$field.metaInfo.jdbcType" == "BIGINT" || "$field.metaInfo.jdbcType" == "FLOAT" || "$field.metaInfo.jdbcType" == "DOUBLE" || "$field.metaInfo.jdbcType" == "NUMERIC" || "$field.metaInfo.jdbcType" == "LONGVARCHAR" || "$field.metaInfo.jdbcType" == "DATE" || "$field.metaInfo.jdbcType" == "TIME" || "$field.metaInfo.jdbcType" == "TIMESTAMP")
`${field.columnName}` ${field.metaInfo.jdbcType}#if(!$field.metaInfo.nullable) NOT NULL#end#if($null.isNull($field.metaInfo.defaultValue) || "$!{field.metaInfo.defaultValue}" == "") DEFAULT NULL#else DEFAULT#if(${field.propertyType} == 'String') '${field.metaInfo.defaultValue}'#else ${field.metaInfo.defaultValue}#end#end COMMENT '$!{field.metaInfo.remarks}',
#else
`${field.columnName}` ${field.metaInfo.jdbcType}(${field.metaInfo.length})#if(!$field.metaInfo.nullable) NOT NULL#end#if($null.isNull($field.metaInfo.defaultValue) || "$!{field.metaInfo.defaultValue}" == "") DEFAULT NULL#else DEFAULT#if(${field.propertyType} == 'String') '${field.metaInfo.defaultValue}'#else ${field.metaInfo.defaultValue}#end#end COMMENT '$!{field.metaInfo.remarks}',
#end
#end
#end
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`creator` VARCHAR(64) DEFAULT NULL COMMENT '创建人',
`updater` VARCHAR(64) DEFAULT NULL COMMENT '更新人',
`deleted` TINYINT DEFAULT '0' COMMENT '是否删除 0未删除 1已删除',
PRIMARY KEY (`id`)
);

View File

@@ -0,0 +1,137 @@
package ${currentPackage};
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.utils.collect.Lists;
import com.orion.ops.framework.test.core.base.BaseUnitTest;
import com.orion.ops.framework.test.core.utils.EntityRandoms;
import ${package.Service}.*;
#foreach($pkg in ${customModuleFilePackages})
import ${pkg}.*;
#end
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* $!{table.comment} 服务单元测试
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/8/23 10:36
*/
@Slf4j
@Import(${type}ServiceImpl.class)
public class ${type}ServiceImplTests extends BaseUnitTest {
@Resource
private ${type}Service ${typeLower}Service;
private static Long lastId;
@Test
@Order(1)
public void create${type}Test() {
${type}CreateRequest req = EntityRandoms.random(${type}CreateRequest.class);
lastId = ${typeLower}Service.create${type}(req);
assertNotNull(lastId);
}
@Test
@Order(2)
public void update${type}ByIdTest() {
${type}UpdateRequest req = EntityRandoms.random(${type}UpdateRequest.class);
req.setId(lastId);
Integer effect = ${typeLower}Service.update${type}ById(req);
assertEquals(effect, 1);
}
@Test
@Order(3)
public void update${type}Test() {
${type}QueryRequest query = new ${type}QueryRequest();
query.setId(lastId);
${type}UpdateRequest req = EntityRandoms.random(${type}UpdateRequest.class);
req.setId(null);
Integer effect = ${typeLower}Service.update${type}(query, req);
assertEquals(effect, 1);
}
@Test
@Order(4)
public void get${type}ByIdTest() {
${type}VO row = ${typeLower}Service.get${type}ById(lastId);
assertNotNull(row);
}
@Test
@Order(5)
public void get${type}ByIdListTest() {
List<${type}VO> rows = ${typeLower}Service.get${type}ByIdList(Lists.of(lastId));
assertFalse(rows.isEmpty());
}
@Test
@Order(6)
public void get${type}ListTest() {
List<${type}VO> rows = ${typeLower}Service.get${type}List(new ${type}QueryRequest());
assertFalse(rows.isEmpty());
}
@Test
@Order(7)
public void get${type}CountTest() {
Long count = ${typeLower}Service.get${type}Count(new ${type}QueryRequest());
assertEquals(count, 1L);
}
@Test
@Order(8)
public void get${type}PageTest() {
${type}QueryRequest request = new ${type}QueryRequest();
request.setPage(1);
request.setLimit(1);
DataGrid<${type}VO> dataGrid = ${typeLower}Service.get${type}Page(request);
assertFalse(dataGrid.isEmpty());
}
@Test
@Order(9)
public void delete${type}ByIdTest() {
Integer effect = ${typeLower}Service.delete${type}ById(lastId);
assertEquals(effect, 1);
}
@Test
@Order(10)
public void delete${type}ByIdListTest() {
Integer effect = ${typeLower}Service.delete${type}ByIdList(Lists.of(lastId));
assertEquals(effect, 0);
}
@Test
@Order(11)
public void delete${type}Test() {
${type}QueryRequest query = new ${type}QueryRequest();
Integer effect = ${typeLower}Service.delete${type}(query);
assertEquals(effect, 0);
}
#if($cacheMeta.enableCache)
// -------------------- cache --------------------
@Test
@Order(12)
public void get${type}ListByCacheTest() {
this.create${type}Test();
List<${type}VO> list = ${typeLower}Service.get${type}ListByCache();
assertEquals(list.size(), 1);
}
#end
}

View File

@@ -0,0 +1,30 @@
-- 执行完成后 需要在菜单页面刷新缓存
-- 父菜单
INSERT INTO system_menu
(parent_id, name, type, sort, visible, status, cache, component)
VALUES
(0, '${table.comment}管理', 1, 10, 1, 1, 1, '$vue.moduleEntityFirstLower');
-- 设置临时父菜单id
SELECT @TMP_PARENT_ID:=LAST_INSERT_ID();
-- 子菜单
INSERT INTO system_menu
(parent_id, name, type, sort, visible, status, cache, component)
VALUES
(@TMP_PARENT_ID, '$table.comment', 2, 10, 1, 1, 1, '$vue.moduleEntityFirstLower$vue.featureEntity');
-- 设置临时子菜单id
SELECT @TMP_SUB_ID:=LAST_INSERT_ID();
-- 功能
INSERT INTO system_menu
(parent_id, name, permission, type, sort)
VALUES
(@TMP_SUB_ID, '查询$table.comment', '${package.ModuleName}:${typeHyphen}:query', 3, 10),
(@TMP_SUB_ID, '创建$table.comment', '${package.ModuleName}:${typeHyphen}:create', 3, 20),
(@TMP_SUB_ID, '修改$table.comment', '${package.ModuleName}:${typeHyphen}:update', 3, 30),
(@TMP_SUB_ID, '删除$table.comment', '${package.ModuleName}:${typeHyphen}:delete', 3, 40),
(@TMP_SUB_ID, '导出$table.comment', '${package.ModuleName}:${typeHyphen}:export', 3, 50),
(@TMP_SUB_ID, '导入$table.comment', '${package.ModuleName}:${typeHyphen}:import', 3, 60);

View File

@@ -0,0 +1,142 @@
import type { DataGrid, Pagination } from '@/types/global';
import type { TableData } from '@arco-design/web-vue/es/table/interface';
import axios from 'axios';
import qs from 'query-string';
/**
* ${table.comment}创建请求
*/
export interface ${vue.featureEntity}CreateRequest {
#foreach($field in ${table.fields})
#if("$!field.propertyName" != "id")
#if("$field.propertyType" == "String" || "$field.propertyType" == "Date")
${field.propertyName}?: string;
#elseif("$field.propertyType" == "Integer" || "$field.propertyType" == "Long")
${field.propertyName}?: number;
#elseif("$field.propertyType" == "Boolean")
${field.propertyName}?: boolean;
#else
${field.propertyName}?: any;
#end
#end
#end
}
/**
* ${table.comment}更新请求
*/
export interface ${vue.featureEntity}UpdateRequest extends ${vue.featureEntity}CreateRequest {
id?: number;
}
/**
* ${table.comment}查询请求
*/
export interface ${vue.featureEntity}QueryRequest extends Pagination {
searchValue?: string;
#foreach($field in ${table.fields})
#if("$field.propertyType" == "String" || "$field.propertyType" == "Date")
${field.propertyName}?: string;
#elseif("$field.propertyType" == "Integer" || "$field.propertyType" == "Long")
${field.propertyName}?: number;
#elseif("$field.propertyType" == "Boolean")
${field.propertyName}?: boolean;
#else
${field.propertyName}?: any;
#end
#end
}
/**
* ${table.comment}查询响应
*/
export interface ${vue.featureEntity}QueryResponse extends TableData {
#foreach($field in ${table.fields})
#if("$field.propertyType" == "String")
${field.propertyName}?: string;
#elseif("$field.propertyType" == "Integer" || "$field.propertyType" == "Long" || "$field.propertyType" == "Date")
${field.propertyName}?: number;
#elseif("$field.propertyType" == "Boolean")
${field.propertyName}?: boolean;
#else
${field.propertyName}?: any;
#end
#end
createTime: number;
updateTime: number;
creator: string;
updater: string;
}
/**
* $apiComment.create
*/
export function create${vue.featureEntity}(request: ${vue.featureEntity}CreateRequest) {
return axios.post('/${package.ModuleName}/${typeHyphen}/create', request);
}
/**
* $apiComment.updateById
*/
export function update${vue.featureEntity}(request: ${vue.featureEntity}UpdateRequest) {
return axios.put('/${package.ModuleName}/${typeHyphen}/update', request);
}
/**
* $apiComment.getById
*/
export function get${vue.featureEntity}(id: number) {
return axios.get<${vue.featureEntity}QueryResponse>('/${package.ModuleName}/${typeHyphen}/get', { params: { id } });
}
/**
* $apiComment.getByIdList
*/
export function batchGet${vue.featureEntity}List(idList: Array<number>) {
return axios.get<${vue.featureEntity}QueryResponse[]>('/${package.ModuleName}/${typeHyphen}/batch-get', {
params: { idList },
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: 'comma' });
}
});
}
/**
* $apiComment.queryList
*/
export function get${vue.featureEntity}List(request: ${vue.featureEntity}QueryRequest) {
return axios.post<Array<${vue.featureEntity}QueryResponse>>('/${package.ModuleName}/${typeHyphen}/list', request);
}
/**
* $apiComment.queryPage
*/
export function get${vue.featureEntity}Page(request: ${vue.featureEntity}QueryRequest) {
return axios.post<DataGrid<${vue.featureEntity}QueryResponse>>('/${package.ModuleName}/${typeHyphen}/query', request);
}
/**
* $apiComment.deleteById
*/
export function delete${vue.featureEntity}(id: number) {
return axios.delete('/${package.ModuleName}/${typeHyphen}/delete', { params: { id } });
}
/**
* $apiComment.batchDelete
*/
export function batchDelete${vue.featureEntity}(idList: Array<number>) {
return axios.delete('/${package.ModuleName}/${typeHyphen}/batch-delete', {
params: { idList },
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: 'comma' });
}
});
}
/**
* $apiComment.export
*/
export function export${vue.featureEntity}(request: ${vue.featureEntity}QueryRequest) {
return axios.post('/${package.ModuleName}/${typeHyphen}/export', request);
}

View File

@@ -0,0 +1,17 @@
import type { AppRouteRecordRaw } from '../types';
import { DEFAULT_LAYOUT } from '../base';
const $vue.moduleConst: AppRouteRecordRaw = {
name: '$vue.moduleEntityFirstLower',
path: '/$vue.module',
component: DEFAULT_LAYOUT,
children: [
{
name: '$vue.moduleEntityFirstLower$vue.featureEntity',
path: '/$vue.module/$vue.feature',
component: () => import('@/views/$vue.module/$vue.feature/index.vue'),
},
],
};
export default $vue.moduleConst;

View File

@@ -0,0 +1,217 @@
<template>
<card-list v-model:searchValue="formModel.searchValue"
search-input-placeholder="输入xxx"
create-card-position="head"
:loading="loading"
:fieldConfig="fieldConfig"
:list="list"
:pagination="pagination"
:card-layout-cols="cardColLayout"
:filter-count="filterCount"
:add-permission="['${package.ModuleName}:${typeHyphen}:create']"
@add="emits('openAdd')"
@reset="reset"
@search="fetchCardData"
@page-change="fetchCardData">
<!-- 标题 -->
<template #title="{ record }">
{{ record.id }}
</template>
<!-- 拓展操作 -->
<template #extra="{ record }">
<a-space>
<!-- 更多操作 -->
<a-dropdown trigger="hover">
<icon-more class="card-extra-icon" />
<template #content>
<!-- 修改 -->
<a-doption v-permission="['${package.ModuleName}:${typeHyphen}:update']"
@click="emits('openUpdate', record)">
<icon-edit />
修改
</a-doption>
<!-- 删除 -->
<a-doption v-permission="['${package.ModuleName}:${typeHyphen}:delete']"
class="span-red"
@click="deleteRow(record.id)">
<icon-delete />
删除
</a-doption>
</template>
</a-dropdown>
</a-space>
</template>
<!-- 右键菜单 -->
<template #contextMenu="{ record }">
<!-- 修改 -->
<a-doption v-permission="['${package.ModuleName}:${typeHyphen}:update']"
@click="emits('openUpdate', record)">
<icon-edit />
修改
</a-doption>
<!-- 删除 -->
<a-doption v-permission="['${package.ModuleName}:${typeHyphen}:delete']"
class="span-red"
@click="deleteRow(record.id)">
<icon-delete />
删除
</a-doption>
</template>
<!-- 过滤条件 -->
<template #filterContent>
<a-form :model="formModel"
class="modal-form"
size="small"
ref="formRef"
label-align="right"
:style="{ width: '320px' }"
:label-col-props="{ span: 6 }"
:wrapper-col-props="{ span: 18 }"
@keyup.enter="() => fetchCardData()">
#foreach($field in ${table.fields})
<!-- $field.comment -->
<a-form-item field="${field.propertyName}" label="${field.comment}">
#if(${vue.enums.containsKey(${field.propertyName})})
<a-select v-model="formModel.${field.propertyName}"
:options="toOptions(${vue.enums.get(${field.propertyName}).className})"
placeholder="请选择${field.comment}"
allow-clear />
#else
#if("$field.propertyType" == "Integer" || "$field.propertyType" == "Long")
<a-input-number v-model="formModel.${field.propertyName}"
placeholder="请输入${field.comment}"
allow-clear
hide-button />
#elseif("$field.propertyType" == "Date")
<a-date-picker v-model="formModel.${field.propertyName}"
style="width: 100%"
placeholder="请选择${field.comment}"
show-time
allow-clear />
#else
<a-input v-model="formModel.${field.propertyName}" placeholder="请输入${field.comment}" allow-clear />
#end
#end
</a-form-item>
#end
</a-form>
</template>
</card-list>
</template>
<script lang="ts">
export default {
name: '${vue.module}-${vue.feature}-card-list'
};
</script>
<script lang="ts" setup>
import type { ${vue.featureEntity}QueryRequest, ${vue.featureEntity}QueryResponse } from '@/api/${vue.module}/${vue.feature}';
import { usePagination, useColLayout } from '@/types/card';
import { computed, reactive, ref, onMounted } from 'vue';
import useLoading from '@/hooks/loading';
import { objectTruthKeyCount, resetObject } from '@/utils';
import fieldConfig from '../types/card.fields';
import { delete${vue.featureEntity}, get${vue.featureEntity}Page } from '@/api/${vue.module}/${vue.feature}';
import { Message, Modal } from '@arco-design/web-vue';
import {} from '../types/const';
#if($vue.enums.isEmpty())
import {} from '../types/enum.types';
#else
import { #foreach($entry in ${vue.enums.entrySet()})${entry.value.className}#if($foreach.hasNext), #end#end } from '../types/enum.types';
#end
import { toOptions, getEnumValue } from '@/utils/enum';
const emits = defineEmits(['openAdd', 'openUpdate']);
const list = ref<${vue.featureEntity}QueryResponse[]>([]);
const cardColLayout = useColLayout();
const pagination = usePagination();
const { loading, setLoading } = useLoading();
const formRef = ref();
const formModel = reactive<${vue.featureEntity}QueryRequest>({
searchValue: undefined,
#foreach($field in ${table.fields})
${field.propertyName}: undefined,
#end
});
// 条件数量
const filterCount = computed(() => {
return objectTruthKeyCount(formModel, ['searchValue']);
});
// 删除当前行
const deleteRow = (id: number) => {
Modal.confirm({
title: '删除前确认!',
titleAlign: 'start',
content: '确定要删除这条记录吗?',
okText: '删除',
onOk: async () => {
try {
setLoading(true);
// 调用删除接口
await delete${vue.featureEntity}(id);
Message.success('删除成功');
// 重新加载数据
fetchCardData();
} catch (e) {
} finally {
setLoading(false);
}
}
});
};
// 添加后回调
const addedCallback = () => {
fetchCardData();
};
// 更新后回调
const updatedCallback = () => {
fetchCardData();
};
defineExpose({
addedCallback, updatedCallback
});
// 重置条件
const reset = () => {
resetObject(formModel);
fetchCardData();
};
// 加载数据
const doFetchCardData = async (request: ${vue.featureEntity}QueryRequest) => {
try {
setLoading(true);
const { data } = await get${vue.featureEntity}Page(request);
list.value = data.rows;
pagination.total = data.total;
pagination.current = request.page;
pagination.pageSize = request.limit;
} catch (e) {
} finally {
setLoading(false);
}
};
// 切换页码
const fetchCardData = (page = 1, limit = pagination.pageSize, form = formModel) => {
doFetchCardData({ page, limit, ...form });
};
onMounted(() => {
fetchCardData();
});
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,157 @@
<template>
<a-drawer v-model:visible="visible"
:title="title"
:width="430"
:mask-closable="false"
:unmount-on-close="true"
:ok-button-props="{ disabled: loading }"
:cancel-button-props="{ disabled: loading }"
:on-before-ok="handlerOk"
@cancel="handleClose">
<a-spin :loading="loading">
<a-form :model="formModel"
ref="formRef"
label-align="right"
:style="{ width: '380px' }"
:label-col-props="{ span: 6 }"
:wrapper-col-props="{ span: 18 }"
:rules="formRules">
#foreach($field in ${table.fields})
#if("$field.propertyName" != "id")
<!-- $field.comment -->
<a-form-item field="${field.propertyName}" label="${field.comment}">
#if(${vue.enums.containsKey(${field.propertyName})})
<a-select v-model="formModel.${field.propertyName}"
:options="toOptions(${vue.enums.get(${field.propertyName}).className})"
placeholder="请选择${field.comment}"/>
#else
#if("$field.propertyType" == "Integer" || "$field.propertyType" == "Long")
<a-input-number v-model="formModel.${field.propertyName}"
placeholder="请输入${field.comment}"
hide-button />
#elseif("$field.propertyType" == "Date")
<a-date-picker v-model="formModel.${field.propertyName}"
style="width: 100%"
placeholder="请选择${field.comment}"
show-time />
#else
<a-input v-model="formModel.${field.propertyName}" placeholder="请输入${field.comment}" allow-clear/>
#end
#end
</a-form-item>
#end
#end
</a-form>
</a-spin>
</a-drawer>
</template>
<script lang="ts">
export default {
name: '${vue.module}-${vue.feature}-form-drawer'
};
</script>
<script lang="ts" setup>
import type { ${vue.featureEntity}UpdateRequest } from '@/api/${vue.module}/${vue.feature}';
import { ref } from 'vue';
import useLoading from '@/hooks/loading';
import useVisible from '@/hooks/visible';
import formRules from '../types/form.rules';
import { create${vue.featureEntity}, update${vue.featureEntity} } from '@/api/${vue.module}/${vue.feature}';
import { Message } from '@arco-design/web-vue';
import {} from '../types/const';
#if($vue.enums.isEmpty())
import {} from '../types/enum.types';
#else
import { #foreach($entry in ${vue.enums.entrySet()})${entry.value.className}#if($foreach.hasNext), #end#end } from '../types/enum.types';
#end
import { toOptions } from '@/utils/enum';
const { visible, setVisible } = useVisible();
const { loading, setLoading } = useLoading();
const title = ref<string>();
const isAddHandle = ref<boolean>(true);
const defaultForm = (): ${vue.featureEntity}UpdateRequest => {
return {
#foreach($field in ${table.fields})
${field.propertyName}: undefined,
#end
};
};
const formRef = ref<any>();
const formModel = ref<${vue.featureEntity}UpdateRequest>({});
const emits = defineEmits(['added', 'updated']);
// 打开新增
const openAdd = () => {
title.value = '添加${table.comment}';
isAddHandle.value = true;
renderForm({ ...defaultForm() });
setVisible(true);
};
// 打开修改
const openUpdate = (record: any) => {
title.value = '修改${table.comment}';
isAddHandle.value = false;
renderForm({ ...defaultForm(), ...record });
setVisible(true);
};
// 渲染表单
const renderForm = (record: any) => {
formModel.value = Object.assign({}, record);
};
defineExpose({ openAdd, openUpdate });
// 确定
const handlerOk = async () => {
setLoading(true);
try {
// 验证参数
const error = await formRef.value.validate();
if (error) {
return false;
}
if (isAddHandle.value) {
// 新增
await create${vue.featureEntity}(formModel.value);
Message.success('创建成功');
emits('added');
} else {
// 修改
await update${vue.featureEntity}(formModel.value);
Message.success('修改成功');
emits('updated');
}
// 清空
handlerClear();
} catch (e) {
return false;
} finally {
setLoading(false);
}
};
// 关闭
const handleClose = () => {
handlerClear();
};
// 清空
const handlerClear = () => {
setLoading(false);
setVisible(false);
};
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,161 @@
<template>
<a-modal v-model:visible="visible"
body-class="modal-form"
title-align="start"
:title="title"
:top="80"
:align-center="false"
:draggable="true"
:mask-closable="false"
:unmount-on-close="true"
:ok-button-props="{ disabled: loading }"
:cancel-button-props="{ disabled: loading }"
:on-before-ok="handlerOk"
@close="handleClose">
<a-spin :loading="loading">
<a-form :model="formModel"
ref="formRef"
label-align="right"
:style="{ width: '460px' }"
:label-col-props="{ span: 6 }"
:wrapper-col-props="{ span: 18 }"
:rules="formRules">
#foreach($field in ${table.fields})
#if("$field.propertyName" != "id")
<!-- $field.comment -->
<a-form-item field="${field.propertyName}" label="${field.comment}">
#if(${vue.enums.containsKey(${field.propertyName})})
<a-select v-model="formModel.${field.propertyName}"
:options="toOptions(${vue.enums.get(${field.propertyName}).className})"
placeholder="请选择${field.comment}" />
#else
#if("$field.propertyType" == "Integer" || "$field.propertyType" == "Long")
<a-input-number v-model="formModel.${field.propertyName}"
placeholder="请输入${field.comment}"
hide-button />
#elseif("$field.propertyType" == "Date")
<a-date-picker v-model="formModel.${field.propertyName}"
style="width: 100%"
placeholder="请选择${field.comment}"
show-time />
#else
<a-input v-model="formModel.${field.propertyName}" placeholder="请输入${field.comment}" allow-clear />
#end
#end
</a-form-item>
#end
#end
</a-form>
</a-spin>
</a-modal>
</template>
<script lang="ts">
export default {
name: '${vue.module}-${vue.feature}-form-modal'
};
</script>
<script lang="ts" setup>
import type { ${vue.featureEntity}UpdateRequest } from '@/api/${vue.module}/${vue.feature}';
import { ref } from 'vue';
import useLoading from '@/hooks/loading';
import useVisible from '@/hooks/visible';
import formRules from '../types/form.rules';
import { create${vue.featureEntity}, update${vue.featureEntity} } from '@/api/${vue.module}/${vue.feature}';
import { Message } from '@arco-design/web-vue';
import {} from '../types/const';
#if($vue.enums.isEmpty())
import {} from '../types/enum.types';
#else
import { #foreach($entry in ${vue.enums.entrySet()})${entry.value.className}#if($foreach.hasNext), #end#end } from '../types/enum.types';
#end
import { toOptions } from '@/utils/enum';
const { visible, setVisible } = useVisible();
const { loading, setLoading } = useLoading();
const title = ref<string>();
const isAddHandle = ref<boolean>(true);
const defaultForm = (): ${vue.featureEntity}UpdateRequest => {
return {
#foreach($field in ${table.fields})
${field.propertyName}: undefined,
#end
};
};
const formRef = ref<any>();
const formModel = ref<${vue.featureEntity}UpdateRequest>({});
const emits = defineEmits(['added', 'updated']);
// 打开新增
const openAdd = () => {
title.value = '添加${table.comment}';
isAddHandle.value = true;
renderForm({ ...defaultForm() });
setVisible(true);
};
// 打开修改
const openUpdate = (record: any) => {
title.value = '修改${table.comment}';
isAddHandle.value = false;
renderForm({ ...defaultForm(), ...record });
setVisible(true);
};
// 渲染表单
const renderForm = (record: any) => {
formModel.value = Object.assign({}, record);
};
defineExpose({ openAdd, openUpdate });
// 确定
const handlerOk = async () => {
setLoading(true);
try {
// 验证参数
const error = await formRef.value.validate();
if (error) {
return false;
}
if (isAddHandle.value) {
// 新增
await create${vue.featureEntity}(formModel.value);
Message.success('创建成功');
emits('added');
} else {
// 修改
await update${vue.featureEntity}(formModel.value);
Message.success('修改成功');
emits('updated');
}
// 清空
handlerClear();
} catch (e) {
return false;
} finally {
setLoading(false);
}
};
// 关闭
const handleClose = () => {
handlerClear();
};
// 清空
const handlerClear = () => {
setLoading(false);
setVisible(false);
};
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,256 @@
<template>
<!-- 搜索 -->
<a-card class="general-card table-search-card">
<a-query-header :model="formModel"
label-align="left"
@submit="fetchTableData"
@reset="fetchTableData"
@keyup.enter="() => fetchTableData()">
#foreach($field in ${table.fields})
<!-- $field.comment -->
<a-form-item field="${field.propertyName}" label="${field.comment}" label-col-flex="50px">
#if(${vue.enums.containsKey(${field.propertyName})})
<a-select v-model="formModel.${field.propertyName}"
:options="toOptions(${vue.enums.get(${field.propertyName}).className})"
placeholder="请选择${field.comment}"
allow-clear />
#else
#if("$field.propertyType" == "Integer" || "$field.propertyType" == "Long")
<a-input-number v-model="formModel.${field.propertyName}"
placeholder="请输入${field.comment}"
allow-clear
hide-button />
#elseif("$field.propertyType" == "Date")
<a-date-picker v-model="formModel.${field.propertyName}"
style="width: 100%"
placeholder="请选择${field.comment}"
show-time
allow-clear />
#else
<a-input v-model="formModel.${field.propertyName}" placeholder="请输入${field.comment}" allow-clear />
#end
#end
</a-form-item>
#end
</a-query-header>
</a-card>
<!-- 表格 -->
<a-card class="general-card table-card">
<template #title>
<!-- 左侧操作 -->
<div class="table-left-bar-handle">
<!-- 标题 -->
<div class="table-title">
${table.comment}列表
</div>
</div>
<!-- 右侧操作 -->
<div class="table-right-bar-handle">
<a-space>
<!-- 新增 -->
<a-button type="primary"
v-permission="['${package.ModuleName}:${typeHyphen}:create']"
@click="emits('openAdd')">
新增
<template #icon>
<icon-plus />
</template>
</a-button>
#if($vue.enableRowSelection)
<!-- 删除 -->
<a-popconfirm position="br"
type="warning"
:content="`确认删除选中的${selectedKeys.length}条记录吗?`"
@ok="deleteSelectRows">
<a-button v-permission="['${package.ModuleName}:${typeHyphen}:delete']"
type="secondary"
status="danger"
:disabled="selectedKeys.length === 0">
删除
<template #icon>
<icon-delete />
</template>
</a-button>
</a-popconfirm>
#end
</a-space>
</div>
</template>
<!-- table -->
<a-table row-key="id"
class="table-wrapper-8"
ref="tableRef"
label-align="left"
:loading="loading"
:columns="columns"
#if($vue.enableRowSelection)
v-model:selected-keys="selectedKeys"
:row-selection="rowSelection"
#end
:data="tableRenderData"
:pagination="pagination"
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size) => fetchTableData(1, size)"
:bordered="false">
#foreach($field in ${table.fields})
#if(${vue.enums.containsKey(${field.propertyName})})
<!-- $field.comment -->
<template #${field.propertyName}="{ record }">
{{ getEnumValue(record.${field.propertyName}, ${vue.enums.get(${field.propertyName}).className}) }}
</template>
#end
#end
<!-- 操作 -->
<template #handle="{ record }">
<div class="table-handle-wrapper">
<!-- 修改 -->
<a-button type="text"
size="mini"
v-permission="['${package.ModuleName}:${typeHyphen}:update']"
@click="emits('openUpdate', record)">
修改
</a-button>
<!-- 删除 -->
<a-popconfirm content="确认删除这条记录吗?"
position="left"
type="warning"
@ok="deleteRow(record)">
<a-button v-permission="['${package.ModuleName}:${typeHyphen}:delete']"
type="text"
size="mini"
status="danger">
删除
</a-button>
</a-popconfirm>
</div>
</template>
</a-table>
</a-card>
</template>
<script lang="ts">
export default {
name: '${vue.module}-${vue.feature}-table'
};
</script>
<script lang="ts" setup>
import type { ${vue.featureEntity}QueryRequest, ${vue.featureEntity}QueryResponse } from '@/api/${vue.module}/${vue.feature}';
import { reactive, ref, onMounted } from 'vue';
import { batchDelete${vue.featureEntity}, delete${vue.featureEntity}, get${vue.featureEntity}Page } from '@/api/${vue.module}/${vue.feature}';
import { Message } from '@arco-design/web-vue';
import useLoading from '@/hooks/loading';
import columns from '../types/table.columns';
#if($vue.enableRowSelection)
import { usePagination, useRowSelection } from '@/types/table';
#else
import { usePagination } from '@/types/table';
#end
import {} from '../types/const';
#if($vue.enums.isEmpty())
import {} from '../types/enum.types';
#else
import { #foreach($entry in ${vue.enums.entrySet()})${entry.value.className}#if($foreach.hasNext), #end#end } from '../types/enum.types';
#end
import { toOptions, getEnumValue } from '@/utils/enum';
const emits = defineEmits(['openAdd', 'openUpdate']);
#if($vue.enableRowSelection)
const selectedKeys = ref<number[]>([]);
#end
const tableRenderData = ref<${vue.featureEntity}QueryResponse[]>([]);
const pagination = usePagination();
#if($vue.enableRowSelection)
const rowSelection = useRowSelection();
#end
const { loading, setLoading } = useLoading();
const formModel = reactive<${vue.featureEntity}QueryRequest>({
#foreach($field in ${table.fields})
${field.propertyName}: undefined,
#end
});
#if($vue.enableRowSelection)
// 删除选中行
const deleteSelectRows = async () => {
try {
setLoading(true);
// 调用删除接口
await batchDelete${vue.featureEntity}(selectedKeys.value);
Message.success(`成功删除${selectedKeys.value.length}条数据`);
selectedKeys.value = [];
// 重新加载数据
fetchTableData();
} catch (e) {
} finally {
setLoading(false);
}
};
#end
// 删除当前行
const deleteRow = async ({ id }: {
id: number
}) => {
try {
setLoading(true);
// 调用删除接口
await delete${vue.featureEntity}(id);
Message.success('删除成功');
// 重新加载数据
fetchTableData();
} catch (e) {
} finally {
setLoading(false);
}
};
// 添加后回调
const addedCallback = () => {
fetchTableData();
};
// 更新后回调
const updatedCallback = () => {
fetchTableData();
};
defineExpose({
addedCallback, updatedCallback
});
// 加载数据
const doFetchTableData = async (request: ${vue.featureEntity}QueryRequest) => {
try {
setLoading(true);
const { data } = await get${vue.featureEntity}Page(request);
tableRenderData.value = data.rows;
pagination.total = data.total;
pagination.current = request.page;
pagination.pageSize = request.limit;
#if($vue.enableRowSelection)
selectedKeys.value = [];
#end
} catch (e) {
} finally {
setLoading(false);
}
};
// 切换页码
const fetchTableData = (page = 1, limit = pagination.pageSize, form = formModel) => {
doFetchTableData({ page, limit, ...form });
};
onMounted(() => {
fetchTableData();
});
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,109 @@
<template>
<div class="layout-container" v-if="render">
#if($vue.enableCardView)
<!-- 列表-表格 -->
<${vue.feature}-table v-if="renderTable"
ref="table"
@openAdd="() =>#if($vue.enableDrawerForm) drawer#else modal#end.openAdd()"
@openUpdate="(e) =>#if($vue.enableDrawerForm) drawer#else modal#end.openUpdate(e)" />
<!-- 列表-卡片 -->
<${vue.feature}-card-list v-else
ref="card"
@openAdd="() =>#if($vue.enableDrawerForm) drawer#else modal#end.openAdd()"
@openUpdate="(e) =>#if($vue.enableDrawerForm) drawer#else modal#end.openUpdate(e)" />
#else
<!-- 列表-表格 -->
<${vue.feature}-table ref="table"
@openAdd="() =>#if($vue.enableDrawerForm) drawer#else modal#end.openAdd()"
@openUpdate="(e) =>#if($vue.enableDrawerForm) drawer#else modal#end.openUpdate(e)" />
#end
#if($vue.enableDrawerForm)
<!-- 添加修改模态框 -->
<${vue.feature}-form-drawer ref="drawer"
@added="modalAddCallback"
@updated="modalUpdateCallback" />
#else
<!-- 添加修改模态框 -->
<${vue.feature}-form-modal ref="modal"
@added="modalAddCallback"
@updated="modalUpdateCallback" />
#end
</div>
</template>
<script lang="ts">
export default {
name: '${vue.moduleEntityFirstLower}${vue.featureEntity}'
};
</script>
<script lang="ts" setup>
#if($vue.enableCardView)
import ${vue.featureEntity}CardList from './components/${vue.feature}-card-list.vue';
#end
import ${vue.featureEntity}Table from './components/${vue.feature}-table.vue';
#if($vue.enableDrawerForm)
import ${vue.featureEntity}FormDrawer from './components/${vue.feature}-form-drawer.vue';
#else
import ${vue.featureEntity}FormModal from './components/${vue.feature}-form-modal.vue';
#end
#if($vue.enableCardView)
import { computed, ref, onBeforeMount } from 'vue';
import { useAppStore } from '@/store';
#else
import { ref } from 'vue';
#end
const render = ref(false);
const table = ref();
#if($vue.enableCardView)
const card = ref();
#end
#if($vue.enableDrawerForm)
const drawer = ref();
#else
const modal = ref();
#end
#if($vue.enableCardView)
const appStore = useAppStore();
// FIXME 这里需要修改一下字段名称
const renderTable = computed(() => appStore.${vue.featureEntityFirstLower}View === 'table');
#end
// 添加回调
const modalAddCallback = () => {
#if($vue.enableCardView)
if (renderTable.value) {
table.value.addedCallback();
} else {
card.value.addedCallback();
}
#else
table.value.addedCallback();
#end
};
// 修改回调
const modalUpdateCallback = () => {
#if($vue.enableCardView)
if (renderTable.value) {
table.value.updatedCallback();
} else {
card.value.updatedCallback();
}
#else
table.value.updatedCallback();
#end
};
onBeforeMount(async () => {
render.value = true;
});
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,49 @@
import type { CardField, CardFieldConfig } from '@/types/card';
import { dateFormat } from '@/utils';
const fieldConfig = {
rowGap: '12px',
labelSpan: 8,
fields: [
{
label: 'id',
dataIndex: 'id',
slotName: 'id',
}, #foreach($field in ${table.fields})#if("$!field.propertyName" != "id"){
label: '${field.comment}',
dataIndex: '${field.propertyName}',
slotName: '${field.propertyName}',
#if(${field.propertyType} == 'String')
ellipsis: true,
#elseif(${field.propertyType} == 'Date')
render: ({ record }) => {
return record.${field.propertyName} && dateFormat(new Date(record.${field.propertyName}));
},
#end
}, #end#end{
label: '创建时间',
dataIndex: 'createTime',
slotName: 'createTime',
render: ({ record }) => {
return dateFormat(new Date(record.createTime));
},
}, {
label: '修改时间',
dataIndex: 'updateTime',
slotName: 'updateTime',
render: ({ record }) => {
return dateFormat(new Date(record.updateTime));
},
}, {
label: '创建人',
dataIndex: 'creator',
slotName: 'creator',
}, {
label: '修改人',
dataIndex: 'updater',
slotName: 'updater',
}
] as CardField[]
} as CardFieldConfig;
export default fieldConfig;

View File

@@ -0,0 +1,15 @@
#foreach($enumEntity in ${vue.enums.entrySet()})
/**
* $!{enumEntity.value.comment}
*/
export const $enumEntity.value.className = {
#foreach($enumEntityItem in $enumEntity.value.info.entrySet())
$enumEntityItem.key: {
#foreach($enumEntityItemFields in $enumEntityItem.value.entrySet())
$enumEntityItemFields.key: '$!enumEntityItemFields.value',
#end
},
#end
}
#end

View File

@@ -0,0 +1,27 @@
import type { FieldRule } from '@arco-design/web-vue';
#foreach($field in ${table.fields})
#if("$!field.propertyName" != "id")
#if(${field.propertyType} == 'String' && "$field.metaInfo.jdbcType" != "LONGVARCHAR")
export const ${field.propertyName} = [{
required: true,
message: '请输入${field.comment}'
}, {
maxLength: $field.metaInfo.length,
message: '${field.comment}长度不能大于$field.metaInfo.length位'
}] as FieldRule[];
#else
export const ${field.propertyName} = [{
required: true,
message: '请输入${field.comment}'
}] as FieldRule[];
#end
#end
#end
export default {
#foreach($field in ${table.fields})
#if("$!field.propertyName" != "id")
${field.propertyName},
#end
#end
} as Record<string, FieldRule | FieldRule[]>;

View File

@@ -0,0 +1,61 @@
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
import { dateFormat } from '@/utils';
const columns = [
{
title: 'id',
dataIndex: 'id',
slotName: 'id',
width: 70,
align: 'left',
fixed: 'left',
}, #foreach($field in ${table.fields})#if("$!field.propertyName" != "id"){
title: '${field.comment}',
dataIndex: '${field.propertyName}',
slotName: '${field.propertyName}',
align: 'left',
#if(${field.propertyType} == 'String')
ellipsis: true,
tooltip: true,
#elseif(${field.propertyType} == 'Date')
width: 180,
render: ({ record }) => {
return record.${field.propertyName} && dateFormat(new Date(record.${field.propertyName}));
},
#end
}, #end#end{
title: '创建时间',
dataIndex: 'createTime',
slotName: 'createTime',
align: 'center',
width: 180,
render: ({ record }) => {
return dateFormat(new Date(record.createTime));
},
}, {
title: '修改时间',
dataIndex: 'updateTime',
slotName: 'updateTime',
align: 'center',
width: 180,
render: ({ record }) => {
return dateFormat(new Date(record.updateTime));
},
}, {
title: '创建人',
dataIndex: 'creator',
slotName: 'creator',
}, {
title: '修改人',
dataIndex: 'updater',
slotName: 'updater',
}, {
title: '操作',
slotName: 'handle',
width: 130,
align: 'center',
fixed: 'right',
},
] as TableColumnData[];
export default columns;