diff --git a/backend/src/main/java/io/dataease/auth/aop/DeLogAnnotationHandler.java b/backend/src/main/java/io/dataease/auth/aop/DeLogAnnotationHandler.java index 7b3f41e148..503e60bc20 100644 --- a/backend/src/main/java/io/dataease/auth/aop/DeLogAnnotationHandler.java +++ b/backend/src/main/java/io/dataease/auth/aop/DeLogAnnotationHandler.java @@ -43,6 +43,7 @@ public class DeLogAnnotationHandler { befores.add(SysLogConstants.OPERATE_TYPE.DELETE.getValue()); befores.add(SysLogConstants.OPERATE_TYPE.UNSHARE.getValue()); befores.add(SysLogConstants.OPERATE_TYPE.UNAUTHORIZE.getValue()); + befores.add(SysLogConstants.OPERATE_TYPE.UPLOADFILE.getValue()); } private SysLogDTO exec(JoinPoint point, DeLog deLog) throws Exception{ diff --git a/backend/src/main/java/io/dataease/commons/constants/SysLogConstants.java b/backend/src/main/java/io/dataease/commons/constants/SysLogConstants.java index 01e68c4647..3218776040 100644 --- a/backend/src/main/java/io/dataease/commons/constants/SysLogConstants.java +++ b/backend/src/main/java/io/dataease/commons/constants/SysLogConstants.java @@ -21,7 +21,8 @@ public class SysLogConstants { UNAUTHORIZE(7, "OPERATE_TYPE_UNAUTHORIZE"), CREATELINK(8, "OPERATE_TYPE_CREATELINK"), DELETELINK(9, "OPERATE_TYPE_DELETELINK"), - MODIFYLINK(10, "OPERATE_TYPE_MODIFYLINK"); + MODIFYLINK(10, "OPERATE_TYPE_MODIFYLINK"), + UPLOADFILE(11, "OPERATE_TYPE_UPLOADFILE"); private Integer value; private String name; OPERATE_TYPE(Integer value, String name) { @@ -52,7 +53,9 @@ public class SysLogConstants { /*LINK(5, "SOURCE_TYPE_LINK"),*/ USER(6, "SOURCE_TYPE_USER"), DEPT(7, "SOURCE_TYPE_DEPT"), - ROLE(8, "SOURCE_TYPE_ROLE"); + ROLE(8, "SOURCE_TYPE_ROLE"), + DRIVER(9, "SOURCE_TYPE_DRIVER"), + DRIVER_FILE(10, "SOURCE_TYPE_DRIVER_FILE"); private Integer value; private String name; diff --git a/backend/src/main/java/io/dataease/controller/datasource/DriverMgmController.java b/backend/src/main/java/io/dataease/controller/datasource/DriverMgmController.java new file mode 100644 index 0000000000..049a9d4120 --- /dev/null +++ b/backend/src/main/java/io/dataease/controller/datasource/DriverMgmController.java @@ -0,0 +1,126 @@ +package io.dataease.controller.datasource; + + +import io.dataease.auth.annotation.DeLog; +import io.dataease.commons.constants.SysLogConstants; +import io.dataease.dto.DriverDTO; +import io.dataease.plugins.common.base.domain.DeDriver; +import io.dataease.plugins.common.base.domain.DeDriverDetails; +import io.dataease.service.datasource.DriverService; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import springfox.documentation.annotations.ApiIgnore; + +import javax.annotation.Resource; +import java.util.List; +import java.util.stream.Collectors; + +@ApiIgnore +@RequestMapping("driver") +@RestController +public class DriverMgmController { + + @Resource + private DriverService driverService; + + @RequiresPermissions("datasource:read") + @ApiOperation("驱动列表") + @PostMapping("/list") + public List listDeDriver() throws Exception{ + return driverService.list(); + } + + @RequiresPermissions("datasource:read") + @ApiOperation("删除驱动") + @PostMapping("/delete") + @DeLog( + operatetype = SysLogConstants.OPERATE_TYPE.DELETE, + sourcetype = SysLogConstants.SOURCE_TYPE.DRIVER, + positionIndex = 0, + positionKey = "type", + value = "id" + ) + public void delete(@RequestBody DeDriver deDriver) throws Exception{ + driverService.delete(deDriver.getId()); + } + + @RequiresPermissions("datasource:read") + @ApiOperation("驱动列表") + @GetMapping("/list/{type}") + public List listDeDriver(@PathVariable String type) throws Exception{ + return listDeDriver().stream().filter(driverDTO -> driverDTO.getType().equalsIgnoreCase(type)).collect(Collectors.toList()); + } + + @RequiresPermissions("datasource:read") + @ApiOperation("添加驱动") + @PostMapping("/save") + @DeLog( + operatetype = SysLogConstants.OPERATE_TYPE.CREATE, + sourcetype = SysLogConstants.SOURCE_TYPE.DRIVER, + positionIndex = 0, + positionKey = "type", + value = "id" + ) + public DeDriver save(@RequestBody DeDriver deDriver) throws Exception{ + return driverService.save(deDriver); + } + + @RequiresPermissions("datasource:read") + @ApiOperation("更新驱动") + @PostMapping("/update") + @DeLog( + operatetype = SysLogConstants.OPERATE_TYPE.MODIFY, + sourcetype = SysLogConstants.SOURCE_TYPE.DRIVER, + positionIndex = 0,positionKey = "type", + value = "id" + ) + public DeDriver update(@RequestBody DeDriver deDriver) throws Exception{ + return driverService.update(deDriver); + } + + @RequiresPermissions("datasource:read") + @ApiOperation("驱动文件列表") + @GetMapping("/listDriverDetails/{id}") + public List listDriverDetails(@PathVariable String id) throws Exception{ + return driverService.listDriverDetails(id); + } + + @RequiresPermissions("datasource:read") + @ApiOperation("删除驱动文件") + @PostMapping("/deleteDriverFile") + @DeLog( + operatetype = SysLogConstants.OPERATE_TYPE.DELETE, + sourcetype = SysLogConstants.SOURCE_TYPE.DRIVER_FILE, + positionIndex = 0, + positionKey = "deDriverId", + value = "id" + ) + public void deleteDriverFile(@RequestBody DeDriverDetails deDriverDetails) throws Exception{ + driverService.deleteDriverFile(deDriverDetails.getId()); + } + + @RequiresPermissions("datasource:read") + @ApiOperation("驱动文件上传") + @PostMapping("file/upload") + @DeLog( + operatetype = SysLogConstants.OPERATE_TYPE.UPLOADFILE, + sourcetype = SysLogConstants.SOURCE_TYPE.DRIVER_FILE, + positionIndex = 0, + positionKey = "deDriverId", + value = "id" + ) + @ApiImplicitParams({ + @ApiImplicitParam(name = "file", value = "文件", required = true, dataType = "MultipartFile"), + @ApiImplicitParam(name = "id", value = "驱动D", required = true, dataType = "String") + }) + public DeDriverDetails excelUpload(@RequestParam("id") String id, @RequestParam("file") MultipartFile file) throws Exception { + return driverService.saveJar(file, id); + } + + + +} diff --git a/backend/src/main/java/io/dataease/dto/DriverDTO.java b/backend/src/main/java/io/dataease/dto/DriverDTO.java new file mode 100644 index 0000000000..774ecdbbd6 --- /dev/null +++ b/backend/src/main/java/io/dataease/dto/DriverDTO.java @@ -0,0 +1,9 @@ +package io.dataease.dto; + +import io.dataease.plugins.common.base.domain.DeDriver; +import lombok.Data; + +@Data +public class DriverDTO extends DeDriver { + private String typeDesc; +} diff --git a/backend/src/main/java/io/dataease/ext/ExtSysLogMapper.xml b/backend/src/main/java/io/dataease/ext/ExtSysLogMapper.xml index ca90d1006e..a4786de7b9 100644 --- a/backend/src/main/java/io/dataease/ext/ExtSysLogMapper.xml +++ b/backend/src/main/java/io/dataease/ext/ExtSysLogMapper.xml @@ -112,6 +112,28 @@ + + id, name + from de_driver + + id in + + #{id} + + + + + + id, name + from de_driver + + id in + + #{id} + + + + diff --git a/backend/src/main/java/io/dataease/provider/ProviderFactory.java b/backend/src/main/java/io/dataease/provider/ProviderFactory.java index 1727d1a705..e5dcaf4ff9 100644 --- a/backend/src/main/java/io/dataease/provider/ProviderFactory.java +++ b/backend/src/main/java/io/dataease/provider/ProviderFactory.java @@ -1,6 +1,5 @@ package io.dataease.provider; -import com.google.gson.Gson; import io.dataease.plugins.common.constants.DatasourceTypes; import io.dataease.plugins.common.dto.datasource.DataSourceType; import io.dataease.plugins.config.SpringContextUtil; @@ -25,7 +24,7 @@ public class ProviderFactory implements ApplicationContextAware { for(final DatasourceTypes d: DatasourceTypes.values()) { final ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) context).getBeanFactory(); if(d.isDatasource()){ - DataSourceType dataSourceType = new DataSourceType(d.getType(), d.getName(), false, d.getExtraParams(), d.getCalculationMode()); + DataSourceType dataSourceType = new DataSourceType(d.getType(), d.getName(), false, d.getExtraParams(), d.getCalculationMode(), d.isJdbc()); if(dataSourceType.getType().equalsIgnoreCase("oracle")){ dataSourceType.setCharset(d.getCharset()); } diff --git a/backend/src/main/java/io/dataease/provider/datasource/JdbcProvider.java b/backend/src/main/java/io/dataease/provider/datasource/JdbcProvider.java index ddb7d3c58a..dc7204f859 100644 --- a/backend/src/main/java/io/dataease/provider/datasource/JdbcProvider.java +++ b/backend/src/main/java/io/dataease/provider/datasource/JdbcProvider.java @@ -6,16 +6,20 @@ import com.google.gson.Gson; import io.dataease.dto.datasource.*; import io.dataease.exception.DataEaseException; import io.dataease.i18n.Translator; +import io.dataease.plugins.common.base.domain.DeDriver; +import io.dataease.plugins.common.base.mapper.DeDriverMapper; import io.dataease.plugins.common.constants.DatasourceTypes; import io.dataease.plugins.common.dto.datasource.TableField; import io.dataease.plugins.common.request.datasource.DatasourceRequest; import io.dataease.plugins.datasource.entity.JdbcConfiguration; import io.dataease.plugins.datasource.provider.DefaultJdbcProvider; +import io.dataease.plugins.datasource.provider.ExtendedJdbcClassLoader; import io.dataease.plugins.datasource.query.QueryProvider; import io.dataease.provider.ProviderFactory; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import javax.annotation.Resource; import java.beans.PropertyVetoException; import java.lang.reflect.Method; import java.sql.*; @@ -25,6 +29,8 @@ import java.util.*; public class JdbcProvider extends DefaultJdbcProvider { + @Resource + private DeDriverMapper deDriverMapper; @Override public boolean isUseDatasourcePool(){ return true; @@ -294,10 +300,12 @@ public class JdbcProvider extends DefaultJdbcProvider { public Connection getConnection(DatasourceRequest datasourceRequest) throws Exception { String username = null; String password = null; - String driver = null; + String defaultDriver = null; String jdbcurl = null; + String customDriver = null; DatasourceTypes datasourceType = DatasourceTypes.valueOf(datasourceRequest.getDatasource().getType()); Properties props = new Properties(); + DeDriver deDriver = null; switch (datasourceType) { case mysql: case mariadb: @@ -309,21 +317,24 @@ public class JdbcProvider extends DefaultJdbcProvider { MysqlConfiguration mysqlConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), MysqlConfiguration.class); username = mysqlConfiguration.getUsername(); password = mysqlConfiguration.getPassword(); - driver = "com.mysql.jdbc.Driver"; + defaultDriver = "com.mysql.jdbc.Driver"; jdbcurl = mysqlConfiguration.getJdbc(); + customDriver = mysqlConfiguration.getCustomDriver(); break; case sqlServer: SqlServerConfiguration sqlServerConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), SqlServerConfiguration.class); username = sqlServerConfiguration.getUsername(); password = sqlServerConfiguration.getPassword(); - driver = sqlServerConfiguration.getDriver(); + defaultDriver = sqlServerConfiguration.getDriver(); + customDriver = sqlServerConfiguration.getCustomDriver(); jdbcurl = sqlServerConfiguration.getJdbc(); break; case oracle: OracleConfiguration oracleConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), OracleConfiguration.class); username = oracleConfiguration.getUsername(); password = oracleConfiguration.getPassword(); - driver = oracleConfiguration.getDriver(); + defaultDriver = oracleConfiguration.getDriver(); + customDriver = oracleConfiguration.getCustomDriver(); jdbcurl = oracleConfiguration.getJdbc(); props.put("oracle.net.CONNECT_TIMEOUT", "5000"); break; @@ -331,56 +342,84 @@ public class JdbcProvider extends DefaultJdbcProvider { PgConfiguration pgConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), PgConfiguration.class); username = pgConfiguration.getUsername(); password = pgConfiguration.getPassword(); - driver = pgConfiguration.getDriver(); + defaultDriver = pgConfiguration.getDriver(); + customDriver = pgConfiguration.getCustomDriver(); jdbcurl = pgConfiguration.getJdbc(); break; case ck: CHConfiguration chConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), CHConfiguration.class); username = chConfiguration.getUsername(); password = chConfiguration.getPassword(); - driver = chConfiguration.getDriver(); + defaultDriver = chConfiguration.getDriver(); + customDriver = chConfiguration.getCustomDriver(); jdbcurl = chConfiguration.getJdbc(); break; case mongo: MongodbConfiguration mongodbConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), MongodbConfiguration.class); username = mongodbConfiguration.getUsername(); password = mongodbConfiguration.getPassword(); - driver = mongodbConfiguration.getDriver(); + defaultDriver = mongodbConfiguration.getDriver(); + customDriver = mongodbConfiguration.getCustomDriver(); jdbcurl = mongodbConfiguration.getJdbc(datasourceRequest.getDatasource().getId()); break; case redshift: RedshiftConfigration redshiftConfigration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), RedshiftConfigration.class); username = redshiftConfigration.getUsername(); password = redshiftConfigration.getPassword(); - driver = redshiftConfigration.getDriver(); + defaultDriver = redshiftConfigration.getDriver(); + customDriver = redshiftConfigration.getCustomDriver(); jdbcurl = redshiftConfigration.getJdbc(); break; case hive: HiveConfiguration hiveConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), HiveConfiguration.class); - username = hiveConfiguration.getUsername(); - password = hiveConfiguration.getPassword(); - driver = hiveConfiguration.getDriver(); + defaultDriver = hiveConfiguration.getDriver(); + customDriver = hiveConfiguration.getCustomDriver(); jdbcurl = hiveConfiguration.getJdbc(); + + if(StringUtils.isNotEmpty(hiveConfiguration.getAuthMethod()) && hiveConfiguration.getAuthMethod().equalsIgnoreCase("kerberos")){ + System.setProperty("java.security.krb5.conf", "/opt/dataease/conf/krb5.conf"); + ExtendedJdbcClassLoader classLoader; + if(isDefaultClassLoader(customDriver)){ + classLoader = extendedJdbcClassLoader; + }else { + deDriver = deDriverMapper.selectByPrimaryKey(customDriver); + classLoader = getCustomJdbcClassLoader(deDriver); + } + Class ConfigurationClass = classLoader.loadClass("org.apache.hadoop.conf.Configuration"); + Method set = ConfigurationClass.getMethod("set",String.class, String.class) ; + Object obj = ConfigurationClass.newInstance(); + set.invoke(obj, "hadoop.security.authentication", "Kerberos"); + + Class UserGroupInformationClass = classLoader.loadClass("org.apache.hadoop.security.UserGroupInformation"); + Method setConfiguration = UserGroupInformationClass.getMethod("setConfiguration",ConfigurationClass) ; + Method loginUserFromKeytab = UserGroupInformationClass.getMethod("loginUserFromKeytab",String.class, String.class) ; + setConfiguration.invoke(null, obj); + loginUserFromKeytab.invoke(null, hiveConfiguration.getUsername(), "/opt/dataease/conf/" + hiveConfiguration.getPassword()); + }else { + username = hiveConfiguration.getUsername(); + password = hiveConfiguration.getPassword(); + } break; case impala: ImpalaConfiguration impalaConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), ImpalaConfiguration.class); username = impalaConfiguration.getUsername(); password = impalaConfiguration.getPassword(); - driver = impalaConfiguration.getDriver(); + defaultDriver = impalaConfiguration.getDriver(); + customDriver = impalaConfiguration.getCustomDriver(); jdbcurl = impalaConfiguration.getJdbc(); break; case db2: Db2Configuration db2Configuration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), Db2Configuration.class); username = db2Configuration.getUsername(); password = db2Configuration.getPassword(); - driver = db2Configuration.getDriver(); + defaultDriver = db2Configuration.getDriver(); + customDriver = db2Configuration.getCustomDriver(); jdbcurl = db2Configuration.getJdbc(); break; default: break; } - Driver driverClass = (Driver) extendedJdbcClassLoader.loadClass(driver).newInstance(); if (StringUtils.isNotBlank(username)) { props.setProperty("user", username); if (StringUtils.isNotBlank(password)) { @@ -388,12 +427,34 @@ public class JdbcProvider extends DefaultJdbcProvider { } } - Connection conn = driverClass.connect(jdbcurl, props); + Connection conn; + String driverClassName ; + ExtendedJdbcClassLoader jdbcClassLoader; + if(isDefaultClassLoader(customDriver)){ + driverClassName = defaultDriver; + jdbcClassLoader = extendedJdbcClassLoader; + }else { + driverClassName = deDriver.getDriverClass(); + jdbcClassLoader = getCustomJdbcClassLoader(deDriver); + } + + Driver driverClass = (Driver) jdbcClassLoader.loadClass(driverClassName).newInstance(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(jdbcClassLoader); + conn= driverClass.connect(jdbcurl, props); + }catch (Exception e){ + e.printStackTrace(); + throw e; + }finally { + Thread.currentThread().setContextClassLoader(classLoader); + } return conn; } + @Override - public JdbcConfiguration setCredential(DatasourceRequest datasourceRequest, DruidDataSource dataSource) throws PropertyVetoException { + public JdbcConfiguration setCredential(DatasourceRequest datasourceRequest, DruidDataSource dataSource) throws Exception{ DatasourceTypes datasourceType = DatasourceTypes.valueOf(datasourceRequest.getDatasource().getType()); JdbcConfiguration jdbcConfiguration = new JdbcConfiguration(); switch (datasourceType) { @@ -474,7 +535,15 @@ public class JdbcProvider extends DefaultJdbcProvider { } dataSource.setUsername(jdbcConfiguration.getUsername()); - dataSource.setDriverClassLoader(extendedJdbcClassLoader); + + ExtendedJdbcClassLoader classLoader; + if(isDefaultClassLoader(jdbcConfiguration.getCustomDriver())){ + classLoader = extendedJdbcClassLoader; + }else { + DeDriver deDriver = deDriverMapper.selectByPrimaryKey(jdbcConfiguration.getCustomDriver()); + classLoader = getCustomJdbcClassLoader(deDriver); + } + dataSource.setDriverClassLoader(classLoader); dataSource.setPassword(jdbcConfiguration.getPassword()); return jdbcConfiguration; diff --git a/backend/src/main/java/io/dataease/service/chart/ChartViewService.java b/backend/src/main/java/io/dataease/service/chart/ChartViewService.java index f8d10b09be..d4ec1cfd96 100644 --- a/backend/src/main/java/io/dataease/service/chart/ChartViewService.java +++ b/backend/src/main/java/io/dataease/service/chart/ChartViewService.java @@ -54,6 +54,7 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; +import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.SimpleDateFormat; @@ -128,6 +129,9 @@ public class ChartViewService { } public ChartViewWithBLOBs newOne(ChartViewWithBLOBs chartView) { + if (StringUtils.isBlank(chartView.getViewFields())) { + chartView.setViewFields("[]"); + } long timestamp = System.currentTimeMillis(); chartView.setUpdateTime(timestamp); chartView.setId(UUID.randomUUID().toString()); @@ -300,23 +304,23 @@ public class ChartViewService { if (ObjectUtils.isEmpty(view)) { throw new RuntimeException(Translator.get("i18n_chart_delete")); } - List xAxis = new Gson().fromJson(view.getXAxis(), new TypeToken>() { + List xAxis = gson.fromJson(view.getXAxis(), new TypeToken>() { }.getType()); if (StringUtils.equalsIgnoreCase(view.getType(), "table-pivot")) { - List xAxisExt = new Gson().fromJson(view.getXAxisExt(), new TypeToken>() { + List xAxisExt = gson.fromJson(view.getXAxisExt(), new TypeToken>() { }.getType()); xAxis.addAll(xAxisExt); } - List yAxis = new Gson().fromJson(view.getYAxis(), new TypeToken>() { + List yAxis = gson.fromJson(view.getYAxis(), new TypeToken>() { }.getType()); if (StringUtils.equalsIgnoreCase(view.getType(), "chart-mix")) { - List yAxisExt = new Gson().fromJson(view.getYAxisExt(), new TypeToken>() { + List yAxisExt = gson.fromJson(view.getYAxisExt(), new TypeToken>() { }.getType()); yAxis.addAll(yAxisExt); } - List extStack = new Gson().fromJson(view.getExtStack(), new TypeToken>() { + List extStack = gson.fromJson(view.getExtStack(), new TypeToken>() { }.getType()); - List extBubble = new Gson().fromJson(view.getExtBubble(), new TypeToken>() { + List extBubble = gson.fromJson(view.getExtBubble(), new TypeToken>() { }.getType()); List fieldCustomFilter = new ArrayList(); List drill = new ArrayList(); @@ -401,7 +405,7 @@ public class ChartViewService { // 如果是插件视图 走插件内部的逻辑 if (ObjectUtils.isNotEmpty(view.getIsPlugin()) && view.getIsPlugin()) { Map> fieldMap = new HashMap<>(); - List xAxisExt = new Gson().fromJson(view.getXAxisExt(), new TypeToken>() { + List xAxisExt = gson.fromJson(view.getXAxisExt(), new TypeToken>() { }.getType()); fieldMap.put("xAxisExt", xAxisExt); fieldMap.put("xAxis", xAxis); @@ -434,7 +438,7 @@ public class ChartViewService { } // DatasourceProvider datasourceProvider = ProviderFactory.getProvider(ds.getType()); datasourceRequest.setDatasource(ds); - DataTableInfoDTO dataTableInfoDTO = new Gson().fromJson(table.getInfo(), DataTableInfoDTO.class); + DataTableInfoDTO dataTableInfoDTO = gson.fromJson(table.getInfo(), DataTableInfoDTO.class); QueryProvider qp = ProviderFactory.getQueryProvider(ds.getType()); if (StringUtils.equalsIgnoreCase(table.getType(), "db")) { datasourceRequest.setTable(dataTableInfoDTO.getTable()); @@ -462,7 +466,7 @@ public class ChartViewService { datasourceRequest.setQuery(qp.getSQLAsTmp(dataTableInfoDTO.getSql(), xAxis, yAxis, fieldCustomFilter, extFilterList, view)); } } else if (StringUtils.equalsIgnoreCase(table.getType(), "custom")) { - DataTableInfoDTO dt = new Gson().fromJson(table.getInfo(), DataTableInfoDTO.class); + DataTableInfoDTO dt = gson.fromJson(table.getInfo(), DataTableInfoDTO.class); List list = dataSetTableUnionService.listByTableId(dt.getList().get(0).getTableId()); String sql = dataSetTableService.getCustomSQLDatasource(dt, list, ds); if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) { @@ -477,7 +481,7 @@ public class ChartViewService { datasourceRequest.setQuery(qp.getSQLAsTmp(sql, xAxis, yAxis, fieldCustomFilter, extFilterList, view)); } } else if (StringUtils.equalsIgnoreCase(table.getType(), "union")) { - DataTableInfoDTO dt = new Gson().fromJson(table.getInfo(), DataTableInfoDTO.class); + DataTableInfoDTO dt = gson.fromJson(table.getInfo(), DataTableInfoDTO.class); Map sqlMap = dataSetTableService.getUnionSQLDatasource(dt, ds); String sql = (String) sqlMap.get("sql"); @@ -538,28 +542,30 @@ public class ChartViewService { if (ObjectUtils.isEmpty(view)) { throw new RuntimeException(Translator.get("i18n_chart_delete")); } - List xAxis = new Gson().fromJson(view.getXAxis(), new TypeToken>() { - }.getType()); + Type tokenType = new TypeToken>() {}.getType(); + Type filterTokenType = new TypeToken>() {}.getType(); + + List viewFields = gson.fromJson(view.getViewFields(), tokenType); + Map> extFieldsMap = null; + if (CollectionUtils.isNotEmpty(viewFields)) { + extFieldsMap = viewFields.stream().collect(Collectors.groupingBy(ChartViewFieldDTO::getBusiType)); + } + + + List xAxis = gson.fromJson(view.getXAxis(), tokenType); if (StringUtils.equalsIgnoreCase(view.getType(), "table-pivot")) { - List xAxisExt = new Gson().fromJson(view.getXAxisExt(), new TypeToken>() { - }.getType()); + List xAxisExt = gson.fromJson(view.getXAxisExt(), tokenType); xAxis.addAll(xAxisExt); } - List yAxis = new Gson().fromJson(view.getYAxis(), new TypeToken>() { - }.getType()); + List yAxis = gson.fromJson(view.getYAxis(), tokenType); if (StringUtils.equalsIgnoreCase(view.getType(), "chart-mix")) { - List yAxisExt = new Gson().fromJson(view.getYAxisExt(), new TypeToken>() { - }.getType()); + List yAxisExt = gson.fromJson(view.getYAxisExt(), tokenType); yAxis.addAll(yAxisExt); } - List extStack = new Gson().fromJson(view.getExtStack(), new TypeToken>() { - }.getType()); - List extBubble = new Gson().fromJson(view.getExtBubble(), new TypeToken>() { - }.getType()); - List fieldCustomFilter = new Gson().fromJson(view.getCustomFilter(), new TypeToken>() { - }.getType()); - List drill = new Gson().fromJson(view.getDrillFields(), new TypeToken>() { - }.getType()); + List extStack = gson.fromJson(view.getExtStack(), tokenType); + List extBubble = gson.fromJson(view.getExtBubble(), tokenType); + List fieldCustomFilter = gson.fromJson(view.getCustomFilter(), filterTokenType); + List drill = gson.fromJson(view.getDrillFields(), tokenType); DatasetTableField datasetTableFieldObj = DatasetTableField.builder().tableId(view.getTableId()).checked(Boolean.TRUE).build(); @@ -741,14 +747,12 @@ public class ChartViewService { // 如果是插件视图 走插件内部的逻辑 if (ObjectUtils.isNotEmpty(view.getIsPlugin()) && view.getIsPlugin()) { - Map> fieldMap = new HashMap<>(); - List xAxisExt = new Gson().fromJson(view.getXAxisExt(), new TypeToken>() { - }.getType()); - fieldMap.put("xAxisExt", xAxisExt); - fieldMap.put("xAxis", xAxis); + Map> fieldMap = ObjectUtils.isEmpty(extFieldsMap) ? new HashMap<>() : extFieldsMap; + fieldMap.put("yAxis", yAxis); fieldMap.put("extStack", extStack); fieldMap.put("extBubble", extBubble); + fieldMap.put("xAxis", xAxis); PluginViewParam pluginViewParam = buildPluginParam(fieldMap, fieldCustomFilter, extFilterList, ds, table, view); String sql = pluginViewSql(pluginViewParam, view); if (StringUtils.isBlank(sql)) { @@ -775,7 +779,7 @@ public class ChartViewService { } // DatasourceProvider datasourceProvider = ProviderFactory.getProvider(ds.getType()); datasourceRequest.setDatasource(ds); - DataTableInfoDTO dataTableInfoDTO = new Gson().fromJson(table.getInfo(), DataTableInfoDTO.class); + DataTableInfoDTO dataTableInfoDTO = gson.fromJson(table.getInfo(), DataTableInfoDTO.class); QueryProvider qp = ProviderFactory.getQueryProvider(ds.getType()); if (StringUtils.equalsIgnoreCase(table.getType(), "db")) { datasourceRequest.setTable(dataTableInfoDTO.getTable()); @@ -803,7 +807,7 @@ public class ChartViewService { datasourceRequest.setQuery(qp.getSQLAsTmp(dataTableInfoDTO.getSql(), xAxis, yAxis, fieldCustomFilter, extFilterList, view)); } } else if (StringUtils.equalsIgnoreCase(table.getType(), "custom")) { - DataTableInfoDTO dt = new Gson().fromJson(table.getInfo(), DataTableInfoDTO.class); + DataTableInfoDTO dt = gson.fromJson(table.getInfo(), DataTableInfoDTO.class); List list = dataSetTableUnionService.listByTableId(dt.getList().get(0).getTableId()); String sql = dataSetTableService.getCustomSQLDatasource(dt, list, ds); if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) { @@ -818,7 +822,7 @@ public class ChartViewService { datasourceRequest.setQuery(qp.getSQLAsTmp(sql, xAxis, yAxis, fieldCustomFilter, extFilterList, view)); } } else if (StringUtils.equalsIgnoreCase(table.getType(), "union")) { - DataTableInfoDTO dt = new Gson().fromJson(table.getInfo(), DataTableInfoDTO.class); + DataTableInfoDTO dt = gson.fromJson(table.getInfo(), DataTableInfoDTO.class); Map sqlMap = dataSetTableService.getUnionSQLDatasource(dt, ds); String sql = (String) sqlMap.get("sql"); @@ -1376,7 +1380,7 @@ public class ChartViewService { public List getFieldData(String id, ChartExtRequest requestList, boolean cache, String fieldId) throws Exception { ChartViewDTO view = getOne(id, requestList.getQueryFrom()); List sqlData = sqlData(view, requestList, cache, fieldId); - List xAxis = new Gson().fromJson(view.getXAxis(), new TypeToken>() { + List xAxis = gson.fromJson(view.getXAxis(), new TypeToken>() { }.getType()); DatasetTableField field = dataSetTableFieldsService.get(fieldId); diff --git a/backend/src/main/java/io/dataease/service/dataset/DataSetTableService.java b/backend/src/main/java/io/dataease/service/dataset/DataSetTableService.java index 227f06975c..a7339ea79c 100644 --- a/backend/src/main/java/io/dataease/service/dataset/DataSetTableService.java +++ b/backend/src/main/java/io/dataease/service/dataset/DataSetTableService.java @@ -2270,7 +2270,7 @@ public class DataSetTableService { record.setSyncStatus(JobStatus.Error.name()); example.clear(); example.createCriteria().andSyncStatusEqualTo(JobStatus.Underway.name()) - .andIdIn(datasetTableTasks.stream().map(DatasetTableTask::getTableId).collect(Collectors.toList())); + .andIdIn(jobStoppeddDatasetTables.stream().map(DatasetTable::getId).collect(Collectors.toList())); datasetTableMapper.updateByExampleSelective(record, example); //TaskLog diff --git a/backend/src/main/java/io/dataease/service/datasource/DatasourceService.java b/backend/src/main/java/io/dataease/service/datasource/DatasourceService.java index 58920ad4bc..f096eed2b9 100644 --- a/backend/src/main/java/io/dataease/service/datasource/DatasourceService.java +++ b/backend/src/main/java/io/dataease/service/datasource/DatasourceService.java @@ -27,11 +27,13 @@ import io.dataease.i18n.Translator; import io.dataease.plugins.common.base.domain.*; import io.dataease.plugins.common.base.mapper.DatasetTableMapper; import io.dataease.plugins.common.base.mapper.DatasourceMapper; +import io.dataease.plugins.common.constants.DatasourceCalculationMode; import io.dataease.plugins.common.constants.DatasourceTypes; import io.dataease.plugins.common.dto.datasource.DataSourceType; import io.dataease.plugins.common.dto.datasource.TableDesc; import io.dataease.plugins.common.request.datasource.DatasourceRequest; import io.dataease.plugins.config.SpringContextUtil; +import io.dataease.plugins.datasource.entity.JdbcConfiguration; import io.dataease.plugins.datasource.provider.Provider; import io.dataease.provider.ProviderFactory; import io.dataease.provider.datasource.ApiProvider; @@ -41,10 +43,8 @@ import io.dataease.service.sys.SysAuthService; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -131,6 +131,13 @@ public class DatasourceService { datasourceDTO.setCalculationMode(dataSourceType.getCalculationMode()); } }); + if(!datasourceDTO.getType().equalsIgnoreCase(DatasourceTypes.api.toString())){ + JdbcConfiguration configuration = new Gson().fromJson(datasourceDTO.getConfiguration(), JdbcConfiguration.class); + if(StringUtils.isNotEmpty(configuration.getCustomDriver()) && !configuration.getCustomDriver().equalsIgnoreCase("default")){ + datasourceDTO.setCalculationMode(DatasourceCalculationMode.DIRECT); + } + } + if(datasourceDTO.getType().equalsIgnoreCase(DatasourceTypes.mysql.toString())){ datasourceDTO.setConfiguration(JSONObject.toJSONString(new Gson().fromJson(datasourceDTO.getConfiguration(), MysqlConfiguration.class)) ); } diff --git a/backend/src/main/java/io/dataease/service/datasource/DriverService.java b/backend/src/main/java/io/dataease/service/datasource/DriverService.java new file mode 100644 index 0000000000..5f48ef2434 --- /dev/null +++ b/backend/src/main/java/io/dataease/service/datasource/DriverService.java @@ -0,0 +1,176 @@ +package io.dataease.service.datasource; + +import io.dataease.commons.utils.BeanUtils; +import io.dataease.commons.utils.DeFileUtils; +import io.dataease.dto.DriverDTO; +import io.dataease.plugins.common.base.domain.DeDriver; +import io.dataease.plugins.common.base.domain.DeDriverDetails; +import io.dataease.plugins.common.base.domain.DeDriverDetailsExample; +import io.dataease.plugins.common.base.mapper.DeDriverDetailsMapper; +import io.dataease.plugins.common.base.mapper.DeDriverMapper; +import io.dataease.plugins.datasource.provider.ExtendedJdbcClassLoader; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.UUID; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +@Transactional(rollbackFor = Exception.class) +@Service +public class DriverService { + + private static String DRIVER_PATH = "/opt/dataease/drivers/custom/"; + @Resource + private DeDriverMapper deDriverMapper; + @Resource + private DeDriverDetailsMapper deDriverDetailsMapper; + @Resource + private DatasourceService datasourceService; + + + public List list() throws Exception { + List driverDTOS = new ArrayList<>(); + deDriverMapper.selectByExample(null).forEach(deDriver -> { + DriverDTO driverDTO = new DriverDTO(); + BeanUtils.copyBean(driverDTO, deDriver); + datasourceService.types().forEach(dataSourceType -> { + if (dataSourceType.getType().equalsIgnoreCase(deDriver.getType())) { + driverDTO.setTypeDesc(dataSourceType.getName()); + } + + }); + driverDTOS.add(driverDTO); + }); + return driverDTOS; + } + + public void delete(String driverId) { + deDriverMapper.deleteByPrimaryKey(driverId); + + DeDriverDetailsExample example = new DeDriverDetailsExample(); + example.createCriteria().andDeDriverIdEqualTo(driverId); + deDriverDetailsMapper.deleteByExample(example); + DeFileUtils.deleteFile(DRIVER_PATH + driverId + "/"); + } + + public DeDriver save(DeDriver deDriver) { + deDriver.setCreateTime(System.currentTimeMillis()); + deDriver.setId(UUID.randomUUID().toString()); + deDriverMapper.insert(deDriver); + return deDriver; + } + + public DeDriver update(DeDriver deDriver) { + deDriverMapper.updateByPrimaryKey(deDriver); + return deDriver; + } + + + public List listDriverDetails(String driverId) { + DeDriverDetailsExample example = new DeDriverDetailsExample(); + example.createCriteria().andDeDriverIdEqualTo(driverId); + return deDriverDetailsMapper.selectByExampleWithBLOBs(example); + } + + public void deleteDriverFile(String driverFileId) { + DeDriverDetails deDriverDetails = deDriverDetailsMapper.selectByPrimaryKey(driverFileId); + DeFileUtils.deleteFile(DRIVER_PATH + deDriverDetails.getDeDriverId() + "/" + deDriverDetails.getFileName()); + deDriverDetailsMapper.deleteByPrimaryKey(driverFileId); + } + + public DeDriverDetails saveJar(MultipartFile file, String driverId) throws Exception { + String filename = file.getOriginalFilename(); + String dirPath = DRIVER_PATH + driverId + "/"; + String filePath = dirPath + filename; + + saveFile(file, dirPath, filePath); + List jdbcList = new ArrayList<>(); + String version = ""; +// ExtendedJdbcClassLoader extendedJdbcClassLoader = new ExtendedJdbcClassLoader(new URL[]{new File(filePath).toURI().toURL()}, null); +// for (String className : getClassNameFrom(filePath)) { +// if (isChildClass(className, java.sql.Driver.class, extendedJdbcClassLoader)) { +// jdbcList.add(className); +// version = classVersion(extendedJdbcClassLoader, className); +// } +// } + + DeDriverDetails deDriverDetails = new DeDriverDetails(); + deDriverDetails.setId(UUID.randomUUID().toString()); + deDriverDetails.setDeDriverId(driverId); + deDriverDetails.setVersion(version); + deDriverDetails.setFileName(filename); + deDriverDetails.setDriverClass(String.join(",", jdbcList)); + deDriverDetailsMapper.insert(deDriverDetails); + return deDriverDetails; + } + + private List getClassNameFrom(String jarName) { + List fileList = new ArrayList(); + try { + JarFile jarFile = new JarFile(new File(jarName)); + Enumeration en = jarFile.entries(); // 枚举获得JAR文件内的实体,即相对路径 + while (en.hasMoreElements()) { + String name1 = en.nextElement().getName(); + if (!name1.endsWith(".class")) {//不是class文件 + continue; + } + String name2 = name1.substring(0, name1.lastIndexOf(".class")); + String name3 = name2.replaceAll("/", "."); + fileList.add(name3); + } + } catch (IOException e) { + e.printStackTrace(); + } + return fileList; + } + + public boolean isChildClass(String className, Class parentClazz, ExtendedJdbcClassLoader extendedJdbcClassLoader) { + if (className == null) return false; + + Class clazz = null; + try { + clazz = extendedJdbcClassLoader.loadClass(className); + if (Modifier.isAbstract(clazz.getModifiers())) {//抽象类忽略 + return false; + } + if (Modifier.isInterface(clazz.getModifiers())) {//接口忽略 + return false; + } + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return parentClazz.isAssignableFrom(clazz); + } + + private String classVersion(ExtendedJdbcClassLoader extendedJdbcClassLoader, String className) throws Exception { + Class clazz = extendedJdbcClassLoader.loadClass(className); + return clazz.getPackage().getImplementationVersion(); + } + + private String saveFile(MultipartFile file, String dirPath, String filePath) throws Exception { + File p = new File(dirPath); + if (!p.exists()) { + p.mkdirs(); + } + File f = new File(filePath); + FileOutputStream fileOutputStream = new FileOutputStream(f); + fileOutputStream.write(file.getBytes()); + fileOutputStream.flush(); + fileOutputStream.close(); + return filePath; + } + + +} diff --git a/backend/src/main/resources/db/migration/V35__1.11.sql b/backend/src/main/resources/db/migration/V35__1.11.sql index 71ebcc2c8c..11d973b236 100644 --- a/backend/src/main/resources/db/migration/V35__1.11.sql +++ b/backend/src/main/resources/db/migration/V35__1.11.sql @@ -1,6 +1,27 @@ ALTER TABLE `panel_group` ADD COLUMN `status` varchar(255) NULL DEFAULT 'publish' COMMENT '1.publish--发布 2.unpublished--未发布' AFTER `mobile_layout`; +CREATE TABLE `de_driver` ( + `id` varchar(50) NOT NULL COMMENT '主键', + `name` varchar(50) NOT NULL COMMENT '用户ID', + `create_time` bigint(13) NOT NULL COMMENT '创健时间', + `type` varchar(255) DEFAULT NULL COMMENT '数据源类型', + `driver_class` varchar(255) DEFAULT NULL COMMENT '驱动类', + `desc` varchar(255) DEFAULT NULL COMMENT '描述', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='驱动'; + + +CREATE TABLE `de_driver_details` ( + `id` varchar(50) NOT NULL COMMENT '主键', + `de_driver_id` varchar(50) NOT NULL COMMENT '驱动主键', + `file_name` varchar(255) DEFAULT NULL COMMENT '名称', + `version` varchar(255) DEFAULT NULL COMMENT '版本', + `driver_class` longtext DEFAULT NULL COMMENT '驱动类', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='驱动详情'; + + UPDATE `sys_menu` SET `sub_count` = 1 WHERE `menu_id` = 40; INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (401, 40, 0, 2, '查看系统模板', NULL, NULL, 999, NULL, NULL, 0, 0, 0, 'sys-template:read', NULL, NULL, 1614930862373, 1614930862373); diff --git a/frontend/src/api/system/datasource.js b/frontend/src/api/system/datasource.js index f85c70d160..f7ff85e801 100644 --- a/frontend/src/api/system/datasource.js +++ b/frontend/src/api/system/datasource.js @@ -15,6 +15,13 @@ export function listDatasource() { method: 'get' }) } +export function listDrivers() { + return request({ + url: '/driver/list', + loading: true, + method: 'post' + }) +} export function listDatasourceType() { return request({ url: '/datasource/types', @@ -29,6 +36,13 @@ export function listDatasourceByType(type) { method: 'get' }) } +export function listDriverByType(type) { + return request({ + url: '/driver/list/' + type, + loading: true, + method: 'get' + }) +} export function addDs(data) { return request({ url: 'datasource/add/', @@ -91,4 +105,47 @@ export function checkApiDatasource(data) { }) } +export function addDriver(data) { + return request({ + url: '/driver/save', + method: 'post', + loading: true, + data + }) +} + +export function listDriverDetails(id) { + return request({ + url: '/driver/listDriverDetails/' + id, + method: 'get', + loading: true + }) +} + +export function deleteDriverFile(data) { + return request({ + url: '/driver/deleteDriverFile', + method: 'post', + loading: true, + data + }) +} + +export function delDriver(data) { + return request({ + url: 'driver/delete', + loading: true, + method: 'post', + data + }) +} +export function updateDriver(data) { + return request({ + url: 'driver/update/', + loading: true, + method: 'post', + data + }) +} + export default { dsGrid, addDs, editDs, delDs, validateDs, listDatasource, getSchema } diff --git a/frontend/src/lang/en.js b/frontend/src/lang/en.js index 348729085a..76d5c9a5ea 100644 --- a/frontend/src/lang/en.js +++ b/frontend/src/lang/en.js @@ -1328,7 +1328,22 @@ export default { sql_ds_union_error: 'Direct connect SQL dataset can not be union', api_data: 'API dataset' }, + driver:{ + driver: 'Driver', + please_choose_driver: 'Please choose driver', + mgm: 'Driver', + add: 'ADD Driver', + modify: 'Modify', + show_info: 'Driver info', + file_name: 'File name', + version: 'version' + }, datasource: { + auth_method: 'Auth method', + passwd: 'UserName Password', + kerbers_info: 'Please make sure krb5 Conf, KeyTab key, added to path: /opt/dataease/conf', + client_principal: 'Client Principal', + keytab_Key_path: 'Keytab Key Path', datasource: 'Data Source', please_select_left: 'Please select the data source from the left', show_info: 'Data Source Info', diff --git a/frontend/src/lang/tw.js b/frontend/src/lang/tw.js index bb3bf25c6a..866cb49c7e 100644 --- a/frontend/src/lang/tw.js +++ b/frontend/src/lang/tw.js @@ -1328,7 +1328,22 @@ export default { sql_ds_union_error: '直連模式下SQL數據集,不支持關聯', api_data: 'API 數據集' }, + driver:{ + driver: '驅動', + please_choose_driver: '青選擇驅動', + mgm: '驅動管理', + add: '添加驅動', + modify: '修改', + show_info: '驅動信息', + file_name: '文件名', + version: '版本' + }, datasource: { + auth_method: '認證方式', + passwd: '用户名密码', + kerbers_info: '请确保 krb5.Conf、Keytab Key,已经添加到路径:/opt/dataease/conf', + client_principal: 'Client Principal', + keytab_Key_path: 'Keytab Key Path', datasource: '數據源', please_select_left: '請從左側選擇數據源', show_info: '數據源信息', diff --git a/frontend/src/lang/zh.js b/frontend/src/lang/zh.js index bcc4f0f0ea..344decdc99 100644 --- a/frontend/src/lang/zh.js +++ b/frontend/src/lang/zh.js @@ -1330,7 +1330,22 @@ export default { sql_ds_union_error: '直连模式下SQL数据集,不支持关联', api_data: 'API 数据集' }, + driver:{ + driver: '驱动', + please_choose_driver: '请选择驱动', + mgm: '驱动管理', + add: '添加驱动', + modify: '修改', + show_info: '驱动信息', + file_name: '文件名', + version: '版本' + }, datasource: { + auth_method: '认证方式', + passwd: '用户名密码', + kerbers_info: '请确保 krb5.Conf、Keytab Key,已经添加到路径:/opt/dataease/conf', + client_principal: 'Client Principal', + keytab_Key_path: 'Keytab Key Path', datasource: '数据源', please_select_left: '请从左侧选择数据源', show_info: '数据源信息', diff --git a/frontend/src/views/chart/chart/bar/bar_antv.js b/frontend/src/views/chart/chart/bar/bar_antv.js index d84248628c..96e127edac 100644 --- a/frontend/src/views/chart/chart/bar/bar_antv.js +++ b/frontend/src/views/chart/chart/bar/bar_antv.js @@ -10,6 +10,7 @@ import { getSlider, getAnalyse } from '@/views/chart/chart/common/common_antv' +import { antVCustomColor } from '@/views/chart/chart/util' export function baseBarOptionAntV(plot, container, chart, action, isGroup, isStack) { // theme @@ -98,6 +99,8 @@ export function baseBarOptionAntV(plot, container, chart, action, isGroup, isSta } else { delete options.isStack } + // custom color + options.color = antVCustomColor(chart) // 开始渲染 if (plot) { @@ -198,6 +201,8 @@ export function hBaseBarOptionAntV(plot, container, chart, action, isGroup, isSt } else { delete options.isStack } + // custom color + options.color = antVCustomColor(chart) // 开始渲染 if (plot) { diff --git a/frontend/src/views/chart/chart/funnel/funnel_antv.js b/frontend/src/views/chart/chart/funnel/funnel_antv.js index 0d940e4a4a..eaa588f6da 100644 --- a/frontend/src/views/chart/chart/funnel/funnel_antv.js +++ b/frontend/src/views/chart/chart/funnel/funnel_antv.js @@ -1,5 +1,6 @@ import { getLabel, getLegend, getPadding, getTheme, getTooltip } from '@/views/chart/chart/common/common_antv' import { Funnel } from '@antv/g2plot' +import { antVCustomColor } from '@/views/chart/chart/util' export function baseFunnelOptionAntV(plot, container, chart, action) { // theme @@ -62,6 +63,8 @@ export function baseFunnelOptionAntV(plot, container, chart, action) { const s = JSON.parse(JSON.stringify(customAttr.size)) } } + // custom color + options.color = antVCustomColor(chart) // 开始渲染 if (plot) { diff --git a/frontend/src/views/chart/chart/line/line_antv.js b/frontend/src/views/chart/chart/line/line_antv.js index eb7d0eacc0..d4017b2d44 100644 --- a/frontend/src/views/chart/chart/line/line_antv.js +++ b/frontend/src/views/chart/chart/line/line_antv.js @@ -10,6 +10,7 @@ import { getSlider, getAnalyse } from '@/views/chart/chart/common/common_antv' +import { antVCustomColor } from '@/views/chart/chart/util' export function baseLineOptionAntV(plot, container, chart, action) { // theme @@ -90,6 +91,8 @@ export function baseLineOptionAntV(plot, container, chart, action) { } } } + // custom color + options.color = antVCustomColor(chart) // 开始渲染 if (plot) { @@ -184,6 +187,8 @@ export function baseAreaOptionAntV(plot, container, chart, action) { } } } + // custom color + options.color = antVCustomColor(chart) // 开始渲染 if (plot) { diff --git a/frontend/src/views/chart/chart/pie/pie_antv.js b/frontend/src/views/chart/chart/pie/pie_antv.js index d66180b364..7eb3c42d40 100644 --- a/frontend/src/views/chart/chart/pie/pie_antv.js +++ b/frontend/src/views/chart/chart/pie/pie_antv.js @@ -7,6 +7,7 @@ import { } from '@/views/chart/chart/common/common_antv' import { Pie, Rose } from '@antv/g2plot' +import { antVCustomColor } from '@/views/chart/chart/util' export function basePieOptionAntV(plot, container, chart, action) { // theme @@ -84,6 +85,8 @@ export function basePieOptionAntV(plot, container, chart, action) { options.innerRadius = parseFloat(parseInt(s.pieInnerRadius) / 100) } } + // custom color + options.color = antVCustomColor(chart) // 开始渲染 if (plot) { @@ -165,6 +168,8 @@ export function basePieRoseOptionAntV(plot, container, chart, action) { options.innerRadius = parseFloat(parseInt(s.pieInnerRadius) / 100) } } + // custom color + options.color = antVCustomColor(chart) // 开始渲染 if (plot) { diff --git a/frontend/src/views/chart/chart/radar/radar_antv.js b/frontend/src/views/chart/chart/radar/radar_antv.js index a4f25b2ac4..115a336276 100644 --- a/frontend/src/views/chart/chart/radar/radar_antv.js +++ b/frontend/src/views/chart/chart/radar/radar_antv.js @@ -1,5 +1,6 @@ import { getLabel, getLegend, getPadding, getTheme, getTooltip } from '@/views/chart/chart/common/common_antv' import { Radar } from '@antv/g2plot' +import { antVCustomColor } from '@/views/chart/chart/util' export function baseRadarOptionAntV(plot, container, chart, action) { // theme @@ -132,6 +133,9 @@ export function baseRadarOptionAntV(plot, container, chart, action) { options.xAxis = xAxis options.yAxis = yAxis + // custom color + options.color = antVCustomColor(chart) + // 开始渲染 if (plot) { plot.destroy() diff --git a/frontend/src/views/chart/chart/scatter/scatter_antv.js b/frontend/src/views/chart/chart/scatter/scatter_antv.js index 77fd43d137..6bfc0529a6 100644 --- a/frontend/src/views/chart/chart/scatter/scatter_antv.js +++ b/frontend/src/views/chart/chart/scatter/scatter_antv.js @@ -11,6 +11,7 @@ import { } from '@/views/chart/chart/common/common_antv' import { Scatter } from '@antv/g2plot' +import { antVCustomColor } from '@/views/chart/chart/util' export function baseScatterOptionAntV(plot, container, chart, action) { // theme @@ -86,6 +87,8 @@ export function baseScatterOptionAntV(plot, container, chart, action) { options.shape = s.scatterSymbol } } + // custom color + options.color = antVCustomColor(chart) // 开始渲染 if (plot) { diff --git a/frontend/src/views/chart/chart/util.js b/frontend/src/views/chart/chart/util.js index 13aa623460..d853803129 100644 --- a/frontend/src/views/chart/chart/util.js +++ b/frontend/src/views/chart/chart/util.js @@ -2263,3 +2263,124 @@ export function customSort(custom, data) { return joinArr.concat(subArr) } + +export function customColor(custom, res, colors) { + const result = [] + for (let i = 0; i < res.length; i++) { + const r = res[i] + let flag = false + for (let j = 0; j < custom.length; j++) { + const c = custom[j] + if (r.name === c.name) { + flag = true + result.push(c) + } + } + if (!flag) { + result.push(r) + } + } + return result +} + +export function getColors(chart, colors, reset) { + // 自定义颜色,先按照没有设定的情况,并排好序,当做最终结果 + let seriesColors = [] + let series + if (chart.type.includes('stack')) { + if (chart.data) { + const data = chart.data.datas + const stackData = [] + for (let i = 0; i < data.length; i++) { + const s = data[i] + stackData.push(s.category) + } + const sArr = stackData.filter(function(item, index, stackData) { + return stackData.indexOf(item, 0) === index + }) + + for (let i = 0; i < sArr.length; i++) { + const s = sArr[i] + seriesColors.push({ + name: s, + color: colors[i % colors.length], + isCustom: false + }) + } + } + } else if (chart.type.includes('bar') || chart.type.includes('line') || chart.type.includes('scatter') || chart.type.includes('radar')) { + if (Object.prototype.toString.call(chart.yaxis) === '[object Array]') { + series = JSON.parse(JSON.stringify(chart.yaxis)) + } else { + series = JSON.parse(chart.yaxis) + } + for (let i = 0; i < series.length; i++) { + const s = series[i] + seriesColors.push({ + name: s.name, + color: colors[i % colors.length], + isCustom: false + }) + } + } else { + if (chart.data) { + const data = chart.data.datas + // data 的维度值,需要根据自定义顺序排序 + // let customSortData + // if (Object.prototype.toString.call(chart.customSort) === '[object Array]') { + // customSortData = JSON.parse(JSON.stringify(chart.customSort)) + // } else { + // customSortData = JSON.parse(chart.customSort) + // } + // if (customSortData && customSortData.length > 0) { + // data = customSort(customSortData, data) + // } + + for (let i = 0; i < data.length; i++) { + const s = data[i] + seriesColors.push({ + name: s.field, + color: colors[i % colors.length], + isCustom: false + }) + } + } + } + // 如果有自定义,则与上述中的结果合并。 + // res,custom,以custom为准,去掉res中不存在的,并将custom中name一样的color赋值给res,不存在的name,即新增值,使用i % colors.length,从配色方案中选 + if (!reset) { + let sc = null + if (Object.prototype.toString.call(chart.customAttr) === '[object Object]') { + sc = JSON.parse(JSON.stringify(chart.customAttr)).color.seriesColors + } else { + sc = JSON.parse(chart.customAttr).color.seriesColors + } + if (sc && sc.length > 0) { + seriesColors = customColor(sc, seriesColors) + } + // 根据isCustom字段,修正color + for (let i = 0; i < seriesColors.length; i++) { + if (!seriesColors[i].isCustom) { + seriesColors[i].color = colors[i % colors.length] + } + } + } + return seriesColors +} + +export function antVCustomColor(chart) { + const colors = [] + if (chart.customAttr) { + const customAttr = JSON.parse(chart.customAttr) + // color + if (customAttr.color) { + const c = JSON.parse(JSON.stringify(customAttr.color)) + + const customColors = getColors(chart, c.colors, false) + for (let i = 0; i < customColors.length; i++) { + colors.push(hexColorToRGBA(customColors[i].color, c.alpha)) + } + } + } + return colors +} diff --git a/frontend/src/views/chart/components/shape-attr/ColorSelectorExt.vue b/frontend/src/views/chart/components/shape-attr/ColorSelectorExt.vue new file mode 100644 index 0000000000..902097eaed --- /dev/null +++ b/frontend/src/views/chart/components/shape-attr/ColorSelectorExt.vue @@ -0,0 +1,416 @@ + + + + + diff --git a/frontend/src/views/chart/group/Group.vue b/frontend/src/views/chart/group/Group.vue index 6fe4be270c..43097c1a5f 100644 --- a/frontend/src/views/chart/group/Group.vue +++ b/frontend/src/views/chart/group/Group.vue @@ -822,6 +822,7 @@ export default { view.customFilter = JSON.stringify([]) view.drillFields = JSON.stringify([]) view.extBubble = JSON.stringify([]) + view.viewFields = JSON.stringify([]) this.setChartDefaultOptions(view) const _this = this post('/chart/view/newOne/' + this.panelInfo.id, view, true).then(response => { diff --git a/frontend/src/views/chart/view/ChartEdit.vue b/frontend/src/views/chart/view/ChartEdit.vue index ab20dc0c1c..b5f4b023c8 100644 --- a/frontend/src/views/chart/view/ChartEdit.vue +++ b/frontend/src/views/chart/view/ChartEdit.vue @@ -658,6 +658,8 @@ :chart="chart" :properties="chartProperties" :property-inner-all="chartPropertyInnerAll" + :dimension-data="dimensionData" + :quota-data="quotaData" @calcStyle="calcStyle" @onColorChange="onColorChange" @onSizeChange="onSizeChange" @@ -1083,6 +1085,7 @@ export default { yaxisExt: [], extStack: [], drillFields: [], + viewFields: [], extBubble: [], show: true, type: 'bar', @@ -1535,6 +1538,7 @@ export default { this.view = JSON.parse(JSON.stringify(view)) // stringify json param view.xaxis = JSON.stringify(view.xaxis) + view.viewFields = JSON.stringify(view.viewFields) view.xaxisExt = JSON.stringify(view.xaxisExt) view.yaxis = JSON.stringify(view.yaxis) view.yaxisExt = JSON.stringify(view.yaxisExt) @@ -1594,6 +1598,7 @@ export default { const view = this.buildParam(true, 'chart', false, switchType) if (!view) return viewEditSave(this.panelInfo.id, view).then(() => { + this.getData(this.param.id) bus.$emit('view-in-cache', { type: 'propChange', viewId: this.param.id }) }) }, @@ -1602,6 +1607,7 @@ export default { // 将视图传入echart...组件 const view = JSON.parse(JSON.stringify(this.view)) view.xaxis = JSON.stringify(this.view.xaxis) + view.viewFields = JSON.stringify(this.view.viewFields) view.xaxisExt = JSON.stringify(this.view.xaxisExt) view.yaxis = JSON.stringify(this.view.yaxis) view.yaxisExt = JSON.stringify(this.view.yaxisExt) @@ -1666,6 +1672,7 @@ export default { }).then(response => { this.initTableData(response.data.tableId) this.view = JSON.parse(JSON.stringify(response.data)) + this.view.viewFields = this.view.viewFields ? JSON.parse(this.view.viewFields) : [] this.view.xaxis = this.view.xaxis ? JSON.parse(this.view.xaxis) : [] this.view.xaxisExt = this.view.xaxisExt ? JSON.parse(this.view.xaxisExt) : [] this.view.yaxis = this.view.yaxis ? JSON.parse(this.view.yaxis) : [] @@ -1716,6 +1723,7 @@ export default { this.initTableData(response.data.tableId) } this.view = JSON.parse(JSON.stringify(response.data)) + this.view.viewFields = this.view.viewFields ? JSON.parse(this.view.viewFields) : [] this.view.xaxis = this.view.xaxis ? JSON.parse(this.view.xaxis) : [] this.view.xaxisExt = this.view.xaxisExt ? JSON.parse(this.view.xaxisExt) : [] this.view.yaxis = this.view.yaxis ? JSON.parse(this.view.yaxis) : [] @@ -2412,6 +2420,8 @@ export default { this.view.customAttr.label.position = 'middle' } } + // reset custom colors + this.view.customAttr.color.seriesColors = [] }, valueFormatter(item) { diff --git a/frontend/src/views/chart/view/ChartStyle.vue b/frontend/src/views/chart/view/ChartStyle.vue index 35fa9389e7..85c323bea6 100644 --- a/frontend/src/views/chart/view/ChartStyle.vue +++ b/frontend/src/views/chart/view/ChartStyle.vue @@ -5,7 +5,7 @@ style="overflow:auto;border-right: 1px solid #e6e6e6;height: 100%;width: 100%;" class="attr-style theme-border-class" :component-name="view.type + '-style'" - :obj="{view, param, chart}" + :obj="{view, param, chart, dimensionData, quotaData}" />
{{ $t('chart.shape_attr') }} + + import PluginCom from '@/views/system/plugin/PluginCom' import ColorSelector from '@/views/chart/components/shape-attr/ColorSelector' +import ColorSelectorExt from '@/views/chart/components/shape-attr/ColorSelectorExt' import SizeSelector from '@/views/chart/components/shape-attr/SizeSelector' import SizeSelectorAntV from '@/views/chart/components/shape-attr/SizeSelectorAntV' import LabelSelector from '@/views/chart/components/shape-attr/LabelSelector' @@ -315,7 +325,13 @@ export default { TotalCfg, TooltipSelectorAntV, TooltipSelector, - LabelSelectorAntV, LabelSelector, SizeSelectorAntV, SizeSelector, ColorSelector, PluginCom + LabelSelectorAntV, + LabelSelector, + SizeSelectorAntV, + SizeSelector, + ColorSelector, + PluginCom, + ColorSelectorExt }, props: { chart: { @@ -334,6 +350,14 @@ export default { type: Array, required: true }, + dimensionData: { + type: Array, + required: true + }, + quotaData: { + type: Array, + required: true + }, propertyInnerAll: { type: Object, required: true @@ -358,7 +382,7 @@ export default { }, methods: { showProperties(property) { - return this.properties.includes(property) + return this.properties && this.properties.length && this.properties.includes(property) }, showPropertiesCollapse(propertiesInfo) { let includeCount = 0 diff --git a/frontend/src/views/system/datasource/DriverForm.vue b/frontend/src/views/system/datasource/DriverForm.vue new file mode 100644 index 0000000000..8d4784cb51 --- /dev/null +++ b/frontend/src/views/system/datasource/DriverForm.vue @@ -0,0 +1,294 @@ + + + + diff --git a/frontend/src/views/system/datasource/DsConfiguration.vue b/frontend/src/views/system/datasource/DsConfiguration.vue index a7fe183691..4d2d421fbb 100644 --- a/frontend/src/views/system/datasource/DsConfiguration.vue +++ b/frontend/src/views/system/datasource/DsConfiguration.vue @@ -3,13 +3,13 @@ @@ -77,7 +77,8 @@
- + @@ -103,7 +104,10 @@ @@ -125,20 +129,55 @@ - - {{ $t('datasource.oracle_sid') }} + {{ + $t('datasource.oracle_sid') + }} + {{ $t('datasource.oracle_service_name') }} - + + + + + + + + + + + +

+ {{$t('datasource.kerbers_info')}} +

+
+ + + + + + - @@ -164,21 +203,26 @@ - {{ $t('datasource.get_schema') }} + v-if="form.type=='oracle' || form.type=='sqlServer' || form.type=='pg' || form.type=='redshift' || form.type=='db2'"> + {{ + $t('datasource.get_schema') + }} + - + - @@ -208,7 +252,6 @@ diff --git a/frontend/src/views/system/plugin/index.vue b/frontend/src/views/system/plugin/index.vue index 3713f245ee..59276e27c5 100644 --- a/frontend/src/views/system/plugin/index.vue +++ b/frontend/src/views/system/plugin/index.vue @@ -8,7 +8,6 @@ @search="search" >