mirror of
https://github.com/continew-org/continew-admin.git
synced 2025-09-10 19:00:53 +08:00
refactor: 优化项目模块命名(简化、分类、统一)
This commit is contained in:
14
continew-plugin/continew-plugin-generator/pom.xml
Normal file
14
continew-plugin/continew-plugin-generator/pom.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-plugin</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>continew-plugin-generator</artifactId>
|
||||
<description>代码生成器插件</description>
|
||||
</project>
|
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.config.properties;
|
||||
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.continew.starter.data.core.enums.DatabaseType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 代码生成器配置属性
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/8/5 11:08
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "generator")
|
||||
public class GeneratorProperties {
|
||||
|
||||
/**
|
||||
* 排除数据表(哪些数据表不展示在代码生成中)
|
||||
*/
|
||||
private String[] excludeTables = new String[0];
|
||||
|
||||
/**
|
||||
* 类型映射
|
||||
*/
|
||||
private Map<DatabaseType, Map<String, List<String>>> typeMappings = MapUtil.newHashMap();
|
||||
|
||||
/**
|
||||
* 模板配置
|
||||
*/
|
||||
private Map<String, TemplateConfig> templateConfigs = MapUtil.newHashMap(true);
|
||||
|
||||
/**
|
||||
* 模板配置
|
||||
*/
|
||||
@Data
|
||||
public static class TemplateConfig {
|
||||
|
||||
/**
|
||||
* 模板路径
|
||||
*/
|
||||
private String templatePath;
|
||||
|
||||
/**
|
||||
* 包名称
|
||||
*/
|
||||
private String packageName;
|
||||
|
||||
/**
|
||||
* 排除字段
|
||||
*/
|
||||
private String[] excludeFields;
|
||||
|
||||
/**
|
||||
* 扩展名
|
||||
*/
|
||||
private String extension = FileNameUtil.EXT_JAVA;
|
||||
|
||||
/**
|
||||
* 是否为后端模板
|
||||
*/
|
||||
private boolean backend = true;
|
||||
}
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import top.continew.starter.core.enums.BaseEnum;
|
||||
|
||||
/**
|
||||
* 表单类型枚举
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/8/6 10:49
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum FormTypeEnum implements BaseEnum<Integer> {
|
||||
|
||||
/**
|
||||
* 输入框
|
||||
*/
|
||||
INPUT(1, "输入框"),
|
||||
|
||||
/**
|
||||
* 数字输入框
|
||||
*/
|
||||
INPUT_NUMBER(2, "数字输入框"),
|
||||
|
||||
/**
|
||||
* 密码输入框
|
||||
*/
|
||||
INPUT_PASSWORD(3, "密码输入框"),
|
||||
|
||||
/**
|
||||
* 下拉框
|
||||
*/
|
||||
SELECT(4, "下拉框"),
|
||||
|
||||
/**
|
||||
* 单选框
|
||||
*/
|
||||
RADIO(5, "单选框"),
|
||||
|
||||
/**
|
||||
* 开关
|
||||
*/
|
||||
SWITCH(6, "开关"),
|
||||
|
||||
/**
|
||||
* 复选框
|
||||
*/
|
||||
CHECK_BOX(7, "复选框"),
|
||||
|
||||
/**
|
||||
* 文本域
|
||||
*/
|
||||
TEXT_AREA(8, "文本域"),
|
||||
|
||||
/**
|
||||
* 日期时间框
|
||||
*/
|
||||
DATE_TIME(9, "日期时间框"),
|
||||
|
||||
/**
|
||||
* 日期框
|
||||
*/
|
||||
DATE(10, "日期框"),
|
||||
|
||||
/**
|
||||
* 树形选择
|
||||
*/
|
||||
TREE_SELECT(11, "树选择"),;
|
||||
|
||||
private final Integer value;
|
||||
private final String description;
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import top.continew.starter.core.enums.BaseEnum;
|
||||
|
||||
/**
|
||||
* 查询类型枚举
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/8/6 10:49
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum QueryTypeEnum implements BaseEnum<Integer> {
|
||||
|
||||
/**
|
||||
* 等于 =,例如:WHERE age = 18
|
||||
*/
|
||||
EQ(1, "="),
|
||||
|
||||
/**
|
||||
* 不等于 !=,例如:WHERE age != 18
|
||||
*/
|
||||
NE(2, "!="),
|
||||
|
||||
/**
|
||||
* 大于 >,例如:WHERE age > 18
|
||||
*/
|
||||
GT(3, ">"),
|
||||
|
||||
/**
|
||||
* 大于等于 >= ,例如:WHERE age >= 18
|
||||
*/
|
||||
GE(4, ">="),
|
||||
|
||||
/**
|
||||
* 小于 <,例如:WHERE age < 18
|
||||
*/
|
||||
LT(5, "<"),
|
||||
|
||||
/**
|
||||
* 小于等于 <=,例如:WHERE age <= 18
|
||||
*/
|
||||
LE(6, "<="),
|
||||
|
||||
/**
|
||||
* 范围查询,例如:WHERE age BETWEEN 10 AND 18
|
||||
*/
|
||||
BETWEEN(7, "BETWEEN"),
|
||||
|
||||
/**
|
||||
* LIKE '%值%',例如:WHERE nickname LIKE '%s%'
|
||||
*/
|
||||
LIKE(8, "LIKE '%s%'"),
|
||||
|
||||
/**
|
||||
* LIKE '%值',例如:WHERE nickname LIKE '%s'
|
||||
*/
|
||||
LIKE_LEFT(9, "LIKE '%s'"),
|
||||
|
||||
/**
|
||||
* LIKE '值%',例如:WHERE nickname LIKE 's%'
|
||||
*/
|
||||
LIKE_RIGHT(10, "LIKE 's%'"),
|
||||
|
||||
/**
|
||||
* 包含查询,例如:WHERE age IN (10, 20, 30)
|
||||
*/
|
||||
IN(11, "IN"),
|
||||
|
||||
/**
|
||||
* 不包含查询,例如:WHERE age NOT IN (20, 30)
|
||||
*/
|
||||
NOT_IN(12, "NOT IN"),
|
||||
|
||||
/**
|
||||
* 空查询,例如:WHERE email IS NULL
|
||||
*/
|
||||
IS_NULL(13, "IS NULL"),
|
||||
|
||||
/**
|
||||
* 非空查询,例如:WHERE email IS NOT NULL
|
||||
*/
|
||||
IS_NOT_NULL(14, "IS NOT NULL"),;
|
||||
|
||||
private final Integer value;
|
||||
private final String description;
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import top.continew.admin.generator.model.entity.FieldConfigDO;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字段配置 Mapper
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/4/12 23:56
|
||||
*/
|
||||
public interface FieldConfigMapper extends BaseMapper<FieldConfigDO> {
|
||||
|
||||
/**
|
||||
* 根据表名称查询
|
||||
*
|
||||
* @param tableName 表名称
|
||||
* @return 字段配置信息
|
||||
*/
|
||||
@Select("SELECT * FROM gen_field_config WHERE table_name = #{tableName} ORDER BY field_sort ASC")
|
||||
List<FieldConfigDO> selectListByTableName(@Param("tableName") String tableName);
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.mapper;
|
||||
|
||||
import top.continew.admin.generator.model.entity.GenConfigDO;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
|
||||
/**
|
||||
* 生成配置 Mapper
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/4/12 23:56
|
||||
*/
|
||||
public interface GenConfigMapper extends BaseMapper<GenConfigDO> {
|
||||
}
|
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.model.entity;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.meta.Column;
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import top.continew.admin.generator.enums.FormTypeEnum;
|
||||
import top.continew.admin.generator.enums.QueryTypeEnum;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 字段配置实体
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/4/12 20:21
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@TableName("gen_field_config")
|
||||
@Schema(description = "字段配置信息")
|
||||
public class FieldConfigDO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 表名称
|
||||
*/
|
||||
@Schema(description = "表名称", example = "sys_user")
|
||||
@NotBlank(message = "表名称不能为空")
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 列名称
|
||||
*/
|
||||
@Schema(description = "列名称", example = "nickname")
|
||||
@NotBlank(message = "列名称不能为空")
|
||||
private String columnName;
|
||||
|
||||
/**
|
||||
* 列类型
|
||||
*/
|
||||
@Schema(description = "列类型", example = "varchar")
|
||||
@NotBlank(message = "列类型不能为空")
|
||||
private String columnType;
|
||||
|
||||
/**
|
||||
* 列大小
|
||||
*/
|
||||
@Schema(description = "列大小", example = "255")
|
||||
private String columnSize;
|
||||
|
||||
/**
|
||||
* 字段名称
|
||||
*/
|
||||
@Schema(description = "字段名称", example = "nickname")
|
||||
@NotBlank(message = "字段名称不能为空")
|
||||
private String fieldName;
|
||||
|
||||
/**
|
||||
* 字段类型
|
||||
*/
|
||||
@Schema(description = "字段类型", example = "String")
|
||||
@NotBlank(message = "字段类型不能为空")
|
||||
private String fieldType;
|
||||
|
||||
/**
|
||||
* 字段排序
|
||||
*/
|
||||
@Schema(description = "字段排序", example = "字段排序")
|
||||
@NotNull(message = "字段排序不能为空")
|
||||
private Integer fieldSort;
|
||||
|
||||
/**
|
||||
* 注释
|
||||
*/
|
||||
@Schema(description = "注释", example = "昵称")
|
||||
private String comment;
|
||||
|
||||
/**
|
||||
* 是否必填
|
||||
*/
|
||||
@Schema(description = "是否必填", example = "true")
|
||||
private Boolean isRequired;
|
||||
|
||||
/**
|
||||
* 是否在列表中显示
|
||||
*/
|
||||
@Schema(description = "是否在列表中显示", example = "true")
|
||||
private Boolean showInList;
|
||||
|
||||
/**
|
||||
* 是否在表单中显示
|
||||
*/
|
||||
@Schema(description = "是否在表单中显示", example = "true")
|
||||
private Boolean showInForm;
|
||||
|
||||
/**
|
||||
* 是否在查询中显示
|
||||
*/
|
||||
@Schema(description = "是否在查询中显示", example = "true")
|
||||
private Boolean showInQuery;
|
||||
|
||||
/**
|
||||
* 表单类型
|
||||
*/
|
||||
@Schema(description = "表单类型", example = "1")
|
||||
private FormTypeEnum formType;
|
||||
|
||||
/**
|
||||
* 查询方式
|
||||
*/
|
||||
@Schema(description = "查询方式", example = "1")
|
||||
private QueryTypeEnum queryType;
|
||||
|
||||
/**
|
||||
* 字典编码
|
||||
*/
|
||||
@Schema(description = "字典编码", example = "notice_type")
|
||||
private String dictCode;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@Schema(description = "创建时间", example = "2023-08-08 08:08:08", type = "string")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
public FieldConfigDO(@NonNull Column column) {
|
||||
this.setTableName(column.getTableName());
|
||||
this.setColumnName(column.getName());
|
||||
this.setColumnType(column.getTypeName());
|
||||
this.setColumnSize(Convert.toStr(column.getSize()));
|
||||
this.setComment(column.getComment());
|
||||
this.setIsRequired(!column.isPk() && !column.isNullable());
|
||||
this.setShowInList(true);
|
||||
this.setShowInForm(this.getIsRequired());
|
||||
this.setShowInQuery(this.getIsRequired());
|
||||
}
|
||||
|
||||
public void setColumnName(String columnName) {
|
||||
this.columnName = columnName;
|
||||
this.fieldName = StrUtil.toCamelCase(this.columnName);
|
||||
}
|
||||
|
||||
public void setColumnType(String columnType) {
|
||||
String[] arr = StrUtil.splitToArray(columnType, StringConstants.SPACE);
|
||||
this.columnType = arr.length > 1 ? arr[0].toLowerCase() : columnType.toLowerCase();
|
||||
}
|
||||
}
|
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.model.entity;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
import top.continew.admin.common.constant.RegexConstants;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 生成配置实体
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/4/12 20:21
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@TableName("gen_config")
|
||||
@Schema(description = "生成配置信息")
|
||||
public class GenConfigDO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 表名称
|
||||
*/
|
||||
@Schema(description = "表名称", example = "sys_user")
|
||||
@TableId(type = IdType.INPUT)
|
||||
@NotBlank(message = "表名称不能为空")
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 模块名称
|
||||
*/
|
||||
@Schema(description = "模块名称", example = "continew-admin-system")
|
||||
@NotBlank(message = "模块名称不能为空")
|
||||
@Length(max = 60, message = "模块名称不能超过 {max} 个字符")
|
||||
private String moduleName;
|
||||
|
||||
/**
|
||||
* 包名称
|
||||
*/
|
||||
@Schema(description = "包名称", example = "top.continew.admin.system")
|
||||
@NotBlank(message = "包名称不能为空")
|
||||
@Pattern(regexp = RegexConstants.PACKAGE_NAME, message = "包名称格式错误")
|
||||
@Length(max = 60, message = "包名称不能超过 {max} 个字符")
|
||||
private String packageName;
|
||||
|
||||
/**
|
||||
* 业务名称
|
||||
*/
|
||||
@Schema(description = "业务名称", example = "用户")
|
||||
@NotBlank(message = "业务名称不能为空")
|
||||
@Length(max = 50, message = "业务名称不能超过 {max} 个字符")
|
||||
private String businessName;
|
||||
|
||||
/**
|
||||
* 作者
|
||||
*/
|
||||
@Schema(description = "作者", example = "Charles7c")
|
||||
@NotBlank(message = "作者名称不能为空")
|
||||
@Length(max = 100, message = "作者名称不能超过 {max} 个字符")
|
||||
private String author;
|
||||
|
||||
/**
|
||||
* 表前缀
|
||||
*/
|
||||
@Schema(description = "表前缀", example = "sys_")
|
||||
private String tablePrefix;
|
||||
|
||||
/**
|
||||
* 是否覆盖
|
||||
*/
|
||||
@Schema(description = "是否覆盖", example = "false")
|
||||
@NotNull(message = "是否覆盖不能为空")
|
||||
private Boolean isOverride;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@Schema(description = "创建时间", example = "2023-08-08 08:08:08", type = "string")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
@Schema(description = "修改时间", example = "2023-08-08 08:08:08", type = "string")
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
public GenConfigDO(String tableName) {
|
||||
this.setTableName(tableName);
|
||||
}
|
||||
|
||||
public void setTableName(String tableName) {
|
||||
this.tableName = tableName;
|
||||
// 默认表前缀(sys_user -> sys_)
|
||||
int underLineIndex = StrUtil.indexOf(tableName, StringConstants.C_UNDERLINE);
|
||||
if (-1 != underLineIndex) {
|
||||
this.tablePrefix = StrUtil.subPre(tableName, underLineIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.model.entity;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.core.util.StrUtils;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 内部生成配置信息
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2024/8/30 19:35
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class InnerGenConfigDO extends GenConfigDO {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 字段配置信息
|
||||
*/
|
||||
private List<FieldConfigDO> fieldConfigs;
|
||||
|
||||
/**
|
||||
* 生成时间
|
||||
*/
|
||||
private String datetime;
|
||||
|
||||
/**
|
||||
* API 模块名称
|
||||
*/
|
||||
private String apiModuleName;
|
||||
|
||||
/**
|
||||
* API 名称
|
||||
*/
|
||||
private String apiName;
|
||||
|
||||
/**
|
||||
* 类名
|
||||
*/
|
||||
private String className;
|
||||
|
||||
/**
|
||||
* 类名前缀
|
||||
*/
|
||||
private String classNamePrefix;
|
||||
|
||||
/**
|
||||
* 子包名称
|
||||
*/
|
||||
private String subPackageName;
|
||||
|
||||
/**
|
||||
* 字典编码列表
|
||||
*/
|
||||
private Set<String> dictCodes;
|
||||
|
||||
/**
|
||||
* 是否包含必填字段
|
||||
*/
|
||||
private boolean hasRequiredField;
|
||||
|
||||
/**
|
||||
* 是否包含字典字段
|
||||
*/
|
||||
private boolean hasDictField;
|
||||
|
||||
/**
|
||||
* 是否包含 BigDecimal 字段
|
||||
*/
|
||||
private boolean hasBigDecimalField;
|
||||
|
||||
/**
|
||||
* 是否包含 List 字段
|
||||
*/
|
||||
private boolean hasListField;
|
||||
|
||||
/**
|
||||
* 是否包含 Time 包字段
|
||||
*/
|
||||
private boolean hasTimeField;
|
||||
|
||||
public InnerGenConfigDO() {
|
||||
}
|
||||
|
||||
public InnerGenConfigDO(GenConfigDO genConfig) {
|
||||
BeanUtil.copyProperties(genConfig, this);
|
||||
this.setDatetime(DateUtil.date().toString("yyyy/MM/dd HH:mm"));
|
||||
this.setApiName(StrUtil.lowerFirst(this.getClassNamePrefix()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPackageName(String packageName) {
|
||||
super.setPackageName(packageName);
|
||||
String realPackageName = this.getPackageName();
|
||||
this.setApiModuleName(StrUtil.subSuf(realPackageName, StrUtil
|
||||
.lastIndexOfIgnoreCase(realPackageName, StringConstants.DOT) + 1));
|
||||
}
|
||||
|
||||
public String getClassNamePrefix() {
|
||||
String tableName = super.getTableName();
|
||||
String rawClassName = StrUtils.blankToDefault(super.getTablePrefix(), tableName, prefix -> StrUtil
|
||||
.removePrefix(tableName, prefix));
|
||||
return StrUtil.upperFirst(StrUtil.toCamelCase(rawClassName));
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.model.query;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 表信息查询条件
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/4/12 20:21
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "表信息查询条件")
|
||||
public class TableQuery implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 表名称
|
||||
*/
|
||||
@Schema(description = "表名称", example = "sys_user")
|
||||
private String tableName;
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.model.req;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import top.continew.admin.generator.model.entity.FieldConfigDO;
|
||||
import top.continew.admin.generator.model.entity.GenConfigDO;
|
||||
|
||||
/**
|
||||
* 代码生成配置信息
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/8/8 20:40
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "代码生成配置信息")
|
||||
public class GenConfigReq implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 字段配置信息
|
||||
*/
|
||||
@Valid
|
||||
@Schema(description = "字段配置信息")
|
||||
@NotEmpty(message = "字段配置不能为空")
|
||||
private List<FieldConfigDO> fieldConfigs = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 生成配置信息
|
||||
*/
|
||||
@Valid
|
||||
@Schema(description = "生成配置信息")
|
||||
@NotNull(message = "生成配置不能为空")
|
||||
private GenConfigDO genConfig;
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.model.resp;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 生成预览信息
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/12/19 21:34
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "生成预览信息")
|
||||
public class GeneratePreviewResp implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "生成的文件路径", example = "continew-admin\\continew-admin\\continew-admin-generator\\src\\main\\java\\top\\continew\\admin\\generator\\service")
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
@Schema(description = "文件名", example = "UserController.java")
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
@Schema(description = "内容", example = "public class UserController {...}")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 是否为后端代码
|
||||
*/
|
||||
@Schema(hidden = true)
|
||||
@JsonIgnore
|
||||
private boolean backend;
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.model.resp;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* 表信息
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/4/12 20:21
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "表信息")
|
||||
public class TableResp implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 表名称
|
||||
*/
|
||||
@Schema(description = "表名称", example = "sys_user")
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
@Schema(description = "描述", example = "用户表")
|
||||
private String comment;
|
||||
|
||||
/**
|
||||
* 存储引擎
|
||||
*/
|
||||
@Schema(description = "存储引擎", example = "InnoDB")
|
||||
private String engine;
|
||||
|
||||
/**
|
||||
* 字符集
|
||||
*/
|
||||
@Schema(description = "字符集", example = "utf8mb4_general_ci")
|
||||
private String charset;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@Schema(description = "创建时间", example = "2023-08-08 08:08:08", type = "string")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 是否已配置
|
||||
*/
|
||||
@Schema(description = "是否已配置", example = "true")
|
||||
private Boolean isConfiged;
|
||||
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
@Schema(description = "是否禁用", example = "true")
|
||||
private Boolean disabled;
|
||||
|
||||
public Boolean getDisabled() {
|
||||
return !isConfiged;
|
||||
}
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.service;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import top.continew.admin.generator.model.entity.FieldConfigDO;
|
||||
import top.continew.admin.generator.model.entity.GenConfigDO;
|
||||
import top.continew.admin.generator.model.query.TableQuery;
|
||||
import top.continew.admin.generator.model.req.GenConfigReq;
|
||||
import top.continew.admin.generator.model.resp.GeneratePreviewResp;
|
||||
import top.continew.admin.generator.model.resp.TableResp;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 代码生成业务接口
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/4/12 23:57
|
||||
*/
|
||||
public interface GeneratorService {
|
||||
|
||||
/**
|
||||
* 分页查询表信息列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param pageQuery 分页查询条件
|
||||
* @return 表信息分页列表
|
||||
* @throws SQLException /
|
||||
*/
|
||||
PageResp<TableResp> pageTable(TableQuery query, PageQuery pageQuery) throws SQLException;
|
||||
|
||||
/**
|
||||
* 查询生成配置信息
|
||||
*
|
||||
* @param tableName 表名称
|
||||
* @return 生成配置信息
|
||||
* @throws SQLException /
|
||||
*/
|
||||
GenConfigDO getGenConfig(String tableName) throws SQLException;
|
||||
|
||||
/**
|
||||
* 查询字段配置列表
|
||||
*
|
||||
* @param tableName 表名称
|
||||
* @param requireSync 是否需要同步
|
||||
* @return 字段配置列表
|
||||
*/
|
||||
List<FieldConfigDO> listFieldConfig(String tableName, Boolean requireSync);
|
||||
|
||||
/**
|
||||
* 保存代码生成配置信息
|
||||
*
|
||||
* @param req 代码生成配置信息
|
||||
* @param tableName 表名称
|
||||
*/
|
||||
void saveConfig(GenConfigReq req, String tableName);
|
||||
|
||||
/**
|
||||
* 生成预览
|
||||
*
|
||||
* @param tableName 表名称
|
||||
* @return 预览信息
|
||||
*/
|
||||
List<GeneratePreviewResp> preview(String tableName);
|
||||
|
||||
/**
|
||||
* 生成代码
|
||||
*
|
||||
* @param tableNames 表明层
|
||||
* @param response 响应对象
|
||||
*/
|
||||
void generate(List<String> tableNames, HttpServletResponse response);
|
||||
}
|
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.admin.generator.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.ZipUtil;
|
||||
import cn.hutool.db.meta.Column;
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import top.continew.admin.generator.config.properties.GeneratorProperties;
|
||||
import top.continew.admin.generator.enums.FormTypeEnum;
|
||||
import top.continew.admin.generator.enums.QueryTypeEnum;
|
||||
import top.continew.admin.generator.mapper.FieldConfigMapper;
|
||||
import top.continew.admin.generator.mapper.GenConfigMapper;
|
||||
import top.continew.admin.generator.model.entity.FieldConfigDO;
|
||||
import top.continew.admin.generator.model.entity.GenConfigDO;
|
||||
import top.continew.admin.generator.model.entity.InnerGenConfigDO;
|
||||
import top.continew.admin.generator.model.query.TableQuery;
|
||||
import top.continew.admin.generator.model.req.GenConfigReq;
|
||||
import top.continew.admin.generator.model.resp.GeneratePreviewResp;
|
||||
import top.continew.admin.generator.model.resp.TableResp;
|
||||
import top.continew.admin.generator.service.GeneratorService;
|
||||
import top.continew.starter.core.autoconfigure.project.ProjectProperties;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.core.exception.BusinessException;
|
||||
import top.continew.starter.core.util.TemplateUtils;
|
||||
import top.continew.starter.core.util.validate.CheckUtils;
|
||||
import top.continew.starter.data.core.enums.DatabaseType;
|
||||
import top.continew.starter.data.core.util.MetaUtils;
|
||||
import top.continew.starter.data.core.util.Table;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.web.util.FileUploadUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.File;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 代码生成业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/4/12 23:58
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GeneratorServiceImpl implements GeneratorService {
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final GeneratorProperties generatorProperties;
|
||||
private final ProjectProperties projectProperties;
|
||||
private final FieldConfigMapper fieldConfigMapper;
|
||||
private final GenConfigMapper genConfigMapper;
|
||||
private static final List<String> TIME_PACKAGE_CLASS = Arrays.asList("LocalDate", "LocalTime", "LocalDateTime");
|
||||
|
||||
@Override
|
||||
public PageResp<TableResp> pageTable(TableQuery query, PageQuery pageQuery) throws SQLException {
|
||||
List<Table> tableList = MetaUtils.getTables(dataSource);
|
||||
String tableName = query.getTableName();
|
||||
if (StrUtil.isNotBlank(tableName)) {
|
||||
tableList.removeIf(table -> !StrUtil.containsAnyIgnoreCase(table.getTableName(), tableName));
|
||||
}
|
||||
tableList.removeIf(table -> StrUtil.equalsAnyIgnoreCase(table.getTableName(), generatorProperties
|
||||
.getExcludeTables()));
|
||||
CollUtil.sort(tableList, Comparator.comparing(Table::getCreateTime)
|
||||
.thenComparing(table -> Optional.ofNullable(table.getUpdateTime()).orElse(table.getCreateTime()))
|
||||
.reversed());
|
||||
List<TableResp> tableRespList = BeanUtil.copyToList(tableList, TableResp.class);
|
||||
PageResp<TableResp> pageResp = PageResp.build(pageQuery.getPage(), pageQuery.getSize(), tableRespList);
|
||||
pageResp.getList().parallelStream().forEach(tableResp -> {
|
||||
long count = genConfigMapper.selectCount(Wrappers.lambdaQuery(GenConfigDO.class)
|
||||
.eq(GenConfigDO::getTableName, tableResp.getTableName()));
|
||||
tableResp.setIsConfiged(count > 0);
|
||||
});
|
||||
return pageResp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenConfigDO getGenConfig(String tableName) throws SQLException {
|
||||
GenConfigDO genConfig = genConfigMapper.selectById(tableName);
|
||||
if (null == genConfig) {
|
||||
genConfig = new GenConfigDO(tableName);
|
||||
// 默认包名(当前包名)
|
||||
String packageName = ClassUtil.getPackage(GeneratorService.class);
|
||||
genConfig.setPackageName(StrUtil.subBefore(packageName, StringConstants.DOT, true));
|
||||
// 默认业务名(表注释)
|
||||
List<Table> tableList = MetaUtils.getTables(dataSource, tableName);
|
||||
if (CollUtil.isNotEmpty(tableList)) {
|
||||
Table table = tableList.get(0);
|
||||
genConfig.setBusinessName(StrUtil.replace(table.getComment(), "表", StringConstants.EMPTY));
|
||||
}
|
||||
// 默认作者名称(上次保存使用的作者名称)
|
||||
GenConfigDO lastGenConfig = genConfigMapper.selectOne(Wrappers.lambdaQuery(GenConfigDO.class)
|
||||
.orderByDesc(GenConfigDO::getCreateTime)
|
||||
.last("LIMIT 1"));
|
||||
if (null != lastGenConfig) {
|
||||
genConfig.setAuthor(lastGenConfig.getAuthor());
|
||||
}
|
||||
}
|
||||
return genConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FieldConfigDO> listFieldConfig(String tableName, Boolean requireSync) {
|
||||
List<FieldConfigDO> fieldConfigList = fieldConfigMapper.selectListByTableName(tableName);
|
||||
if (CollUtil.isNotEmpty(fieldConfigList) && Boolean.FALSE.equals(requireSync)) {
|
||||
return fieldConfigList;
|
||||
}
|
||||
List<FieldConfigDO> latestFieldConfigList = new ArrayList<>();
|
||||
// 获取最新数据表列信息
|
||||
Collection<Column> columnList = MetaUtils.getColumns(dataSource, tableName);
|
||||
// 获取数据库对应的类型映射配置
|
||||
DatabaseType databaseType = MetaUtils.getDatabaseType(dataSource);
|
||||
Map<String, List<String>> typeMappingMap = generatorProperties.getTypeMappings().get(databaseType);
|
||||
CheckUtils.throwIfEmpty(typeMappingMap, "请先配置对应数据库的类型映射");
|
||||
Set<Map.Entry<String, List<String>>> typeMappingEntrySet = typeMappingMap.entrySet();
|
||||
// 新增或更新字段配置
|
||||
Map<String, FieldConfigDO> fieldConfigMap = fieldConfigList.stream()
|
||||
.collect(Collectors.toMap(FieldConfigDO::getColumnName, Function.identity(), (key1, key2) -> key2));
|
||||
int i = 1;
|
||||
for (Column column : columnList) {
|
||||
FieldConfigDO fieldConfig = Optional.ofNullable(fieldConfigMap.get(column.getName()))
|
||||
.orElseGet(() -> new FieldConfigDO(column));
|
||||
// 更新已有字段配置
|
||||
if (null != fieldConfig.getCreateTime()) {
|
||||
fieldConfig.setColumnType(column.getTypeName());
|
||||
fieldConfig.setColumnSize(Convert.toStr(column.getSize()));
|
||||
}
|
||||
String fieldType = typeMappingEntrySet.stream()
|
||||
.filter(entry -> entry.getValue().contains(fieldConfig.getColumnType()))
|
||||
.map(Map.Entry::getKey)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
fieldConfig.setFieldType(fieldType);
|
||||
fieldConfig.setFieldSort(i++);
|
||||
latestFieldConfigList.add(fieldConfig);
|
||||
}
|
||||
return latestFieldConfigList;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveConfig(GenConfigReq req, String tableName) {
|
||||
// 保存字段配置(先删除再保存)
|
||||
fieldConfigMapper.delete(Wrappers.lambdaQuery(FieldConfigDO.class).eq(FieldConfigDO::getTableName, tableName));
|
||||
List<FieldConfigDO> fieldConfigList = req.getFieldConfigs();
|
||||
for (int i = 0; i < fieldConfigList.size(); i++) {
|
||||
FieldConfigDO fieldConfig = fieldConfigList.get(i);
|
||||
// 重新设置排序
|
||||
fieldConfig.setFieldSort(i + 1);
|
||||
if (Boolean.TRUE.equals(fieldConfig.getShowInForm())) {
|
||||
fieldConfig.setFormType(ObjectUtil.defaultIfNull(fieldConfig.getFormType(), FormTypeEnum.INPUT));
|
||||
} else {
|
||||
// 在表单中不显示,不需要设置必填
|
||||
fieldConfig.setIsRequired(false);
|
||||
}
|
||||
if (Boolean.TRUE.equals(fieldConfig.getShowInQuery())) {
|
||||
fieldConfig.setFormType(ObjectUtil.defaultIfNull(fieldConfig.getFormType(), FormTypeEnum.INPUT));
|
||||
fieldConfig.setQueryType(ObjectUtil.defaultIfNull(fieldConfig.getQueryType(), QueryTypeEnum.EQ));
|
||||
} else {
|
||||
// 在查询中不显示,不需要设置查询方式
|
||||
fieldConfig.setQueryType(null);
|
||||
}
|
||||
// 既不在表单也不在查询中显示,不需要设置表单类型
|
||||
if (Boolean.FALSE.equals(fieldConfig.getShowInForm()) && Boolean.FALSE.equals(fieldConfig
|
||||
.getShowInQuery())) {
|
||||
fieldConfig.setFormType(null);
|
||||
}
|
||||
fieldConfig.setTableName(tableName);
|
||||
}
|
||||
fieldConfigMapper.insert(fieldConfigList);
|
||||
// 保存或更新生成配置信息
|
||||
GenConfigDO newGenConfig = req.getGenConfig();
|
||||
GenConfigDO oldGenConfig = genConfigMapper.selectById(tableName);
|
||||
if (null != oldGenConfig) {
|
||||
BeanUtil.copyProperties(newGenConfig, oldGenConfig);
|
||||
genConfigMapper.updateById(oldGenConfig);
|
||||
} else {
|
||||
genConfigMapper.insert(newGenConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GeneratePreviewResp> preview(String tableName) {
|
||||
List<GeneratePreviewResp> generatePreviewList = new ArrayList<>();
|
||||
// 初始化配置
|
||||
GenConfigDO genConfig = genConfigMapper.selectById(tableName);
|
||||
CheckUtils.throwIfNull(genConfig, "请先进行数据表 [{}] 生成配置", tableName);
|
||||
List<FieldConfigDO> fieldConfigList = fieldConfigMapper.selectListByTableName(tableName);
|
||||
CheckUtils.throwIfEmpty(fieldConfigList, "请先进行数据表 [{}] 字段配置", tableName);
|
||||
InnerGenConfigDO innerGenConfig = new InnerGenConfigDO(genConfig);
|
||||
// 渲染代码
|
||||
String classNamePrefix = innerGenConfig.getClassNamePrefix();
|
||||
Map<String, GeneratorProperties.TemplateConfig> templateConfigMap = generatorProperties.getTemplateConfigs();
|
||||
for (Map.Entry<String, GeneratorProperties.TemplateConfig> templateConfigEntry : templateConfigMap.entrySet()) {
|
||||
GeneratorProperties.TemplateConfig templateConfig = templateConfigEntry.getValue();
|
||||
// 移除需要忽略的字段
|
||||
innerGenConfig.setFieldConfigs(fieldConfigList.stream()
|
||||
.filter(fieldConfig -> !StrUtil.equalsAny(fieldConfig.getFieldName(), templateConfig
|
||||
.getExcludeFields()))
|
||||
.toList());
|
||||
// 预处理配置
|
||||
this.pretreatment(innerGenConfig);
|
||||
// 处理其他配置
|
||||
innerGenConfig.setSubPackageName(templateConfig.getPackageName());
|
||||
String classNameSuffix = templateConfigEntry.getKey();
|
||||
String className = classNamePrefix + classNameSuffix;
|
||||
innerGenConfig.setClassName(className);
|
||||
boolean isBackend = templateConfig.isBackend();
|
||||
String extension = templateConfig.getExtension();
|
||||
GeneratePreviewResp generatePreview = new GeneratePreviewResp();
|
||||
generatePreview.setBackend(isBackend);
|
||||
generatePreviewList.add(generatePreview);
|
||||
String fileName = className + extension;
|
||||
if (!isBackend) {
|
||||
fileName = ".vue".equals(extension) && "index".equals(classNameSuffix)
|
||||
? "index.vue"
|
||||
: this.getFrontendFileName(classNamePrefix, className, extension);
|
||||
}
|
||||
generatePreview.setFileName(fileName);
|
||||
generatePreview.setContent(TemplateUtils.render(templateConfig.getTemplatePath(), BeanUtil
|
||||
.beanToMap(innerGenConfig)));
|
||||
this.setPreviewPath(generatePreview, innerGenConfig, templateConfig);
|
||||
}
|
||||
return generatePreviewList;
|
||||
}
|
||||
|
||||
private void setPreviewPath(GeneratePreviewResp generatePreview,
|
||||
InnerGenConfigDO genConfig,
|
||||
GeneratorProperties.TemplateConfig templateConfig) {
|
||||
// 获取前后端基础路径
|
||||
String backendBasicPackagePath = this.buildBackendBasicPackagePath(genConfig);
|
||||
String frontendBasicPackagePath = String.join(File.separator, projectProperties.getAppName(), projectProperties
|
||||
.getAppName() + "-ui");
|
||||
String packagePath;
|
||||
if (generatePreview.isBackend()) {
|
||||
// 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl
|
||||
packagePath = String.join(File.separator, backendBasicPackagePath, templateConfig.getPackageName()
|
||||
.replace(StringConstants.DOT, File.separator));
|
||||
} else {
|
||||
// 例如:continew-admin/continew-admin-ui/src/views/system
|
||||
packagePath = String.join(File.separator, frontendBasicPackagePath, templateConfig.getPackageName()
|
||||
.replace(StringConstants.SLASH, File.separator), genConfig.getApiModuleName());
|
||||
// 例如:continew-admin/continew-admin-ui/src/views/system/user
|
||||
packagePath = ".vue".equals(templateConfig.getExtension())
|
||||
? packagePath + File.separator + StrUtil.lowerFirst(genConfig.getClassNamePrefix())
|
||||
: packagePath;
|
||||
}
|
||||
generatePreview.setPath(packagePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(List<String> tableNames, HttpServletResponse response) {
|
||||
try {
|
||||
String tempDir = SystemUtil.getUserInfo().getTempDir();
|
||||
// 删除旧代码
|
||||
FileUtil.del(tempDir + projectProperties.getAppName());
|
||||
tableNames.forEach(tableName -> {
|
||||
// 初始化配置及数据
|
||||
List<GeneratePreviewResp> generatePreviewList = this.preview(tableName);
|
||||
// 生成代码
|
||||
this.generateCode(generatePreviewList, genConfigMapper.selectById(tableName));
|
||||
});
|
||||
// 打包下载
|
||||
File tempDirFile = new File(tempDir, projectProperties.getAppName());
|
||||
String zipFilePath = tempDirFile.getPath() + jodd.io.ZipUtil.ZIP_EXT;
|
||||
ZipUtil.zip(tempDirFile.getPath(), zipFilePath);
|
||||
FileUploadUtils.download(response, new File(zipFilePath));
|
||||
} catch (Exception e) {
|
||||
log.error("Generate code of table '{}' occurred an error. {}", tableNames, e.getMessage(), e);
|
||||
throw new BusinessException("代码生成失败,请手动清理生成文件");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成代码
|
||||
*
|
||||
* @param generatePreviewList 生成预览列表
|
||||
* @param genConfig 生成配置
|
||||
*/
|
||||
private void generateCode(List<GeneratePreviewResp> generatePreviewList, GenConfigDO genConfig) {
|
||||
for (GeneratePreviewResp generatePreview : generatePreviewList) {
|
||||
// 后端:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl/XxxServiceImpl.java
|
||||
// 前端:continew-admin/continew-admin-ui/src/views/system/user/index.vue
|
||||
File file = new File(SystemUtil.getUserInfo().getTempDir() + generatePreview.getPath(), generatePreview
|
||||
.getFileName());
|
||||
// 如果已经存在,且不允许覆盖,则跳过
|
||||
if (!file.exists() || Boolean.TRUE.equals(genConfig.getIsOverride())) {
|
||||
FileUtil.writeUtf8String(generatePreview.getContent(), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建后端包路径
|
||||
*
|
||||
* @param genConfig 生成配置
|
||||
* @return 后端包路径
|
||||
*/
|
||||
private String buildBackendBasicPackagePath(GenConfigDO genConfig) {
|
||||
// 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system
|
||||
return String.join(File.separator, projectProperties.getAppName(), projectProperties.getAppName(), genConfig
|
||||
.getModuleName(), "src", "main", "java", genConfig.getPackageName()
|
||||
.replace(StringConstants.DOT, File.separator));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取前端文件名
|
||||
*
|
||||
* @param classNamePrefix 类名前缀
|
||||
* @param className 类名
|
||||
* @param extension 扩展名
|
||||
* @return 前端文件名
|
||||
*/
|
||||
private String getFrontendFileName(String classNamePrefix, String className, String extension) {
|
||||
return (".ts".equals(extension) ? StrUtil.lowerFirst(classNamePrefix) : className) + extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预处理生成配置
|
||||
*
|
||||
* @param genConfig 生成配置
|
||||
*/
|
||||
private void pretreatment(InnerGenConfigDO genConfig) {
|
||||
List<FieldConfigDO> fieldConfigList = genConfig.getFieldConfigs();
|
||||
// 统计部分特殊字段特征
|
||||
Set<String> dictCodeSet = new HashSet<>();
|
||||
for (FieldConfigDO fieldConfig : fieldConfigList) {
|
||||
String fieldType = fieldConfig.getFieldType();
|
||||
// 必填项
|
||||
if (Boolean.TRUE.equals(fieldConfig.getIsRequired())) {
|
||||
genConfig.setHasRequiredField(true);
|
||||
}
|
||||
// 数据类型
|
||||
if ("BigDecimal".equals(fieldType)) {
|
||||
genConfig.setHasBigDecimalField(true);
|
||||
}
|
||||
if (TIME_PACKAGE_CLASS.contains(fieldType)) {
|
||||
genConfig.setHasTimeField(true);
|
||||
}
|
||||
QueryTypeEnum queryType = fieldConfig.getQueryType();
|
||||
if (null != queryType && StrUtil.equalsAny(queryType.name(), QueryTypeEnum.IN.name(), QueryTypeEnum.NOT_IN
|
||||
.name(), QueryTypeEnum.BETWEEN.name())) {
|
||||
genConfig.setHasListField(true);
|
||||
}
|
||||
// 字典码
|
||||
if (StrUtil.isNotBlank(fieldConfig.getDictCode())) {
|
||||
genConfig.setHasDictField(true);
|
||||
dictCodeSet.add(fieldConfig.getDictCode());
|
||||
}
|
||||
}
|
||||
genConfig.setDictCodes(dictCodeSet);
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import top.continew.starter.extension.crud.enums.Api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
||||
import top.continew.starter.extension.crud.controller.BaseController;
|
||||
import ${packageName}.model.query.${classNamePrefix}Query;
|
||||
import ${packageName}.model.req.${classNamePrefix}Req;
|
||||
import ${packageName}.model.resp.${classNamePrefix}DetailResp;
|
||||
import ${packageName}.model.resp.${classNamePrefix}Resp;
|
||||
import ${packageName}.service.${classNamePrefix}Service;
|
||||
|
||||
/**
|
||||
* ${businessName}管理 API
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Tag(name = "${businessName}管理 API")
|
||||
@RestController
|
||||
@CrudRequestMapping(value = "/${apiModuleName}/${apiName}", api = {Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT})
|
||||
public class ${className} extends BaseController<${classNamePrefix}Service, ${classNamePrefix}Resp, ${classNamePrefix}DetailResp, ${classNamePrefix}Query, ${classNamePrefix}Req> {}
|
@@ -0,0 +1,44 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import java.io.Serial;
|
||||
<#if hasTimeField>
|
||||
import java.time.*;
|
||||
</#if>
|
||||
<#if hasBigDecimalField>
|
||||
import java.math.BigDecimal;
|
||||
</#if>
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
|
||||
import top.continew.starter.extension.crud.model.resp.BaseDetailResp;
|
||||
|
||||
/**
|
||||
* ${businessName}详情信息
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@Schema(description = "${businessName}详情信息")
|
||||
public class ${className} extends BaseDetailResp {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
<#if fieldConfigs??>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
|
||||
/**
|
||||
* ${fieldConfig.comment}
|
||||
*/
|
||||
@Schema(description = "${fieldConfig.comment}")
|
||||
@ExcelProperty(value = "${fieldConfig.comment}")
|
||||
private ${fieldConfig.fieldType} ${fieldConfig.fieldName};
|
||||
</#list>
|
||||
</#if>
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import java.io.Serial;
|
||||
<#if hasTimeField>
|
||||
import java.time.*;
|
||||
</#if>
|
||||
<#if hasBigDecimalField>
|
||||
import java.math.BigDecimal;
|
||||
</#if>
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import top.continew.starter.extension.crud.model.entity.BaseDO;
|
||||
|
||||
/**
|
||||
* ${businessName}实体
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Data
|
||||
@TableName("${tableName}")
|
||||
public class ${className} extends BaseDO {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
<#if fieldConfigs??>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
|
||||
/**
|
||||
* ${fieldConfig.comment}
|
||||
*/
|
||||
private ${fieldConfig.fieldType} ${fieldConfig.fieldName};
|
||||
</#list>
|
||||
</#if>
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
import ${packageName}.model.entity.${classNamePrefix}DO;
|
||||
|
||||
/**
|
||||
* ${businessName} Mapper
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
public interface ${className} extends BaseMapper<${classNamePrefix}DO> {}
|
@@ -0,0 +1,51 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
<#if hasTimeField>
|
||||
import java.time.*;
|
||||
</#if>
|
||||
<#if hasBigDecimalField>
|
||||
import java.math.BigDecimal;
|
||||
</#if>
|
||||
<#if hasListField>
|
||||
import java.util.List;
|
||||
</#if>
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import top.continew.starter.data.core.annotation.Query;
|
||||
import top.continew.starter.data.core.enums.QueryType;
|
||||
|
||||
/**
|
||||
* ${businessName}查询条件
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "${businessName}查询条件")
|
||||
public class ${className} implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
<#if fieldConfigs??>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInQuery>
|
||||
|
||||
/**
|
||||
* ${fieldConfig.comment}
|
||||
*/
|
||||
@Schema(description = "${fieldConfig.comment}")
|
||||
@Query(type = QueryType.${fieldConfig.queryType})
|
||||
<#if fieldConfig.queryType = 'IN' || fieldConfig.queryType = 'NOT_IN' || fieldConfig.queryType = 'BETWEEN'>
|
||||
private List<${fieldConfig.fieldType}> ${fieldConfig.fieldName};
|
||||
<#else>
|
||||
private ${fieldConfig.fieldType} ${fieldConfig.fieldName};
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
</#if>
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import java.io.Serial;
|
||||
<#if hasTimeField>
|
||||
import java.time.*;
|
||||
</#if>
|
||||
<#if hasBigDecimalField>
|
||||
import java.math.BigDecimal;
|
||||
</#if>
|
||||
|
||||
<#if hasRequiredField>
|
||||
import jakarta.validation.constraints.*;
|
||||
</#if>
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import top.continew.starter.extension.crud.model.req.BaseReq;
|
||||
|
||||
/**
|
||||
* 创建或修改${businessName}信息
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "创建或修改${businessName}信息")
|
||||
public class ${className} extends BaseReq {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
<#if fieldConfigs??>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInForm>
|
||||
|
||||
/**
|
||||
* ${fieldConfig.comment}
|
||||
*/
|
||||
@Schema(description = "${fieldConfig.comment}")
|
||||
<#if fieldConfig.isRequired>
|
||||
<#if fieldConfig.fieldType = 'String'>
|
||||
@NotBlank(message = "${fieldConfig.comment}不能为空")
|
||||
<#else>
|
||||
@NotNull(message = "${fieldConfig.comment}不能为空")
|
||||
</#if>
|
||||
</#if>
|
||||
<#if fieldConfig.fieldType = 'String' && fieldConfig.columnSize??>
|
||||
@Length(max = ${fieldConfig.columnSize}, message = "${fieldConfig.comment}长度不能超过 {max} 个字符")
|
||||
</#if>
|
||||
private ${fieldConfig.fieldType} ${fieldConfig.fieldName};
|
||||
</#if>
|
||||
</#list>
|
||||
</#if>
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import java.io.Serial;
|
||||
<#if hasTimeField>
|
||||
import java.time.*;
|
||||
</#if>
|
||||
<#if hasBigDecimalField>
|
||||
import java.math.BigDecimal;
|
||||
</#if>
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import top.continew.starter.extension.crud.model.resp.BaseResp;
|
||||
|
||||
/**
|
||||
* ${businessName}信息
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "${businessName}信息")
|
||||
public class ${className} extends BaseResp {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
<#if fieldConfigs??>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInList>
|
||||
|
||||
/**
|
||||
* ${fieldConfig.comment}
|
||||
*/
|
||||
@Schema(description = "${fieldConfig.comment}")
|
||||
private ${fieldConfig.fieldType} ${fieldConfig.fieldName};
|
||||
</#if>
|
||||
</#list>
|
||||
</#if>
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
import ${packageName}.model.query.${classNamePrefix}Query;
|
||||
import ${packageName}.model.req.${classNamePrefix}Req;
|
||||
import ${packageName}.model.resp.${classNamePrefix}DetailResp;
|
||||
import ${packageName}.model.resp.${classNamePrefix}Resp;
|
||||
|
||||
/**
|
||||
* ${businessName}业务接口
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
public interface ${className} extends BaseService<${classNamePrefix}Resp, ${classNamePrefix}DetailResp, ${classNamePrefix}Query, ${classNamePrefix}Req> {}
|
@@ -0,0 +1,24 @@
|
||||
package ${packageName}.${subPackageName};
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;
|
||||
import ${packageName}.mapper.${classNamePrefix}Mapper;
|
||||
import ${packageName}.model.entity.${classNamePrefix}DO;
|
||||
import ${packageName}.model.query.${classNamePrefix}Query;
|
||||
import ${packageName}.model.req.${classNamePrefix}Req;
|
||||
import ${packageName}.model.resp.${classNamePrefix}DetailResp;
|
||||
import ${packageName}.model.resp.${classNamePrefix}Resp;
|
||||
import ${packageName}.service.${classNamePrefix}Service;
|
||||
|
||||
/**
|
||||
* ${businessName}业务实现
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${datetime}
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ${className} extends BaseServiceImpl<${classNamePrefix}Mapper, ${classNamePrefix}DO, ${classNamePrefix}Resp, ${classNamePrefix}DetailResp, ${classNamePrefix}Query, ${classNamePrefix}Req> implements ${classNamePrefix}Service {}
|
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
:title="title"
|
||||
:mask-closable="false"
|
||||
:esc-to-close="false"
|
||||
:modal-style="{ maxWidth: '520px' }"
|
||||
width="90%"
|
||||
@before-ok="save"
|
||||
@close="reset"
|
||||
>
|
||||
<GiForm ref="formRef" v-model="form" :options="options" :columns="columns" />
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { get${classNamePrefix}, add${classNamePrefix}, update${classNamePrefix} } from '@/apis/${apiModuleName}/${apiName}'
|
||||
import { type Columns, GiForm, type Options } from '@/components/GiForm'
|
||||
import { useForm } from '@/hooks'
|
||||
import { useDict } from '@/hooks/app'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'save-success'): void
|
||||
}>()
|
||||
|
||||
const dataId = ref('')
|
||||
const isUpdate = computed(() => !!dataId.value)
|
||||
const title = computed(() => (isUpdate.value ? '修改${businessName}' : '新增${businessName}'))
|
||||
const formRef = ref<InstanceType<typeof GiForm>>()
|
||||
|
||||
<#if hasDictField>
|
||||
const { <#list dictCodes as dictCode>${dictCode}<#if dictCode_has_next>,</#if></#list> } = useDict(<#list dictCodes as dictCode>'${dictCode}'<#if dictCode_has_next>,</#if></#list>)
|
||||
</#if>
|
||||
|
||||
const options: Options = {
|
||||
form: {},
|
||||
col: { xs: 24, sm: 24, md: 24, lg: 24, xl: 24, xxl: 24 },
|
||||
btns: { hide: true }
|
||||
}
|
||||
|
||||
const columns: Columns = reactive([
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInForm>
|
||||
{
|
||||
label: '${fieldConfig.comment}',
|
||||
field: '${fieldConfig.fieldName}',
|
||||
<#if fieldConfig.formType = 'INPUT'>
|
||||
type: 'input',
|
||||
<#elseif fieldConfig.formType = 'TEXT_AREA'>
|
||||
type: 'textarea',
|
||||
<#elseif fieldConfig.formType = 'DATE'>
|
||||
type: 'date-picker',
|
||||
<#elseif fieldConfig.formType = 'DATE_TIME'>
|
||||
type: 'time-picker',
|
||||
<#elseif fieldConfig.formType = 'INPUT_NUMBER'>
|
||||
type: 'input-number',
|
||||
<#elseif fieldConfig.formType = 'INPUT_PASSWORD'>
|
||||
type: 'input-password',
|
||||
<#elseif fieldConfig.formType = 'SWITCH'>
|
||||
type: 'switch',
|
||||
<#elseif fieldConfig.formType = 'CHECK_BOX'>
|
||||
type: 'check-group',
|
||||
<#elseif fieldConfig.formType = 'TREE_SELECT'>
|
||||
type: 'tree-select',
|
||||
<#elseif fieldConfig.formType = 'SELECT'>
|
||||
type: 'select',
|
||||
<#elseif fieldConfig.formType = 'RADIO'>
|
||||
type: 'radio-group'
|
||||
</#if>
|
||||
<#if fieldConfig.dictCode?? && fieldConfig.dictCode != ''>
|
||||
options: ${fieldConfig.dictCode},
|
||||
</#if>
|
||||
<#if fieldConfig.isRequired>
|
||||
rules: [{ required: true, message: '请输入${fieldConfig.comment}' }]
|
||||
</#if>
|
||||
},
|
||||
</#if>
|
||||
</#list>
|
||||
])
|
||||
|
||||
const { form, resetForm } = useForm({
|
||||
// todo 待补充
|
||||
})
|
||||
|
||||
// 重置
|
||||
const reset = () => {
|
||||
formRef.value?.formRef?.resetFields()
|
||||
resetForm()
|
||||
}
|
||||
|
||||
const visible = ref(false)
|
||||
// 新增
|
||||
const onAdd = () => {
|
||||
reset()
|
||||
dataId.value = ''
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
// 修改
|
||||
const onUpdate = async (id: string) => {
|
||||
reset()
|
||||
dataId.value = id
|
||||
const res = await get${classNamePrefix}(id)
|
||||
Object.assign(form, res.data)
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
// 保存
|
||||
const save = async () => {
|
||||
try {
|
||||
const isInvalid = await formRef.value?.formRef?.validate()
|
||||
if (isInvalid) return false
|
||||
if (isUpdate.value) {
|
||||
await update${classNamePrefix}(form, dataId.value)
|
||||
Message.success('修改成功')
|
||||
} else {
|
||||
await add${classNamePrefix}(form)
|
||||
Message.success('新增成功')
|
||||
}
|
||||
emit('save-success')
|
||||
return true
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ onAdd, onUpdate })
|
||||
</script>
|
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<a-drawer v-model:visible="visible" title="${businessName}详情" :width="width >= 580 ? 580 : '100%'" :footer="false">
|
||||
<a-descriptions :column="2" size="large" class="general-description">
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<a-descriptions-item label="${fieldConfig.comment}">{{ dataDetail?.${fieldConfig.fieldName} }}</a-descriptions-item>
|
||||
<#if fieldConfig.fieldName = 'createUser'>
|
||||
<a-descriptions-item label="创建人">{{ dataDetail?.createUserString }}</a-descriptions-item>
|
||||
<#elseif fieldConfig.fieldName = 'updateUser'>
|
||||
<a-descriptions-item label="修改人">{{ dataDetail?.updateUserString }}</a-descriptions-item>
|
||||
</#if>
|
||||
</#list>
|
||||
</a-descriptions>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import { type ${classNamePrefix}DetailResp, get${classNamePrefix} } from '@/apis/${apiModuleName}/${apiName}'
|
||||
|
||||
const { width } = useWindowSize()
|
||||
|
||||
const visible = ref(false)
|
||||
const dataId = ref('')
|
||||
const dataDetail = ref<${classNamePrefix}DetailResp>()
|
||||
// 查询详情
|
||||
const getDataDetail = async () => {
|
||||
const res = await get${classNamePrefix}(dataId.value)
|
||||
dataDetail.value = res.data
|
||||
}
|
||||
|
||||
// 打开详情
|
||||
const onDetail = async (id: string) => {
|
||||
dataId.value = id
|
||||
await getDataDetail()
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
defineExpose({ onDetail })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@@ -0,0 +1,65 @@
|
||||
import http from '@/utils/http'
|
||||
|
||||
const BASE_URL = '/${apiModuleName}/${apiName}'
|
||||
|
||||
export interface ${classNamePrefix}Resp {
|
||||
<#if fieldConfigs??>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInList>
|
||||
${fieldConfig.fieldName}: string
|
||||
</#if>
|
||||
</#list>
|
||||
createUserString: string
|
||||
updateUserString: string
|
||||
</#if>
|
||||
}
|
||||
export interface ${classNamePrefix}DetailResp {
|
||||
<#if fieldConfigs??>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
${fieldConfig.fieldName}: string
|
||||
</#list>
|
||||
createUserString: string
|
||||
updateUserString: string
|
||||
</#if>
|
||||
}
|
||||
export interface ${classNamePrefix}Query {
|
||||
<#if fieldConfigs??>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInQuery>
|
||||
${fieldConfig.fieldName}: string
|
||||
</#if>
|
||||
</#list>
|
||||
</#if>
|
||||
sort: Array<string>
|
||||
}
|
||||
export interface ${classNamePrefix}PageQuery extends ${classNamePrefix}Query, PageQuery {}
|
||||
|
||||
/** @desc 查询${businessName}列表 */
|
||||
export function list${classNamePrefix}(query: ${classNamePrefix}PageQuery) {
|
||||
return http.get<PageRes<${classNamePrefix}Resp[]>>(`${'$'}{BASE_URL}`, query)
|
||||
}
|
||||
|
||||
/** @desc 查询${businessName}详情 */
|
||||
export function get${classNamePrefix}(id: string) {
|
||||
return http.get<${classNamePrefix}DetailResp>(`${'$'}{BASE_URL}/${'$'}{id}`)
|
||||
}
|
||||
|
||||
/** @desc 新增${businessName} */
|
||||
export function add${classNamePrefix}(data: any) {
|
||||
return http.post(`${'$'}{BASE_URL}`, data)
|
||||
}
|
||||
|
||||
/** @desc 修改${businessName} */
|
||||
export function update${classNamePrefix}(data: any, id: string) {
|
||||
return http.put(`${'$'}{BASE_URL}/${'$'}{id}`, data)
|
||||
}
|
||||
|
||||
/** @desc 删除${businessName} */
|
||||
export function delete${classNamePrefix}(id: string) {
|
||||
return http.del(`${'$'}{BASE_URL}/${'$'}{id}`)
|
||||
}
|
||||
|
||||
/** @desc 导出${businessName} */
|
||||
export function export${classNamePrefix}(query: ${classNamePrefix}Query) {
|
||||
return http.download<any>(`${'$'}{BASE_URL}/export`, query)
|
||||
}
|
@@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<div class="table-page">
|
||||
<GiTable
|
||||
row-key="id"
|
||||
title="${businessName}管理"
|
||||
:data="dataList"
|
||||
:columns="columns"
|
||||
:loading="loading"
|
||||
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||
:pagination="pagination"
|
||||
:disabled-tools="['size']"
|
||||
:disabled-column-keys="['name']"
|
||||
@refresh="search"
|
||||
>
|
||||
<#-- 查询字段配置 -->
|
||||
<template #toolbar-left>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInQuery>
|
||||
<#if fieldConfig.formType == "SELECT"><#-- 下拉框 -->
|
||||
<a-select
|
||||
v-model="queryForm.${fieldConfig.fieldName}"
|
||||
:options="${fieldConfig.columnName}_enum"
|
||||
placeholder="请选择${fieldConfig.comment}"
|
||||
allow-clear
|
||||
style="width: 150px"
|
||||
@change="search"
|
||||
/>
|
||||
<#elseif fieldConfig.formType == "RADIO"><#-- 单选框 -->
|
||||
<a-radio-group v-model="queryForm.${fieldConfig.fieldName}" :options="${fieldConfig.dictCode}" @change="search"/>
|
||||
<#elseif fieldConfig.formType == "DATE"><#-- 日期框 -->
|
||||
<#if fieldConfig.queryType == "BETWEEN">
|
||||
<DateRangePicker v-model="queryForm.${fieldConfig.fieldName}" format="YYYY-MM-DD" @change="search" />
|
||||
<#else>
|
||||
<a-date-picker
|
||||
v-model="queryForm.${fieldConfig.fieldName}"
|
||||
placeholder="请选择${fieldConfig.comment}"
|
||||
format="YYYY-MM-DD"
|
||||
style="height: 32px"
|
||||
/>
|
||||
</#if>
|
||||
<#elseif fieldConfig.formType == "DATE_TIME"><#-- 日期时间框 -->
|
||||
<#if fieldConfig.queryType == "BETWEEN">
|
||||
<DateRangePicker v-model="queryForm.${fieldConfig.fieldName}" @change="search" />
|
||||
<#else>
|
||||
<a-date-picker
|
||||
v-model="queryForm.${fieldConfig.fieldName}"
|
||||
placeholder="请选择${fieldConfig.comment}"
|
||||
show-time
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="height: 32px"
|
||||
/>
|
||||
</#if>
|
||||
<#else>
|
||||
<a-input v-model="queryForm.${fieldConfig.fieldName}" placeholder="请输入${fieldConfig.comment}" allow-clear @change="search">
|
||||
<template #prefix><icon-search /></template>
|
||||
</a-input>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<a-button @click="reset">
|
||||
<template #icon><icon-refresh /></template>
|
||||
<template #default>重置</template>
|
||||
</a-button>
|
||||
</template>
|
||||
<template #toolbar-right>
|
||||
<a-button v-permission="['${apiModuleName}:${apiName}:add']" type="primary" @click="onAdd">
|
||||
<template #icon><icon-plus /></template>
|
||||
<template #default>新增</template>
|
||||
</a-button>
|
||||
<a-button v-permission="['${apiModuleName}:${apiName}:export']" @click="onExport">
|
||||
<template #icon><icon-download /></template>
|
||||
<template #default>导出</template>
|
||||
</a-button>
|
||||
</template>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInList>
|
||||
<#if fieldConfig.dictCode?? && fieldConfig.dictCode != "">
|
||||
<template #${fieldConfig.fieldName}="{ record }">
|
||||
<GiCellTag :value="record.${fieldConfig.fieldName}" :dict="${fieldConfig.dictCode}" />
|
||||
</template>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a-link v-permission="['${apiModuleName}:${apiName}:list']" @click="onDetail(record)">查看</a-link>
|
||||
<a-link v-permission="['${apiModuleName}:${apiName}:update']" @click="onUpdate(record)">修改</a-link>
|
||||
<a-link
|
||||
v-permission="['${apiModuleName}:${apiName}:delete']"
|
||||
status="danger"
|
||||
:disabled="record.disabled"
|
||||
@click="onDelete(record)"
|
||||
>
|
||||
删除
|
||||
</a-link>
|
||||
</a-space>
|
||||
</template>
|
||||
</GiTable>
|
||||
|
||||
<${classNamePrefix}AddModal ref="${classNamePrefix}AddModalRef" @save-success="search" />
|
||||
<${classNamePrefix}DetailDrawer ref="${classNamePrefix}DetailDrawerRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ${classNamePrefix}AddModal from './${classNamePrefix}AddModal.vue'
|
||||
import ${classNamePrefix}DetailDrawer from './${classNamePrefix}DetailDrawer.vue'
|
||||
import { type ${classNamePrefix}Resp, type ${classNamePrefix}Query, delete${classNamePrefix}, export${classNamePrefix}, list${classNamePrefix} } from '@/apis/${apiModuleName}'
|
||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||
import { useDownload, useTable } from '@/hooks'
|
||||
import { isMobile } from '@/utils'
|
||||
import has from '@/utils/has'
|
||||
import { useDict } from '@/hooks/app'
|
||||
|
||||
defineOptions({ name: '${classNamePrefix}' })
|
||||
|
||||
<#if hasDictField>
|
||||
const { <#list dictCodes as dictCode>${dictCode}<#if dictCode_has_next>,</#if></#list> } = useDict(<#list dictCodes as dictCode>'${dictCode}'<#if dictCode_has_next>,</#if></#list>)
|
||||
</#if>
|
||||
|
||||
const queryForm = reactive<${classNamePrefix}Query>({
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInQuery>
|
||||
${fieldConfig.fieldName}: undefined,
|
||||
</#if>
|
||||
</#list>
|
||||
sort: ['createTime,desc']
|
||||
})
|
||||
|
||||
const {
|
||||
tableData: dataList,
|
||||
loading,
|
||||
pagination,
|
||||
search,
|
||||
handleDelete
|
||||
} = useTable((page) => list${classNamePrefix}({ ...queryForm, ...page }), { immediate: true })
|
||||
|
||||
const columns: TableInstanceColumns[] = [
|
||||
<#if fieldConfigs??>
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInList>
|
||||
{ title: '${fieldConfig.comment}', dataIndex: '${fieldConfig.fieldName}', slotName: '${fieldConfig.fieldName}' },
|
||||
</#if>
|
||||
</#list>
|
||||
</#if>
|
||||
{
|
||||
title: '操作',
|
||||
slotName: 'action',
|
||||
width: 130,
|
||||
align: 'center',
|
||||
fixed: !isMobile() ? 'right' : undefined,
|
||||
show: has.hasPermOr(['${apiModuleName}:${apiName}:update', '${apiModuleName}:${apiName}:delete'])
|
||||
}
|
||||
]
|
||||
|
||||
// 重置
|
||||
const reset = () => {
|
||||
<#list fieldConfigs as fieldConfig>
|
||||
<#if fieldConfig.showInQuery>
|
||||
queryForm.${fieldConfig.fieldName} = undefined
|
||||
</#if>
|
||||
</#list>
|
||||
search()
|
||||
}
|
||||
|
||||
// 删除
|
||||
const onDelete = (record: ${classNamePrefix}Resp) => {
|
||||
return handleDelete(() => delete${classNamePrefix}(record.id), {
|
||||
content: `是否确定删除该条数据?`,
|
||||
showModal: true
|
||||
})
|
||||
}
|
||||
|
||||
// 导出
|
||||
const onExport = () => {
|
||||
useDownload(() => export${classNamePrefix}(queryForm))
|
||||
}
|
||||
|
||||
const ${classNamePrefix}AddModalRef = ref<InstanceType<typeof ${classNamePrefix}AddModal>>()
|
||||
// 新增
|
||||
const onAdd = () => {
|
||||
${classNamePrefix}AddModalRef.value?.onAdd()
|
||||
}
|
||||
|
||||
// 修改
|
||||
const onUpdate = (record: ${classNamePrefix}Resp) => {
|
||||
${classNamePrefix}AddModalRef.value?.onUpdate(record.id)
|
||||
}
|
||||
|
||||
const ${classNamePrefix}DetailDrawerRef = ref<InstanceType<typeof ${classNamePrefix}DetailDrawer>>()
|
||||
// 详情
|
||||
const onDetail = (record: ${classNamePrefix}Resp) => {
|
||||
${classNamePrefix}DetailDrawerRef.value?.onDetail(record.id)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
Reference in New Issue
Block a user