第二次提交并推送代码

This commit is contained in:
root 2025-02-27 14:59:16 +08:00
parent 7e87c8e5b5
commit 6a8c6da49f
449 changed files with 57161 additions and 1 deletions

View File

@ -0,0 +1,20 @@
package io.dataease;
import io.dataease.listener.EhCacheStartListener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication(exclude = {QuartzAutoConfiguration.class})
@EnableCaching
@EnableScheduling
public class CoreApplication {
public static void main(String[] args) {
SpringApplication context = new SpringApplication(CoreApplication.class);
context.addInitializers(new EhCacheStartListener());
context.run(args);
}
}

View File

@ -0,0 +1,70 @@
package io.dataease;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.TemplateType;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
/**
* 研发使用请勿提交
*/
public class MybatisPlusGenerator {
/**
* 为什么不从配置文件读
* 第一 我嫌麻烦
* 第二 后面配置会放到nacos读起来更麻烦了
*/
private static final String url = "jdbc:mysql://localhost:3306/dataease?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false";
private static final String username = "root";
private static final String password = "123456";
/**
* 业务模块例如datasource,dataset,panel等
*/
private static final String busi = "visualization";
/**
* 这是要生成代码的表名称
*/
private static final String TABLE_NAME = "data_visualization_info";
/**
* 下面两个配置基本上不用动
*/
private static final String codeDir = "src/main/java/";
private static final String AUTO_DAO = ".dao.auto";
public static void main(String[] args) throws Exception{
String path = System.getProperty("java.class.path");
path = path.substring(0, path.indexOf("target/classes"));
String packageName = packageName() + "." + busi + AUTO_DAO;
String outPath = path + codeDir;
DataSourceConfig.Builder dsc = new DataSourceConfig.Builder(url, username, password);
dsc.typeConvert( MySqlTypeConvert.INSTANCE);
FastAutoGenerator.create(dsc)
.globalConfig(builder -> {
builder.author("fit2cloud").outputDir(outPath);
})
.packageConfig(builder -> {
builder.parent(packageName);
})
.strategyConfig(builder -> {
builder.addInclude(TABLE_NAME).entityBuilder().enableFileOverride().mapperBuilder().mapperAnnotation(org.apache.ibatis.annotations.Mapper.class).enableFileOverride(); //设置需要生成的表名
})
.templateConfig(builder -> {
builder.disable(TemplateType.CONTROLLER).disable(TemplateType.SERVICE).disable(TemplateType.SERVICE_IMPL).disable(TemplateType.XML).build();
})
.execute();
}
private static String packageName() {
return new Object() {
public String getPackageName() {
String packageName = this.getClass().getPackageName();
return packageName;
}
}.getPackageName();
}
}

View File

@ -0,0 +1,34 @@
package io.dataease.ai.service;
import io.dataease.api.ai.AiComponentApi;
import io.dataease.commons.utils.UrlTestUtils;
import io.dataease.system.manage.SysParameterManage;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* @author : WangJiaHao
* @date : 2024/3/27 09:47
*/
@RestController
@RequestMapping("aiBase")
public class AiBaseService implements AiComponentApi {
@Resource
private SysParameterManage sysParameterManage;
@Override
public Map<String, String> findTargetUrl() {
Map<String, String> templateParams = sysParameterManage.groupVal("ai.");
if (templateParams != null && StringUtils.isNotEmpty(templateParams.get("ai.baseUrl"))) {
return templateParams;
} else {
return new HashMap<>();
}
}
}

View File

@ -0,0 +1,29 @@
package io.dataease.chart.charts;
import io.dataease.chart.charts.impl.DefaultChartHandler;
import io.dataease.extensions.view.plugin.AbstractChartPlugin;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class ChartHandlerManager {
@Lazy
@Resource
private DefaultChartHandler defaultChartHandler;
private static final ConcurrentHashMap<String, AbstractChartPlugin> CHART_HANDLER_MAP = new ConcurrentHashMap<>();
public void registerChartHandler(String render, String type, AbstractChartPlugin chartHandler) {
CHART_HANDLER_MAP.put(render + "-" + type, chartHandler);
}
public AbstractChartPlugin getChartHandler(String render, String type) {
var handler = CHART_HANDLER_MAP.get(render + "-" + type);
if (handler == null) {
return defaultChartHandler;
}
return handler;
}
}

View File

@ -0,0 +1,809 @@
package io.dataease.chart.charts.impl;
import io.dataease.chart.charts.ChartHandlerManager;
import io.dataease.chart.constant.ChartConstants;
import io.dataease.chart.manage.ChartDataManage;
import io.dataease.chart.manage.ChartViewManege;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.dataset.manage.DatasetTableFieldManage;
import io.dataease.constant.SQLConstants;
import io.dataease.engine.sql.SQLProvider;
import io.dataease.engine.trans.Dimension2SQLObj;
import io.dataease.engine.trans.Quota2SQLObj;
import io.dataease.engine.utils.Utils;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.api.PluginManageApi;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.datasource.vo.DatasourceConfiguration;
import io.dataease.extensions.datasource.vo.XpackPluginsDatasourceVO;
import io.dataease.extensions.view.dto.*;
import io.dataease.extensions.view.plugin.AbstractChartPlugin;
import io.dataease.extensions.view.util.ChartDataUtil;
import io.dataease.extensions.view.util.FieldUtil;
import io.dataease.license.utils.LicenseUtil;
import io.dataease.utils.BeanUtils;
import io.dataease.utils.JsonUtil;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@Component
public class DefaultChartHandler extends AbstractChartPlugin {
public static Logger logger = LoggerFactory.getLogger(ChartDataManage.class);
@Resource
protected ChartHandlerManager chartHandlerManager;
@Resource
protected DatasetTableFieldManage datasetTableFieldManage;
@Resource
protected ChartViewManege chartViewManege;
@Getter
private String render = "antv";
@Getter
private String type = "*";
@Autowired(required = false)
public PluginManageApi pluginManage;
@PostConstruct
public void init() {
chartHandlerManager.registerChartHandler(this.getRender(), this.getType(), this);
}
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var axisMap = new HashMap<ChartAxis, List<ChartViewFieldDTO>>();
var context = new HashMap<String, Object>();
var result = new AxisFormatResult(axisMap, context);
axisMap.put(ChartAxis.xAxis, new ArrayList<>(view.getXAxis()));
axisMap.put(ChartAxis.yAxis, new ArrayList<>(view.getYAxis()));
axisMap.put(ChartAxis.drill, new ArrayList<>(view.getDrillFields()));
return result;
}
@Override
public <T extends CustomFilterResult> T customFilter(ChartViewDTO view, List<ChartExtFilterDTO> filterList, AxisFormatResult formatResult) {
var desensitizationList = (Map<String, ColumnPermissionItem>) formatResult.getContext().get("desensitizationList");
if (MapUtils.isNotEmpty(desensitizationList)) {
formatResult.getAxisMap().forEach((axis, fields) -> {
fields.removeIf(f -> desensitizationList.containsKey(f.getDataeaseName()));
});
}
return (T) new CustomFilterResult(filterList, formatResult.getContext());
}
public Map<String, Object> buildResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult
.getFilterList()
.stream()
.anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
Map<String, Object> result = ChartDataBuild.transChartData(xAxis, yAxis, view, data, isDrill);
return result;
}
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
boolean needOrder = Utils.isNeedOrder(dsList);
boolean crossDs = Utils.isCrossDs(dsMap);
DatasourceRequest datasourceRequest = new DatasourceRequest();
datasourceRequest.setDsList(dsMap);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var allFields = (List<ChartViewFieldDTO>) filterResult.getContext().get("allFields");
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, FieldUtil.transFields(allFields), crossDs, dsMap, Utils.getParams(FieldUtil.transFields(allFields)), view.getCalParams(), pluginManage);
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, FieldUtil.transFields(allFields), crossDs, dsMap, Utils.getParams(FieldUtil.transFields(allFields)), view.getCalParams(), pluginManage);
String querySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
querySql = provider.rebuildSQL(querySql, sqlMeta, crossDs, dsMap);
datasourceRequest.setQuery(querySql);
logger.debug("calcite chart sql: " + querySql);
List<String[]> data = (List<String[]>) provider.fetchResultField(datasourceRequest).get("data");
//自定义排序
data = ChartDataUtil.resultCustomSort(xAxis, yAxis, view.getSortPriority(), data);
//快速计算
var extStack = formatResult.getAxisMap().get(ChartAxis.extStack);
var xAxisExt = formatResult.getAxisMap().get(ChartAxis.xAxisExt);
quickCalc(xAxis, yAxis, xAxisExt, extStack, view.getType(), data);
//数据重组逻辑可重载
var result = this.buildResult(view, formatResult, filterResult, data);
T calcResult = (T) new ChartCalcDataResult();
calcResult.setData(result);
calcResult.setContext(filterResult.getContext());
calcResult.setQuerySql(querySql);
calcResult.setOriginData(data);
return calcResult;
}
@Override
public ChartViewDTO buildChart(ChartViewDTO view, ChartCalcDataResult calcResult, AxisFormatResult formatResult, CustomFilterResult filterResult) {
var desensitizationList = (Map<String, ColumnPermissionItem>) filterResult.getContext().get("desensitizationList");
var allFields = (List<ChartViewFieldDTO>) filterResult.getContext().get("allFields");
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
// 如果是表格导出查询 则在此处直接就可以返回
var extStack = formatResult.getAxisMap().get(ChartAxis.extStack);
if (CollectionUtils.isNotEmpty(extStack) && xAxis.size() > extStack.size()) {
xAxis = xAxis.subList(0, xAxis.size() - extStack.size());
}
if (view.getIsExcelExport()) {
Map<String, Object> sourceInfo = ChartDataBuild.transTableNormal(xAxis, yAxis, view, calcResult.getOriginData(), extStack, desensitizationList);
sourceInfo.put("sourceData", calcResult.getOriginData());
view.setData(sourceInfo);
return view;
}
Map<String, Object> mapTableNormal = ChartDataBuild.transTableNormal(xAxis, yAxis, view, calcResult.getOriginData(), extStack, desensitizationList);
var drillFilters = filterResult.getFilterList().stream().filter(f -> f.getFilterType() == 1).collect(Collectors.toList());
// 日期下钻替换回去
drillFilters.forEach(f -> {
if (CollectionUtils.isNotEmpty(f.getOriginValue())) {
f.setValue(f.getOriginValue());
}
});
var isDrill = CollectionUtils.isNotEmpty(drillFilters);
// 构建结果
Map<String, Object> dataMap = new HashMap<>();
dataMap.putAll(calcResult.getData());
dataMap.putAll(mapTableNormal);
dataMap.put("sourceFields", allFields);
mergeAssistField(calcResult.getDynamicAssistFields(), calcResult.getAssistData());
dataMap.put("dynamicAssistLines", calcResult.getDynamicAssistFields());
view.setData(dataMap);
view.setSql(Base64.getEncoder().encodeToString(calcResult.getQuerySql().getBytes()));
view.setDrill(isDrill);
view.setDrillFilters(drillFilters);
return view;
}
protected void mergeAssistField(List<ChartSeniorAssistDTO> dynamicAssistFields, List<String[]> assistData) {
if (ObjectUtils.isEmpty(assistData)) {
return;
}
String[] strings = assistData.get(0);
for (int i = 0; i < dynamicAssistFields.size(); i++) {
if (i < strings.length) {
ChartSeniorAssistDTO chartSeniorAssistDTO = dynamicAssistFields.get(i);
chartSeniorAssistDTO.setValue(strings[i]);
}
}
}
protected List<ChartSeniorAssistDTO> getDynamicAssistFields(ChartViewDTO view) {
List<ChartSeniorAssistDTO> list = new ArrayList<>();
Map<String, Object> senior = view.getSenior();
if (ObjectUtils.isEmpty(senior)) {
return list;
}
ChartSeniorAssistCfgDTO assistLineCfg = JsonUtil.parseObject((String) JsonUtil.toJSONString(senior.get("assistLineCfg")), ChartSeniorAssistCfgDTO.class);
if (null == assistLineCfg || !assistLineCfg.isEnable()) {
return list;
}
List<ChartSeniorAssistDTO> assistLines = assistLineCfg.getAssistLine();
if (ObjectUtils.isEmpty(assistLines)) {
return list;
}
for (ChartSeniorAssistDTO dto : assistLines) {
if (StringUtils.equalsIgnoreCase(dto.getField(), "0")) {
continue;
}
Long fieldId = dto.getFieldId();
String summary = dto.getSummary();
if (ObjectUtils.isEmpty(fieldId) || StringUtils.isEmpty(summary)) {
continue;
}
DatasetTableFieldDTO datasetTableFieldDTO = datasetTableFieldManage.selectById(fieldId);
if (ObjectUtils.isEmpty(datasetTableFieldDTO)) {
continue;
}
list.add(dto);
}
return list;
}
protected List<ChartViewFieldDTO> getAssistFields(List<ChartSeniorAssistDTO> list, List<ChartViewFieldDTO> yAxis) {
List<ChartViewFieldDTO> res = new ArrayList<>();
for (ChartSeniorAssistDTO dto : list) {
DatasetTableFieldDTO curField = dto.getCurField();
ChartViewFieldDTO yField = null;
String alias = "";
for (int i = 0; i < yAxis.size(); i++) {
ChartViewFieldDTO field = yAxis.get(i);
if (Objects.equals(field.getId(), curField.getId())) {
yField = field;
alias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i);
break;
}
}
if (ObjectUtils.isEmpty(yField)) {
continue;
}
ChartViewFieldDTO chartViewFieldDTO = new ChartViewFieldDTO();
BeanUtils.copyBean(chartViewFieldDTO, curField);
chartViewFieldDTO.setSummary(dto.getSummary());
chartViewFieldDTO.setOriginName(alias);// yAxis的字段别名就是查找的字段名
res.add(chartViewFieldDTO);
}
return res;
}
protected List<ChartViewFieldDTO> getAssistFields(List<ChartSeniorAssistDTO> list, List<ChartViewFieldDTO> yAxis, List<ChartViewFieldDTO> xAxis) {
List<ChartViewFieldDTO> res = new ArrayList<>();
for (ChartSeniorAssistDTO dto : list) {
DatasetTableFieldDTO curField = dto.getCurField();
ChartViewFieldDTO field = null;
String alias = "";
for (int i = 0; i < yAxis.size(); i++) {
ChartViewFieldDTO yField = yAxis.get(i);
if (Objects.equals(yField.getId(), curField.getId())) {
field = yField;
alias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i);
break;
}
}
if (ObjectUtils.isEmpty(field) && CollectionUtils.isNotEmpty(xAxis)) {
for (int i = 0; i < xAxis.size(); i++) {
ChartViewFieldDTO xField = xAxis.get(i);
if (StringUtils.equalsIgnoreCase(String.valueOf(xField.getId()), String.valueOf(curField.getId()))) {
field = xField;
alias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i);
break;
}
}
}
if (ObjectUtils.isEmpty(field)) {
continue;
}
ChartViewFieldDTO chartViewFieldDTO = new ChartViewFieldDTO();
BeanUtils.copyBean(chartViewFieldDTO, curField);
chartViewFieldDTO.setSummary(dto.getSummary());
chartViewFieldDTO.setOriginName(alias);// yAxis的字段别名就是查找的字段名
res.add(chartViewFieldDTO);
}
return res;
}
public List<ChartSeniorAssistDTO> getDynamicThresholdFields(ChartViewDTO view) {
List<ChartSeniorAssistDTO> list = new ArrayList<>();
Map<String, Object> senior = view.getSenior();
if (ObjectUtils.isEmpty(senior)) {
return list;
}
ChartSeniorThresholdCfgDTO thresholdCfg = JsonUtil.parseObject((String) JsonUtil.toJSONString(senior.get("threshold")), ChartSeniorThresholdCfgDTO.class);
if (null == thresholdCfg || !thresholdCfg.isEnable()) {
return list;
}
List<TableThresholdDTO> tableThreshold = thresholdCfg.getTableThreshold();
if (ObjectUtils.isEmpty(tableThreshold)) {
return list;
}
List<ChartSeniorThresholdDTO> conditionsList = tableThreshold.stream()
.filter(item -> !ObjectUtils.isEmpty(item))
.map(TableThresholdDTO::getConditions)
.flatMap(List::stream)
.filter(condition -> StringUtils.equalsAnyIgnoreCase(condition.getType(), "dynamic"))
.toList();
List<ChartSeniorAssistDTO> assistDTOs = conditionsList.stream()
.flatMap(condition -> getConditionFields(condition).stream())
.filter(this::solveThresholdCondition)
.toList();
list.addAll(assistDTOs);
return list;
}
private boolean solveThresholdCondition(ChartSeniorAssistDTO fieldDTO) {
Long fieldId = fieldDTO.getFieldId();
String summary = fieldDTO.getValue();
if (ObjectUtils.isEmpty(fieldId) || StringUtils.isEmpty(summary)) {
return false;
}
DatasetTableFieldDTO datasetTableFieldDTO = datasetTableFieldManage.selectById(fieldId);
if (ObjectUtils.isEmpty(datasetTableFieldDTO)) {
return false;
}
ChartViewFieldDTO datasetTableField = new ChartViewFieldDTO();
BeanUtils.copyBean(datasetTableField, datasetTableFieldDTO);
fieldDTO.setCurField(datasetTableField);
fieldDTO.setSummary(summary);
return true;
}
private List<ChartSeniorAssistDTO> getConditionFields(ChartSeniorThresholdDTO condition) {
List<ChartSeniorAssistDTO> list = new ArrayList<>();
if ("between".equals(condition.getTerm())) {
if (!StringUtils.equalsIgnoreCase(condition.getDynamicMaxField().getSummary(), "value")) {
list.add(of(condition.getDynamicMaxField()));
}
if (!StringUtils.equalsIgnoreCase(condition.getDynamicMinField().getSummary(), "value")) {
list.add(of(condition.getDynamicMinField()));
}
} else {
if (!StringUtils.equalsIgnoreCase(condition.getDynamicField().getSummary(), "value")) {
list.add(of(condition.getDynamicField()));
}
}
return list;
}
private ChartSeniorAssistDTO of(ThresholdDynamicFieldDTO dynamicField) {
ChartSeniorAssistDTO conditionField = new ChartSeniorAssistDTO();
conditionField.setFieldId(Long.parseLong(dynamicField.getFieldId()));
conditionField.setValue(dynamicField.getSummary());
return conditionField;
}
protected String assistSQL(String sql, List<ChartViewFieldDTO> assistFields, Map<Long, DatasourceSchemaDTO> dsMap) {
// get datasource prefix and suffix
String dsType = dsMap.entrySet().iterator().next().getValue().getType();
String prefix = "";
String suffix = "";
if (Arrays.stream(DatasourceConfiguration.DatasourceType.values()).map(DatasourceConfiguration.DatasourceType::getType).toList().contains(dsType)) {
DatasourceConfiguration.DatasourceType datasourceType = DatasourceConfiguration.DatasourceType.valueOf(dsType);
prefix = datasourceType.getPrefix();
suffix = datasourceType.getSuffix();
} else {
if (LicenseUtil.licenseValid()) {
List<XpackPluginsDatasourceVO> xpackPluginsDatasourceVOS = pluginManage.queryPluginDs();
List<XpackPluginsDatasourceVO> list = xpackPluginsDatasourceVOS.stream().filter(ele -> StringUtils.equals(ele.getType(), dsType)).toList();
if (ObjectUtils.isNotEmpty(list)) {
XpackPluginsDatasourceVO first = list.getFirst();
prefix = first.getPrefix();
suffix = first.getSuffix();
} else {
DEException.throwException("当前数据源插件不存在");
}
}
}
boolean crossDs = Utils.isCrossDs(dsMap);
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < assistFields.size(); i++) {
ChartViewFieldDTO dto = assistFields.get(i);
if (crossDs) {
if (i == (assistFields.size() - 1)) {
stringBuilder.append(dto.getSummary() + "(" + dto.getOriginName() + ")");
} else {
stringBuilder.append(dto.getSummary() + "(" + dto.getOriginName() + "),");
}
} else {
if (i == (assistFields.size() - 1)) {
stringBuilder.append(dto.getSummary() + "(" + prefix + dto.getOriginName() + suffix + ")");
} else {
stringBuilder.append(dto.getSummary() + "(" + prefix + dto.getOriginName() + suffix + "),");
}
}
}
return "SELECT " + stringBuilder + " FROM (" + sql + ") tmp";
}
protected List<String> mergeIds(List<ChartViewFieldDTO> xAxisExt, List<ChartViewFieldDTO> extStack) {
Set<String> idSet = new HashSet<>();
if (xAxisExt != null) {
xAxisExt.forEach(field -> idSet.add(String.valueOf(field.getId())));
}
if (extStack != null) {
extStack.forEach(field -> idSet.add(String.valueOf(field.getId())));
}
return new ArrayList<>(idSet);
}
protected void quickCalc(List<ChartViewFieldDTO> xAxis, List<ChartViewFieldDTO> yAxis
, List<ChartViewFieldDTO> xAxisExt, List<ChartViewFieldDTO> extStack, String chartType, List<String[]> data) {
for (int i = 0; i < yAxis.size(); i++) {
ChartViewFieldDTO chartViewFieldDTO = yAxis.get(i);
ChartFieldCompareDTO compareCalc = chartViewFieldDTO.getCompareCalc();
if (ObjectUtils.isEmpty(compareCalc)) {
continue;
}
if (StringUtils.isNotEmpty(compareCalc.getType())
&& !StringUtils.equalsIgnoreCase(compareCalc.getType(), "none")) {
Long compareFieldId = compareCalc.getField();// 选中字段
// 数据字段下标
int dataIndex = xAxis.size() + i;
if (Arrays.asList(ChartConstants.M_Y).contains(compareCalc.getType())) {
String resultData = compareCalc.getResultData();// 数据设置
// 获取选中字段以及下标
List<ChartViewFieldDTO> checkedField = new ArrayList<>(xAxis);
int timeIndex = 0;// 时间字段下标
ChartViewFieldDTO timeField = null;
for (int j = 0; j < checkedField.size(); j++) {
if (Objects.equals(checkedField.get(j).getId(), compareFieldId)) {
timeIndex = j;
timeField = checkedField.get(j);
}
}
// 无选中字段或者选中字段已经不在维度list中或者选中字段日期格式不符合对比类型的直接将对应数据置为null
if (ObjectUtils.isEmpty(timeField) || !checkCalcType(timeField.getDateStyle(), compareCalc.getType())) {
// set null
for (String[] item : data) {
item[dataIndex] = null;
}
} else {
// 计算 同比/环比
// 1处理当期数据2根据type计算上一期数据3根据resultData计算结果
Map<String, String> currentMap = new LinkedHashMap<>();
for (String[] item : data) {
String[] dimension = Arrays.copyOfRange(item, 0, checkedField.size());
currentMap.put(StringUtils.join(dimension, "-"), item[dataIndex]);
}
for (int index = 0; index < data.size(); index++) {
String[] item = data.get(index);
String cTime = item[timeIndex];
String cValue = item[dataIndex];
// 获取计算后的时间并且与所有维度拼接
String lastTime = calcLastTime(cTime, compareCalc.getType(), timeField.getDateStyle(), timeField.getDatePattern());
String[] dimension = Arrays.copyOfRange(item, 0, checkedField.size());
dimension[timeIndex] = lastTime;
String lastValue = currentMap.get(StringUtils.join(dimension, "-"));
if (StringUtils.isEmpty(cValue) || StringUtils.isEmpty(lastValue)) {
item[dataIndex] = null;
} else {
if (StringUtils.equalsIgnoreCase(resultData, "sub")) {
item[dataIndex] = new BigDecimal(cValue).subtract(new BigDecimal(lastValue)).toString();
} else if (StringUtils.equalsIgnoreCase(resultData, "percent")) {
if (new BigDecimal(lastValue).compareTo(BigDecimal.ZERO) == 0) {
item[dataIndex] = null;
} else {
item[dataIndex] = new BigDecimal(cValue)
.divide(new BigDecimal(lastValue).abs(), 8, RoundingMode.HALF_UP)
.subtract(new BigDecimal(1))
.setScale(8, RoundingMode.HALF_UP)
.toString();
}
} else if (StringUtils.equalsIgnoreCase(resultData, "pre")) {
item[dataIndex] = new BigDecimal(lastValue).toString();
}
}
}
}
} else if (StringUtils.equalsIgnoreCase(compareCalc.getType(), "percent")) {
// 求和
BigDecimal sum = new BigDecimal(0);
for (int index = 0; index < data.size(); index++) {
String[] item = data.get(index);
String cValue = item[dataIndex];
if (StringUtils.isEmpty(cValue)) {
continue;
}
sum = sum.add(new BigDecimal(cValue));
}
// 计算占比
for (int index = 0; index < data.size(); index++) {
String[] item = data.get(index);
String cValue = item[dataIndex];
if (StringUtils.isEmpty(cValue)) {
continue;
}
item[dataIndex] = new BigDecimal(cValue)
.divide(sum, 8, RoundingMode.HALF_UP)
.toString();
}
} else if (StringUtils.equalsIgnoreCase(compareCalc.getType(), "accumulate")) {
// 累加
if (CollectionUtils.isEmpty(data)) {
break;
}
if (StringUtils.containsAny(chartType, "group", "stack")) {
if (CollectionUtils.isEmpty(xAxis)) {
break;
}
if (StringUtils.containsIgnoreCase(chartType, "stack") && extStack.isEmpty()) {
break;
}
if (StringUtils.containsIgnoreCase(chartType, "group") && xAxisExt.isEmpty()) {
break;
}
final Map<String, Integer> mainIndexMap = new HashMap<>();
final List<List<String[]>> mainMatrix = new ArrayList<>();
// 排除group和stack的字段
List<String> groupStackAxisIds = mergeIds(xAxisExt, extStack);
List<ChartViewFieldDTO> finalXAxisBase = xAxis.stream().filter(ele->!groupStackAxisIds.contains(String.valueOf(ele.getId()))).toList();
if (CollectionUtils.isEmpty(finalXAxisBase) && CollectionUtils.isNotEmpty(xAxis)) {
finalXAxisBase.add(xAxis.get(0));
}
data.forEach(item -> {
String[] mainAxisArr = Arrays.copyOfRange(item, 0, finalXAxisBase.size());
String mainAxis = StringUtils.join(mainAxisArr, '-');
Integer index = mainIndexMap.get(mainAxis);
if (index == null) {
mainIndexMap.put(mainAxis, mainMatrix.size());
List<String[]> tmp = new ArrayList<>();
tmp.add(item);
mainMatrix.add(tmp);
} else {
List<String[]> tmp = mainMatrix.get(index);
tmp.add(item);
}
});
int finalDataIndex = dataIndex;
int subEndIndex = finalXAxisBase.size();
if (StringUtils.containsIgnoreCase(chartType, "group")) {
subEndIndex += xAxisExt.size();
}
if (StringUtils.containsIgnoreCase(chartType, "stack")) {
subEndIndex += extStack.size();
}
int finalSubEndIndex = subEndIndex;
//滑动累加
for (int k = 1; k < mainMatrix.size(); k++) {
List<String[]> preDataItems = mainMatrix.get(k - 1);
List<String[]> curDataItems = mainMatrix.get(k);
Map<String, BigDecimal> preDataMap = new HashMap<>();
preDataItems.forEach(preDataItem -> {
String[] groupStackAxisArr = Arrays.copyOfRange(preDataItem, finalXAxisBase.size(), finalSubEndIndex);
String groupStackAxis = StringUtils.join(groupStackAxisArr, '-');
String preVal = preDataItem[finalDataIndex];
if (StringUtils.isBlank(preVal)) {
preVal = "0";
}
preDataMap.put(groupStackAxis, new BigDecimal(preVal));
});
curDataItems.forEach(curDataItem -> {
String[] groupStackAxisArr = Arrays.copyOfRange(curDataItem, finalXAxisBase.size(), finalSubEndIndex);
String groupStackAxis = StringUtils.join(groupStackAxisArr, '-');
BigDecimal preValue = preDataMap.get(groupStackAxis);
if (preValue != null) {
curDataItem[finalDataIndex] = new BigDecimal(curDataItem[finalDataIndex])
.add(preValue)
.toString();
}
});
}
} else {
final int index = dataIndex;
final AtomicReference<BigDecimal> accumValue = new AtomicReference<>(new BigDecimal(0));
data.forEach(item -> {
String val = item[index];
BigDecimal curAccumValue = accumValue.get();
if (!StringUtils.isBlank(val)) {
BigDecimal curVal = new BigDecimal(val);
curAccumValue = curAccumValue.add(curVal);
accumValue.set(curAccumValue);
}
item[index] = curAccumValue.toString();
});
}
}
}
}
}
private String calcLastTime(String cTime, String type, String dateStyle, String datePattern) {
try {
String lastTime = null;
Calendar calendar = Calendar.getInstance();
if (StringUtils.equalsIgnoreCase(type, ChartConstants.YEAR_MOM)) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy");
Date date = simpleDateFormat.parse(cTime);
calendar.setTime(date);
calendar.add(Calendar.YEAR, -1);
lastTime = simpleDateFormat.format(calendar.getTime());
} else if (StringUtils.equalsIgnoreCase(type, ChartConstants.MONTH_MOM)) {
SimpleDateFormat simpleDateFormat = null;
if (StringUtils.equalsIgnoreCase(datePattern, "date_split")) {
simpleDateFormat = new SimpleDateFormat("yyyy/MM");
} else {
simpleDateFormat = new SimpleDateFormat("yyyy-MM");
}
Date date = simpleDateFormat.parse(cTime);
calendar.setTime(date);
calendar.add(Calendar.MONTH, -1);
lastTime = simpleDateFormat.format(calendar.getTime());
} else if (StringUtils.equalsIgnoreCase(type, ChartConstants.YEAR_YOY)) {
SimpleDateFormat simpleDateFormat = null;
if (StringUtils.equalsIgnoreCase(dateStyle, "y_M")) {
if (StringUtils.equalsIgnoreCase(datePattern, "date_split")) {
simpleDateFormat = new SimpleDateFormat("yyyy/MM");
} else {
simpleDateFormat = new SimpleDateFormat("yyyy-MM");
}
} else if (StringUtils.equalsIgnoreCase(dateStyle, "y_M_d")) {
if (StringUtils.equalsIgnoreCase(datePattern, "date_split")) {
simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
} else {
simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
}
}
Date date = simpleDateFormat.parse(cTime);
calendar.setTime(date);
calendar.add(Calendar.YEAR, -1);
lastTime = simpleDateFormat.format(calendar.getTime());
} else if (StringUtils.equalsIgnoreCase(type, ChartConstants.DAY_MOM)) {
SimpleDateFormat simpleDateFormat = null;
if (StringUtils.equalsIgnoreCase(datePattern, "date_split")) {
simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
} else {
simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
}
Date date = simpleDateFormat.parse(cTime);
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_MONTH, -1);
lastTime = simpleDateFormat.format(calendar.getTime());
} else if (StringUtils.equalsIgnoreCase(type, ChartConstants.MONTH_YOY)) {
SimpleDateFormat simpleDateFormat = null;
if (StringUtils.equalsIgnoreCase(dateStyle, "y_M")) {
if (StringUtils.equalsIgnoreCase(datePattern, "date_split")) {
simpleDateFormat = new SimpleDateFormat("yyyy/MM");
} else {
simpleDateFormat = new SimpleDateFormat("yyyy-MM");
}
} else if (StringUtils.equalsIgnoreCase(dateStyle, "y_M_d")) {
if (StringUtils.equalsIgnoreCase(datePattern, "date_split")) {
simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
} else {
simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
}
}
Date date = simpleDateFormat.parse(cTime);
calendar.setTime(date);
calendar.add(Calendar.MONTH, -1);
lastTime = simpleDateFormat.format(calendar.getTime());
}
return lastTime;
} catch (Exception e) {
return cTime;
}
}
private boolean checkCalcType(String dateStyle, String calcType) {
switch (dateStyle) {
case "y":
return StringUtils.equalsIgnoreCase(calcType, "year_mom");
case "y_M":
return StringUtils.equalsIgnoreCase(calcType, "month_mom")
|| StringUtils.equalsIgnoreCase(calcType, "year_yoy");
case "y_M_d":
return StringUtils.equalsIgnoreCase(calcType, "day_mom")
|| StringUtils.equalsIgnoreCase(calcType, "month_yoy")
|| StringUtils.equalsIgnoreCase(calcType, "year_yoy");
}
return false;
}
protected boolean checkYoyFilter(List<ChartExtFilterDTO> filter, List<ChartViewFieldDTO> yoyAxis) {
boolean flag = false;
for (ChartExtFilterDTO filterDTO : filter) {
for (ChartViewFieldDTO chartViewFieldDTO : yoyAxis) {
ChartFieldCompareDTO compareCalc = chartViewFieldDTO.getCompareCalc();
if (ObjectUtils.isEmpty(compareCalc)) {
continue;
}
if (StringUtils.isNotEmpty(compareCalc.getType())
&& !StringUtils.equalsIgnoreCase(compareCalc.getType(), "none")) {
if (Arrays.asList(ChartConstants.M_Y).contains(compareCalc.getType())) {
if (StringUtils.equalsIgnoreCase(compareCalc.getField() + "", filterDTO.getFieldId())
&& (filterDTO.getFilterType() == 0 || filterDTO.getFilterType() == 2)) {
// -1 year
try {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date(Long.parseLong(filterDTO.getValue().getFirst())));
calendar.add(Calendar.YEAR, -1);
filterDTO.getValue().set(0, String.valueOf(calendar.getTime().getTime()));
flag = true;
} catch (Exception e) {
}
}
}
}
}
}
return flag;
}
;
protected void groupStackDrill(List<ChartViewFieldDTO> xAxis,
List<ChartExtFilterDTO> filterList,
List<ChartViewFieldDTO> fieldsToFilter,
List<ChartViewFieldDTO> drillFields,
List<ChartDrillRequest> drillRequestList) {
var fields = xAxis.stream().map(ChartViewFieldDTO::getId).collect(Collectors.toSet());
ChartDrillRequest head = drillRequestList.getFirst();
Map<Long, String> dimValMap = new HashMap<>();
head.getDimensionList().forEach(item -> dimValMap.put(item.getId(), item.getValue()));
Map<Long, ChartViewFieldDTO> fieldMap = xAxis.stream().collect(Collectors.toMap(ChartViewFieldDTO::getId, o -> o, ((p, n) -> p)));
for (int i = 0; i < drillRequestList.size(); i++) {
ChartDrillRequest request = drillRequestList.get(i);
ChartViewFieldDTO chartViewFieldDTO = drillFields.get(i);
for (ChartDimensionDTO requestDimension : request.getDimensionList()) {
// 将钻取值作为条件传递将所有钻取字段作为xAxis并加上下一个钻取字段
if (Objects.equals(requestDimension.getId(), chartViewFieldDTO.getId())) {
fieldsToFilter.add(chartViewFieldDTO);
dimValMap.put(requestDimension.getId(), requestDimension.getValue());
if (!fields.contains(requestDimension.getId())) {
fieldMap.put(chartViewFieldDTO.getId(), chartViewFieldDTO);
chartViewFieldDTO.setSource(FieldSource.DRILL);
xAxis.add(chartViewFieldDTO);
fields.add(requestDimension.getId());
}
if (i == drillRequestList.size() - 1) {
ChartViewFieldDTO nextDrillField = drillFields.get(i + 1);
if (!fields.contains(nextDrillField.getId())) {
// get drill list first element's sort,then assign to nextDrillField
nextDrillField.setSort(getDrillSort(xAxis, drillFields.get(0)));
nextDrillField.setSource(FieldSource.DRILL);
xAxis.add(nextDrillField);
fields.add(nextDrillField.getId());
}
}
}
}
}
for (int i = 0; i < fieldsToFilter.size(); i++) {
ChartViewFieldDTO tmpField = fieldsToFilter.get(i);
ChartExtFilterDTO tmpFilter = new ChartExtFilterDTO();
DatasetTableFieldDTO datasetTableField = datasetTableFieldManage.selectById(tmpField.getId());
tmpFilter.setDatasetTableField(datasetTableField);
tmpFilter.setDateStyle(fieldMap.get(tmpField.getId()).getDateStyle());
tmpFilter.setDatePattern(fieldMap.get(tmpField.getId()).getDatePattern());
tmpFilter.setFieldId(String.valueOf(tmpField.getId()));
tmpFilter.setFilterType(1);
if (datasetTableField.getDeType() == 1) {
tmpFilter.setOriginValue(Collections.singletonList(dimValMap.get(tmpField.getId())));
tmpFilter.setOperator("between");
// 把value类似过滤组件处理获得start time和end time
Map<String, Long> stringLongMap = Utils.parseDateTimeValue(dimValMap.get(tmpField.getId()));
tmpFilter.setValue(Arrays.asList(String.valueOf(stringLongMap.get("startTime")), String.valueOf(stringLongMap.get("endTime"))));
} else {
tmpFilter.setOperator("in");
tmpFilter.setValue(Collections.singletonList(dimValMap.get(tmpField.getId())));
}
filterList.add(tmpFilter);
}
}
private String getDrillSort(List<ChartViewFieldDTO> xAxis, ChartViewFieldDTO field) {
String res = "";
for (ChartViewFieldDTO f : xAxis) {
if (Objects.equals(f.getId(), field.getId())) {
if (StringUtils.equalsIgnoreCase(f.getSort(), "asc") || StringUtils.equalsIgnoreCase(f.getSort(), "desc")) {
res = f.getSort();
break;
}
}
}
return res;
}
}

View File

@ -0,0 +1,21 @@
package io.dataease.chart.charts.impl;
import io.dataease.extensions.view.dto.AxisFormatResult;
import io.dataease.extensions.view.dto.ChartAxis;
import io.dataease.extensions.view.dto.ChartViewDTO;
import io.dataease.extensions.view.dto.ChartViewFieldDTO;
import java.util.ArrayList;
public class ExtQuotaChartHandler extends DefaultChartHandler {
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var yAxis = result.getAxisMap().get(ChartAxis.yAxis);
yAxis.addAll(view.getExtLabel());
yAxis.addAll(view.getExtTooltip());
result.getAxisMap().put(ChartAxis.extLabel, view.getExtLabel());
result.getAxisMap().put(ChartAxis.extTooltip, view.getExtTooltip());
return result;
}
}

View File

@ -0,0 +1,20 @@
package io.dataease.chart.charts.impl;
import io.dataease.extensions.view.dto.AxisFormatResult;
import io.dataease.extensions.view.dto.ChartAxis;
import io.dataease.extensions.view.dto.ChartViewDTO;
import io.dataease.extensions.view.dto.ChartViewFieldDTO;
import java.util.ArrayList;
public class GroupChartHandler extends YoyChartHandler {
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var xAxis = new ArrayList<ChartViewFieldDTO>(view.getXAxis());
xAxis.addAll(view.getXAxisExt());
result.getAxisMap().put(ChartAxis.xAxis, xAxis);
return result;
}
}

View File

@ -0,0 +1,184 @@
package io.dataease.chart.charts.impl;
import com.fasterxml.jackson.core.type.TypeReference;
import io.dataease.engine.sql.SQLProvider;
import io.dataease.engine.trans.ExtWhere2Str;
import io.dataease.engine.utils.Utils;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import io.dataease.extensions.view.util.FieldUtil;
import io.dataease.utils.JsonUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 带同环比计算的图表处理器
*/
public class YoyChartHandler extends DefaultChartHandler {
@Override
public <T extends CustomFilterResult> T customFilter(ChartViewDTO view, List<ChartExtFilterDTO> filterList, AxisFormatResult formatResult) {
var result = super.customFilter(view, filterList, formatResult);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
String originFilterJson = (String) JsonUtil.toJSONString(filterList);
// 如果设置了同环比的指标字段设置了过滤器那就需要把该过滤器的时间往前回调一年
// 计算完同环比之后再把计算结果和原有的过滤结果对比去除不该出现的前一年的数据
boolean yoyFiltered = checkYoyFilter(filterList, yAxis);
if (yoyFiltered) {
List<ChartExtFilterDTO> originFilter = JsonUtil.parseList(originFilterJson, new TypeReference<>() {
});
formatResult.getContext().put("originFilter", originFilter);
formatResult.getContext().put("yoyFiltered", true);
}
return (T) result;
}
@Override
public Map<String, Object> buildResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
var yoyFiltered = filterResult.getContext().get("yoyFiltered") != null;
// 带过滤同环比直接返回原始数据,再由视图重新组装
if (yoyFiltered) {
var result = new HashMap<String, Object>();
result.put("data", data);
return result;
}
return buildNormalResult(view, formatResult, filterResult, data);
}
/**
* 构建同环比类型的数据
*
* @param view 视图对象
* @param formatResult 处理后的轴
* @param filterResult 处理后的过滤器
* @param data 原始数据
* @return 视图构建结果
*/
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
return super.buildResult(view, formatResult, filterResult, data);
}
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
boolean needOrder = Utils.isNeedOrder(dsList);
boolean crossDs = Utils.isCrossDs(dsMap);
// 这里拿到的可能有一年前的数据
var expandedResult = (T) super.calcChartResult(view, formatResult, filterResult, sqlMap, sqlMeta, provider);
// 检查同环比过滤拿到实际数据
var yoyFiltered = filterResult.getContext().get("yoyFiltered") != null;
if (yoyFiltered) {
var originFilter = (List<ChartExtFilterDTO>) filterResult.getContext().get("originFilter");
var allFields = (List<ChartViewFieldDTO>) filterResult.getContext().get("allFields");
ExtWhere2Str.extWhere2sqlOjb(sqlMeta, originFilter, FieldUtil.transFields(allFields), crossDs, dsMap, Utils.getParams(FieldUtil.transFields(allFields)), view.getCalParams(), pluginManage);
var originSql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
originSql = provider.rebuildSQL(originSql, sqlMeta, crossDs, dsMap);
var request = new DatasourceRequest();
request.setDsList(dsMap);
request.setQuery(originSql);
logger.debug("calcite yoy sql: " + originSql);
// 实际过滤后的数据
var originData = (List<String[]>) provider.fetchResultField(request).get("data");
List<String[]> resultData = new ArrayList<>();
// 包含一年前的数据, 已计算同环比
var yoyData = (List<String[]>) expandedResult.getData().get("data");
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
// 对比维度,只保留实际过滤后的数据
for (String[] yoyDataLine : yoyData) {
StringBuilder x1 = new StringBuilder();
for (int i = 0; i < xAxis.size(); i++) {
x1.append(yoyDataLine[i]);
}
for (String[] originDataLine : originData) {
StringBuilder x2 = new StringBuilder();
for (int i = 0; i < xAxis.size(); i++) {
x2.append(originDataLine[i]);
}
if (StringUtils.equals(x1, x2)) {
resultData.add(yoyDataLine);
break;
}
}
}
yoyData.clear();
yoyData.addAll(resultData);
var result = this.buildNormalResult(view, formatResult, filterResult, yoyData);
expandedResult.setData(result);
expandedResult.setOriginData(resultData);
expandedResult.setQuerySql(originSql);
}
// 同环比数据排序
expandedResult.setOriginData(sortData(view, expandedResult.getOriginData(), formatResult));
return expandedResult;
}
public static List<String[]> sortData(ChartViewDTO view, List<String[]> data, AxisFormatResult formatResult) {
// 维度排序
List<ChartViewFieldDTO> xAxisSortList = view.getXAxis().stream().filter(x -> !StringUtils.equalsIgnoreCase("none", x.getSort())).toList();
// 指标排序
List<ChartViewFieldDTO> yAxisSortList = view.getYAxis().stream().filter(y -> {
//需要针对区间条形图的时间类型判断一下
if (StringUtils.equalsIgnoreCase("bar-range", view.getType()) && StringUtils.equalsIgnoreCase(y.getGroupType(), "d") && y.getDeType() == 1) {
return false;
} else {
return !StringUtils.equalsIgnoreCase("none", y.getSort());
}
}).toList();
// 不包含维度排序时指标排序生效
if (!data.isEmpty() && CollectionUtils.isEmpty(xAxisSortList) && CollectionUtils.isNotEmpty(yAxisSortList)) {
// 指标排序仅第一个生效
ChartViewFieldDTO firstYAxis = yAxisSortList.getFirst();
boolean asc = firstYAxis.getSort().equalsIgnoreCase("asc");
// 维度指标
List<ChartViewFieldDTO> allAxisList = new ArrayList<>();
allAxisList.addAll(formatResult.getAxisMap().get(ChartAxis.xAxis));
allAxisList.addAll(formatResult.getAxisMap().get(ChartAxis.yAxis));
int index = findIndex(allAxisList, firstYAxis.getId());
return sortData(data, asc, index);
}
return data;
}
public static List<String[]> sortData(List<String[]> data, boolean ascending, int index) {
Comparator<String[]> comparator;
if (ascending) {
comparator = Comparator.comparing(item -> toBigDecimal(item[index]), Comparator.nullsFirst(Comparator.naturalOrder()));
} else {
comparator = Comparator.comparing(item -> toBigDecimal(item[index]), Comparator.nullsLast(Comparator.reverseOrder()));
}
return data.stream().sorted(comparator).collect(Collectors.toList());
}
private static BigDecimal toBigDecimal(String value) {
if (value == null) {
return null;
}
try {
return new BigDecimal(value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid number format: " + value, e);
}
}
public static int findIndex(List<ChartViewFieldDTO> list, Long id) {
for (int i = 0; i < list.size(); i++) {
if (StringUtils.equalsIgnoreCase(list.get(i).getId().toString(), id.toString())) {
return i;
}
}
return -1;
}
}

View File

@ -0,0 +1,68 @@
package io.dataease.chart.charts.impl.bar;
import io.dataease.chart.charts.impl.YoyChartHandler;
import io.dataease.engine.utils.Utils;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
public class BarHandler extends YoyChartHandler {
@Override
public void init() {
chartHandlerManager.registerChartHandler(this.getRender(), "bar", this);
chartHandlerManager.registerChartHandler(this.getRender(), "bar-horizontal", this);
}
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var yAxis = result.getAxisMap().get(ChartAxis.yAxis);
yAxis.addAll(view.getExtLabel());
yAxis.addAll(view.getExtTooltip());
result.getAxisMap().put(ChartAxis.extLabel, view.getExtLabel());
result.getAxisMap().put(ChartAxis.extTooltip, view.getExtTooltip());
return result;
}
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
boolean needOrder = Utils.isNeedOrder(dsList);
boolean crossDs = Utils.isCrossDs(dsMap);
var result = (T) super.calcChartResult(view, formatResult, filterResult, sqlMap, sqlMeta, provider);
try {
//如果有同环比过滤,应该用原始sql
var originSql = result.getQuerySql();
var dynamicAssistFields = getDynamicAssistFields(view);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var assistFields = getAssistFields(dynamicAssistFields, yAxis);
if (CollectionUtils.isNotEmpty(assistFields)) {
var req = new DatasourceRequest();
req.setDsList(dsMap);
var assistSql = assistSQL(originSql, assistFields, dsMap);
req.setQuery(assistSql);
logger.debug("calcite assistSql sql: " + assistSql);
var assistData = (List<String[]>) provider.fetchResultField(req).get("data");
result.setAssistData(assistData);
result.setDynamicAssistFields(dynamicAssistFields);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}

View File

@ -0,0 +1,10 @@
package io.dataease.chart.charts.impl.bar;
import lombok.Getter;
import org.springframework.stereotype.Component;
@Component
public class BidirectionalBarHandler extends ProgressBarHandler {
@Getter
private String type = "bidirectional-bar";
}

View File

@ -0,0 +1,73 @@
package io.dataease.chart.charts.impl.bar;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class GroupBarHandler extends BarHandler {
@Getter
private String type = "bar-group";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var xAxis = result.getAxisMap().get(ChartAxis.xAxis);
xAxis.addAll(view.getXAxisExt());
result.getAxisMap().put(ChartAxis.xAxisExt, view.getXAxisExt());
return result;
}
@Override
public void init() {
chartHandlerManager.registerChartHandler(this.getRender(), this.getType(), this);
}
@Override
public <T extends CustomFilterResult> T customFilter(ChartViewDTO view, List<ChartExtFilterDTO> filterList, AxisFormatResult formatResult) {
var result = super.customFilter(view, filterList, formatResult);
List<ChartDrillRequest> drillRequestList = view.getChartExtRequest().getDrill();
var drillFields = formatResult.getAxisMap().get(ChartAxis.drill);
// 分组维度下钻
if (ObjectUtils.isNotEmpty(drillRequestList) && (drillFields.size() > drillRequestList.size())) {
List<ChartExtFilterDTO> noDrillFilterList = filterList
.stream()
.filter(ele -> ele.getFilterType() != 1)
.collect(Collectors.toList());
var noDrillFieldAxis = formatResult.getAxisMap().get(ChartAxis.xAxis)
.stream()
.filter(ele -> ele.getSource() != FieldSource.DRILL)
.collect(Collectors.toList());
List<ChartExtFilterDTO> drillFilters = new ArrayList<>();
ArrayList<ChartViewFieldDTO> fieldsToFilter = new ArrayList<>();
var xAxisExt = formatResult.getAxisMap().get(ChartAxis.xAxisExt);
if (ObjectUtils.isNotEmpty(xAxisExt) &&
Objects.equals(drillFields.get(0).getId(), xAxisExt.get(0).getId())) {
fieldsToFilter.addAll(view.getXAxis());
groupStackDrill(noDrillFieldAxis, noDrillFilterList, fieldsToFilter, drillFields, drillRequestList);
formatResult.getAxisMap().put(ChartAxis.xAxis, noDrillFieldAxis);
result.setFilterList(noDrillFilterList);
}
}
return (T) result;
}
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult
.getFilterList()
.stream()
.anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var xAxisExt = formatResult.getAxisMap().get(ChartAxis.xAxisExt);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var drillAxis = xAxis.stream().filter(axis -> FieldSource.DRILL == axis.getSource()).toList();
var xAxisBase = xAxis.subList(0, xAxis.size() - xAxisExt.size() - drillAxis.size());
return ChartDataBuild.transBaseGroupDataAntV(xAxisBase, xAxis, xAxisExt, yAxis, view, data, isDrill);
}
}

View File

@ -0,0 +1,44 @@
package io.dataease.chart.charts.impl.bar;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
public class ProgressBarHandler extends BarHandler {
@Getter
private String type = "progress-bar";
@Override
public void init() {
chartHandlerManager.registerChartHandler(this.getRender(), this.getType(), this);
}
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var yAxis = new ArrayList<>(view.getYAxis());
yAxis.addAll(view.getYAxisExt());
yAxis.addAll(view.getExtTooltip());
result.getAxisMap().put(ChartAxis.yAxis, yAxis);
result.getAxisMap().put(ChartAxis.yAxisExt, view.getYAxisExt());
return result;
}
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult
.getFilterList()
.stream()
.anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
Map<String, Object> result = ChartDataBuild.transMixChartDataAntV(xAxis, xAxis, new ArrayList<>(), yAxis, view, data, isDrill);
return result;
}
}

View File

@ -0,0 +1,83 @@
package io.dataease.chart.charts.impl.bar;
import io.dataease.chart.charts.impl.YoyChartHandler;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
public class RangeBarHandler extends YoyChartHandler {
@Getter
private final String type = "bar-range";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var yAxis = new ArrayList<ChartViewFieldDTO>();
var xAxis = new ArrayList<ChartViewFieldDTO>(view.getXAxis());
var xAxisBase = new ArrayList<ChartViewFieldDTO>(view.getXAxis());
boolean skipBarRange = false;
boolean barRangeDate = false;
if (CollectionUtils.isNotEmpty(view.getYAxis()) && CollectionUtils.isNotEmpty(view.getYAxisExt())) {
ChartViewFieldDTO axis1 = view.getYAxis().get(0);
ChartViewFieldDTO axis2 = view.getYAxisExt().get(0);
if (StringUtils.equalsIgnoreCase(axis1.getGroupType(), "q") && StringUtils.equalsIgnoreCase(axis2.getGroupType(), "q")) {
yAxis.add(axis1);
yAxis.add(axis2);
} else if (StringUtils.equalsIgnoreCase(axis1.getGroupType(), "d") && axis1.getDeType() == 1 && StringUtils.equalsIgnoreCase(axis2.getGroupType(), "d") && axis2.getDeType() == 1) {
barRangeDate = true;
if (BooleanUtils.isTrue(view.getAggregate())) {
axis1.setSummary("min");
axis2.setSummary("max");
yAxis.add(axis1);
yAxis.add(axis2);
} else {
xAxis.add(axis1);
xAxis.add(axis2);
}
} else {
skipBarRange = true;
}
} else {
skipBarRange = true;
}
result.getContext().put("skipBarRange", skipBarRange);
result.getContext().put("barRangeDate", barRangeDate);
result.getAxisMap().put(ChartAxis.xAxis, xAxis);
result.getAxisMap().put(ChartAxis.yAxis, yAxis);
result.getContext().put("xAxisBase", xAxisBase);
return result;
}
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult
.getFilterList()
.stream()
.anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var xAxisBase = (List<ChartViewFieldDTO>) formatResult.getContext().get("xAxisBase");
var skipBarRange = (boolean) formatResult.getContext().get("skipBarRange");
var barRangeDate = (boolean) formatResult.getContext().get("barRangeDate");
Map<String, Object> result = ChartDataBuild.transBarRangeDataAntV(skipBarRange, barRangeDate, xAxisBase, xAxis, yAxis, view, data, isDrill);
return result;
}
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
sqlMeta.setChartType(this.type);
return super.calcChartResult(view, formatResult, filterResult, sqlMap, sqlMeta, provider);
}
}

View File

@ -0,0 +1,72 @@
package io.dataease.chart.charts.impl.bar;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@Component
public class StackBarHandler extends BarHandler {
@Getter
private String type = "bar-stack";
@Override
public void init() {
chartHandlerManager.registerChartHandler(this.getRender(), "bar-stack", this);
chartHandlerManager.registerChartHandler(this.getRender(), "bar-stack-horizontal", this);
chartHandlerManager.registerChartHandler(this.getRender(), "percentage-bar-stack", this);
chartHandlerManager.registerChartHandler(this.getRender(), "percentage-bar-stack-horizontal", this);
}
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var xAxis = result.getAxisMap().get(ChartAxis.xAxis);
xAxis.addAll(view.getExtStack());
result.getAxisMap().put(ChartAxis.extStack, view.getExtStack());
return result;
}
@Override
public <T extends CustomFilterResult> T customFilter(ChartViewDTO view, List<ChartExtFilterDTO> filterList, AxisFormatResult formatResult) {
var result = super.customFilter(view, filterList, formatResult);
List<ChartDrillRequest> drillRequestList = view.getChartExtRequest().getDrill();
var drillFields = formatResult.getAxisMap().get(ChartAxis.drill);
// 堆叠维度下钻
if (ObjectUtils.isNotEmpty(drillRequestList) && (drillFields.size() > drillRequestList.size())) {
List<ChartExtFilterDTO> noDrillFilterList = filterList
.stream()
.filter(ele -> ele.getFilterType() != 1)
.collect(Collectors.toList());
var noDrillFieldAxis = formatResult.getAxisMap().get(ChartAxis.xAxis)
.stream()
.filter(ele -> ele.getSource() != FieldSource.DRILL)
.collect(Collectors.toList());
List<ChartExtFilterDTO> drillFilters = new ArrayList<>();
ArrayList<ChartViewFieldDTO> fieldsToFilter = new ArrayList<>();
var extStack = formatResult.getAxisMap().get(ChartAxis.extStack);
if (ObjectUtils.isNotEmpty(extStack) &&
Objects.equals(drillFields.get(0).getId(), extStack.get(0).getId())) {
fieldsToFilter.addAll(view.getXAxis());
groupStackDrill(noDrillFieldAxis, noDrillFilterList, fieldsToFilter, drillFields, drillRequestList);
formatResult.getAxisMap().put(ChartAxis.xAxis, noDrillFieldAxis);
result.setFilterList(noDrillFilterList);
}
}
return (T) result;
}
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult.getFilterList().stream().anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var extStack = formatResult.getAxisMap().get(ChartAxis.extStack);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var drillAxis = xAxis.stream().filter(axis -> FieldSource.DRILL == axis.getSource()).toList();
var xAxisBase = xAxis.subList(0, xAxis.size() - extStack.size() - drillAxis.size());
return ChartDataBuild.transStackChartDataAntV(xAxisBase, xAxis, yAxis, view, data, extStack, isDrill);
}
}

View File

@ -0,0 +1,88 @@
package io.dataease.chart.charts.impl.bar;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import static io.dataease.extensions.view.dto.ChartAxis.extStack;
import static io.dataease.extensions.view.dto.ChartAxis.xAxisExt;
@Component
public class StackGroupBarHandler extends BarHandler {
@Getter
private String type = "bar-group-stack";
@Override
public void init() {
chartHandlerManager.registerChartHandler(this.getRender(), this.getType(), this);
}
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var xAxis = result.getAxisMap().get(ChartAxis.xAxis);
xAxis.addAll(view.getXAxisExt());
xAxis.addAll(view.getExtStack());
result.getAxisMap().put(ChartAxis.extStack, view.getExtStack());
result.getAxisMap().put(ChartAxis.xAxisExt, view.getXAxisExt());
return result;
}
@Override
public <T extends CustomFilterResult> T customFilter(ChartViewDTO view, List<ChartExtFilterDTO> filterList, AxisFormatResult formatResult) {
var result = super.customFilter(view, filterList, formatResult);
List<ChartDrillRequest> drillRequestList = view.getChartExtRequest().getDrill();
var drillFields = formatResult.getAxisMap().get(ChartAxis.drill);
// 分组维度下钻
if (ObjectUtils.isNotEmpty(drillRequestList) && (drillFields.size() > drillRequestList.size())) {
List<ChartExtFilterDTO> noDrillFilterList = filterList
.stream()
.filter(ele -> ele.getFilterType() != 1)
.collect(Collectors.toList());
var noDrillFieldAxis = formatResult.getAxisMap().get(ChartAxis.xAxis)
.stream()
.filter(ele -> ele.getSource() != FieldSource.DRILL)
.collect(Collectors.toList());
ArrayList<ChartViewFieldDTO> fieldsToFilter = new ArrayList<>();
var xAxisExt = formatResult.getAxisMap().get(ChartAxis.xAxisExt);
var extStack = formatResult.getAxisMap().get(ChartAxis.extStack);
if (ObjectUtils.isNotEmpty(xAxisExt) && ObjectUtils.isNotEmpty(extStack)) {
if (Objects.equals(drillFields.get(0).getId(), xAxisExt.get(0).getId())) {
fieldsToFilter.addAll(view.getXAxis());
fieldsToFilter.addAll(extStack);
}
if (Objects.equals(drillFields.get(0).getId(), extStack.get(0).getId())) {
fieldsToFilter.addAll(view.getXAxis());
fieldsToFilter.addAll(xAxisExt);
}
groupStackDrill(noDrillFieldAxis, noDrillFilterList, fieldsToFilter, drillFields, drillRequestList);
formatResult.getAxisMap().put(ChartAxis.xAxis, noDrillFieldAxis);
result.setFilterList(noDrillFilterList);
}
}
return (T) result;
}
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult
.getFilterList()
.stream()
.anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var xAxisExt = formatResult.getAxisMap().get(ChartAxis.xAxisExt);
var extStack = formatResult.getAxisMap().get(ChartAxis.extStack);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var drillAxis = xAxis.stream().filter(axis -> FieldSource.DRILL == axis.getSource()).toList();
var xAxisBase = xAxis.subList(0, xAxis.size() - xAxisExt.size() - extStack.size() - drillAxis.size());
return ChartDataBuild.transGroupStackDataAntV(xAxisBase, xAxis, xAxisExt, yAxis, extStack, data, view, isDrill);
}
}

View File

@ -0,0 +1,31 @@
package io.dataease.chart.charts.impl.line;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@Component
public class AreaHandler extends LineHandler {
@Getter
private String type = "area";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
result.getAxisMap().put(ChartAxis.xAxis, view.getXAxis());
return result;
}
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult.getFilterList().stream().anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
return ChartDataBuild.transChartData(xAxis, yAxis, view, data, isDrill);
}
}

View File

@ -0,0 +1,80 @@
package io.dataease.chart.charts.impl.line;
import io.dataease.chart.charts.impl.YoyChartHandler;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
public class LineHandler extends YoyChartHandler {
@Getter
private String type = "line";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var xAxis = result.getAxisMap().get(ChartAxis.xAxis);
xAxis.addAll(view.getXAxisExt());
var yAxis = result.getAxisMap().get(ChartAxis.yAxis);
yAxis.addAll(view.getExtLabel());
yAxis.addAll(view.getExtTooltip());
result.getAxisMap().put(ChartAxis.xAxisExt, view.getXAxisExt());
result.getAxisMap().put(ChartAxis.extLabel, view.getExtLabel());
result.getAxisMap().put(ChartAxis.extTooltip, view.getExtTooltip());
return result;
}
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult
.getFilterList()
.stream()
.anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var xAxisExt = formatResult.getAxisMap().get(ChartAxis.xAxisExt);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var drillAxis = xAxis.stream().filter(axis -> FieldSource.DRILL == axis.getSource()).toList();
var xAxisBase = xAxis.subList(0, xAxis.size() - xAxisExt.size() - drillAxis.size());
return ChartDataBuild.transBaseGroupDataAntV(xAxisBase, xAxis, xAxisExt, yAxis, view, data, isDrill);
}
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
var result = (T) super.calcChartResult(view, formatResult, filterResult, sqlMap, sqlMeta, provider);
try {
//如果有同环比过滤,应该用原始sql
var originSql = result.getQuerySql();
var dynamicAssistFields = getDynamicAssistFields(view);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var assistFields = getAssistFields(dynamicAssistFields, yAxis);
if (CollectionUtils.isNotEmpty(assistFields)) {
var req = new DatasourceRequest();
req.setDsList(dsMap);
var assistSql = assistSQL(originSql, assistFields, dsMap);
req.setQuery(assistSql);
logger.debug("calcite assistSql sql: " + assistSql);
var assistData = (List<String[]>) provider.fetchResultField(req).get("data");
result.setAssistData(assistData);
result.setDynamicAssistFields(dynamicAssistFields);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}

View File

@ -0,0 +1,115 @@
package io.dataease.chart.charts.impl.line;
import io.dataease.chart.charts.impl.YoyChartHandler;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.engine.utils.Utils;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@Component
public class StackAreaHandler extends YoyChartHandler {
@Getter
private String type = "area-stack";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var xAxis = result.getAxisMap().get(ChartAxis.xAxis);
xAxis.addAll(view.getExtStack());
var yAxis = result.getAxisMap().get(ChartAxis.yAxis);
yAxis.addAll(view.getExtLabel());
yAxis.addAll(view.getExtTooltip());
result.getAxisMap().put(ChartAxis.extStack, view.getExtStack());
result.getAxisMap().put(ChartAxis.extLabel, view.getExtLabel());
result.getAxisMap().put(ChartAxis.extTooltip, view.getExtTooltip());
return result;
}
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult
.getFilterList()
.stream()
.anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var extStack = formatResult.getAxisMap().get(ChartAxis.extStack);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var drillAxis = xAxis.stream().filter(axis -> FieldSource.DRILL == axis.getSource()).toList();
var xAxisBase = xAxis.subList(0, xAxis.size() - extStack.size() - drillAxis.size());
return ChartDataBuild.transStackChartDataAntV(xAxisBase, xAxis, yAxis, view, data, extStack, isDrill);
}
@Override
public <T extends CustomFilterResult> T customFilter(ChartViewDTO view, List<ChartExtFilterDTO> filterList, AxisFormatResult formatResult) {
var result = super.customFilter(view, filterList, formatResult);
List<ChartDrillRequest> drillRequestList = view.getChartExtRequest().getDrill();
var drillFields = formatResult.getAxisMap().get(ChartAxis.drill);
// 堆叠维度下钻
if (ObjectUtils.isNotEmpty(drillRequestList) && (drillFields.size() > drillRequestList.size())) {
List<ChartExtFilterDTO> noDrillFilterList = filterList
.stream()
.filter(ele -> ele.getFilterType() != 1)
.collect(Collectors.toList());
var noDrillFieldAxis = formatResult.getAxisMap().get(ChartAxis.xAxis)
.stream()
.filter(ele -> ele.getSource() != FieldSource.DRILL)
.collect(Collectors.toList());
List<ChartExtFilterDTO> drillFilters = new ArrayList<>();
ArrayList<ChartViewFieldDTO> fieldsToFilter = new ArrayList<>();
var extStack = formatResult.getAxisMap().get(ChartAxis.extStack);
if (ObjectUtils.isNotEmpty(extStack) &&
Objects.equals(drillFields.get(0).getId(), extStack.get(0).getId())) {
fieldsToFilter.addAll(view.getXAxis());
}
groupStackDrill(noDrillFieldAxis, noDrillFilterList, fieldsToFilter, drillFields, drillRequestList);
formatResult.getAxisMap().put(ChartAxis.xAxis, noDrillFieldAxis);
result.setFilterList(noDrillFilterList);
}
return (T) result;
}
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
boolean needOrder = Utils.isNeedOrder(dsList);
boolean crossDs = Utils.isCrossDs(dsMap);
var result = (T) super.calcChartResult(view, formatResult, filterResult, sqlMap, sqlMeta, provider);
try {
//如果有同环比过滤,应该用原始sql
var originSql = result.getQuerySql();
var dynamicAssistFields = getDynamicAssistFields(view);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var assistFields = getAssistFields(dynamicAssistFields, yAxis);
if (CollectionUtils.isNotEmpty(assistFields)) {
var req = new DatasourceRequest();
req.setDsList(dsMap);
var assistSql = assistSQL(originSql, assistFields, dsMap);
req.setQuery(assistSql);
logger.debug("calcite assist sql: " + assistSql);
var assistData = (List<String[]>) provider.fetchResultField(req).get("data");
result.setAssistData(assistData);
result.setDynamicAssistFields(dynamicAssistFields);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}

View File

@ -0,0 +1,20 @@
package io.dataease.chart.charts.impl.map;
import io.dataease.chart.charts.impl.ExtQuotaChartHandler;
import io.dataease.extensions.view.dto.AxisFormatResult;
import io.dataease.extensions.view.dto.ChartViewDTO;
import lombok.Getter;
import org.springframework.stereotype.Component;
@Component
public class BubbleMapHandler extends ExtQuotaChartHandler {
@Getter
private String type = "bubble-map";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
return super.formatAxis(view);
}
}

View File

@ -0,0 +1,26 @@
package io.dataease.chart.charts.impl.map;
import io.dataease.chart.charts.impl.GroupChartHandler;
import io.dataease.extensions.view.dto.AxisFormatResult;
import io.dataease.extensions.view.dto.ChartAxis;
import io.dataease.extensions.view.dto.ChartViewDTO;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Optional;
@Component
public class FlowMapHandler extends GroupChartHandler {
@Getter
private String type = "flow-map";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var xAxis = result.getAxisMap().get(ChartAxis.xAxis);
xAxis.addAll(Optional.ofNullable(view.getFlowMapStartName()).orElse(new ArrayList<>()));
xAxis.addAll(Optional.ofNullable(view.getFlowMapEndName()).orElse(new ArrayList<>()));
result.getAxisMap().put(ChartAxis.xAxis, xAxis);
return result;
}
}

View File

@ -0,0 +1,28 @@
package io.dataease.chart.charts.impl.map;
import io.dataease.chart.charts.impl.DefaultChartHandler;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@Component
public class HeatMapHandler extends DefaultChartHandler {
@Getter
private String type = "heat-map";
@Override
public Map<String, Object> buildResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult
.getFilterList()
.stream()
.anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
Map<String, Object> result = ChartDataBuild.transHeatMapChartDataAntV(xAxis, xAxis, yAxis, view, data, isDrill);
return result;
}
}

View File

@ -0,0 +1,15 @@
package io.dataease.chart.charts.impl.map;
import io.dataease.chart.charts.impl.ExtQuotaChartHandler;
import io.dataease.extensions.view.dto.AxisFormatResult;
import io.dataease.extensions.view.dto.ChartAxis;
import io.dataease.chart.charts.impl.DefaultChartHandler;
import io.dataease.extensions.view.dto.ChartViewDTO;
import lombok.Getter;
import org.springframework.stereotype.Component;
@Component
public class MapHandler extends ExtQuotaChartHandler {
@Getter
private String type = "map";
}

View File

@ -0,0 +1,153 @@
package io.dataease.chart.charts.impl.map;
import io.dataease.chart.charts.impl.GroupChartHandler;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.dataset.manage.DatasetDataManage;
import io.dataease.engine.sql.SQLProvider;
import io.dataease.engine.trans.Dimension2SQLObj;
import io.dataease.engine.trans.Field2SQLObj;
import io.dataease.engine.trans.Quota2SQLObj;
import io.dataease.engine.trans.Table2SQLObj;
import io.dataease.engine.utils.Utils;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import io.dataease.extensions.view.util.ChartDataUtil;
import io.dataease.extensions.view.util.FieldUtil;
import io.dataease.utils.BeanUtils;
import jakarta.annotation.Resource;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class SymbolicMapHandler extends GroupChartHandler {
@Resource
private DatasetDataManage datasetDataManage;
@Getter
private String type = "symbolic-map";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
result.getAxisMap().put(ChartAxis.extBubble, view.getExtBubble());
return result;
}
private Map<String, Object> customBuildResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data, List<ChartViewFieldDTO> detailFields, List<String[]> detailData) {
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var extBubble = formatResult.getAxisMap().get(ChartAxis.extBubble);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
return ChartDataBuild.transSymbolicMapNormalWithDetail(view, xAxis, yAxis, extBubble, data, detailFields, detailData);
}
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
boolean needOrder = Utils.isNeedOrder(dsList);
boolean crossDs = Utils.isCrossDs(dsMap);
DatasourceRequest datasourceRequest = new DatasourceRequest();
datasourceRequest.setDsList(dsMap);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var extBubble = formatResult.getAxisMap().get(ChartAxis.extBubble);
var allFields = (List<ChartViewFieldDTO>) filterResult.getContext().get("allFields");
List<ChartViewFieldDTO> countField =chartViewManege.transFieldDTO(Collections.singletonList(chartViewManege.createCountField(view.getTableId())));
List<DatasetTableFieldDTO> datasetTableFieldDTOList = FieldUtil.transFields(allFields);
SQLMeta sqlMeta1 = new SQLMeta();
BeanUtils.copyBean(sqlMeta1, sqlMeta);
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, datasetTableFieldDTOList, crossDs, dsMap, Utils.getParams(datasetTableFieldDTOList), view.getCalParams(), pluginManage);
List<ChartViewFieldDTO> yAxis = new ArrayList<>();
if(!extBubble.isEmpty() && !"*".equals(extBubble.get(0).getDataeaseName())){
yAxis.addAll(extBubble);
}
yAxis.addAll(countField);
datasetTableFieldDTOList.addAll(FieldUtil.transFields(countField));
formatResult.getAxisMap().put(ChartAxis.yAxis,countField);
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, datasetTableFieldDTOList, crossDs, dsMap, Utils.getParams(datasetTableFieldDTOList), view.getCalParams(), pluginManage);
String querySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
querySql = provider.rebuildSQL(querySql, sqlMeta, crossDs, dsMap);
datasourceRequest.setQuery(querySql);
logger.debug("calcite chart sql: " + querySql);
List<String[]> data = (List<String[]>) provider.fetchResultField(datasourceRequest).get("data");
// 获取所有字段数据作为数据详情返回
List<String[]> detailData = new ArrayList<>();
List<Long> xAxisIds = xAxis.stream().map(ChartViewFieldDTO::getId).toList();
List<ChartViewFieldDTO> detailFields = new ArrayList<>();
detailFields.addAll(xAxis);
detailFields.addAll(allFields.stream().filter(field -> !xAxisIds.contains(field.getId())).toList());
if (ObjectUtils.isNotEmpty(detailFields)) {
List<DatasetTableFieldDTO> allFieldsTmp = FieldUtil.transFields(detailFields);
datasetDataManage.buildFieldName(sqlMap, allFieldsTmp);
String sql = (String) sqlMap.get("sql");
sql = Utils.replaceSchemaAlias(sql, dsMap);
SQLMeta sqlMeta2 = new SQLMeta();
Table2SQLObj.table2sqlobj(sqlMeta2, null, "(" + sql + ")", crossDs);
Field2SQLObj.field2sqlObj(sqlMeta2, allFieldsTmp, allFieldsTmp, crossDs, dsMap, Utils.getParams(allFieldsTmp), null, pluginManage);// todo chartParam从视图里取
String querySQL;
querySQL = SQLProvider.createQuerySQL(sqlMeta2, false, needOrder, false);
querySQL = provider.rebuildSQL(querySQL, sqlMeta2, crossDs, dsMap);
logger.debug("calcite data preview sql: " + querySQL);
// 调用数据源的calcite获得data
DatasourceRequest datasourceRequest1 = new DatasourceRequest();
datasourceRequest1.setQuery(querySQL);
datasourceRequest1.setDsList(dsMap);
detailData = (List<String[]>) provider.fetchResultField(datasourceRequest1).get("data");
}
//自定义排序
data = ChartDataUtil.resultCustomSort(xAxis, yAxis, view.getSortPriority(), data);
//数据重组逻辑可重载
var result = customBuildResult(view, formatResult, filterResult, data, detailFields, detailData);
T calcResult = (T) new ChartCalcDataResult();
calcResult.setData(result);
calcResult.setContext(filterResult.getContext());
calcResult.setQuerySql(querySql);
calcResult.setOriginData(data);
formatResult.getAxisMap().put(ChartAxis.yAxis,new ArrayList<>());
return calcResult;
}
@Override
public ChartViewDTO buildChart(ChartViewDTO view, ChartCalcDataResult calcResult, AxisFormatResult formatResult, CustomFilterResult filterResult) {
var desensitizationList = (Map<String, ColumnPermissionItem>) filterResult.getContext().get("desensitizationList");
var allFields = (List<ChartViewFieldDTO>) filterResult.getContext().get("allFields");
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var extBubble = formatResult.getAxisMap().get(ChartAxis.extBubble);
// 如果是表格导出查询 则在此处直接就可以返回
var extStack = formatResult.getAxisMap().get(ChartAxis.extStack);
if (view.getIsExcelExport()) {
Map<String, Object> sourceInfo = ChartDataBuild.transTableNormal(xAxis, extBubble, view, calcResult.getOriginData(), extStack, desensitizationList);
sourceInfo.put("sourceData", calcResult.getOriginData());
view.setData(sourceInfo);
return view;
}
// 图表组件可再扩展
Map<String, Object> mapTableNormal = calcResult.getData();
var drillFilters = filterResult.getFilterList().stream().filter(f -> f.getFilterType() == 1).collect(Collectors.toList());
var isDrill = CollectionUtils.isNotEmpty(drillFilters);
// 构建结果
Map<String, Object> dataMap = new TreeMap<>();
dataMap.putAll(calcResult.getData());
dataMap.putAll(mapTableNormal);
dataMap.put("sourceFields", allFields);
mergeAssistField(calcResult.getDynamicAssistFields(), calcResult.getAssistData());
dataMap.put("dynamicAssistLines", calcResult.getDynamicAssistFields());
view.setData(dataMap);
view.setSql(Base64.getEncoder().encodeToString(calcResult.getQuerySql().getBytes()));
view.setDrill(isDrill);
view.setDrillFilters(drillFilters);
return view;
}
}

View File

@ -0,0 +1,32 @@
package io.dataease.chart.charts.impl.mix;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.view.dto.AxisFormatResult;
import io.dataease.extensions.view.dto.ChartAxis;
import io.dataease.extensions.view.dto.ChartViewDTO;
import io.dataease.extensions.view.dto.CustomFilterResult;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@Component
public class DualLineMixHandler extends GroupMixHandler {
@Getter
private final String type = "chart-mix-dual-line";
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult
.getFilterList()
.stream()
.anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var xAxisExt = formatResult.getAxisMap().get(ChartAxis.xAxisExt);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var xAxisBase = xAxis.subList(0, xAxis.size() - xAxisExt.size());
return ChartDataBuild.transMixChartDataAntV(xAxisBase, xAxis, xAxisExt, yAxis, view, data, isDrill, true);
}
}

View File

@ -0,0 +1,52 @@
package io.dataease.chart.charts.impl.mix;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class GroupMixHandler extends MixHandler {
@Getter
private final String type = "chart-mix-group";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var axisMap = new HashMap<ChartAxis, List<ChartViewFieldDTO>>();
var context = new HashMap<String, Object>();
AxisFormatResult result = new AxisFormatResult(axisMap, context);
//左轴分组子维度,非分组不需要
axisMap.put(ChartAxis.xAxisExt, view.getXAxisExt());
//左轴堆叠子维度,非堆叠不需要
axisMap.put(ChartAxis.extStack, Collections.emptyList());
//左轴指标
axisMap.put(ChartAxis.yAxis, view.getYAxis());
//右轴分组子维度
axisMap.put(ChartAxis.extBubble, view.getExtBubble());
//右轴指标
axisMap.put(ChartAxis.yAxisExt, view.getYAxisExt());
//去除除了x轴以外的排序
axisMap.forEach((k, v) -> {
if (!ChartAxis.xAxisExt.equals(k)) {
v.forEach(x -> x.setSort("none"));
}
});
axisMap.put(ChartAxis.extLabel, view.getExtLabel());
axisMap.put(ChartAxis.extTooltip, view.getExtTooltip());
//图表整体主维度
var xAxis = new ArrayList<>(view.getXAxis());
var xAxisGroup = new ArrayList<>(view.getXAxis());
xAxisGroup.addAll(view.getXAxisExt());
axisMap.put(ChartAxis.xAxis, xAxisGroup);
context.put("xAxisBase", xAxis);
axisMap.put(ChartAxis.drill, new ArrayList<>(view.getDrillFields()));
return result;
}
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
return super.buildNormalResult(view, formatResult, filterResult, data);
}
}

View File

@ -0,0 +1,212 @@
package io.dataease.chart.charts.impl.mix;
import io.dataease.chart.charts.impl.YoyChartHandler;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.engine.utils.Utils;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class MixHandler extends YoyChartHandler {
@Getter
private final String type = "chart-mix";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var axisMap = new HashMap<ChartAxis, List<ChartViewFieldDTO>>();
var context = new HashMap<String, Object>();
AxisFormatResult result = new AxisFormatResult(axisMap, context);
//左轴分组子维度,非分组不需要
axisMap.put(ChartAxis.xAxisExt, Collections.emptyList());
//左轴堆叠子维度,非堆叠不需要
axisMap.put(ChartAxis.extStack, Collections.emptyList());
//左轴指标
axisMap.put(ChartAxis.yAxis, view.getYAxis());
//右轴分组子维度
axisMap.put(ChartAxis.extBubble, view.getExtBubble());
//右轴指标
axisMap.put(ChartAxis.yAxisExt, view.getYAxisExt());
//去除除了x轴以外的排序
axisMap.forEach((k, v) -> {
v.forEach(x -> x.setSort("none"));
});
axisMap.put(ChartAxis.extLabel, view.getExtLabel());
axisMap.put(ChartAxis.extTooltip, view.getExtTooltip());
//图表整体主维度
axisMap.put(ChartAxis.xAxis, new ArrayList<>(view.getXAxis()));
context.put("xAxisBase", new ArrayList<>(view.getXAxis()));
axisMap.put(ChartAxis.drill, new ArrayList<>(view.getDrillFields()));
return result;
}
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult
.getFilterList()
.stream()
.anyMatch(ele -> ele.getFilterType() == 1);
if (StringUtils.equals((String) formatResult.getContext().get("isRight"), "isRight")) {
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var xAxisExt = formatResult.getAxisMap().get(ChartAxis.xAxisExt);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var xAxisBase = xAxis.subList(0, xAxis.size() - xAxisExt.size());
return ChartDataBuild.transMixChartDataAntV(xAxisBase, xAxis, xAxisExt, yAxis, view, data, isDrill, true);
}
var xAxisBase = (List<ChartViewFieldDTO>) formatResult.getContext().get("xAxisBase");
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var xAxisExt = formatResult.getAxisMap().get(ChartAxis.xAxisExt);
var result = ChartDataBuild.transMixChartDataAntV(xAxisBase, xAxis, xAxisExt, yAxis, view, data, isDrill, false);
return result;
}
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
//计算左轴, 包含 xAxis, yAxis
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
boolean needOrder = Utils.isNeedOrder(dsList);
boolean crossDs = Utils.isCrossDs(dsMap);
var leftResult = (T) super.calcChartResult(view, formatResult, filterResult, sqlMap, sqlMeta, provider);
var dynamicAssistFields = getDynamicAssistFields(view);
try {
//如果有同环比过滤,应该用原始sql
var originSql = leftResult.getQuerySql();
var leftAssistFields = dynamicAssistFields.stream().filter(x -> StringUtils.equalsAnyIgnoreCase(x.getYAxisType(), "left")).toList();
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var assistFields = getAssistFields(leftAssistFields, yAxis);
if (CollectionUtils.isNotEmpty(assistFields)) {
var req = new DatasourceRequest();
req.setDsList(dsMap);
var assistSql = assistSQL(originSql, assistFields, dsMap);
req.setQuery(assistSql);
logger.debug("calcite assistSql sql: " + assistSql);
var assistData = (List<String[]>) provider.fetchResultField(req).get("data");
leftResult.setAssistData(assistData);
leftResult.setDynamicAssistFields(leftAssistFields);
}
} catch (Exception e) {
e.printStackTrace();
}
AxisFormatResult formatResult2 = new AxisFormatResult();
var axisMap = new HashMap<ChartAxis, List<ChartViewFieldDTO>>();
axisMap.put(ChartAxis.extLabel, new ArrayList<>(formatResult.getAxisMap().get(ChartAxis.extLabel)));
axisMap.put(ChartAxis.extTooltip, new ArrayList<>(formatResult.getAxisMap().get(ChartAxis.extTooltip)));
axisMap.put(ChartAxis.drill, new ArrayList<>(formatResult.getAxisMap().get(ChartAxis.drill)));
formatResult2.setAxisMap(axisMap);
formatResult2.setContext(formatResult.getContext());
// 计算右轴包含 xAxis,xAxisExt,yAxisExt,需要去掉 group stack
var xAxis = new ArrayList<>(view.getXAxis());
var extBubble = new ArrayList<>(formatResult.getAxisMap().get(ChartAxis.extBubble));
xAxis.addAll(extBubble);
var dillAxis = (ArrayList<ChartViewFieldDTO>) formatResult.getContext().get("dillAxis");
var fields = xAxis.stream().map(ChartViewFieldDTO::getId).collect(Collectors.toSet());
for (ChartViewFieldDTO dillAxi : dillAxis) {
if (!fields.contains(dillAxi.getId())) {
xAxis.add(dillAxi);
}
}
formatResult2.getAxisMap().put(ChartAxis.xAxis, xAxis);
formatResult2.getAxisMap().put(ChartAxis.xAxisExt, extBubble);
var yAxisExt = new ArrayList<>(formatResult.getAxisMap().get(ChartAxis.yAxisExt));
formatResult2.getAxisMap().put(ChartAxis.yAxis, yAxisExt);
formatResult2.getContext().remove("yoyFiltered");
formatResult2.getContext().put("isRight", "isRight");
formatResult.getContext().put("subAxisMap", axisMap);
// 右轴重新检测同环比过滤
customFilter(view, filterResult.getFilterList(), formatResult2);
var rightResult = (T) super.calcChartResult(view, formatResult2, filterResult, sqlMap, sqlMeta, provider);
try {
//如果有同环比过滤,应该用原始sql
var originSql = rightResult.getQuerySql();
var rightAssistFields = dynamicAssistFields.stream().filter(x -> StringUtils.equalsAnyIgnoreCase(x.getYAxisType(), "right")).toList();
var yAxis = formatResult2.getAxisMap().get(ChartAxis.yAxis);
var assistFields = getAssistFields(rightAssistFields, yAxis);
if (CollectionUtils.isNotEmpty(assistFields)) {
var req = new DatasourceRequest();
req.setDsList(dsMap);
var assistSql = assistSQL(originSql, assistFields, dsMap);
req.setQuery(assistSql);
var assistData = (List<String[]>) provider.fetchResultField(req).get("data");
rightResult.setAssistData(assistData);
rightResult.setDynamicAssistFields(rightAssistFields);
}
} catch (Exception e) {
e.printStackTrace();
}
var mixResult = (T) new ChartCalcDataResult();
var data = new HashMap<String, Object>();
data.put("left", leftResult);
data.put("right", rightResult);
mixResult.setData(data);
mixResult.setContext(filterResult.getContext());
return mixResult;
}
@Override
public ChartViewDTO buildChart(ChartViewDTO view, ChartCalcDataResult calcResult, AxisFormatResult formatResult, CustomFilterResult filterResult) {
var desensitizationList = (Map<String, ColumnPermissionItem>) filterResult.getContext().get("desensitizationList");
var leftCalcResult = (ChartCalcDataResult) calcResult.getData().get("left");
var leftFields = new ArrayList<ChartViewFieldDTO>();
leftFields.addAll(formatResult.getAxisMap().get(ChartAxis.xAxis));
leftFields.addAll(formatResult.getAxisMap().get(ChartAxis.yAxis));
mergeAssistField(leftCalcResult.getDynamicAssistFields(), leftCalcResult.getAssistData());
var leftOriginData = leftCalcResult.getOriginData();
var leftTable = ChartDataBuild.transTableNormal(leftFields, view, leftOriginData, desensitizationList);
var leftData = new HashMap<String, Object>(leftTable);
leftData.putAll(leftCalcResult.getData());
leftData.put("dynamicAssistLines", leftCalcResult.getDynamicAssistFields());
var rightCalcResult = (ChartCalcDataResult) calcResult.getData().get("right");
var rightFields = new ArrayList<ChartViewFieldDTO>();
var subAxisMap = (HashMap<ChartAxis, List<ChartViewFieldDTO>>) formatResult.getContext().get("subAxisMap");
rightFields.addAll(subAxisMap.get(ChartAxis.xAxis));
rightFields.addAll(subAxisMap.get(ChartAxis.yAxis));
mergeAssistField(rightCalcResult.getDynamicAssistFields(), rightCalcResult.getAssistData());
var rightOriginData = rightCalcResult.getOriginData();
var rightTable = ChartDataBuild.transTableNormal(rightFields, view, rightOriginData, desensitizationList);
var rightData = new HashMap<String, Object>(rightTable);
rightData.putAll(rightCalcResult.getData());
rightData.put("dynamicAssistLines", rightCalcResult.getDynamicAssistFields());
// 构建结果
Map<String, Object> chartData = new TreeMap<>();
chartData.put("left", leftData);
chartData.put("right", rightData);
var drillFilters = filterResult.getFilterList().stream().filter(f -> f.getFilterType() == 1).collect(Collectors.toList());
// 日期下钻替换回去
drillFilters.forEach(f -> {
if (CollectionUtils.isNotEmpty(f.getOriginValue())) {
f.setValue(f.getOriginValue());
}
});
var isDrill = CollectionUtils.isNotEmpty(drillFilters);
view.setDrillFilters(drillFilters);
view.setDrill(isDrill);
view.setSql(leftCalcResult.getQuerySql());
view.setData(chartData);
return view;
}
}

View File

@ -0,0 +1,72 @@
package io.dataease.chart.charts.impl.mix;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class StackMixHandler extends MixHandler {
@Getter
private final String type = "chart-mix-stack";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var axisMap = new HashMap<ChartAxis, List<ChartViewFieldDTO>>();
var context = new HashMap<String, Object>();
AxisFormatResult result = new AxisFormatResult(axisMap, context);
//左轴分组子维度,非分组不需要
axisMap.put(ChartAxis.xAxisExt, Collections.emptyList());
//左轴堆叠子维度,非堆叠不需要
axisMap.put(ChartAxis.extStack, view.getExtStack());
//左轴指标
axisMap.put(ChartAxis.yAxis, view.getYAxis());
//右轴分组子维度
axisMap.put(ChartAxis.extBubble, view.getExtBubble());
//右轴指标
axisMap.put(ChartAxis.yAxisExt, view.getYAxisExt());
//去除除了x轴以外的排序
axisMap.forEach((k, v) -> {
if (!ChartAxis.extStack.equals(k)) {
v.forEach(x -> x.setSort("none"));
}
});
axisMap.put(ChartAxis.extLabel, view.getExtLabel());
axisMap.put(ChartAxis.extTooltip, view.getExtTooltip());
//图表整体主维度
var xAxis = new ArrayList<>(view.getXAxis());
var xAxisStack = new ArrayList<>(view.getXAxis());
xAxisStack.addAll(view.getExtStack());
axisMap.put(ChartAxis.xAxis, xAxisStack);
context.put("xAxisBase", xAxis);
axisMap.put(ChartAxis.drill, new ArrayList<>(view.getDrillFields()));
return result;
}
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult
.getFilterList()
.stream()
.anyMatch(ele -> ele.getFilterType() == 1);
var extStack = formatResult.getAxisMap().get(ChartAxis.extStack);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
if (CollectionUtils.isNotEmpty(extStack)) {
// 堆叠左轴
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var drillAxis = xAxis.stream().filter(axis -> FieldSource.DRILL == axis.getSource()).toList();
var xAxisBase = xAxis.subList(0, xAxis.size() - extStack.size() - drillAxis.size());
//var xAxisBase = (List<ChartViewFieldDTO>) formatResult.getContext().get("xAxisBase");
return ChartDataBuild.transMixChartStackDataAntV(xAxisBase, xAxis, extStack, yAxis, view, data, isDrill);
} else {
//无堆叠左轴和右轴还是走原逻辑
var xAxisBase = (List<ChartViewFieldDTO>) formatResult.getContext().get("xAxisBase");
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var xAxisExt = formatResult.getAxisMap().get(ChartAxis.xAxisExt);
return super.buildNormalResult(view, formatResult, filterResult, data);
}
}
}

View File

@ -0,0 +1,45 @@
package io.dataease.chart.charts.impl.numeric;
import io.dataease.extensions.view.dto.AxisFormatResult;
import io.dataease.extensions.view.dto.ChartAxis;
import io.dataease.dataset.manage.DatasetTableFieldManage;
import io.dataease.extensions.view.dto.ChartViewDTO;
import io.dataease.extensions.view.dto.ChartViewFieldDTO;
import jakarta.annotation.Resource;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class GaugeHandler extends NumericalChartHandler {
@Getter
private String type = "gauge";
@Resource
private DatasetTableFieldManage datasetTableFieldManage;
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var axisMap = new HashMap<ChartAxis, List<ChartViewFieldDTO>>();
var yAxis = new ArrayList<>(view.getYAxis());
Map<String, Object> customAttr = view.getCustomAttr();
Map<String, Object> size = (Map<String, Object>) customAttr.get("misc");
ChartViewFieldDTO gaugeMinViewField = getDynamicField(size, "gaugeMinType", "gaugeMinField");
if (gaugeMinViewField != null) {
yAxis.add(gaugeMinViewField);
}
ChartViewFieldDTO gaugeMaxViewField = getDynamicField(size, "gaugeMaxType", "gaugeMaxField");
if (gaugeMaxViewField != null) {
yAxis.add(gaugeMaxViewField);
}
axisMap.put(ChartAxis.xAxis, new ArrayList<>());
axisMap.put(ChartAxis.yAxis, yAxis);
var context = new HashMap<String, Object>();
var result = new AxisFormatResult(axisMap, context);
return result;
}
}

View File

@ -0,0 +1,65 @@
package io.dataease.chart.charts.impl.numeric;
import io.dataease.chart.charts.impl.YoyChartHandler;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class IndicatorHandler extends YoyChartHandler {
@Getter
private String render = "custom";
@Getter
private String type = "indicator";
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult.getFilterList().stream().anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
return ChartDataBuild.transNormalChartData(xAxis, yAxis, view, data, isDrill);
}
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
this.setIndicatorHandlerXAxis(formatResult, filterResult);
return (T) super.calcChartResult(view, formatResult, filterResult, sqlMap, sqlMeta, provider);
}
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var axisMap = new HashMap<ChartAxis, List<ChartViewFieldDTO>>();
var yAxis = new ArrayList<>(view.getYAxis());
axisMap.put(ChartAxis.xAxis, new ArrayList<>());
axisMap.put(ChartAxis.yAxis, yAxis);
var context = new HashMap<String, Object>();
return new AxisFormatResult(axisMap, context);
}
private void setIndicatorHandlerXAxis(AxisFormatResult formatResult, CustomFilterResult filterResult) {
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var allFields = (List<ChartViewFieldDTO>) filterResult.getContext().get("allFields");
ChartViewFieldDTO yAxisChartViewFieldDTO = yAxis.get(0);
ChartFieldCompareDTO compareCalc = yAxisChartViewFieldDTO.getCompareCalc();
boolean isYoy = org.apache.commons.lang3.StringUtils.isNotEmpty(compareCalc.getType())
&& !org.apache.commons.lang3.StringUtils.equalsIgnoreCase(compareCalc.getType(), "none");
if (isYoy) {
xAxis.clear();
// 设置维度字段从同环比中获取用户选择的字段
xAxis.addAll(allFields.stream().filter(i -> org.springframework.util.StringUtils.endsWithIgnoreCase(i.getId().toString(), compareCalc.getField().toString())).toList());
xAxis.get(0).setSort("desc");
if(Objects.isNull(compareCalc.getCustom())){
xAxis.get(0).setDateStyle("y_M_d");
}else{
xAxis.get(0).setDateStyle(compareCalc.getCustom().getTimeType());
}
}
formatResult.getAxisMap().put(ChartAxis.xAxis, xAxis);
}
}

View File

@ -0,0 +1,36 @@
package io.dataease.chart.charts.impl.numeric;
import io.dataease.extensions.view.dto.AxisFormatResult;
import io.dataease.extensions.view.dto.ChartAxis;
import io.dataease.extensions.view.dto.ChartViewDTO;
import io.dataease.extensions.view.dto.ChartViewFieldDTO;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class LiquidHandler extends NumericalChartHandler {
@Getter
private String type = "liquid";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var axisMap = new HashMap<ChartAxis, List<ChartViewFieldDTO>>();
var yAxis = new ArrayList<>(view.getYAxis());
Map<String, Object> customAttr = view.getCustomAttr();
Map<String, Object> misc = (Map<String, Object>) customAttr.get("misc");
ChartViewFieldDTO liquidMaxViewField = getDynamicField(misc, "liquidMaxType", "liquidMaxField");
if (liquidMaxViewField != null) {
yAxis.add(liquidMaxViewField);
}
axisMap.put(ChartAxis.xAxis, new ArrayList<>());
axisMap.put(ChartAxis.yAxis, yAxis);
var context = new HashMap<String, Object>();
var result = new AxisFormatResult(axisMap, context);
return result;
}
}

View File

@ -0,0 +1,85 @@
package io.dataease.chart.charts.impl.numeric;
import io.dataease.chart.charts.impl.DefaultChartHandler;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.engine.sql.SQLProvider;
import io.dataease.engine.trans.Quota2SQLObj;
import io.dataease.engine.utils.Utils;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import io.dataease.extensions.view.util.FieldUtil;
import io.dataease.i18n.Translator;
import io.dataease.utils.BeanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class NumericalChartHandler extends DefaultChartHandler {
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
boolean needOrder = Utils.isNeedOrder(dsList);
boolean crossDs = Utils.isCrossDs(dsMap);
DatasourceRequest datasourceRequest = new DatasourceRequest();
datasourceRequest.setDsList(dsMap);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var allFields = (List<ChartViewFieldDTO>) filterResult.getContext().get("allFields");
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, FieldUtil.transFields(allFields), crossDs, dsMap, Utils.getParams(FieldUtil.transFields(allFields)), view.getCalParams(), pluginManage);
String querySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
querySql = provider.rebuildSQL(querySql, sqlMeta, crossDs, dsMap);
datasourceRequest.setQuery(querySql);
logger.debug("calcite chart sql: " + querySql);
List<String[]> data = (List<String[]>) provider.fetchResultField(datasourceRequest).get("data");
boolean isdrill = filterResult
.getFilterList()
.stream()
.anyMatch(ele -> ele.getFilterType() == 1);
Map<String, Object> result = ChartDataBuild.transNormalChartData(xAxis, yAxis, view, data, isdrill);
T calcResult = (T) new ChartCalcDataResult();
calcResult.setData(result);
calcResult.setContext(filterResult.getContext());
calcResult.setQuerySql(querySql);
calcResult.setOriginData(data);
return calcResult;
}
protected ChartViewFieldDTO getDynamicField(Map<String, Object> target, String type, String field) {
String maxType = (String) target.get(type);
if (StringUtils.equalsIgnoreCase("dynamic", maxType)) {
Map<String, Object> maxField = (Map<String, Object>) target.get(field);
if (maxField.get("id") == null || StringUtils.isEmpty(maxField.get("id").toString())) {
DEException.throwException(Translator.get("i18n_gauge_field_delete"));
}
Long id = Long.valueOf((String) maxField.get("id"));
String summary = (String) maxField.get("summary");
DatasetTableFieldDTO datasetTableField = datasetTableFieldManage.selectById(id);
if (ObjectUtils.isNotEmpty(datasetTableField)) {
if (datasetTableField.getDeType() == 0 || datasetTableField.getDeType() == 1 || datasetTableField.getDeType() == 5) {
if (!StringUtils.containsIgnoreCase(summary, "count")) {
DEException.throwException(Translator.get("i18n_gauge_field_change"));
}
}
ChartViewFieldDTO dto = new ChartViewFieldDTO();
BeanUtils.copyBean(dto, datasetTableField);
dto.setSummary(summary);
return dto;
} else {
DEException.throwException(Translator.get("i18n_gauge_field_delete"));
}
}
return null;
}
}

View File

@ -0,0 +1,11 @@
package io.dataease.chart.charts.impl.others;
import io.dataease.chart.charts.impl.ExtQuotaChartHandler;
import lombok.Getter;
import org.springframework.stereotype.Component;
@Component
public class FunnelHandler extends ExtQuotaChartHandler {
@Getter
private String type = "funnel";
}

View File

@ -0,0 +1,26 @@
package io.dataease.chart.charts.impl.others;
import io.dataease.chart.charts.impl.DefaultChartHandler;
import io.dataease.chart.charts.impl.YoyChartHandler;
import io.dataease.extensions.view.dto.AxisFormatResult;
import io.dataease.extensions.view.dto.ChartAxis;
import io.dataease.extensions.view.dto.ChartViewDTO;
import lombok.Getter;
import org.springframework.stereotype.Component;
@Component
public class RadarHandler extends YoyChartHandler {
@Getter
private String type = "radar";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var yAxis = result.getAxisMap().get(ChartAxis.yAxis);
yAxis.addAll(view.getExtLabel());
yAxis.addAll(view.getExtTooltip());
result.getAxisMap().put(ChartAxis.extLabel, view.getExtLabel());
result.getAxisMap().put(ChartAxis.extTooltip, view.getExtTooltip());
return result;
}
}

View File

@ -0,0 +1,13 @@
package io.dataease.chart.charts.impl.others;
import io.dataease.chart.charts.impl.YoyChartHandler;
import lombok.Getter;
import org.springframework.stereotype.Component;
@Component
public class RichTextHandler extends YoyChartHandler {
@Getter
private String type = "rich-text";
@Getter
private String render = "custom";
}

View File

@ -0,0 +1,11 @@
package io.dataease.chart.charts.impl.others;
import io.dataease.chart.charts.impl.GroupChartHandler;
import lombok.Getter;
import org.springframework.stereotype.Component;
@Component
public class SankeyHandler extends GroupChartHandler {
@Getter
private String type = "sankey";
}

View File

@ -0,0 +1,11 @@
package io.dataease.chart.charts.impl.others;
import io.dataease.chart.charts.impl.ExtQuotaChartHandler;
import lombok.Getter;
import org.springframework.stereotype.Component;
@Component
public class TreemapHandler extends ExtQuotaChartHandler {
@Getter
private String type = "treemap";
}

View File

@ -0,0 +1,11 @@
package io.dataease.chart.charts.impl.others;
import io.dataease.chart.charts.impl.ExtQuotaChartHandler;
import lombok.Getter;
import org.springframework.stereotype.Component;
@Component
public class WaterfallHandler extends ExtQuotaChartHandler {
@Getter
private String type = "waterfall";
}

View File

@ -0,0 +1,15 @@
package io.dataease.chart.charts.impl.others;
import io.dataease.chart.charts.impl.DefaultChartHandler;
import io.dataease.chart.charts.impl.ExtQuotaChartHandler;
import io.dataease.extensions.view.dto.AxisFormatResult;
import io.dataease.extensions.view.dto.ChartAxis;
import io.dataease.extensions.view.dto.ChartViewDTO;
import lombok.Getter;
import org.springframework.stereotype.Component;
@Component
public class WordCloudHandler extends ExtQuotaChartHandler {
@Getter
private String type = "word-cloud";
}

View File

@ -0,0 +1,29 @@
package io.dataease.chart.charts.impl.pie;
import io.dataease.chart.charts.impl.YoyChartHandler;
import io.dataease.extensions.view.dto.AxisFormatResult;
import io.dataease.extensions.view.dto.ChartAxis;
import io.dataease.extensions.view.dto.ChartViewDTO;
import org.springframework.stereotype.Component;
@Component
public class PieHandler extends YoyChartHandler {
@Override
public void init() {
chartHandlerManager.registerChartHandler(this.getRender(), "pie", this);
chartHandlerManager.registerChartHandler(this.getRender(), "pie-rose", this);
chartHandlerManager.registerChartHandler(this.getRender(), "pie-donut", this);
chartHandlerManager.registerChartHandler(this.getRender(), "pie-donut-rose", this);
}
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var yAxis = result.getAxisMap().get(ChartAxis.yAxis);
yAxis.addAll(view.getExtLabel());
yAxis.addAll(view.getExtTooltip());
result.getAxisMap().put(ChartAxis.extLabel, view.getExtLabel());
result.getAxisMap().put(ChartAxis.extTooltip, view.getExtTooltip());
return result;
}
}

View File

@ -0,0 +1,40 @@
package io.dataease.chart.charts.impl.scatter;
import io.dataease.chart.charts.impl.YoyChartHandler;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
public class QuadrantHandler extends YoyChartHandler {
@Getter
private String type = "quadrant";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var yAxis = new ArrayList<>(view.getYAxis());
yAxis.addAll(view.getYAxisExt());
yAxis.addAll(view.getExtBubble());
yAxis.addAll(view.getExtTooltip());
result.getAxisMap().put(ChartAxis.yAxis, yAxis);
result.getAxisMap().put(ChartAxis.extBubble, view.getExtBubble());
result.getAxisMap().put(ChartAxis.extTooltip, view.getExtTooltip());
result.getAxisMap().put(ChartAxis.yAxisExt, view.getYAxisExt());
return result;
}
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult.getFilterList().stream().anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
Map<String, Object> result = ChartDataBuild.transMixChartDataAntV(xAxis, xAxis, new ArrayList<>(), yAxis, view, data, isDrill);
return result;
}
}

View File

@ -0,0 +1,54 @@
package io.dataease.chart.charts.impl.scatter;
import io.dataease.chart.charts.impl.YoyChartHandler;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@Component
public class ScatterHandler extends YoyChartHandler {
@Getter
private String type = "scatter";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var yAxis = new ArrayList<>(view.getYAxis());
yAxis.addAll(view.getExtBubble());
yAxis.addAll(view.getExtLabel());
yAxis.addAll(view.getExtTooltip());
result.getAxisMap().put(ChartAxis.yAxis, yAxis);
result.getAxisMap().put(ChartAxis.extBubble, view.getExtBubble());
result.getAxisMap().put(ChartAxis.extTooltip, view.getExtTooltip());
result.getAxisMap().put(ChartAxis.extLabel, view.getExtLabel());
return result;
}
@Override
public Map<String, Object> buildNormalResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
boolean isDrill = filterResult.getFilterList().stream().anyMatch(ele -> ele.getFilterType() == 1);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var yAxisTemp = new ArrayList<>(yAxis);
var extBubble = formatResult.getAxisMap().get(ChartAxis.extBubble);
if (!extBubble.isEmpty()) {
// 剔除气泡大小移除一个
Iterator<ChartViewFieldDTO> iterator = yAxisTemp.iterator();
while (iterator.hasNext()) {
ChartViewFieldDTO obj = iterator.next();
if (obj.getId().equals(extBubble.getFirst().getId())) {
iterator.remove();
break;
}
}
}
Map<String, Object> result = ChartDataBuild.transScatterDataAntV(xAxis, yAxisTemp, view, data, extBubble, isDrill);
return result;
}
}

View File

@ -0,0 +1,83 @@
package io.dataease.chart.charts.impl.table;
import io.dataease.chart.charts.impl.DefaultChartHandler;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.engine.sql.SQLProvider;
import io.dataease.engine.trans.Dimension2SQLObj;
import io.dataease.engine.trans.Quota2SQLObj;
import io.dataease.engine.utils.Utils;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import io.dataease.extensions.view.util.ChartDataUtil;
import io.dataease.extensions.view.util.FieldUtil;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@Component
public class TableHeatmapHandler extends DefaultChartHandler {
@Getter
private String type = "t-heatmap";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
var xAxis = new ArrayList<ChartViewFieldDTO>(view.getXAxis());
xAxis.addAll(view.getXAxisExt());
var yAxis = new ArrayList<ChartViewFieldDTO>(view.getYAxis());
yAxis.addAll(view.getExtColor());
result.getAxisMap().put(ChartAxis.xAxis, xAxis);
result.getAxisMap().put(ChartAxis.yAxis, yAxis);
return result;
}
public Map<String, Object> buildResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, List<String[]> data) {
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
Map<String, Object> result = ChartDataBuild.transChartData( xAxis, new ArrayList<>(), view, data, false);
return result;
}
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
boolean needOrder = Utils.isNeedOrder(dsList);
boolean crossDs = Utils.isCrossDs(dsMap);
DatasourceRequest datasourceRequest = new DatasourceRequest();
datasourceRequest.setDsList(dsMap);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var allFields = (List<ChartViewFieldDTO>) filterResult.getContext().get("allFields");
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, FieldUtil.transFields(allFields), crossDs, dsMap, Utils.getParams(FieldUtil.transFields(allFields)), view.getCalParams(), pluginManage);
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, FieldUtil.transFields(allFields), crossDs, dsMap, Utils.getParams(FieldUtil.transFields(allFields)), view.getCalParams(), pluginManage);
String querySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
querySql = provider.rebuildSQL(querySql, sqlMeta, crossDs, dsMap);
datasourceRequest.setQuery(querySql);
logger.debug("calcite chart sql: " + querySql);
List<String[]> data = (List<String[]>) provider.fetchResultField(datasourceRequest).get("data");
//自定义排序
data = ChartDataUtil.resultCustomSort(xAxis, yAxis, view.getSortPriority(), data);
//数据重组逻辑可重载
var result = this.buildResult(view, formatResult, filterResult, data);
T calcResult = (T) new ChartCalcDataResult();
calcResult.setData(result);
calcResult.setContext(filterResult.getContext());
calcResult.setQuerySql(querySql);
calcResult.setOriginData(data);
return calcResult;
}
}

View File

@ -0,0 +1,165 @@
package io.dataease.chart.charts.impl.table;
import io.dataease.api.chart.dto.PageInfo;
import io.dataease.chart.charts.impl.DefaultChartHandler;
import io.dataease.engine.sql.SQLProvider;
import io.dataease.engine.trans.Dimension2SQLObj;
import io.dataease.engine.utils.Utils;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import io.dataease.extensions.view.util.ChartDataUtil;
import io.dataease.extensions.view.util.FieldUtil;
import io.dataease.utils.BeanUtils;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class TableInfoHandler extends DefaultChartHandler {
@Getter
private String type = "table-info";
@Override
public AxisFormatResult formatAxis(ChartViewDTO view) {
var result = super.formatAxis(view);
result.getAxisMap().put(ChartAxis.yAxis, new ArrayList<>());
return result;
}
@Override
public <T extends CustomFilterResult> T customFilter(ChartViewDTO view, List<ChartExtFilterDTO> filterList, AxisFormatResult formatResult) {
var chartExtRequest = view.getChartExtRequest();
Map<String, Object> mapAttr = view.getCustomAttr();
Map<String, Object> mapSize = (Map<String, Object>) mapAttr.get("basicStyle");
var tablePageMode = (String) mapSize.get("tablePageMode");
formatResult.getContext().put("tablePageMode", tablePageMode);
if (StringUtils.equalsIgnoreCase(tablePageMode, "page")) {
if (chartExtRequest.getGoPage() == null) {
chartExtRequest.setGoPage(1L);
}
if (chartExtRequest.getPageSize() == null) {
int pageSize = (int) mapSize.get("tablePageSize");
if (StringUtils.equalsIgnoreCase(view.getResultMode(), "custom")) {
chartExtRequest.setPageSize(Math.min(pageSize, view.getResultCount().longValue()));
} else {
chartExtRequest.setPageSize((long) pageSize);
}
}
} else {
if (StringUtils.equalsIgnoreCase(view.getResultMode(), "custom")) {
chartExtRequest.setGoPage(1L);
chartExtRequest.setPageSize(view.getResultCount().longValue());
} else if (!view.getIsExcelExport()) {
chartExtRequest.setGoPage(null);
chartExtRequest.setPageSize(null);
}
}
return (T) new CustomFilterResult(filterList, formatResult.getContext());
}
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
var chartExtRequest = view.getChartExtRequest();
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
boolean crossDs = Utils.isCrossDs(dsMap);
DatasourceRequest datasourceRequest = new DatasourceRequest();
datasourceRequest.setDsList(dsMap);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var allFields = (List<ChartViewFieldDTO>) filterResult.getContext().get("allFields");
PageInfo pageInfo = new PageInfo();
pageInfo.setGoPage(chartExtRequest.getGoPage());
if (StringUtils.equalsIgnoreCase(view.getResultMode(), "custom")) {
pageInfo.setPageSize(Math.min(view.getResultCount() - (chartExtRequest.getGoPage() - 1) * chartExtRequest.getPageSize(), chartExtRequest.getPageSize()));
} else {
pageInfo.setPageSize(chartExtRequest.getPageSize());
}
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, FieldUtil.transFields(allFields), crossDs, dsMap, Utils.getParams(FieldUtil.transFields(allFields)), view.getCalParams(), pluginManage);
if (view.getIsExcelExport()) {
for (int i = 0; i < xAxis.size(); i++) {
ChartViewFieldDTO fieldDTO = null;
for (ChartViewFieldDTO allField : allFields) {
if (allField.getId().equals(xAxis.get(i).getId())) {
fieldDTO = allField;
}
}
if (fieldDTO != null && fieldDTO.isAgg()) {
sqlMeta.getXFields().get(i).setFieldName("'-'");
}
}
}
String originSql = SQLProvider.createQuerySQL(sqlMeta, false, !StringUtils.equalsIgnoreCase(dsMap.entrySet().iterator().next().getValue().getType(), "es"), view);// 明细表强制加排序
String limit = ((pageInfo.getGoPage() != null && pageInfo.getPageSize() != null) ? " LIMIT " + pageInfo.getPageSize() + " OFFSET " + (pageInfo.getGoPage() - 1) * chartExtRequest.getPageSize() : "");
var querySql = originSql + limit;
var tablePageMode = (String) filterResult.getContext().get("tablePageMode");
var totalPageSql = "SELECT COUNT(*) FROM (" + SQLProvider.createQuerySQLNoSort(sqlMeta, false, view) + ") COUNT_TEMP";
if (StringUtils.isNotEmpty(totalPageSql) && StringUtils.equalsIgnoreCase(tablePageMode, "page")) {
totalPageSql = provider.rebuildSQL(totalPageSql, sqlMeta, crossDs, dsMap);
datasourceRequest.setQuery(totalPageSql);
datasourceRequest.setTotalPageFlag(true);
logger.debug("calcite total sql: " + totalPageSql);
List<String[]> tmpData = (List<String[]>) provider.fetchResultField(datasourceRequest).get("data");
var totalItems = ObjectUtils.isEmpty(tmpData) ? 0 : Long.valueOf(tmpData.get(0)[0]);
if (StringUtils.equalsIgnoreCase(view.getResultMode(), "custom")) {
totalItems = totalItems <= view.getResultCount() ? totalItems : view.getResultCount();
}
var totalPage = (totalItems / pageInfo.getPageSize()) + (totalItems % pageInfo.getPageSize() > 0 ? 1 : 0);
view.setTotalItems(totalItems);
view.setTotalPage(totalPage);
}
querySql = provider.rebuildSQL(querySql, sqlMeta, crossDs, dsMap);
datasourceRequest.setQuery(querySql);
logger.debug("calcite chart sql: " + querySql);
List<String[]> data = (List<String[]>) provider.fetchResultField(datasourceRequest).get("data");
//自定义排序
data = ChartDataUtil.resultCustomSort(xAxis, Collections.emptyList(), view.getSortPriority(), data);
//数据重组逻辑可重载
var result = this.buildResult(view, formatResult, filterResult, data);
T calcResult = (T) new ChartCalcDataResult();
calcResult.setData(result);
calcResult.setContext(filterResult.getContext());
calcResult.setQuerySql(querySql);
calcResult.setOriginData(data);
try {
var dynamicAssistFields = getDynamicThresholdFields(view);
Set<Long> fieldIds = xAxis.stream().map(ChartViewFieldDTO::getId).collect(Collectors.toSet());
List<ChartViewFieldDTO> finalXAxis = xAxis;
dynamicAssistFields.forEach(i -> {
if (!fieldIds.contains(i.getFieldId())) {
ChartViewFieldDTO fieldDTO = new ChartViewFieldDTO();
BeanUtils.copyBean(fieldDTO, i.getCurField());
finalXAxis.add(fieldDTO);
}
});
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var assistFields = getAssistFields(dynamicAssistFields, yAxis, xAxis);
if (CollectionUtils.isNotEmpty(assistFields)) {
var req = new DatasourceRequest();
req.setDsList(dsMap);
var assistSql = assistSQL(querySql, assistFields, dsMap);
req.setQuery(assistSql);
logger.debug("calcite assistSql sql: " + assistSql);
var assistData = (List<String[]>) provider.fetchResultField(req).get("data");
calcResult.setAssistData(assistData);
calcResult.setDynamicAssistFields(dynamicAssistFields);
}
} catch (Exception e) {
e.printStackTrace();
}
return calcResult;
}
}

View File

@ -0,0 +1,49 @@
package io.dataease.chart.charts.impl.table;
import io.dataease.chart.charts.impl.YoyChartHandler;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
* @author jianneng
* @date 2024/9/11 11:37
**/
@Component
public class TableNormalHandler extends YoyChartHandler {
@Getter
private String type = "table-normal";
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
var result = (T) super.calcChartResult(view, formatResult, filterResult, sqlMap, sqlMeta, provider);
try {
var originSql = result.getQuerySql();
var dynamicAssistFields = getDynamicThresholdFields(view);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var assistFields = getAssistFields(dynamicAssistFields, yAxis);
if (CollectionUtils.isNotEmpty(assistFields)) {
var req = new DatasourceRequest();
req.setDsList(dsMap);
var assistSql = assistSQL(originSql, assistFields, dsMap);
req.setQuery(assistSql);
logger.debug("calcite assistSql sql: " + assistSql);
var assistData = (List<String[]>) provider.fetchResultField(req).get("data");
result.setAssistData(assistData);
result.setDynamicAssistFields(dynamicAssistFields);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}

View File

@ -0,0 +1,346 @@
package io.dataease.chart.charts.impl.table;
import io.dataease.chart.charts.impl.GroupChartHandler;
import io.dataease.constant.DeTypeConstants;
import io.dataease.engine.constant.ExtFieldConstant;
import io.dataease.engine.sql.SQLProvider;
import io.dataease.engine.trans.Dimension2SQLObj;
import io.dataease.engine.trans.Quota2SQLObj;
import io.dataease.engine.utils.Utils;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import io.dataease.extensions.view.util.FieldUtil;
import io.dataease.utils.BeanUtils;
import io.dataease.utils.IDUtils;
import io.dataease.utils.JsonUtil;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class TablePivotHandler extends GroupChartHandler {
@Getter
private String type = "table-pivot";
@Override
public <T extends ChartCalcDataResult> T calcChartResult(ChartViewDTO view, AxisFormatResult formatResult, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
T result = super.calcChartResult(view, formatResult, filterResult, sqlMap, sqlMeta, provider);
Map<String, Object> customCalc = calcCustomExpr(view, filterResult, sqlMap, sqlMeta, provider);
result.getData().put("customCalc", customCalc);
try {
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
var originSql = result.getQuerySql();
var dynamicAssistFields = getDynamicThresholdFields(view);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
var assistFields = getAssistFields(dynamicAssistFields, yAxis);
if (CollectionUtils.isNotEmpty(assistFields)) {
var req = new DatasourceRequest();
req.setDsList(dsMap);
var assistSql = assistSQL(originSql, assistFields, dsMap);
req.setQuery(assistSql);
logger.debug("calcite assistSql sql: " + assistSql);
var assistData = (List<String[]>) provider.fetchResultField(req).get("data");
result.setAssistData(assistData);
result.setDynamicAssistFields(dynamicAssistFields);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private Map<String, Object> calcCustomExpr(ChartViewDTO view, CustomFilterResult filterResult, Map<String, Object> sqlMap, SQLMeta sqlMeta, Provider provider) {
Object totalStr = JsonUtil.toJSONString(view.getCustomAttr().get("tableTotal"));
TableTotal tableTotal = JsonUtil.parseObject((String) totalStr, TableTotal.class);
var dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
boolean needOrder = Utils.isNeedOrder(dsList);
boolean crossDs = Utils.isCrossDs(dsMap);
DatasourceRequest datasourceRequest = new DatasourceRequest();
datasourceRequest.setDsList(dsMap);
var allFields = (List<ChartViewFieldDTO>) filterResult.getContext().get("allFields");
var rowAxis = view.getXAxis();
var colAxis = view.getXAxisExt();
var dataMap = new HashMap<String, Object>();
if (CollectionUtils.isEmpty(rowAxis)) {
return dataMap;
}
// 行总计列维度聚合加上自定义字段
var row = tableTotal.getRow();
if (row.isShowGrandTotals()) {
var yAxis = getCustomFields(view, row.getCalcTotals().getCfg());
if (!yAxis.isEmpty()) {
var tmpList = new ArrayList<>(allFields);
tmpList.addAll(yAxis);
var result = getData(sqlMeta, colAxis, yAxis, tmpList, crossDs, dsMap, view, provider, needOrder);
var querySql = result.getT1();
var data = result.getT2();
var tmp = new HashMap<String, Object>();
dataMap.put("rowTotal", tmp);
tmp.put("data", buildCustomCalcResult(data, colAxis, yAxis));
tmp.put("sql", Base64.getEncoder().encodeToString(querySql.getBytes()));
}
}
// 行小计列维度聚合自定义指标数 * (行维度的数量 - 1)
if (row.isShowSubTotals()) {
var yAxis = getCustomFields(view, row.getCalcSubTotals().getCfg());
if (!yAxis.isEmpty()) {
var tmpData = new ArrayList<Map<String, Object>>();
dataMap.put("rowSubTotal", tmpData);
for (int i = 0; i < rowAxis.size(); i++) {
if (i == rowAxis.size() - 1) {
break;
}
var xAxis = new ArrayList<>(colAxis);
var subRowAxis = rowAxis.subList(0, i + 1);
xAxis.addAll(subRowAxis);
if (!yAxis.isEmpty()) {
var tmpList = new ArrayList<>(allFields);
tmpList.addAll(yAxis);
var result = getData(sqlMeta, xAxis, yAxis, tmpList, crossDs, dsMap, view, provider, needOrder);
var querySql = result.getT1();
var data = result.getT2();
var tmp = new HashMap<String, Object>();
tmp.put("data", buildCustomCalcResult(data, xAxis, yAxis));
tmp.put("sql", Base64.getEncoder().encodeToString(querySql.getBytes()));
tmpData.add(tmp);
}
}
}
}
// 列总计行维度聚合加上自定义字段
var col = tableTotal.getCol();
if (col.isShowGrandTotals() && CollectionUtils.isNotEmpty(colAxis)) {
var yAxis = getCustomFields(view, col.getCalcTotals().getCfg());
if (!yAxis.isEmpty()) {
var tmpList = new ArrayList<>(allFields);
tmpList.addAll(yAxis);
var result = getData(sqlMeta, rowAxis, yAxis, tmpList, crossDs, dsMap, view, provider, needOrder);
var querySql = result.getT1();
var data = result.getT2();
var tmp = new HashMap<String, Object>();
dataMap.put("colTotal", tmp);
tmp.put("data", buildCustomCalcResult(data, rowAxis, yAxis));
tmp.put("sql", Base64.getEncoder().encodeToString(querySql.getBytes()));
}
}
// 列小计行维度聚合自定义指标数 * (列维度的数量 - 1)
if (col.isShowSubTotals() && colAxis.size() >= 2) {
var yAxis = getCustomFields(view, col.getCalcSubTotals().getCfg());
if (!yAxis.isEmpty()) {
var tmpData = new ArrayList<Map<String, Object>>();
dataMap.put("colSubTotal", tmpData);
for (int i = 0; i < colAxis.size(); i++) {
if (i == colAxis.size() - 1) {
break;
}
var xAxis = new ArrayList<>(rowAxis);
var subColAxis = colAxis.subList(0, i + 1);
xAxis.addAll(subColAxis);
if (!yAxis.isEmpty()) {
var tmpList = new ArrayList<>(allFields);
tmpList.addAll(yAxis);
var result = getData(sqlMeta, xAxis, yAxis, tmpList, crossDs, dsMap, view, provider, needOrder);
var querySql = result.getT1();
var data = result.getT2();
var tmp = new HashMap<String, Object>();
tmp.put("data", buildCustomCalcResult(data, xAxis, yAxis));
tmp.put("sql", Base64.getEncoder().encodeToString(querySql.getBytes()));
tmpData.add(tmp);
}
}
}
}
// 行列交叉部分总计无聚合直接算用列总计公式
if (row.isShowGrandTotals() && col.isShowGrandTotals()) {
var yAxis = getCustomFields(view, col.getCalcTotals().getCfg());
if (!yAxis.isEmpty()) {
// 清掉聚合轴
var tmpList = new ArrayList<>(allFields);
tmpList.addAll(yAxis);
var result = getData(sqlMeta, Collections.emptyList(), yAxis, tmpList, crossDs, dsMap, view, provider, needOrder);
var querySql = result.getT1();
var data = result.getT2();
var tmp = new HashMap<String, Object>();
dataMap.put("rowColTotal", tmp);
var tmpData = new HashMap<String, String>();
for (int i = 0; i < yAxis.size(); i++) {
var a = yAxis.get(i);
tmpData.put(a.getDataeaseName(), data.getFirst()[i]);
}
tmp.put("data", tmpData);
tmp.put("sql", Base64.getEncoder().encodeToString(querySql.getBytes()));
}
}
// 行总计里面的列小计
if (row.isShowGrandTotals() && col.isShowSubTotals() && colAxis.size() >= 2) {
var yAxis = getCustomFields(view, col.getCalcTotals().getCfg());
if (!yAxis.isEmpty()) {
var tmpData = new ArrayList<Map<String, Object>>();
dataMap.put("colSubInRowTotal", tmpData);
for (int i = 0; i < colAxis.size(); i++) {
if (i == colAxis.size() - 1) {
break;
}
var tmpList = new ArrayList<>(allFields);
tmpList.addAll(yAxis);
var xAxis = colAxis.subList(0, i + 1);
var result = getData(sqlMeta, xAxis, yAxis, tmpList, crossDs, dsMap, view, provider, needOrder);
var querySql = result.getT1();
var data = result.getT2();
var tmp = new HashMap<String, Object>();
tmp.put("data", buildCustomCalcResult(data, xAxis, yAxis));
tmp.put("sql", Base64.getEncoder().encodeToString(querySql.getBytes()));
tmpData.add(tmp);
}
}
}
// 列总计里面的行小计
if (col.isShowGrandTotals() && row.isShowGrandTotals() && rowAxis.size() >= 2) {
var yAxis = getCustomFields(view, row.getCalcTotals().getCfg());
if (!yAxis.isEmpty()) {
var tmpData = new ArrayList<Map<String, Object>>();
dataMap.put("rowSubInColTotal", tmpData);
for (int i = 0; i < rowAxis.size(); i++) {
if (i == rowAxis.size() - 1) {
break;
}
var tmpList = new ArrayList<>(allFields);
tmpList.addAll(yAxis);
var xAxis = rowAxis.subList(0, i + 1);
var result = getData(sqlMeta, xAxis, yAxis, tmpList, crossDs, dsMap, view, provider, needOrder);
var querySql = result.getT1();
var data = result.getT2();
var tmp = new HashMap<String, Object>();
tmp.put("data", buildCustomCalcResult(data, xAxis, yAxis));
tmp.put("sql", Base64.getEncoder().encodeToString(querySql.getBytes()));
tmpData.add(tmp);
}
}
}
// 行小计和列小计相交部分
if (row.isShowSubTotals() && col.isShowSubTotals() && colAxis.size() >= 2 && rowAxis.size() >= 2) {
var yAxis = getCustomFields(view, col.getCalcTotals().getCfg());
if (!yAxis.isEmpty()) {
var tmpData = new ArrayList<List<Map<String, Object>>>();
dataMap.put("rowSubInColSub", tmpData);
for (int i = 0; i < rowAxis.size(); i++) {
if (i == rowAxis.size() - 1) {
break;
}
var tmpList = new ArrayList<Map<String, Object>>();
tmpData.add(tmpList);
var subRow = rowAxis.subList(0, i + 1);
var xAxis = new ArrayList<>(subRow);
for (int j = 0; j < colAxis.size(); j++) {
if (j == colAxis.size() - 1) {
break;
}
var subCol = colAxis.subList(0, j + 1);
xAxis.addAll(subCol);
var tmpAllList = new ArrayList<>(allFields);
tmpAllList.addAll(yAxis);
var result = getData(sqlMeta, xAxis, yAxis, tmpAllList, crossDs, dsMap, view, provider, needOrder);
var querySql = result.getT1();
var data = result.getT2();
var tmp = new HashMap<String, Object>();
tmp.put("data", buildCustomCalcResult(data, xAxis, yAxis));
tmp.put("sql", Base64.getEncoder().encodeToString(querySql.getBytes()));
tmpList.add(tmp);
}
}
}
}
return dataMap;
}
private Map<String, Object> buildCustomCalcResult(List<String[]> data, List<ChartViewFieldDTO> dimAxis, List<ChartViewFieldDTO> quotaAxis) {
var rootResult = new HashMap<String, Object>();
if (CollectionUtils.isEmpty(dimAxis)) {
var rowData = data.getFirst();
for (int i = 0; i < rowData.length; i++) {
var qAxis = quotaAxis.get(i);
rootResult.put(qAxis.getDataeaseName(), rowData[i]);
}
return rootResult;
}
for (int i = 0; i < data.size(); i++) {
var rowData = data.get(i);
Map<String, Object> curSubMap = rootResult;
for (int j = 0; j < dimAxis.size(); j++) {
var tmpMap = curSubMap.get(rowData[j]);
if (tmpMap == null) {
tmpMap = new HashMap<String, Object>();
curSubMap.put(rowData[j], tmpMap);
curSubMap = (Map<String, Object>) tmpMap;
} else {
curSubMap = (Map<String, Object>) tmpMap;
}
if (j == dimAxis.size() - 1) {
for (int k = 0; k < quotaAxis.size(); k++) {
var qAxis = quotaAxis.get(k);
curSubMap.put(qAxis.getDataeaseName(), rowData[j + k + 1]);
}
}
}
}
return rootResult;
}
private Tuple2<String, List<String[]>> getData(SQLMeta sqlMeta, List<ChartViewFieldDTO> xAxis, List<ChartViewFieldDTO> yAxis,
List<ChartViewFieldDTO> allFields, boolean crossDs, Map<Long, DatasourceSchemaDTO> dsMap,
ChartViewDTO view, Provider provider, boolean needOrder) {
DatasourceRequest datasourceRequest = new DatasourceRequest();
datasourceRequest.setDsList(dsMap);
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, FieldUtil.transFields(allFields), crossDs, dsMap, Utils.getParams(FieldUtil.transFields(allFields)), view.getCalParams(), pluginManage);
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, FieldUtil.transFields(allFields), crossDs, dsMap, Utils.getParams(FieldUtil.transFields(allFields)), view.getCalParams(), pluginManage);
String querySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
querySql = provider.rebuildSQL(querySql, sqlMeta, crossDs, dsMap);
datasourceRequest.setQuery(querySql);
logger.debug("calcite chart sql: " + querySql);
List<String[]> data = (List<String[]>) provider.fetchResultField(datasourceRequest).get("data");
nullToBlank(data);
return Tuples.of(querySql, data);
}
private void nullToBlank(List<String[]> data) {
data.forEach(r -> {
for (int i = 0; i < r.length; i++) {
if (r[i] == null) {
r[i] = "";
}
}
});
}
private List<ChartViewFieldDTO> getCustomFields(ChartViewDTO view, List<TableCalcTotalCfg> cfgList) {
var quotaIds = view.getYAxis().stream().map(ChartViewFieldDTO::getDataeaseName).collect(Collectors.toSet());
var customFields = new ArrayList<ChartViewFieldDTO>();
for (TableCalcTotalCfg totalCfg : cfgList) {
if (!quotaIds.contains(totalCfg.getDataeaseName())) {
continue;
}
if (StringUtils.equalsIgnoreCase(totalCfg.getAggregation(), "CUSTOM")) {
var field = new ChartViewFieldDTO();
field.setDeType(DeTypeConstants.DE_FLOAT);
BeanUtils.copyBean(field, totalCfg);
field.setId(IDUtils.snowID());
field.setExtField(ExtFieldConstant.EXT_CALC);
customFields.add(field);
}
}
return customFields;
}
}

View File

@ -0,0 +1,23 @@
package io.dataease.chart.constant;
/**
* @Author gin
*/
public class ChartConstants {
public static final String YEAR_MOM = "year_mom";
public static final String MONTH_MOM = "month_mom";
public static final String YEAR_YOY = "year_yoy";
public static final String DAY_MOM = "day_mom";
public static final String MONTH_YOY = "month_yoy";
public static final String[] M_Y = {YEAR_MOM, MONTH_MOM, YEAR_YOY, DAY_MOM, MONTH_YOY};
//图表数据查询模式
public static final class VIEW_RESULT_MODE {
// 所有
public static final String ALL = "all";
// 自定义
public static final String CUSTOM = "custom";
}
}

View File

@ -0,0 +1,640 @@
package io.dataease.chart.dao.auto.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
/**
* <p>
* 组件图表表
* </p>
*
* @author fit2cloud
* @since 2024-12-12
*/
@TableName("core_chart_view")
public class CoreChartView implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
* 标题
*/
private String title;
/**
* 场景ID chart_type为private的时候 是仪表板id
*/
private Long sceneId;
/**
* 数据集表ID
*/
private Long tableId;
/**
* 图表类型
*/
private String type;
/**
* 图表渲染方式
*/
private String render;
/**
* 展示结果
*/
private Integer resultCount;
/**
* 展示模式
*/
private String resultMode;
/**
* 横轴field
*/
private String xAxis;
/**
* table-row
*/
private String xAxisExt;
/**
* 纵轴field
*/
private String yAxis;
/**
* 副轴
*/
private String yAxisExt;
/**
* 堆叠项
*/
private String extStack;
/**
* 气泡大小
*/
private String extBubble;
/**
* 动态标签
*/
private String extLabel;
/**
* 动态提示
*/
private String extTooltip;
/**
* 图形属性
*/
private String customAttr;
/**
* 组件样式
*/
private String customStyle;
/**
* 结果过滤
*/
private String customFilter;
/**
* 钻取字段
*/
private String drillFields;
/**
* 高级
*/
private String senior;
/**
* 创建人ID
*/
private String createBy;
/**
* 创建时间
*/
private Long createTime;
/**
* 更新时间
*/
private Long updateTime;
/**
* 缩略图
*/
private String snapshot;
/**
* 样式优先级 panel 仪表板 view 图表
*/
private String stylePriority;
/**
* 图表类型 public 公共 历史可复用的图表private 私有 专属某个仪表板
*/
private String chartType;
/**
* 是否插件
*/
private Boolean isPlugin;
/**
* 数据来源 template 模板数据 dataset 数据集数据
*/
private String dataFrom;
/**
* 图表字段集合
*/
private String viewFields;
/**
* 是否开启刷新
*/
private Boolean refreshViewEnable;
/**
* 刷新时间单位
*/
private String refreshUnit;
/**
* 刷新时间
*/
private Integer refreshTime;
/**
* 是否开启联动
*/
private Boolean linkageActive;
/**
* 是否开启跳转
*/
private Boolean jumpActive;
/**
* 复制来源
*/
private Long copyFrom;
/**
* 复制ID
*/
private Long copyId;
/**
* 区间条形图开启时间纬度开启聚合
*/
private Boolean aggregate;
/**
* 流向地图起点名称field
*/
private String flowMapStartName;
/**
* 流向地图终点名称field
*/
private String flowMapEndName;
/**
* 颜色维度field
*/
private String extColor;
/**
* 图形属性_移动端
*/
private String customAttrMobile;
/**
* 组件样式_移动端
*/
private String customStyleMobile;
/**
* 字段排序优先级
*/
private String sortPriority;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Long getSceneId() {
return sceneId;
}
public void setSceneId(Long sceneId) {
this.sceneId = sceneId;
}
public Long getTableId() {
return tableId;
}
public void setTableId(Long tableId) {
this.tableId = tableId;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getRender() {
return render;
}
public void setRender(String render) {
this.render = render;
}
public Integer getResultCount() {
return resultCount;
}
public void setResultCount(Integer resultCount) {
this.resultCount = resultCount;
}
public String getResultMode() {
return resultMode;
}
public void setResultMode(String resultMode) {
this.resultMode = resultMode;
}
public String getxAxis() {
return xAxis;
}
public void setxAxis(String xAxis) {
this.xAxis = xAxis;
}
public String getxAxisExt() {
return xAxisExt;
}
public void setxAxisExt(String xAxisExt) {
this.xAxisExt = xAxisExt;
}
public String getyAxis() {
return yAxis;
}
public void setyAxis(String yAxis) {
this.yAxis = yAxis;
}
public String getyAxisExt() {
return yAxisExt;
}
public void setyAxisExt(String yAxisExt) {
this.yAxisExt = yAxisExt;
}
public String getExtStack() {
return extStack;
}
public void setExtStack(String extStack) {
this.extStack = extStack;
}
public String getExtBubble() {
return extBubble;
}
public void setExtBubble(String extBubble) {
this.extBubble = extBubble;
}
public String getExtLabel() {
return extLabel;
}
public void setExtLabel(String extLabel) {
this.extLabel = extLabel;
}
public String getExtTooltip() {
return extTooltip;
}
public void setExtTooltip(String extTooltip) {
this.extTooltip = extTooltip;
}
public String getCustomAttr() {
return customAttr;
}
public void setCustomAttr(String customAttr) {
this.customAttr = customAttr;
}
public String getCustomStyle() {
return customStyle;
}
public void setCustomStyle(String customStyle) {
this.customStyle = customStyle;
}
public String getCustomFilter() {
return customFilter;
}
public void setCustomFilter(String customFilter) {
this.customFilter = customFilter;
}
public String getDrillFields() {
return drillFields;
}
public void setDrillFields(String drillFields) {
this.drillFields = drillFields;
}
public String getSenior() {
return senior;
}
public void setSenior(String senior) {
this.senior = senior;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Long getCreateTime() {
return createTime;
}
public void setCreateTime(Long createTime) {
this.createTime = createTime;
}
public Long getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Long updateTime) {
this.updateTime = updateTime;
}
public String getSnapshot() {
return snapshot;
}
public void setSnapshot(String snapshot) {
this.snapshot = snapshot;
}
public String getStylePriority() {
return stylePriority;
}
public void setStylePriority(String stylePriority) {
this.stylePriority = stylePriority;
}
public String getChartType() {
return chartType;
}
public void setChartType(String chartType) {
this.chartType = chartType;
}
public Boolean getIsPlugin() {
return isPlugin;
}
public void setIsPlugin(Boolean isPlugin) {
this.isPlugin = isPlugin;
}
public String getDataFrom() {
return dataFrom;
}
public void setDataFrom(String dataFrom) {
this.dataFrom = dataFrom;
}
public String getViewFields() {
return viewFields;
}
public void setViewFields(String viewFields) {
this.viewFields = viewFields;
}
public Boolean getRefreshViewEnable() {
return refreshViewEnable;
}
public void setRefreshViewEnable(Boolean refreshViewEnable) {
this.refreshViewEnable = refreshViewEnable;
}
public String getRefreshUnit() {
return refreshUnit;
}
public void setRefreshUnit(String refreshUnit) {
this.refreshUnit = refreshUnit;
}
public Integer getRefreshTime() {
return refreshTime;
}
public void setRefreshTime(Integer refreshTime) {
this.refreshTime = refreshTime;
}
public Boolean getLinkageActive() {
return linkageActive;
}
public void setLinkageActive(Boolean linkageActive) {
this.linkageActive = linkageActive;
}
public Boolean getJumpActive() {
return jumpActive;
}
public void setJumpActive(Boolean jumpActive) {
this.jumpActive = jumpActive;
}
public Long getCopyFrom() {
return copyFrom;
}
public void setCopyFrom(Long copyFrom) {
this.copyFrom = copyFrom;
}
public Long getCopyId() {
return copyId;
}
public void setCopyId(Long copyId) {
this.copyId = copyId;
}
public Boolean getAggregate() {
return aggregate;
}
public void setAggregate(Boolean aggregate) {
this.aggregate = aggregate;
}
public String getFlowMapStartName() {
return flowMapStartName;
}
public void setFlowMapStartName(String flowMapStartName) {
this.flowMapStartName = flowMapStartName;
}
public String getFlowMapEndName() {
return flowMapEndName;
}
public void setFlowMapEndName(String flowMapEndName) {
this.flowMapEndName = flowMapEndName;
}
public String getExtColor() {
return extColor;
}
public void setExtColor(String extColor) {
this.extColor = extColor;
}
public String getCustomAttrMobile() {
return customAttrMobile;
}
public void setCustomAttrMobile(String customAttrMobile) {
this.customAttrMobile = customAttrMobile;
}
public String getCustomStyleMobile() {
return customStyleMobile;
}
public void setCustomStyleMobile(String customStyleMobile) {
this.customStyleMobile = customStyleMobile;
}
public String getSortPriority() {
return sortPriority;
}
public void setSortPriority(String sortPriority) {
this.sortPriority = sortPriority;
}
@Override
public String toString() {
return "CoreChartView{" +
"id = " + id +
", title = " + title +
", sceneId = " + sceneId +
", tableId = " + tableId +
", type = " + type +
", render = " + render +
", resultCount = " + resultCount +
", resultMode = " + resultMode +
", xAxis = " + xAxis +
", xAxisExt = " + xAxisExt +
", yAxis = " + yAxis +
", yAxisExt = " + yAxisExt +
", extStack = " + extStack +
", extBubble = " + extBubble +
", extLabel = " + extLabel +
", extTooltip = " + extTooltip +
", customAttr = " + customAttr +
", customStyle = " + customStyle +
", customFilter = " + customFilter +
", drillFields = " + drillFields +
", senior = " + senior +
", createBy = " + createBy +
", createTime = " + createTime +
", updateTime = " + updateTime +
", snapshot = " + snapshot +
", stylePriority = " + stylePriority +
", chartType = " + chartType +
", isPlugin = " + isPlugin +
", dataFrom = " + dataFrom +
", viewFields = " + viewFields +
", refreshViewEnable = " + refreshViewEnable +
", refreshUnit = " + refreshUnit +
", refreshTime = " + refreshTime +
", linkageActive = " + linkageActive +
", jumpActive = " + jumpActive +
", copyFrom = " + copyFrom +
", copyId = " + copyId +
", aggregate = " + aggregate +
", flowMapStartName = " + flowMapStartName +
", flowMapEndName = " + flowMapEndName +
", extColor = " + extColor +
", customAttrMobile = " + customAttrMobile +
", customStyleMobile = " + customStyleMobile +
", sortPriority = " + sortPriority +
"}";
}
}

View File

@ -0,0 +1,18 @@
package io.dataease.chart.dao.auto.mapper;
import io.dataease.chart.dao.auto.entity.CoreChartView;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* 组件图表表 Mapper 接口
* </p>
*
* @author fit2cloud
* @since 2024-12-12
*/
@Mapper
public interface CoreChartViewMapper extends BaseMapper<CoreChartView> {
}

View File

@ -0,0 +1,53 @@
package io.dataease.chart.dao.ext.entity;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
@Data
public class ChartBasePO implements Serializable {
@Serial
private static final long serialVersionUID = 183064537525500481L;
private Long chartId;
private String chartType;
private String chartName;
private Long resourceId;
private String resourceType;
private String resourceName;
private Long tableId;
private String xAxis;
private String xAxisExt;
private String yAxis;
private String yAxisExt;
private String extStack;
private String extBubble;
private String flowMapStartName;
private String flowMapEndName;
private String extColor;
private String extLabel;
private String extTooltip;
}

View File

@ -0,0 +1,44 @@
package io.dataease.chart.dao.ext.mapper;
import io.dataease.api.chart.vo.ViewSelectorVO;
import io.dataease.chart.dao.ext.entity.ChartBasePO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface ExtChartViewMapper {
@Select("""
select id, scene_id as pid, title, type from core_chart_view where type != 'VQuery' and scene_id = #{resourceId}
""")
List<ViewSelectorVO> queryViewOption(@Param("resourceId") Long resourceId);
@Select("""
select
ccv.id as chart_id,
ccv.title as chart_name,
ccv.type as chart_type,
ccv.table_id,
dvi.id as resource_id,
dvi.name as resource_name,
dvi.type as resource_type,
ccv.x_axis,
ccv.x_axis_ext,
ccv.y_axis,
ccv.y_axis_ext,
ccv.ext_stack,
ccv.ext_bubble,
ccv.ext_label,
ccv.ext_tooltip,
ccv.flow_map_start_name,
ccv.flow_map_end_name,
ccv.ext_color
from core_chart_view ccv
left join data_visualization_info dvi on dvi.id = ccv.scene_id
where ccv.id = #{id}
""")
ChartBasePO queryChart(@Param("id") Long id);
}

View File

@ -0,0 +1,830 @@
package io.dataease.chart.manage;
import io.dataease.api.dataset.union.DatasetGroupInfoDTO;
import io.dataease.api.permissions.auth.dto.BusiPerCheckDTO;
import io.dataease.api.permissions.dataset.dto.DataSetRowPermissionsTreeDTO;
import io.dataease.chart.charts.ChartHandlerManager;
import io.dataease.chart.constant.ChartConstants;
import io.dataease.constant.AuthEnum;
import io.dataease.dataset.manage.DatasetGroupManage;
import io.dataease.dataset.manage.DatasetSQLManage;
import io.dataease.dataset.manage.DatasetTableFieldManage;
import io.dataease.dataset.manage.PermissionManage;
import io.dataease.engine.sql.SQLProvider;
import io.dataease.engine.trans.*;
import io.dataease.engine.utils.SQLUtils;
import io.dataease.engine.utils.Utils;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.api.PluginManageApi;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.factory.ProviderFactory;
import io.dataease.extensions.datasource.model.SQLMeta;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.*;
import io.dataease.extensions.view.factory.PluginsChartFactory;
import io.dataease.extensions.view.filter.FilterTreeObj;
import io.dataease.extensions.view.plugin.AbstractChartPlugin;
import io.dataease.extensions.view.util.FieldUtil;
import io.dataease.i18n.Translator;
import io.dataease.result.ResultCode;
import io.dataease.system.manage.CorePermissionManage;
import io.dataease.utils.AuthUtils;
import io.dataease.utils.BeanUtils;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Author Junjun
*/
@Component
public class ChartDataManage {
@Resource
private DatasetTableFieldManage datasetTableFieldManage;
@Resource
private DatasetGroupManage datasetGroupManage;
@Resource
private DatasetSQLManage datasetSQLManage;
@Resource
private ChartViewManege chartViewManege;
@Resource
private PermissionManage permissionManage;
@Resource
private ChartFilterTreeService chartFilterTreeService;
@Resource
private ChartHandlerManager chartHandlerManager;
@Resource
private CorePermissionManage corePermissionManage;
@Autowired(required = false)
private PluginManageApi pluginManage;
public static final String START_END_SEPARATOR = "_START_END_SPLIT";
private static final Logger logger = LoggerFactory.getLogger(ChartDataManage.class);
public ChartViewDTO calcData(ChartViewDTO view) throws Exception {
ChartExtRequest chartExtRequest = view.getChartExtRequest();
if (chartExtRequest == null) {
chartExtRequest = new ChartExtRequest();
}
if (ObjectUtils.isEmpty(view)) {
DEException.throwException(ResultCode.DATA_IS_WRONG.code(), Translator.get("i18n_chart_delete"));
}
if (ObjectUtils.isNotEmpty(AuthUtils.getUser())) {
chartExtRequest.setUser(AuthUtils.getUser().getUserId());
}
if (view.getChartExtRequest() == null) {
view.setChartExtRequest(chartExtRequest);
}
//excel导出如果是从仪表板获取图表数据则仪表板的查询模式查询结果的数量覆盖图表对应的属性
if (view.getIsExcelExport()) {
view.setResultMode(ChartConstants.VIEW_RESULT_MODE.CUSTOM);
} else if (ChartConstants.VIEW_RESULT_MODE.CUSTOM.equals(chartExtRequest.getResultMode())) {
view.setResultMode(chartExtRequest.getResultMode());
view.setResultCount(chartExtRequest.getResultCount());
}
AbstractChartPlugin chartHandler;
if (BooleanUtils.isTrue(view.getIsPlugin())) {
chartHandler = PluginsChartFactory.getInstance(view.getRender(), view.getType());
} else {
chartHandler = chartHandlerManager.getChartHandler(view.getRender(), view.getType());
}
if (chartHandler == null) {
DEException.throwException(ResultCode.DATA_IS_WRONG.code(), Translator.get("i18n_chart_not_handler") + ": " + view.getRender() + "," + view.getType());
}
var dillAxis = new ArrayList<ChartViewFieldDTO>();
DatasetGroupInfoDTO table = datasetGroupManage.getDatasetGroupInfoDTO(view.getTableId(), null);
if (table == null) {
DEException.throwException(ResultCode.DATA_IS_WRONG.code(), Translator.get("i18n_no_ds"));
}
// check permission
BusiPerCheckDTO dto = new BusiPerCheckDTO();
dto.setId(table.getId());
dto.setAuthEnum(AuthEnum.READ);
boolean checked = corePermissionManage.checkAuth(dto);
if (!checked) {
DEException.throwException(Translator.get("i18n_no_dataset_permission"));
}
List<ChartViewFieldDTO> allFields = getAllChartFields(view);
// column permission
Map<String, ColumnPermissionItem> desensitizationList = new HashMap<>();
List<DatasetTableFieldDTO> columnPermissionFields = permissionManage.filterColumnPermissions(transFields(allFields), desensitizationList, table.getId(), chartExtRequest.getUser());
// row permission
List<DataSetRowPermissionsTreeDTO> rowPermissionsTree = permissionManage.getRowPermissionsTree(table.getId(), chartExtRequest.getUser());
//将没有权限的列删掉
List<String> dataeaseNames = columnPermissionFields.stream().map(DatasetTableFieldDTO::getDataeaseName).collect(Collectors.toList());
//计数字段
dataeaseNames.add("*");
AxisFormatResult formatResult = chartHandler.formatAxis(view);
formatResult.getContext().put("desensitizationList", desensitizationList);
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
formatResult.getContext().put("allFields", allFields);
var axisMap = formatResult.getAxisMap();
axisMap.forEach((axis, fields) -> {
fields.removeIf(fieldDTO -> !dataeaseNames.contains(fieldDTO.getDataeaseName()));
});
// 过滤来自仪表板的条件
List<ChartExtFilterDTO> extFilterList = new ArrayList<>();
//组件过滤条件
List<SqlVariableDetails> sqlVariables = datasetGroupManage.getSqlParams(Collections.singletonList(view.getTableId()));
if (ObjectUtils.isNotEmpty(chartExtRequest.getFilter())) {
for (ChartExtFilterDTO request : chartExtRequest.getFilter()) {
// 解析多个fieldId,fieldId是一个逗号分隔的字符串
String fieldId = request.getFieldId();
if (request.getIsTree() == null) {
request.setIsTree(false);
}
boolean hasParameters = false;
if (CollectionUtils.isNotEmpty(sqlVariables)) {
for (SqlVariableDetails parameter : Optional.ofNullable(request.getParameters()).orElse(new ArrayList<>())) {
String parameterId = StringUtils.endsWith(parameter.getId(), START_END_SEPARATOR) ? parameter.getId().split(START_END_SEPARATOR)[0] : parameter.getId();
if (sqlVariables.stream().map(SqlVariableDetails::getId).collect(Collectors.toList()).contains(parameterId)) {
hasParameters = true;
}
}
}
if (hasParameters) {
continue;
}
if (StringUtils.isNotEmpty(fieldId)) {
List<Long> fieldIds = Arrays.stream(fieldId.split(",")).map(Long::valueOf).collect(Collectors.toList());
if (request.getIsTree()) {
ChartExtFilterDTO filterRequest = new ChartExtFilterDTO();
BeanUtils.copyBean(filterRequest, request);
filterRequest.setFilterType(0);
filterRequest.setDatasetTableFieldList(new ArrayList<>());
for (Long fId : fieldIds) {
DatasetTableFieldDTO datasetTableField = datasetTableFieldManage.selectById(fId);
if (datasetTableField == null) {
continue;
}
if (Objects.equals(datasetTableField.getDatasetGroupId(), view.getTableId())) {
if (ObjectUtils.isNotEmpty(filterRequest.getViewIds())) {
if (filterRequest.getViewIds().contains(view.getId())) {
filterRequest.getDatasetTableFieldList().add(datasetTableField);
}
} else {
filterRequest.getDatasetTableFieldList().add(datasetTableField);
}
}
}
if (ObjectUtils.isNotEmpty(filterRequest.getDatasetTableFieldList())) {
extFilterList.add(filterRequest);
}
} else {
for (Long fId : fieldIds) {
ChartExtFilterDTO filterRequest = new ChartExtFilterDTO();
BeanUtils.copyBean(filterRequest, request);
filterRequest.setFilterType(0);
filterRequest.setFieldId(fId + "");
DatasetTableFieldDTO datasetTableField = datasetTableFieldManage.selectById(fId);
if (datasetTableField == null) {
continue;
}
filterRequest.setDatasetTableField(datasetTableField);
if (Objects.equals(datasetTableField.getDatasetGroupId(), view.getTableId())) {
if (ObjectUtils.isNotEmpty(filterRequest.getViewIds())) {
if (filterRequest.getViewIds().contains(view.getId())) {
extFilterList.add(filterRequest);
}
} else {
extFilterList.add(filterRequest);
}
}
}
}
}
}
}
List<ChartExtFilterDTO> filters = new ArrayList<>();
// 联动条件
if (ObjectUtils.isNotEmpty(chartExtRequest.getLinkageFilters())) {
filters.addAll(chartExtRequest.getLinkageFilters());
}
// 外部参数条件
if (ObjectUtils.isNotEmpty(chartExtRequest.getOuterParamsFilters())) {
filters.addAll(chartExtRequest.getOuterParamsFilters());
}
// web参数条件
if (ObjectUtils.isNotEmpty(chartExtRequest.getWebParamsFilters())) {
filters.addAll(chartExtRequest.getWebParamsFilters());
}
//联动过滤条件和外部参数过滤条件全部加上
if (ObjectUtils.isNotEmpty(filters)) {
for (ChartExtFilterDTO request : filters) {
// 包含 DE 的为数据集参数
if (request.getFieldId().contains("DE")) {
// 组装sql 参数原始数据
if (CollectionUtils.isNotEmpty(sqlVariables)) {
for (SqlVariableDetails sourceVariables : sqlVariables) {
if (sourceVariables.getId().equals(request.getFieldId())) {
if (CollectionUtils.isEmpty(request.getParameters())) {
request.setParameters(new ArrayList<>());
}
request.getParameters().add(sourceVariables);
}
}
}
} else {
DatasetTableFieldDTO datasetTableField = datasetTableFieldManage.selectById(Long.valueOf(request.getFieldId()));
request.setDatasetTableField(datasetTableField);
request.setFilterType(2);
// 相同数据集
if (Objects.equals(datasetTableField.getDatasetGroupId(), view.getTableId())) {
if (ObjectUtils.isNotEmpty(request.getViewIds())) {
if (request.getViewIds().contains(view.getId())) {
extFilterList.add(request);
}
} else {
extFilterList.add(request);
}
}
}
}
}
// 下钻
List<ChartDrillRequest> drillRequestList = chartExtRequest.getDrill();
var drill = formatResult.getAxisMap().get(ChartAxis.drill);
if (ObjectUtils.isNotEmpty(drillRequestList) && (drill.size() > drillRequestList.size())) {
var fields = xAxis.stream().map(ChartViewFieldDTO::getId).collect(Collectors.toSet());
for (int i = 0; i < drillRequestList.size(); i++) {
ChartDrillRequest request = drillRequestList.get(i);
for (ChartDimensionDTO dim : request.getDimensionList()) {
ChartViewFieldDTO viewField = drill.get(i);
// 将钻取值作为条件传递将所有钻取字段作为xAxis并加上下一个钻取字段
if (Objects.equals(dim.getId(), viewField.getId())) {
DatasetTableFieldDTO datasetTableField = datasetTableFieldManage.selectById(dim.getId());
ChartViewFieldDTO d = new ChartViewFieldDTO();
BeanUtils.copyBean(d, datasetTableField);
ChartExtFilterDTO drillFilter = new ChartExtFilterDTO();
drillFilter.setFieldId(String.valueOf(dim.getId()));
drillFilter.setDatasetTableField(datasetTableField);
drillFilter.setDatePattern(viewField.getDatePattern());
drillFilter.setDateStyle(viewField.getDateStyle());
drillFilter.setFilterType(1);
if (datasetTableField.getDeType() == 1) {
drillFilter.setOperator("between");
drillFilter.setOriginValue(Collections.singletonList(dim.getValue()));
// 把value类似过滤组件处理获得start time和end time
Map<String, Long> stringLongMap = Utils.parseDateTimeValue(dim.getValue());
drillFilter.setValue(Arrays.asList(String.valueOf(stringLongMap.get("startTime")), String.valueOf(stringLongMap.get("endTime"))));
} else {
drillFilter.setOperator("in");
drillFilter.setValue(Collections.singletonList(dim.getValue()));
}
extFilterList.add(drillFilter);
if (!fields.contains(dim.getId())) {
viewField.setSource(FieldSource.DRILL);
xAxis.add(viewField);
dillAxis.add(viewField);
fields.add(dim.getId());
}
if (i == drillRequestList.size() - 1) {
ChartViewFieldDTO nextDrillField = drill.get(i + 1);
if (!fields.contains(nextDrillField.getId())) {
nextDrillField.setSource(FieldSource.DRILL);
xAxis.add(nextDrillField);
dillAxis.add(nextDrillField);
fields.add(nextDrillField.getId());
} else {
Optional<ChartViewFieldDTO> axis = xAxis.stream().filter(x -> Objects.equals(x.getId(), nextDrillField.getId())).findFirst();
axis.ifPresent(field -> {
field.setSort(nextDrillField.getSort());
field.setCustomSort(nextDrillField.getCustomSort());
});
dillAxis.add(nextDrillField);
}
}
}
}
}
}
formatResult.getContext().put("dillAxis", dillAxis);
//转义特殊字符
extFilterList = extFilterList.stream().peek(ele -> {
if (ObjectUtils.isNotEmpty(ele.getValue())) {
List<String> collect = ele.getValue().stream().map(SQLUtils::transKeyword).collect(Collectors.toList());
if (CollectionUtils.isEmpty(ele.getOriginValue())) {
ele.setOriginValue(ele.getValue());
}
ele.setValue(collect);
}
}).collect(Collectors.toList());
// 视图自定义过滤逻辑
CustomFilterResult filterResult = chartHandler.customFilter(view, extFilterList, formatResult);
if (ObjectUtils.isEmpty(xAxis) && ObjectUtils.isEmpty(yAxis)) {
return emptyChartViewDTO(view);
}
// 字段过滤器
FilterTreeObj fieldCustomFilter = view.getCustomFilter();
chartFilterTreeService.searchFieldAndSet(fieldCustomFilter);
fieldCustomFilter = chartFilterTreeService.charReplace(fieldCustomFilter);
// 获取dsMap,union sql
Map<String, Object> sqlMap = datasetSQLManage.getUnionSQLForEdit(table, chartExtRequest);
String sql = (String) sqlMap.get("sql");
Map<Long, DatasourceSchemaDTO> dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
boolean crossDs = Utils.isCrossDs(dsMap);
if (!crossDs) {
sql = Utils.replaceSchemaAlias(sql, dsMap);
}
if (ObjectUtils.isEmpty(dsMap)) {
DEException.throwException(ResultCode.DATA_IS_WRONG.code(), Translator.get("i18n_datasource_delete"));
}
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
DatasourceSchemaDTO ds = next.getValue();
if (StringUtils.isNotEmpty(ds.getStatus()) && "Error".equalsIgnoreCase(ds.getStatus())) {
DEException.throwException(ResultCode.DATA_IS_WRONG.code(), Translator.get("i18n_invalid_ds"));
}
}
Provider provider;
if (crossDs) {
provider = ProviderFactory.getDefaultProvider();
} else {
provider = ProviderFactory.getProvider(dsMap.entrySet().iterator().next().getValue().getType());
}
if (ObjectUtils.isEmpty(view.getCalParams())) {
view.setCalParams(Utils.getParams(transFields(allFields)));
}
SQLMeta sqlMeta = new SQLMeta();
Table2SQLObj.table2sqlobj(sqlMeta, null, "(" + sql + ")", crossDs);
CustomWhere2Str.customWhere2sqlObj(sqlMeta, fieldCustomFilter, transFields(allFields), crossDs, dsMap, Utils.getParams(transFields(allFields)), view.getCalParams(), pluginManage);
ExtWhere2Str.extWhere2sqlOjb(sqlMeta, extFilterList, transFields(allFields), crossDs, dsMap, Utils.getParams(transFields(allFields)), view.getCalParams(), pluginManage);
WhereTree2Str.transFilterTrees(sqlMeta, rowPermissionsTree, transFields(allFields), crossDs, dsMap, Utils.getParams(transFields(allFields)), view.getCalParams(), pluginManage);
// TODO 數據源插件化之後放到插件裡面組裝SQL
if (BooleanUtils.isTrue(view.getIsPlugin())) {
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
boolean needOrder = Utils.isNeedOrder(dsList);
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, FieldUtil.transFields(allFields), crossDs, dsMap, Utils.getParams(FieldUtil.transFields(allFields)), view.getCalParams(), pluginManage);
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, FieldUtil.transFields(allFields), crossDs, dsMap, Utils.getParams(FieldUtil.transFields(allFields)), view.getCalParams(), pluginManage);
String querySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
querySql = provider.rebuildSQL(querySql, sqlMeta, crossDs, dsMap);
filterResult.getContext().put("querySql", querySql);
}
ChartCalcDataResult calcResult = chartHandler.calcChartResult(view, formatResult, filterResult, sqlMap, sqlMeta, provider);
return chartHandler.buildChart(view, calcResult, formatResult, filterResult);
}
private List<ChartViewFieldDTO> getSizeField(ChartViewDTO view) throws Exception {
List<ChartViewFieldDTO> list = new ArrayList<>();
Map<String, Object> customAttr = view.getCustomAttr();
Map<String, Object> size = (Map<String, Object>) customAttr.get("misc");
ChartViewFieldDTO gaugeMinViewField = getDynamicField(size, "gaugeMinType", "gaugeMinField");
if (gaugeMinViewField != null) {
list.add(gaugeMinViewField);
}
ChartViewFieldDTO gaugeMaxViewField = getDynamicField(size, "gaugeMaxType", "gaugeMaxField");
if (gaugeMaxViewField != null) {
list.add(gaugeMaxViewField);
}
ChartViewFieldDTO liquidMaxViewField = getDynamicField(size, "liquidMaxType", "liquidMaxField");
if (liquidMaxViewField != null) {
list.add(liquidMaxViewField);
}
return list;
}
private ChartViewFieldDTO getDynamicField(Map<String, Object> sizeObj, String type, String field) {
String maxType = (String) sizeObj.get(type);
if (StringUtils.equalsIgnoreCase("dynamic", maxType)) {
Map<String, Object> maxField = (Map<String, Object>) sizeObj.get(field);
Long id = Long.valueOf((String) maxField.get("id"));
String summary = (String) maxField.get("summary");
DatasetTableFieldDTO datasetTableField = datasetTableFieldManage.selectById(id);
if (ObjectUtils.isNotEmpty(datasetTableField)) {
if (datasetTableField.getDeType() == 0 || datasetTableField.getDeType() == 1 || datasetTableField.getDeType() == 5) {
if (!StringUtils.containsIgnoreCase(summary, "count")) {
DEException.throwException(Translator.get("i18n_gauge_field_change"));
}
}
ChartViewFieldDTO dto = new ChartViewFieldDTO();
BeanUtils.copyBean(dto, datasetTableField);
dto.setSummary(summary);
return dto;
} else {
DEException.throwException(Translator.get("i18n_gauge_field_delete"));
}
}
return null;
}
private ChartViewDTO emptyChartViewDTO(ChartViewDTO view) {
ChartViewDTO dto = new ChartViewDTO();
BeanUtils.copyBean(dto, view);
return dto;
}
private String getDrillSort(List<ChartViewFieldDTO> xAxis, ChartViewFieldDTO field) {
String res = "";
for (ChartViewFieldDTO f : xAxis) {
if (Objects.equals(f.getId(), field.getId())) {
if (StringUtils.equalsIgnoreCase(f.getSort(), "asc") || StringUtils.equalsIgnoreCase(f.getSort(), "desc")) {
res = f.getSort();
break;
}
}
}
return res;
}
private List<DatasetTableFieldDTO> transFields(List<? extends ChartViewFieldBaseDTO> list) {
return list.stream().map(ele -> {
DatasetTableFieldDTO dto = new DatasetTableFieldDTO();
BeanUtils.copyBean(dto, ele);
return dto;
}).collect(Collectors.toList());
}
// 对结果排序
public List<String[]> resultCustomSort(List<ChartViewFieldDTO> xAxis, List<String[]> data) {
List<String[]> res = new ArrayList<>(data);
if (xAxis.size() > 0) {
// 找到对应维度
for (int i = 0; i < xAxis.size(); i++) {
ChartViewFieldDTO item = xAxis.get(i);
if (StringUtils.equalsIgnoreCase(item.getSort(), "custom_sort")) {
// 获取自定义值与data对应列的结果
if (i > 0) {
// 首先根据优先级高的字段分类在每个前置字段相同的组里排序
Map<String, List<String[]>> map = new LinkedHashMap<>();
for (String[] d : res) {
StringBuilder stringBuilder = new StringBuilder();
for (int j = 0; j < i; j++) {
if (StringUtils.equalsIgnoreCase(xAxis.get(j).getSort(), "none")) {
continue;
}
stringBuilder.append(d[j]);
}
if (ObjectUtils.isEmpty(map.get(stringBuilder.toString()))) {
map.put(stringBuilder.toString(), new ArrayList<>());
}
map.get(stringBuilder.toString()).add(d);
}
Iterator<Map.Entry<String, List<String[]>>> iterator = map.entrySet().iterator();
List<String[]> list = new ArrayList<>();
while (iterator.hasNext()) {
Map.Entry<String, List<String[]>> next = iterator.next();
list.addAll(customSort(Optional.ofNullable(item.getCustomSort()).orElse(new ArrayList<>()), next.getValue(), i));
}
res.clear();
res.addAll(list);
} else {
res = customSort(Optional.ofNullable(item.getCustomSort()).orElse(new ArrayList<>()), res, i);
}
}
}
}
return res;
}
public List<String[]> customSort(List<String> custom, List<String[]> data, int index) {
List<String[]> res = new ArrayList<>();
List<Integer> indexArr = new ArrayList<>();
List<String[]> joinArr = new ArrayList<>();
for (int i = 0; i < custom.size(); i++) {
String ele = custom.get(i);
for (int j = 0; j < data.size(); j++) {
String[] d = data.get(j);
if (StringUtils.equalsIgnoreCase(ele, d[index])) {
joinArr.add(d);
indexArr.add(j);
}
}
}
// 取得 joinArr 就是两者的交集
List<Integer> indexArrData = new ArrayList<>();
for (int i = 0; i < data.size(); i++) {
indexArrData.add(i);
}
List<Integer> indexResult = new ArrayList<>();
for (int i = 0; i < indexArrData.size(); i++) {
if (!indexArr.contains(indexArrData.get(i))) {
indexResult.add(indexArrData.get(i));
}
}
List<String[]> subArr = new ArrayList<>();
for (int i = 0; i < indexResult.size(); i++) {
subArr.add(data.get(indexResult.get(i)));
}
res.addAll(joinArr);
res.addAll(subArr);
return res;
}
public List<String> getFieldData(ChartViewDTO view, Long fieldId, String fieldType) throws Exception {
ChartExtRequest requestList = view.getChartExtRequest();
List<String[]> sqlData = sqlData(view, requestList, fieldId);
List<ChartViewFieldDTO> fieldList = new ArrayList<>();
switch (fieldType) {
case "xAxis" -> fieldList = view.getXAxis();
case "xAxisExt" -> fieldList = view.getXAxisExt();
case "extStack" -> fieldList = view.getExtStack();
}
DatasetTableFieldDTO field = datasetTableFieldManage.selectById(fieldId);
List<String> res = new ArrayList<>();
if (ObjectUtils.isNotEmpty(field) && fieldList.size() > 0) {
// 找到对应维度
ChartViewFieldDTO chartViewFieldDTO = null;
int index = 0;
int getIndex = 0;
for (int i = 0; i < fieldList.size(); i++) {
ChartViewFieldDTO item = fieldList.get(i);
if (StringUtils.equalsIgnoreCase(item.getSort(), "custom_sort")) {// 此处与已有的自定义字段对比
chartViewFieldDTO = item;
index = i;
}
if (Objects.equals(item.getId(), field.getId())) {// 获得当前自定义的字段
getIndex = i;
}
}
if (StringUtils.equalsIgnoreCase(fieldType, "xAxisExt")) {
List<ChartViewFieldDTO> xAxis = view.getXAxis();
index += xAxis.size();
getIndex += xAxis.size();
}
if (StringUtils.equalsIgnoreCase(fieldType, "extStack")) {
int xAxisSize = CollectionUtils.size(view.getXAxis());
int extSize = CollectionUtils.size(view.getXAxisExt());
index += xAxisSize + extSize;
getIndex += xAxisSize + extSize;
}
List<String[]> sortResult = resultCustomSort(fieldList, sqlData);
if (ObjectUtils.isNotEmpty(chartViewFieldDTO) && (getIndex >= index)) {
// 获取自定义值与data对应列的结果
List<String[]> strings = customSort(Optional.ofNullable(chartViewFieldDTO.getCustomSort()).orElse(new ArrayList<>()), sortResult, index);
for (int i = 0; i < strings.size(); i++) {
res.add(strings.get(i)[getIndex]);
}
} else {
// 返回请求结果
for (int i = 0; i < sortResult.size(); i++) {
res.add(sortResult.get(i)[getIndex]);
}
}
}
return res.stream().distinct().collect(Collectors.toList());
}
public List<String[]> sqlData(ChartViewDTO view, ChartExtRequest requestList, Long fieldId) throws Exception {
if (ObjectUtils.isEmpty(view)) {
DEException.throwException(Translator.get("i18n_chart_delete"));
}
// get all fields
List<ChartViewFieldDTO> allFields = getAllChartFields(view);
// 针对分组切换堆叠时会遇到的问题
if (StringUtils.equalsIgnoreCase(view.getType(), "bar-stack") || StringUtils.equalsIgnoreCase(view.getType(), "chart-mix-stack")) {
view.setXAxisExt(new ArrayList<>());
}
List<ChartViewFieldDTO> xAxisBase = new ArrayList<>(view.getXAxis());
List<ChartViewFieldDTO> xAxis = new ArrayList<>(view.getXAxis());
List<ChartViewFieldDTO> xAxisExt = new ArrayList<>(view.getXAxisExt());
if (StringUtils.equalsIgnoreCase(view.getType(), "table-pivot")
|| StringUtils.containsIgnoreCase(view.getType(), "group")
|| ("antv".equalsIgnoreCase(view.getRender()) && "line".equalsIgnoreCase(view.getType()))
|| StringUtils.equalsIgnoreCase(view.getType(), "flow-map")
|| StringUtils.equalsIgnoreCase(view.getType(), "t-heatmap")
|| StringUtils.equalsIgnoreCase(view.getType(), "sankey")
) {
xAxis.addAll(xAxisExt);
}
List<ChartViewFieldDTO> yAxis = new ArrayList<>(view.getYAxis());
if (StringUtils.containsIgnoreCase(view.getType(), "chart-mix")) {
List<ChartViewFieldDTO> yAxisExt = new ArrayList<>(view.getYAxisExt());
yAxis.addAll(yAxisExt);
}
if (StringUtils.equalsIgnoreCase(view.getRender(), "antv") && StringUtils.equalsAnyIgnoreCase(view.getType(), "gauge", "liquid")) {
List<ChartViewFieldDTO> sizeField = getSizeField(view);
yAxis.addAll(sizeField);
}
List<ChartViewFieldDTO> extStack = new ArrayList<>(view.getExtStack());
List<ChartViewFieldDTO> extBubble = new ArrayList<>(view.getExtBubble());
FilterTreeObj fieldCustomFilter = view.getCustomFilter();
List<ChartViewFieldDTO> drill = new ArrayList<>(view.getDrillFields());
// 获取数据集,需校验权限
DatasetGroupInfoDTO table = datasetGroupManage.getDatasetGroupInfoDTO(view.getTableId(), null);
Map<String, ColumnPermissionItem> desensitizationList = new HashMap<>();
List<DataSetRowPermissionsTreeDTO> rowPermissionsTree = permissionManage.getRowPermissionsTree(table.getId(), view.getChartExtRequest().getUser());
chartFilterTreeService.searchFieldAndSet(fieldCustomFilter);
if (ObjectUtils.isEmpty(xAxis) && ObjectUtils.isEmpty(yAxis)) {
return new ArrayList<String[]>();
}
switch (view.getType()) {
case "label":
yAxis = new ArrayList<>();
if (ObjectUtils.isEmpty(xAxis)) {
return new ArrayList<String[]>();
}
break;
case "indicator":
case "gauge":
case "liquid":
xAxis = new ArrayList<>();
if (ObjectUtils.isEmpty(yAxis)) {
return new ArrayList<String[]>();
}
break;
case "table-info":
yAxis = new ArrayList<>();
if (ObjectUtils.isEmpty(xAxis)) {
return new ArrayList<String[]>();
}
break;
case "table-normal":
break;
case "bar-group":
case "bar-group-stack":
case "flow-map":
break;
default:
}
// 获取dsMap,union sql
Map<String, Object> sqlMap = datasetSQLManage.getUnionSQLForEdit(table, null);
String sql = (String) sqlMap.get("sql");
Map<Long, DatasourceSchemaDTO> dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
boolean needOrder = Utils.isNeedOrder(dsList);
boolean crossDs = Utils.isCrossDs(dsMap);
if (!crossDs) {
sql = Utils.replaceSchemaAlias(sql, dsMap);
}
// 调用数据源的calcite获得data
DatasourceRequest datasourceRequest = new DatasourceRequest();
datasourceRequest.setDsList(dsMap);
Provider provider;
if (crossDs) {
provider = ProviderFactory.getDefaultProvider();
} else {
provider = ProviderFactory.getProvider(dsMap.entrySet().iterator().next().getValue().getType());
}
List<String[]> data = new ArrayList<>();
String querySql = null;
//如果不是插件图表 走原生逻辑
if (table.getMode() == 0) {// 直连
if (ObjectUtils.isEmpty(dsMap)) {
DEException.throwException(Translator.get("i18n_datasource_delete"));
}
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
DatasourceSchemaDTO ds = next.getValue();
if (StringUtils.isNotEmpty(ds.getStatus()) && "Error".equalsIgnoreCase(ds.getStatus())) {
DEException.throwException(Translator.get("i18n_invalid_ds"));
}
}
SQLMeta sqlMeta = new SQLMeta();
Table2SQLObj.table2sqlobj(sqlMeta, null, "(" + sql + ")", crossDs);
WhereTree2Str.transFilterTrees(sqlMeta, rowPermissionsTree, transFields(allFields), crossDs, dsMap, Utils.getParams(transFields(allFields)), view.getCalParams(), pluginManage);
if (StringUtils.equalsAnyIgnoreCase(view.getType(), "indicator", "gauge", "liquid")) {
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, transFields(allFields), crossDs, dsMap, Utils.getParams(transFields(allFields)), view.getCalParams(), pluginManage);
querySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
} else if (StringUtils.containsIgnoreCase(view.getType(), "stack")) {
List<ChartViewFieldDTO> xFields = new ArrayList<>();
xFields.addAll(xAxis);
xFields.addAll(extStack);
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xFields, transFields(allFields), crossDs, dsMap, Utils.getParams(transFields(allFields)), view.getCalParams(), pluginManage);
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, transFields(allFields), crossDs, dsMap, Utils.getParams(transFields(allFields)), view.getCalParams(), pluginManage);
querySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
} else if (StringUtils.containsIgnoreCase(view.getType(), "scatter")) {
List<ChartViewFieldDTO> yFields = new ArrayList<>();
yFields.addAll(yAxis);
yFields.addAll(extBubble);
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, transFields(allFields), crossDs, dsMap, Utils.getParams(transFields(allFields)), view.getCalParams(), pluginManage);
Quota2SQLObj.quota2sqlObj(sqlMeta, yFields, transFields(allFields), crossDs, dsMap, Utils.getParams(transFields(allFields)), view.getCalParams(), pluginManage);
querySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
} else if (StringUtils.equalsIgnoreCase("table-info", view.getType())) {
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, transFields(allFields), crossDs, dsMap, Utils.getParams(transFields(allFields)), view.getCalParams(), pluginManage);
querySql = SQLProvider.createQuerySQL(sqlMeta, false, true, view);
} else {
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, transFields(allFields), crossDs, dsMap, Utils.getParams(transFields(allFields)), view.getCalParams(), pluginManage);
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, transFields(allFields), crossDs, dsMap, Utils.getParams(transFields(allFields)), view.getCalParams(), pluginManage);
querySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
}
querySql = provider.rebuildSQL(querySql, sqlMeta, crossDs, dsMap);
datasourceRequest.setQuery(querySql);
logger.debug("calcite chart get field enum sql: " + querySql);
data = (List<String[]>) provider.fetchResultField(datasourceRequest).get("data");
}
return data;
}
private List<ChartViewFieldDTO> getAllChartFields(ChartViewDTO view) {
// get all fields
Map<String, List<ChartViewFieldDTO>> stringListMap = chartViewManege.listByDQ(view.getTableId(), view.getId(), view);
List<ChartViewFieldDTO> dimensionList = stringListMap.get("dimensionList");
List<ChartViewFieldDTO> quotaList = stringListMap.get("quotaList");
List<ChartViewFieldDTO> allFields = new ArrayList<>();
allFields.addAll(dimensionList);
allFields.addAll(quotaList);
return allFields.stream().filter(ele -> ele.getId() != -1L).collect(Collectors.toList());
}
public void saveChartViewFromVisualization(String checkData, Long sceneId, Map<Long, ChartViewDTO> chartViewsInfo) {
if (!MapUtils.isEmpty(chartViewsInfo)) {
List<Long> disuseChartIdList = new ArrayList<>();
chartViewsInfo.forEach((key, chartViewDTO) -> {
if (checkData.contains(chartViewDTO.getId() + "")) {
try {
chartViewDTO.setSceneId(sceneId);
chartViewManege.save(chartViewDTO);
} catch (Exception e) {
DEException.throwException(e);
}
} else {
disuseChartIdList.add(chartViewDTO.getId());
}
});
if (CollectionUtils.isNotEmpty(disuseChartIdList)) {
chartViewManege.disuse(disuseChartIdList);
}
}
}
public List<String> getDrillFieldData(ChartViewDTO view, Long fieldId) throws Exception {
List<ChartViewFieldDTO> drillField = view.getDrillFields();
ChartViewFieldDTO targetField = null;
for (int i = 0; i < drillField.size(); i++) {
ChartViewFieldDTO tmp = drillField.get(i);
if (tmp.getId().equals(fieldId)) {
targetField = tmp;
break;
}
}
if (targetField == null) {
return Collections.emptyList();
}
view.setXAxis(Collections.singletonList(targetField));
List<String[]> sqlData = sqlData(view, view.getChartExtRequest(), fieldId);
List<String[]> result = customSort(Optional.ofNullable(targetField.getCustomSort()).orElse(new ArrayList<>()), sqlData, 0);
return result.stream().map(i -> i[0]).distinct().collect(Collectors.toList());
}
}

View File

@ -0,0 +1,82 @@
package io.dataease.chart.manage;
import com.fasterxml.jackson.core.type.TypeReference;
import io.dataease.dataset.dao.auto.entity.CoreDatasetTableField;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetTableFieldMapper;
import io.dataease.engine.utils.SQLUtils;
import io.dataease.extensions.datasource.dto.CalParam;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.FieldGroupDTO;
import io.dataease.extensions.view.filter.FilterTreeItem;
import io.dataease.extensions.view.filter.FilterTreeObj;
import io.dataease.utils.BeanUtils;
import io.dataease.utils.JsonUtil;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author Junjun
*/
@Service
public class ChartFilterTreeService {
@Resource
private CoreDatasetTableFieldMapper coreDatasetTableFieldMapper;
public void searchFieldAndSet(FilterTreeObj tree) {
if (ObjectUtils.isNotEmpty(tree)) {
if (ObjectUtils.isNotEmpty(tree.getItems())) {
for (FilterTreeItem item : tree.getItems()) {
if (ObjectUtils.isNotEmpty(item)) {
if (StringUtils.equalsIgnoreCase(item.getType(), "item") || ObjectUtils.isEmpty(item.getSubTree())) {
CoreDatasetTableField coreDatasetTableField = coreDatasetTableFieldMapper.selectById(item.getFieldId());
DatasetTableFieldDTO dto = new DatasetTableFieldDTO();
BeanUtils.copyBean(dto, coreDatasetTableField);
if (StringUtils.isNotEmpty(coreDatasetTableField.getParams())) {
TypeReference<List<CalParam>> tokenType = new TypeReference<>() {
};
List<CalParam> calParams = JsonUtil.parseList(coreDatasetTableField.getParams(), tokenType);
dto.setParams(calParams);
}
if (StringUtils.isNotEmpty(coreDatasetTableField.getGroupList())) {
TypeReference<List<FieldGroupDTO>> groupTokenType = new TypeReference<>() {
};
List<FieldGroupDTO> fieldGroups = JsonUtil.parseList(coreDatasetTableField.getGroupList(), groupTokenType);
dto.setGroupList(fieldGroups);
}
item.setField(dto);
} else if (StringUtils.equalsIgnoreCase(item.getType(), "tree") || (ObjectUtils.isNotEmpty(item.getSubTree()) && StringUtils.isNotEmpty(item.getSubTree().getLogic()))) {
searchFieldAndSet(item.getSubTree());
}
}
}
}
}
}
public FilterTreeObj charReplace(FilterTreeObj tree) {
if (ObjectUtils.isNotEmpty(tree)) {
if (ObjectUtils.isNotEmpty(tree.getItems())) {
for (FilterTreeItem item : tree.getItems()) {
if (ObjectUtils.isNotEmpty(item)) {
if (StringUtils.equalsIgnoreCase(item.getType(), "item") || ObjectUtils.isEmpty(item.getSubTree())) {
if (CollectionUtils.isNotEmpty(item.getEnumValue())) {
List<String> collect = item.getEnumValue().stream().map(SQLUtils::transKeyword).collect(Collectors.toList());
item.setEnumValue(collect);
}
item.setValue(SQLUtils.transKeyword(item.getValue()));
} else if (StringUtils.equalsIgnoreCase(item.getType(), "tree") || (ObjectUtils.isNotEmpty(item.getSubTree()) && StringUtils.isNotEmpty(item.getSubTree().getLogic()))) {
charReplace(item.getSubTree());
}
}
}
}
}
return tree;
}
}

View File

@ -0,0 +1,468 @@
package io.dataease.chart.manage;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.dataease.api.chart.vo.ChartBaseVO;
import io.dataease.api.chart.vo.ViewSelectorVO;
import io.dataease.chart.dao.auto.entity.CoreChartView;
import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper;
import io.dataease.chart.dao.ext.entity.ChartBasePO;
import io.dataease.chart.dao.ext.mapper.ExtChartViewMapper;
import io.dataease.dataset.dao.auto.entity.CoreDatasetTableField;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetTableFieldMapper;
import io.dataease.dataset.manage.DatasetTableFieldManage;
import io.dataease.dataset.manage.PermissionManage;
import io.dataease.dataset.utils.TableUtils;
import io.dataease.engine.constant.ExtFieldConstant;
import io.dataease.engine.func.FunctionConstant;
import io.dataease.engine.utils.Utils;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.api.PluginManageApi;
import io.dataease.extensions.datasource.dto.CalParam;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.FieldGroupDTO;
import io.dataease.extensions.datasource.model.SQLObj;
import io.dataease.extensions.view.dto.*;
import io.dataease.extensions.view.filter.FilterTreeObj;
import io.dataease.i18n.Translator;
import io.dataease.license.config.XpackInteract;
import io.dataease.utils.BeanUtils;
import io.dataease.utils.IDUtils;
import io.dataease.utils.JsonUtil;
import io.dataease.utils.LogUtil;
import io.dataease.visualization.dao.auto.entity.DataVisualizationInfo;
import io.dataease.visualization.dao.auto.mapper.DataVisualizationInfoMapper;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* @Author Junjun
*/
@Component
public class ChartViewManege {
@Resource
private CoreChartViewMapper coreChartViewMapper;
@Resource
private ChartDataManage chartDataManage;
@Resource
private CoreDatasetTableFieldMapper coreDatasetTableFieldMapper;
@Resource
private PermissionManage permissionManage;
@Resource
private DataVisualizationInfoMapper visualizationInfoMapper;
@Resource
private ExtChartViewMapper extChartViewMapper;
@Resource
private DatasetTableFieldManage datasetTableFieldManage;
@Autowired(required = false)
private PluginManageApi pluginManage;
private ObjectMapper objectMapper = new ObjectMapper();
@Transactional
public ChartViewDTO save(ChartViewDTO chartViewDTO) throws Exception {
if (chartViewDTO.getTitle().length() > 100) {
DEException.throwException(Translator.get("i18n_name_limit_100"));
}
Long id = chartViewDTO.getId();
if (id == null) {
DEException.throwException(Translator.get("i18n_no_id"));
}
CoreChartView coreChartView = coreChartViewMapper.selectById(id);
CoreChartView record = transDTO2Record(chartViewDTO);
if (ObjectUtils.isEmpty(coreChartView)) {
coreChartViewMapper.insert(record);
} else {
UpdateWrapper<CoreChartView> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", record.getId());
//富文本允许设置空的tableId 这里额外更新一下
if (record.getTableId() == null) {
updateWrapper.set("table_id", null);
}
coreChartViewMapper.update(record, updateWrapper);
}
return chartViewDTO;
}
public void delete(Long id) {
coreChartViewMapper.deleteById(id);
}
@XpackInteract(value = "chartViewManage")
public void disuse(List<Long> chartIdList) {
}
@Transactional
public void deleteBySceneId(Long sceneId, List<Long> chartIds) {
QueryWrapper<CoreChartView> wrapper = new QueryWrapper<>();
wrapper.eq("scene_id", sceneId);
wrapper.notIn("id", chartIds);
coreChartViewMapper.delete(wrapper);
}
public ChartViewDTO getDetails(Long id) {
CoreChartView coreChartView = coreChartViewMapper.selectById(id);
if (ObjectUtils.isEmpty(coreChartView)) {
return null;
}
ChartViewDTO dto = transRecord2DTO(coreChartView);
return dto;
}
/**
* sceneId 为仪表板或者数据大屏id
*/
public List<ChartViewDTO> listBySceneId(Long sceneId) {
QueryWrapper<CoreChartView> wrapper = new QueryWrapper<>();
wrapper.eq("scene_id", sceneId);
List<ChartViewDTO> chartViewDTOS = transChart(coreChartViewMapper.selectList(wrapper));
if (!CollectionUtils.isEmpty(chartViewDTOS)) {
List<Long> tableIds = chartViewDTOS.stream()
.map(ChartViewDTO::getTableId)
.filter(tableId -> tableId != null) // 过滤掉空值
.distinct()
.toList();
if (!CollectionUtils.isEmpty(tableIds)) {
QueryWrapper<CoreDatasetTableField> wp = new QueryWrapper<>();
wp.in("dataset_group_id", tableIds);
List<CoreDatasetTableField> coreDatasetTableFields = coreDatasetTableFieldMapper.selectList(wp);
Map<Long, List<CoreDatasetTableField>> groupedByTableId = coreDatasetTableFields.stream()
.collect(Collectors.groupingBy(CoreDatasetTableField::getDatasetGroupId));
if (chartViewDTOS.size() < 10) {
chartViewDTOS.forEach(dto -> {
if (dto.getTableId() != null) {
dto.setCalParams(Utils.getParams(datasetTableFieldManage.transDTO(groupedByTableId.get(dto.getTableId()))));
}
});
} else {
ExecutorService executor = Executors.newFixedThreadPool(10);
try {
// 超过10个图表要处理启用多线程处理
CountDownLatch latch = new CountDownLatch(chartViewDTOS.size());
chartViewDTOS.forEach(dto -> {
executor.submit(() -> {
try {
if (dto.getTableId() != null) {
dto.setCalParams(Utils.getParams(datasetTableFieldManage.transDTO(groupedByTableId.get(dto.getTableId()))));
}
} finally {
latch.countDown(); // 减少计数器
}
});
});
// 等待所有线程完成
boolean completedInTime = latch.await(200, TimeUnit.SECONDS);
if (!completedInTime) {
throw new InterruptedException("Tasks did not complete within 200 seconds");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LogUtil.error(e);
} finally {
executor.shutdown(); // 确保线程池关闭
}
}
}
}
return chartViewDTOS;
}
public List<ChartViewDTO> transChart(List<CoreChartView> list) {
if (ObjectUtils.isEmpty(list)) {
return Collections.emptyList();
}
return list.stream().map(ele -> {
ChartViewDTO dto = transRecord2DTO(ele);
return dto;
}).collect(Collectors.toList());
}
public ChartViewDTO getChart(Long id) throws Exception {
ChartViewDTO details = getDetails(id);
if (details == null) {
return null;
}
return chartDataManage.calcData(details);
}
public Map<String, List<ChartViewFieldDTO>> listByDQ(Long id, Long chartId, ChartViewDTO chartViewDTO) {
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("dataset_group_id", id);
wrapper.eq("checked", true);
wrapper.isNull("chart_id");
TypeReference<List<CalParam>> typeToken = new TypeReference<>() {
};
TypeReference<List<FieldGroupDTO>> groupTokenType = new TypeReference<>() {
};
List<CoreDatasetTableField> fields = coreDatasetTableFieldMapper.selectList(wrapper);
List<DatasetTableFieldDTO> collect = fields.stream().map(ele -> {
DatasetTableFieldDTO dto = new DatasetTableFieldDTO();
BeanUtils.copyBean(dto, ele);
dto.setParams(JsonUtil.parseList(ele.getParams(), typeToken));
dto.setGroupList(JsonUtil.parseList(ele.getGroupList(), groupTokenType));
return dto;
}).collect(Collectors.toList());
// filter column disable field
Map<String, ColumnPermissionItem> desensitizationList = new HashMap<>();
List<DatasetTableFieldDTO> datasetTableFieldDTOS = permissionManage.filterColumnPermissions(collect, desensitizationList, id, null);
datasetTableFieldDTOS.forEach(ele -> ele.setDesensitized(desensitizationList.containsKey(ele.getDataeaseName())));
datasetTableFieldDTOS.add(createCountField(id));
List<ChartViewFieldDTO> list = transFieldDTO(datasetTableFieldDTOS);
// 获取图表计算字段
wrapper.clear();
wrapper.eq("chart_id", chartId);
List<DatasetTableFieldDTO> chartFields = coreDatasetTableFieldMapper.selectList(wrapper).stream().map(ele -> {
DatasetTableFieldDTO dto = new DatasetTableFieldDTO();
BeanUtils.copyBean(dto, ele);
dto.setGroupList(JsonUtil.parseList(ele.getGroupList(), groupTokenType));
return dto;
}).collect(Collectors.toList());
list.addAll(transFieldDTO(chartFields));
// 获取list中的聚合函数将字段的summary设置成空
SQLObj tableObj = new SQLObj();
tableObj.setTableAlias("");
for (ChartViewFieldDTO ele : list) {
if (Objects.equals(ele.getExtField(), ExtFieldConstant.EXT_CALC)) {
List<DatasetTableFieldDTO> f = list.stream().map(e -> {
DatasetTableFieldDTO dto = new DatasetTableFieldDTO();
BeanUtils.copyBean(dto, e);
return dto;
}).collect(Collectors.toList());
String originField = Utils.calcFieldRegex(ele, tableObj, f, true, null, Utils.mergeParam(Utils.getParams(f), null), pluginManage);
for (String func : FunctionConstant.AGG_FUNC) {
if (Utils.matchFunction(func, originField)) {
ele.setSummary("");
ele.setAgg(true);
break;
}
}
}
}
List<ChartViewFieldDTO> dimensionList = list.stream().filter(ele -> StringUtils.equalsIgnoreCase(ele.getGroupType(), "d")).collect(Collectors.toList());
List<ChartViewFieldDTO> quotaList = list.stream().filter(ele -> StringUtils.equalsIgnoreCase(ele.getGroupType(), "q")).collect(Collectors.toList());
Map<String, List<ChartViewFieldDTO>> map = new LinkedHashMap<>();
map.put("dimensionList", dimensionList);
map.put("quotaList", quotaList);
return map;
}
public void copyField(Long id, Long chartId) {
CoreDatasetTableField coreDatasetTableField = coreDatasetTableFieldMapper.selectById(id);
QueryWrapper<CoreDatasetTableField> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("dataset_group_id", coreDatasetTableField.getDatasetGroupId());
List<CoreDatasetTableField> coreDatasetTableFields = coreDatasetTableFieldMapper.selectList(queryWrapper);
HashMap<String, String> map = new HashMap<>();
for (CoreDatasetTableField ele : coreDatasetTableFields) {
map.put(ele.getName(), ele.getName());
}
newName(map, coreDatasetTableField, coreDatasetTableField.getName());
coreDatasetTableField.setChartId(chartId);
coreDatasetTableField.setExtField(2);
coreDatasetTableField.setOriginName("[" + id + "]");
coreDatasetTableField.setId(IDUtils.snowID());
coreDatasetTableField.setDataeaseName(TableUtils.fieldNameShort(coreDatasetTableField.getId() + "_" + coreDatasetTableField.getOriginName()));
coreDatasetTableField.setFieldShortName(coreDatasetTableField.getDataeaseName());
coreDatasetTableFieldMapper.insert(coreDatasetTableField);
}
private void newName(HashMap<String, String> map, CoreDatasetTableField coreDatasetTableField, String name) {
name = name + "_copy";
if (map.containsKey(name)) {
newName(map, coreDatasetTableField, name);
} else {
coreDatasetTableField.setName(name);
}
}
public void deleteField(Long id) {
coreDatasetTableFieldMapper.deleteById(id);
}
public void deleteFieldByChartId(Long chartId) {
QueryWrapper<CoreDatasetTableField> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("chart_id", chartId);
coreDatasetTableFieldMapper.delete(queryWrapper);
}
public ChartBaseVO chartBaseInfo(Long id) {
ChartBasePO po = extChartViewMapper.queryChart(id);
if (ObjectUtils.isEmpty(po)) return null;
ChartBaseVO vo = BeanUtils.copyBean(new ChartBaseVO(), po);
TypeReference<List<ChartViewFieldDTO>> tokenType = new TypeReference<>() {
};
vo.setXAxis(JsonUtil.parseList(po.getXAxis(), tokenType));
vo.setXAxisExt(JsonUtil.parseList(po.getXAxisExt(), tokenType));
vo.setYAxis(JsonUtil.parseList(po.getYAxis(), tokenType));
vo.setYAxisExt(JsonUtil.parseList(po.getYAxisExt(), tokenType));
vo.setExtStack(JsonUtil.parseList(po.getExtStack(), tokenType));
vo.setExtBubble(JsonUtil.parseList(po.getExtBubble(), tokenType));
vo.setFlowMapStartName(JsonUtil.parseList(po.getFlowMapStartName(), tokenType));
vo.setFlowMapEndName(JsonUtil.parseList(po.getFlowMapEndName(), tokenType));
if (StringUtils.isBlank(po.getExtColor()) || StringUtils.equals("null", po.getExtColor())) {
vo.setExtColor(new ArrayList<>());
} else {
vo.setExtColor(JsonUtil.parseList(po.getExtColor(), tokenType));
}
vo.setExtLabel(JsonUtil.parseList(po.getExtLabel(), tokenType));
vo.setExtTooltip(JsonUtil.parseList(po.getExtTooltip(), tokenType));
return vo;
}
public DatasetTableFieldDTO createCountField(Long id) {
DatasetTableFieldDTO dto = new DatasetTableFieldDTO();
dto.setId(-1L);
dto.setDatasetGroupId(id);
dto.setOriginName("*");
dto.setName("记录数*");
dto.setDataeaseName("*");
dto.setType("INT");
dto.setChecked(true);
dto.setColumnIndex(999);
dto.setDeType(2);
dto.setExtField(1);
dto.setGroupType("q");
return dto;
}
public List<ChartViewFieldDTO> transFieldDTO(List<DatasetTableFieldDTO> list) {
return list.stream().map(ele -> {
ChartViewFieldDTO dto = new ChartViewFieldDTO();
if (ele == null) return null;
BeanUtils.copyBean(dto, ele);
dto.setDateStyle("y_M_d");
dto.setDatePattern("date_sub");
dto.setChartType("bar");
if (dto.getId() == -1L || dto.getDeType() == 0 || dto.getDeType() == 1 || dto.getDeType() == 7) {
dto.setSummary("count");
} else {
dto.setSummary("sum");
}
ChartFieldCompareDTO chartFieldCompareDTO = new ChartFieldCompareDTO();
chartFieldCompareDTO.setType("none");
dto.setCompareCalc(chartFieldCompareDTO);
dto.setFormatterCfg(new FormatterCfgDTO());
dto.setSort("none");
dto.setFilter(Collections.emptyList());
return dto;
}).collect(Collectors.toList());
}
public CoreChartView transDTO2Record(ChartViewDTO dto) throws Exception {
CoreChartView record = new CoreChartView();
BeanUtils.copyBean(record, dto);
record.setxAxis(objectMapper.writeValueAsString(dto.getXAxis()));
record.setxAxisExt(objectMapper.writeValueAsString(dto.getXAxisExt()));
record.setyAxis(objectMapper.writeValueAsString(dto.getYAxis()));
record.setyAxisExt(objectMapper.writeValueAsString(dto.getYAxisExt()));
record.setExtStack(objectMapper.writeValueAsString(dto.getExtStack()));
record.setExtBubble(objectMapper.writeValueAsString(dto.getExtBubble()));
record.setExtLabel(objectMapper.writeValueAsString(dto.getExtLabel()));
record.setExtTooltip(objectMapper.writeValueAsString(dto.getExtTooltip()));
record.setCustomAttr(objectMapper.writeValueAsString(dto.getCustomAttr()));
if (dto.getCustomAttrMobile() != null) {
record.setCustomAttrMobile(objectMapper.writeValueAsString(dto.getCustomAttrMobile()));
}
record.setCustomStyle(objectMapper.writeValueAsString(dto.getCustomStyle()));
if (dto.getCustomAttrMobile() != null) {
record.setCustomStyleMobile(objectMapper.writeValueAsString(dto.getCustomStyleMobile()));
}
record.setSenior(objectMapper.writeValueAsString(dto.getSenior()));
record.setDrillFields(objectMapper.writeValueAsString(dto.getDrillFields()));
record.setCustomFilter(objectMapper.writeValueAsString(dto.getCustomFilter()));
record.setViewFields(objectMapper.writeValueAsString(dto.getViewFields()));
record.setFlowMapStartName(objectMapper.writeValueAsString(dto.getFlowMapStartName()));
record.setFlowMapEndName(objectMapper.writeValueAsString(dto.getFlowMapEndName()));
record.setExtColor(objectMapper.writeValueAsString(dto.getExtColor()));
return record;
}
public ChartViewDTO transRecord2DTO(CoreChartView record) {
ChartViewDTO dto = new ChartViewDTO();
BeanUtils.copyBean(dto, record);
TypeReference<List<ChartViewFieldDTO>> tokenType = new TypeReference<>() {
};
dto.setXAxis(JsonUtil.parseList(record.getxAxis(), tokenType));
dto.setXAxisExt(JsonUtil.parseList(record.getxAxisExt(), tokenType));
dto.setYAxis(JsonUtil.parseList(record.getyAxis(), tokenType));
dto.setYAxisExt(JsonUtil.parseList(record.getyAxisExt(), tokenType));
dto.setExtStack(JsonUtil.parseList(record.getExtStack(), tokenType));
dto.setExtBubble(JsonUtil.parseList(record.getExtBubble(), tokenType));
dto.setExtLabel(JsonUtil.parseList(record.getExtLabel(), tokenType));
dto.setExtTooltip(JsonUtil.parseList(record.getExtTooltip(), tokenType));
dto.setCustomAttr(JsonUtil.parse(record.getCustomAttr(), Map.class));
if (record.getCustomAttrMobile() != null) {
dto.setCustomAttrMobile(JsonUtil.parse(record.getCustomAttrMobile(), Map.class));
}
dto.setCustomStyle(JsonUtil.parse(record.getCustomStyle(), Map.class));
if (record.getCustomStyleMobile() != null) {
dto.setCustomStyleMobile(JsonUtil.parse(record.getCustomStyleMobile(), Map.class));
}
dto.setSenior(JsonUtil.parse(record.getSenior(), Map.class));
dto.setDrillFields(JsonUtil.parseList(record.getDrillFields(), tokenType));
dto.setCustomFilter(JsonUtil.parseObject(record.getCustomFilter(), FilterTreeObj.class));
dto.setViewFields(JsonUtil.parseList(record.getViewFields(), tokenType));
dto.setFlowMapStartName(JsonUtil.parseList(record.getFlowMapStartName(), tokenType));
dto.setFlowMapEndName(JsonUtil.parseList(record.getFlowMapEndName(), tokenType));
dto.setExtColor(JsonUtil.parseList(record.getExtColor(), tokenType));
return dto;
}
public String checkSameDataSet(String viewIdSource, String viewIdTarget) {
QueryWrapper<CoreChartView> wrapper = new QueryWrapper<>();
wrapper.select("distinct table_id");
wrapper.in("id", Arrays.asList(viewIdSource, viewIdTarget));
coreChartViewMapper.selectCount(wrapper);
if (coreChartViewMapper.selectCount(wrapper) == 1) {
return "YES";
} else {
return "NO";
}
}
public List<ViewSelectorVO> viewOption(Long resourceId) {
List<ViewSelectorVO> result = extChartViewMapper.queryViewOption(resourceId);
DataVisualizationInfo dvInfo = visualizationInfoMapper.selectById(resourceId);
if (dvInfo != null && !CollectionUtils.isEmpty(result)) {
String componentData = dvInfo.getComponentData();
return result.stream().filter(item -> componentData.indexOf(String.valueOf(item.getId())) > 0).toList();
} else {
return result;
}
}
}

View File

@ -0,0 +1,205 @@
package io.dataease.chart.manage;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import io.dataease.chart.dao.auto.entity.CoreChartView;
import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper;
import io.dataease.extensions.view.dto.ChartCustomFilterItemDTO;
import io.dataease.extensions.view.dto.ChartFieldCustomFilterDTO;
import io.dataease.extensions.view.filter.FilterTreeItem;
import io.dataease.extensions.view.filter.FilterTreeObj;
import io.dataease.utils.JsonUtil;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @Author Junjun
*/
@Service
public class ChartViewOldDataMergeService {
@Resource
private CoreChartViewMapper coreChartViewMapper;
/**
* 视图过滤器重构合并老数据将list变成tree
*/
public void mergeOldData() {
// 获取所有视图数据
// 把一个视图中的过滤器即customFilter字段进行重构
// 之前是以list形式储存一个字段是一个item
// 现在把一个字段当做tree中的一个节点
// 节点中如果是logic且length>1保留and或or每一条都变成一个子节点如果是枚举或只有1条则当做and处理并保留值
// 最后把字段之间通过and的逻辑合并
List<CoreChartView> chartViewWithBLOBs = coreChartViewMapper.selectList(new QueryWrapper<>());
if (CollectionUtils.isEmpty(chartViewWithBLOBs)) {
return;
}
for (CoreChartView view : chartViewWithBLOBs) {
TypeReference<List<ChartFieldCustomFilterDTO>> tokenType = new TypeReference<>() {
};
List<ChartFieldCustomFilterDTO> fieldCustomFilter;
// 尝试将历史数据转成list如果转换出现异常则忽略该视图继续执行下一个
try {
fieldCustomFilter = JsonUtil.parseList(view.getCustomFilter(), tokenType);
} catch (Exception e) {
continue;
}
if (CollectionUtils.isEmpty(fieldCustomFilter)) {
// '[]' 转换成 '{}'
view.setCustomFilter("{}");
} else {
// array -> tree
FilterTreeObj tree = transArr2Obj(fieldCustomFilter);
view.setCustomFilter((String) JsonUtil.toJSONString(tree));
}
try {
coreChartViewMapper.updateById(view);
} catch (Exception e) {
// do nothing,to continue
e.printStackTrace();
}
}
}
public FilterTreeObj transArr2Obj(List<ChartFieldCustomFilterDTO> fieldCustomFilter) {
FilterTreeObj tree = new FilterTreeObj();
tree.setItems(new ArrayList<>());
if (fieldCustomFilter.size() == 1) {
ChartFieldCustomFilterDTO filterDTO = fieldCustomFilter.get(0);
tree.setLogic(filterDTO.getLogic());
if (StringUtils.equalsIgnoreCase(filterDTO.getFilterType(), "enum")) {
FilterTreeItem item = new FilterTreeItem();
item.setType("item");
item.setFieldId(filterDTO.getId());
item.setFilterType(filterDTO.getFilterType());
item.setEnumValue(filterDTO.getEnumCheckField());
tree.getItems().add(item);
} else {
List<ChartCustomFilterItemDTO> filter = filterDTO.getFilter();
if (CollectionUtils.isNotEmpty(filter)) {
for (ChartCustomFilterItemDTO f : filter) {
FilterTreeItem item = new FilterTreeItem();
item.setType("item");
item.setFieldId(filterDTO.getId());
item.setFilterType(filterDTO.getFilterType());
item.setTerm(f.getTerm());
item.setValue(f.getValue());
item.setEnumValue(new ArrayList<>());
tree.getItems().add(item);
}
}
}
} else {
tree.setLogic("and");
for (ChartFieldCustomFilterDTO dto : fieldCustomFilter) {
if (StringUtils.equalsIgnoreCase(dto.getFilterType(), "enum")) {
FilterTreeItem item = new FilterTreeItem();
item.setType("item");
item.setFieldId(dto.getId());
item.setFilterType(dto.getFilterType());
item.setEnumValue(dto.getEnumCheckField());
tree.getItems().add(item);
} else {
List<ChartCustomFilterItemDTO> filter = dto.getFilter();
if (CollectionUtils.isNotEmpty(filter)) {
if (filter.size() == 1) {
ChartCustomFilterItemDTO f = filter.get(0);
FilterTreeItem item = new FilterTreeItem();
item.setType("item");
item.setFieldId(dto.getId());
item.setFilterType(dto.getFilterType());
item.setTerm(f.getTerm());
item.setValue(f.getValue());
item.setEnumValue(new ArrayList<>());
tree.getItems().add(item);
} else {
FilterTreeItem item = new FilterTreeItem();
item.setType("tree");
item.setEnumValue(new ArrayList<>());
FilterTreeObj subTree = new FilterTreeObj();
subTree.setLogic(dto.getLogic());
subTree.setItems(new ArrayList<>());
for (ChartCustomFilterItemDTO f : filter) {
FilterTreeItem itemTree = new FilterTreeItem();
itemTree.setType("item");
itemTree.setFieldId(dto.getId());
itemTree.setFilterType(dto.getFilterType());
itemTree.setTerm(f.getTerm());
itemTree.setValue(f.getValue());
itemTree.setEnumValue(new ArrayList<>());
subTree.getItems().add(itemTree);
}
item.setSubTree(subTree);
tree.getItems().add(item);
}
}
}
}
}
return tree;
}
/**
* 视图过滤器动态时间兼容老数据
*/
public void refreshFilter() {
// 获取所有视图数据
// 在filter中增加filterTypeTime = dateValue
List<CoreChartView> chartViewWithBLOBs = coreChartViewMapper.selectList(new QueryWrapper<>());
if (CollectionUtils.isEmpty(chartViewWithBLOBs)) {
return;
}
for (CoreChartView view : chartViewWithBLOBs) {
FilterTreeObj filterTreeObj;
try {
filterTreeObj = JsonUtil.parseObject(view.getCustomFilter(), FilterTreeObj.class);
} catch (Exception e) {
continue;
}
if (ObjectUtils.isNotEmpty(filterTreeObj)) {
if (ObjectUtils.isEmpty(filterTreeObj.getItems())) {
continue;
}
FilterTreeObj tree = fixFilter(filterTreeObj);
view.setCustomFilter((String) JsonUtil.toJSONString(tree));
}
try {
coreChartViewMapper.updateById(view);
} catch (Exception e) {
// do nothing,to continue
e.printStackTrace();
}
}
}
public FilterTreeObj fixFilter(FilterTreeObj filterTreeObj) {
doFix(filterTreeObj.getItems());
return filterTreeObj;
}
public void doFix(List<FilterTreeItem> items) {
if (ObjectUtils.isEmpty(items)) {
return;
}
for (FilterTreeItem item : items) {
if (StringUtils.equalsIgnoreCase(item.getType(), "item")) {
item.setFilterTypeTime("dateValue");
} else {
doFix(item.getSubTree().getItems());
}
}
}
}

View File

@ -0,0 +1,582 @@
package io.dataease.chart.manage;
import io.dataease.api.chart.request.ThresholdCheckRequest;
import io.dataease.api.chart.vo.ThresholdCheckVO;
import io.dataease.constant.DeTypeConstants;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.view.dto.ChartViewDTO;
import io.dataease.extensions.view.dto.ChartViewFieldDTO;
import io.dataease.extensions.view.filter.FilterTreeItem;
import io.dataease.extensions.view.filter.FilterTreeObj;
import io.dataease.i18n.Translator;
import io.dataease.utils.DateUtils;
import io.dataease.utils.JsonUtil;
import io.dataease.utils.LogUtil;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Component("chartViewThresholdManage")
public class ChartViewThresholdManage {
@Resource
private ChartViewManege chartViewManege;
public String convertThresholdRules(Long chartId, String thresholdRules) {
ChartViewDTO details = chartViewManege.getDetails(chartId);
return convertThresholdRules(details, thresholdRules);
}
private String convertThresholdRules(ChartViewDTO chart, String thresholdRules) {
List<DatasetTableFieldDTO> fieldList = chartFields(chart);
FilterTreeObj filterTreeObj = JsonUtil.parseObject(thresholdRules, FilterTreeObj.class);
Map<String, DatasetTableFieldDTO> fieldMap = fieldList.stream().collect(Collectors.toMap(item -> item.getId().toString(), item -> item));
return convertTree(filterTreeObj, fieldMap);
}
private List<DatasetTableFieldDTO> chartFields(ChartViewDTO details) {
List<DatasetTableFieldDTO> result = new ArrayList<>();
List<ChartViewFieldDTO> xAxis = details.getXAxis();
if (CollectionUtils.isNotEmpty(xAxis)) {
result.addAll(xAxis);
}
List<ChartViewFieldDTO> xAxisExt = details.getXAxisExt();
if (CollectionUtils.isNotEmpty(xAxisExt)) {
result.addAll(xAxisExt);
}
List<ChartViewFieldDTO> yAxis = details.getYAxis();
if (CollectionUtils.isNotEmpty(yAxis)) {
result.addAll(yAxis);
}
List<ChartViewFieldDTO> yAxisExt = details.getYAxisExt();
if (CollectionUtils.isNotEmpty(yAxisExt)) {
result.addAll(yAxisExt);
}
List<ChartViewFieldDTO> extStack = details.getExtStack();
if (CollectionUtils.isNotEmpty(extStack)) {
result.addAll(extStack);
}
List<ChartViewFieldDTO> extBubble = details.getExtBubble();
if (CollectionUtils.isNotEmpty(extBubble)) {
result.addAll(extBubble);
}
List<ChartViewFieldDTO> extLabel = details.getExtLabel();
if (CollectionUtils.isNotEmpty(extLabel)) {
result.addAll(extLabel);
}
List<ChartViewFieldDTO> extTooltip = details.getExtTooltip();
if (CollectionUtils.isNotEmpty(extTooltip)) {
result.addAll(extTooltip);
}
List<ChartViewFieldDTO> extColor = details.getExtColor();
if (CollectionUtils.isNotEmpty(extColor)) {
result.addAll(extColor);
}
List<ChartViewFieldDTO> flowMapStartName = details.getFlowMapStartName();
if (CollectionUtils.isNotEmpty(flowMapStartName)) {
result.addAll(flowMapStartName);
}
List<ChartViewFieldDTO> flowMapEndName = details.getFlowMapEndName();
if (CollectionUtils.isNotEmpty(flowMapEndName)) {
result.addAll(flowMapEndName);
}
return result;
}
private String convertTree(FilterTreeObj filterTreeObj, Map<String, DatasetTableFieldDTO> fieldMap) {
String logic = filterTreeObj.getLogic();
String logicText = translateLogic(logic);
List<FilterTreeItem> items = filterTreeObj.getItems();
StringBuilder result = new StringBuilder();
for (FilterTreeItem item : items) {
String type = item.getType();
if (StringUtils.equals("tree", type) && ObjectUtils.isNotEmpty(item.getSubTree())) {
String childResult = convertTree(item.getSubTree(), fieldMap);
result.append(childResult);
} else {
String itemResult = convertItem(item, fieldMap);
result.append(itemResult);
}
result.append(logicText);
}
int lastIndex = -1;
if ((!result.isEmpty()) && (lastIndex = result.lastIndexOf(logicText)) > 0) {
return result.substring(0, lastIndex);
}
return null;
}
private String convertItem(FilterTreeItem item, Map<String, DatasetTableFieldDTO> fieldMap) {
String filterType = item.getFilterType();
Long fieldId = item.getFieldId();
DatasetTableFieldDTO map = fieldMap.get(fieldId.toString());
String fieldName = map.getName();
if (StringUtils.equals(filterType, "enum")) {
List<String> enumValue = item.getEnumValue();
String enumValueText = String.join(",", enumValue);
return fieldName + " " + Translator.get("i18n_threshold_logic_in") + " " + "( " + enumValueText + " )";
} else {
Integer deType = map.getDeType();
String valueType = item.getValueType();
return fieldName + " " + translateTerm(item.getTerm()) + " " + formatFieldValue(item.getValue(), valueType, deType);
}
}
private String formatFieldValue(String value, String valueType, Integer deType) {
if (StringUtils.isBlank(valueType)) {
valueType = "fixed";
}
if (StringUtils.equals("fixed", valueType)) {
return value;
}
if (StringUtils.equals("max", value)) {
return Translator.get("i18n_threshold_max");
} else if (StringUtils.equals("min", value)) {
return Translator.get("i18n_threshold_min");
} else if (StringUtils.equals("average", value)) {
return Translator.get("i18n_threshold_average");
} else if (deType == 1) {
return formatDynamicTimeLabel(value);
} else {
return value;
}
}
private String formatDynamicTimeLabel(String value) {
if (StringUtils.isBlank(value)) {
return value;
}
try {
Map map = JsonUtil.parseObject(value, Map.class);
String format = map.get("format").toString();
int timeFlag = Integer.parseInt(map.get("timeFlag").toString());
if (timeFlag == 9) {
int count = Integer.parseInt(map.get("count").toString());
int unit = Integer.parseInt(map.get("unit").toString());
int suffix = Integer.parseInt(map.get("suffix").toString());
String time = map.get("time").toString();
List<String> unitLabels = null;
if (StringUtils.equalsIgnoreCase("YYYY", format)) {
unitLabels = List.of(Translator.get("i18n_time_year"));
} else if (StringUtils.equalsIgnoreCase("YYYY-MM", format)) {
unitLabels = List.of(Translator.get("i18n_time_year"), Translator.get("i18n_time_month"));
} else if (StringUtils.equalsIgnoreCase("YYYY-MM-DD", format)) {
unitLabels = List.of(Translator.get("i18n_time_year"), Translator.get("i18n_time_month"), Translator.get("i18n_time_date"));
} else if (StringUtils.equalsIgnoreCase("HH:mm:ss", format)) {
DEException.throwException("纯时间格式不支持动态格式");
} else {
unitLabels = List.of(Translator.get("i18n_time_year"), Translator.get("i18n_time_month"), Translator.get("i18n_time_date"));
}
String unitText = unitLabels.get(unit - 1);
String suffixText = Translator.get("i18n_time_ago");
if (suffix == 2) {
suffixText = Translator.get("i18n_time_later");
}
String timeText = "";
if (StringUtils.containsIgnoreCase(format, "HH")) {
timeText = " " + time;
}
return count + " " + unitText + suffixText + timeText;
} else {
List<String> shortLabels = null;
if (StringUtils.equalsIgnoreCase("YYYY", format)) {
shortLabels = List.of(Translator.get("i18n_time_year_current"), Translator.get("i18n_time_year_last"), Translator.get("i18n_time_year_next"));
} else if (StringUtils.equalsIgnoreCase("YYYY-MM", format)) {
shortLabels = List.of(Translator.get("i18n_time_month_current"), Translator.get("i18n_time_month_last"), Translator.get("i18n_time_month_next"),
Translator.get("i18n_time_month_start"), Translator.get("i18n_time_month_end"));
} else if (StringUtils.equalsIgnoreCase("YYYY-MM-DD", format)) {
shortLabels = List.of(Translator.get("i18n_time_date_current"), Translator.get("i18n_time_date_last"), Translator.get("i18n_time_date_next"),
Translator.get("i18n_time_date_start"), Translator.get("i18n_time_date_end"));
} else if (StringUtils.equalsIgnoreCase("HH:mm:ss", format)) {
shortLabels = List.of("当前", "1小时前", "1小时后");
} else {
shortLabels = List.of(Translator.get("i18n_time_date_current"), Translator.get("i18n_time_date_last"), Translator.get("i18n_time_date_next"),
Translator.get("i18n_time_date_start"), Translator.get("i18n_time_date_end"));
}
return shortLabels.get(timeFlag - 1);
}
} catch (Exception e) {
LogUtil.error("动态时间配置错误,请重新配置!");
return value;
}
}
private String translateTerm(String term) {
if (StringUtils.equals(term, "not in")) {
return Translator.get("i18n_threshold_logic_not_in");
} else if (StringUtils.equals(term, "not like")) {
return Translator.get("i18n_threshold_logic_not_like");
} else {
return Translator.get("i18n_threshold_logic_" + term);
}
}
private String translateLogic(String logic) {
if (StringUtils.equals(logic, "and")) return String.format(" %s ", Translator.get("i18n_threshold_logic_and"));
return String.format(" %s ", Translator.get("i18n_threshold_logic_or"));
}
private String convertStyle(String htmlString) {
String regex = "<span\\s+id=\"(changeText-0|changeText-1)\"\\s+style=\"([^\"]*)\">";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(htmlString);
if (matcher.find()) {
String styleAttribute = matcher.group();
String newStyle = styleAttribute.replace("background: #3370FF33", "background: #FFFFFF")
.replace("color: #2b5fd9", "color: #000000");
return matcher.replaceAll(Matcher.quoteReplacement(newStyle));
}
return htmlString;
}
public ThresholdCheckVO checkThreshold(ThresholdCheckRequest request) throws Exception {
String thresholdTemplate = request.getThresholdTemplate();
String thresholdRules = request.getThresholdRules();
Long chartId = request.getChartId();
try {
ChartViewDTO chart = chartViewManege.getChart(chartId);
Map<String, Object> data = null;
if (ObjectUtils.isEmpty(chart) || MapUtils.isEmpty(data = chart.getData())) {
return new ThresholdCheckVO(false, null, "查询图表异常!", null);
}
thresholdTemplate = thresholdTemplate.replace("[检测时间]", DateUtils.time2String(System.currentTimeMillis()));
String s = convertThresholdRules(chart, thresholdRules);
thresholdTemplate = convertStyle(thresholdTemplate.replace("[触发告警]", s));
List<Map<String, Object>> tableRow = (List<Map<String, Object>>) data.get("tableRow");
List<DatasetTableFieldDTO> fields = (List<DatasetTableFieldDTO>) data.get("fields");
if (CollectionUtils.isEmpty(fields)) {
return new ThresholdCheckVO(false, null, String.format("当前图表类型[%s]暂不支持阈值告警!", chart.getType()), null);
}
Map<Long, DatasetTableFieldDTO> fieldMap = fields.stream().collect(Collectors.toMap(DatasetTableFieldDTO::getId, item -> item));
FilterTreeObj filterTreeObj = JsonUtil.parseObject(thresholdRules, FilterTreeObj.class);
List<Map<String, Object>> rows = filterRows(tableRow, filterTreeObj, fieldMap);
if (CollectionUtils.isEmpty(rows)) {
return new ThresholdCheckVO(false, null, null, null);
}
String regex = "<span[^>]*id=\"changeText-(-?\\d+)(?!0$)(?!1$)\"[^>]*>.*?</span>";
Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
Matcher matcher = pattern.matcher(thresholdTemplate);
StringBuilder sb = new StringBuilder();
while (matcher.find()) {
long id = Long.parseLong(matcher.group(1));
// 根据id从map中获取替换文本
DatasetTableFieldDTO fieldDTO = fieldMap.get(id);
if (ObjectUtils.isEmpty(fieldDTO)) continue;
String fieldDTOName = fieldDTO.getName();
String dataeaseName = fieldDTO.getDataeaseName();
List<String> valueList = rows.stream().map(row -> ObjectUtils.isEmpty(row.get(dataeaseName)) ? null : row.get(dataeaseName).toString()).collect(Collectors.toList());
String replacement = fieldDTOName + ": " + JsonUtil.toJSONString(valueList);
// 替换文本
matcher.appendReplacement(sb, replacement);
}
matcher.appendTail(sb);
// 输出替换后的HTML内容
String result = sb.toString();
return new ThresholdCheckVO(true, result, null, null);
} catch (Exception e) {
LogUtil.error(e.getMessage(), new Throwable(e));
return new ThresholdCheckVO(false, null, e.getMessage(), null);
}
}
private void chartDynamicMap(List<Map<String, Object>> rows, FilterTreeObj conditionTree, Map<Long, DatasetTableFieldDTO> fieldMap) {
List<FilterTreeItem> items = conditionTree.getItems();
items.forEach(item -> {
if (!StringUtils.equals("item", item.getType())) {
chartDynamicMap(rows, item.getSubTree(), fieldMap);
} else {
Long fieldId = item.getFieldId();
DatasetTableFieldDTO fieldDTO = fieldMap.get(fieldId);
if ((Objects.equals(fieldDTO.getDeType(), DeTypeConstants.DE_INT) || Objects.equals(fieldDTO.getDeType(), DeTypeConstants.DE_FLOAT)) && StringUtils.equals("dynamic", item.getValueType())) {
item.setField(fieldDTO);
item.setValue(formatValue(rows, item));
} else if (Objects.equals(fieldDTO.getDeType(), DeTypeConstants.DE_TIME) && StringUtils.equals("dynamic", item.getValueType())) {
item.setField(fieldDTO);
item.setValue(dynamicFormatValue(item));
}
}
});
}
private String dynamicFormatValue(FilterTreeItem item) {
String value = item.getValue();
if (StringUtils.isBlank(value)) {
return value;
}
try {
Map map = JsonUtil.parseObject(value, Map.class);
String format = map.get("format").toString();
int timeFlag = Integer.parseInt(map.get("timeFlag").toString());
if (timeFlag == 9) {
int count = Integer.parseInt(map.get("count").toString());
int unit = Integer.parseInt(map.get("unit").toString());
int suffix = Integer.parseInt(map.get("suffix").toString());
String time = map.get("time").toString();
String timeValue = getCustomTimeValue(format, unit, suffix, count, false);
if (StringUtils.containsIgnoreCase(format, "yyyy-MM-dd HH") && StringUtils.isNotBlank(time)) {
return timeValue + " " + time;
}
return timeValue;
} else {
LocalDateTime now = LocalDateTime.now();
String fullFormat = "yyyy-MM-dd HH:mm:ss";
int length = format.length();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(fullFormat.substring(0, length));
int count = timeFlag == 1 ? 0 : 1;
int suffix = timeFlag - 1;
if (StringUtils.equalsIgnoreCase("YYYY", format)) {
return getCustomTimeValue(format, 1, suffix, count, true);
} else if (StringUtils.equalsIgnoreCase("YYYY-MM", format)) {
if (timeFlag == 4) {
return now.withMonth(1).withDayOfMonth(1).format(formatter);
} else if (timeFlag == 5) {
return now.withMonth(12).withDayOfMonth(31).format(formatter);
} else {
return getCustomTimeValue(format, 2, suffix, count, true);
}
} else {
if (timeFlag == 4) {
return now.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).format(formatter);
} else if (timeFlag == 5) {
return now.plusMonths(1).withDayOfMonth(1).minusDays(1).withHour(0).withMinute(0).withSecond(0).format(formatter);
} else {
return getCustomTimeValue(format, 3, suffix, count, true);
}
}
}
} catch (Exception e) {
LogUtil.error("动态时间配置错误,请重新配置!" + e.getMessage());
return value;
}
}
private String getCustomTimeValue(String format, int unit, int suffix, int count, boolean hasTime) {
LocalDateTime now = LocalDateTime.now();
String fullFormat = "yyyy-MM-dd HH:mm:ss";
int len = format.length();
if (hasTime) {
now = now.withHour(0).withMinute(0).withSecond(0);
} else {
len = Math.min(len, 10);
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(fullFormat.substring(0, len));
if (count == 0) {
return now.format(formatter);
}
if (unit == 1) {
if (suffix == 1) {
return now.minusYears(count).format(formatter);
}
return now.plusYears(count).format(formatter);
} else if (unit == 2) {
if (suffix == 1) {
return now.minusMonths(count).format(formatter);
}
return now.plusMonths(count).format(formatter);
} else {
if (suffix == 1) {
return now.minusDays(count).format(formatter);
}
return now.plusDays(count).format(formatter);
}
}
private String formatValue(List<Map<String, Object>> rows, FilterTreeItem item) {
DatasetTableFieldDTO field = item.getField();
String dataeaseName = field.getDataeaseName();
String value = item.getValue();
Float tempFVal = StringUtils.equalsAny(value, "min", "max") ? null : 0f;
int validLen = 0;
for (Map<String, Object> row : rows) {
Object o = row.get(dataeaseName);
if (ObjectUtils.isEmpty(o)) continue;
float fvalue = Float.parseFloat(o.toString());
if (StringUtils.equals("min", value)) {
if (ObjectUtils.isEmpty(tempFVal)) {
tempFVal = fvalue;
} else {
tempFVal = Math.min(tempFVal, fvalue);
}
} else if (StringUtils.equals("max", value)) {
if (ObjectUtils.isEmpty(tempFVal)) {
tempFVal = fvalue;
} else {
tempFVal = Math.max(tempFVal, fvalue);
}
} else if (StringUtils.equals("average", value)) {
tempFVal += fvalue;
validLen++;
}
}
if (StringUtils.equals("average", value)) {
return validLen == 0 ? "0f" : String.valueOf((tempFVal / validLen));
}
return String.valueOf(tempFVal);
}
public List<Map<String, Object>> filterRows(List<Map<String, Object>> rows, FilterTreeObj conditionTree, Map<Long, DatasetTableFieldDTO> fieldMap) {
chartDynamicMap(rows, conditionTree, fieldMap);
List<Map<String, Object>> filteredRows = new ArrayList<>();
for (Map<String, Object> row : rows) {
if (matchesConditionTree(row, conditionTree, fieldMap)) {
filteredRows.add(row);
}
}
return filteredRows;
}
private boolean matchesConditionTree(Map<String, Object> row, FilterTreeObj conditionTree, Map<Long, DatasetTableFieldDTO> fieldMap) {
if (conditionTree == null || conditionTree.getItems().isEmpty()) {
return true; // 如果没有条件树或条件列表为空返回所有行
}
List<FilterTreeItem> items = conditionTree.getItems();
if (conditionTree.getLogic().equals("or")) {
return matchesAnyItem(row, items, fieldMap);
}
return matchesAllItems(row, items, fieldMap);
}
private boolean matchesAllItems(Map<String, Object> row, List<FilterTreeItem> items, Map<Long, DatasetTableFieldDTO> fieldMap) {
for (FilterTreeItem item : items) {
if (!matchesConditionItem(row, item, fieldMap)) {
return false;
}
}
return true;
}
private boolean matchesAnyItem(Map<String, Object> row, List<FilterTreeItem> items, Map<Long, DatasetTableFieldDTO> fieldMap) {
for (FilterTreeItem item : items) {
if (matchesConditionItem(row, item, fieldMap)) {
return true;
}
}
return false;
}
private boolean matchesConditionItem(Map<String, Object> row, FilterTreeItem item, Map<Long, DatasetTableFieldDTO> fieldMap) {
if ("item".equals(item.getType())) {
DatasetTableFieldDTO fieldDTO = fieldMap.get(item.getFieldId());
return rowMatch(row, item, fieldDTO);
} else if ("tree".equals(item.getType()) && item.getSubTree() != null) {
return matchesConditionTree(row, item.getSubTree(), fieldMap);
}
return false; // 如果类型不匹配或没有子树不匹配
}
private boolean rowMatch(Map<String, Object> row, FilterTreeItem item, DatasetTableFieldDTO fieldDTO) {
String dataeaseName = fieldDTO.getDataeaseName();
String filterType = item.getFilterType();
Integer deType = fieldDTO.getDeType();
Object valueObj = row.get(dataeaseName);
if (StringUtils.equals(filterType, "enum")) {
List<String> enumValue = item.getEnumValue();
return ObjectUtils.isNotEmpty(valueObj) && enumValue.contains(valueObj);
} else {
String term = item.getTerm();
if (Objects.equals(deType, DeTypeConstants.DE_STRING)) {
if (valueObj == null) {
return StringUtils.equals(term, "null");
}
if (StringUtils.equals(term, "eq")) {
return StringUtils.equals(item.getValue(), valueObj.toString());
} else if (StringUtils.equals(term, "not_eq")) {
return !StringUtils.equals(item.getValue(), valueObj.toString());
} else if (StringUtils.equals(term, "in")) {
return Arrays.stream(item.getValue().split(",")).toList().contains(valueObj.toString());
} else if (StringUtils.equals(term, "not_in")) {
return !Arrays.stream(item.getValue().split(",")).toList().contains(valueObj.toString());
} else if (StringUtils.equals(term, "like")) {
return StringUtils.contains(item.getValue(), valueObj.toString());
} else if (StringUtils.equals(term, "not_like")) {
return !StringUtils.contains(item.getValue(), valueObj.toString());
} else if (StringUtils.equals(term, "null")) {
return false;
} else if (StringUtils.equals(term, "not_null")) {
return true;
} else if (StringUtils.equals(term, "empty")) {
return StringUtils.isBlank(valueObj.toString());
} else if (StringUtils.equals(term, "not_empty")) {
return !StringUtils.isBlank(valueObj.toString());
} else {
return StringUtils.equals(item.getValue(), valueObj.toString());
}
} else if (Objects.equals(deType, DeTypeConstants.DE_INT) || Objects.equals(deType, DeTypeConstants.DE_FLOAT)) {
if (valueObj == null) return false;
if (ObjectUtils.isEmpty(item.getValue())) {
return false;
}
float targetVal = Float.parseFloat(item.getValue());
float originVal = Float.parseFloat(valueObj.toString());
if (StringUtils.equals(term, "eq")) {
return StringUtils.equals(String.valueOf(originVal), String.valueOf(targetVal));
} else if (StringUtils.equals(term, "not_eq")) {
return !StringUtils.equals(String.valueOf(originVal), String.valueOf(targetVal));
} else if (StringUtils.equals(term, "gt")) {
return targetVal < originVal;
} else if (StringUtils.equals(term, "ge")) {
return targetVal <= originVal;
} else if (StringUtils.equals(term, "lt")) {
return targetVal > originVal;
} else if (StringUtils.equals(term, "le")) {
return targetVal >= originVal;
} else {
return StringUtils.equals(item.getValue(), valueObj.toString());
}
} else if (Objects.equals(deType, DeTypeConstants.DE_TIME)) {
// 补充时间逻辑
return timeMatch(item, valueObj);
} else {
return true;
}
}
}
private boolean timeMatch(FilterTreeItem item, Object valueObj) {
if (ObjectUtils.isEmpty(valueObj)) return false;
String valueText = valueObj.toString();
String target = item.getValue();
target = target.replaceAll("[^0-9]", "");
valueText = valueText.replaceAll("[^0-9]", "");
long targetLong = Long.parseLong(target);
long valueLong = Long.parseLong(valueText);
String term = item.getTerm();
if (StringUtils.equals(term, "eq")) {
return valueLong == targetLong;
} else if (StringUtils.equals(term, "not_eq")) {
return valueLong != targetLong;
} else if (StringUtils.equals(term, "gt")) {
return valueLong > targetLong;
} else if (StringUtils.equals(term, "ge")) {
return valueLong >= targetLong;
} else if (StringUtils.equals(term, "lt")) {
return valueLong < targetLong;
} else if (StringUtils.equals(term, "le")) {
return valueLong <= targetLong;
} else {
return valueLong == targetLong;
}
}
}

View File

@ -0,0 +1,567 @@
package io.dataease.chart.server;
import com.fasterxml.jackson.core.type.TypeReference;
import io.dataease.api.chart.ChartDataApi;
import io.dataease.api.chart.dto.ViewDetailField;
import io.dataease.api.chart.request.ChartExcelRequest;
import io.dataease.api.chart.request.ChartExcelRequestInner;
import io.dataease.auth.DeLinkPermit;
import io.dataease.chart.constant.ChartConstants;
import io.dataease.chart.manage.ChartDataManage;
import io.dataease.constant.AuthConstant;
import io.dataease.constant.CommonConstants;
import io.dataease.dataset.manage.PermissionManage;
import io.dataease.dataset.server.DatasetFieldServer;
import io.dataease.constant.DeTypeConstants;
import io.dataease.exception.DEException;
import io.dataease.exportCenter.manage.ExportCenterManage;
import io.dataease.exportCenter.util.ExportCenterUtils;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.view.dto.*;
import io.dataease.license.manage.F2CLicLimitedManage;
import io.dataease.result.ResultCode;
import io.dataease.utils.JsonUtil;
import io.dataease.utils.LogUtil;
import io.dataease.visualization.manage.VisualizationTemplateExtendDataManage;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* @Author Junjun
*/
@RestController
@RequestMapping("/chartData")
public class ChartDataServer implements ChartDataApi {
@Resource
private ChartDataManage chartDataManage;
@Resource
private ExportCenterManage exportCenterManage;
@Resource
private VisualizationTemplateExtendDataManage extendDataManage;
@Resource
private PermissionManage permissionManage;
@Resource
private DatasetFieldServer datasetFieldServer;
@Resource(name = "f2CLicLimitedManage")
private F2CLicLimitedManage f2CLicLimitedManage;
@Value("${dataease.export.page.size:50000}")
private Integer extractPageSize;
private final Long sheetLimit = 1000000L;
@DeLinkPermit("#p0.sceneId")
@Override
public ChartViewDTO getData(ChartViewDTO chartViewDTO) throws Exception {
try {
// 从模板数据获取
if (CommonConstants.VIEW_DATA_FROM.TEMPLATE.equalsIgnoreCase(chartViewDTO.getDataFrom())) {
return extendDataManage.getChartDataInfo(chartViewDTO.getId(), chartViewDTO);
} else {
return chartDataManage.calcData(chartViewDTO);
}
} catch (Exception e) {
DEException.throwException(ResultCode.DATA_IS_WRONG.code(), e.getMessage() + "\n\n" + ExceptionUtils.getStackTrace(e));
}
return null;
}
public ChartViewDTO findExcelData(ChartExcelRequest request) {
ChartViewDTO chartViewInfo = new ChartViewDTO();
try {
ChartViewDTO viewDTO = request.getViewInfo();
viewDTO.setIsExcelExport(true);
String[] dsHeader = null;
Integer[] dsTypes = null;
//downloadType = dataset 为下载原始名字 这里做数据转换模拟 table-info类型图表导出
if ("dataset".equals(request.getDownloadType())) {
viewDTO.setResultMode(ChartConstants.VIEW_RESULT_MODE.ALL);
viewDTO.setType("table-info");
viewDTO.setRender("antv");
List<DatasetTableFieldDTO> sourceFields = datasetFieldServer.listByDatasetGroup(viewDTO.getTableId());
List<String> fileNames = permissionManage.filterColumnPermissions(sourceFields, new HashMap<>(), viewDTO.getTableId(), null).stream().map(DatasetTableFieldDTO::getDataeaseName).collect(Collectors.toList());
sourceFields = sourceFields.stream().filter(datasetTableFieldDTO -> fileNames.contains(datasetTableFieldDTO.getDataeaseName())).collect(Collectors.toList());
dsHeader = sourceFields.stream().map(DatasetTableFieldDTO::getName).toArray(String[]::new);
dsTypes = sourceFields.stream().map(DatasetTableFieldDTO::getDeType).toArray(Integer[]::new);
TypeReference<List<ChartViewFieldDTO>> listTypeReference = new TypeReference<List<ChartViewFieldDTO>>() {
};
viewDTO.setXAxis(JsonUtil.parseList(JsonUtil.toJSONString(sourceFields).toString(), listTypeReference));
}
int curLimit = Math.toIntExact(ExportCenterUtils.getExportLimit("view"));
int curDsLimit = Math.toIntExact(ExportCenterUtils.getExportLimit("dataset"));
int viewLimit = Math.min(curLimit, curDsLimit);
if (ChartConstants.VIEW_RESULT_MODE.CUSTOM.equals(viewDTO.getResultMode())) {
Integer limitCount = viewDTO.getResultCount();
viewDTO.setResultCount(Math.min(viewLimit, limitCount));
} else {
viewDTO.setResultCount(viewLimit);
}
chartViewInfo = getData(viewDTO);
List<Object[]> tableRow = (List) chartViewInfo.getData().get("sourceData");
if ("dataset".equals(request.getDownloadType())) {
request.setHeader(dsHeader);
request.setExcelTypes(dsTypes);
}
request.setDetails(tableRow);
request.setData(chartViewInfo.getData());
} catch (Exception e) {
throw new RuntimeException(e);
}
return chartViewInfo;
}
public static String valueFormatter(BigDecimal value, FormatterCfgDTO formatter) {
if (value == null) {
return null;
}
String result;
if (formatter.getType().equals("auto")) {
result = transSeparatorAndSuffix(String.valueOf(transUnit(value, formatter)), formatter);
} else if (formatter.getType().equals("value")) {
result = transSeparatorAndSuffix(transDecimal(transUnit(value, formatter), formatter), formatter);
} else if (formatter.getType().equals("percent")) {
value = value.multiply(BigDecimal.valueOf(100));
result = transSeparatorAndSuffix(transDecimal(value, formatter), formatter);
} else {
result = value.toString();
}
return result;
}
private static BigDecimal transUnit(BigDecimal value, FormatterCfgDTO formatter) {
return value.divide(BigDecimal.valueOf(formatter.getUnit()));
}
private static String transDecimal(BigDecimal value, FormatterCfgDTO formatter) {
DecimalFormat df = new DecimalFormat("0." + new String(new char[formatter.getDecimalCount()]).replace('\0', '0'));
return df.format(value);
}
private static String transSeparatorAndSuffix(String value, FormatterCfgDTO formatter) {
StringBuilder sb = new StringBuilder(value);
if (formatter.getThousandSeparator()) {
Pattern thousandsPattern = Pattern.compile("(\\d)(?=(\\d{3})+$)");
String[] numArr = value.split("\\.");
numArr[0] = addThousandSeparator(numArr[0], thousandsPattern);
sb = new StringBuilder(String.join(".", numArr));
}
if (formatter.getType().equals("percent")) {
sb.append('%');
} else {
switch (formatter.getUnit()) {
case 1000:
sb.append("");
break;
case 10000:
sb.append("");
break;
case 1000000:
sb.append("百万");
break;
case 100000000:
sb.append('亿');
break;
default:
break;
}
}
String suffix = formatter.getSuffix().trim();
if (!suffix.isEmpty()) {
if (suffix.equals("%")) {
sb.append("\"%\"");
} else {
sb.append(suffix);
}
}
return sb.toString();
}
private static String addThousandSeparator(String numStr, Pattern pattern) {
Matcher matcher = pattern.matcher(numStr);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb, matcher.group(1) + ",");
}
matcher.appendTail(sb);
return sb.toString();
}
@DeLinkPermit("#p0.dvId")
@Override
public void innerExportDetails(ChartExcelRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String linkToken = httpServletRequest.getHeader(AuthConstant.LINK_TOKEN_KEY);
LogUtil.info(request.getViewInfo().getId() + " " + StringUtils.isNotEmpty(linkToken) + " " + request.isDataEaseBi());
if ((StringUtils.isNotEmpty(linkToken) && !request.isDataEaseBi()) || (request.isDataEaseBi() && StringUtils.isEmpty(linkToken))) {
OutputStream outputStream = response.getOutputStream();
try {
Workbook wb = new SXSSFWorkbook();
//给单元格设置样式
CellStyle cellStyle = wb.createCellStyle();
Font font = wb.createFont();
//设置字体大小
font.setFontHeightInPoints((short) 12);
//设置字体加粗
font.setBold(true);
//给字体设置样式
cellStyle.setFont(font);
//设置单元格背景颜色
cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
//设置单元格填充样式(使用纯色背景颜色填充)
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
if ("dataset".equals(request.getDownloadType()) || request.getViewInfo().getType().equalsIgnoreCase("table-info")) {
List<Object[]> details = new ArrayList<>();
Sheet detailsSheet;
Integer sheetIndex = 1;
request.getViewInfo().getChartExtRequest().setPageSize(Long.valueOf(extractPageSize));
ChartViewDTO chartViewDTO = findExcelData(request);
for (long i = 1; i < chartViewDTO.getTotalPage() + 1; i++) {
request.getViewInfo().getChartExtRequest().setGoPage(i);
findExcelData(request);
details.addAll(request.getDetails());
if ((details.size() + extractPageSize) > sheetLimit || i == chartViewDTO.getTotalPage()) {
detailsSheet = wb.createSheet("数据" + sheetIndex);
Integer[] excelTypes = request.getExcelTypes();
details.add(0, request.getHeader());
ViewDetailField[] detailFields = request.getDetailFields();
Object[] header = request.getHeader();
ChartDataServer.setExcelData(detailsSheet, cellStyle, header, details, detailFields, excelTypes, request.getViewInfo(), wb);
sheetIndex++;
details.clear();
}
}
} else {
findExcelData(request);
if (CollectionUtils.isEmpty(request.getMultiInfo())) {
List<Object[]> details = request.getDetails();
Integer[] excelTypes = request.getExcelTypes();
details.add(0, request.getHeader());
ViewDetailField[] detailFields = request.getDetailFields();
Object[] header = request.getHeader();
Sheet detailsSheet = wb.createSheet("数据");
setExcelData(detailsSheet, cellStyle, header, details, detailFields, excelTypes, request.getViewInfo(), null);
} else {
for (int i = 0; i < request.getMultiInfo().size(); i++) {
ChartExcelRequestInner requestInner = request.getMultiInfo().get(i);
List<Object[]> details = requestInner.getDetails();
Integer[] excelTypes = requestInner.getExcelTypes();
details.add(0, requestInner.getHeader());
ViewDetailField[] detailFields = requestInner.getDetailFields();
Object[] header = requestInner.getHeader();
Sheet detailsSheet = wb.createSheet("数据 " + (i + 1));
setExcelData(detailsSheet, cellStyle, header, details, detailFields, excelTypes, request.getViewInfo(), null);
}
}
}
exportCenterManage.addWatermarkTools(wb);
response.setContentType("application/vnd.ms-excel");
//文件名称
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(request.getViewName(), StandardCharsets.UTF_8) + ".xlsx");
wb.write(outputStream);
outputStream.flush();
outputStream.close();
} catch (Exception e) {
DEException.throwException(e);
}
} else {
exportCenterManage.addTask(request.getViewId(), "chart", request);
return;
}
}
@DeLinkPermit("#p0.dvId")
@Override
public void innerExportDataSetDetails(ChartExcelRequest request, HttpServletResponse response) throws Exception {
this.innerExportDetails(request, response);
}
public static void setExcelData(Sheet detailsSheet, CellStyle cellStyle, Object[] header, List<Object[]> details, ViewDetailField[] detailFields, Integer[] excelTypes, ChartViewDTO viewInfo, Workbook wb) {
setExcelData(detailsSheet, cellStyle, header, details, detailFields, excelTypes, null, viewInfo, wb);
}
public static void setExcelData(Sheet detailsSheet, CellStyle cellStyle, Object[] header, List<Object[]> details, ViewDetailField[] detailFields, Integer[] excelTypes, Comment comment, ChartViewDTO viewInfo, Workbook wb) {
List<CellStyle> styles = new ArrayList<>();
List<ChartViewFieldDTO> xAxis = new ArrayList<>();
xAxis.addAll(viewInfo.getXAxis());
xAxis.addAll(viewInfo.getYAxis());
xAxis.addAll(viewInfo.getXAxisExt());
xAxis.addAll(viewInfo.getYAxisExt());
xAxis.addAll(viewInfo.getExtStack());
if (viewInfo.getType().equalsIgnoreCase("table-normal") || viewInfo.getType().equalsIgnoreCase("table-info")) {
for (ChartViewFieldDTO xAxi : xAxis) {
if (xAxi.getDeType().equals(DeTypeConstants.DE_INT) || xAxi.getDeType().equals(DeTypeConstants.DE_FLOAT)) {
CellStyle formatterCellStyle = createCellStyle(wb, xAxi.getFormatterCfg(), null);
styles.add(formatterCellStyle);
} else {
styles.add(null);
}
}
}
boolean mergeHead = false;
if (ArrayUtils.isNotEmpty(detailFields)) {
cellStyle.setBorderTop(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderLeft(BorderStyle.THIN);
String[] detailField = Arrays.stream(detailFields).map(ViewDetailField::getName).toList().toArray(new String[detailFields.length]);
Row row = detailsSheet.createRow(0);
int headLen = header.length;
int detailFieldLen = detailField.length;
for (int i = 0; i < headLen; i++) {
Cell cell = row.createCell(i);
cell.setCellValue(header[i].toString());
if (i < headLen - 1) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 1, i, i);
detailsSheet.addMergedRegion(cellRangeAddress);
} else {
for (int j = i + 1; j < detailFieldLen + i; j++) {
row.createCell(j).setCellStyle(cellStyle);
}
CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 0, i, i + detailFieldLen - 1);
detailsSheet.addMergedRegion(cellRangeAddress);
}
cell.setCellStyle(cellStyle);
detailsSheet.setColumnWidth(i, 255 * 20);
}
Row detailRow = detailsSheet.createRow(1);
for (int i = 0; i < headLen - 1; i++) {
Cell cell = detailRow.createCell(i);
cell.setCellStyle(cellStyle);
}
for (int i = 0; i < detailFieldLen; i++) {
int colIndex = headLen - 1 + i;
Cell cell = detailRow.createCell(colIndex);
cell.setCellValue(detailField[i]);
cell.setCellStyle(cellStyle);
detailsSheet.setColumnWidth(colIndex, 255 * 20);
}
details.add(1, detailField);
mergeHead = true;
}
if (CollectionUtils.isNotEmpty(details) && (!mergeHead || details.size() > 2)) {
int realDetailRowIndex = 2;
for (int i = (mergeHead ? 2 : 0); i < details.size(); i++) {
Row row = detailsSheet.createRow(realDetailRowIndex > 2 ? realDetailRowIndex : i);
Object[] rowData = details.get(i);
if (rowData != null) {
for (int j = 0; j < rowData.length; j++) {
Object cellValObj = rowData[j];
if (mergeHead && j == rowData.length - 1 && (cellValObj.getClass().isArray() || cellValObj instanceof ArrayList)) {
Object[] detailRowArray = ((List<Object>) cellValObj).toArray(new Object[((List<?>) cellValObj).size()]);
int detailRowArrayLen = detailRowArray.length;
int temlJ = j;
while (detailRowArrayLen > 1 && temlJ-- > 0) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(realDetailRowIndex, realDetailRowIndex + detailRowArrayLen - 1, temlJ, temlJ);
detailsSheet.addMergedRegion(cellRangeAddress);
}
for (int k = 0; k < detailRowArrayLen; k++) {
List<Object> detailRows = (List<Object>) detailRowArray[k];
Row curRow = row;
if (k > 0) {
curRow = detailsSheet.createRow(realDetailRowIndex + k);
}
for (int l = 0; l < detailRows.size(); l++) {
Object col = detailRows.get(l);
Cell cell = curRow.createCell(j + l);
cell.setCellValue(col.toString());
}
}
realDetailRowIndex += detailRowArrayLen;
break;
}
Cell cell = row.createCell(j);
if (i == 0) {// 头部
cell.setCellValue(cellValObj.toString());
cell.setCellStyle(cellStyle);
//设置列的宽度
detailsSheet.setColumnWidth(j, 255 * 20);
} else if (cellValObj != null) {
try {
if ((viewInfo.getType().equalsIgnoreCase("table-normal") || viewInfo.getType().equalsIgnoreCase("table-info")) && (xAxis.get(j).getDeType().equals(DeTypeConstants.DE_INT) || xAxis.get(j).getDeType().equals(DeTypeConstants.DE_FLOAT))) {
try {
FormatterCfgDTO formatterCfgDTO = xAxis.get(j).getFormatterCfg() == null ? new FormatterCfgDTO() : xAxis.get(j).getFormatterCfg();
row.getCell(j).setCellStyle(styles.get(j));
row.getCell(j).setCellValue(Double.valueOf(cellValue(formatterCfgDTO, new BigDecimal(cellValObj.toString()))));
} catch (Exception e) {
cell.setCellValue(cellValObj.toString());
}
} else {
if ((excelTypes[j].equals(DeTypeConstants.DE_INT) || excelTypes[j].equals(DeTypeConstants.DE_FLOAT)) && StringUtils.isNotEmpty(cellValObj.toString())) {
cell.setCellValue(Double.valueOf(cellValObj.toString()));
} else if (cellValObj != null) {
cell.setCellValue(cellValObj.toString());
}
}
} catch (Exception e) {
LogUtil.warn("export excel data transform error");
}
} else {
if (!viewInfo.getType().equalsIgnoreCase("circle-packing")) {
Map<String, Object> senior = viewInfo.getSenior();
ChartSeniorFunctionCfgDTO functionCfgDTO = JsonUtil.parseObject((String) JsonUtil.toJSONString(senior.get("functionCfg")), ChartSeniorFunctionCfgDTO.class);
if (functionCfgDTO != null && StringUtils.isNotEmpty(functionCfgDTO.getEmptyDataStrategy()) && functionCfgDTO.getEmptyDataStrategy().equalsIgnoreCase("setZero")) {
if ((viewInfo.getType().equalsIgnoreCase("table-normal") || viewInfo.getType().equalsIgnoreCase("table-info"))) {
if (functionCfgDTO.getEmptyDataFieldCtrl().contains(xAxis.get(j).getDataeaseName())) {
cell.setCellValue(0);
}
} else {
cell.setCellValue(0);
}
}
}
}
}
}
}
}
}
private static String cellValue(FormatterCfgDTO formatterCfgDTO, BigDecimal value) {
if (formatterCfgDTO.getType().equalsIgnoreCase("percent")) {
return value.toString();
} else {
return value.divide(BigDecimal.valueOf(formatterCfgDTO.getUnit())).toString();
}
}
private static CellStyle createCellStyle(Workbook workbook, FormatterCfgDTO formatter, String value) {
CellStyle cellStyle = workbook.createCellStyle();
DataFormat format = workbook.createDataFormat();
if (formatter == null) {
cellStyle.setDataFormat(format.getFormat("General"));
return cellStyle;
}
String formatStr = "";
if (formatter.getType().equals("auto")) {
String[] valueSplit = String.valueOf(value).split(".");
if (StringUtils.isEmpty(value) || !value.contains(".")) {
formatStr = "0";
} else {
formatStr = "0." + new String(new char[valueSplit.length]).replace('\0', '0');
}
switch (formatter.getUnit()) {
case 1000:
formatStr = formatStr + "";
break;
case 10000:
formatStr = formatStr + "";
break;
case 1000000:
formatStr = formatStr + "百万";
break;
case 100000000:
formatStr = formatStr + "'亿'";
break;
default:
break;
}
if (formatter.getThousandSeparator()) {
formatStr = "#,##" + formatStr;
}
if (StringUtils.isNotEmpty(formatter.getSuffix())) {
if (formatter.getSuffix().equals("%")) {
formatStr = formatStr + "\"%\"";
} else {
formatStr = formatStr + formatter.getSuffix();
}
}
}
if (formatter.getType().equals("value")) {
if (formatter.getDecimalCount() > 0) {
formatStr = "0." + new String(new char[formatter.getDecimalCount()]).replace('\0', '0');
} else {
formatStr = "0";
}
switch (formatter.getUnit()) {
case 1000:
formatStr = formatStr + "";
break;
case 10000:
formatStr = formatStr + "";
break;
case 1000000:
formatStr = formatStr + "百万";
break;
case 100000000:
formatStr = formatStr + "'亿'";
break;
default:
break;
}
if (formatter.getThousandSeparator()) {
formatStr = "#,##" + formatStr;
}
if (StringUtils.isNotEmpty(formatter.getSuffix())) {
if (formatter.getSuffix().equals("%")) {
formatStr = formatStr + "\"%\"";
} else {
formatStr = formatStr + formatter.getSuffix();
}
}
} else if (formatter.getType().equals("percent")) {
if (formatter.getDecimalCount() > 0) {
formatStr = "0." + new String(new char[formatter.getDecimalCount()]).replace('\0', '0');
} else {
formatStr = "0";
}
formatStr = formatStr + "%";
}
if (StringUtils.isNotEmpty(formatStr)) {
cellStyle.setDataFormat(format.getFormat(formatStr));
} else {
return null;
}
return cellStyle;
}
@Override
public List<String> getFieldData(ChartViewDTO view, Long fieldId, String fieldType) throws Exception {
return chartDataManage.getFieldData(view, fieldId, fieldType);
}
@Override
public List<String> getDrillFieldData(ChartViewDTO view, Long fieldId) throws Exception {
return chartDataManage.getDrillFieldData(view, fieldId);
}
}

View File

@ -0,0 +1,81 @@
package io.dataease.chart.server;
import io.dataease.api.chart.ChartViewApi;
import io.dataease.api.chart.vo.ChartBaseVO;
import io.dataease.extensions.view.dto.ChartViewDTO;
import io.dataease.extensions.view.dto.ChartViewFieldDTO;
import io.dataease.api.chart.vo.ViewSelectorVO;
import io.dataease.chart.manage.ChartViewManege;
import io.dataease.exception.DEException;
import io.dataease.result.ResultCode;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
/**
* @Author Junjun
*/
@RestController
@RequestMapping("chart")
public class ChartViewServer implements ChartViewApi {
@Resource
private ChartViewManege chartViewManege;
@Override
public ChartViewDTO getData(Long id) throws Exception {
try {
return chartViewManege.getChart(id);
} catch (Exception e) {
DEException.throwException(ResultCode.DATA_IS_WRONG.code(), e.getMessage());
}
return null;
}
@Override
public Map<String, List<ChartViewFieldDTO>> listByDQ(Long id, Long chartId, ChartViewDTO dto) {
return chartViewManege.listByDQ(id, chartId, dto);
}
@Override
public ChartViewDTO save(ChartViewDTO dto) throws Exception {
return chartViewManege.save(dto);
}
@Override
public String checkSameDataSet(String viewIdSource, String viewIdTarget) {
return chartViewManege.checkSameDataSet(viewIdSource, viewIdTarget);
}
@Override
public ChartViewDTO getDetail(Long id) {
return chartViewManege.getDetails(id);
}
@Override
public List<ViewSelectorVO> viewOption(Long resourceId) {
return chartViewManege.viewOption(resourceId);
}
@Override
public void copyField(Long id, Long chartId) {
chartViewManege.copyField(id, chartId);
}
@Override
public void deleteField(Long id) {
chartViewManege.deleteField(id);
}
@Override
public void deleteFieldByChart(Long chartId) {
chartViewManege.deleteFieldByChartId(chartId);
}
@Override
public ChartBaseVO chartBaseInfo(Long id) {
return chartViewManege.chartBaseInfo(id);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
package io.dataease.commons;
import java.util.UUID;
public class UUIDUtils {
public static String getUUID() {
return UUID.randomUUID().toString();
}
}

View File

@ -0,0 +1,75 @@
package io.dataease.commons.constants;
public class DataVisualizationConstants {
//新建仪表板来源
public static final class QUERY_SOURCE {
// 定时报告
public static final String REPORT = "report";
// 主工程
public static final String MAIN = "main";
}
//新建仪表板来源
public static final class NEW_PANEL_FROM {
// 直接新建
public static final String NEW = "new";
// 内部模板新建
public static final String NEW_INNER_TEMPLATE = "new_inner_template";
// 外部模板新建
public static final String NEW_OUTER_TEMPLATE = "new_outer_template";
// 模板市场新建
public static final String NEW_MARKET_TEMPLATE = "new_market_template";
}
//删除标志
public static final class DELETE_FLAG {
//已删除
public static final boolean DELETED = true;
//未删除可用
public static final boolean AVAILABLE = false;
}
//节点类型
public static final class NODE_TYPE {
//目录
public static final String FOLDER = "folder";
//资源节点
public static final String LEAF = "leaf";
}
//操作
public static final class RESOURCE_OPT_TYPE {
//新建资源节点
public static final String NEW_LEAF = "newLeaf";
//新建文件夹
public static final String NEW_FOLDER = "newFolder";
//移动
public static final String MOVE = "move";
//重命名
public static final String RENAME = "rename";
public static final String EDIT = "edit";
//复制
public static final String COPY = "copy";
}
public static final class TEMPLATE_SOURCE {
//模板市场
public static final String MARKET = "market";
//模板管理
public static final String MANAGE = "manage";
//公共
public static final String PUBLIC = "public";
}
}

View File

@ -0,0 +1,33 @@
package io.dataease.commons.constants;
/**
* @author : WangJiaHao
* @date : 2023/10/8 09:37
*/
public class OptConstants {
public static final class OPT_TYPE {
//新建
public static final int NEW = 1;
//更新
public static final int UPDATE = 2;
//删除
public static final int DELETE = 3;
}
public static final class OPT_RESOURCE_TYPE {
//可视化资源
public static final int VISUALIZATION = 1;
//仪表板
public static final int DASHBOARD = 2;
//数据大屏
public static final int DATA_VISUALIZATION = 3;
//数据集
public static final int DATASET = 4;
//数据源
public static final int DATASOURCE = 5;
//模板
public static final int TEMPLATE = 6;
}
}

View File

@ -0,0 +1,12 @@
package io.dataease.commons.constants;
public enum TaskStatus {
WaitingForExecution, // 等待执行
Stopped, // 停止
Suspend, // 暂停
UnderExecution, // 执行中
Completed, //完成
Error, //错误
Warning //警告
}

View File

@ -0,0 +1,210 @@
package io.dataease.commons.utils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.UUID;
/**
* 加密解密工具
*
* @author kun.mo
*/
public class CodingUtil {
private static final String UTF_8 = "UTF-8";
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
public static String[] chars = new String[] { "a", "b", "c", "d", "e", "f",
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
"t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
"W", "X", "Y", "Z" };
/**
* MD5加密
*
* @param src 要加密的串
* @return 加密后的字符串
*/
public static String md5(String src) {
return md5(src, UTF_8);
}
/**
* MD5加密
*
* @param src 要加密的串
* @param charset 加密字符集
* @return 加密后的字符串
*/
public static String md5(String src, String charset) {
try {
byte[] strTemp = StringUtils.isEmpty(charset) ? src.getBytes() : src.getBytes(charset);
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(strTemp);
byte[] md = mdTemp.digest();
int j = md.length;
char[] str = new char[j * 2];
int k = 0;
for (byte byte0 : md) {
str[k++] = HEX_DIGITS[byte0 >>> 4 & 0xf];
str[k++] = HEX_DIGITS[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
throw new RuntimeException("MD5 encrypt error:", e);
}
}
/**
* BASE64解密
*
* @param src 待解密的字符串
* @return 解密后的字符串
*/
public static String base64Decoding(String src) {
byte[] b;
String result = null;
if (src != null) {
try {
b = Base64.decodeBase64(src);
result = new String(b, UTF_8);
} catch (Exception e) {
throw new RuntimeException("BASE64 decoding error:", e);
}
}
return result;
}
/**
* BASE64加密
*
* @param src 待加密的字符串
* @return 加密后的字符串
*/
public static String base64Encoding(String src) {
String result = null;
if (src != null) {
try {
result = Base64.encodeBase64String(src.getBytes(UTF_8));
} catch (Exception e) {
throw new RuntimeException("BASE64 encoding error:", e);
}
}
return result;
}
/**
* AES加密
*
* @param src 待加密字符串
* @param secretKey 密钥
* @param iv 向量
* @return 加密后字符串
*/
public static String aesEncrypt(String src, String secretKey, String iv) {
if (StringUtils.isBlank(secretKey)) {
throw new RuntimeException("secretKey is empty");
}
try {
byte[] raw = secretKey.getBytes(UTF_8);
SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES");
// "算法/模式/补码方式" ECB
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv1 = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv1);
byte[] encrypted = cipher.doFinal(src.getBytes(UTF_8));
return Base64.encodeBase64String(encrypted);
} catch (Exception e) {
throw new RuntimeException("AES encrypt error:", e);
}
}
/**
* AES 解密
*
* @param src 待解密字符串
* @param secretKey 密钥
* @param iv 向量
* @return 解密后字符串
*/
public static String aesDecrypt(String src, String secretKey, String iv) {
if (StringUtils.isBlank(secretKey)) {
throw new RuntimeException("secretKey is empty");
}
try {
byte[] raw = secretKey.getBytes(UTF_8);
SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv1 = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv1);
byte[] encrypted1 = Base64.decodeBase64(src);
byte[] original = cipher.doFinal(encrypted1);
return new String(original, UTF_8);
} catch (BadPaddingException | IllegalBlockSizeException e) {
// 解密的原字符串为非加密字符串则直接返回原字符串
return src;
} catch (Exception e) {
throw new RuntimeException("decrypt errorplease check parameters", e);
}
}
public static String secretKey() {
try {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
SecretKey secretKey = keyGen.generateKey();
return Base64.encodeBase64String(secretKey.getEncoded());
} catch (Exception e) {
throw new RuntimeException("generate secretKey error", e);
}
}
public static boolean isNumeric(String str){
for (int i = str.length();--i>=0;){
if (!Character.isDigit(str.charAt(i))){
return false;
}
}
return true;
}
public static String shortUuid() {
StringBuffer shortBuffer = new StringBuffer();
String uuid = UUID.randomUUID().toString().replace("-", "");
for (int i = 0; i < 8; i++) {
String str = uuid.substring(i * 4, i * 4 + 4);
int x = Integer.parseInt(str, 16);
shortBuffer.append(chars[x % 0x3E]);
}
return shortBuffer.toString();
}
public static Integer string2Integer(String str) {
StringBuffer sb = new StringBuffer();
if (StringUtils.isBlank(str)) return null;
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (Character.isDigit(c)) {
sb.append(c);
}
}
return sb.length() > 0 ? Integer.parseInt(sb.toString()) : null;
}
}

View File

@ -0,0 +1,35 @@
package io.dataease.commons.utils;
import io.dataease.constant.SortConstants;
import io.dataease.visualization.dto.VisualizationNodeBO;
import org.apache.commons.lang3.StringUtils;
import java.text.Collator;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author : WangJiaHao
* @date : 2024/3/18 10:53
*/
public class CoreTreeUtils {
public static List<VisualizationNodeBO> customSortBO(List<VisualizationNodeBO> list, String sortType) {
Collator collator = Collator.getInstance(Locale.CHINA);
if (StringUtils.equalsIgnoreCase(SortConstants.NAME_DESC, sortType)) {
Set<VisualizationNodeBO> poSet = new TreeSet<>(Comparator.comparing(VisualizationNodeBO::getName, collator));
poSet.addAll(list);
return poSet.stream().collect(Collectors.toList());
} else if (StringUtils.equalsIgnoreCase(SortConstants.NAME_ASC, sortType)) {
Set<VisualizationNodeBO> poSet = new TreeSet<>(Comparator.comparing(VisualizationNodeBO::getName, collator).reversed());
poSet.addAll(list);
return poSet.stream().collect(Collectors.toList());
} else if (StringUtils.equalsIgnoreCase(SortConstants.TIME_ASC, sortType)) {
Collections.reverse(list);
return list;
} else {
// 默认时间倒序
return list;
}
}
}

View File

@ -0,0 +1,98 @@
package io.dataease.commons.utils;
import io.dataease.utils.LogUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.TriggerBuilder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class CronUtils {
public static CronTrigger getCronTrigger(String cron) {
if (!CronExpression.isValidExpression(cron)) {
throw new RuntimeException("cron :" + cron + "表达式解析错误");
}
return TriggerBuilder.newTrigger().withIdentity("Calculate Date").withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
}
public static Date getNextTriggerTime(String cron) {
Date date = null;
try {
CronTrigger trigger = getCronTrigger(cron);
Date startDate = trigger.getStartTime();
date = trigger.getFireTimeAfter(startDate);
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
}
return date;
}
public static String tempCron() {
Calendar instance = Calendar.getInstance();
instance.add(Calendar.SECOND, 5);
return instance.get(Calendar.SECOND) + " " +
instance.get(Calendar.MINUTE) + " " +
instance.get(Calendar.HOUR_OF_DAY) + " * * ?";
}
public static String cron(Integer rateType, String rateVal) {
if (rateType == 0) {
return rateVal;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = sdf.parse(rateVal);
} catch (ParseException e) {
e.printStackTrace();
}
Calendar instance = Calendar.getInstance();
assert date != null;
instance.setTime(date);
if (rateType == 1) {
return instance.get(Calendar.SECOND) + " " +
instance.get(Calendar.MINUTE) + " " +
instance.get(Calendar.HOUR_OF_DAY) + " * * ?";
}
if (rateType == 2) {
return instance.get(Calendar.SECOND) + " " +
instance.get(Calendar.MINUTE) + " " +
instance.get(Calendar.HOUR_OF_DAY) + " ? * " +
getDayOfWeek(instance);
}
if (rateType == 3) {
return instance.get(Calendar.SECOND) + " " +
instance.get(Calendar.MINUTE) + " " +
instance.get(Calendar.HOUR_OF_DAY) + " " +
instance.get(Calendar.DATE) + " * ?";
}
return null;
}
private static String getDayOfWeek(Calendar instance) {
int index = instance.get(Calendar.DAY_OF_WEEK);
index = (index % 7) + 1;
return String.valueOf(index);
}
// 判断任务是否过期
public static Boolean taskExpire(Long endTime) {
if (ObjectUtils.isEmpty(endTime))
return false;
long now = System.currentTimeMillis();
return now > endTime;
}
}

View File

@ -0,0 +1,44 @@
package io.dataease.commons.utils;
import io.dataease.utils.BeanUtils;
import java.util.List;
import java.util.stream.Collectors;
public class EncryptUtils extends CodingUtil {
private static final String secretKey = "www.fit2cloud.co";
private static final String iv = "1234567890123456";
public static Object aesEncrypt(Object o) {
if (o == null) {
return null;
}
return aesEncrypt(o.toString(), secretKey, iv);
}
public static Object aesDecrypt(Object o) {
if (o == null) {
return null;
}
return aesDecrypt(o.toString(), secretKey, iv);
}
public static <T> Object aesDecrypt(List<T> o, String attrName) {
if (o == null) {
return null;
}
return o.stream()
.filter(element -> BeanUtils.getFieldValueByName(attrName, element) != null)
.peek(element -> BeanUtils.setFieldValueByName(element, attrName, aesDecrypt(BeanUtils.getFieldValueByName(attrName, element).toString(), secretKey, iv), String.class))
.collect(Collectors.toList());
}
public static Object md5Encrypt(Object o) {
if (o == null) {
return null;
}
return md5(o.toString());
}
}

View File

@ -0,0 +1,132 @@
package io.dataease.commons.utils;
import io.dataease.api.permissions.user.vo.UserFormVO;
import io.dataease.i18n.Translator;
import io.dataease.utils.IPUtils;
import io.dataease.visualization.dto.WatermarkContentDTO;
import org.apache.poi.ss.usermodel.*;
import java.awt.Color;
import java.awt.Font;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ExcelWatermarkUtils {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String transContent(WatermarkContentDTO watermarkContent, UserFormVO userInfo) {
String content = "";
switch (watermarkContent.getType()) {
case "custom" -> content = watermarkContent.getContent();
case "nickName" -> content = "${nickName}";
case "ip" -> content = "${ip}";
case "time" -> content = "${time}";
default -> content = "${username}";
}
String nickName = userInfo.getName().contains("i18n_") ?Translator.get(userInfo.getName()):userInfo.getName();
content = content.replaceAll("\\$\\{ip}", IPUtils.get() == null ? "127.0.0.1" : IPUtils.get());
content = content.replaceAll("\\$\\{username}", userInfo.getAccount());
content = content.replaceAll("\\$\\{nickName}", nickName);
content = content.replaceAll("\\$\\{time}", sdf.format(new Date()));
return content;
}
/**
* 添加水印图片到工作簿并返回图片 ID
*/
public static int addWatermarkImage(Workbook wb, WatermarkContentDTO watermarkContent, UserFormVO userInfo) {
byte[] imageBytes = createTextImage(transContent(watermarkContent, userInfo), watermarkContent); // 生成文字水印图片
return wb.addPicture(imageBytes, Workbook.PICTURE_TYPE_PNG); // 添加到工作簿并返回 ID
}
public static void addWatermarkToSheet(Sheet sheet, Integer pictureIdx) {
Drawing<?> drawing = sheet.createDrawingPatriarch();
CreationHelper helper = sheet.getWorkbook().getCreationHelper();
// 获取工作表的总列数和行数
int lastRowNum = sheet.getLastRowNum();
int totalColumns = 0;
for (int i = 0; i <= lastRowNum; i++) {
Row row = sheet.getRow(i);
if (row != null) {
totalColumns = Math.max(totalColumns, row.getLastCellNum());
}
}
// 如果没有内容则假设默认覆盖100行和50列
if (lastRowNum == 0 && totalColumns == 0) {
lastRowNum = 100;
totalColumns = 50;
}
int picCount = 0;
// 根据总行列数循环绘制水印
for (int row = 0; row <= lastRowNum; row += 15) { // 每15行绘制一行水印
for (int col = 0; col <= totalColumns; col += 8) { // 每8列绘制一列水印
// 创建水印图片位置
addWater(helper, drawing, picCount++, pictureIdx, col, row, col + 5, row + 10);
}
}
}
public static void addWater(CreationHelper helper, Drawing<?> drawing, int picCount, int pictureIdx, int col1, int row1, int col2, int row2) {
ClientAnchor anchor = helper.createClientAnchor();
// 创建水印图片位置
anchor.setCol1(col1); // 水印起始列
anchor.setRow1(row1); // 水印起始行
anchor.setCol2(col2); // 水印结束列
anchor.setRow2(row2); // 水印结束行
Picture picture = drawing.createPicture(anchor, pictureIdx);
//picture.resize 作用 因为Excel 显示图片出现bug 使用同一个pictureIdx时如果图片所以参数都一样只是位置不同的时候
//会只显示一个 使用picture.resize进行放大倍数的小范围调整可以全部显示
picture.resize(1 + (0.000001 * picCount));
}
public static byte[] createTextImage(String text, WatermarkContentDTO watermarkContent) {
double radians = Math.toRadians(15);// 15度偏转
int width = watermarkContent.getWatermark_fontsize() * text.length();
int height = (int) Math.round(watermarkContent.getWatermark_fontsize() + width * Math.sin(radians));
int fontSize = watermarkContent.getWatermark_fontsize();
Color baseColor = Color.decode(watermarkContent.getWatermark_color());
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
// 设置透明背景
image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g2d.dispose();
g2d = image.createGraphics();
// 设置抗锯齿
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 设置字体
g2d.setFont(new Font(null, Font.PLAIN, fontSize));
g2d.setColor(new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), 50)); // 半透明颜色
g2d.rotate(radians, width / 2.0, height / 2.0); // 旋转文字
// 绘制文字
FontMetrics fontMetrics = g2d.getFontMetrics();
int textWidth = fontMetrics.stringWidth(text);
int textHeight = fontMetrics.getHeight();
int x = (width - textWidth) / 2;
int y = (height + textHeight) / 2 - fontMetrics.getDescent();
g2d.drawString(text, x, y);
g2d.dispose();
// 转为字节数组
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ImageIO.write(image, "png", baos);
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
}

View File

@ -0,0 +1,46 @@
package io.dataease.commons.utils;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class MybatisInterceptorConfig {
private String modelName;
private String attrName;
private String attrNameForList;
private String interceptorClass;
private String interceptorMethod;
private String undoClass;
private String undoMethod;
public MybatisInterceptorConfig() {
}
/**
* 用时需谨慎
* 主要配置多个的时候参数少一点
*
* @param modelClass
* @param attrName
*/
public MybatisInterceptorConfig(Class<?> modelClass, String attrName) {
this.modelName = modelClass.getName();
this.attrName = attrName;
this.interceptorClass = EncryptUtils.class.getName();
this.interceptorMethod = "aesEncrypt";
this.undoClass = EncryptUtils.class.getName();
this.undoMethod = "aesDecrypt";
}
public MybatisInterceptorConfig(Class<?> modelClass, String attrName, Class<?> interceptorClass, String interceptorMethod, String undoMethod) {
this.modelName = modelClass.getName();
this.attrName = attrName;
this.interceptorClass = interceptorClass.getName();
this.interceptorMethod = interceptorMethod;
this.undoClass = interceptorClass.getName();
this.undoMethod = undoMethod;
}
}

View File

@ -0,0 +1,759 @@
package io.dataease.commons.utils;
import com.fasterxml.jackson.core.type.TypeReference;
import io.dataease.api.permissions.user.vo.UserFormVO;
import io.dataease.api.permissions.variable.dto.SysVariableValueDto;
import io.dataease.api.permissions.variable.dto.SysVariableValueItem;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.api.PluginManageApi;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.vo.DatasourceConfiguration;
import io.dataease.extensions.datasource.vo.XpackPluginsDatasourceVO;
import io.dataease.extensions.view.dto.SqlVariableDetails;
import io.dataease.i18n.Translator;
import io.dataease.license.utils.LicenseUtil;
import io.dataease.utils.JsonUtil;
import io.dataease.utils.LogUtil;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.util.deparser.ExpressionDeParser;
import net.sf.jsqlparser.util.deparser.SelectDeParser;
import org.apache.calcite.sql.*;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.sql.util.SqlShuttle;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.junit.jupiter.params.provider.CsvSource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static io.dataease.chart.manage.ChartDataManage.START_END_SEPARATOR;
import static org.apache.calcite.sql.SqlKind.*;
public class SqlparserUtils {
public static final String regex = "\\$\\{(.*?)\\}";
public static final String regex2 = "\\[(.*?)\\]";
private static final String SubstitutedParams = "DATAEASE_PATAMS_BI";
private static final String SysParamsSubstitutedParams = "DeSysParams_";
private static final String SubstitutedSql = " 'DE-BI' = 'DE-BI' ";
private boolean removeSysParams;
private UserFormVO userEntity;
private final List<Map<String, String>> sysParams = new ArrayList<>();
public String handleVariableDefaultValue(String sql, String sqlVariableDetails, boolean isEdit, boolean isFromDataSet, List<SqlVariableDetails> parameters, boolean isCross, Map<Long, DatasourceSchemaDTO> dsMap, PluginManageApi pluginManage, UserFormVO userEntity) {
DatasourceSchemaDTO ds = dsMap.entrySet().iterator().next().getValue();
if (StringUtils.isEmpty(sql)) {
DEException.throwException(Translator.get("i18n_sql_not_empty"));
}
this.userEntity = userEntity;
try {
this.removeSysParams = true;
removeVariables(sql, ds.getType());
} catch (Exception e) {
DEException.throwException(e);
}
sql = sql.trim();
if (sql.endsWith(";")) {
sql = sql.substring(0, sql.length() - 1);
}
if (StringUtils.isNotEmpty(sqlVariableDetails)) {
TypeReference<List<SqlVariableDetails>> listTypeReference = new TypeReference<List<SqlVariableDetails>>() {
};
List<SqlVariableDetails> defaultsSqlVariableDetails = JsonUtil.parseList(sqlVariableDetails, listTypeReference);
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(sql);
while (matcher.find()) {
SqlVariableDetails defaultsSqlVariableDetail = null;
for (SqlVariableDetails sqlVariableDetail : defaultsSqlVariableDetails) {
if (matcher.group().substring(2, matcher.group().length() - 1).equalsIgnoreCase(sqlVariableDetail.getVariableName())) {
defaultsSqlVariableDetail = sqlVariableDetail;
break;
}
}
SqlVariableDetails filterParameter = null;
if (ObjectUtils.isNotEmpty(parameters)) {
for (SqlVariableDetails parameter : parameters) {
if (parameter.getVariableName().equalsIgnoreCase(defaultsSqlVariableDetail.getVariableName())) {
filterParameter = parameter;
}
}
}
if (filterParameter != null) {
sql = sql.replace(matcher.group(), transFilter(filterParameter, dsMap));
} else {
if (defaultsSqlVariableDetail != null && StringUtils.isNotEmpty(defaultsSqlVariableDetail.getDefaultValue())) {
if (!isEdit && isFromDataSet && defaultsSqlVariableDetail.getDefaultValueScope().equals(SqlVariableDetails.DefaultValueScope.ALLSCOPE)) {
sql = sql.replace(matcher.group(), defaultsSqlVariableDetail.getDefaultValue());
}
if (isEdit) {
sql = sql.replace(matcher.group(), defaultsSqlVariableDetail.getDefaultValue());
}
}
}
}
}
try {
this.removeSysParams = false;
sql = removeVariables(sql, ds.getType());
// replace keyword '`'
if (!isCross) {
Map.Entry<Long, DatasourceSchemaDTO> next = dsMap.entrySet().iterator().next();
DatasourceSchemaDTO value = next.getValue();
String prefix = "";
String suffix = "";
if (Arrays.stream(DatasourceConfiguration.DatasourceType.values()).map(DatasourceConfiguration.DatasourceType::getType).toList().contains(value.getType())) {
DatasourceConfiguration.DatasourceType datasourceType = DatasourceConfiguration.DatasourceType.valueOf(value.getType());
prefix = datasourceType.getPrefix();
suffix = datasourceType.getSuffix();
} else {
if (LicenseUtil.licenseValid()) {
List<XpackPluginsDatasourceVO> xpackPluginsDatasourceVOS = pluginManage.queryPluginDs();
List<XpackPluginsDatasourceVO> list = xpackPluginsDatasourceVOS.stream().filter(ele -> StringUtils.equals(ele.getType(), value.getType())).toList();
if (ObjectUtils.isNotEmpty(list)) {
XpackPluginsDatasourceVO first = list.getFirst();
prefix = first.getPrefix();
suffix = first.getSuffix();
} else {
DEException.throwException("当前数据源插件不存在");
}
}
}
Pattern pattern = Pattern.compile("(`.*?`)");
Matcher matcher = pattern.matcher(sql);
while (matcher.find()) {
String group = matcher.group();
String info = group.substring(1, group.length() - 1);
sql = sql.replaceAll(group, prefix + info + suffix);
}
}
this.removeSysParams = true;
sql = removeVariables(sql, ds.getType());
} catch (Exception e) {
e.printStackTrace();
}
return sql;
}
private String removeVariables(final String sql, String dsType) throws Exception {
String tmpSql = sql.replaceAll("(?m)^\\s*$[\n\r]{0,}", "");
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(tmpSql);
boolean hasVariables = false;
while (matcher.find()) {
hasVariables = true;
tmpSql = tmpSql.replace(matcher.group(), SubstitutedParams);
}
if (removeSysParams) {
for (Map<String, String> sysParam : sysParams) {
tmpSql = tmpSql.replaceAll(sysParam.get("replace"), sysParam.get("origin"));
}
pattern = Pattern.compile(regex2);
matcher = pattern.matcher(tmpSql);
while (matcher.find()) {
hasVariables = true;
tmpSql = tmpSql.replace(matcher.group(), SubstitutedParams);
}
} else {
pattern = Pattern.compile(regex2);
matcher = pattern.matcher(tmpSql);
sysParams.clear();
while (matcher.find()) {
hasVariables = true;
tmpSql = tmpSql.replace(matcher.group(), SysParamsSubstitutedParams + matcher.group().substring(1, matcher.group().length() - 1));
Map<String, String> sysParam = new HashMap<>();
sysParam.put("origin", matcher.group());
sysParam.put("replace", SysParamsSubstitutedParams + matcher.group().substring(1, matcher.group().length() - 1));
sysParams.add(sysParam);
}
}
if(!hasVariables && !sql.contains(SubstitutedParams)){
return sql;
}
Statement statement = CCJSqlParserUtil.parse(tmpSql);
Select select = (Select) statement;
if (CollectionUtils.isNotEmpty(select.getWithItemsList())) {
for (Iterator<WithItem> iter = select.getWithItemsList().iterator(); iter.hasNext(); ) {
WithItem withItem = iter.next();
ParenthesedSelect parenthesedSelect = (ParenthesedSelect) withItem.getSelect();
parenthesedSelect.setSelect((Select) CCJSqlParserUtil.parse(removeVariables(parenthesedSelect.getSelect().toString(), dsType)));
}
}
if (select.getSelectBody() instanceof PlainSelect) {
return handlePlainSelect((PlainSelect) select.getSelectBody(), select, dsType);
} else {
StringBuilder result = new StringBuilder();
SetOperationList setOperationList = (SetOperationList) select.getSelectBody();
for (int i = 0; i < setOperationList.getSelects().size(); i++) {
result.append(handlePlainSelect((PlainSelect) setOperationList.getSelects().get(i), null, dsType));
if (i < setOperationList.getSelects().size() - 1) {
result.append(" ").append(setOperationList.getOperations().get(i).toString()).append(" ");
}
}
return select.toString();
}
}
private String handlePlainSelect(PlainSelect plainSelect, Select statementSelect, String dsType) throws Exception {
handleSelectItems(plainSelect, dsType);
handleFromItems(plainSelect, dsType);
handleJoins(plainSelect, dsType);
handleHaving(plainSelect);
return handleWhere(plainSelect, statementSelect, dsType);
}
private void handleSelectItems(PlainSelect plainSelect, String dsType) throws Exception {
List<SelectItem<?>> selectItems = new ArrayList<>();
for (SelectItem selectItem : plainSelect.getSelectItems()) {
try {
if (selectItem.getExpression() instanceof ParenthesedSelect) {
ParenthesedSelect parenthesedSelect = (ParenthesedSelect) selectItem.getExpression();
parenthesedSelect.setSelect((Select) CCJSqlParserUtil.parse(removeVariables(((Select) selectItem.getExpression()).getPlainSelect().toString(), dsType)));
selectItem.setExpression(parenthesedSelect);
}
} catch (Exception e) {
e.printStackTrace();
}
selectItems.add(selectItem);
}
plainSelect.setSelectItems(selectItems);
}
private void handleFromItems(PlainSelect plainSelect, String dsType) throws Exception {
FromItem fromItem = plainSelect.getFromItem();
if (fromItem instanceof ParenthesedSelect) {
handleParenthesedSelect(fromItem, dsType);
plainSelect.setFromItem(fromItem);
} else {
if (fromItem instanceof ParenthesedFromItem) {
fromItem = ((ParenthesedFromItem) fromItem).getFromItem();
while (fromItem instanceof ParenthesedFromItem) {
fromItem = ((ParenthesedFromItem) fromItem).getFromItem();
}
handleParenthesedSelect(fromItem, dsType);
}
plainSelect.setFromItem(fromItem);
}
}
private void handleParenthesedSelect(FromItem fromItem, String dsType) throws Exception {
if (((ParenthesedSelect) fromItem).getSelect() instanceof SetOperationList) {
StringBuilder result = new StringBuilder();
SetOperationList setOperationList = (SetOperationList) ((ParenthesedSelect) fromItem).getSelect().getSelectBody();
for (int i = 0; i < setOperationList.getSelects().size(); i++) {
result.append(handlePlainSelect((PlainSelect) setOperationList.getSelects().get(i), null, dsType));
if (i < setOperationList.getSelects().size() - 1) {
result.append(" ").append(setOperationList.getOperations().get(i).toString()).append(" ");
}
}
} else {
PlainSelect selectBody = ((ParenthesedSelect) fromItem).getSelect().getPlainSelect();
Select subSelectTmp = (Select) CCJSqlParserUtil.parse(removeVariables(selectBody.toString(), dsType));
((ParenthesedSelect) fromItem).setSelect(subSelectTmp.getSelectBody());
if (dsType.equals(DatasourceConfiguration.DatasourceType.oracle.getType())) {
if (fromItem.getAlias() != null) {
fromItem.setAlias(new Alias(fromItem.getAlias().toString(), false));
}
} else {
if (fromItem.getAlias() == null) {
throw new Exception("Failed to parse sql, Every derived table must have its own alias");
}
fromItem.setAlias(new Alias(fromItem.getAlias().toString(), false));
}
}
}
private void handleJoins(PlainSelect plainSelect, String dsType) throws Exception {
List<Join> joins = plainSelect.getJoins();
if (joins != null) {
List<Join> joinsList = new ArrayList<>();
for (Join join : joins) {
FromItem rightItem = join.getRightItem();
Collection<Expression> exprs = join.getOnExpressions();
Collection<Expression> exprs2 = new ArrayList<>();
for (Expression expr : exprs) {
StringBuilder stringBuilder = new StringBuilder();
BinaryExpression binaryExpression = null;
try {
binaryExpression = (BinaryExpression) expr;
} catch (Exception e) {
}
if (binaryExpression != null) {
boolean hasSubBinaryExpression = binaryExpression instanceof AndExpression || binaryExpression instanceof OrExpression;
if (!hasSubBinaryExpression && !(binaryExpression.getLeftExpression() instanceof BinaryExpression) && !(binaryExpression.getLeftExpression() instanceof InExpression) && (hasVariable(binaryExpression.getLeftExpression().toString()) || hasVariable(binaryExpression.getRightExpression().toString()))) {
stringBuilder.append(handleSubstitutedSql(binaryExpression.toString()));
} else {
expr.accept(getExpressionDeParser(stringBuilder));
}
} else {
expr.accept(getExpressionDeParser(stringBuilder));
}
exprs2.add(CCJSqlParserUtil.parseCondExpression(stringBuilder.toString()));
}
join.setOnExpressions(exprs2);
if (rightItem instanceof ParenthesedSelect) {
try {
PlainSelect selectBody = ((ParenthesedSelect) rightItem).getPlainSelect();
Select subSelectTmp = (Select) CCJSqlParserUtil.parse(removeVariables(selectBody.toString(), dsType));
PlainSelect subPlainSelect = ((PlainSelect) subSelectTmp.getSelectBody());
((ParenthesedSelect) rightItem).setSelect(subPlainSelect);
} catch (Exception e) {
SetOperationList select = ((ParenthesedSelect) rightItem).getSetOperationList();
SetOperationList setOperationList = new SetOperationList();
setOperationList.setSelects(new ArrayList<>());
setOperationList.setOperations(select.getOperations());
for (Select selectSelect : select.getSelects()) {
Select subSelectTmp = (Select) CCJSqlParserUtil.parse(removeVariables(selectSelect.toString(), dsType));
setOperationList.getSelects().add(subSelectTmp);
}
((ParenthesedSelect) rightItem).setSelect(setOperationList);
}
if (dsType.equals(DatasourceConfiguration.DatasourceType.oracle.getType())) {
rightItem.setAlias(new Alias(rightItem.getAlias().toString(), false));
} else {
if (rightItem.getAlias() == null) {
throw new Exception("Failed to parse sql, Every derived table must have its own alias");
}
rightItem.setAlias(new Alias(rightItem.getAlias().toString(), false));
}
join.setRightItem(rightItem);
}
joinsList.add(join);
}
plainSelect.setJoins(joinsList);
}
}
private void handleHaving(PlainSelect plainSelect) throws Exception {
Expression expr = plainSelect.getHaving();
if (expr == null) {
return;
}
StringBuilder stringBuilder = new StringBuilder();
BinaryExpression binaryExpression = null;
try {
binaryExpression = (BinaryExpression) expr;
} catch (Exception e) {
}
if (binaryExpression != null) {
boolean hasSubBinaryExpression = binaryExpression instanceof AndExpression || binaryExpression instanceof OrExpression;
if (!hasSubBinaryExpression && !(binaryExpression.getLeftExpression() instanceof BinaryExpression) && !(binaryExpression.getLeftExpression() instanceof InExpression) && (hasVariable(binaryExpression.getLeftExpression().toString()) || hasVariable(binaryExpression.getRightExpression().toString()))) {
stringBuilder.append(handleSubstitutedSql(binaryExpression.toString()));
} else {
expr.accept(getExpressionDeParser(stringBuilder));
}
} else {
expr.accept(getExpressionDeParser(stringBuilder));
}
plainSelect.setHaving(CCJSqlParserUtil.parseCondExpression(stringBuilder.toString()));
}
private String handleWhere(PlainSelect plainSelect, Select statementSelect, String dsType) throws Exception {
Expression expr = plainSelect.getWhere();
if (expr == null) {
return handleWith(plainSelect, statementSelect, dsType);
}
StringBuilder stringBuilder = new StringBuilder();
BinaryExpression binaryExpression = null;
try {
binaryExpression = (BinaryExpression) expr;
} catch (Exception e) {
}
if (binaryExpression != null) {
boolean hasSubBinaryExpression = binaryExpression instanceof AndExpression || binaryExpression instanceof OrExpression;
if (!hasSubBinaryExpression && !(binaryExpression.getLeftExpression() instanceof BinaryExpression) && !(binaryExpression.getLeftExpression() instanceof InExpression) && (hasVariable(binaryExpression.getLeftExpression().toString()) || hasVariable(binaryExpression.getRightExpression().toString()))) {
stringBuilder.append(handleSubstitutedSql(binaryExpression.toString()));
} else {
expr.accept(getExpressionDeParser(stringBuilder));
}
} else {
expr.accept(getExpressionDeParser(stringBuilder));
}
plainSelect.setWhere(CCJSqlParserUtil.parseCondExpression(stringBuilder.toString()));
return handleWith(plainSelect, statementSelect, dsType);
}
private String handleWith(PlainSelect plainSelect, Select select, String dsType) throws Exception {
if (select != null && CollectionUtils.isNotEmpty(select.getWithItemsList())) {
for (Iterator<WithItem> iter = select.getWithItemsList().iterator(); iter.hasNext(); ) {
WithItem withItem = iter.next();
ParenthesedSelect parenthesedSelect = (ParenthesedSelect) withItem.getSelect();
parenthesedSelect.setSelect((Select) CCJSqlParserUtil.parse(removeVariables(parenthesedSelect.getSelect().toString(), dsType)));
}
}
return plainSelect.toString();
}
private ExpressionDeParser getExpressionDeParser(StringBuilder stringBuilder) {
ExpressionDeParser expressionDeParser = new ExpressionDeParser(null, stringBuilder) {
@Override
public void visit(Parenthesis parenthesis) {
getBuffer().append("(");
parenthesis.getExpression().accept(this);
getBuffer().append(")");
}
@Override
public void visit(OrExpression orExpression) {
visitBinaryExpr(orExpression, "OR");
}
@Override
public void visit(AndExpression andExpression) {
visitBinaryExpr(andExpression, andExpression.isUseOperator() ? " && " : " AND ");
}
@Override
public void visit(Between between) {
if (hasVariable(between.getBetweenExpressionStart().toString()) || hasVariable(between.getBetweenExpressionEnd().toString())) {
getBuffer().append(handleSubstitutedSql(between.toString()));
} else {
getBuffer().append(between.getLeftExpression()).append(" BETWEEN ").append(between.getBetweenExpressionStart()).append(" AND ").append(between.getBetweenExpressionEnd());
}
}
@Override
public void visit(MinorThan minorThan) {
if (hasVariable(minorThan.getLeftExpression().toString()) || hasVariable(minorThan.getRightExpression().toString())) {
getBuffer().append(handleSubstitutedSql(minorThan.toString()));
return;
}
getBuffer().append(minorThan.getLeftExpression());
getBuffer().append(" < ");
getBuffer().append(minorThan.getRightExpression());
}
@Override
public void visit(MinorThanEquals minorThan) {
if (hasVariable(minorThan.getLeftExpression().toString()) || hasVariable(minorThan.getRightExpression().toString())) {
getBuffer().append(handleSubstitutedSql(minorThan.toString()));
return;
}
getBuffer().append(minorThan.getLeftExpression());
getBuffer().append(" <= ");
getBuffer().append(minorThan.getRightExpression());
}
@Override
public void visit(GreaterThanEquals minorThan) {
if (hasVariable(minorThan.getLeftExpression().toString()) || hasVariable(minorThan.getRightExpression().toString())) {
getBuffer().append(handleSubstitutedSql(minorThan.toString()));
return;
}
getBuffer().append(minorThan.getLeftExpression());
getBuffer().append(" >= ");
getBuffer().append(minorThan.getRightExpression());
}
@Override
public void visit(GreaterThan greaterThan) {
if (hasVariable(greaterThan.getLeftExpression().toString()) || hasVariable(greaterThan.getRightExpression().toString())) {
getBuffer().append(handleSubstitutedSql(greaterThan.toString()));
return;
}
getBuffer().append(greaterThan.getLeftExpression());
getBuffer().append(" > ");
getBuffer().append(greaterThan.getRightExpression());
}
@Override
public void visit(ExpressionList expressionList) {
for (Iterator<Expression> iter = expressionList.getExpressions().iterator(); iter.hasNext(); ) {
Expression expression = iter.next();
expression.accept(this);
if (iter.hasNext()) {
buffer.append(", ");
}
}
}
@Override
public void visit(LikeExpression likeExpression) {
if (hasVariable(likeExpression.toString())) {
getBuffer().append(handleSubstitutedSql(likeExpression.toString()));
return;
}
visitBinaryExpression(likeExpression, (likeExpression.isNot() ? " NOT" : "") + (likeExpression.isCaseInsensitive() ? " ILIKE " : " LIKE "));
if (likeExpression.getEscape() != null) {
buffer.append(" ESCAPE '").append(likeExpression.getEscape()).append('\'');
}
}
@Override
public void visit(InExpression inExpression) {
if (inExpression.getRightExpression() != null && hasVariable(inExpression.getRightExpression().toString()) && !(inExpression.getRightExpression() instanceof ParenthesedSelect)) {
stringBuilder.append(handleSubstitutedSqlForIn(inExpression.toString()));
return;
}
inExpression.getLeftExpression().accept(this);
if (inExpression.isNot()) {
getBuffer().append(" " + " NOT IN " + " ");
} else {
getBuffer().append(" IN ");
}
if (inExpression.getRightExpression() != null && inExpression.getRightExpression() instanceof ParenthesedSelect) {
try {
ParenthesedSelect subSelect = (ParenthesedSelect) inExpression.getRightExpression();
Select select = (Select) CCJSqlParserUtil.parse(removeVariables(subSelect.getPlainSelect().toString(), ""));
subSelect.setSelect(select);
inExpression.setRightExpression(subSelect);
} catch (Exception e) {
e.printStackTrace();
}
inExpression.getRightExpression().accept(this);
}
if (inExpression.getRightExpression() instanceof ParenthesedExpressionList) {
buffer.append(inExpression.getRightExpression());
}
}
@Override
public void visit(ParenthesedSelect subSelect) {
StringBuilder stringBuilder = new StringBuilder();
Expression in = ((PlainSelect) subSelect.getSelectBody()).getWhere();
if (in instanceof BinaryExpression && hasVariable(in.toString())) {
stringBuilder.append(SubstitutedParams);
} else {
in.accept(getExpressionDeParser(stringBuilder));
}
try {
Expression where = CCJSqlParserUtil.parseCondExpression(stringBuilder.toString());
((PlainSelect) subSelect.getSelectBody()).setWhere(where);
getBuffer().append(subSelect.getSelectBody());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void visit(Select selectBody) {
getBuffer().append(selectBody.toString());
}
private void visitBinaryExpr(BinaryExpression expr, String operator) {
boolean hasSubBinaryExpression = false;
if (expr.getLeftExpression() instanceof Parenthesis) {
try {
Parenthesis parenthesis = (Parenthesis) expr.getLeftExpression();
BinaryExpression leftBinaryExpression = (BinaryExpression) parenthesis.getExpression();
hasSubBinaryExpression = leftBinaryExpression instanceof AndExpression || leftBinaryExpression instanceof OrExpression;
} catch (Exception e) {
e.printStackTrace();
}
}
if (expr.getLeftExpression() instanceof BinaryExpression) {
try {
BinaryExpression leftBinaryExpression = (BinaryExpression) expr.getLeftExpression();
hasSubBinaryExpression = leftBinaryExpression instanceof AndExpression || leftBinaryExpression instanceof OrExpression;
} catch (Exception e) {
e.printStackTrace();
}
}
if ((expr.getLeftExpression() instanceof BinaryExpression || expr.getLeftExpression() instanceof Parenthesis) && !hasSubBinaryExpression && hasVariable(expr.getLeftExpression().toString())) {
getBuffer().append(handleSubstitutedSql(expr.getLeftExpression().toString()));
} else {
expr.getLeftExpression().accept(this);
}
getBuffer().append(" " + operator + " ");
hasSubBinaryExpression = false;
if (expr.getRightExpression() instanceof Parenthesis) {
try {
Parenthesis parenthesis = (Parenthesis) expr.getRightExpression();
BinaryExpression rightBinaryExpression = (BinaryExpression) parenthesis.getExpression();
hasSubBinaryExpression = rightBinaryExpression instanceof AndExpression || rightBinaryExpression instanceof OrExpression;
} catch (Exception e) {
LogUtil.error("Failed parse sql", e);
}
}
if (expr.getRightExpression() instanceof BinaryExpression) {
try {
BinaryExpression rightBinaryExpression = (BinaryExpression) expr.getRightExpression();
hasSubBinaryExpression = rightBinaryExpression instanceof AndExpression || rightBinaryExpression instanceof OrExpression;
} catch (Exception e) {
e.printStackTrace();
}
}
if ((expr.getRightExpression() instanceof Parenthesis || expr.getRightExpression() instanceof BinaryExpression || expr.getRightExpression() instanceof Function) && !hasSubBinaryExpression && hasVariable(expr.getRightExpression().toString())) {
getBuffer().append(handleSubstitutedSql(expr.getRightExpression().toString()));
} else {
expr.getRightExpression().accept(this);
}
}
};
return expressionDeParser;
}
private boolean hasVariable(String sql) {
return sql.contains(SubstitutedParams) || (!removeSysParams && sql.contains(SysParamsSubstitutedParams));
}
private void getDependencies(SqlNode sqlNode, Boolean fromOrJoin) {
if (sqlNode == null) {
return;
}
if (sqlNode.getKind() == JOIN) {
SqlJoin sqlKind = (SqlJoin) sqlNode;
} else if (sqlNode.getKind() == IDENTIFIER) {
} else if (sqlNode.getKind() == AS) {
SqlBasicCall sqlKind = (SqlBasicCall) sqlNode;
} else if (sqlNode.getKind() == SELECT) {
SqlSelect sqlKind = (SqlSelect) sqlNode;
List<SqlNode> list = sqlKind.getSelectList().getList();
for (SqlNode i : list) {
getDependencies(i, false);
}
SqlNode from = sqlKind.getFrom().accept(getSqlShuttle());
sqlKind.setFrom(from);
if (sqlKind.getWhere() != null) {
SqlNode newWhere = sqlKind.getWhere().accept(getSqlShuttle());
sqlKind.setWhere(newWhere);
}
} else if (sqlNode.getKind() == ORDER_BY) {
SqlOrderBy sqlKind = (SqlOrderBy) sqlNode;
List<SqlNode> operandList = sqlKind.getOperandList();
for (int i = 0; i < operandList.size(); i++) {
getDependencies(operandList.get(i), false);
}
} else if (sqlNode.getKind() == UNION) {
SqlBasicCall sqlKind = (SqlBasicCall) sqlNode;
if (sqlKind.getOperandList().size() >= 2) {
for (int i = 0; i < sqlKind.getOperandList().size(); i++) {
getDependencies(sqlKind.getOperandList().get(i), false);
}
}
}
}
private SqlShuttle getSqlShuttle() {
return new SqlShuttle() {
@Override
public @Nullable SqlNode visit(final SqlCall call) {
CallCopyingArgHandler argHandler = new CallCopyingArgHandler(call, false);
call.getOperator().acceptCall(this, call, false, argHandler);
if (argHandler.result().toString().contains(SubstitutedParams)) {
SqlNode sqlNode1 = null;
try {
sqlNode1 = SqlParser.create(SubstitutedSql).parseExpression();
} catch (Exception e) {
}
return sqlNode1;
}
return argHandler.result();
}
};
}
private String transFilter(SqlVariableDetails sqlVariableDetails, Map<Long, DatasourceSchemaDTO> dsMap) {
if (sqlVariableDetails.getOperator().equals("in")) {
if (StringUtils.equalsIgnoreCase(dsMap.entrySet().iterator().next().getValue().getType(), DatasourceConfiguration.DatasourceType.sqlServer.getType())
&& sqlVariableDetails.getDeType() == 0) {
return "N'" + String.join("', N'", sqlVariableDetails.getValue()) + "'";
} else {
if (sqlVariableDetails.getDeType() == 2 || sqlVariableDetails.getDeType() == 3) {
return String.join(",", sqlVariableDetails.getValue());
} else {
return "'" + String.join("','", sqlVariableDetails.getValue()) + "'";
}
}
} else if (sqlVariableDetails.getOperator().equals("between")) {
if (sqlVariableDetails.getDeType() == 1) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(sqlVariableDetails.getType().size() > 1 ? (String) sqlVariableDetails.getType().get(1).replace("DD", "dd").replace("YYYY", "yyyy") : "yyyy");
if (StringUtils.endsWith(sqlVariableDetails.getId(), START_END_SEPARATOR)) {
return simpleDateFormat.format(new Date(Long.parseLong((String) sqlVariableDetails.getValue().get(1))));
} else {
return simpleDateFormat.format(new Date(Long.parseLong((String) sqlVariableDetails.getValue().get(0))));
}
} else {
if (StringUtils.endsWith(sqlVariableDetails.getId(), START_END_SEPARATOR)) {
return sqlVariableDetails.getValue().get(1);
} else {
return sqlVariableDetails.getValue().get(0);
}
}
} else {
return (String) sqlVariableDetails.getValue().get(0);
}
}
private String handleSubstitutedSql(String sql) {
if (sql.contains(SysParamsSubstitutedParams) && userEntity != null) {
sql = sql.replace(SysParamsSubstitutedParams + "sysParams.userId", userEntity.getAccount());
sql = sql.replace(SysParamsSubstitutedParams + "sysParams.userEmail", userEntity.getEmail());
sql = sql.replace(SysParamsSubstitutedParams + "sysParams.userName", userEntity.getName());
for (SysVariableValueItem variable : userEntity.getVariables()) {
String value = null;
if (!variable.isValid()) {
continue;
}
if (variable.getSysVariableDto().getType().equalsIgnoreCase("text")) {
for (SysVariableValueDto sysVariableValueDto : variable.getValueList()) {
if (variable.getVariableValueIds().contains(sysVariableValueDto.getId().toString())) {
value = sysVariableValueDto.getValue();
break;
}
}
} else {
value = variable.getVariableValue();
}
if (StringUtils.isNotEmpty(value)) {
sql = sql.replace(SysParamsSubstitutedParams + variable.getVariableId(), value);
}
}
return sql;
} else {
return SubstitutedSql;
}
}
private String handleSubstitutedSqlForIn(String sql) {
if (sql.contains(SysParamsSubstitutedParams) && userEntity != null) {
for (SysVariableValueItem variable : userEntity.getVariables()) {
List<String> values = new ArrayList<>();
if (!variable.isValid()) {
continue;
}
if (variable.getSysVariableDto().getType().equalsIgnoreCase("text")) {
for (SysVariableValueDto sysVariableValueDto : variable.getValueList()) {
if (variable.getVariableValueIds().contains(sysVariableValueDto.getId().toString())) {
values.add(sysVariableValueDto.getValue());
}
}
}
if (CollectionUtils.isNotEmpty(values)) {
sql = sql.replace(SysParamsSubstitutedParams + variable.getVariableId(), "'" + String.join("','", values) + "'");
}
}
return sql;
} else {
return SubstitutedSql;
}
}
}

View File

@ -0,0 +1,34 @@
package io.dataease.commons.utils;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
public class UrlTestUtils {
public static boolean testUrlWithTimeOut(String urlString, int timeOutMillSeconds) {
try {
URL url = new URL(urlString);
URLConnection co = url.openConnection();
co.setConnectTimeout(timeOutMillSeconds);
co.connect();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static boolean isURLAvailable(String urlString) {
try {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("HEAD");
int responseCode = connection.getResponseCode();
return responseCode == HttpURLConnection.HTTP_OK;
} catch (IOException e) {
return false;
}
}
}

View File

@ -0,0 +1,22 @@
package io.dataease.config;
import com.fit2cloud.autoconfigure.QuartzAutoConfiguration;
import io.dataease.utils.CommonThreadPool;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@AutoConfigureBefore(QuartzAutoConfiguration.class)
public class CommonConfig {
@Bean(destroyMethod = "shutdown")
public CommonThreadPool resourcePoolThreadPool() {
CommonThreadPool commonThreadPool = new CommonThreadPool();
commonThreadPool.setCorePoolSize(50);
commonThreadPool.setMaxQueueSize(100);
commonThreadPool.setKeepAliveSeconds(3600);
return commonThreadPool;
}
}

View File

@ -0,0 +1,50 @@
package io.dataease.config;
import io.dataease.constant.AuthConstant;
import io.dataease.share.interceptor.LinkInterceptor;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import static io.dataease.constant.StaticResourceConstants.*;
import static io.dataease.utils.StaticResourceUtils.ensureBoth;
import static io.dataease.utils.StaticResourceUtils.ensureSuffix;
@Configuration
public class DeMvcConfig implements WebMvcConfigurer {
@Resource
private LinkInterceptor linkInterceptor;
/**
* Configuring static resource path
*
* @param registry registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String workDir = FILE_PROTOCOL + ensureSuffix(WORK_DIR, FILE_SEPARATOR);
String uploadUrlPattern = ensureBoth(URL_SEPARATOR + UPLOAD_URL_PREFIX, AuthConstant.DE_API_PREFIX, URL_SEPARATOR) + "**";
registry.addResourceHandler(uploadUrlPattern).addResourceLocations(workDir);
// map
String mapDir = FILE_PROTOCOL + ensureSuffix(MAP_DIR, FILE_SEPARATOR);
String mapUrlPattern = ensureBoth(MAP_URL, AuthConstant.DE_API_PREFIX, URL_SEPARATOR) + "**";
registry.addResourceHandler(mapUrlPattern).addResourceLocations(mapDir);
String geoDir = FILE_PROTOCOL + ensureSuffix(CUSTOM_MAP_DIR, FILE_SEPARATOR);
String geoUrlPattern = ensureBoth(GEO_URL, AuthConstant.DE_API_PREFIX, URL_SEPARATOR) + "**";
registry.addResourceHandler(geoUrlPattern).addResourceLocations(geoDir);
String i18nDir = FILE_PROTOCOL + ensureSuffix(I18N_DIR, FILE_SEPARATOR);
String i18nUrlPattern = ensureBoth(I18N_URL, AuthConstant.DE_API_PREFIX, URL_SEPARATOR) + "**";
registry.addResourceHandler(i18nUrlPattern).addResourceLocations(i18nDir);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(linkInterceptor).addPathPatterns("/**");
}
}

View File

@ -0,0 +1,30 @@
package io.dataease.config;
import io.dataease.commons.utils.MybatisInterceptorConfig;
import io.dataease.datasource.dao.auto.entity.CoreDatasource;
import io.dataease.datasource.dao.auto.entity.CoreDeEngine;
import io.dataease.interceptor.MybatisInterceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableTransactionManagement
public class MybatisConfig {
@Bean
@ConditionalOnMissingBean
public MybatisInterceptor dbInterceptor() {
MybatisInterceptor interceptor = new MybatisInterceptor();
List<MybatisInterceptorConfig> configList = new ArrayList<>();
configList.add(new MybatisInterceptorConfig(CoreDeEngine.class, "configuration"));
configList.add(new MybatisInterceptorConfig(CoreDatasource.class, "configuration"));
interceptor.setInterceptorConfigList(configList);
return interceptor;
}
}

View File

@ -0,0 +1,102 @@
package io.dataease.copilot.api;
import io.dataease.api.copilot.dto.ReceiveDTO;
import io.dataease.api.copilot.dto.SendDTO;
import io.dataease.copilot.dao.auto.entity.CoreCopilotConfig;
import io.dataease.copilot.dao.auto.mapper.CoreCopilotConfigMapper;
import io.dataease.exception.DEException;
import io.dataease.utils.HttpClientConfig;
import io.dataease.utils.HttpClientUtil;
import io.dataease.utils.JsonUtil;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.json.simple.JSONObject;
import org.springframework.stereotype.Component;
import java.util.Base64;
import java.util.Map;
/**
* @Author Junjun
*/
@Component
public class CopilotAPI {
public static final String TOKEN = "/auth/token/license";
public static final String FREE_TOKEN = "/auth/token/free";
public static final String API = "/copilot/v1";
public static final String CHART = "/generate-chart";
public static final String RATE_LIMIT = "/rate-limit";
@Resource
private CoreCopilotConfigMapper coreCopilotConfigMapper;
public String basicAuth(String userName, String password) {
String auth = userName + ":" + password;
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
return "Basic " + encodedAuth;
}
public String bearerAuth(String token) {
return "Bearer " + token;
}
public CoreCopilotConfig getConfig() {
CoreCopilotConfig coreCopilotConfig = coreCopilotConfigMapper.selectById(1);
coreCopilotConfig.setPwd(new String(Base64.getDecoder().decode(coreCopilotConfig.getPwd())));
return coreCopilotConfig;
}
public String getToken(String license) throws Exception {
String url = getConfig().getCopilotUrl() + TOKEN;
JSONObject json = new JSONObject();
json.put("licenseText", license);
HttpClientConfig httpClientConfig = new HttpClientConfig();
String tokenJson = HttpClientUtil.post(url, json.toString(), httpClientConfig);
return (String) JsonUtil.parse(tokenJson, Map.class).get("accessToken");
}
public String getFreeToken() throws Exception {
String url = getConfig().getCopilotUrl() + FREE_TOKEN;
HttpClientConfig httpClientConfig = new HttpClientConfig();
String tokenJson = HttpClientUtil.post(url, "", httpClientConfig);
return (String) JsonUtil.parse(tokenJson, Map.class).get("accessToken");
}
public ReceiveDTO generateChart(String token, SendDTO sendDTO) {
String url = getConfig().getCopilotUrl() + API + CHART;
String request = (String) JsonUtil.toJSONString(sendDTO);
HttpClientConfig httpClientConfig = new HttpClientConfig();
httpClientConfig.addHeader("Authorization", bearerAuth(token));
String result = HttpClientUtil.post(url, request, httpClientConfig);
return JsonUtil.parseObject(result, ReceiveDTO.class);
}
public void checkRateLimit(String token) {
String url = getConfig().getCopilotUrl() + API + RATE_LIMIT;
HttpClientConfig httpClientConfig = new HttpClientConfig();
httpClientConfig.addHeader("Authorization", bearerAuth(token));
HttpResponse httpResponse = HttpClientUtil.postWithHeaders(url, null, httpClientConfig);
Header[] allHeaders = httpResponse.getAllHeaders();
String limit = "";
String seconds = "";
for (Header header : allHeaders) {
if (StringUtils.equalsIgnoreCase(header.getName(), "x-rate-limit-remaining")) {
limit = header.getValue();
}
if (StringUtils.equalsIgnoreCase(header.getName(), "x-rate-limit-retry-after-seconds")) {
seconds = header.getValue();
}
}
if (Long.parseLong(limit) <= 0) {
DEException.throwException(String.format("当前请求频率已达上限,请在%s秒后重试", seconds));
}
}
}

View File

@ -0,0 +1,71 @@
package io.dataease.copilot.dao.auto.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author fit2cloud
* @since 2024-07-08
*/
@TableName("core_copilot_config")
public class CoreCopilotConfig implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
private String copilotUrl;
private String username;
private String pwd;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCopilotUrl() {
return copilotUrl;
}
public void setCopilotUrl(String copilotUrl) {
this.copilotUrl = copilotUrl;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "CoreCopilotConfig{" +
"id = " + id +
", copilotUrl = " + copilotUrl +
", username = " + username +
", pwd = " + pwd +
"}";
}
}

View File

@ -0,0 +1,276 @@
package io.dataease.copilot.dao.auto.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author fit2cloud
* @since 2024-07-04
*/
@TableName("core_copilot_msg")
public class CoreCopilotMsg implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 数据集ID
*/
private Long datasetGroupId;
/**
* user or api
*/
private String msgType;
/**
* mysql oracle ...
*/
private String engineType;
/**
* create sql
*/
private String schemaSql;
/**
* 用户提问
*/
private String question;
/**
* 历史信息
*/
private String history;
/**
* copilot 返回 sql
*/
private String copilotSql;
/**
* copilot 返回信息
*/
private String apiMsg;
/**
* sql 状态
*/
private Integer sqlOk;
/**
* chart 状态
*/
private Integer chartOk;
/**
* chart 内容
*/
private String chart;
/**
* 视图数据
*/
private String chartData;
/**
* 执行请求的SQL
*/
private String execSql;
/**
* msg状态0失败 1成功
*/
private Integer msgStatus;
/**
* de错误信息
*/
private String errMsg;
/**
* 创建时间
*/
private Long createTime;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Long getDatasetGroupId() {
return datasetGroupId;
}
public void setDatasetGroupId(Long datasetGroupId) {
this.datasetGroupId = datasetGroupId;
}
public String getMsgType() {
return msgType;
}
public void setMsgType(String msgType) {
this.msgType = msgType;
}
public String getEngineType() {
return engineType;
}
public void setEngineType(String engineType) {
this.engineType = engineType;
}
public String getSchemaSql() {
return schemaSql;
}
public void setSchemaSql(String schemaSql) {
this.schemaSql = schemaSql;
}
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
public String getHistory() {
return history;
}
public void setHistory(String history) {
this.history = history;
}
public String getCopilotSql() {
return copilotSql;
}
public void setCopilotSql(String copilotSql) {
this.copilotSql = copilotSql;
}
public String getApiMsg() {
return apiMsg;
}
public void setApiMsg(String apiMsg) {
this.apiMsg = apiMsg;
}
public Integer getSqlOk() {
return sqlOk;
}
public void setSqlOk(Integer sqlOk) {
this.sqlOk = sqlOk;
}
public Integer getChartOk() {
return chartOk;
}
public void setChartOk(Integer chartOk) {
this.chartOk = chartOk;
}
public String getChart() {
return chart;
}
public void setChart(String chart) {
this.chart = chart;
}
public String getChartData() {
return chartData;
}
public void setChartData(String chartData) {
this.chartData = chartData;
}
public String getExecSql() {
return execSql;
}
public void setExecSql(String execSql) {
this.execSql = execSql;
}
public Integer getMsgStatus() {
return msgStatus;
}
public void setMsgStatus(Integer msgStatus) {
this.msgStatus = msgStatus;
}
public String getErrMsg() {
return errMsg;
}
public void setErrMsg(String errMsg) {
this.errMsg = errMsg;
}
public Long getCreateTime() {
return createTime;
}
public void setCreateTime(Long createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "CoreCopilotMsg{" +
"id = " + id +
", userId = " + userId +
", datasetGroupId = " + datasetGroupId +
", msgType = " + msgType +
", engineType = " + engineType +
", schemaSql = " + schemaSql +
", question = " + question +
", history = " + history +
", copilotSql = " + copilotSql +
", apiMsg = " + apiMsg +
", sqlOk = " + sqlOk +
", chartOk = " + chartOk +
", chart = " + chart +
", chartData = " + chartData +
", execSql = " + execSql +
", msgStatus = " + msgStatus +
", errMsg = " + errMsg +
", createTime = " + createTime +
"}";
}
}

View File

@ -0,0 +1,74 @@
package io.dataease.copilot.dao.auto.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author fit2cloud
* @since 2024-07-08
*/
@TableName("core_copilot_token")
public class CoreCopilotToken implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
* free or license
*/
private String type;
private String token;
private Long updateTime;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Long getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Long updateTime) {
this.updateTime = updateTime;
}
@Override
public String toString() {
return "CoreCopilotToken{" +
"id = " + id +
", type = " + type +
", token = " + token +
", updateTime = " + updateTime +
"}";
}
}

View File

@ -0,0 +1,18 @@
package io.dataease.copilot.dao.auto.mapper;
import io.dataease.copilot.dao.auto.entity.CoreCopilotConfig;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author fit2cloud
* @since 2024-07-08
*/
@Mapper
public interface CoreCopilotConfigMapper extends BaseMapper<CoreCopilotConfig> {
}

View File

@ -0,0 +1,18 @@
package io.dataease.copilot.dao.auto.mapper;
import io.dataease.copilot.dao.auto.entity.CoreCopilotMsg;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author fit2cloud
* @since 2024-07-04
*/
@Mapper
public interface CoreCopilotMsgMapper extends BaseMapper<CoreCopilotMsg> {
}

View File

@ -0,0 +1,18 @@
package io.dataease.copilot.dao.auto.mapper;
import io.dataease.copilot.dao.auto.entity.CoreCopilotToken;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author fit2cloud
* @since 2024-07-08
*/
@Mapper
public interface CoreCopilotTokenMapper extends BaseMapper<CoreCopilotToken> {
}

View File

@ -0,0 +1,487 @@
package io.dataease.copilot.manage;
import com.fasterxml.jackson.core.type.TypeReference;
import io.dataease.api.copilot.dto.*;
import io.dataease.api.dataset.union.DatasetGroupInfoDTO;
import io.dataease.api.dataset.union.UnionDTO;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.copilot.api.CopilotAPI;
import io.dataease.dataset.dao.auto.entity.CoreDatasetGroup;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetGroupMapper;
import io.dataease.dataset.manage.DatasetDataManage;
import io.dataease.dataset.manage.DatasetSQLManage;
import io.dataease.dataset.manage.DatasetTableFieldManage;
import io.dataease.dataset.manage.PermissionManage;
import io.dataease.dataset.utils.FieldUtils;
import io.dataease.constant.DeTypeConstants;
import io.dataease.engine.utils.Utils;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.constant.SqlPlaceholderConstants;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceRequest;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.dto.TableField;
import io.dataease.extensions.datasource.factory.ProviderFactory;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.view.dto.ColumnPermissionItem;
import io.dataease.i18n.Translator;
import io.dataease.license.dao.po.LicensePO;
import io.dataease.license.manage.F2CLicManage;
import io.dataease.license.utils.LicenseUtil;
import io.dataease.utils.AuthUtils;
import io.dataease.utils.BeanUtils;
import io.dataease.utils.JsonUtil;
import jakarta.annotation.Resource;
import org.apache.calcite.config.Lex;
import org.apache.calcite.sql.SqlDialect;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Author Junjun
*/
@Component
public class CopilotManage {
@Resource
private DatasetSQLManage datasetSQLManage;
@Resource
private CoreDatasetGroupMapper coreDatasetGroupMapper;
@Resource
private DatasetTableFieldManage datasetTableFieldManage;
@Resource
private DatasetDataManage datasetDataManage;
@Resource
private PermissionManage permissionManage;
@Resource
private MsgManage msgManage;
private static Logger logger = LoggerFactory.getLogger(CopilotManage.class);
@Resource
private TokenManage tokenManage;
@Resource
private CopilotAPI copilotAPI;
@Resource
private F2CLicManage f2CLicManage;
private String[] chartType = {"bar", "line", "pie"};
public MsgDTO chat(MsgDTO msgDTO) throws Exception {
CoreDatasetGroup coreDatasetGroup = coreDatasetGroupMapper.selectById(msgDTO.getDatasetGroupId());
if (coreDatasetGroup == null) {
return null;
}
DatasetGroupInfoDTO dto = new DatasetGroupInfoDTO();
BeanUtils.copyBean(dto, coreDatasetGroup);
dto.setUnionSql(null);
List<UnionDTO> unionDTOList = JsonUtil.parseList(coreDatasetGroup.getInfo(), new TypeReference<>() {
});
dto.setUnion(unionDTOList);
// 获取field
List<DatasetTableFieldDTO> dsFields = datasetTableFieldManage.selectByDatasetGroupId(msgDTO.getDatasetGroupId());
List<DatasetTableFieldDTO> allFields = dsFields.stream().filter(ele -> ele.getExtField() == 0)
.map(ele -> {
DatasetTableFieldDTO datasetTableFieldDTO = new DatasetTableFieldDTO();
BeanUtils.copyBean(datasetTableFieldDTO, ele);
datasetTableFieldDTO.setFieldShortName(ele.getDataeaseName());
return datasetTableFieldDTO;
}).collect(Collectors.toList());
Map<String, Object> sqlMap = datasetSQLManage.getUnionSQLForEdit(dto, null);
String sql = (String) sqlMap.get("sql");// 数据集原始SQL
Map<Long, DatasourceSchemaDTO> dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
boolean crossDs = Utils.isCrossDs(dsMap);
if (crossDs) {
DEException.throwException(Translator.get("i18n_copilot_cross_ds_error"));
}
// 调用copilot service 获取SQL和chart struct将返回SQL中表名替换成数据集SQL
// deSendDTO 构建schema和engine
if (ObjectUtils.isEmpty(dsMap)) {
DEException.throwException("No datasource");
}
DatasourceSchemaDTO ds = dsMap.entrySet().iterator().next().getValue();
String type = engine(ds.getType());// 数据库类型如mysqloracle等可能需要映射成copilot需要的类型
datasetDataManage.buildFieldName(sqlMap, allFields);
List<String> strings = transCreateTableFields(allFields);
String createSql = "CREATE TABLE de_tmp_table (" + StringUtils.join(strings, ",") + ")";
logger.debug("Copilot Schema SQL: " + createSql);
// PerMsgDTO perMsgDTO = new PerMsgDTO();
msgDTO.setDatasetGroupId(dto.getId());
msgDTO.setMsgType("user");
msgDTO.setEngineType(type);
msgDTO.setSchemaSql(createSql);
msgDTO.setHistory(msgDTO.getHistory());
msgDTO.setMsgStatus(1);
msgManage.save(msgDTO);// 到这里为止提问所需参数构建完毕往数据库插入一条提问记录
DESendDTO deSendDTO = new DESendDTO();
deSendDTO.setDatasetGroupId(dto.getId());
deSendDTO.setQuestion(msgDTO.getQuestion());
deSendDTO.setHistory(msgDTO.getHistory());
deSendDTO.setEngine(type);
deSendDTO.setSchema(createSql);
// do copilot chat
ReceiveDTO receiveDTO = copilotChat(deSendDTO);
// copilot 请求结束继续de获取数据
// 获取数据集相关行列权限最终再套一层SQL
Map<String, ColumnPermissionItem> desensitizationList = new HashMap<>();
allFields = permissionManage.filterColumnPermissions(allFields, desensitizationList, dto.getId(), null);
if (ObjectUtils.isEmpty(allFields)) {
DEException.throwException(Translator.get("i18n_no_column_permission"));
}
List<String> dsList = new ArrayList<>();
for (Map.Entry<Long, DatasourceSchemaDTO> next : dsMap.entrySet()) {
dsList.add(next.getValue().getType());
}
if (!crossDs) {
sql = Utils.replaceSchemaAlias(sql, dsMap);
}
Provider provider;
if (crossDs) {
provider = ProviderFactory.getDefaultProvider();
} else {
provider = ProviderFactory.getProvider(dsList.getFirst());
}
// List<DataSetRowPermissionsTreeDTO> rowPermissionsTree = new ArrayList<>();
// TokenUserBO user = AuthUtils.getUser();
// if (user != null) {
// rowPermissionsTree = permissionManage.getRowPermissionsTree(dto.getId(), user.getUserId());
// }
// build query sql
// SQLMeta sqlMeta = new SQLMeta();
// Table2SQLObj.table2sqlobj(sqlMeta, null, "(" + sql + ")", crossDs);
// Field2SQLObj.field2sqlObj(sqlMeta, allFields, allFields, crossDs, dsMap);
// WhereTree2Str.transFilterTrees(sqlMeta, rowPermissionsTree, allFields, crossDs, dsMap);
// Order2SQLObj.getOrders(sqlMeta, dto.getSortFields(), allFields, crossDs, dsMap);
// String querySQL = SQLProvider.createQuerySQL(sqlMeta, false, false, needOrder);
// querySQL = provider.rebuildSQL(querySQL, sqlMeta, crossDs, dsMap);
// logger.debug("preview sql: " + querySQL);
// 无法加行权限的情况下直接用sql
String querySQL = sql;
String copilotSQL = receiveDTO.getSql();
// 用calcite尝试将SQL转方言如果失败了就按照原SQL执行
// copilotSQL = transSql(type, copilotSQL, provider, receiveDTO);
// 通过数据源请求数据
// 调用数据源的calcite获得data
DatasourceRequest datasourceRequest = new DatasourceRequest();
datasourceRequest.setDsList(dsMap);
String s = "";
Map<String, Object> data;
try {
s = copilotSQL
.replaceAll(SqlPlaceholderConstants.KEYWORD_PREFIX_REGEX + "de_tmp_table" + SqlPlaceholderConstants.KEYWORD_SUFFIX_REGEX, "(" + querySQL + ")")
.replaceAll(SqlPlaceholderConstants.KEYWORD_PREFIX_REGEX + "DE_TMP_TABLE" + SqlPlaceholderConstants.KEYWORD_SUFFIX_REGEX, "(" + querySQL + ")");
logger.debug("copilot sql: " + s);
datasourceRequest.setQuery(s);
data = provider.fetchResultField(datasourceRequest);
} catch (Exception e) {
try {
s = copilotSQL
.replaceAll(SqlPlaceholderConstants.KEYWORD_PREFIX_REGEX + "de_tmp_table" + SqlPlaceholderConstants.KEYWORD_SUFFIX_REGEX, "(" + querySQL + ") tmp")
.replaceAll(SqlPlaceholderConstants.KEYWORD_PREFIX_REGEX + "DE_TMP_TABLE" + SqlPlaceholderConstants.KEYWORD_SUFFIX_REGEX, "(" + querySQL + ") tmp");
logger.debug("copilot sql: " + s);
datasourceRequest.setQuery(s);
data = provider.fetchResultField(datasourceRequest);
} catch (Exception e1) {
// 如果异常则获取最后一条成功的history
MsgDTO lastSuccessMsg = msgManage.getLastSuccessMsg(AuthUtils.getUser().getUserId(), dto.getId());
// 请求数据出错记录错误信息和copilot返回的信息
MsgDTO result = new MsgDTO();
result.setDatasetGroupId(dto.getId());
result.setMsgType("api");
result.setHistory(lastSuccessMsg == null ? new ArrayList<>() : lastSuccessMsg.getHistory());
result.setCopilotSql(receiveDTO.getSql());
result.setApiMsg(receiveDTO.getApiMessage());
result.setSqlOk(receiveDTO.getSqlOk() ? 1 : 0);
result.setChartOk(receiveDTO.getChartOk() ? 1 : 0);
result.setChart(receiveDTO.getChart());
result.setExecSql(s);
result.setMsgStatus(0);
result.setErrMsg(e1.getMessage());
msgManage.save(result);
return result;
}
}
List<TableField> fields = (List<TableField>) data.get("fields");
fields = transField(fields);
Map<String, Object> map = new LinkedHashMap<>();
// 重新构造data
Map<String, Object> previewData = buildPreviewData(data, fields, desensitizationList);
map.put("data", previewData);
map.put("sql", Base64.getEncoder().encodeToString(s.getBytes()));
// 重构chart结构补全缺失字段
rebuildChart(receiveDTO, fields);
MsgDTO result = new MsgDTO();
result.setDatasetGroupId(dto.getId());
result.setMsgType("api");
result.setHistory(receiveDTO.getHistory());
result.setCopilotSql(receiveDTO.getSql());
result.setApiMsg(receiveDTO.getApiMessage());
result.setSqlOk(receiveDTO.getSqlOk() ? 1 : 0);
result.setChartOk(receiveDTO.getChartOk() ? 1 : 0);
result.setChart(receiveDTO.getChart());
result.setChartData(map);
result.setExecSql(s);
result.setMsgStatus(1);
msgManage.save(result);
return result;
}
public ReceiveDTO copilotChat(DESendDTO deSendDTO) throws Exception {
boolean valid = LicenseUtil.licenseValid();
// call copilot service
TokenDTO tokenDTO = tokenManage.getToken(valid);
ReceiveDTO receiveDTO;
if (StringUtils.isEmpty(tokenDTO.getToken())) {
// get token
String token;
if (valid) {
LicensePO read = f2CLicManage.read();
token = copilotAPI.getToken(read.getLicense());
} else {
token = copilotAPI.getFreeToken();
}
tokenManage.updateToken(token, valid);
receiveDTO = copilotAPI.generateChart(token, deSendDTO);
} else {
try {
receiveDTO = copilotAPI.generateChart(tokenDTO.getToken(), deSendDTO);
} catch (Exception e) {
// error, get token again
String token;
if (valid) {
LicensePO read = f2CLicManage.read();
token = copilotAPI.getToken(read.getLicense());
} else {
token = copilotAPI.getFreeToken();
}
tokenManage.updateToken(token, valid);
throw new Exception(e.getMessage());
}
}
if (!receiveDTO.getSqlOk() || !receiveDTO.getChartOk()) {
DEException.throwException((String) JsonUtil.toJSONString(receiveDTO));
}
logger.debug("Copilot Service SQL: " + receiveDTO.getSql());
logger.debug("Copilot Service Chart: " + JsonUtil.toJSONString(receiveDTO.getChart()));
return receiveDTO;
}
public void rebuildChart(ReceiveDTO receiveDTO, List<TableField> fields) {
if (StringUtils.equalsIgnoreCase(receiveDTO.getChart().getType(), "pie")) {
AxisFieldDTO column = receiveDTO.getChart().getColumn();
if (fields.size() != 2 || column == null) {
DEException.throwException("build pie error: " + JsonUtil.toJSONString(receiveDTO));
}
AxisDTO axisDTO = new AxisDTO();
AxisFieldDTO x = new AxisFieldDTO();
AxisFieldDTO y = new AxisFieldDTO();
if (StringUtils.equals(fields.get(0).getOriginName(), column.getValue())) {
x.setName(column.getName());
x.setValue(column.getValue());
y.setName(fields.get(1).getOriginName());
y.setValue(fields.get(1).getOriginName());
} else if (StringUtils.equals(fields.get(1).getOriginName(), column.getValue())) {
x.setName(fields.get(0).getOriginName());
x.setValue(fields.get(0).getOriginName());
y.setName(column.getName());
y.setValue(column.getValue());
} else {
DEException.throwException("build pie error: " + JsonUtil.toJSONString(receiveDTO));
}
axisDTO.setX(x);
axisDTO.setY(y);
receiveDTO.getChart().setAxis(axisDTO);
} else if (StringUtils.equalsIgnoreCase(receiveDTO.getChart().getType(), "table")) {
// 将fields赋值给columns
if (ObjectUtils.isEmpty(receiveDTO.getChart().getColumns())) {
List<AxisFieldDTO> columns = new ArrayList<>();
for (TableField field : fields) {
AxisFieldDTO dto = new AxisFieldDTO();
dto.setName(field.getOriginName());
dto.setValue(field.getOriginName());
columns.add(dto);
}
receiveDTO.getChart().setColumns(columns);
}
}
// 所有图表都加上columns字段用于切换明细表展示
if (Arrays.asList(chartType).contains(receiveDTO.getChart().getType())) {
List<AxisFieldDTO> columns = new ArrayList<>();
columns.add(receiveDTO.getChart().getAxis().getX());
columns.add(receiveDTO.getChart().getAxis().getY());
receiveDTO.getChart().setColumns(columns);
}
}
public List<MsgDTO> getList(Long userId) {
MsgDTO lastMsg = msgManage.getLastMsg(userId);
if (lastMsg == null) {
return null;
}
List<MsgDTO> msg = msgManage.getMsg(lastMsg);
msgManage.deleteMsg(lastMsg);
return msg;
}
public void clearAll(Long userId) {
msgManage.clearAllUserMsg(userId);
}
public MsgDTO errorMsg(MsgDTO msgDTO, String errMsg) throws Exception {
// 如果异常则获取最后一条成功的history
MsgDTO lastSuccessMsg = msgManage.getLastSuccessMsg(AuthUtils.getUser().getUserId(), msgDTO.getDatasetGroupId());
MsgDTO dto = new MsgDTO();
dto.setDatasetGroupId(msgDTO.getDatasetGroupId());
dto.setHistory(lastSuccessMsg == null ? new ArrayList<>() : lastSuccessMsg.getHistory());
dto.setMsgStatus(0);
dto.setMsgType("api");
dto.setErrMsg(errMsg);
msgManage.save(dto);
return dto;
}
public List<TableField> transField(List<TableField> fields) {
fields.forEach(field -> {
field.setDeExtractType(FieldUtils.transType2DeType(field.getFieldType()));
field.setDeType(FieldUtils.transType2DeType(field.getFieldType()));
});
return fields;
}
public Map<String, Object> buildPreviewData(Map<String, Object> data, List<TableField> fields, Map<String, ColumnPermissionItem> desensitizationList) {
Map<String, Object> map = new LinkedHashMap<>();
List<String[]> dataList = (List<String[]>) data.get("data");
List<LinkedHashMap<String, Object>> dataObjectList = new ArrayList<>();
if (ObjectUtils.isNotEmpty(dataList)) {
for (int i = 0; i < dataList.size(); i++) {
String[] row = dataList.get(i);
LinkedHashMap<String, Object> obj = new LinkedHashMap<>();
if (row.length > 0) {
for (int j = 0; j < fields.size(); j++) {
TableField tableField = fields.get(j);
if (desensitizationList.containsKey(tableField.getOriginName())) {
obj.put(tableField.getOriginName(), ChartDataBuild.desensitizationValue(desensitizationList.get(tableField.getOriginName()), String.valueOf(row[j])));
} else {
if (tableField.getDeExtractType() == DeTypeConstants.DE_INT
|| tableField.getDeExtractType() == DeTypeConstants.DE_FLOAT
|| tableField.getDeExtractType() == DeTypeConstants.DE_BOOL) {
try {
obj.put(tableField.getOriginName(), new BigDecimal(row[j]));
} catch (Exception e) {
obj.put(tableField.getOriginName(), row[j]);
}
} else {
obj.put(tableField.getOriginName(), row[j]);
}
}
}
}
dataObjectList.add(obj);
}
}
map.put("fields", fields);
map.put("data", dataObjectList);
return map;
}
public List<String> transCreateTableFields(List<DatasetTableFieldDTO> allFields) {
List<String> list = new ArrayList<>();
for (DatasetTableFieldDTO dto : allFields) {
list.add(" " + dto.getDataeaseName() + " " + transNum2Type(dto.getDeExtractType()) +
" COMMENT '" + dto.getName() + "'");
}
return list;
}
public String transNum2Type(Integer num) {
return switch (num) {
case 0, 1, 5 -> "VARCHAR(50)";
case 2, 3, 4 -> "INT(10)";
default -> "VARCHAR(50)";
};
}
public Lex getLex(String type) {
switch (type) {
case "oracle":
return Lex.ORACLE;
case "sqlServer":
case "mssql":
return Lex.SQL_SERVER;
default:
return Lex.JAVA;
}
}
public String transSql(String type, String copilotSQL, Provider provider, ReceiveDTO receiveDTO) {
if (type.equals("oracle") || type.equals("sqlServer")) {
try {
copilotSQL = copilotSQL.trim();
if (copilotSQL.endsWith(";")) {
copilotSQL = copilotSQL.substring(0, copilotSQL.length() - 1);
}
DatasourceSchemaDTO datasourceSchemaDTO = new DatasourceSchemaDTO();
datasourceSchemaDTO.setType(type);
SqlDialect dialect = provider.getDialect(datasourceSchemaDTO);
SqlParser parser = SqlParser.create(copilotSQL, SqlParser.Config.DEFAULT.withLex(getLex(type)));
SqlNode sqlNode = parser.parseStmt();
return sqlNode.toSqlString(dialect).toString().toLowerCase();
} catch (Exception e) {
logger.debug("calcite trans copilot SQL error");
return receiveDTO.getSql();
}
} else {
return copilotSQL;
}
}
private String engine(String type) {
switch (type) {
case "ck":
return "ClickHouse";
case "pg":
return "PostgreSQL";
case "mysql":
return "MySQL";
case "sqlServer":
return "SQL Server";
default:
return type;
}
}
}

View File

@ -0,0 +1,100 @@
package io.dataease.copilot.manage;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.dataease.api.copilot.dto.ChartDTO;
import io.dataease.api.copilot.dto.HistoryDTO;
import io.dataease.api.copilot.dto.MsgDTO;
import io.dataease.copilot.dao.auto.entity.CoreCopilotMsg;
import io.dataease.copilot.dao.auto.mapper.CoreCopilotMsgMapper;
import io.dataease.utils.AuthUtils;
import io.dataease.utils.BeanUtils;
import io.dataease.utils.IDUtils;
import io.dataease.utils.JsonUtil;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Author Junjun
*/
@Component
public class MsgManage {
@Resource
private CoreCopilotMsgMapper coreCopilotMsgMapper;
private ObjectMapper objectMapper = new ObjectMapper();
public void save(MsgDTO msgDTO) throws Exception {
msgDTO.setId(IDUtils.snowID());
msgDTO.setCreateTime(System.currentTimeMillis());
msgDTO.setUserId(AuthUtils.getUser().getUserId());
coreCopilotMsgMapper.insert(transDTO(msgDTO));
}
public List<MsgDTO> getMsg(MsgDTO msgDTO) {
QueryWrapper<CoreCopilotMsg> wrapper = new QueryWrapper<>();
wrapper.eq("user_id", msgDTO.getUserId());
wrapper.eq("dataset_group_id", msgDTO.getDatasetGroupId());
wrapper.orderByAsc("create_time");
List<CoreCopilotMsg> perCopilotMsgs = coreCopilotMsgMapper.selectList(wrapper);
return perCopilotMsgs.stream().map(this::transRecord).toList();
}
public void deleteMsg(MsgDTO msgDTO) {
QueryWrapper<CoreCopilotMsg> wrapper = new QueryWrapper<>();
wrapper.eq("user_id", msgDTO.getUserId());
wrapper.ne("dataset_group_id", msgDTO.getDatasetGroupId());
coreCopilotMsgMapper.delete(wrapper);
}
public void clearAllUserMsg(Long userId) {
QueryWrapper<CoreCopilotMsg> wrapper = new QueryWrapper<>();
wrapper.eq("user_id", userId);
coreCopilotMsgMapper.delete(wrapper);
}
public MsgDTO getLastMsg(Long userId) {
QueryWrapper<CoreCopilotMsg> wrapper = new QueryWrapper<>();
wrapper.eq("user_id", userId);
wrapper.orderByDesc("create_time");
List<CoreCopilotMsg> perCopilotMsgs = coreCopilotMsgMapper.selectList(wrapper);
return ObjectUtils.isEmpty(perCopilotMsgs) ? null : transRecord(perCopilotMsgs.getFirst());
}
public MsgDTO getLastSuccessMsg(Long userId, Long datasetGroupId) {
QueryWrapper<CoreCopilotMsg> wrapper = new QueryWrapper<>();
wrapper.eq("user_id", userId);
wrapper.eq("dataset_group_id", datasetGroupId);
wrapper.eq("msg_status", 1);
wrapper.eq("msg_type", "api");
wrapper.orderByDesc("create_time");
List<CoreCopilotMsg> perCopilotMsgs = coreCopilotMsgMapper.selectList(wrapper);
return ObjectUtils.isEmpty(perCopilotMsgs) ? null : transRecord(perCopilotMsgs.getFirst());
}
private CoreCopilotMsg transDTO(MsgDTO dto) throws Exception {
CoreCopilotMsg record = new CoreCopilotMsg();
BeanUtils.copyBean(record, dto);
record.setHistory(dto.getHistory() == null ? null : objectMapper.writeValueAsString(dto.getHistory()));
record.setChart(dto.getChart() == null ? null : objectMapper.writeValueAsString(dto.getChart()));
record.setChartData(dto.getChartData() == null ? null : objectMapper.writeValueAsString(dto.getChartData()));
return record;
}
private MsgDTO transRecord(CoreCopilotMsg record) {
MsgDTO dto = new MsgDTO();
BeanUtils.copyBean(dto, record);
TypeReference<List<HistoryDTO>> tokenType = new TypeReference<>() {
};
dto.setHistory(record.getHistory() == null ? new ArrayList<>() : JsonUtil.parseList(record.getHistory(), tokenType));
dto.setChart(record.getChart() == null ? null : JsonUtil.parseObject(record.getChart(), ChartDTO.class));
dto.setChartData(record.getChartData() == null ? null : JsonUtil.parse(record.getChartData(), Map.class));
return dto;
}
}

View File

@ -0,0 +1,36 @@
package io.dataease.copilot.manage;
import io.dataease.api.copilot.dto.TokenDTO;
import io.dataease.copilot.dao.auto.entity.CoreCopilotToken;
import io.dataease.copilot.dao.auto.mapper.CoreCopilotTokenMapper;
import io.dataease.utils.BeanUtils;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
/**
* @Author Junjun
*/
@Component
public class TokenManage {
@Resource
private CoreCopilotTokenMapper coreCopilotTokenMapper;
public TokenDTO getToken(boolean valid) {
CoreCopilotToken perCopilotToken = coreCopilotTokenMapper.selectById(valid ? 2 : 1);
return transRecord(perCopilotToken);
}
public void updateToken(String token, boolean valid) {
CoreCopilotToken record = new CoreCopilotToken();
record.setId(valid ? 2L : 1L);
record.setToken(token);
record.setUpdateTime(System.currentTimeMillis());
coreCopilotTokenMapper.updateById(record);
}
private TokenDTO transRecord(CoreCopilotToken perCopilotToken) {
TokenDTO dto = new TokenDTO();
BeanUtils.copyBean(dto, perCopilotToken);
return dto;
}
}

View File

@ -0,0 +1,40 @@
package io.dataease.copilot.service;
import io.dataease.api.copilot.CopilotApi;
import io.dataease.api.copilot.dto.MsgDTO;
import io.dataease.copilot.manage.CopilotManage;
import io.dataease.utils.AuthUtils;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @Author Junjun
*/
@RestController
@RequestMapping("copilot")
public class CopilotService implements CopilotApi {
@Resource
private CopilotManage copilotManage;
@Override
public MsgDTO chat(MsgDTO msgDTO) throws Exception {
try {
return copilotManage.chat(msgDTO);
} catch (Exception e) {
return copilotManage.errorMsg(msgDTO, e.getMessage());
}
}
@Override
public List<MsgDTO> getList() throws Exception {
return copilotManage.getList(AuthUtils.getUser().getUserId());
}
@Override
public void clearAll() throws Exception {
copilotManage.clearAll(AuthUtils.getUser().getUserId());
}
}

View File

@ -0,0 +1,10 @@
package io.dataease.dataset.constant;
/**
* @Author Junjun
*/
public class DatasetTableType {
public static String DB = "db";
public static String SQL = "sql";
public static String Es = "es";
}

View File

@ -0,0 +1,231 @@
package io.dataease.dataset.dao.auto.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author fit2cloud
* @since 2023-08-28
*/
@TableName("core_dataset_group")
public class CoreDatasetGroup implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
* 名称
*/
private String name;
/**
* 父级ID
*/
private Long pid;
/**
* 当前分组处于第几级
*/
private Integer level;
/**
* node类型folder or dataset
*/
private String nodeType;
/**
* sql,union
*/
private String type;
/**
* 连接模式0-直连1-同步(包括excelapi等数据存在de中的表)
*/
private Integer mode;
/**
* 关联关系树
*/
private String info;
/**
* 创建人ID
*/
private String createBy;
/**
* 创建时间
*/
private Long createTime;
private String qrtzInstance;
/**
* 同步状态
*/
private String syncStatus;
/**
* 更新人ID
*/
private String updateBy;
/**
* 最后同步时间
*/
private Long lastUpdateTime;
/**
* 关联sql
*/
private String unionSql;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getPid() {
return pid;
}
public void setPid(Long pid) {
this.pid = pid;
}
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
public String getNodeType() {
return nodeType;
}
public void setNodeType(String nodeType) {
this.nodeType = nodeType;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Integer getMode() {
return mode;
}
public void setMode(Integer mode) {
this.mode = mode;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Long getCreateTime() {
return createTime;
}
public void setCreateTime(Long createTime) {
this.createTime = createTime;
}
public String getQrtzInstance() {
return qrtzInstance;
}
public void setQrtzInstance(String qrtzInstance) {
this.qrtzInstance = qrtzInstance;
}
public String getSyncStatus() {
return syncStatus;
}
public void setSyncStatus(String syncStatus) {
this.syncStatus = syncStatus;
}
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public Long getLastUpdateTime() {
return lastUpdateTime;
}
public void setLastUpdateTime(Long lastUpdateTime) {
this.lastUpdateTime = lastUpdateTime;
}
public String getUnionSql() {
return unionSql;
}
public void setUnionSql(String unionSql) {
this.unionSql = unionSql;
}
@Override
public String toString() {
return "CoreDatasetGroup{" +
"id = " + id +
", name = " + name +
", pid = " + pid +
", level = " + level +
", nodeType = " + nodeType +
", type = " + type +
", mode = " + mode +
", info = " + info +
", createBy = " + createBy +
", createTime = " + createTime +
", qrtzInstance = " + qrtzInstance +
", syncStatus = " + syncStatus +
", updateBy = " + updateBy +
", lastUpdateTime = " + lastUpdateTime +
", unionSql = " + unionSql +
"}";
}
}

View File

@ -0,0 +1,136 @@
package io.dataease.dataset.dao.auto.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author fit2cloud
* @since 2023-04-14
*/
@TableName("core_dataset_table")
public class CoreDatasetTable implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
* 名称
*/
private String name;
/**
* 物理表名
*/
private String tableName;
/**
* 数据源ID
*/
private Long datasourceId;
/**
* 数据集ID
*/
private Long datasetGroupId;
/**
* db,sql,union,excel,api
*/
private String type;
/**
* 表原始信息,表名,sql等
*/
private String info;
/**
* SQL参数
*/
private String sqlVariableDetails;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public Long getDatasourceId() {
return datasourceId;
}
public void setDatasourceId(Long datasourceId) {
this.datasourceId = datasourceId;
}
public Long getDatasetGroupId() {
return datasetGroupId;
}
public void setDatasetGroupId(Long datasetGroupId) {
this.datasetGroupId = datasetGroupId;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public String getSqlVariableDetails() {
return sqlVariableDetails;
}
public void setSqlVariableDetails(String sqlVariableDetails) {
this.sqlVariableDetails = sqlVariableDetails;
}
@Override
public String toString() {
return "CoreDatasetTable{" +
"id = " + id +
", name = " + name +
", tableName = " + tableName +
", datasourceId = " + datasourceId +
", datasetGroupId = " + datasetGroupId +
", type = " + type +
", info = " + info +
", sqlVariableDetails = " + sqlVariableDetails +
"}";
}
}

View File

@ -0,0 +1,374 @@
package io.dataease.dataset.dao.auto.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
/**
* <p>
* table数据集表字段
* </p>
*
* @author fit2cloud
* @since 2025-02-06
*/
@TableName("core_dataset_table_field")
public class CoreDatasetTableField implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
* 数据源ID
*/
private Long datasourceId;
/**
* 数据表ID
*/
private Long datasetTableId;
/**
* 数据集ID
*/
private Long datasetGroupId;
/**
* 视图ID
*/
private Long chartId;
/**
* 原始字段名
*/
private String originName;
/**
* 字段名用于展示
*/
private String name;
/**
* 描述
*/
private String description;
/**
* de字段名用作唯一标识
*/
private String dataeaseName;
/**
* de字段别名
*/
private String fieldShortName;
/**
* 分组设置
*/
private String groupList;
/**
* 未分组的值
*/
private String otherGroup;
/**
* 维度/指标标识 d:维度q:指标
*/
private String groupType;
/**
* 原始字段类型
*/
private String type;
/**
* 字段长度允许为空默认0
*/
private Integer size;
/**
* dataease字段类型0-文本1-时间2-整型数值3-浮点数值4-布尔5-地理位置6-二进制
*/
private Integer deType;
/**
* de记录的原始类型
*/
private Integer deExtractType;
/**
* 是否扩展字段 0原始 1复制 2计算字段...
*/
private Integer extField;
/**
* 是否选中
*/
private Boolean checked;
/**
* 列位置
*/
private Integer columnIndex;
/**
* 同步时间
*/
private Long lastSyncTime;
/**
* 精度
*/
private Integer accuracy;
/**
* 时间字段类型
*/
private String dateFormat;
/**
* 时间格式类型
*/
private String dateFormatType;
/**
* 计算字段参数
*/
private String params;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getDatasourceId() {
return datasourceId;
}
public void setDatasourceId(Long datasourceId) {
this.datasourceId = datasourceId;
}
public Long getDatasetTableId() {
return datasetTableId;
}
public void setDatasetTableId(Long datasetTableId) {
this.datasetTableId = datasetTableId;
}
public Long getDatasetGroupId() {
return datasetGroupId;
}
public void setDatasetGroupId(Long datasetGroupId) {
this.datasetGroupId = datasetGroupId;
}
public Long getChartId() {
return chartId;
}
public void setChartId(Long chartId) {
this.chartId = chartId;
}
public String getOriginName() {
return originName;
}
public void setOriginName(String originName) {
this.originName = originName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getDataeaseName() {
return dataeaseName;
}
public void setDataeaseName(String dataeaseName) {
this.dataeaseName = dataeaseName;
}
public String getFieldShortName() {
return fieldShortName;
}
public void setFieldShortName(String fieldShortName) {
this.fieldShortName = fieldShortName;
}
public String getGroupList() {
return groupList;
}
public void setGroupList(String groupList) {
this.groupList = groupList;
}
public String getOtherGroup() {
return otherGroup;
}
public void setOtherGroup(String otherGroup) {
this.otherGroup = otherGroup;
}
public String getGroupType() {
return groupType;
}
public void setGroupType(String groupType) {
this.groupType = groupType;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
public Integer getDeType() {
return deType;
}
public void setDeType(Integer deType) {
this.deType = deType;
}
public Integer getDeExtractType() {
return deExtractType;
}
public void setDeExtractType(Integer deExtractType) {
this.deExtractType = deExtractType;
}
public Integer getExtField() {
return extField;
}
public void setExtField(Integer extField) {
this.extField = extField;
}
public Boolean getChecked() {
return checked;
}
public void setChecked(Boolean checked) {
this.checked = checked;
}
public Integer getColumnIndex() {
return columnIndex;
}
public void setColumnIndex(Integer columnIndex) {
this.columnIndex = columnIndex;
}
public Long getLastSyncTime() {
return lastSyncTime;
}
public void setLastSyncTime(Long lastSyncTime) {
this.lastSyncTime = lastSyncTime;
}
public Integer getAccuracy() {
return accuracy;
}
public void setAccuracy(Integer accuracy) {
this.accuracy = accuracy;
}
public String getDateFormat() {
return dateFormat;
}
public void setDateFormat(String dateFormat) {
this.dateFormat = dateFormat;
}
public String getDateFormatType() {
return dateFormatType;
}
public void setDateFormatType(String dateFormatType) {
this.dateFormatType = dateFormatType;
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
@Override
public String toString() {
return "CoreDatasetTableField{" +
"id = " + id +
", datasourceId = " + datasourceId +
", datasetTableId = " + datasetTableId +
", datasetGroupId = " + datasetGroupId +
", chartId = " + chartId +
", originName = " + originName +
", name = " + name +
", description = " + description +
", dataeaseName = " + dataeaseName +
", fieldShortName = " + fieldShortName +
", groupList = " + groupList +
", otherGroup = " + otherGroup +
", groupType = " + groupType +
", type = " + type +
", size = " + size +
", deType = " + deType +
", deExtractType = " + deExtractType +
", extField = " + extField +
", checked = " + checked +
", columnIndex = " + columnIndex +
", lastSyncTime = " + lastSyncTime +
", accuracy = " + accuracy +
", dateFormat = " + dateFormat +
", dateFormatType = " + dateFormatType +
", params = " + params +
"}";
}
}

View File

@ -0,0 +1,122 @@
package io.dataease.dataset.dao.auto.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author fit2cloud
* @since 2023-08-08
*/
@TableName("core_dataset_table_sql_log")
public class CoreDatasetTableSqlLog implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private String id;
/**
* 数据集SQL节点ID
*/
private String tableId;
/**
* 开始时间
*/
private Long startTime;
/**
* 结束时间
*/
private Long endTime;
/**
* 耗时(毫秒)
*/
private Long spend;
/**
* 详细信息
*/
private String sql;
/**
* 状态
*/
private String status;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTableId() {
return tableId;
}
public void setTableId(String tableId) {
this.tableId = tableId;
}
public Long getStartTime() {
return startTime;
}
public void setStartTime(Long startTime) {
this.startTime = startTime;
}
public Long getEndTime() {
return endTime;
}
public void setEndTime(Long endTime) {
this.endTime = endTime;
}
public Long getSpend() {
return spend;
}
public void setSpend(Long spend) {
this.spend = spend;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public String toString() {
return "CoreDatasetTableSqlLog{" +
"id = " + id +
", tableId = " + tableId +
", startTime = " + startTime +
", endTime = " + endTime +
", spend = " + spend +
", sql = " + sql +
", status = " + status +
"}";
}
}

View File

@ -0,0 +1,18 @@
package io.dataease.dataset.dao.auto.mapper;
import io.dataease.dataset.dao.auto.entity.CoreDatasetGroup;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author fit2cloud
* @since 2023-08-28
*/
@Mapper
public interface CoreDatasetGroupMapper extends BaseMapper<CoreDatasetGroup> {
}

View File

@ -0,0 +1,18 @@
package io.dataease.dataset.dao.auto.mapper;
import io.dataease.dataset.dao.auto.entity.CoreDatasetTableField;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* table数据集表字段 Mapper 接口
* </p>
*
* @author fit2cloud
* @since 2025-02-06
*/
@Mapper
public interface CoreDatasetTableFieldMapper extends BaseMapper<CoreDatasetTableField> {
}

View File

@ -0,0 +1,18 @@
package io.dataease.dataset.dao.auto.mapper;
import io.dataease.dataset.dao.auto.entity.CoreDatasetTable;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author fit2cloud
* @since 2023-04-14
*/
@Mapper
public interface CoreDatasetTableMapper extends BaseMapper<CoreDatasetTable> {
}

View File

@ -0,0 +1,18 @@
package io.dataease.dataset.dao.auto.mapper;
import io.dataease.dataset.dao.auto.entity.CoreDatasetTableSqlLog;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author fit2cloud
* @since 2023-08-08
*/
@Mapper
public interface CoreDatasetTableSqlLogMapper extends BaseMapper<CoreDatasetTableSqlLog> {
}

View File

@ -0,0 +1,24 @@
package io.dataease.dataset.dao.ext.mapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.dataease.api.dataset.vo.DataSetBarVO;
import io.dataease.dataset.dao.ext.po.DataSetNodePO;
import io.dataease.model.BusiNodeRequest;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface CoreDataSetExtMapper {
@Select("""
select id, name, node_type, pid from core_dataset_group
${ew.customSqlSegment}
""")
List<DataSetNodePO> query(@Param("ew") QueryWrapper queryWrapper);
@Select("select id, name, node_type, create_by, create_time, update_by, last_update_time from core_dataset_group where id = #{id}")
DataSetBarVO queryBarInfo(@Param("id") Long id);
}

View File

@ -0,0 +1,22 @@
package io.dataease.dataset.dao.ext.po;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DataSetNodePO implements Serializable {
@Serial
private static final long serialVersionUID = -4457506330575500164L;
private Long id;
private String name;
private String nodeType;
private Long pid;
}

View File

@ -0,0 +1,25 @@
package io.dataease.dataset.dto;
import io.dataease.model.TreeBaseModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DataSetNodeBO implements TreeBaseModel {
@Serial
private static final long serialVersionUID = 728340676442387790L;
private Long id;
private String name;
private Boolean leaf;
private Integer weight = 3;
private Long pid;
private Integer extraFlag;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,605 @@
package io.dataease.dataset.manage;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import io.dataease.api.dataset.union.DatasetGroupInfoDTO;
import io.dataease.api.dataset.union.UnionDTO;
import io.dataease.api.dataset.vo.DataSetBarVO;
import io.dataease.api.permissions.relation.api.RelationApi;
import io.dataease.commons.constants.OptConstants;
import io.dataease.dataset.dao.auto.entity.CoreDatasetGroup;
import io.dataease.dataset.dao.auto.entity.CoreDatasetTable;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetGroupMapper;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetTableMapper;
import io.dataease.dataset.dao.ext.mapper.CoreDataSetExtMapper;
import io.dataease.dataset.dao.ext.po.DataSetNodePO;
import io.dataease.dataset.dto.DataSetNodeBO;
import io.dataease.dataset.utils.FieldUtils;
import io.dataease.dataset.utils.TableUtils;
import io.dataease.datasource.dao.auto.entity.CoreDatasource;
import io.dataease.datasource.dao.auto.mapper.CoreDatasourceMapper;
import io.dataease.engine.constant.ExtFieldConstant;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.dto.DatasetTableDTO;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceDTO;
import io.dataease.extensions.view.dto.SqlVariableDetails;
import io.dataease.i18n.Translator;
import io.dataease.license.config.XpackInteract;
import io.dataease.license.utils.LicenseUtil;
import io.dataease.model.BusiNodeRequest;
import io.dataease.model.BusiNodeVO;
import io.dataease.operation.manage.CoreOptRecentManage;
import io.dataease.system.manage.CoreUserManage;
import io.dataease.utils.*;
import jakarta.annotation.Resource;
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.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
/**
* @Author Junjun
*/
@Component
@Transactional(rollbackFor = Exception.class)
public class DatasetGroupManage {
@Resource
private CoreDatasetGroupMapper coreDatasetGroupMapper;
@Resource
private DatasetSQLManage datasetSQLManage;
@Resource
private DatasetDataManage datasetDataManage;
@Resource
private DatasetTableManage datasetTableManage;
@Resource
private DatasetTableFieldManage datasetTableFieldManage;
@Resource
private PermissionManage permissionManage;
@Resource
private CoreDataSetExtMapper coreDataSetExtMapper;
@Resource
private CoreDatasetTableMapper coreDatasetTableMapper;
@Resource
private CoreDatasourceMapper coreDatasourceMapper;
@Resource
private CoreUserManage coreUserManage;
@Resource
private CoreOptRecentManage coreOptRecentManage;
@Autowired(required = false)
private RelationApi relationManage;
private static final String leafType = "dataset";
private Lock lock = new ReentrantLock();
@Transactional
public DatasetGroupInfoDTO save(DatasetGroupInfoDTO datasetGroupInfoDTO, boolean rename) throws Exception {
lock.lock();
try {
boolean isCreate;
// 用于重命名获取pid
if (ObjectUtils.isEmpty(datasetGroupInfoDTO.getPid()) && ObjectUtils.isNotEmpty(datasetGroupInfoDTO.getId())) {
CoreDatasetGroup coreDatasetGroup = coreDatasetGroupMapper.selectById(datasetGroupInfoDTO.getId());
datasetGroupInfoDTO.setPid(coreDatasetGroup.getPid());
}
datasetGroupInfoDTO.setUpdateBy(AuthUtils.getUser().getUserId() + "");
datasetGroupInfoDTO.setLastUpdateTime(System.currentTimeMillis());
if (StringUtils.equalsIgnoreCase(datasetGroupInfoDTO.getNodeType(), leafType)) {
if (!rename && ObjectUtils.isEmpty(datasetGroupInfoDTO.getAllFields())) {
DEException.throwException(Translator.get("i18n_no_fields"));
}
// get union sql
Map<String, Object> sqlMap = datasetSQLManage.getUnionSQLForEdit(datasetGroupInfoDTO, null);
if (ObjectUtils.isNotEmpty(sqlMap)) {
String sql = (String) sqlMap.get("sql");
datasetGroupInfoDTO.setUnionSql(sql);
datasetGroupInfoDTO.setInfo(Objects.requireNonNull(JsonUtil.toJSONString(datasetGroupInfoDTO.getUnion())).toString());
}
}
// save dataset/group
long time = System.currentTimeMillis();
if (ObjectUtils.isEmpty(datasetGroupInfoDTO.getId())) {
isCreate = true;
datasetGroupInfoDTO.setId(IDUtils.snowID());
datasetGroupInfoDTO.setCreateBy(AuthUtils.getUser().getUserId() + "");
datasetGroupInfoDTO.setUpdateBy(AuthUtils.getUser().getUserId() + "");
datasetGroupInfoDTO.setCreateTime(time);
datasetGroupInfoDTO.setLastUpdateTime(time);
datasetGroupInfoDTO.setPid(datasetGroupInfoDTO.getPid() == null ? 0L : datasetGroupInfoDTO.getPid());
Objects.requireNonNull(CommonBeanFactory.getBean(this.getClass())).innerSave(datasetGroupInfoDTO);
} else {
isCreate = false;
if (Objects.equals(datasetGroupInfoDTO.getId(), datasetGroupInfoDTO.getPid())) {
DEException.throwException(Translator.get("i18n_pid_not_eq_id"));
}
Objects.requireNonNull(CommonBeanFactory.getBean(this.getClass())).innerEdit(datasetGroupInfoDTO);
}
// node_type=dataset需要创建dataset_table和field
if (StringUtils.equalsIgnoreCase(datasetGroupInfoDTO.getNodeType(), "dataset")) {
List<Long> tableIds = new ArrayList<>();
List<Long> fieldIds = new ArrayList<>();
// 解析tree保存
saveTable(datasetGroupInfoDTO, datasetGroupInfoDTO.getUnion(), tableIds, isCreate);
saveField(datasetGroupInfoDTO, fieldIds);
// 删除不要的table和field
datasetTableManage.deleteByDatasetGroupUpdate(datasetGroupInfoDTO.getId(), tableIds);
datasetTableFieldManage.deleteByDatasetGroupUpdate(datasetGroupInfoDTO.getId(), fieldIds);
}
return datasetGroupInfoDTO;
} catch (Exception e) {
DEException.throwException(e.getMessage());
} finally {
lock.unlock();
}
return null;
}
@XpackInteract(value = "authResourceTree", before = false)
public void innerEdit(DatasetGroupInfoDTO datasetGroupInfoDTO) {
checkName(datasetGroupInfoDTO);
CoreDatasetGroup coreDatasetGroup = BeanUtils.copyBean(new CoreDatasetGroup(), datasetGroupInfoDTO);
coreDatasetGroup.setLastUpdateTime(System.currentTimeMillis());
coreDatasetGroupMapper.updateById(coreDatasetGroup);
coreOptRecentManage.saveOpt(datasetGroupInfoDTO.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASET, OptConstants.OPT_TYPE.UPDATE);
}
@XpackInteract(value = "authResourceTree", before = false)
public void innerSave(DatasetGroupInfoDTO datasetGroupInfoDTO) {
checkName(datasetGroupInfoDTO);
CoreDatasetGroup coreDatasetGroup = BeanUtils.copyBean(new CoreDatasetGroup(), datasetGroupInfoDTO);
coreDatasetGroupMapper.insert(coreDatasetGroup);
coreOptRecentManage.saveOpt(coreDatasetGroup.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASET, OptConstants.OPT_TYPE.NEW);
}
@XpackInteract(value = "authResourceTree", before = false)
public DatasetGroupInfoDTO move(DatasetGroupInfoDTO datasetGroupInfoDTO) {
checkName(datasetGroupInfoDTO);
if (datasetGroupInfoDTO.getPid() != 0) {
checkMove(datasetGroupInfoDTO);
}
// save dataset/group
long time = System.currentTimeMillis();
CoreDatasetGroup coreDatasetGroup = new CoreDatasetGroup();
BeanUtils.copyBean(coreDatasetGroup, datasetGroupInfoDTO);
datasetGroupInfoDTO.setUpdateBy(AuthUtils.getUser().getUserId() + "");
coreDatasetGroup.setLastUpdateTime(time);
coreDatasetGroupMapper.updateById(coreDatasetGroup);
coreOptRecentManage.saveOpt(coreDatasetGroup.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASET, OptConstants.OPT_TYPE.UPDATE);
return datasetGroupInfoDTO;
}
public boolean perDelete(Long id) {
if (LicenseUtil.licenseValid()) {
try {
relationManage.checkAuth();
} catch (Exception e) {
return false;
}
Long count = relationManage.getDatasetResource(id);
if (count > 0) {
return true;
}
}
return false;
}
@XpackInteract(value = "authResourceTree", before = false)
public void delete(Long id) {
CoreDatasetGroup coreDatasetGroup = coreDatasetGroupMapper.selectById(id);
if (ObjectUtils.isEmpty(coreDatasetGroup)) {
DEException.throwException("resource not exist");
}
Objects.requireNonNull(CommonBeanFactory.getBean(this.getClass())).recursionDel(id);
coreOptRecentManage.saveOpt(coreDatasetGroup.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASET, OptConstants.OPT_TYPE.DELETE);
}
public void recursionDel(Long id) {
coreDatasetGroupMapper.deleteById(id);
datasetTableManage.deleteByDatasetGroupDelete(id);
datasetTableFieldManage.deleteByDatasetGroupDelete(id);
QueryWrapper<CoreDatasetGroup> wrapper = new QueryWrapper<>();
wrapper.eq("pid", id);
List<CoreDatasetGroup> coreDatasetGroups = coreDatasetGroupMapper.selectList(wrapper);
if (ObjectUtils.isNotEmpty(coreDatasetGroups)) {
for (CoreDatasetGroup record : coreDatasetGroups) {
recursionDel(record.getId());
}
}
}
@XpackInteract(value = "authResourceTree", replace = true, invalid = true)
public List<BusiNodeVO> tree(BusiNodeRequest request) {
QueryWrapper<Object> queryWrapper = new QueryWrapper<>();
if (ObjectUtils.isNotEmpty(request.getLeaf())) {
queryWrapper.eq("node_type", request.getLeaf() ? "dataset" : "folder");
}
String info = CommunityUtils.getInfo();
if (StringUtils.isNotBlank(info)) {
queryWrapper.notExists(String.format(info, "core_dataset_group.id"));
}
queryWrapper.orderByDesc("create_time");
List<DataSetNodePO> pos = coreDataSetExtMapper.query(queryWrapper);
List<DataSetNodeBO> nodes = new ArrayList<>();
if (ObjectUtils.isEmpty(request.getLeaf()) || !request.getLeaf()) nodes.add(rootNode());
List<DataSetNodeBO> bos = pos.stream().map(this::convert).toList();
if (CollectionUtils.isNotEmpty(bos)) {
nodes.addAll(bos);
}
return TreeUtils.mergeTree(nodes, BusiNodeVO.class, false);
}
public DataSetBarVO queryBarInfo(Long id) {
DataSetBarVO dataSetBarVO = coreDataSetExtMapper.queryBarInfo(id);
// get creator
String userName = coreUserManage.getUserName(Long.valueOf(dataSetBarVO.getCreateBy()));
if (StringUtils.isNotBlank(userName)) {
dataSetBarVO.setCreator(userName);
}
String updateUserName = coreUserManage.getUserName(Long.valueOf(dataSetBarVO.getUpdateBy()));
if (StringUtils.isNotBlank(updateUserName)) {
dataSetBarVO.setUpdater(updateUserName);
}
dataSetBarVO.setDatasourceDTOList(getDatasource(id));
return dataSetBarVO;
}
private List<DatasourceDTO> getDatasource(Long datasetId) {
QueryWrapper<CoreDatasetTable> wrapper = new QueryWrapper<>();
wrapper.eq("dataset_group_id", datasetId);
List<CoreDatasetTable> coreDatasetTables = coreDatasetTableMapper.selectList(wrapper);
Set<Long> ids = new LinkedHashSet();
coreDatasetTables.forEach(ele -> ids.add(ele.getDatasourceId()));
if (CollectionUtils.isEmpty(ids)) {
DEException.throwException(Translator.get("i18n_dataset_create_error"));
}
QueryWrapper<CoreDatasource> datasourceQueryWrapper = new QueryWrapper<>();
datasourceQueryWrapper.in("id", ids);
List<DatasourceDTO> datasourceDTOList = coreDatasourceMapper.selectList(datasourceQueryWrapper).stream().map(ele -> {
DatasourceDTO dto = new DatasourceDTO();
BeanUtils.copyBean(dto, ele);
dto.setConfiguration(null);
return dto;
}).collect(Collectors.toList());
if (ids.size() != datasourceDTOList.size()) {
DEException.throwException(Translator.get("i18n_dataset_ds_delete"));
}
return datasourceDTOList;
}
private DataSetNodeBO rootNode() {
return new DataSetNodeBO(0L, "root", false, 7, -1L, 0);
}
private DataSetNodeBO convert(DataSetNodePO po) {
return new DataSetNodeBO(po.getId(), po.getName(), StringUtils.equals(po.getNodeType(), leafType), 9, po.getPid(), 0);
}
public void checkName(DatasetGroupInfoDTO dto) {
if (!LicenseUtil.licenseValid()) {
QueryWrapper<CoreDatasetGroup> wrapper = new QueryWrapper<>();
if (ObjectUtils.isNotEmpty(dto.getPid())) {
wrapper.eq("pid", dto.getPid());
}
if (StringUtils.isNotEmpty(dto.getName())) {
wrapper.eq("name", dto.getName());
}
if (ObjectUtils.isNotEmpty(dto.getId())) {
wrapper.ne("id", dto.getId());
}
if (ObjectUtils.isNotEmpty(dto.getLevel())) {
wrapper.eq("level", dto.getLevel());
}
if (ObjectUtils.isNotEmpty(dto.getNodeType())) {
wrapper.eq("node_type", dto.getNodeType());
}
List<CoreDatasetGroup> list = coreDatasetGroupMapper.selectList(wrapper);
if (list.size() > 0) {
DEException.throwException(Translator.get("i18n_ds_name_exists"));
}
}
}
public void saveTable(DatasetGroupInfoDTO datasetGroupInfoDTO, List<UnionDTO> union, List<Long> tableIds, boolean isCreate) {
// table和field均由前端生成id如果没有id
Long datasetGroupId = datasetGroupInfoDTO.getId();
if (ObjectUtils.isNotEmpty(union)) {
for (UnionDTO unionDTO : union) {
DatasetTableDTO currentDs = unionDTO.getCurrentDs();
CoreDatasetTable coreDatasetTable = datasetTableManage.selectById(currentDs.getId());
if (coreDatasetTable != null && isCreate) {
DEException.throwException(Translator.get("i18n_table_duplicate"));
}
currentDs.setDatasetGroupId(datasetGroupId);
datasetTableManage.save(currentDs);
tableIds.add(currentDs.getId());
saveTable(datasetGroupInfoDTO, unionDTO.getChildrenDs(), tableIds, isCreate);
}
}
}
public void saveField(DatasetGroupInfoDTO datasetGroupInfoDTO, List<Long> fieldIds) throws Exception {
if (ObjectUtils.isEmpty(datasetGroupInfoDTO.getUnion())) {
return;
}
datasetDataManage.previewDataWithLimit(datasetGroupInfoDTO, 0, 1, false);
// table和field均由前端生成id如果没有id
Long datasetGroupId = datasetGroupInfoDTO.getId();
List<DatasetTableFieldDTO> allFields = datasetGroupInfoDTO.getAllFields();
if (ObjectUtils.isNotEmpty(allFields)) {
// 获取内层union sql和字段
Map<String, Object> map = datasetSQLManage.getUnionSQLForEdit(datasetGroupInfoDTO, null);
List<DatasetTableFieldDTO> unionFields = (List<DatasetTableFieldDTO>) map.get("field");
for (DatasetTableFieldDTO datasetTableFieldDTO : allFields) {
DatasetTableFieldDTO dto = datasetTableFieldManage.selectById(datasetTableFieldDTO.getId());
if (ObjectUtils.isEmpty(dto)) {
if (Objects.equals(datasetTableFieldDTO.getExtField(), ExtFieldConstant.EXT_NORMAL)) {
for (DatasetTableFieldDTO fieldDTO : unionFields) {
if (Objects.equals(datasetTableFieldDTO.getDatasetTableId(), fieldDTO.getDatasetTableId())
&& Objects.equals(datasetTableFieldDTO.getOriginName(), fieldDTO.getOriginName())) {
datasetTableFieldDTO.setDataeaseName(fieldDTO.getDataeaseName());
datasetTableFieldDTO.setFieldShortName(fieldDTO.getFieldShortName());
}
}
}
if (Objects.equals(datasetTableFieldDTO.getExtField(), ExtFieldConstant.EXT_CALC)) {
String dataeaseName = TableUtils.fieldNameShort(datasetTableFieldDTO.getId() + "_" + datasetTableFieldDTO.getOriginName());
datasetTableFieldDTO.setDataeaseName(dataeaseName);
datasetTableFieldDTO.setFieldShortName(dataeaseName);
datasetTableFieldDTO.setDeExtractType(datasetTableFieldDTO.getDeType());
}
if (Objects.equals(datasetTableFieldDTO.getExtField(), ExtFieldConstant.EXT_GROUP)) {
String dataeaseName = TableUtils.fieldNameShort(datasetTableFieldDTO.getId() + "_" + datasetTableFieldDTO.getOriginName());
datasetTableFieldDTO.setDataeaseName(dataeaseName);
datasetTableFieldDTO.setFieldShortName(dataeaseName);
datasetTableFieldDTO.setDeExtractType(0);
datasetTableFieldDTO.setDeType(0);
datasetTableFieldDTO.setGroupType("d");
}
datasetTableFieldDTO.setDatasetGroupId(datasetGroupId);
} else {
datasetTableFieldDTO.setDataeaseName(dto.getDataeaseName());
datasetTableFieldDTO.setFieldShortName(dto.getFieldShortName());
}
datasetTableFieldDTO = datasetTableFieldManage.save(datasetTableFieldDTO);
fieldIds.add(datasetTableFieldDTO.getId());
}
}
}
public DatasetGroupInfoDTO getForCount(Long id) throws Exception {
CoreDatasetGroup coreDatasetGroup = coreDatasetGroupMapper.selectById(id);
if (coreDatasetGroup == null) {
return null;
}
DatasetGroupInfoDTO dto = new DatasetGroupInfoDTO();
BeanUtils.copyBean(dto, coreDatasetGroup);
if (StringUtils.equalsIgnoreCase(dto.getNodeType(), "dataset")) {
dto.setUnion(JsonUtil.parseList(coreDatasetGroup.getInfo(), new TypeReference<>() {
}));
// 获取field
List<DatasetTableFieldDTO> dsFields = datasetTableFieldManage.selectByDatasetGroupId(id);
List<DatasetTableFieldDTO> allFields = dsFields.stream().map(ele -> {
DatasetTableFieldDTO datasetTableFieldDTO = new DatasetTableFieldDTO();
BeanUtils.copyBean(datasetTableFieldDTO, ele);
datasetTableFieldDTO.setFieldShortName(ele.getDataeaseName());
return datasetTableFieldDTO;
}).collect(Collectors.toList());
dto.setAllFields(allFields);
}
return dto;
}
public DatasetGroupInfoDTO getDetail(Long id) throws Exception {
CoreDatasetGroup coreDatasetGroup = coreDatasetGroupMapper.selectById(id);
if (coreDatasetGroup == null) {
return null;
}
DatasetGroupInfoDTO dto = new DatasetGroupInfoDTO();
BeanUtils.copyBean(dto, coreDatasetGroup);
// get creator
String userName = coreUserManage.getUserName(Long.valueOf(dto.getCreateBy()));
if (StringUtils.isNotBlank(userName)) {
dto.setCreator(userName);
}
String updateUserName = coreUserManage.getUserName(Long.valueOf(dto.getUpdateBy()));
if (StringUtils.isNotBlank(updateUserName)) {
dto.setUpdater(updateUserName);
}
dto.setUnionSql(null);
if (StringUtils.equalsIgnoreCase(dto.getNodeType(), "dataset")) {
List<UnionDTO> unionDTOList = JsonUtil.parseList(coreDatasetGroup.getInfo(), new TypeReference<>() {
});
dto.setUnion(unionDTOList);
// 获取field
List<DatasetTableFieldDTO> dsFields = datasetTableFieldManage.selectByDatasetGroupId(id);
List<DatasetTableFieldDTO> allFields = dsFields.stream().map(ele -> {
DatasetTableFieldDTO datasetTableFieldDTO = new DatasetTableFieldDTO();
BeanUtils.copyBean(datasetTableFieldDTO, ele);
datasetTableFieldDTO.setFieldShortName(ele.getDataeaseName());
return datasetTableFieldDTO;
}).collect(Collectors.toList());
dto.setAllFields(allFields);
}
return dto;
}
public DatasetGroupInfoDTO getDatasetGroupInfoDTO(Long id, String type) throws Exception {
CoreDatasetGroup coreDatasetGroup = coreDatasetGroupMapper.selectById(id);
if (coreDatasetGroup == null) {
return null;
}
DatasetGroupInfoDTO dto = new DatasetGroupInfoDTO();
BeanUtils.copyBean(dto, coreDatasetGroup);
// get creator
String userName = coreUserManage.getUserName(Long.valueOf(dto.getCreateBy()));
if (StringUtils.isNotBlank(userName)) {
dto.setCreator(userName);
}
String updateUserName = coreUserManage.getUserName(Long.valueOf(dto.getUpdateBy()));
if (StringUtils.isNotBlank(updateUserName)) {
dto.setUpdater(updateUserName);
}
dto.setUnionSql(null);
if (StringUtils.equalsIgnoreCase(dto.getNodeType(), "dataset")) {
List<UnionDTO> unionDTOList = JsonUtil.parseList(coreDatasetGroup.getInfo(), new TypeReference<>() {
});
dto.setUnion(unionDTOList);
// 获取field
List<DatasetTableFieldDTO> dsFields = datasetTableFieldManage.selectByDatasetGroupId(id);
List<DatasetTableFieldDTO> allFields = dsFields.stream().map(ele -> {
DatasetTableFieldDTO datasetTableFieldDTO = new DatasetTableFieldDTO();
BeanUtils.copyBean(datasetTableFieldDTO, ele);
datasetTableFieldDTO.setFieldShortName(ele.getDataeaseName());
return datasetTableFieldDTO;
}).collect(Collectors.toList());
dto.setAllFields(allFields);
if ("preview".equalsIgnoreCase(type)) {
// 请求数据
Map<String, Object> map = datasetDataManage.previewDataWithLimit(dto, 0, 100, true);
// 获取data,sql
Map<String, List> data = (Map<String, List>) map.get("data");
String sql = (String) map.get("sql");
Long total = (Long) map.get("total");
dto.setData(data);
dto.setSql(Base64.getEncoder().encodeToString(sql.getBytes()));
dto.setTotal(total);
}
}
return dto;
}
public List<DatasetTableDTO> getDetail(List<Long> ids) {
if (ObjectUtils.isEmpty(ids)) {
DEException.throwException(Translator.get("i18n_table_id_can_not_empty"));
}
List<DatasetTableDTO> list = new ArrayList<>();
for (Long id : ids) {
CoreDatasetGroup coreDatasetGroup = coreDatasetGroupMapper.selectById(id);
if (coreDatasetGroup == null) {
list.add(null);
} else {
DatasetTableDTO dto = new DatasetTableDTO();
BeanUtils.copyBean(dto, coreDatasetGroup);
Map<String, List<DatasetTableFieldDTO>> listByDQ = datasetTableFieldManage.listByDQ(id);
dto.setFields(listByDQ);
list.add(dto);
}
}
return list;
}
public List<SqlVariableDetails> getSqlParams(List<Long> ids) {
List<SqlVariableDetails> list = new ArrayList<>();
if (ObjectUtils.isEmpty(ids)) {
return list;
}
TypeReference<List<SqlVariableDetails>> listTypeReference = new TypeReference<List<SqlVariableDetails>>() {
};
for (Long id : ids) {
List<CoreDatasetTable> datasetTables = datasetTableManage.selectByDatasetGroupId(id);
for (CoreDatasetTable datasetTable : datasetTables) {
if (StringUtils.isNotEmpty(datasetTable.getSqlVariableDetails())) {
List<SqlVariableDetails> defaultsSqlVariableDetails = JsonUtil.parseList(datasetTable.getSqlVariableDetails(), listTypeReference);
if (CollectionUtils.isNotEmpty(defaultsSqlVariableDetails)) {
List<String> fullName = new ArrayList<>();
geFullName(id, fullName);
Collections.reverse(fullName);
List<String> finalFullName = fullName;
defaultsSqlVariableDetails.forEach(sqlVariableDetails -> {
sqlVariableDetails.setDatasetGroupId(id);
sqlVariableDetails.setDatasetTableId(datasetTable.getId());
sqlVariableDetails.setDatasetFullName(String.join("/", finalFullName));
});
}
list.addAll(defaultsSqlVariableDetails);
}
}
}
list.forEach(sqlVariableDetail -> {
sqlVariableDetail.setId(sqlVariableDetail.getDatasetTableId() + "|DE|" + sqlVariableDetail.getVariableName());
sqlVariableDetail.setDeType(FieldUtils.transType2DeType(sqlVariableDetail.getType().get(0).contains("DATETIME") ? "DATETIME" : sqlVariableDetail.getType().get(0)));
});
return list;
}
public void checkMove(DatasetGroupInfoDTO datasetGroupInfoDTO) {
if (Objects.equals(datasetGroupInfoDTO.getId(), datasetGroupInfoDTO.getPid())) {
DEException.throwException(Translator.get("i18n_pid_not_eq_id"));
}
List<Long> ids = new ArrayList<>();
getParents(datasetGroupInfoDTO.getPid(), ids);
if (ids.contains(datasetGroupInfoDTO.getId())) {
DEException.throwException(Translator.get("i18n_pid_not_eq_id"));
}
}
private void getParents(Long pid, List<Long> ids) {
CoreDatasetGroup parent = coreDatasetGroupMapper.selectById(pid);// 查找父级folder
ids.add(parent.getId());
if (parent.getPid() != null && parent.getPid() != 0) {
getParents(parent.getPid(), ids);
}
}
public void geFullName(Long pid, List<String> fullName) {
CoreDatasetGroup parent = coreDatasetGroupMapper.selectById(pid);// 查找父级folder
if (parent == null) {
return;
}
fullName.add(parent.getName());
if (parent.getId().equals(parent.getPid())) {
return;
}
if (parent.getPid() != null && parent.getPid() != 0) {
geFullName(parent.getPid(), fullName);
}
}
public List<DatasetTableDTO> getDetailWithPerm(List<Long> ids) {
var result = new ArrayList<DatasetTableDTO>();
if (CollectionUtils.isNotEmpty(ids)) {
var dsList = coreDatasetGroupMapper.selectBatchIds(ids);
if (CollectionUtils.isNotEmpty(dsList)) {
dsList.forEach(ds -> {
DatasetTableDTO dto = new DatasetTableDTO();
BeanUtils.copyBean(dto, ds);
var fields = datasetTableFieldManage.listFieldsWithPermissions(ds.getId());
List<DatasetTableFieldDTO> dimensionList = fields.stream().filter(ele -> StringUtils.equalsIgnoreCase(ele.getGroupType(), "d")).toList();
List<DatasetTableFieldDTO> quotaList = fields.stream().filter(ele -> StringUtils.equalsIgnoreCase(ele.getGroupType(), "q")).toList();
Map<String, List<DatasetTableFieldDTO>> map = new LinkedHashMap<>();
map.put("dimensionList", dimensionList);
map.put("quotaList", quotaList);
dto.setFields(map);
result.add(dto);
});
}
}
return result;
}
}

View File

@ -0,0 +1,551 @@
package io.dataease.dataset.manage;
import io.dataease.api.dataset.union.*;
import io.dataease.api.permissions.auth.dto.BusiPerCheckDTO;
import io.dataease.api.permissions.dataset.api.RowPermissionsApi;
import io.dataease.api.permissions.user.vo.UserFormVO;
import io.dataease.commons.utils.SqlparserUtils;
import io.dataease.constant.AuthEnum;
import io.dataease.dataset.constant.DatasetTableType;
import io.dataease.dataset.utils.DatasetTableTypeConstants;
import io.dataease.dataset.utils.SqlUtils;
import io.dataease.dataset.utils.TableUtils;
import io.dataease.datasource.dao.auto.entity.CoreDatasource;
import io.dataease.datasource.dao.auto.mapper.CoreDatasourceMapper;
import io.dataease.datasource.manage.DataSourceManage;
import io.dataease.datasource.manage.EngineManage;
import io.dataease.engine.constant.ExtFieldConstant;
import io.dataease.constant.SQLConstants;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.api.PluginManageApi;
import io.dataease.extensions.datasource.dto.DatasetTableDTO;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.dto.DsTypeDTO;
import io.dataease.extensions.datasource.factory.ProviderFactory;
import io.dataease.extensions.datasource.model.SQLObj;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.datasource.vo.DatasourceConfiguration;
import io.dataease.extensions.datasource.vo.XpackPluginsDatasourceVO;
import io.dataease.extensions.view.dto.ChartExtFilterDTO;
import io.dataease.extensions.view.dto.ChartExtRequest;
import io.dataease.extensions.view.dto.SqlVariableDetails;
import io.dataease.i18n.Translator;
import io.dataease.license.utils.LicenseUtil;
import io.dataease.system.manage.CorePermissionManage;
import io.dataease.utils.AuthUtils;
import io.dataease.utils.BeanUtils;
import io.dataease.utils.JsonUtil;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Author Junjun
*/
@Component
public class DatasetSQLManage {
@Resource
private CoreDatasourceMapper coreDatasourceMapper;
@Resource
private EngineManage engineManage;
@Resource
private CorePermissionManage corePermissionManage;
@Autowired(required = false)
private PluginManageApi pluginManage;
@Autowired(required = false)
private RowPermissionsApi rowPermissionsApi;
@Resource
private DataSourceManage dataSourceManage;
private RowPermissionsApi getRowPermissionsApi() {
return rowPermissionsApi;
}
private static Logger logger = LoggerFactory.getLogger(DatasetSQLManage.class);
private List<SqlVariableDetails> filterParameters(ChartExtRequest chartExtRequest, Long datasetTableId) {
List<SqlVariableDetails> parameters = new ArrayList<>();
if (chartExtRequest != null && ObjectUtils.isNotEmpty(chartExtRequest.getOuterParamsFilters())) {
for (ChartExtFilterDTO filterDTO : chartExtRequest.getOuterParamsFilters()) {
if (CollectionUtils.isEmpty(filterDTO.getValue())) {
continue;
}
filterParametersAdaptor(parameters, filterDTO, datasetTableId);
}
}
if (chartExtRequest != null && ObjectUtils.isNotEmpty(chartExtRequest.getWebParamsFilters())) {
for (ChartExtFilterDTO filterDTO : chartExtRequest.getWebParamsFilters()) {
if (CollectionUtils.isEmpty(filterDTO.getValue())) {
continue;
}
filterParametersAdaptor(parameters, filterDTO, datasetTableId);
}
}
if (chartExtRequest != null && ObjectUtils.isNotEmpty(chartExtRequest.getFilter())) {
for (ChartExtFilterDTO filterDTO : chartExtRequest.getFilter()) {
if (CollectionUtils.isEmpty(filterDTO.getValue())) {
continue;
}
filterParametersAdaptor(parameters, filterDTO, datasetTableId);
}
}
return parameters;
}
private void filterParametersAdaptor(List<SqlVariableDetails> parameters, ChartExtFilterDTO filterDTO, Long datasetTableId) {
if (ObjectUtils.isNotEmpty(filterDTO.getParameters())) {
for (SqlVariableDetails parameter : filterDTO.getParameters()) {
if (parameter.getDatasetTableId().equals(datasetTableId)) {
parameter.setValue(filterDTO.getValue());
parameter.setOperator(filterDTO.getOperator());
parameters.add(parameter);
}
}
}
}
public Map<String, Object> getUnionSQLForEdit(DatasetGroupInfoDTO dataTableInfoDTO, ChartExtRequest chartExtRequest) throws Exception {
Map<Long, DatasourceSchemaDTO> dsMap = new LinkedHashMap<>();
List<UnionDTO> union = dataTableInfoDTO.getUnion();
// 所有选中的字段即select后的查询字段
Map<String, String[]> checkedInfo = new LinkedHashMap<>();
List<UnionParamDTO> unionList = new ArrayList<>();
List<DatasetTableFieldDTO> checkedFields = new ArrayList<>();
String sql = "";
if (ObjectUtils.isEmpty(union)) {
return null;
}
Set<Long> allDs = getAllDs(union);
boolean isCross = allDs.size() > 1;
DatasetTableDTO currentDs = union.get(0).getCurrentDs();
// get datasource and schema,put map
String tableSchema = putObj2Map(dsMap, currentDs, isCross);
// get table
DatasetTableInfoDTO infoDTO = JsonUtil.parseObject(currentDs.getInfo(), DatasetTableInfoDTO.class);
SQLObj tableName = getUnionTable(currentDs, infoDTO, tableSchema, 0, filterParameters(chartExtRequest, currentDs.getId()), chartExtRequest == null, isCross, dsMap);
for (int i = 0; i < union.size(); i++) {
UnionDTO unionDTO = union.get(i);
DatasetTableDTO datasetTable = unionDTO.getCurrentDs();
DatasetTableInfoDTO tableInfo = JsonUtil.parseObject(datasetTable.getInfo(), DatasetTableInfoDTO.class);
String schema;
if (dsMap.containsKey(datasetTable.getDatasourceId())) {
schema = dsMap.get(datasetTable.getDatasourceId()).getSchemaAlias();
} else {
schema = putObj2Map(dsMap, datasetTable, isCross);
}
SQLObj table = getUnionTable(datasetTable, tableInfo, schema, i, filterParameters(chartExtRequest, currentDs.getId()), chartExtRequest == null, isCross, dsMap);
// 获取前端传过来选中的字段
List<DatasetTableFieldDTO> fields = unionDTO.getCurrentDsFields();
fields = fields.stream().filter(DatasetTableFieldDTO::getChecked).collect(Collectors.toList());
String[] array = fields.stream()
.map(f -> {
String alias;
if (StringUtils.isEmpty(f.getDataeaseName())) {
alias = TableUtils.fieldNameShort(table.getTableAlias() + "_" + f.getOriginName());
} else {
alias = f.getDataeaseName();
}
f.setFieldShortName(alias);
f.setDataeaseName(f.getFieldShortName());
f.setDatasetTableId(datasetTable.getId());
String prefix = "";
String suffix = "";
DsTypeDTO datasourceType = getDatasourceType(dsMap, datasetTable.getDatasourceId());
if (Objects.equals(f.getExtField(), ExtFieldConstant.EXT_NORMAL)) {
if (isCross) {
prefix = "`";
suffix = "`";
} else {
prefix = datasourceType.getPrefix();
suffix = datasourceType.getSuffix();
}
}
if (StringUtils.equalsIgnoreCase(datasourceType.getType(), "es")) {
return table.getTableAlias() + "." + prefix + f.getOriginName() + suffix;
} else {
return table.getTableAlias() + "." + prefix + f.getOriginName() + suffix + " AS " + prefix + alias + suffix;
}
})
.toArray(String[]::new);
checkedInfo.put(table.getTableAlias(), array);
checkedFields.addAll(fields);
// 获取child的fields和union
if (!CollectionUtils.isEmpty(unionDTO.getChildrenDs())) {
getUnionForEdit(datasetTable, table, unionDTO.getChildrenDs(), checkedInfo, unionList, checkedFields, dsMap, chartExtRequest, isCross);
}
}
// build sql
boolean isFullJoin = false;
if (!CollectionUtils.isEmpty(unionList)) {
// field
StringBuilder field = new StringBuilder();
for (Map.Entry<String, String[]> next : checkedInfo.entrySet()) {
if (next.getValue().length > 0) {
field.append(StringUtils.join(next.getValue(), ",")).append(",");
}
}
String f = subPrefixSuffixChar(field.toString());
// join
StringBuilder join = new StringBuilder();
for (UnionParamDTO unionParamDTO : unionList) {
// get join type
String joinType = convertUnionTypeToSQL(unionParamDTO.getUnionType());
// 如果不是全连接则需要校验连接方式
if (!isFullJoin) {
if (StringUtils.equalsIgnoreCase(unionParamDTO.getUnionType(), "full")) {
isFullJoin = true;
}
}
SQLObj parentSQLObj = unionParamDTO.getParentSQLObj();
SQLObj currentSQLObj = unionParamDTO.getCurrentSQLObj();
DatasetTableDTO parentDs = unionParamDTO.getParentDs();
DatasetTableDTO currentDs1 = unionParamDTO.getCurrentDs();
String ts = "";
String tablePrefix = "";
String tableSuffix = "";
if (ObjectUtils.isNotEmpty(currentSQLObj.getTableSchema())) {
if (isCross) {
tablePrefix = "`";
tableSuffix = "`";
} else {
DsTypeDTO datasourceType = getDatasourceType(dsMap, currentDs1.getDatasourceId());
tablePrefix = datasourceType.getPrefix();
tableSuffix = datasourceType.getSuffix();
}
ts = tablePrefix + currentSQLObj.getTableSchema() + tableSuffix + ".";
}
// build join
join.append(" ").append(joinType).append(" ")
.append(ts)
.append(tablePrefix + currentSQLObj.getTableName() + tableSuffix)
.append(" ").append(currentSQLObj.getTableAlias()).append(" ")
.append(" ON ");
if (unionParamDTO.getUnionFields().size() == 0) {
DEException.throwException(Translator.get("i18n_union_field_can_not_empty"));
}
for (int i = 0; i < unionParamDTO.getUnionFields().size(); i++) {
UnionItemDTO unionItemDTO = unionParamDTO.getUnionFields().get(i);
// 通过field id取得field详情并且以第一组为准寻找dataset table
DatasetTableFieldDTO parentField = unionItemDTO.getParentField();
DatasetTableFieldDTO currentField = unionItemDTO.getCurrentField();
String pPrefix = "";
String pSuffix = "";
if (Objects.equals(parentField.getExtField(), ExtFieldConstant.EXT_NORMAL)) {
if (isCross) {
pPrefix = "`";
pSuffix = "`";
} else {
DsTypeDTO datasourceType = getDatasourceType(dsMap, parentDs.getDatasourceId());
pPrefix = datasourceType.getPrefix();
pSuffix = datasourceType.getSuffix();
}
}
String cPrefix = "";
String cSuffix = "";
if (Objects.equals(currentField.getExtField(), ExtFieldConstant.EXT_NORMAL)) {
if (isCross) {
cPrefix = "`";
cSuffix = "`";
} else {
DsTypeDTO datasourceType = getDatasourceType(dsMap, currentDs1.getDatasourceId());
cPrefix = datasourceType.getPrefix();
cSuffix = datasourceType.getSuffix();
}
}
join.append(parentSQLObj.getTableAlias()).append(".")
.append(pPrefix + parentField.getOriginName() + pSuffix)
.append(" = ")
.append(currentSQLObj.getTableAlias()).append(".")
.append(cPrefix + currentField.getOriginName() + cSuffix);
if (i < unionParamDTO.getUnionFields().size() - 1) {
join.append(" AND ");
}
}
}
if (StringUtils.isEmpty(f)) {
DEException.throwException(Translator.get("i18n_union_ds_no_checked"));
}
sql = MessageFormat.format("SELECT {0} FROM {1}", f, TableUtils.getTableAndAlias(tableName, getDatasourceType(dsMap, currentDs.getDatasourceId()), isCross)) + join.toString();
} else {
String f = StringUtils.join(checkedInfo.get(tableName.getTableAlias()), ",");
if (StringUtils.isEmpty(f)) {
DEException.throwException(Translator.get("i18n_union_ds_no_checked"));
}
sql = MessageFormat.format("SELECT {0} FROM {1}", f, TableUtils.getTableAndAlias(tableName, getDatasourceType(dsMap, currentDs.getDatasourceId()), isCross));
}
logger.debug("calcite origin sql: " + sql);
Map<String, Object> map = new HashMap<>();
map.put("sql", sql);
map.put("field", checkedFields);
map.put("join", unionList);
map.put("dsMap", dsMap);
map.put("isFullJoin", isFullJoin);
return map;
}
// 递归计算出所有子级的checkedFields和unionParam
private void getUnionForEdit(DatasetTableDTO parentTable, SQLObj parentSQLObj,
List<UnionDTO> childrenDs, Map<String, String[]> checkedInfo,
List<UnionParamDTO> unionList, List<DatasetTableFieldDTO> checkedFields,
Map<Long, DatasourceSchemaDTO> dsMap, ChartExtRequest chartExtRequest,
boolean isCross) throws Exception {
for (int i = 0; i < childrenDs.size(); i++) {
int index = unionList.size() + 1;
UnionDTO unionDTO = childrenDs.get(i);
DatasetTableDTO datasetTable = unionDTO.getCurrentDs();
DatasetTableInfoDTO tableInfo = JsonUtil.parseObject(datasetTable.getInfo(), DatasetTableInfoDTO.class);
String schema;
if (dsMap.containsKey(datasetTable.getDatasourceId())) {
schema = dsMap.get(datasetTable.getDatasourceId()).getSchemaAlias();
} else {
schema = putObj2Map(dsMap, datasetTable, isCross);
}
SQLObj table = getUnionTable(datasetTable, tableInfo, schema, index, filterParameters(chartExtRequest, datasetTable.getId()), chartExtRequest == null, isCross, dsMap);
List<DatasetTableFieldDTO> fields = unionDTO.getCurrentDsFields();
fields = fields.stream().filter(DatasetTableFieldDTO::getChecked).collect(Collectors.toList());
String[] array = fields.stream()
.map(f -> {
String alias;
if (StringUtils.isEmpty(f.getDataeaseName())) {
alias = TableUtils.fieldNameShort(table.getTableAlias() + "_" + f.getOriginName());
} else {
alias = f.getDataeaseName();
}
f.setFieldShortName(alias);
f.setDataeaseName(f.getFieldShortName());
f.setDatasetTableId(datasetTable.getId());
String prefix = "";
String suffix = "";
if (Objects.equals(f.getExtField(), ExtFieldConstant.EXT_NORMAL)) {
if (isCross) {
prefix = "`";
suffix = "`";
} else {
DsTypeDTO datasourceType = getDatasourceType(dsMap, datasetTable.getDatasourceId());
prefix = datasourceType.getPrefix();
suffix = datasourceType.getSuffix();
}
}
return table.getTableAlias() + "." + prefix + f.getOriginName() + suffix + " AS " + prefix + alias + suffix;
})
.toArray(String[]::new);
checkedInfo.put(table.getTableAlias(), array);
checkedFields.addAll(fields);
UnionParamDTO unionToParent = unionDTO.getUnionToParent();
// 设置关联关系中两个table信息
unionToParent.setParentDs(parentTable);
unionToParent.setParentSQLObj(parentSQLObj);
unionToParent.setCurrentDs(datasetTable);
unionToParent.setCurrentSQLObj(table);
unionList.add(unionToParent);
if (!CollectionUtils.isEmpty(unionDTO.getChildrenDs())) {
getUnionForEdit(datasetTable, table, unionDTO.getChildrenDs(), checkedInfo, unionList, checkedFields, dsMap, chartExtRequest, isCross);
}
}
}
private Set<Long> getAllDs(List<UnionDTO> union) {
Set<Long> set = new HashSet<>();
for (UnionDTO unionDTO : union) {
Long datasourceId = unionDTO.getCurrentDs().getDatasourceId();
set.add(datasourceId);
getChildrenDs(unionDTO.getChildrenDs(), set);
}
return set;
}
private void getChildrenDs(List<UnionDTO> childrenDs, Set<Long> set) {
for (UnionDTO unionDTO : childrenDs) {
set.add(unionDTO.getCurrentDs().getDatasourceId());
if (!CollectionUtils.isEmpty(unionDTO.getChildrenDs())) {
getChildrenDs(unionDTO.getChildrenDs(), set);
}
}
}
private DsTypeDTO getDatasourceType(Map<Long, DatasourceSchemaDTO> dsMap, Long datasourceId) {
DatasourceSchemaDTO datasourceSchemaDTO = dsMap.get(datasourceId);
String type;
if (datasourceSchemaDTO == null) {
CoreDatasource coreDatasource = dataSourceManage.getCoreDatasource(datasourceId);
if (coreDatasource == null) {
DEException.throwException(Translator.get("i18n_dataset_ds_error") + ",ID:" + datasourceId);
}
type = engineManage.getDeEngine().getType();
} else {
type = datasourceSchemaDTO.getType();
}
if (Arrays.stream(DatasourceConfiguration.DatasourceType.values()).map(DatasourceConfiguration.DatasourceType::getType).toList().contains(type)) {
DatasourceConfiguration.DatasourceType datasourceType = DatasourceConfiguration.DatasourceType.valueOf(type);
DsTypeDTO dto = new DsTypeDTO();
BeanUtils.copyBean(dto, datasourceType);
return dto;
} else {
if (LicenseUtil.licenseValid()) {
List<XpackPluginsDatasourceVO> xpackPluginsDatasourceVOS = pluginManage.queryPluginDs();
List<XpackPluginsDatasourceVO> list = xpackPluginsDatasourceVOS.stream().filter(ele -> StringUtils.equals(ele.getType(), type)).toList();
if (ObjectUtils.isNotEmpty(list)) {
XpackPluginsDatasourceVO first = list.getFirst();
DsTypeDTO dto = new DsTypeDTO();
dto.setName(first.getName());
dto.setCatalog(first.getCategory());
dto.setType(first.getType());
dto.setPrefix(first.getPrefix());
dto.setSuffix(first.getSuffix());
return dto;
} else {
DEException.throwException(Translator.get("i18n_dataset_plugin_error"));
}
}
return null;
}
}
public String subPrefixSuffixChar(String str) {
while (StringUtils.startsWith(str, ",")) {
str = str.substring(1, str.length());
}
while (StringUtils.endsWith(str, ",")) {
str = str.substring(0, str.length() - 1);
}
return str;
}
private String convertUnionTypeToSQL(String unionType) {
switch (unionType) {
case "1:1":
case "inner":
return " INNER JOIN ";
case "1:N":
case "left":
return " LEFT JOIN ";
case "N:1":
case "right":
return " RIGHT JOIN ";
case "N:N":
case "full":
return " FULL JOIN ";
default:
return " INNER JOIN ";
}
}
private UserFormVO getUserEntity() {
if (getRowPermissionsApi() == null) {
return null;
}
return getRowPermissionsApi().getUserById(AuthUtils.getUser().getUserId());
}
private SQLObj getUnionTable(DatasetTableDTO currentDs, DatasetTableInfoDTO infoDTO, String tableSchema, int index, List<SqlVariableDetails> parameters, boolean isFromDataSet, boolean isCross, Map<Long, DatasourceSchemaDTO> dsMap) {
SQLObj tableObj;
String tableAlias = String.format(SQLConstants.TABLE_ALIAS_PREFIX, index);
if (StringUtils.equalsIgnoreCase(currentDs.getType(), DatasetTableTypeConstants.DATASET_TABLE_DB)) {
tableObj = SQLObj.builder().tableSchema(tableSchema).tableName(infoDTO.getTable()).tableAlias(tableAlias).build();
} else if (StringUtils.equalsIgnoreCase(currentDs.getType(), DatasetTableTypeConstants.DATASET_TABLE_SQL)) {
Provider provider = ProviderFactory.getProvider(dsMap.entrySet().iterator().next().getValue().getType());
// parser sql params and replace default value
String s = new String(Base64.getDecoder().decode(infoDTO.getSql()));
String sql = new SqlparserUtils().handleVariableDefaultValue(s, currentDs.getSqlVariableDetails(), false, isFromDataSet, parameters, isCross, dsMap, pluginManage, getUserEntity());
sql = provider.replaceComment(sql);
// add table schema
if (isCross) {
sql = SqlUtils.addSchema(sql, tableSchema);
}
tableObj = SQLObj.builder().tableSchema("").tableName("(" + sql + ")").tableAlias(tableAlias).build();
} else {
// excel,api
tableObj = SQLObj.builder().tableSchema(tableSchema).tableName(infoDTO.getTable()).tableAlias(tableAlias).build();
}
return tableObj;
}
private String putObj2Map(Map<Long, DatasourceSchemaDTO> dsMap, DatasetTableDTO ds, boolean isCross) throws Exception {
// 通过datasource id校验数据源权限
BusiPerCheckDTO dto = new BusiPerCheckDTO();
dto.setId(ds.getDatasourceId());
dto.setAuthEnum(AuthEnum.READ);
boolean checked = corePermissionManage.checkAuth(dto);
if (!checked) {
DEException.throwException(Translator.get("i18n_no_datasource_permission"));
}
String schemaAlias;
if (StringUtils.equalsIgnoreCase(ds.getType(), DatasetTableType.DB) || StringUtils.equalsIgnoreCase(ds.getType(), DatasetTableType.SQL)) {
CoreDatasource coreDatasource = dataSourceManage.getCoreDatasource(ds.getDatasourceId());
if (coreDatasource == null) {
DEException.throwException(Translator.get("i18n_dataset_ds_error") + ",ID:" + ds.getDatasourceId());
}
if (StringUtils.equalsIgnoreCase("excel", coreDatasource.getType()) || coreDatasource.getType().contains(DatasourceConfiguration.DatasourceType.API.name())) {
coreDatasource = engineManage.getDeEngine();
}
Map map = JsonUtil.parseObject(coreDatasource.getConfiguration(), Map.class);
if (!isCross && ObjectUtils.isNotEmpty(map.get("schema"))) {
schemaAlias = (String) map.get("schema");
} else {
schemaAlias = String.format(SQLConstants.SCHEMA, coreDatasource.getId());
}
if (!dsMap.containsKey(coreDatasource.getId())) {
DatasourceSchemaDTO datasourceSchemaDTO = new DatasourceSchemaDTO();
BeanUtils.copyBean(datasourceSchemaDTO, coreDatasource);
datasourceSchemaDTO.setSchemaAlias(schemaAlias);
dsMap.put(coreDatasource.getId(), datasourceSchemaDTO);
}
} else if (StringUtils.equalsIgnoreCase(ds.getType(), DatasetTableType.Es)) {
CoreDatasource coreDatasource = dataSourceManage.getCoreDatasource(ds.getDatasourceId());
schemaAlias = String.format(SQLConstants.SCHEMA, coreDatasource.getId());
if (!dsMap.containsKey(coreDatasource.getId())) {
DatasourceSchemaDTO datasourceSchemaDTO = new DatasourceSchemaDTO();
BeanUtils.copyBean(datasourceSchemaDTO, coreDatasource);
datasourceSchemaDTO.setSchemaAlias(schemaAlias);
dsMap.put(coreDatasource.getId(), datasourceSchemaDTO);
}
} else {
CoreDatasource coreDatasource = engineManage.getDeEngine();
schemaAlias = String.format(SQLConstants.SCHEMA, coreDatasource.getId());
if (!dsMap.containsKey(coreDatasource.getId())) {
DatasourceSchemaDTO datasourceSchemaDTO = new DatasourceSchemaDTO();
BeanUtils.copyBean(datasourceSchemaDTO, coreDatasource);
datasourceSchemaDTO.setSchemaAlias(schemaAlias);
dsMap.put(coreDatasource.getId(), datasourceSchemaDTO);
}
}
return schemaAlias;
}
}

View File

@ -0,0 +1,332 @@
package io.dataease.dataset.manage;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import io.dataease.api.dataset.union.DatasetGroupInfoDTO;
import io.dataease.dataset.dao.auto.entity.CoreDatasetTableField;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetTableFieldMapper;
import io.dataease.dataset.utils.TableUtils;
import io.dataease.engine.constant.ExtFieldConstant;
import io.dataease.engine.func.FunctionConstant;
import io.dataease.engine.utils.Utils;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.api.PluginManageApi;
import io.dataease.extensions.datasource.dto.CalParam;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.dto.FieldGroupDTO;
import io.dataease.extensions.datasource.model.SQLObj;
import io.dataease.extensions.view.dto.ColumnPermissionItem;
import io.dataease.i18n.Translator;
import io.dataease.utils.AuthUtils;
import io.dataease.utils.BeanUtils;
import io.dataease.utils.IDUtils;
import io.dataease.utils.JsonUtil;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Author Junjun
*/
@Component
@Transactional
public class DatasetTableFieldManage {
@Resource
private CoreDatasetTableFieldMapper coreDatasetTableFieldMapper;
@Resource
private PermissionManage permissionManage;
@Resource
private DatasetSQLManage datasetSQLManage;
@Resource
private DatasetGroupManage datasetGroupManage;
@Autowired(required = false)
private PluginManageApi pluginManage;
public void save(CoreDatasetTableField coreDatasetTableField) {
checkNameLength(coreDatasetTableField.getName());
if (ObjectUtils.isEmpty(coreDatasetTableField.getId())) {
coreDatasetTableField.setId(IDUtils.snowID());
coreDatasetTableFieldMapper.insert(coreDatasetTableField);
} else {
coreDatasetTableFieldMapper.updateById(coreDatasetTableField);
}
}
public DatasetTableFieldDTO chartFieldSave(DatasetTableFieldDTO datasetTableFieldDTO) {
checkNameLength(datasetTableFieldDTO.getName());
CoreDatasetTableField coreDatasetTableField = coreDatasetTableFieldMapper.selectById(datasetTableFieldDTO.getId());
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("name", datasetTableFieldDTO.getName());
wrapper.eq("chart_id", datasetTableFieldDTO.getChartId());
if (ObjectUtils.isNotEmpty(coreDatasetTableField)) {
wrapper.ne("id", datasetTableFieldDTO.getId());
}
List<CoreDatasetTableField> fields = coreDatasetTableFieldMapper.selectList(wrapper);
if (ObjectUtils.isNotEmpty(fields)) {
DEException.throwException(Translator.get("i18n_field_name_duplicated"));
}
datasetTableFieldDTO.setDatasetGroupId(null);
return save(datasetTableFieldDTO);
}
/**
* 数据集保存时使用
*
* @param datasetTableFieldDTO
* @return
*/
public DatasetTableFieldDTO save(DatasetTableFieldDTO datasetTableFieldDTO) {
checkNameLength(datasetTableFieldDTO.getName());
CoreDatasetTableField coreDatasetTableField = coreDatasetTableFieldMapper.selectById(datasetTableFieldDTO.getId());
CoreDatasetTableField record = transDTO2Record(datasetTableFieldDTO);
if (ObjectUtils.isEmpty(record.getDataeaseName())) {
String n = TableUtils.fieldNameShort(record.getId() + "");
record.setFieldShortName(n);
record.setDataeaseName(n);
}
if (ObjectUtils.isEmpty(coreDatasetTableField)) {
coreDatasetTableFieldMapper.insert(record);
} else {
coreDatasetTableFieldMapper.updateById(record);
}
return datasetTableFieldDTO;
}
public DatasetTableFieldDTO saveField(DatasetTableFieldDTO datasetTableFieldDTO) {
CoreDatasetTableField record = new CoreDatasetTableField();
if (ObjectUtils.isEmpty(datasetTableFieldDTO.getId())) {
datasetTableFieldDTO.setId(IDUtils.snowID());
BeanUtils.copyBean(record, datasetTableFieldDTO);
coreDatasetTableFieldMapper.insert(record);
} else {
BeanUtils.copyBean(record, datasetTableFieldDTO);
coreDatasetTableFieldMapper.updateById(record);
}
return datasetTableFieldDTO;
}
public List<DatasetTableFieldDTO> getChartCalcFields(Long chartId) {
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("chart_id", chartId);
return transDTO(coreDatasetTableFieldMapper.selectList(wrapper));
}
public void deleteById(Long id) {
coreDatasetTableFieldMapper.deleteById(id);
}
public void deleteByDatasetTableUpdate(Long datasetTableId, List<Long> fieldIds) {
if (!CollectionUtils.isEmpty(fieldIds)) {
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("dataset_table_id", datasetTableId);
wrapper.notIn("id", fieldIds);
coreDatasetTableFieldMapper.delete(wrapper);
}
}
public void deleteByDatasetGroupUpdate(Long datasetGroupId, List<Long> fieldIds) {
if (!CollectionUtils.isEmpty(fieldIds)) {
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("dataset_group_id", datasetGroupId);
wrapper.notIn("id", fieldIds);
coreDatasetTableFieldMapper.delete(wrapper);
}
}
public void deleteByDatasetGroupDelete(Long datasetGroupId) {
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("dataset_group_id", datasetGroupId);
coreDatasetTableFieldMapper.delete(wrapper);
}
public void deleteByChartId(Long chartId) {
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("chart_id", chartId);
coreDatasetTableFieldMapper.delete(wrapper);
}
public List<DatasetTableFieldDTO> selectByDatasetTableId(Long id) {
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("dataset_table_id", id);
return transDTO(coreDatasetTableFieldMapper.selectList(wrapper));
}
public List<DatasetTableFieldDTO> selectByDatasetGroupId(Long id) {
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("dataset_group_id", id);
wrapper.eq("checked", true);
wrapper.isNull("chart_id");
return transDTO(coreDatasetTableFieldMapper.selectList(wrapper));
}
public Map<String, List<DatasetTableFieldDTO>> selectByDatasetGroupIds(List<Long> ids) {
Map<String, List<DatasetTableFieldDTO>> map = new HashMap<>();
for (Long id : ids) {
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("dataset_group_id", id);
wrapper.eq("checked", true);
wrapper.isNull("chart_id");
wrapper.eq("ext_field", 0);
map.put(String.valueOf(id), transDTO(coreDatasetTableFieldMapper.selectList(wrapper)));
}
return map;
}
public List<DatasetTableFieldDTO> selectByFieldIds(List<Long> ids) {
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.in("id", ids);
return transDTO(coreDatasetTableFieldMapper.selectList(wrapper));
}
public DatasetTableFieldDTO selectById(Long id) {
CoreDatasetTableField coreDatasetTableField = coreDatasetTableFieldMapper.selectById(id);
if (coreDatasetTableField == null) return null;
return transObj(coreDatasetTableField);
}
/**
* 返回维度指标列表
*
* @return
*/
public Map<String, List<DatasetTableFieldDTO>> listByDQ(Long id) {
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("dataset_group_id", id);
wrapper.eq("checked", true);
List<DatasetTableFieldDTO> list = transDTO(coreDatasetTableFieldMapper.selectList(wrapper));
List<DatasetTableFieldDTO> dimensionList = list.stream().filter(ele -> StringUtils.equalsIgnoreCase(ele.getGroupType(), "d")).collect(Collectors.toList());
List<DatasetTableFieldDTO> quotaList = list.stream().filter(ele -> StringUtils.equalsIgnoreCase(ele.getGroupType(), "q")).collect(Collectors.toList());
Map<String, List<DatasetTableFieldDTO>> map = new LinkedHashMap<>();
map.put("dimensionList", dimensionList);
map.put("quotaList", quotaList);
return map;
}
public Map<String, List<DatasetTableFieldDTO>> copilotFields(Long id) throws Exception {
DatasetGroupInfoDTO datasetGroupInfoDTO = datasetGroupManage.getDatasetGroupInfoDTO(id, null);
Map<String, Object> sqlMap = datasetSQLManage.getUnionSQLForEdit(datasetGroupInfoDTO, null);
Map<Long, DatasourceSchemaDTO> dsMap = (Map<Long, DatasourceSchemaDTO>) sqlMap.get("dsMap");
boolean crossDs = Utils.isCrossDs(dsMap);
if (crossDs) {
DEException.throwException(Translator.get("i18n_dataset_cross_error"));
}
if (!isCopilotSupport(dsMap)) {
DEException.throwException(Translator.get("i18n_copilot_ds"));
}
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("dataset_group_id", id);
wrapper.eq("checked", true);
wrapper.eq("ext_field", 0);
List<DatasetTableFieldDTO> list = transDTO(coreDatasetTableFieldMapper.selectList(wrapper));
Map<String, ColumnPermissionItem> desensitizationList = new HashMap<>();
list = permissionManage.filterColumnPermissions(list, desensitizationList, id, null);
List<DatasetTableFieldDTO> dimensionList = list.stream().filter(ele -> StringUtils.equalsIgnoreCase(ele.getGroupType(), "d")).collect(Collectors.toList());
List<DatasetTableFieldDTO> quotaList = list.stream().filter(ele -> StringUtils.equalsIgnoreCase(ele.getGroupType(), "q")).collect(Collectors.toList());
Map<String, List<DatasetTableFieldDTO>> map = new LinkedHashMap<>();
map.put("dimensionList", dimensionList);
map.put("quotaList", quotaList);
return map;
}
public List<DatasetTableFieldDTO> listFieldsWithPermissions(Long id) {
List<DatasetTableFieldDTO> fields = selectByDatasetGroupId(id);
Map<String, ColumnPermissionItem> desensitizationList = new HashMap<>();
Long userId = AuthUtils.getUser() == null ? null : AuthUtils.getUser().getUserId();
List<DatasetTableFieldDTO> tmp = permissionManage
.filterColumnPermissions(fields, desensitizationList, id, userId)
.stream()
.sorted(Comparator.comparing(DatasetTableFieldDTO::getGroupType))
.toList();
tmp.forEach(ele -> ele.setDesensitized(desensitizationList.containsKey(ele.getDataeaseName())));
return tmp;
}
public List<DatasetTableFieldDTO> listFieldsWithPermissionsRemoveAgg(Long id) {
List<DatasetTableFieldDTO> fields = selectByDatasetGroupId(id);
Map<String, ColumnPermissionItem> desensitizationList = new HashMap<>();
Long userId = AuthUtils.getUser() == null ? null : AuthUtils.getUser().getUserId();
SQLObj tableObj = new SQLObj();
tableObj.setTableAlias("");
List<DatasetTableFieldDTO> tmp = permissionManage
.filterColumnPermissions(fields, desensitizationList, id, userId)
.stream()
.filter(ele -> {
boolean flag = true;
if (Objects.equals(ele.getExtField(), ExtFieldConstant.EXT_CALC)) {
String originField = Utils.calcFieldRegex(ele, tableObj, fields, true, null, Utils.mergeParam(Utils.getParams(fields), null), pluginManage);
for (String func : FunctionConstant.AGG_FUNC) {
if (Utils.matchFunction(func, originField)) {
flag = false;
break;
}
}
}
return flag;
})
.sorted(Comparator.comparing(DatasetTableFieldDTO::getGroupType))
.toList();
tmp.forEach(ele -> ele.setDesensitized(desensitizationList.containsKey(ele.getDataeaseName())));
return tmp;
}
public DatasetTableFieldDTO transObj(CoreDatasetTableField ele) {
DatasetTableFieldDTO dto = new DatasetTableFieldDTO();
if (ele == null) return null;
BeanUtils.copyBean(dto, ele);
if (StringUtils.isNotEmpty(ele.getParams())) {
TypeReference<List<CalParam>> tokenType = new TypeReference<>() {
};
List<CalParam> calParams = JsonUtil.parseList(ele.getParams(), tokenType);
dto.setParams(calParams);
}
if (StringUtils.isNotEmpty(ele.getGroupList())) {
TypeReference<List<FieldGroupDTO>> groupTokenType = new TypeReference<>() {
};
List<FieldGroupDTO> fieldGroups = JsonUtil.parseList(ele.getGroupList(), groupTokenType);
dto.setGroupList(fieldGroups);
}
return dto;
}
public List<DatasetTableFieldDTO> transDTO(List<CoreDatasetTableField> list) {
if (!CollectionUtils.isEmpty(list)) {
return list.stream().map(this::transObj).collect(Collectors.toList());
} else {
return new ArrayList<>();
}
}
private CoreDatasetTableField transDTO2Record(DatasetTableFieldDTO dto) {
CoreDatasetTableField record = new CoreDatasetTableField();
BeanUtils.copyBean(record, dto);
if (ObjectUtils.isNotEmpty(dto.getParams())) {
record.setParams(JsonUtil.toJSONString(dto.getParams()).toString());
}
if (ObjectUtils.isNotEmpty(dto.getGroupList())) {
record.setGroupList(JsonUtil.toJSONString(dto.getGroupList()).toString());
}
return record;
}
private void checkNameLength(String name) {
if (name != null && name.length() > 100) {
DEException.throwException(Translator.get("i18n_name_limit_100"));
}
}
public boolean isCopilotSupport(Map<Long, DatasourceSchemaDTO> dsMap) {
DatasourceSchemaDTO value = dsMap.entrySet().iterator().next().getValue();
return StringUtils.equalsIgnoreCase(value.getType(), "mysql");
}
}

Some files were not shown because too many files have changed in this diff Show More