!149 代码生成器支持 MySQL、Oracle、PostgreSQL、SQLServer、DM 等数据库

Merge pull request !149 from 芋道源码/feature/1.6.2
pull/2/head
芋道源码 2022-04-29 16:08:29 +00:00 committed by Gitee
commit 99137289e1
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
58 changed files with 1709 additions and 1112 deletions

File diff suppressed because one or more lines are too long

View File

@ -25,6 +25,7 @@
<mysql.version>5.1.46</mysql.version>
<druid.version>1.2.8</druid.version>
<mybatis-plus.version>3.4.3.4</mybatis-plus.version>
<mybatis-plus-generator.version>3.5.2</mybatis-plus-generator.version>
<dynamic-datasource.version>3.5.0</dynamic-datasource.version>
<redisson.version>3.17.0</redisson.version>
<!-- Config 配置中心相关 -->
@ -193,6 +194,11 @@
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 -->
<version>${mybatis-plus-generator.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 -->

View File

@ -38,6 +38,11 @@
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>

View File

@ -4,16 +4,16 @@ import java.sql.Connection;
import java.sql.DriverManager;
/**
*
* JDBC
*
* @author
*/
public class DatabaseUtils {
public class JdbcUtils {
/**
*
*
* @param url
* @param url
* @param username
* @param password
* @return

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>

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

@ -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

@ -12,8 +12,8 @@ import javax.validation.constraints.*;
@Data
public class DataSourceConfigBaseVO {
@ApiModelProperty(value = "数名称", required = true, example = "test")
@NotNull(message = "数名称不能为空")
@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")

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

@ -41,7 +41,7 @@ public class CodegenColumnDO extends BaseDO {
/**
*
*/
private String columnType;
private String dataType;
/**
*
*/
@ -74,7 +74,6 @@ public class CodegenColumnDO extends BaseDO {
/**
* Java
*/
// @NotBlank(message = "Java属性不能为空")
private String javaField;
/**
*

View File

@ -1,6 +1,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.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.TableName;
@ -25,11 +26,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

@ -13,6 +13,11 @@ import lombok.Data;
@Data
public class DataSourceConfigDO extends BaseDO {
/**
* - Master
*/
public static final Long ID_MASTER = 0L;
/**
*
*/

View File

@ -3,16 +3,18 @@ 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.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) {
@ -22,4 +24,8 @@ public interface CodegenTableMapper extends BaseMapperX<CodegenTableDO> {
.betweenIfPresent("create_time", 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

@ -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

@ -3,24 +3,21 @@ package cn.iocoder.yudao.module.infra.service.codegen;
import cn.hutool.core.collection.CollUtil;
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.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 +40,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,70 +55,47 @@ 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) {
if (tableInfo == null) {
throw exception(CODEGEN_IMPORT_TABLE_NULL);
}
if (CollUtil.isEmpty(schemaColumns)) {
if (CollUtil.isEmpty(tableInfo.getFields())) {
throw exception(CODEGEN_IMPORT_COLUMNS_NULL);
}
// 校验是否已经存在
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.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());
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);
}
// 导入
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;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateCodegen(CodegenUpdateReqVO updateReqVO) {
@ -147,56 +120,34 @@ 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) {
private void syncCodegen0(Long tableId, TableInfo tableInfo) {
// 校验导入的字段不为空
if (CollUtil.isEmpty(schemaColumns)) {
List<TableField> tableFields = tableInfo.getFields();
if (CollUtil.isEmpty(tableFields)) {
throw exception(CODEGEN_SYNC_COLUMNS_NULL);
}
Set<String> schemaColumnNames = CollectionUtils.convertSet(schemaColumns, SchemaColumnDO::getColumnName);
Set<String> tableFieldNames = CollectionUtils.convertSet(tableFields, TableField::getName);
// 构建 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<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 +179,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 +201,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().startsWith("QRTZ_"));
tables.removeIf(table -> table.getName().startsWith("ACT_"));
tables.removeIf(table -> table.getName().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;
}
@ -133,45 +117,19 @@ public class CodegenBuilder {
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

@ -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,10 +1,8 @@
package cn.iocoder.yudao.module.infra.service.db;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
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 org.w3c.dom.stylesheets.LinkStyle;
import javax.validation.Valid;
import java.util.List;

View File

@ -1,22 +1,20 @@
package cn.iocoder.yudao.module.infra.service.db;
import cn.hutool.db.DbUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.util.DatabaseUtils;
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.mybatisplus.extension.toolkit.JdbcUtils;
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.sql.Connection;
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;
@ -37,6 +35,9 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService {
@Resource
private StringEncryptor stringEncryptor;
@Resource
private DynamicDataSourceProperties dynamicDataSourceProperties;
@Override
public Long createDataSourceConfig(DataSourceConfigCreateReqVO createReqVO) {
DataSourceConfigDO dataSourceConfig = DataSourceConfigConvert.INSTANCE.convert(createReqVO);
@ -77,21 +78,41 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService {
@Override
public DataSourceConfigDO getDataSourceConfig(Long id) {
// 如果 id 为 0默认为 master 的数据源
if (Objects.equals(id, DataSourceConfigDO.ID_MASTER)) {
return buildMasterDataSourceConfig();
}
// 从 DB 中读取
DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(id);
dataSourceConfig.setPassword(stringEncryptor.decrypt(dataSourceConfig.getPassword()));
try {
dataSourceConfig.setPassword(stringEncryptor.decrypt(dataSourceConfig.getPassword()));
} catch (Exception ignore) { // 解码失败,则不解码
}
return dataSourceConfig;
}
@Override
public List<DataSourceConfigDO> getDataSourceConfigList() {
return dataSourceConfigMapper.selectList();
List<DataSourceConfigDO> result = dataSourceConfigMapper.selectList();
// 补充 master 数据源
result.add(0, buildMasterDataSourceConfig());
return result;
}
private void checkConnectionOK(DataSourceConfigDO config) {
boolean success = DatabaseUtils.isConnectionOK(config.getUrl(), config.getUsername(), config.getPassword());
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

@ -1,19 +1,30 @@
-- 将该建表 SQL 语句,添加到 yudao-module-${table.moduleName}-biz 模块的 test/resources/sql/create_tables.sql 文件里
CREATE TABLE IF NOT EXISTS "${table.tableName}" (
#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}" ${dataType}#if (${column.nullable} == false) NOT NULL#end,
#end
#end
#end

View File

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.infra.service;
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();
// 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();
System.out.println(tableInfos.size());
System.out.println(System.currentTimeMillis() - time);
}
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.infra.service.db;
import cn.iocoder.yudao.framework.mybatis.core.util.DatabaseUtils;
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;
@ -41,12 +41,12 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest {
@Test
public void testCreateDataSourceConfig_success() {
try (MockedStatic<DatabaseUtils> databaseUtilsMock = mockStatic(DatabaseUtils.class)) {
try (MockedStatic<JdbcUtils> databaseUtilsMock = mockStatic(JdbcUtils.class)) {
// 准备参数
DataSourceConfigCreateReqVO reqVO = randomPojo(DataSourceConfigCreateReqVO.class);
// mock 方法
when(stringEncryptor.encrypt(eq(reqVO.getPassword()))).thenReturn("123456");
databaseUtilsMock.when(() -> DatabaseUtils.isConnectionOK(eq(reqVO.getUrl()),
databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()),
eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true);
// 调用
@ -62,7 +62,7 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest {
@Test
public void testUpdateDataSourceConfig_success() {
try (MockedStatic<DatabaseUtils> databaseUtilsMock = mockStatic(DatabaseUtils.class)) {
try (MockedStatic<JdbcUtils> databaseUtilsMock = mockStatic(JdbcUtils.class)) {
// mock 数据
DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class);
dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据
@ -72,7 +72,7 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest {
});
// mock 方法
when(stringEncryptor.encrypt(eq(reqVO.getPassword()))).thenReturn("123456");
databaseUtilsMock.when(() -> DatabaseUtils.isConnectionOK(eq(reqVO.getUrl()),
databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()),
eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true);
// 调用

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -56,7 +56,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n
/***/ (function(module, exports, __webpack_require__) {
"use strict";
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireDefault.js */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\").default;\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\n__webpack_require__(/*! core-js/modules/es.regexp.exec.js */ \"./node_modules/core-js/modules/es.regexp.exec.js\");\n\n__webpack_require__(/*! core-js/modules/es.string.replace.js */ \"./node_modules/core-js/modules/es.string.replace.js\");\n\nvar _objectSpread2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/objectSpread2.js */ \"./node_modules/@babel/runtime/helpers/objectSpread2.js\"));\n\nvar _constants = __webpack_require__(/*! @/utils/constants */ \"./src/utils/constants.js\");\n\nvar _login = __webpack_require__(/*! @/api/login */ \"./src/api/login.js\");\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n props: {\n user: {\n type: Object\n },\n getUser: {\n // 刷新用户\n type: Function\n },\n setActiveTab: {\n // 设置激活的\n type: Function\n }\n },\n data: function data() {\n return {};\n },\n computed: {\n socialUsers: function socialUsers() {\n var socialUsers = [];\n\n for (var i in _constants.SystemUserSocialTypeEnum) {\n var socialUser = (0, _objectSpread2.default)({}, _constants.SystemUserSocialTypeEnum[i]);\n socialUsers.push(socialUser);\n\n if (this.user.socialUsers) {\n for (var j in this.user.socialUsers) {\n if (socialUser.type === this.user.socialUsers[j].type) {\n socialUser.unionId = this.user.socialUsers[j].unionId;\n break;\n }\n }\n }\n }\n\n return socialUsers;\n }\n },\n created: function created() {\n var _this = this;\n\n // 社交绑定\n var type = this.$route.query.type;\n var code = this.$route.query.code;\n var state = this.$route.query.state;\n\n if (!code) {\n return;\n }\n\n (0, _login.socialBind)(type, code, state).then(function (resp) {\n _this.$modal.msgSuccess(\"绑定成功\");\n\n _this.$router.replace('/user/profile'); // 调用父组件, 刷新\n\n\n _this.getUser();\n\n _this.setActiveTab('userSocial');\n });\n },\n methods: {\n bind: function bind(socialUser) {\n // 计算 redirectUri\n var redirectUri = location.origin + '/user/profile?type=' + socialUser.type; // 进行跳转\n\n (0, _login.socialAuthRedirect)(socialUser.type, encodeURIComponent(redirectUri)).then(function (res) {\n // console.log(res.url);\n window.location.href = res.data;\n });\n },\n unbind: function unbind(socialUser) {\n var _this2 = this;\n\n (0, _login.socialUnbind)(socialUser.type, socialUser.unionId).then(function (resp) {\n _this2.$modal.msgSuccess(\"解绑成功\");\n\n socialUser.unionId = undefined;\n });\n },\n close: function close() {\n this.$tab.closePage();\n }\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./src/views/system/user/profile/userSocial.vue?./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireDefault.js */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\").default;\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\n__webpack_require__(/*! core-js/modules/es.regexp.exec.js */ \"./node_modules/core-js/modules/es.regexp.exec.js\");\n\n__webpack_require__(/*! core-js/modules/es.string.replace.js */ \"./node_modules/core-js/modules/es.string.replace.js\");\n\nvar _objectSpread2 = _interopRequireDefault(__webpack_require__(/*! ./node_modules/@babel/runtime/helpers/objectSpread2.js */ \"./node_modules/@babel/runtime/helpers/objectSpread2.js\"));\n\nvar _constants = __webpack_require__(/*! @/utils/constants */ \"./src/utils/constants.js\");\n\nvar _login = __webpack_require__(/*! @/api/login */ \"./src/api/login.js\");\n\nvar _socialUser = __webpack_require__(/*! @/api/system/socialUser */ \"./src/api/system/socialUser.js\");\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\nvar _default = {\n props: {\n user: {\n type: Object\n },\n getUser: {\n // 刷新用户\n type: Function\n },\n setActiveTab: {\n // 设置激活的\n type: Function\n }\n },\n data: function data() {\n return {};\n },\n computed: {\n socialUsers: function socialUsers() {\n var socialUsers = [];\n\n for (var i in _constants.SystemUserSocialTypeEnum) {\n var socialUser = (0, _objectSpread2.default)({}, _constants.SystemUserSocialTypeEnum[i]);\n socialUsers.push(socialUser);\n\n if (this.user.socialUsers) {\n for (var j in this.user.socialUsers) {\n if (socialUser.type === this.user.socialUsers[j].type) {\n socialUser.openid = this.user.socialUsers[j].openid;\n break;\n }\n }\n }\n }\n\n return socialUsers;\n }\n },\n created: function created() {\n var _this = this;\n\n // 社交绑定\n var type = this.$route.query.type;\n var code = this.$route.query.code;\n var state = this.$route.query.state;\n\n if (!code) {\n return;\n }\n\n (0, _socialUser.socialBind)(type, code, state).then(function (resp) {\n _this.$modal.msgSuccess(\"绑定成功\");\n\n _this.$router.replace('/user/profile'); // 调用父组件, 刷新\n\n\n _this.getUser();\n\n _this.setActiveTab('userSocial');\n });\n },\n methods: {\n bind: function bind(socialUser) {\n // 计算 redirectUri\n var redirectUri = location.origin + '/user/profile?type=' + socialUser.type; // 进行跳转\n\n (0, _login.socialAuthRedirect)(socialUser.type, encodeURIComponent(redirectUri)).then(function (res) {\n // console.log(res.url);\n window.location.href = res.data;\n });\n },\n unbind: function unbind(socialUser) {\n var _this2 = this;\n\n (0, _socialUser.socialUnbind)(socialUser.type, socialUser.openid).then(function (resp) {\n _this2.$modal.msgSuccess(\"解绑成功\");\n\n socialUser.openid = undefined;\n });\n },\n close: function close() {\n this.$tab.closePage();\n }\n }\n};\nexports.default = _default;\n\n//# sourceURL=webpack:///./src/views/system/user/profile/userSocial.vue?./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
@ -116,7 +116,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) *
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function () {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"el-table\",\n { attrs: { data: _vm.socialUsers, \"show-header\": false } },\n [\n _c(\"el-table-column\", {\n attrs: { label: \"社交平台\", align: \"left\", width: \"120\" },\n scopedSlots: _vm._u([\n {\n key: \"default\",\n fn: function (scope) {\n return [\n _c(\"img\", {\n staticStyle: { height: \"20px\", \"vertical-align\": \"middle\" },\n attrs: { src: scope.row.img },\n }),\n _vm._v(\" \" + _vm._s(scope.row.title) + \" \"),\n ]\n },\n },\n ]),\n }),\n _c(\"el-table-column\", {\n attrs: { label: \"操作\", align: \"left\" },\n scopedSlots: _vm._u([\n {\n key: \"default\",\n fn: function (scope) {\n return [\n scope.row.unionId\n ? _c(\n \"div\",\n [\n _vm._v(\" 已绑定 \"),\n _c(\n \"el-button\",\n {\n attrs: { size: \"large\", type: \"text\" },\n on: {\n click: function ($event) {\n return _vm.unbind(scope.row)\n },\n },\n },\n [_vm._v(\"(解绑)\")]\n ),\n ],\n 1\n )\n : _c(\n \"div\",\n [\n _vm._v(\" 未绑定 \"),\n _c(\n \"el-button\",\n {\n attrs: { size: \"large\", type: \"text\" },\n on: {\n click: function ($event) {\n return _vm.bind(scope.row)\n },\n },\n },\n [_vm._v(\"(绑定)\")]\n ),\n ],\n 1\n ),\n ]\n },\n },\n ]),\n }),\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./src/views/system/user/profile/userSocial.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%22f587f70a-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function () {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"el-table\",\n { attrs: { data: _vm.socialUsers, \"show-header\": false } },\n [\n _c(\"el-table-column\", {\n attrs: { label: \"社交平台\", align: \"left\", width: \"120\" },\n scopedSlots: _vm._u([\n {\n key: \"default\",\n fn: function (scope) {\n return [\n _c(\"img\", {\n staticStyle: { height: \"20px\", \"vertical-align\": \"middle\" },\n attrs: { src: scope.row.img },\n }),\n _vm._v(\" \" + _vm._s(scope.row.title) + \" \"),\n ]\n },\n },\n ]),\n }),\n _c(\"el-table-column\", {\n attrs: { label: \"操作\", align: \"left\" },\n scopedSlots: _vm._u([\n {\n key: \"default\",\n fn: function (scope) {\n return [\n scope.row.openid\n ? _c(\n \"div\",\n [\n _vm._v(\" 已绑定 \"),\n _c(\n \"el-button\",\n {\n attrs: { size: \"large\", type: \"text\" },\n on: {\n click: function ($event) {\n return _vm.unbind(scope.row)\n },\n },\n },\n [_vm._v(\"(解绑)\")]\n ),\n ],\n 1\n )\n : _c(\n \"div\",\n [\n _vm._v(\" 未绑定 \"),\n _c(\n \"el-button\",\n {\n attrs: { size: \"large\", type: \"text\" },\n on: {\n click: function ($event) {\n return _vm.bind(scope.row)\n },\n },\n },\n [_vm._v(\"(绑定)\")]\n ),\n ],\n 1\n ),\n ]\n },\n },\n ]),\n }),\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./src/views/system/user/profile/userSocial.vue?./node_modules/cache-loader/dist/cjs.js?%7B%22cacheDirectory%22:%22node_modules/.cache/vue-loader%22,%22cacheIdentifier%22:%22f587f70a-vue-loader-template%22%7D!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
@ -153,6 +153,18 @@ eval("// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// lo
/***/ }),
/***/ "./src/api/system/socialUser.js":
/*!**************************************!*\
!*** ./src/api/system/socialUser.js ***!
\**************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
eval("\n\nvar _interopRequireDefault = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/interopRequireDefault.js */ \"./node_modules/@babel/runtime/helpers/interopRequireDefault.js\").default;\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.socialBind = socialBind;\nexports.socialUnbind = socialUnbind;\n\nvar _request = _interopRequireDefault(__webpack_require__(/*! @/utils/request */ \"./src/utils/request.js\"));\n\n// 社交绑定,使用 code 授权码\nfunction socialBind(type, code, state) {\n return (0, _request.default)({\n url: '/system/social-user/bind',\n method: 'post',\n data: {\n type: type,\n code: code,\n state: state\n }\n });\n} // 取消社交绑定\n\n\nfunction socialUnbind(type, openid) {\n return (0, _request.default)({\n url: '/system/social-user/unbind',\n method: 'delete',\n data: {\n type: type,\n openid: openid\n }\n });\n}\n\n//# sourceURL=webpack:///./src/api/system/socialUser.js?");
/***/ }),
/***/ "./src/api/system/user.js":
/*!********************************!*\
!*** ./src/api/system/user.js ***!

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -73,26 +73,11 @@ export function getSchemaTableList(query) {
}
// 基于数据库的表结构,创建代码生成器的表定义
export function createCodegenListFromDB(tableNames) {
export function createCodegenList(data) {
return request({
url: '/infra/codegen/create-list-from-db',
url: '/infra/codegen/create-list',
method: 'post',
headers:{
'Content-type': 'application/x-www-form-urlencoded'
},
data: 'tableNames=' + tableNames
})
}
// 基于 SQL 建表语句,创建代码生成器的表定义
export function createCodegenListFromSQL(data) {
return request({
url: '/infra/codegen/create-list-from-sql',
method: 'post',
headers:{
'Content-type': 'application/x-www-form-urlencoded'
},
data: 'sql=' + data.sql,
data: data
})
}

View File

@ -19,7 +19,7 @@
</el-table-column>
<el-table-column
label="物理类型"
prop="columnType"
prop="dataType"
min-width="10%"
:show-overflow-tooltip="true"
/>

View File

@ -1,24 +1,18 @@
<template>
<!-- 导入表 -->
<el-dialog title="导入表" :visible.sync="visible" width="800px" top="5vh" append-to-body>
<el-form :model="queryParams" ref="queryForm" :inline="true">
<el-form-item label="表名称" prop="tableName">
<el-input
v-model="queryParams.tableName"
placeholder="请输入表名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true">
<el-form-item label="数据源" prop="dataSourceConfigId">
<el-select v-model="queryParams.dataSourceConfigId" placeholder="请选择数据源" clearable>
<el-option v-for="config in dataSourceConfigs"
:key="config.id" :label="config.name" :value="config.id"/>
</el-select>
</el-form-item>
<el-form-item label="表描述" prop="tableComment">
<el-input
v-model="queryParams.tableComment"
placeholder="请输入表描述"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
<el-form-item label="表名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入表名称" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="表描述" prop="comment">
<el-input v-model="queryParams.comment" placeholder="请输入表描述" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
@ -26,16 +20,11 @@
</el-form-item>
</el-form>
<el-row>
<el-table @row-click="clickRow" ref="table" :data="dbTableList" @selection-change="handleSelectionChange" height="260px">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="tableSchema" label="数据库" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="tableName" label="表名称" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="tableComment" label="表描述" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="createTime" label="创建时间">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table v-loading="loading" @row-click="clickRow" ref="table" :data="dbTableList"
@selection-change="handleSelectionChange" height="260px">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="表名称" :show-overflow-tooltip="true" />
<el-table-column prop="comment" label="表描述" :show-overflow-tooltip="true" />
</el-table>
</el-row>
<div slot="footer" class="dialog-footer">
@ -46,10 +35,13 @@
</template>
<script>
import { getSchemaTableList, createCodegenListFromDB } from "@/api/infra/codegen";
import { getSchemaTableList, createCodegenList } from "@/api/infra/codegen";
import {getDataSourceConfigList} from "@/api/infra/dataSourceConfig";
export default {
data() {
return {
//
loading: false,
//
visible: false,
//
@ -60,28 +52,40 @@ export default {
dbTableList: [],
//
queryParams: {
tableName: undefined,
tableComment: undefined
}
dataSourceConfigId: undefined,
name: undefined,
comment: undefined,
},
//
dataSourceConfigs: [],
};
},
methods: {
//
show() {
this.getList();
this.visible = true;
//
getDataSourceConfigList().then(response => {
this.dataSourceConfigs = response.data;
this.queryParams.dataSourceConfigId = this.dataSourceConfigs[0].id;
//
this.getList();
});
},
clickRow(row) {
this.$refs.table.toggleRowSelection(row);
},
//
handleSelectionChange(selection) {
this.tables = selection.map(item => item.tableName);
this.tables = selection.map(item => item.name);
},
//
getList() {
this.loading = true;
getSchemaTableList(this.queryParams).then(res => {
this.dbTableList = res.data;
}).finally(() => {
this.loading = false;
});
},
/** 搜索按钮操作 */
@ -91,11 +95,15 @@ export default {
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.queryParams.dataSourceConfigId = this.dataSourceConfigs[0].id;
this.handleQuery();
},
/** 导入按钮操作 */
handleImportTable() {
createCodegenListFromDB(this.tables.join(",")).then(res => {
createCodegenList({
dataSourceConfigId: this.queryParams.dataSourceConfigId,
tableNames: this.tables
}).then(res => {
this.$modal.msgSuccess("导入成功");
this.visible = false;
this.$emit("ok");

View File

@ -26,29 +26,28 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="info" plain icon="el-icon-upload" size="mini" @click="openImportTable"
v-hasPermi="['infra:codegen:create']">基于 DB 导入</el-button>
<el-button type="info" plain icon="el-icon-upload" size="mini" @click="openImportSQL"
v-hasPermi="['infra:codegen:create']">基于 SQL 导入</el-button>
v-hasPermi="['infra:codegen:create']">导入</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="tableList">
<el-table-column label="表名称" align="center" prop="tableName" :show-overflow-tooltip="true" width="200"/>
<el-table-column label="数据源" align="center" :formatter="dataSourceConfigNameFormat"/>
<el-table-column label="表名称" align="center" prop="tableName" width="200"/>
<el-table-column label="表描述" align="center" prop="tableComment" :show-overflow-tooltip="true" width="120"/>
<el-table-column label="实体" align="center" prop="className" :show-overflow-tooltip="true" width="200"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
<el-table-column label="实体" align="center" prop="className" width="200"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="更新时间" align="center" prop="createTime" width="160">
<el-table-column label="更新时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.updateTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" width="300px" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="text" size="small" icon="el-icon-view" @click="handlePreview(scope.row)" v-hasPermi="['infra:codegen:preview']"></el-button>
<el-button type="text" size="small" icon="el-icon-edit" @click="handleEditTable(scope.row)" v-hasPermi="['infra:codegen:update']"></el-button>
@ -81,23 +80,6 @@
<!-- 基于 DB 导入 -->
<import-table ref="import" @ok="handleQuery" />
<!-- 基于 SQL 导入 -->
<el-dialog :title="importSQL.title" :visible.sync="importSQL.open" width="800px" append-to-body>
<el-form ref="importSQLForm" :model="importSQL.form" :rules="importSQL.rules" label-width="120px">
<el-row>
<el-col :span="12">
<el-form-item label="建表 SQL 语句" prop="sql">
<el-input v-model="importSQL.form.sql" type="textarea" rows="30" style="width: 650px;" placeholder="请输入建 SQL 语句" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitImportSQLForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
@ -109,6 +91,7 @@ import importTable from "./importTable";
//
import hljs from "highlight.js/lib/highlight";
import "highlight.js/styles/github-gist.css";
import {getDataSourceConfigList} from "@/api/infra/dataSourceConfig";
hljs.registerLanguage("java", require("highlight.js/lib/languages/java"));
hljs.registerLanguage("xml", require("highlight.js/lib/languages/xml"));
hljs.registerLanguage("html", require("highlight.js/lib/languages/xml"));
@ -150,21 +133,16 @@ export default {
data: {},
activeName: "",
},
// SQL
importSQL: {
open: false,
title: "",
form: {
},
rules: {
sql: [{ required: true, message: "SQL 不能为空", trigger: "blur" }]
}
}
//
dataSourceConfigs: [],
};
},
created() {
this.getList();
//
getDataSourceConfigList().then(response => {
this.dataSourceConfigs = response.data;
});
},
activated() {
const time = this.$route.query.t;
@ -200,12 +178,6 @@ export default {
},
/** 同步数据库操作 */
handleSynchDb(row) {
// SQL
if (row.importType === 2) {
this.importSQL.open = true;
this.importSQL.form.tableId = row.id;
return;
}
// DB
const tableName = row.tableName;
this.$modal.confirm('确认要强制同步"' + tableName + '"表结构吗?').then(function() {
@ -218,10 +190,6 @@ export default {
openImportTable() {
this.$refs.import.show();
},
/** 打开 SQL 导入的弹窗 **/
openImportSQL() {
this.importSQL.open = true;
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
@ -336,43 +304,15 @@ export default {
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
//
cancel() {
this.importSQL.open = false;
this.reset();
},
//
reset() {
this.importSQL.form = {
tableId: undefined,
sql: undefined,
};
this.resetForm("importSQLForm");
},
// import SQL
submitImportSQLForm() {
this.$refs["importSQLForm"].validate(valid => {
if (!valid) {
return;
//
dataSourceConfigNameFormat(row, column) {
for (const config of this.dataSourceConfigs) {
if (row.dataSourceConfigId === config.id) {
return config.name;
}
//
let form = this.importSQL.form;
if (form.tableId != null) {
syncCodegenFromSQL(form.tableId, form.sql).then(response => {
this.$modal.msgSuccess("同步成功");
this.importSQL.open = false;
this.getList();
});
return;
}
//
createCodegenListFromSQL(form).then(response => {
this.$modal.msgSuccess("导入成功");
this.importSQL.open = false;
this.getList();
});
});
}
}
return '未知【' + row.leaderUserId + '】';
},
}
};
</script>

View File

@ -11,7 +11,7 @@
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="主键编号" align="center" prop="id" />
<el-table-column label="数名称" align="center" prop="name" />
<el-table-column label="据源名称" align="center" prop="name" />
<el-table-column label="数据源连接" align="center" prop="url" />
<el-table-column label="用户名" align="center" prop="username" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
@ -32,7 +32,7 @@
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form-item label="数名称" prop="name">
<el-form-item label="据源名称" prop="name">
<el-input v-model="form.name" placeholder="请输入参数名称" />
</el-form-item>
<el-form-item label="数据源连接" prop="url">
@ -76,7 +76,7 @@ export default {
form: {},
//
rules: {
name: [{ required: true, message: "数名称不能为空", trigger: "blur" }],
name: [{ required: true, message: "据源名称不能为空", trigger: "blur" }],
url: [{ required: true, message: "数据源连接不能为空", trigger: "blur" }],
username: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
password: [{ required: true, message: "密码不能为空", trigger: "blur" }],