Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/1.8.0-uniapp

 Conflicts:
	sql/ruoyi-vue-pro.sql
	yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/index.vue.vm
This commit is contained in:
YunaiV
2022-05-04 23:44:11 +08:00
290 changed files with 28512 additions and 14831 deletions

View File

@@ -47,6 +47,10 @@
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 -->
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
@@ -79,6 +83,11 @@
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId> <!-- 加解密 -->
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-excel</artifactId>

View File

@@ -4,19 +4,18 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ZipUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenDetailRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.SchemaTableRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
import cn.iocoder.yudao.module.infra.convert.codegen.CodegenConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import cn.iocoder.yudao.module.infra.service.codegen.CodegenService;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
@@ -33,7 +32,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@@ -50,19 +48,16 @@ public class CodegenController {
@GetMapping("/db/table/list")
@ApiOperation(value = "获得数据库自带的表定义列表", notes = "会过滤掉已经导入 Codegen 的表")
@ApiImplicitParams({
@ApiImplicitParam(name = "tableName", value = "表名,模糊匹配", required = true, example = "yudao", dataTypeClass = String.class),
@ApiImplicitParam(name = "tableComment", value = "描述,模糊匹配", required = true, example = "芋道", dataTypeClass = String.class)
@ApiImplicitParam(name = "dataSourceConfigId", value = "数据源配置的编号", required = true, example = "1", dataTypeClass = Long.class),
@ApiImplicitParam(name = "name", value = "表名,模糊匹配", example = "yudao", dataTypeClass = String.class),
@ApiImplicitParam(name = "comment", value = "描述,模糊匹配", example = "芋道", dataTypeClass = String.class)
})
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
public CommonResult<List<SchemaTableRespVO>> getSchemaTableList(
@RequestParam(value = "tableName", required = false) String tableName,
@RequestParam(value = "tableComment", required = false) String tableComment) {
// 获得数据库自带的表定义列表
List<SchemaTableDO> schemaTables = codegenService.getSchemaTableList(tableName, tableComment);
// 移除在 Codegen 中,已经存在的
Set<String> existsTables = CollectionUtils.convertSet(codegenService.getCodeGenTableList(), CodegenTableDO::getTableName);
schemaTables.removeIf(table -> existsTables.contains(table.getTableName()));
return success(CodegenConvert.INSTANCE.convertList04(schemaTables));
public CommonResult<List<DatabaseTableRespVO>> getDatabaseTableList(
@RequestParam(value = "dataSourceConfigId") Long dataSourceConfigId,
@RequestParam(value = "name", required = false) String name,
@RequestParam(value = "comment", required = false) String comment) {
return success(codegenService.getDatabaseTableList(dataSourceConfigId, name, comment));
}
@GetMapping("/table/page")
@@ -85,19 +80,10 @@ public class CodegenController {
}
@ApiOperation("基于数据库的表结构,创建代码生成器的表和字段定义")
@ApiImplicitParam(name = "tableNames", value = "表名数组", required = true, example = "sys_user", dataTypeClass = List.class)
@PostMapping("/create-list-from-db")
@PostMapping("/create-list")
@PreAuthorize("@ss.hasPermission('infra:codegen:create')")
public CommonResult<List<Long>> createCodegenListFromDB(@RequestParam("tableNames") List<String> tableNames) {
return success(codegenService.createCodegenListFromDB(getLoginUserId(), tableNames));
}
@ApiOperation("基于 SQL 建表语句,创建代码生成器的表和字段定义")
@ApiImplicitParam(name = "sql", value = "SQL 建表语句", required = true, example = "sql", dataTypeClass = String.class)
@PostMapping("/create-list-from-sql")
@PreAuthorize("@ss.hasPermission('infra:codegen:create')")
public CommonResult<Long> createCodegenListFromSQL(@RequestParam("sql") String sql) {
return success(codegenService.createCodegenListFromSQL(getLoginUserId(), sql));
public CommonResult<List<Long>> createCodegenList(@Valid @RequestBody CodegenCreateListReqVO reqVO) {
return success(codegenService.createCodegenList(getLoginUserId(), reqVO));
}
@ApiOperation("更新数据库的表和字段定义")
@@ -117,19 +103,6 @@ public class CodegenController {
return success(true);
}
@ApiOperation("基于 SQL 建表语句,同步数据库的表和字段定义")
@PutMapping("/sync-from-sql")
@ApiImplicitParams({
@ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class),
@ApiImplicitParam(name = "sql", value = "SQL 建表语句", required = true, example = "sql", dataTypeClass = String.class)
})
@PreAuthorize("@ss.hasPermission('infra:codegen:update')")
public CommonResult<Boolean> syncCodegenFromSQL(@RequestParam("tableId") Long tableId,
@RequestParam("sql") String sql) {
codegenService.syncCodegenFromSQL(tableId, sql);
return success(true);
}
@ApiOperation("删除数据库的表和字段定义")
@DeleteMapping("/delete")
@ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class)

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.List;
@ApiModel("管理后台 - 基于数据库的表结构,创建代码生成器的表和字段定义 Request VO")
@Data
public class CodegenCreateListReqVO {
@ApiModelProperty(value = "数据源配置的编号", required = true, example = "1")
@NotNull(message = "数据源配置的编号不能为空")
private Long dataSourceConfigId;
@ApiModelProperty(value = "表名数组", required = true, example = "[1, 2, 3]")
@NotNull(message = "表名数组不能为空")
private List<String> tableNames;
}

View File

@@ -22,7 +22,7 @@ public class CodegenColumnBaseVO {
@ApiModelProperty(value = "字段类型", required = true, example = "int(11)")
@NotNull(message = "字段类型不能为空")
private String columnType;
private String dataType;
@ApiModelProperty(value = "字段描述", required = true, example = "年龄")
@NotNull(message = "字段描述不能为空")

View File

@@ -12,10 +12,6 @@ import javax.validation.constraints.NotNull;
@Data
public class CodegenTableBaseVO {
@ApiModelProperty(value = "导入类型", required = true, example = "1", notes = "参见 CodegenImportTypeEnum 枚举")
@NotNull(message = "导入类型不能为空")
private Integer importType;
@ApiModelProperty(value = "生成场景", required = true, example = "1", notes = "参见 CodegenSceneEnum 枚举")
@NotNull(message = "导入类型不能为空")
private Integer scene;

View File

@@ -17,6 +17,9 @@ public class CodegenTableRespVO extends CodegenTableBaseVO {
@ApiModelProperty(value = "编号", required = true, example = "1")
private Long id;
@ApiModelProperty(value = "主键编号", required = true, example = "1024")
private Integer dataSourceConfigId;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;

View File

@@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ApiModel("管理后台 - 数据库的表定义 Response VO")
@Data
public class DatabaseTableRespVO {
@ApiModelProperty(value = "表名称", required = true, example = "yuanma")
private String name;
@ApiModelProperty(value = "表描述", required = true, example = "芋道源码")
private String comment;
}

View File

@@ -1,25 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@ApiModel("管理后台 - 数据字典的表定义 Response VO")
@Data
public class SchemaTableRespVO {
@ApiModelProperty(value = "数据库", required = true, example = "yudao")
private String tableSchema;
@ApiModelProperty(value = "表名称", required = true, example = "yuanma")
private String tableName;
@ApiModelProperty(value = "表描述", required = true, example = "芋道源码")
private String tableComment;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

View File

@@ -68,15 +68,15 @@ public class ConfigController {
}
@GetMapping(value = "/get-value-by-key")
@ApiOperation(value = "根据参数键名查询参数值", notes = "敏感配置,不允许返回给前端")
@ApiOperation(value = "根据参数键名查询参数值", notes = "不可见的配置,不允许返回给前端")
@ApiImplicitParam(name = "key", value = "参数键", required = true, example = "yunai.biz.username", dataTypeClass = String.class)
public CommonResult<String> getConfigKey(@RequestParam("key") String key) {
ConfigDO config = configService.getConfigByKey(key);
if (config == null) {
return null;
}
if (config.getSensitive()) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.CONFIG_GET_VALUE_ERROR_IF_SENSITIVE);
if (config.getVisible()) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.CONFIG_GET_VALUE_ERROR_IF_VISIBLE);
}
return success(config.getValue());
}

View File

@@ -18,7 +18,7 @@ public class ConfigBaseVO {
@ApiModelProperty(value = "参数分组", required = true, example = "biz")
@NotEmpty(message = "参数分组不能为空")
@Size(max = 50, message = "参数名称不能超过50个字符")
private String group;
private String category;
@ApiModelProperty(value = "参数名称", required = true, example = "数据库名")
@NotBlank(message = "参数名称不能为空")
@@ -32,7 +32,7 @@ public class ConfigBaseVO {
@ApiModelProperty(value = "是否敏感", required = true, example = "true")
@NotNull(message = "是否敏感不能为空")
private Boolean sensitive;
private Boolean visible;
@ApiModelProperty(value = "备注", example = "备注一下很帅气!")
private String remark;

View File

@@ -18,7 +18,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
@ToString(callSuper = true)
public class ConfigPageReqVO extends PageParam {
@ApiModelProperty(value = "数名称", example = "模糊匹配")
@ApiModelProperty(value = "据源名称", example = "模糊匹配")
private String name;
@ApiModelProperty(value = "参数键名", example = "yunai.db.username", notes = "模糊匹配")

View File

@@ -0,0 +1,73 @@
package cn.iocoder.yudao.module.infra.controller.admin.db;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO;
import cn.iocoder.yudao.module.infra.convert.db.DataSourceConfigConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
import cn.iocoder.yudao.module.infra.service.db.DataSourceConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
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.validation.Valid;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Api(tags = "管理后台 - 数据源配置")
@RestController
@RequestMapping("/infra/data-source-config")
@Validated
public class DataSourceConfigController {
@Resource
private DataSourceConfigService dataSourceConfigService;
@PostMapping("/create")
@ApiOperation("创建数据源配置")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:create')")
public CommonResult<Long> createDataSourceConfig(@Valid @RequestBody DataSourceConfigCreateReqVO createReqVO) {
return success(dataSourceConfigService.createDataSourceConfig(createReqVO));
}
@PutMapping("/update")
@ApiOperation("更新数据源配置")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:update')")
public CommonResult<Boolean> updateDataSourceConfig(@Valid @RequestBody DataSourceConfigUpdateReqVO updateReqVO) {
dataSourceConfigService.updateDataSourceConfig(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@ApiOperation("删除数据源配置")
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('infra:data-source-config:delete')")
public CommonResult<Boolean> deleteDataSourceConfig(@RequestParam("id") Long id) {
dataSourceConfigService.deleteDataSourceConfig(id);
return success(true);
}
@GetMapping("/get")
@ApiOperation("获得数据源配置")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
public CommonResult<DataSourceConfigRespVO> getDataSourceConfig(@RequestParam("id") Long id) {
DataSourceConfigDO dataSourceConfig = dataSourceConfigService.getDataSourceConfig(id);
return success(DataSourceConfigConvert.INSTANCE.convert(dataSourceConfig));
}
@GetMapping("/list")
@ApiOperation("获得数据源配置列表")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
public CommonResult<List<DataSourceConfigRespVO>> getDataSourceConfigList() {
List<DataSourceConfigDO> list = dataSourceConfigService.getDataSourceConfigList();
return success(DataSourceConfigConvert.INSTANCE.convertList(list));
}
}

View File

@@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.infra.controller.admin.doc;
package cn.iocoder.yudao.module.infra.controller.admin.db;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
@@ -30,7 +30,7 @@ import java.util.Arrays;
@Api(tags = "管理后台 - 数据库文档")
@RestController
@RequestMapping("/infra/db-doc")
public class DbDocController {
public class DatabaseDocController {
@Resource
private DynamicDataSourceProperties dynamicDataSourceProperties;

View File

@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.infra.controller.admin.db.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
/**
* 数据源配置 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class DataSourceConfigBaseVO {
@ApiModelProperty(value = "数据源名称", required = true, example = "test")
@NotNull(message = "数据源名称不能为空")
private String name;
@ApiModelProperty(value = "数据源连接", required = true, example = "jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro")
@NotNull(message = "数据源连接不能为空")
private String url;
@ApiModelProperty(value = "用户名", required = true, example = "root")
@NotNull(message = "用户名不能为空")
private String username;
}

View File

@@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.infra.controller.admin.db.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
@ApiModel("管理后台 - 数据源配置创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DataSourceConfigCreateReqVO extends DataSourceConfigBaseVO {
@ApiModelProperty(value = "密码", required = true, example = "123456")
@NotNull(message = "密码不能为空")
private String password;
}

View File

@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.infra.controller.admin.db.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
@ApiModel("管理后台 - 数据源配置 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DataSourceConfigRespVO extends DataSourceConfigBaseVO {
@ApiModelProperty(value = "主键编号", required = true, example = "1024")
private Integer id;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.infra.controller.admin.db.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
@ApiModel("管理后台 - 数据源配置更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DataSourceConfigUpdateReqVO extends DataSourceConfigBaseVO {
@ApiModelProperty(value = "主键编号", required = true, example = "1024")
@NotNull(message = "主键编号不能为空")
private Long id;
@ApiModelProperty(value = "密码", required = true, example = "123456")
@NotNull(message = "密码不能为空")
private String password;
}

View File

@@ -6,12 +6,14 @@ import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenPreviewR
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.SchemaTableRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@@ -23,13 +25,27 @@ public interface CodegenConvert {
CodegenConvert INSTANCE = Mappers.getMapper(CodegenConvert.class);
// ========== InformationSchemaTableDO 和 InformationSchemaColumnDO 相关 ==========
// ========== TableInfo 相关 ==========
CodegenTableDO convert(SchemaTableDO bean);
@Mappings({
@Mapping(source = "name", target = "tableName"),
@Mapping(source = "comment", target = "tableComment"),
})
CodegenTableDO convert(TableInfo bean);
List<CodegenColumnDO> convertList(List<SchemaColumnDO> list);
List<CodegenColumnDO> convertList(List<TableField> list);
CodegenTableRespVO convert(SchemaColumnDO bean);
@Mappings({
@Mapping(source = "name", target = "columnName"),
@Mapping(source = "type", target = "dataType"),
@Mapping(source = "comment", target = "columnComment"),
@Mapping(source = "metaInfo.nullable", target = "nullable"),
@Mapping(source = "keyFlag", target = "primaryKey"),
@Mapping(source = "keyIdentityFlag", target = "autoIncrement"),
@Mapping(source = "columnType.type", target = "javaType"),
@Mapping(source = "propertyName", target = "javaField"),
})
CodegenColumnDO convert(TableField bean);
// ========== CodegenTableDO 相关 ==========
@@ -47,7 +63,7 @@ public interface CodegenConvert {
List<CodegenColumnDO> convertList03(List<CodegenUpdateReqVO.Column> columns);
List<SchemaTableRespVO> convertList04(List<SchemaTableDO> list);
List<DatabaseTableRespVO> convertList04(List<TableInfo> list);
// ========== 其它 ==========

View File

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigUpdateReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
@@ -18,12 +19,15 @@ public interface ConfigConvert {
PageResult<ConfigRespVO> convertPage(PageResult<ConfigDO> page);
@Mapping(source = "configKey", target = "key")
ConfigRespVO convert(ConfigDO bean);
@Mapping(source = "key", target = "configKey")
ConfigDO convert(ConfigCreateReqVO bean);
ConfigDO convert(ConfigUpdateReqVO bean);
@Mapping(source = "configKey", target = "key")
List<ConfigExcelVO> convertList(List<ConfigDO> list);
}

View File

@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.infra.convert.db;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
/**
* 数据源配置 Convert
*
* @author 芋道源码
*/
@Mapper
public interface DataSourceConfigConvert {
DataSourceConfigConvert INSTANCE = Mappers.getMapper(DataSourceConfigConvert.class);
DataSourceConfigDO convert(DataSourceConfigCreateReqVO bean);
DataSourceConfigDO convert(DataSourceConfigUpdateReqVO bean);
DataSourceConfigRespVO convert(DataSourceConfigDO bean);
List<DataSourceConfigRespVO> convertList(List<DataSourceConfigDO> list);
}

View File

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.infra.dal.dataobject.codegen;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnListConditionEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -15,6 +16,7 @@ import lombok.experimental.Accessors;
* @author 芋道源码
*/
@TableName(value = "infra_codegen_column", autoResultMap = true)
@KeySequence("infra_codegen_column_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@@ -41,7 +43,7 @@ public class CodegenColumnDO extends BaseDO {
/**
* 字段类型
*/
private String columnType;
private String dataType;
/**
* 字段描述
*/
@@ -74,7 +76,6 @@ public class CodegenColumnDO extends BaseDO {
/**
* Java 属性名
*/
// @NotBlank(message = "Java属性不能为空")
private String javaField;
/**
* 字典类型

View File

@@ -1,8 +1,10 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.codegen;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -14,6 +16,7 @@ import lombok.experimental.Accessors;
* @author 芋道源码
*/
@TableName(value = "infra_codegen_table", autoResultMap = true)
@KeySequence("infra_codegen_table_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@@ -25,11 +28,11 @@ public class CodegenTableDO extends BaseDO {
private Long id;
/**
* 导入类型
* 数据源编号
*
* 枚举 {@link CodegenTemplateTypeEnum}
* 关联 {@link DataSourceConfigDO#getId()}
*/
private Integer importType;
private Long dataSourceConfigId;
/**
* 生成场景
*

View File

@@ -1,54 +0,0 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.codegen;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Builder;
import lombok.Data;
/**
* MySQL 数据库中的 column 字段定义
*
* @author 芋道源码
*/
@TableName(value = "information_schema.columns", autoResultMap = true)
@Data
@Builder
public class SchemaColumnDO {
/**
* 表名称
*/
private String tableName;
/**
* 字段名
*/
private String columnName;
/**
* 字段类型
*/
private String columnType;
/**
* 字段描述
*/
private String columnComment;
/**
* 是否允许为空
*/
@TableField("case when is_nullable = 'yes' then '1' else '0' end")
private Boolean nullable;
/**
* 是否主键
*/
@TableField("case when column_key = 'PRI' then '1' else '0' end")
private Boolean primaryKey;
/**
* 是否自增
*/
@TableField("case when extra = 'auto_increment' then '1' else '0' end")
private Boolean autoIncrement;
/**
* 排序字段
*/
private Integer ordinalPosition;
}

View File

@@ -1,36 +0,0 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.codegen;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Builder;
import lombok.Data;
import java.util.Date;
/**
* MySQL 数据库中的 table 表定义
*
* @author 芋道源码
*/
@TableName(value = "information_schema.tables", autoResultMap = true)
@Data
@Builder
public class SchemaTableDO {
/**
* 数据库
*/
private String tableSchema;
/**
* 表名称
*/
private String tableName;
/**
* 表描述
*/
private String tableComment;
/**
* 创建时间
*/
private Date createTime;
}

View File

@@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.infra.dal.dataobject.config;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.infra.enums.config.ConfigTypeEnum;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -15,6 +15,7 @@ import lombok.ToString;
* @author 芋道源码
*/
@TableName("infra_config")
@KeySequence("infra_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@@ -26,19 +27,19 @@ public class ConfigDO extends BaseDO {
@TableId
private Long id;
/**
* 参数分
* 参数分
*/
@TableField("`group`")
private String group;
private String category;
/**
* 参数名称
*/
private String name;
/**
* 参数键名
*
* 支持多 DB 类型时,无法直接使用 key + @TableField("config_key") 来实现转换,原因是 "config_key" AS key 而存在报错
*/
@TableField("`key`")
private String key;
private String configKey;
/**
* 参数键值
*/
@@ -48,15 +49,13 @@ public class ConfigDO extends BaseDO {
*
* 枚举 {@link ConfigTypeEnum}
*/
@TableField("`type`")
private Integer type;
/**
* 是否敏感
* 是否可见
*
* 对于敏感配置,需要管理权限才能查看
* 不可见的参数,一般是敏感参数,前端不可获取
*/
@TableField("`sensitive`")
private Boolean sensitive;
private Boolean visible;
/**
* 备注
*/

View File

@@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.db;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 数据源配置
*
* @author 芋道源码
*/
@TableName("infra_data_source_config")
@KeySequence("infra_data_source_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
public class DataSourceConfigDO extends BaseDO {
/**
* 主键编号 - Master 数据源
*/
public static final Long ID_MASTER = 0L;
/**
* 主键编号
*/
private Long id;
/**
* 连接名
*/
private String name;
/**
* 数据源连接
*/
private String url;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
}

View File

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.infra.dal.dataobject.file;
import cn.iocoder.yudao.framework.file.core.client.FileClientConfig;
import cn.iocoder.yudao.framework.file.core.enums.FileStorageEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
@@ -13,8 +14,9 @@ import lombok.*;
*
* @author 芋道源码
*/
@Data
@TableName(value = "infra_file_config", autoResultMap = true)
@KeySequence("infra_file_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder

View File

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.infra.dal.dataobject.file;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
@@ -13,8 +14,9 @@ import lombok.*;
*
* @author 芋道源码
*/
@Data
@TableName("infra_file_content")
@KeySequence("infra_file_content_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder

View File

@@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.file;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
@@ -13,8 +13,9 @@ import java.io.InputStream;
*
* @author 芋道源码
*/
@Data
@TableName("infra_file")
@KeySequence("infra_file_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@@ -45,7 +46,6 @@ public class FileDO extends BaseDO {
*
* 通过 {@link cn.hutool.core.io.FileTypeUtil#getType(InputStream)} 获取
*/
@TableField(value = "`type`")
private String type;
/**
* 文件大小

View File

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.infra.dal.dataobject.job;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.infra.enums.job.JobStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
@@ -12,6 +13,7 @@ import lombok.*;
* @author 芋道源码
*/
@TableName("infra_job")
@KeySequence("infra_job_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)

View File

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.infra.dal.dataobject.job;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.module.infra.enums.job.JobLogStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
@@ -14,6 +15,7 @@ import java.util.Date;
* @author 芋道源码
*/
@TableName("infra_job_log")
@KeySequence("infra_job_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)

View File

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.infra.dal.dataobject.logger;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
@@ -15,6 +16,7 @@ import java.util.Date;
* @author 芋道源码
*/
@TableName("infra_api_access_log")
@KeySequence(value = "infra_api_access_log_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)

View File

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.infra.dal.dataobject.logger;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.infra.enums.logger.ApiErrorLogProcessStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
@@ -21,6 +22,7 @@ import java.util.Date;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@KeySequence(value = "infra_api_error_log_seq")
public class ApiErrorLogDO extends BaseDO {
/**

View File

@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.test;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
@@ -11,6 +12,7 @@ import lombok.*;
* @author 芋道源码
*/
@TableName("infra_test_demo")
@KeySequence("infra_test_demo_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)

View File

@@ -1,8 +1,8 @@
package cn.iocoder.yudao.module.infra.dal.mysql.codegen;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@@ -11,12 +11,14 @@ import java.util.List;
public interface CodegenColumnMapper extends BaseMapperX<CodegenColumnDO> {
default List<CodegenColumnDO> selectListByTableId(Long tableId) {
return selectList(new QueryWrapper<CodegenColumnDO>().eq("table_id", tableId)
.orderByAsc("ordinal_position"));
return selectList(new LambdaQueryWrapperX<CodegenColumnDO>()
.eq(CodegenColumnDO::getTableId, tableId)
.orderByAsc(CodegenColumnDO::getId));
}
default void deleteListByTableId(Long tableId) {
delete(new QueryWrapper<CodegenColumnDO>().eq("table_id", tableId));
delete(new LambdaQueryWrapperX<CodegenColumnDO>()
.eq(CodegenColumnDO::getTableId, tableId));
}
}

View File

@@ -2,24 +2,30 @@ package cn.iocoder.yudao.module.infra.dal.mysql.codegen;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CodegenTableMapper extends BaseMapperX<CodegenTableDO> {
default CodegenTableDO selectByTableName(String tableName) {
return selectOne(new QueryWrapper<CodegenTableDO>().eq("table_name", tableName));
default CodegenTableDO selectByTableNameAndDataSourceConfigId(String tableName, Long dataSourceConfigId) {
return selectOne(CodegenTableDO::getTableName, tableName,
CodegenTableDO::getDataSourceConfigId, dataSourceConfigId);
}
default PageResult<CodegenTableDO> selectPage(CodegenTablePageReqVO pageReqVO) {
return selectPage(pageReqVO, new QueryWrapperX<CodegenTableDO>()
.likeIfPresent("table_name", pageReqVO.getTableName())
.likeIfPresent("table_comment", pageReqVO.getTableComment())
.betweenIfPresent("create_time", pageReqVO.getBeginCreateTime(), pageReqVO.getEndCreateTime()));
return selectPage(pageReqVO, new LambdaQueryWrapperX<CodegenTableDO>()
.likeIfPresent(CodegenTableDO::getTableName, pageReqVO.getTableName())
.likeIfPresent(CodegenTableDO::getTableComment, pageReqVO.getTableComment())
.betweenIfPresent(CodegenTableDO::getCreateTime, pageReqVO.getBeginCreateTime(), pageReqVO.getEndCreateTime()));
}
default List<CodegenTableDO> selectListByDataSourceConfigId(Long dataSourceConfigId) {
return selectList(CodegenTableDO::getDataSourceConfigId, dataSourceConfigId);
}
}

View File

@@ -1,19 +0,0 @@
package cn.iocoder.yudao.module.infra.dal.mysql.codegen;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SchemaColumnMapper extends BaseMapperX<SchemaColumnDO> {
default List<SchemaColumnDO> selectListByTableName(String tableSchema, String tableName) {
return selectList(new QueryWrapper<SchemaColumnDO>().eq("table_name", tableName)
.eq("table_schema", tableSchema)
.orderByAsc("ordinal_position"));
}
}

View File

@@ -1,26 +0,0 @@
package cn.iocoder.yudao.module.infra.dal.mysql.codegen;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
@Mapper
public interface SchemaTableMapper extends BaseMapperX<SchemaTableDO> {
default List<SchemaTableDO> selectList(Collection<String> tableSchemas, String tableName, String tableComment) {
return selectList(new QueryWrapperX<SchemaTableDO>().in("table_schema", tableSchemas)
.likeIfPresent("table_name", tableName)
.likeIfPresent("table_comment", tableComment));
}
default SchemaTableDO selectByTableSchemaAndTableName(String tableSchema, String tableName) {
return selectOne(new QueryWrapper<SchemaTableDO>().eq("table_schema",tableSchema)
.eq("table_name", tableName));
}
}

View File

@@ -2,12 +2,10 @@ package cn.iocoder.yudao.module.infra.dal.mysql.config;
import cn.iocoder.yudao.framework.apollo.internals.ConfigFrameworkDAO;
import cn.iocoder.yudao.framework.apollo.internals.dto.ConfigRespDTO;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.util.Date;
import java.util.List;
@@ -26,14 +24,18 @@ public class ConfigDAOImpl implements ConfigFrameworkDAO {
}
@Override
public boolean selectExistsByUpdateTimeAfter(Date maxUpdateTime) {
return jdbcTemplate.query("SELECT id FROM infra_config WHERE update_time > ? LIMIT 1",
ResultSet::next, maxUpdateTime);
public int selectCountByUpdateTimeGt(Date maxUpdateTime) {
return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM infra_config WHERE update_time > ?",
Integer.class, maxUpdateTime);
}
@Override
public List<ConfigRespDTO> selectList() {
return jdbcTemplate.query("SELECT `key`, `value`, update_time, deleted FROM infra_config", new BeanPropertyRowMapper<>(ConfigRespDTO.class));
return jdbcTemplate.query("SELECT config_key, value, update_time, deleted FROM infra_config",
(rs, rowNum) -> new ConfigRespDTO().setKey(rs.getString("config_key"))
.setValue(rs.getString("value"))
.setUpdateTime(rs.getDate("update_time"))
.setDeleted(rs.getBoolean("deleted")));
}
}

View File

@@ -2,11 +2,10 @@ package cn.iocoder.yudao.module.infra.dal.mysql.config;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigPageReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@@ -15,23 +14,23 @@ import java.util.List;
public interface ConfigMapper extends BaseMapperX<ConfigDO> {
default ConfigDO selectByKey(String key) {
return selectOne(new QueryWrapper<ConfigDO>().eq("`key`", key));
return selectOne(ConfigDO::getConfigKey, key);
}
default PageResult<ConfigDO> selectPage(ConfigPageReqVO reqVO) {
return selectPage(reqVO, new QueryWrapperX<ConfigDO>()
.likeIfPresent("name", reqVO.getName())
.likeIfPresent("`key`", reqVO.getKey())
.eqIfPresent("`type`", reqVO.getType())
.betweenIfPresent("create_time", reqVO.getBeginTime(), reqVO.getEndTime()));
return selectPage(reqVO, new LambdaQueryWrapperX<ConfigDO>()
.likeIfPresent(ConfigDO::getName, reqVO.getName())
.likeIfPresent(ConfigDO::getConfigKey, reqVO.getKey())
.eqIfPresent(ConfigDO::getType, reqVO.getType())
.betweenIfPresent(ConfigDO::getCreateTime, reqVO.getBeginTime(), reqVO.getEndTime()));
}
default List<ConfigDO> selectList(ConfigExportReqVO reqVO) {
return selectList(new QueryWrapperX<ConfigDO>()
.likeIfPresent("name", reqVO.getName())
.likeIfPresent("`key`", reqVO.getKey())
.eqIfPresent("`type`", reqVO.getType())
.betweenIfPresent("create_time", reqVO.getBeginTime(), reqVO.getEndTime()));
return selectList(new LambdaQueryWrapperX<ConfigDO>()
.likeIfPresent(ConfigDO::getName, reqVO.getName())
.likeIfPresent(ConfigDO::getConfigKey, reqVO.getKey())
.eqIfPresent(ConfigDO::getType, reqVO.getType())
.betweenIfPresent(ConfigDO::getCreateTime, reqVO.getBeginTime(), reqVO.getEndTime()));
}
}

View File

@@ -0,0 +1,14 @@
package cn.iocoder.yudao.module.infra.dal.mysql.db;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 数据源配置 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface DataSourceConfigMapper extends BaseMapperX<DataSourceConfigDO> {
}

View File

@@ -26,7 +26,7 @@ public interface FileConfigMapper extends BaseMapperX<FileConfigDO> {
.orderByDesc(FileConfigDO::getId));
}
@Select("SELECT id FROM infra_file_config WHERE update_time > #{maxUpdateTime} LIMIT 1")
Long selectExistsByUpdateTimeAfter(Date maxUpdateTime);
@Select("SELECT COUNT(*) FROM infra_file_config WHERE update_time > #{maxUpdateTime}")
Long selectCountByUpdateTimeGt(Date maxUpdateTime);
}

View File

@@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.infra.dal.mysql.file;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
import org.apache.ibatis.annotations.Mapper;
@@ -16,11 +16,11 @@ import org.apache.ibatis.annotations.Mapper;
public interface FileMapper extends BaseMapperX<FileDO> {
default PageResult<FileDO> selectPage(FilePageReqVO reqVO) {
return selectPage(reqVO, new QueryWrapperX<FileDO>()
.likeIfPresent("path", reqVO.getPath())
.likeIfPresent("type", reqVO.getType())
.betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
.orderByDesc("create_time"));
return selectPage(reqVO, new LambdaQueryWrapperX<FileDO>()
.likeIfPresent(FileDO::getPath, reqVO.getPath())
.likeIfPresent(FileDO::getType, reqVO.getType())
.betweenIfPresent(FileDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
.orderByDesc(FileDO::getId));
}
}

View File

@@ -1,23 +0,0 @@
package cn.iocoder.yudao.module.infra.enums.codegen;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 代码生成的导入类型
*
* @author 芋道源码
*/
@AllArgsConstructor
@Getter
public enum CodegenImportTypeEnum {
DB(1), // 从 information_schema 的 table 和 columns 表导入
SQL(2); // 基于建表 SQL 语句导入
/**
* 类型
*/
private final Integer type;
}

View File

@@ -1,11 +1,12 @@
package cn.iocoder.yudao.module.infra.service.codegen;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import java.util.List;
import java.util.Map;
@@ -17,32 +18,14 @@ import java.util.Map;
*/
public interface CodegenService {
/**
* 基于 SQL 建表语句,创建代码生成器的表定义
*
* @param userId 用户编号
* @param sql SQL 建表语句
* @return 创建的表定义的编号
*/
Long createCodegenListFromSQL(Long userId, String sql);
/**
* 基于数据库的表结构,创建代码生成器的表定义
*
* @param userId 用户编号
* @param tableName 表名称
* @return 创建的表定义的编号
*/
Long createCodegen(Long userId, String tableName);
/**
* 基于 {@link #createCodegen(Long, String)} 的批量创建
*
* @param userId 用户编号
* @param tableNames 表名称数组
* @param reqVO 表信息
* @return 创建的表定义的编号数组
*/
List<Long> createCodegenListFromDB(Long userId, List<String> tableNames);
List<Long> createCodegenList(Long userId, CodegenCreateListReqVO reqVO);
/**
* 更新数据库的表和字段定义
@@ -58,14 +41,6 @@ public interface CodegenService {
*/
void syncCodegenFromDB(Long tableId);
/**
* 基于 SQL 建表语句,同步数据库的表和字段定义
*
* @param tableId 表编号
* @param sql SQL 建表语句
*/
void syncCodegenFromSQL(Long tableId, String sql);
/**
* 删除数据库的表和字段定义
*
@@ -89,13 +64,6 @@ public interface CodegenService {
*/
CodegenTableDO getCodegenTablePage(Long id);
/**
* 获得全部表定义
*
* @return 表定义数组
*/
List<CodegenTableDO> getCodeGenTableList();
/**
* 获得指定表的字段定义数组
*
@@ -115,10 +83,12 @@ public interface CodegenService {
/**
* 获得数据库自带的表定义列表
*
* @param tableName 表名称
* @param tableComment 表描述
*
* @param dataSourceConfigId 数据源的配置编号
* @param name 表名称
* @param comment 表描述
* @return 表定义列表
*/
List<SchemaTableDO> getSchemaTableList(String tableName, String tableComment);
List<DatabaseTableRespVO> getDatabaseTableList(Long dataSourceConfigId, String name, String comment);
}

View File

@@ -1,26 +1,25 @@
package cn.iocoder.yudao.module.infra.service.codegen;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
import cn.iocoder.yudao.module.infra.convert.codegen.CodegenConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.SchemaColumnMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.SchemaTableMapper;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenImportTypeEnum;
import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenBuilder;
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine;
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenSQLParser;
import cn.iocoder.yudao.module.infra.service.db.DatabaseTableService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import org.apache.commons.collections4.KeyValue;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -43,9 +42,8 @@ import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
public class CodegenServiceImpl implements CodegenService {
@Resource
private SchemaTableMapper schemaTableMapper;
@Resource
private SchemaColumnMapper schemaColumnMapper;
private DatabaseTableService databaseTableService;
@Resource
private CodegenTableMapper codegenTableMapper;
@Resource
@@ -59,68 +57,63 @@ public class CodegenServiceImpl implements CodegenService {
@Resource
private CodegenEngine codegenEngine;
@Resource
private CodegenProperties codegenProperties;
@Override
@Transactional(rollbackFor = Exception.class)
public List<Long> createCodegenList(Long userId, CodegenCreateListReqVO reqVO) {
List<Long> ids = new ArrayList<>(reqVO.getTableNames().size());
// 遍历添加。虽然效率会低一点,但是没必要做成完全批量,因为不会这么大量
reqVO.getTableNames().forEach(tableName -> ids.add(createCodegen(userId, reqVO.getDataSourceConfigId(), tableName)));
return ids;
}
private Long createCodegen0(Long userId, CodegenImportTypeEnum importType,
SchemaTableDO schemaTable, List<SchemaColumnDO> schemaColumns) {
public Long createCodegen(Long userId, Long dataSourceConfigId, String tableName) {
// 从数据库中,获得数据库表结构
TableInfo tableInfo = databaseTableService.getTable(dataSourceConfigId, tableName);
// 导入
return createCodegen0(userId, dataSourceConfigId, tableInfo);
}
private Long createCodegen0(Long userId, Long dataSourceConfigId, TableInfo tableInfo) {
// 校验导入的表和字段非空
if (schemaTable == null) {
throw exception(CODEGEN_IMPORT_TABLE_NULL);
}
if (CollUtil.isEmpty(schemaColumns)) {
throw exception(CODEGEN_IMPORT_COLUMNS_NULL);
}
checkTableInfo(tableInfo);
// 校验是否已经存在
if (codegenTableMapper.selectByTableName(schemaTable.getTableName()) != null) {
if (codegenTableMapper.selectByTableNameAndDataSourceConfigId(tableInfo.getName(),
dataSourceConfigId) != null) {
throw exception(CODEGEN_TABLE_EXISTS);
}
// 构建 CodegenTableDO 对象,插入到 DB 中
CodegenTableDO table = codegenBuilder.buildTable(schemaTable);
table.setImportType(importType.getType());
CodegenTableDO table = codegenBuilder.buildTable(tableInfo);
table.setDataSourceConfigId(dataSourceConfigId);
table.setScene(CodegenSceneEnum.ADMIN.getScene()); // 默认配置下,使用管理后台的模板
table.setAuthor(userApi.getUser(userId).getNickname());
codegenTableMapper.insert(table);
// 构建 CodegenColumnDO 数组,插入到 DB 中
List<CodegenColumnDO> columns = codegenBuilder.buildColumns(table.getId(), schemaColumns);
List<CodegenColumnDO> columns = codegenBuilder.buildColumns(table.getId(), tableInfo.getFields());
// 如果没有主键,则使用第一个字段作为主键
if (!tableInfo.isHavePrimaryKey()) {
columns.get(0).setPrimaryKey(true);
}
codegenColumnMapper.insertBatch(columns);
return table.getId();
}
@Override
public Long createCodegenListFromSQL(Long userId, String sql) {
// 从 SQL 中,获得数据库表结构
SchemaTableDO schemaTable;
List<SchemaColumnDO> schemaColumns;
try {
KeyValue<SchemaTableDO, List<SchemaColumnDO>> result = CodegenSQLParser.parse(sql);
schemaTable = result.getKey();
schemaColumns = result.getValue();
} catch (Exception ex) {
throw exception(CODEGEN_PARSE_SQL_ERROR);
private void checkTableInfo(TableInfo tableInfo) {
if (tableInfo == null) {
throw exception(CODEGEN_IMPORT_TABLE_NULL);
}
// 导入
return this.createCodegen0(userId, CodegenImportTypeEnum.SQL, schemaTable, schemaColumns);
}
@Override
public Long createCodegen(Long userId, String tableName) {
// 获取当前schema
String tableSchema = codegenProperties.getDbSchemas().iterator().next();
// 从数据库中,获得数据库表结构
SchemaTableDO schemaTable = schemaTableMapper.selectByTableSchemaAndTableName(tableSchema, tableName);
List<SchemaColumnDO> schemaColumns = schemaColumnMapper.selectListByTableName(tableSchema, tableName);
// 导入
return this.createCodegen0(userId, CodegenImportTypeEnum.DB, schemaTable, schemaColumns);
}
@Override
@Transactional(rollbackFor = Exception.class)
public List<Long> createCodegenListFromDB(Long userId, List<String> tableNames) {
List<Long> ids = new ArrayList<>(tableNames.size());
// 遍历添加。虽然效率会低一点,但是没必要做成完全批量,因为不会这么大量
tableNames.forEach(tableName -> ids.add(createCodegen(userId, tableName)));
return ids;
if (StrUtil.isEmpty(tableInfo.getComment())) {
throw exception(CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL);
}
if (CollUtil.isEmpty(tableInfo.getFields())) {
throw exception(CODEGEN_IMPORT_COLUMNS_NULL);
}
tableInfo.getFields().forEach(field -> {
if (StrUtil.isEmpty(field.getComment())) {
throw exception(CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL, field.getName());
}
});
}
@Override
@@ -147,56 +140,32 @@ public class CodegenServiceImpl implements CodegenService {
if (table == null) {
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
String tableSchema = codegenProperties.getDbSchemas().iterator().next();
// 从数据库中,获得数据库表结构
List<SchemaColumnDO> schemaColumns = schemaColumnMapper.selectListByTableName(tableSchema, table.getTableName());
TableInfo tableInfo = databaseTableService.getTable(table.getDataSourceConfigId(), table.getTableName());
// 执行同步
this.syncCodegen0(tableId, schemaColumns);
syncCodegen0(tableId, tableInfo);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void syncCodegenFromSQL(Long tableId, String sql) {
// 校验是否已经存在
CodegenTableDO table = codegenTableMapper.selectById(tableId);
if (table == null) {
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
// 从 SQL 中,获得数据库表结构
List<SchemaColumnDO> schemaColumns;
try {
KeyValue<SchemaTableDO, List<SchemaColumnDO>> result = CodegenSQLParser.parse(sql);
schemaColumns = result.getValue();
} catch (Exception ex) {
throw exception(CODEGEN_PARSE_SQL_ERROR);
}
// 执行同步
this.syncCodegen0(tableId, schemaColumns);
}
private void syncCodegen0(Long tableId, List<SchemaColumnDO> schemaColumns) {
// 校验导入的字段不为空
if (CollUtil.isEmpty(schemaColumns)) {
throw exception(CODEGEN_SYNC_COLUMNS_NULL);
}
Set<String> schemaColumnNames = CollectionUtils.convertSet(schemaColumns, SchemaColumnDO::getColumnName);
private void syncCodegen0(Long tableId, TableInfo tableInfo) {
// 校验导入的表和字段非空
checkTableInfo(tableInfo);
List<TableField> tableFields = tableInfo.getFields();
// 构建 CodegenColumnDO 数组,只同步新增的字段
List<CodegenColumnDO> codegenColumns = codegenColumnMapper.selectListByTableId(tableId);
Set<String> codegenColumnNames = CollectionUtils.convertSet(codegenColumns, CodegenColumnDO::getColumnName);
// 移除已经存在的字段
schemaColumns.removeIf(column -> codegenColumnNames.contains(column.getColumnName()));
tableFields.removeIf(column -> codegenColumnNames.contains(column.getColumnName()));
// 计算需要删除的字段
Set<Long> deleteColumnIds = codegenColumns.stream().filter(column -> !schemaColumnNames.contains(column.getColumnName()))
Set<String> tableFieldNames = CollectionUtils.convertSet(tableFields, TableField::getName);
Set<Long> deleteColumnIds = codegenColumns.stream().filter(column -> !tableFieldNames.contains(column.getColumnName()))
.map(CodegenColumnDO::getId).collect(Collectors.toSet());
if (CollUtil.isEmpty(schemaColumns) && CollUtil.isEmpty(deleteColumnIds)) {
if (CollUtil.isEmpty(tableFields) && CollUtil.isEmpty(deleteColumnIds)) {
throw exception(CODEGEN_SYNC_NONE_CHANGE);
}
// 插入新增的字段
List<CodegenColumnDO> columns = codegenBuilder.buildColumns(tableId, schemaColumns);
List<CodegenColumnDO> columns = codegenBuilder.buildColumns(tableId, tableFields);
codegenColumnMapper.insertBatch(columns);
// 删除不存在的字段
if (CollUtil.isNotEmpty(deleteColumnIds)) {
@@ -228,11 +197,6 @@ public class CodegenServiceImpl implements CodegenService {
return codegenTableMapper.selectById(id);
}
@Override
public List<CodegenTableDO> getCodeGenTableList() {
return codegenTableMapper.selectList();
}
@Override
public List<CodegenColumnDO> getCodegenColumnListByTableId(Long tableId) {
return codegenColumnMapper.selectListByTableId(tableId);
@@ -255,13 +219,18 @@ public class CodegenServiceImpl implements CodegenService {
}
@Override
public List<SchemaTableDO> getSchemaTableList(String tableName, String tableComment) {
List<SchemaTableDO> tables = schemaTableMapper.selectList(codegenProperties.getDbSchemas(), tableName, tableComment);
// TODO 强制移除 Quartz 的表,未来做成可配置
tables.removeIf(table -> table.getTableName().startsWith("QRTZ_"));
tables.removeIf(table -> table.getTableName().startsWith("ACT_"));
tables.removeIf(table -> table.getTableName().startsWith("FLW_"));
return tables;
public List<DatabaseTableRespVO> getDatabaseTableList(Long dataSourceConfigId, String name, String comment) {
List<TableInfo> tables = databaseTableService.getTableList(dataSourceConfigId, name, comment);
// 移除置顶前缀的表名 // TODO 未来做成可配置
tables.removeIf(table -> table.getName().toUpperCase().startsWith("QRTZ_"));
tables.removeIf(table -> table.getName().toUpperCase().startsWith("ACT_"));
tables.removeIf(table -> table.getName().toUpperCase().startsWith("FLW_"));
// 移除已经生成的表
// 移除在 Codegen 中,已经存在的
Set<String> existsTables = CollectionUtils.convertSet(
codegenTableMapper.selectListByDataSourceConfigId(dataSourceConfigId), CodegenTableDO::getTableName);
tables.removeIf(table -> existsTables.contains(table.getName()));
return CodegenConvert.INSTANCE.convertList04(tables);
}
}

View File

@@ -7,23 +7,22 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.infra.convert.codegen.CodegenConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnListConditionEnum;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.google.common.collect.Sets;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.*;
import static cn.hutool.core.text.CharSequenceUtil.*;
/**
* 代码生成器的 Builder负责
* 1. 将数据库的表 {@link SchemaTableDO} 定义,构建成 {@link CodegenTableDO}
* 2. 将数据库的列 {@link SchemaColumnDO} 构定义,建成 {@link CodegenColumnDO}
* 1. 将数据库的表 {@link TableInfo} 定义,构建成 {@link CodegenTableDO}
* 2. 将数据库的列 {@link TableField} 构定义,建成 {@link CodegenColumnDO}
*/
@Component
public class CodegenBuilder {
@@ -82,21 +81,6 @@ public class CodegenBuilder {
*/
private static final Set<String> LIST_OPERATION_RESULT_EXCLUDE_COLUMN = Sets.newHashSet();
/**
* Java 类型与 MySQL 类型的映射关系
*/
private static final Map<String, Set<String>> javaTypeMappings = MapUtil.<String, Set<String>>builder()
.put(Boolean.class.getSimpleName(), Sets.newHashSet("bit"))
.put(Integer.class.getSimpleName(), Sets.newHashSet("tinyint", "smallint", "mediumint", "int"))
.put(Long.class.getSimpleName(), Collections.singleton("bigint"))
.put(Double.class.getSimpleName(), Sets.newHashSet("float", "double"))
.put(BigDecimal.class.getSimpleName(), Sets.newHashSet("decimal", "numeric"))
.put(String.class.getSimpleName(), Sets.newHashSet("tinytext", "text", "mediumtext", "longtext", // 长文本
"char", "varchar", "nvarchar", "varchar2")) // 短文本
.put(Date.class.getSimpleName(), Sets.newHashSet("datetime", "time", "date", "timestamp"))
.put("byte[]", Sets.newHashSet("blob"))
.build();
static {
Arrays.stream(ReflectUtil.getFields(BaseDO.class)).forEach(field -> BASE_DO_FIELDS.add(field.getName()));
BASE_DO_FIELDS.add(TENANT_ID_FIELD);
@@ -109,8 +93,8 @@ public class CodegenBuilder {
LIST_OPERATION_RESULT_EXCLUDE_COLUMN.remove("createTime"); // 创建时间,还是需要返回的
}
public CodegenTableDO buildTable(SchemaTableDO schemaTable) {
CodegenTableDO table = CodegenConvert.INSTANCE.convert(schemaTable);
public CodegenTableDO buildTable(TableInfo tableInfo) {
CodegenTableDO table = CodegenConvert.INSTANCE.convert(tableInfo);
initTableDefault(table);
return table;
}
@@ -121,57 +105,33 @@ public class CodegenBuilder {
* @param table 表定义
*/
private void initTableDefault(CodegenTableDO table) {
// 以 system_dept 举例子。moduleName 为 system、businessName 为 dept、className 为 SystemDept
// 如果希望 System 前缀,则可以手动在【代码生成 - 修改生成配置 - 基本信息】,将实体类名称改为 Dept 即可
table.setModuleName(subBefore(table.getTableName(), '_', false)); // 第一个 _ 前缀的前面,作为 module 名字
table.setBusinessName(toCamelCase(subAfter(table.getTableName(),
'_', false))); // 第一步,第一个 _ 前缀的后面,作为 module 名字; 第二步,可能存在多个 _ 的情况,转换成驼峰
table.setClassName(upperFirst(toCamelCase( // 驼峰 + 首字母大写
subAfter(table.getTableName(), '_', false)))); // 第一个 _ 前缀的前面,作为 class 名字
table.setClassComment(subBefore(table.getTableComment(), // 去除结尾的表,作为类描述
'', true));
// 以 system_dept 举例子。moduleName 为 system、businessName 为 dept、className 为 Dept
// 如果希望 System 前缀,则可以手动在【代码生成 - 修改生成配置 - 基本信息】,将实体类名称改为 SystemDept 即可
String tableName = table.getTableName().toLowerCase();
// 第一步_ 前缀的前面,作为 module 名字第二步moduleName 必须小写;
table.setModuleName(subBefore(tableName, '_', false).toLowerCase());
// 第一步,第一个 _ 前缀的后面,作为 module 名字; 第二步,可能存在多个 _ 的情况,转换成驼峰; 第三步businessName 必须小写;
table.setBusinessName(toCamelCase(subAfter(tableName, '_', false)).toLowerCase());
// 驼峰 + 首字母大写;第一步,第一个 _ 前缀的后面,作为 class 名字;第二步,驼峰命名
table.setClassName(upperFirst(toCamelCase(subAfter(tableName, '_', false))));
// 去除结尾的表,作为类描述
table.setClassComment(StrUtil.removeSuffixIgnoreCase(table.getTableComment(), ""));
table.setTemplateType(CodegenTemplateTypeEnum.CRUD.getType());
}
public List<CodegenColumnDO> buildColumns(Long tableId, List<SchemaColumnDO> schemaColumns) {
List<CodegenColumnDO> columns = CodegenConvert.INSTANCE.convertList(schemaColumns);
public List<CodegenColumnDO> buildColumns(Long tableId, List<TableField> tableFields) {
List<CodegenColumnDO> columns = CodegenConvert.INSTANCE.convertList(tableFields);
int index = 1;
for (CodegenColumnDO column : columns) {
column.setTableId(tableId);
initColumnDefault(column);
column.setOrdinalPosition(index++);
// 初始化 Column 列的默认字段
processColumnOperation(column); // 处理 CRUD 相关的字段的默认值
processColumnUI(column); // 处理 UI 相关的字段的默认值
}
return columns;
}
/**
* 初始化 Column 列的默认字段
*
* @param column 列定义
*/
private void initColumnDefault(CodegenColumnDO column) {
// 处理 Java 相关的字段的默认值
processColumnJava(column);
// 处理 CRUD 相关的字段的默认值
processColumnOperation(column);
// 处理 UI 相关的字段的默认值
processColumnUI(column);
}
private void processColumnJava(CodegenColumnDO column) {
// 处理 javaField 字段
column.setJavaField(toCamelCase(column.getColumnName()));
// 处理 dictType 字段,暂无
// 处理 javaType 字段(兼容无符号类型)
String dbType = replaceIgnoreCase(subBefore(column.getColumnType(), '(', false),
" UNSIGNED", "");
javaTypeMappings.entrySet().stream()
.filter(entry -> entry.getValue().contains(dbType))
.findFirst().ifPresent(entry -> column.setJavaType(entry.getKey()));
if (column.getJavaType() == null) {
throw new IllegalStateException(String.format("column(%s) 的数据库类型(%s) 找不到匹配的 Java 类型",
column.getColumnName(), column.getColumnType()));
}
}
private void processColumnOperation(CodegenColumnDO column) {
// 处理 createOperation 字段
column.setCreateOperation(!CREATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())

View File

@@ -217,7 +217,7 @@ public class CodegenEngine {
private static String mapperXmlFilePath() {
return "yudao-module-${table.moduleName}/" + // 顶级模块
"yudao-module-${table.moduleName}-biz/" + // 子模块
"src/main/java/resources/mapper/${table.businessName}/${table.className}Mapper.xml";
"src/main/resources/mapper/${table.businessName}/${table.className}Mapper.xml";
}
private static String vueTemplatePath(String path) {

View File

@@ -1,117 +0,0 @@
package cn.iocoder.yudao.module.infra.service.codegen.inner;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLPrimaryKey;
import com.alibaba.druid.sql.ast.statement.SQLTableElement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
import com.alibaba.druid.sql.repository.SchemaRepository;
import org.apache.commons.collections4.KeyValue;
import org.apache.commons.collections4.keyvalue.DefaultKeyValue;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static com.alibaba.druid.sql.SQLUtils.normalize;
/**
* SQL 解析器,将创建表的 SQL解析成 {@link SchemaTableDO} 和 {@link SchemaColumnDO} 对象,
* 后续可以基于它们,生成代码~
*
* @author 芋道源码
*/
public class CodegenSQLParser {
/**
* 解析建表 SQL 语句,返回 {@link SchemaTableDO} 和 {@link SchemaColumnDO} 对象
*
* @param sql 建表 SQL 语句
* @return 解析结果
*/
public static KeyValue<SchemaTableDO, List<SchemaColumnDO>> parse(String sql) {
// 解析 SQL 成 Statement
SQLCreateTableStatement statement = parseCreateSQL(sql);
// 解析 Table 表
SchemaTableDO table = parseTable(statement);
// 解析 Column 字段
List<SchemaColumnDO> columns = parseColumns(statement);
columns.forEach(column -> column.setTableName(table.getTableName()));
// 返回
return new DefaultKeyValue<>(table, columns);
}
/**
* 使用 Druid 工具,建表 SQL 语句
*
* @param sql 建表 SQL 语句
* @return 创建 Statement
*/
private static SQLCreateTableStatement parseCreateSQL(String sql) {
// 解析 SQL
SchemaRepository repository = new SchemaRepository(DbType.mysql);
repository.console(sql);
// 获得该表对应的 MySqlCreateTableStatement 对象
String tableName = CollUtil.getFirst(repository.getDefaultSchema().getObjects()).getName();
return (MySqlCreateTableStatement) repository.findTable(tableName).getStatement();
}
private static SchemaTableDO parseTable(SQLCreateTableStatement statement) {
return SchemaTableDO.builder()
.tableName(statement.getTableSource().getTableName(true))
.tableComment(getCommentText(statement))
.build();
}
private static String getCommentText(SQLCreateTableStatement statement) {
if (statement == null || statement.getComment() == null) {
return "";
}
return ((SQLCharExpr) statement.getComment()).getText();
}
private static List<SchemaColumnDO> parseColumns(SQLCreateTableStatement statement) {
List<SchemaColumnDO> columns = new ArrayList<>();
statement.getTableElementList().forEach(element -> parseColumn(columns, element));
return columns;
}
private static void parseColumn(List<SchemaColumnDO> columns, SQLTableElement element) {
// 处理主键
if (element instanceof SQLPrimaryKey) {
parsePrimaryKey(columns, (SQLPrimaryKey) element);
return;
}
// 处理字段定义
if (element instanceof SQLColumnDefinition) {
parseColumnDefinition(columns, (SQLColumnDefinition) element);
}
}
private static void parsePrimaryKey(List<SchemaColumnDO> columns, SQLPrimaryKey primaryKey) {
String columnName = normalize(primaryKey.getColumns().get(0).toString()); // 暂时不考虑联合主键
// 匹配 columns 主键字段,设置为 primary
columns.stream().filter(column -> column.getColumnName().equals(columnName))
.forEach(column -> column.setPrimaryKey(true));
}
private static void parseColumnDefinition(List<SchemaColumnDO> columns, SQLColumnDefinition definition) {
String text = definition.toString().toUpperCase();
columns.add(SchemaColumnDO.builder()
.columnName(normalize(definition.getColumnName()))
.columnType(definition.getDataType().toString())
.columnComment(Objects.isNull(definition.getComment()) ? ""
: normalize(definition.getComment().toString()))
.nullable(!text.contains(" NOT NULL"))
.primaryKey(false)
.autoIncrement(text.contains("AUTO_INCREMENT"))
.ordinalPosition(columns.size() + 1)
.build());
}
}

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.infra.service.config;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigCreateReqVO;
@@ -96,7 +97,9 @@ public class ConfigServiceImpl implements ConfigService {
// 校验自己存在
checkConfigExists(id);
// 校验参数配置 key 的唯一性
checkConfigKeyUnique(id, key);
if (StrUtil.isNotEmpty(key)) {
checkConfigKeyUnique(id, key);
}
}
@VisibleForTesting

View File

@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.infra.service.db;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
import javax.validation.Valid;
import java.util.List;
/**
* 数据源配置 Service 接口
*
* @author 芋道源码
*/
public interface DataSourceConfigService {
/**
* 创建数据源配置
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createDataSourceConfig(@Valid DataSourceConfigCreateReqVO createReqVO);
/**
* 更新数据源配置
*
* @param updateReqVO 更新信息
*/
void updateDataSourceConfig(@Valid DataSourceConfigUpdateReqVO updateReqVO);
/**
* 删除数据源配置
*
* @param id 编号
*/
void deleteDataSourceConfig(Long id);
/**
* 获得数据源配置
*
* @param id 编号
* @return 数据源配置
*/
DataSourceConfigDO getDataSourceConfig(Long id);
/**
* 获得数据源配置列表
*
* @return 数据源配置列表
*/
List<DataSourceConfigDO> getDataSourceConfigList();
}

View File

@@ -0,0 +1,118 @@
package cn.iocoder.yudao.module.infra.service.db;
import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO;
import cn.iocoder.yudao.module.infra.convert.db.DataSourceConfigConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
import cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_OK;
/**
* 数据源配置 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class DataSourceConfigServiceImpl implements DataSourceConfigService {
@Resource
private DataSourceConfigMapper dataSourceConfigMapper;
@Resource
private StringEncryptor stringEncryptor;
@Resource
private DynamicDataSourceProperties dynamicDataSourceProperties;
@Override
public Long createDataSourceConfig(DataSourceConfigCreateReqVO createReqVO) {
DataSourceConfigDO dataSourceConfig = DataSourceConfigConvert.INSTANCE.convert(createReqVO);
checkConnectionOK(dataSourceConfig);
// 插入
dataSourceConfig.setPassword(stringEncryptor.encrypt(createReqVO.getPassword()));
dataSourceConfigMapper.insert(dataSourceConfig);
// 返回
return dataSourceConfig.getId();
}
@Override
public void updateDataSourceConfig(DataSourceConfigUpdateReqVO updateReqVO) {
// 校验存在
validateDataSourceConfigExists(updateReqVO.getId());
DataSourceConfigDO updateObj = DataSourceConfigConvert.INSTANCE.convert(updateReqVO);
checkConnectionOK(updateObj);
// 更新
updateObj.setPassword(stringEncryptor.encrypt(updateObj.getPassword()));
dataSourceConfigMapper.updateById(updateObj);
}
@Override
public void deleteDataSourceConfig(Long id) {
// 校验存在
validateDataSourceConfigExists(id);
// 删除
dataSourceConfigMapper.deleteById(id);
}
private void validateDataSourceConfigExists(Long id) {
if (dataSourceConfigMapper.selectById(id) == null) {
throw exception(DATA_SOURCE_CONFIG_NOT_EXISTS);
}
}
@Override
public DataSourceConfigDO getDataSourceConfig(Long id) {
// 如果 id 为 0默认为 master 的数据源
if (Objects.equals(id, DataSourceConfigDO.ID_MASTER)) {
return buildMasterDataSourceConfig();
}
// 从 DB 中读取
DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(id);
try {
dataSourceConfig.setPassword(stringEncryptor.decrypt(dataSourceConfig.getPassword()));
} catch (Exception ignore) { // 解码失败,则不解码
}
return dataSourceConfig;
}
@Override
public List<DataSourceConfigDO> getDataSourceConfigList() {
List<DataSourceConfigDO> result = dataSourceConfigMapper.selectList();
// 补充 master 数据源
result.add(0, buildMasterDataSourceConfig());
return result;
}
private void checkConnectionOK(DataSourceConfigDO config) {
boolean success = JdbcUtils.isConnectionOK(config.getUrl(), config.getUsername(), config.getPassword());
if (!success) {
throw exception(DATA_SOURCE_CONFIG_NOT_OK);
}
}
private DataSourceConfigDO buildMasterDataSourceConfig() {
String primary = dynamicDataSourceProperties.getPrimary();
DataSourceProperty dataSourceProperty = dynamicDataSourceProperties.getDatasource().get(primary);
return new DataSourceConfigDO().setId(DataSourceConfigDO.ID_MASTER).setName(primary)
.setUrl(dataSourceProperty.getUrl())
.setUsername(dataSourceProperty.getUsername())
.setPassword(dataSourceProperty.getPassword());
}
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.infra.service.db;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import java.util.List;
/**
* 数据库表 Service
*
* @author 芋道源码
*/
public interface DatabaseTableService {
/**
* 获得表列表,基于表名称 + 表描述进行模糊匹配
*
* @param dataSourceConfigId 数据源配置的编号
* @param nameLike 表名称,模糊匹配
* @param commentLike 表描述,模糊匹配
* @return 表列表
*/
List<TableInfo> getTableList(Long dataSourceConfigId, String nameLike, String commentLike);
/**
* 获得指定表名
*
* @param dataSourceConfigId 数据源配置的编号
* @param tableName 表名称
* @return 表
*/
TableInfo getTable(Long dataSourceConfigId, String tableName);
}

View File

@@ -0,0 +1,65 @@
package cn.iocoder.yudao.module.infra.service.db;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* 数据库表 Service 实现类
*
* @author 芋道源码
*/
@Service
public class DatabaseTableServiceImpl implements DatabaseTableService {
@Resource
private DataSourceConfigService dataSourceConfigService;
@Override
public List<TableInfo> getTableList(Long dataSourceConfigId, String nameLike, String commentLike) {
List<TableInfo> tables = getTableList0(dataSourceConfigId, null);
return tables.stream().filter(tableInfo -> (StrUtil.isEmpty(nameLike) || tableInfo.getName().contains(nameLike))
&& (StrUtil.isEmpty(commentLike) || tableInfo.getComment().contains(commentLike)))
.collect(Collectors.toList());
}
@Override
public TableInfo getTable(Long dataSourceConfigId, String name) {
return CollUtil.getFirst(getTableList0(dataSourceConfigId, name));
}
public List<TableInfo> getTableList0(Long dataSourceConfigId, String name) {
// 获得数据源配置
DataSourceConfigDO config = dataSourceConfigService.getDataSourceConfig(dataSourceConfigId);
Assert.notNull(config, "数据源({}) 不存在!", dataSourceConfigId);
// 使用 MyBatis Plus Generator 解析表结构
DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(config.getUrl(), config.getUsername(),
config.getPassword()).build();
StrategyConfig.Builder strategyConfig = new StrategyConfig.Builder();
if (StrUtil.isNotEmpty(name)) {
strategyConfig.addInclude(name);
}
GlobalConfig globalConfig = new GlobalConfig.Builder().dateType(DateType.ONLY_DATE).build(); // 只使用 Date 类型,不使用 LocalDate
ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, strategyConfig.build(),
null, globalConfig, null);
// 按照名字排序
List<TableInfo> tables = builder.getTableInfoList();
tables.sort(Comparator.comparing(TableInfo::getName));
return tables;
}
}

View File

@@ -123,7 +123,7 @@ public class FileConfigServiceImpl implements FileConfigService {
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
log.info("[loadFileConfigIfUpdate][首次加载全量文件配置]");
} else { // 判断数据库中是否有更新的文件配置
if (fileConfigMapper.selectExistsByUpdateTimeAfter(maxUpdateTime) == null) {
if (fileConfigMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
return null;
}
log.info("[loadFileConfigIfUpdate][增量加载全量文件配置]");

View File

@@ -7,7 +7,7 @@ import javax.validation.constraints.*;
## 处理 Date 字段的引入
#foreach ($column in $columns)
#if (${column.updateOperation} && (!${column.createOperation} || !${column.listOperationResult})
&& ${column.javaType} == "Date"))## 时间类型
&& ${column.javaType} == "Date")## 时间类型
import org.springframework.format.annotation.DateTimeFormat;
import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;

View File

@@ -10,7 +10,8 @@ import ${BaseDOClassName};
*
* @author ${table.author}
*/
@TableName("${table.tableName}")
@TableName("${table.tableName.toLowerCase()}")
@KeySequence("${table.tableName.toLowerCase()}_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)

View File

@@ -1,23 +1,34 @@
-- 将该建表 SQL 语句,添加到 yudao-module-${table.moduleName}-biz 模块的 test/resources/sql/create_tables.sql 文件里
CREATE TABLE IF NOT EXISTS "${table.tableName}" (
CREATE TABLE IF NOT EXISTS "${table.tableName.toLowerCase()}" (
#foreach ($column in $columns)
#if (${column.javaType} == 'Long')
#set ($dataType='bigint')
#elseif (${column.javaType} == 'Integer')
#set ($dataType='int')
#elseif (${column.javaType} == 'Boolean')
#set ($dataType='bit')
#elseif (${column.javaType} == 'Date')
#set ($dataType='datetime')
#else
#set ($dataType='varchar')
#end
#if (${column.primaryKey})##处理主键
"${column.javaField}"#if (${column.javaType} == 'String') ${column.columnType} NOT NULL#else ${column.columnType} NOT NULL GENERATED BY DEFAULT AS IDENTITY#end,
"${column.javaField}"#if (${column.javaType} == 'String') ${dataType} NOT NULL#else ${dataType} NOT NULL GENERATED BY DEFAULT AS IDENTITY#end,
#else
#if (${column.columnName} == 'create_time')
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
#elseif (${column.columnName} == 'update_time')
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
#elseif (${column.columnName} == 'creator' || ${column.columnName} == 'updater')
"${column.columnName}" ${column.columnType} DEFAULT '',
"${column.columnName}" ${dataType} DEFAULT '',
#elseif (${column.columnName} == 'deleted')
"deleted" bit NOT NULL DEFAULT FALSE,
#else
"${column.columnName}" ${column.columnType}#if (${column.nullable} == false) NOT NULL#end,
"${column.columnName.toLowerCase()}" ${dataType}#if (${column.nullable} == false) NOT NULL#end,
#end
#end
#end
PRIMARY KEY ("${primaryColumn.columnName}")
PRIMARY KEY ("${primaryColumn.columnName.toLowerCase()}")
) COMMENT '${table.tableComment}';
-- 将该删表 SQL 语句,添加到 yudao-module-${table.moduleName}-biz 模块的 test/resources/sql/clean.sql 文件里

View File

@@ -1,7 +1,7 @@
-- 菜单 SQL
INSERT INTO `system_menu`(
`name`, `permission`, `menu_type`, `sort`, `parent_id`,
`path`, `icon`, `component`, `status`
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
@@ -16,9 +16,9 @@ SELECT @parentId := LAST_INSERT_ID();
#set ($functionOps = ['query', 'create', 'update', 'delete', 'export'])
#foreach ($functionName in $functionNames)
#set ($index = $foreach.count - 1)
INSERT INTO `system_menu`(
`name`, `permission`, `menu_type`, `sort`, `parent_id`,
`path`, `icon`, `component`, `status`
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,

View File

@@ -160,7 +160,7 @@
</el-form-item>
#elseif($column.htmlType == "datetime")## 时间框
<el-form-item label="${comment}" prop="${javaField}">
<el-date-picker clearable v-model="form.${javaField}" type="date" value-format="yyyy-MM-dd" placeholder="选择${comment}" />
<el-date-picker clearable v-model="form.${javaField}" type="date" value-format="timestamp" placeholder="选择${comment}" />
</el-form-item>
#elseif($column.htmlType == "textarea")## 文本框
<el-form-item label="${comment}" prop="${javaField}">
@@ -390,7 +390,7 @@ export default {
this.exportLoading = true;
return export${simpleClassName}Excel(params);
}).then(response => {
this.$download.excel(response, "${table.classComment}.xls");
this.#[[$]]#download.excel(response, '${table.classComment}.xls');
this.exportLoading = false;
}).catch(() => {});
}

View File

@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.infra.service;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.generator.IDatabaseQuery.DefaultDatabaseQuery;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import java.util.List;
public class DefaultDatabaseQueryTest {
public static void main(String[] args) {
// DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:oracle:thin:@127.0.0.1:1521:xe",
// "root", "123456").build();
DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro",
"root", "123456").build();
// StrategyConfig strategyConfig = new StrategyConfig.Builder().build();
ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, null, null, null, null);
DefaultDatabaseQuery query = new DefaultDatabaseQuery(builder);
long time = System.currentTimeMillis();
List<TableInfo> tableInfos = query.queryTables();
for (TableInfo tableInfo : tableInfos) {
if (StrUtil.startWithAny(tableInfo.getName().toLowerCase(), "act_", "flw_", "qrtz_")) {
continue;
}
// System.out.println(String.format("CREATE SEQUENCE %s_seq MINVALUE 0;", tableInfo.getName()));
System.out.println(String.format("DELETE FROM %s WHERE deleted = '1';", tableInfo.getName()));
}
System.out.println(tableInfos.size());
System.out.println(System.currentTimeMillis() - time);
}
}

View File

@@ -137,7 +137,7 @@ public class ConfigServiceTest extends BaseDbUnitTest {
// 准备参数
String key = randomString();
// mock 数据
configMapper.insert(randomConfigDO(o -> o.setKey(key)));
configMapper.insert(randomConfigDO(o -> o.setConfigKey(key)));
// 调用,校验异常
assertServiceException(() -> configService.checkConfigKeyUnique(null, key),
@@ -150,7 +150,7 @@ public class ConfigServiceTest extends BaseDbUnitTest {
Long id = randomLongId();
String key = randomString();
// mock 数据
configMapper.insert(randomConfigDO(o -> o.setKey(key)));
configMapper.insert(randomConfigDO(o -> o.setConfigKey(key)));
// 调用,校验异常
assertServiceException(() -> configService.checkConfigKeyUnique(id, key),
@@ -162,7 +162,7 @@ public class ConfigServiceTest extends BaseDbUnitTest {
// mock 数据
ConfigDO dbConfig = randomConfigDO(o -> { // 等会查询到
o.setName("芋艿");
o.setKey("yunai");
o.setConfigKey("yunai");
o.setType(ConfigTypeEnum.SYSTEM.getType());
o.setCreateTime(buildTime(2021, 2, 1));
});
@@ -170,7 +170,7 @@ public class ConfigServiceTest extends BaseDbUnitTest {
// 测试 name 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setName("土豆")));
// 测试 key 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setKey("tudou")));
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setConfigKey("tudou")));
// 测试 type 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setType(ConfigTypeEnum.CUSTOM.getType())));
// 测试 createTime 不匹配
@@ -196,7 +196,7 @@ public class ConfigServiceTest extends BaseDbUnitTest {
// mock 数据
ConfigDO dbConfig = randomConfigDO(o -> { // 等会查询到
o.setName("芋艿");
o.setKey("yunai");
o.setConfigKey("yunai");
o.setType(ConfigTypeEnum.SYSTEM.getType());
o.setCreateTime(buildTime(2021, 2, 1));
});
@@ -204,7 +204,7 @@ public class ConfigServiceTest extends BaseDbUnitTest {
// 测试 name 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setName("土豆")));
// 测试 key 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setKey("tudou")));
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setConfigKey("tudou")));
// 测试 type 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setType(ConfigTypeEnum.CUSTOM.getType())));
// 测试 createTime 不匹配
@@ -230,7 +230,7 @@ public class ConfigServiceTest extends BaseDbUnitTest {
ConfigDO dbConfig = randomConfigDO();
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
// 准备参数
String key = dbConfig.getKey();
String key = dbConfig.getConfigKey();
// 调用
ConfigDO config = configService.getConfigByKey(key);

View File

@@ -0,0 +1,123 @@
package cn.iocoder.yudao.module.infra.service.db;
import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
import cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import org.jasypt.encryption.StringEncryptor;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* {@link DataSourceConfigServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(DataSourceConfigServiceImpl.class)
public class DataSourceConfigServiceImplTest extends BaseDbUnitTest {
@Resource
private DataSourceConfigServiceImpl dataSourceConfigService;
@Resource
private DataSourceConfigMapper dataSourceConfigMapper;
@MockBean
private StringEncryptor stringEncryptor;
@MockBean
private DynamicDataSourceProperties dynamicDataSourceProperties;
@Test
public void testCreateDataSourceConfig_success() {
try (MockedStatic<JdbcUtils> databaseUtilsMock = mockStatic(JdbcUtils.class)) {
// 准备参数
DataSourceConfigCreateReqVO reqVO = randomPojo(DataSourceConfigCreateReqVO.class);
// mock 方法
when(stringEncryptor.encrypt(eq(reqVO.getPassword()))).thenReturn("123456");
databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()),
eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true);
// 调用
Long dataSourceConfigId = dataSourceConfigService.createDataSourceConfig(reqVO);
// 断言
assertNotNull(dataSourceConfigId);
// 校验记录的属性是否正确
DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(dataSourceConfigId);
assertPojoEquals(reqVO, dataSourceConfig, "password");
assertEquals("123456", dataSourceConfig.getPassword());
}
}
@Test
public void testUpdateDataSourceConfig_success() {
try (MockedStatic<JdbcUtils> databaseUtilsMock = mockStatic(JdbcUtils.class)) {
// mock 数据
DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class);
dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据
// 准备参数
DataSourceConfigUpdateReqVO reqVO = randomPojo(DataSourceConfigUpdateReqVO.class, o -> {
o.setId(dbDataSourceConfig.getId()); // 设置更新的 ID
});
// mock 方法
when(stringEncryptor.encrypt(eq(reqVO.getPassword()))).thenReturn("123456");
databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()),
eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true);
// 调用
dataSourceConfigService.updateDataSourceConfig(reqVO);
// 校验是否更新正确
DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, dataSourceConfig, "password");
assertEquals("123456", dataSourceConfig.getPassword());
}
}
@Test
public void testUpdateDataSourceConfig_notExists() {
// 准备参数
DataSourceConfigUpdateReqVO reqVO = randomPojo(DataSourceConfigUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> dataSourceConfigService.updateDataSourceConfig(reqVO), DATA_SOURCE_CONFIG_NOT_EXISTS);
}
@Test
public void testDeleteDataSourceConfig_success() {
// mock 数据
DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class);
dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDataSourceConfig.getId();
// 调用
dataSourceConfigService.deleteDataSourceConfig(id);
// 校验数据不存在了
assertNull(dataSourceConfigMapper.selectById(id));
}
@Test
public void testDeleteDataSourceConfig_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> dataSourceConfigService.deleteDataSourceConfig(id), DATA_SOURCE_CONFIG_NOT_EXISTS);
}
}

View File

@@ -9,3 +9,4 @@ DELETE FROM "infra_file";
DELETE FROM "infra_api_error_log";
DELETE FROM "infra_test_demo";
DELETE FROM "infra_file_config";
DELETE FROM "infra_data_source_config";

View File

@@ -168,3 +168,17 @@ CREATE TABLE IF NOT EXISTS "infra_test_demo" (
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '字典类型表';
CREATE TABLE IF NOT EXISTS "infra_data_source_config" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(100) NOT NULL,
"url" varchar(1024) NOT NULL,
"username" varchar(255) NOT NULL,
"password" varchar(255) NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '数据源配置表';