Merge branch 'dev-v2' into pr@dev-v2@feat_report_task_retry
This commit is contained in:
commit
0eb05103ed
@ -0,0 +1,104 @@
|
||||
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();
|
||||
httpClientConfig.addHeader("Authorization", basicAuth(getConfig().getUsername(), getConfig().getPwd()));
|
||||
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();
|
||||
httpClientConfig.addHeader("Authorization", basicAuth(getConfig().getUsername(), getConfig().getPwd()));
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
@ -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 +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
@ -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 +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
@ -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> {
|
||||
|
||||
}
|
||||
@ -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> {
|
||||
|
||||
}
|
||||
@ -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> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,377 @@
|
||||
package io.dataease.copilot.manage;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import io.dataease.api.copilot.dto.DESendDTO;
|
||||
import io.dataease.api.copilot.dto.MsgDTO;
|
||||
import io.dataease.api.copilot.dto.ReceiveDTO;
|
||||
import io.dataease.api.copilot.dto.TokenDTO;
|
||||
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.engine.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.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;
|
||||
|
||||
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("跨源数据集不支持该功能");
|
||||
}
|
||||
|
||||
// 调用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 = ds.getType();// 数据库类型,如mysql,oracle等,可能需要映射成copilot需要的类型
|
||||
|
||||
datasetDataManage.buildFieldName(sqlMap, allFields);
|
||||
List<String> strings = transCreateTableFields(allFields);
|
||||
String createSql = "CREATE TABLE de_tmp_table (" + StringUtils.join(strings, ",") + ")";
|
||||
logger.info("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());
|
||||
}
|
||||
boolean needOrder = Utils.isNeedOrder(dsList);
|
||||
|
||||
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.info("preview sql: " + querySQL);
|
||||
// todo test
|
||||
String querySQL = sql;
|
||||
|
||||
String copilotSQL = receiveDTO.getSql();
|
||||
// 通过数据源请求数据
|
||||
// 调用数据源的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.info("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.info("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");
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
// 重新构造data
|
||||
Map<String, Object> previewData = buildPreviewData(data, fields, desensitizationList);
|
||||
|
||||
map.put("data", previewData);
|
||||
// map.put("allFields", allFields);// map.data 中包含了fields和data
|
||||
// if (ObjectUtils.isEmpty(dto.getId())) {
|
||||
// map.put("allFields", allFields);
|
||||
// } else {
|
||||
// List<DatasetTableFieldDTO> fieldList = datasetTableFieldManage.selectByDatasetGroupId(dto.getId());
|
||||
// map.put("allFields", fieldList);
|
||||
// }
|
||||
map.put("sql", Base64.getEncoder().encodeToString(s.getBytes()));
|
||||
|
||||
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);
|
||||
receiveDTO = copilotAPI.generateChart(token, deSendDTO);
|
||||
}
|
||||
}
|
||||
|
||||
if (!receiveDTO.getSqlOk() || !receiveDTO.getChartOk()) {
|
||||
DEException.throwException((String) JsonUtil.toJSONString(receiveDTO));
|
||||
}
|
||||
logger.info("Copilot Service SQL: " + receiveDTO.getSql());
|
||||
logger.info("Copilot Service Chart: " + JsonUtil.toJSONString(receiveDTO.getChart()));
|
||||
return receiveDTO;
|
||||
}
|
||||
|
||||
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 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)";
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -195,15 +195,22 @@ public class PermissionManage {
|
||||
DatasetRowPermissionsTreeObj tree = JsonUtil.parseObject(record.getExpressionTree(), DatasetRowPermissionsTreeObj.class);
|
||||
List<DatasetRowPermissionsTreeItem> items = new ArrayList<>();
|
||||
for (DatasetRowPermissionsTreeItem datasetRowPermissionsTreeItem : tree.getItems()) {
|
||||
if (StringUtils.isNotEmpty(userEntity.getAccount()) && datasetRowPermissionsTreeItem.getValue().equalsIgnoreCase("\\$\\{sysParams\\.userId}")) {
|
||||
if (StringUtils.isNotEmpty(userEntity.getAccount()) && datasetRowPermissionsTreeItem.getValue().equalsIgnoreCase("${sysParams.userId}")) {
|
||||
datasetRowPermissionsTreeItem.setValue(userEntity.getAccount());
|
||||
items.add(datasetRowPermissionsTreeItem);
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.isNotEmpty(userEntity.getEmail()) && datasetRowPermissionsTreeItem.getValue().equalsIgnoreCase("\\$\\{sysParams\\.userEmail}")) {
|
||||
if (StringUtils.isNotEmpty(userEntity.getEmail()) && datasetRowPermissionsTreeItem.getValue().equalsIgnoreCase("${sysParams.userEmail}")) {
|
||||
datasetRowPermissionsTreeItem.setValue(userEntity.getEmail());
|
||||
items.add(datasetRowPermissionsTreeItem);
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.isNotEmpty(userEntity.getName()) && datasetRowPermissionsTreeItem.getValue().equalsIgnoreCase("\\$\\{sysParams\\.userName}")) {
|
||||
if (StringUtils.isNotEmpty(userEntity.getName()) && datasetRowPermissionsTreeItem.getValue().equalsIgnoreCase("${sysParams.userName}")) {
|
||||
datasetRowPermissionsTreeItem.setValue(userEntity.getName());
|
||||
items.add(datasetRowPermissionsTreeItem);
|
||||
continue;
|
||||
}
|
||||
|
||||
String value = handleSysVariable(userEntity, datasetRowPermissionsTreeItem.getValue());
|
||||
if (value == null) {
|
||||
continue;
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
DROP TABLE IF EXISTS `core_copilot_msg`;
|
||||
CREATE TABLE `core_copilot_msg` (
|
||||
`id` bigint NOT NULL COMMENT 'ID',
|
||||
`user_id` bigint DEFAULT NULL COMMENT '用户ID',
|
||||
`dataset_group_id` bigint DEFAULT NULL COMMENT '数据集ID',
|
||||
`msg_type` varchar(255) DEFAULT NULL COMMENT 'user or api',
|
||||
`engine_type` varchar(255) DEFAULT NULL COMMENT 'mysql oracle ...',
|
||||
`schema_sql` longtext COMMENT 'create sql',
|
||||
`question` longtext COMMENT '用户提问',
|
||||
`history` longtext COMMENT '历史信息',
|
||||
`copilot_sql` longtext COMMENT 'copilot 返回 sql',
|
||||
`api_msg` longtext COMMENT 'copilot 返回信息',
|
||||
`sql_ok` int DEFAULT NULL COMMENT 'sql 状态',
|
||||
`chart_ok` int DEFAULT NULL COMMENT 'chart 状态',
|
||||
`chart` longtext COMMENT 'chart 内容',
|
||||
`chart_data` longtext COMMENT '视图数据',
|
||||
`exec_sql` longtext COMMENT '执行请求的SQL',
|
||||
`msg_status` int DEFAULT NULL COMMENT 'msg状态,0失败 1成功',
|
||||
`err_msg` longtext COMMENT 'de错误信息',
|
||||
`create_time` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `core_copilot_token`;
|
||||
CREATE TABLE `core_copilot_token` (
|
||||
`id` bigint NOT NULL COMMENT 'ID',
|
||||
`type` varchar(255) DEFAULT NULL COMMENT 'free or license',
|
||||
`token` longtext,
|
||||
`update_time` bigint DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
INSERT INTO `core_copilot_token` VALUES (1, 'free', null, null);
|
||||
INSERT INTO `core_copilot_token` VALUES (2, 'license', null, null);
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `core_copilot_config`;
|
||||
CREATE TABLE `core_copilot_config` (
|
||||
`id` bigint NOT NULL COMMENT 'ID',
|
||||
`copilot_url` varchar(255) DEFAULT NULL,
|
||||
`username` varchar(255) DEFAULT NULL,
|
||||
`pwd` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
INSERT INTO `core_copilot_config` VALUES (1, 'https://copilot-demo.test.fit2cloud.dev:5000', 'xlab', 'Q2Fsb25nQDIwMTU=');
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
@ -113,11 +113,12 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
const onClick = () => {
|
||||
const events = config.value.events
|
||||
Object.keys(events).forEach(event => {
|
||||
currentInstance.ctx[event](events[event])
|
||||
})
|
||||
eventBus.emit('v-click', config.value.id)
|
||||
// do event click
|
||||
// const events = config.value.events
|
||||
// Object.keys(events).forEach(event => {
|
||||
// currentInstance.ctx[event](events[event])
|
||||
// })
|
||||
// eventBus.emit('v-click', config.value.id)
|
||||
}
|
||||
|
||||
const getComponentStyleDefault = style => {
|
||||
|
||||
@ -80,7 +80,12 @@ const show = () => {
|
||||
layerStore.showComponent()
|
||||
menuOpt('show')
|
||||
}
|
||||
|
||||
const categoryChange = type => {
|
||||
if (curComponent.value) {
|
||||
snapshotStore.recordSnapshotCache()
|
||||
curComponent.value['category'] = type
|
||||
}
|
||||
}
|
||||
const rename = () => {
|
||||
emit('rename')
|
||||
menuOpt('rename')
|
||||
@ -222,6 +227,12 @@ const editQueryCriteria = () => {
|
||||
<el-divider class="custom-divider" />
|
||||
<li @click="hide" v-show="curComponent['isShow']">隐藏</li>
|
||||
<li @click="show" v-show="!curComponent['isShow']">取消隐藏</li>
|
||||
<li @click="categoryChange('hidden')" v-show="curComponent['category'] === 'base'">
|
||||
转为隐藏组件
|
||||
</li>
|
||||
<li @click="categoryChange('base')" v-show="curComponent['category'] === 'hidden'">
|
||||
转为基础组件
|
||||
</li>
|
||||
<li @click="lock">锁定</li>
|
||||
<el-divider class="custom-divider" />
|
||||
<li v-if="activePosition === 'aside'" @click="rename">重命名</li>
|
||||
|
||||
@ -9,6 +9,7 @@ import { useI18n } from '@/hooks/web/useI18n'
|
||||
import elementResizeDetectorMaker from 'element-resize-detector'
|
||||
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
|
||||
import CommonStyleSet from '@/custom-component/common/CommonStyleSet.vue'
|
||||
import CommonEvent from '@/custom-component/common/CommonEvent.vue'
|
||||
const snapshotStore = snapshotStoreWithOut()
|
||||
|
||||
const { t } = useI18n()
|
||||
@ -87,6 +88,10 @@ const colorPickerWidth = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const eventsShow = computed(() => {
|
||||
return !dashboardActive.value && ['Picture'].includes(element.value.component)
|
||||
})
|
||||
|
||||
const backgroundCustomShow = computed(() => {
|
||||
return (
|
||||
dashboardActive.value ||
|
||||
@ -149,6 +154,15 @@ const stopEvent = e => {
|
||||
:element="element"
|
||||
></common-style-set>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
v-if="element && element.events && eventsShow"
|
||||
:effect="themes"
|
||||
title="事件"
|
||||
name="style"
|
||||
class="common-style-area"
|
||||
>
|
||||
<common-event :themes="themes" :element="element"></common-event>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, toRefs } from 'vue'
|
||||
import { ElFormItem } from 'element-plus-secondary'
|
||||
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
|
||||
|
||||
const snapshotStore = snapshotStoreWithOut()
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
themes?: EditorTheme
|
||||
element: any
|
||||
}>(),
|
||||
{
|
||||
themes: 'dark'
|
||||
}
|
||||
)
|
||||
const { themes, element } = toRefs(props)
|
||||
|
||||
const eventsInfo = computed(() => {
|
||||
return element.value.events
|
||||
})
|
||||
|
||||
const onEventChange = () => {
|
||||
snapshotStore.recordSnapshotCache('renderChart')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-row class="custom-row">
|
||||
<el-form label-position="top">
|
||||
<el-form-item class="form-item" :class="'form-item-' + themes">
|
||||
<el-checkbox
|
||||
:effect="themes"
|
||||
size="small"
|
||||
v-model="eventsInfo.checked"
|
||||
@change="onEventChange"
|
||||
>开启事件绑定</el-checkbox
|
||||
>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item class="form-item" :class="'form-item-' + themes" style="margin-bottom: 8px">
|
||||
<el-radio-group
|
||||
:effect="themes"
|
||||
v-model="eventsInfo.type"
|
||||
class="radio-span"
|
||||
@change="onEventChange"
|
||||
>
|
||||
<el-radio label="displayChange" :effect="themes"> 开启隐藏组件 </el-radio>
|
||||
<el-radio label="jump" :effect="themes"> 跳转 </el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
@ -8,6 +8,18 @@ export const commonStyle = {
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
export const BASE_EVENTS = {
|
||||
checked: false,
|
||||
type: 'displayChange', // openHidden jump
|
||||
jump: {
|
||||
value: null
|
||||
},
|
||||
displayChange: {
|
||||
value: true, // 事件当前值 false
|
||||
target: 'all'
|
||||
}
|
||||
}
|
||||
|
||||
// 流媒体视频信息配置
|
||||
export const STREAMMEDIALINKS = {
|
||||
videoType: 'flv',
|
||||
@ -159,12 +171,13 @@ export const COMMON_COMPONENT_BACKGROUND_MAP = {
|
||||
export const commonAttr = {
|
||||
animations: [],
|
||||
canvasId: 'canvas-main',
|
||||
events: {},
|
||||
events: BASE_EVENTS,
|
||||
groupStyle: {}, // 当一个组件成为 Group 的子组件时使用
|
||||
isLock: false, // 是否锁定组件
|
||||
maintainRadio: false, // 布局时保持宽高比例
|
||||
aspectRatio: 1, // 锁定时的宽高比例
|
||||
isShow: true, // 是否显示组件
|
||||
category: 'base', //组件类型 base 基础组件 hidden隐藏组件
|
||||
// 当前组件动作
|
||||
dragging: false,
|
||||
resizing: false,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="pic-main">
|
||||
<div class="pic-main" @click="onPictureClick">
|
||||
<img
|
||||
draggable="false"
|
||||
v-if="propValue['url']"
|
||||
@ -20,6 +20,8 @@
|
||||
import { CSSProperties, computed, nextTick, toRefs } from 'vue'
|
||||
import { imgUrlTrans } from '@/utils/imgUtils'
|
||||
import eventBus from '@/utils/eventBus'
|
||||
import { eventStoreWithOut } from '@/store/modules/data-visualization/event'
|
||||
const eventStore = eventStoreWithOut()
|
||||
const props = defineProps({
|
||||
propValue: {
|
||||
type: String,
|
||||
@ -52,7 +54,14 @@ const imageAdapter = computed(() => {
|
||||
}
|
||||
return style as CSSProperties
|
||||
})
|
||||
|
||||
const onPictureClick = e => {
|
||||
if (element.value.events && element.value.events.checked) {
|
||||
if (element.value.events.type === 'displayChange') {
|
||||
// 打开隐藏组件
|
||||
eventStore.displayEventChange(element.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
const uploadImg = () => {
|
||||
nextTick(() => {
|
||||
eventBus.emit('uploadImg')
|
||||
|
||||
@ -733,6 +733,8 @@ export default {
|
||||
table_show_col_tooltip: '开启列头提示',
|
||||
table_show_cell_tooltip: '开启单元格提示',
|
||||
table_show_header_tooltip: '开启表头提示',
|
||||
table_show_summary: '显示总计',
|
||||
table_summary_label: '总计标签',
|
||||
stripe: '斑马纹',
|
||||
start_angle: '起始角度',
|
||||
end_angle: '结束角度',
|
||||
|
||||
@ -256,6 +256,14 @@ declare interface ChartBasicStyle {
|
||||
* 对称柱状图方向
|
||||
*/
|
||||
layout?: 'horizontal' | 'vertical'
|
||||
/**
|
||||
* 汇总表显示总计
|
||||
*/
|
||||
showSummary: boolean
|
||||
/**
|
||||
* 汇总表总计标签
|
||||
*/
|
||||
summaryLabel: string
|
||||
}
|
||||
/**
|
||||
* 表头属性
|
||||
|
||||
@ -3,7 +3,7 @@ import { store } from '../../index'
|
||||
import { dvMainStoreWithOut } from './dvMain'
|
||||
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const { curComponent } = storeToRefs(dvMainStore)
|
||||
const { curComponent, componentData } = storeToRefs(dvMainStore)
|
||||
|
||||
export const eventStore = defineStore('event', {
|
||||
actions: {
|
||||
@ -13,6 +13,15 @@ export const eventStore = defineStore('event', {
|
||||
|
||||
removeEvent(event) {
|
||||
delete curComponent.value.events[event]
|
||||
},
|
||||
|
||||
displayEventChange(component) {
|
||||
component.events.displayChange.value = !component.events.displayChange.value
|
||||
componentData.value.forEach(item => {
|
||||
if (item.category === 'hidden') {
|
||||
item.isShow = component.events.displayChange.value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import componentList, {
|
||||
ACTION_SELECTION,
|
||||
BASE_EVENTS,
|
||||
COMMON_COMPONENT_BACKGROUND_DARK,
|
||||
COMMON_COMPONENT_BACKGROUND_LIGHT
|
||||
} from '@/custom-component/component-list'
|
||||
@ -153,6 +154,12 @@ export function historyAdaptor(
|
||||
if ((!canvasVersion || canvasVersion === 2) && canvasInfo.type === 'dashboard') {
|
||||
matrixAdaptor(componentItem)
|
||||
}
|
||||
// 组件事件适配
|
||||
componentItem.events =
|
||||
componentItem.events && componentItem.events.checked !== undefined
|
||||
? componentItem.events
|
||||
: deepCopy(BASE_EVENTS)
|
||||
componentItem['category'] = componentItem['category'] || 'base'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -2,8 +2,9 @@
|
||||
import { SpreadSheet, Node } from '@antv/s2'
|
||||
import { PropType } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { S2Event } from '@antv/s2'
|
||||
import { S2Event, SortFuncParam } from '@antv/s2'
|
||||
import { SortUp, SortDown, Sort } from '@element-plus/icons-vue'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
const { t } = useI18n()
|
||||
const props = defineProps({
|
||||
@ -12,15 +13,45 @@ const props = defineProps({
|
||||
required: true
|
||||
},
|
||||
meta: {
|
||||
type: Object as PropType<Node>
|
||||
type: Object as PropType<Node>,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
const sort = type => {
|
||||
const sortFunc = (sortParams: SortFuncParam) => {
|
||||
if (!sortParams.sortMethod) {
|
||||
return sortParams.data
|
||||
}
|
||||
const data = cloneDeep(sortParams.data)
|
||||
return data.sort((a, b) => {
|
||||
// 总计行放最后
|
||||
if (a['SUMMARY']) {
|
||||
return 1
|
||||
}
|
||||
const field = sortParams.sortFieldId
|
||||
const aValue = a[field]
|
||||
const bValue = b[field]
|
||||
if (aValue === bValue) {
|
||||
return 0
|
||||
}
|
||||
if (sortParams.sortMethod === 'asc') {
|
||||
if (typeof aValue === 'number') {
|
||||
return aValue - bValue
|
||||
}
|
||||
return aValue < bValue ? 1 : -1
|
||||
}
|
||||
if (typeof aValue === 'number') {
|
||||
return bValue - aValue
|
||||
}
|
||||
return aValue > bValue ? 1 : -1
|
||||
})
|
||||
}
|
||||
const sort = (type?) => {
|
||||
props.table.updateSortMethodMap(props.meta.field, type, true)
|
||||
props.table.emit(S2Event.RANGE_SORT, [
|
||||
{
|
||||
sortFieldId: props.meta.field,
|
||||
sortMethod: type
|
||||
sortMethod: type,
|
||||
sortFunc
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
@ -957,6 +957,33 @@ onMounted(() => {
|
||||
<template #append>%</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="showProperty('showSummary')"
|
||||
class="form-item"
|
||||
:class="'form-item-' + themes"
|
||||
>
|
||||
<el-checkbox
|
||||
size="small"
|
||||
:effect="themes"
|
||||
v-model="state.basicStyleForm.showSummary"
|
||||
@change="changeBasicStyle('showSummary')"
|
||||
>
|
||||
{{ t('chart.table_show_summary') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="showProperty('summaryLabel') && state.basicStyleForm.showSummary"
|
||||
:label="$t('chart.table_summary_label')"
|
||||
:class="'form-item-' + themes"
|
||||
class="form-item"
|
||||
>
|
||||
<el-input
|
||||
v-model="state.basicStyleForm.summaryLabel"
|
||||
type="text"
|
||||
:max-length="10"
|
||||
@blur="changeBasicStyle('summaryLabel')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!--table2 end-->
|
||||
<!--gauge start-->
|
||||
<el-form-item
|
||||
|
||||
@ -1483,7 +1483,9 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = {
|
||||
topN: 5,
|
||||
topNLabel: '其他',
|
||||
gaugeAxisLine: true,
|
||||
gaugePercentLabel: true
|
||||
gaugePercentLabel: true,
|
||||
showSummary: false,
|
||||
summaryLabel: '总计'
|
||||
}
|
||||
|
||||
export const BASE_VIEW_CONFIG = {
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
import { S2ChartView, S2DrawOptions } from '@/views/chart/components/js/panel/types/impl/s2'
|
||||
import { S2Event, S2Options, TableSheet, TableColCell, ViewMeta, TableDataCell } from '@antv/s2'
|
||||
import { parseJson } from '@/views/chart/components/js/util'
|
||||
import { formatterItem, valueFormatter } from '@/views/chart/components/js/formatter'
|
||||
import {
|
||||
copyContent,
|
||||
getCurrentField,
|
||||
SortTooltip
|
||||
} from '@/views/chart/components/js/panel/common/common_table'
|
||||
import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { formatterItem, valueFormatter } from '@/views/chart/components/js/formatter'
|
||||
import { copyContent, SortTooltip } from '@/views/chart/components/js/panel/common/common_table'
|
||||
import { S2ChartView, S2DrawOptions } from '@/views/chart/components/js/panel/types/impl/s2'
|
||||
import { parseJson } from '@/views/chart/components/js/util'
|
||||
import { S2Event, S2Options, TableColCell, TableDataCell, TableSheet, ViewMeta } from '@antv/s2'
|
||||
import { isNumber } from 'lodash-es'
|
||||
import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common'
|
||||
|
||||
const { t } = useI18n()
|
||||
/**
|
||||
@ -17,12 +13,17 @@ const { t } = useI18n()
|
||||
*/
|
||||
export class TableNormal extends S2ChartView<TableSheet> {
|
||||
properties = TABLE_EDITOR_PROPERTY
|
||||
propertyInner = {
|
||||
propertyInner: EditorPropertyInner = {
|
||||
...TABLE_EDITOR_PROPERTY_INNER,
|
||||
'table-header-selector': [
|
||||
...TABLE_EDITOR_PROPERTY_INNER['table-header-selector'],
|
||||
'tableHeaderSort',
|
||||
'showTableHeader'
|
||||
],
|
||||
'basic-style-selector': [
|
||||
...TABLE_EDITOR_PROPERTY_INNER['basic-style-selector'],
|
||||
'showSummary',
|
||||
'summaryLabel'
|
||||
]
|
||||
}
|
||||
axis: AxisType[] = ['xAxis', 'yAxis', 'drill', 'filter']
|
||||
@ -160,6 +161,43 @@ export class TableNormal extends S2ChartView<TableSheet> {
|
||||
// header interaction
|
||||
this.configHeaderInteraction(chart, s2Options)
|
||||
}
|
||||
|
||||
// 总计
|
||||
if (customAttr.basicStyle.showSummary) {
|
||||
// 设置汇总行高度和表头一致
|
||||
const heightByField = {}
|
||||
heightByField[newData.length] = customAttr.tableHeader.tableTitleHeight
|
||||
s2Options.style.rowCfg = { heightByField }
|
||||
// 计算汇总加入到数据里,冻结最后一行
|
||||
s2Options.frozenTrailingRowCount = 1
|
||||
const yAxis = chart.yAxis
|
||||
const xAxis = chart.xAxis
|
||||
const summaryObj = newData.reduce(
|
||||
(p, n) => {
|
||||
yAxis.forEach(axis => {
|
||||
p[axis.dataeaseName] = (n[axis.dataeaseName] || 0) + (p[axis.dataeaseName] || 0)
|
||||
})
|
||||
return p
|
||||
},
|
||||
{ SUMMARY: true }
|
||||
)
|
||||
newData.push(summaryObj)
|
||||
s2Options.dataCell = viewMeta => {
|
||||
if (viewMeta.rowIndex !== newData.length - 1) {
|
||||
return new TableDataCell(viewMeta, viewMeta.spreadsheet)
|
||||
}
|
||||
if (viewMeta.colIndex === 0) {
|
||||
if (customAttr.tableHeader.showIndex) {
|
||||
viewMeta.fieldValue = customAttr.basicStyle.summaryLabel ?? '总计'
|
||||
} else {
|
||||
if (xAxis.length) {
|
||||
viewMeta.fieldValue = customAttr.basicStyle.summaryLabel ?? '总计'
|
||||
}
|
||||
}
|
||||
}
|
||||
return new SummaryCell(viewMeta, viewMeta.spreadsheet)
|
||||
}
|
||||
}
|
||||
// 开始渲染
|
||||
const newChart = new TableSheet(containerDom, s2DataConfig, s2Options)
|
||||
|
||||
@ -211,3 +249,13 @@ export class TableNormal extends S2ChartView<TableSheet> {
|
||||
super('table-normal', [])
|
||||
}
|
||||
}
|
||||
|
||||
class SummaryCell extends TableDataCell {
|
||||
getTextStyle() {
|
||||
return this.theme.colCell.bolderText
|
||||
}
|
||||
getBackgroundColor() {
|
||||
const { backgroundColor, backgroundColorOpacity } = this.theme.colCell.cell
|
||||
return { backgroundColor, backgroundColorOpacity }
|
||||
}
|
||||
}
|
||||
|
||||
@ -344,30 +344,30 @@ function customCalcFunc(query, data, totalCfgMap) {
|
||||
switch (aggregation) {
|
||||
case 'SUM': {
|
||||
return data.reduce((p, n) => {
|
||||
return p + n[query[EXTRA_FIELD]]
|
||||
return p + parseFloat(n[query[EXTRA_FIELD]])
|
||||
}, 0)
|
||||
}
|
||||
case 'AVG': {
|
||||
const sum = data.reduce((p, n) => {
|
||||
return p + n[query[EXTRA_FIELD]]
|
||||
return p + parseFloat(n[query[EXTRA_FIELD]])
|
||||
}, 0)
|
||||
return sum / data.length
|
||||
}
|
||||
case 'MIN': {
|
||||
const result = minBy(data, n => {
|
||||
return n[query[EXTRA_FIELD]]
|
||||
return parseFloat(n[query[EXTRA_FIELD]])
|
||||
})
|
||||
return result?.[query[EXTRA_FIELD]]
|
||||
}
|
||||
case 'MAX': {
|
||||
const result = maxBy(data, n => {
|
||||
return n[query[EXTRA_FIELD]]
|
||||
return parseFloat(n[query[EXTRA_FIELD]])
|
||||
})
|
||||
return result?.[query[EXTRA_FIELD]]
|
||||
}
|
||||
default: {
|
||||
return data.reduce((p, n) => {
|
||||
return p + n[query[EXTRA_FIELD]]
|
||||
return p + parseFloat(n[query[EXTRA_FIELD]])
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -598,7 +598,7 @@ export function mappingColor(value, defaultColor, field, type) {
|
||||
}
|
||||
|
||||
export function handleTableEmptyStrategy(chart: Chart) {
|
||||
let newData = chart.data?.tableRow as Record<string, any>[]
|
||||
let newData = (chart.data?.tableRow || []) as Record<string, any>[]
|
||||
let intersectionArr = []
|
||||
const senior = parseJson(chart.senior)
|
||||
let emptyDataStrategy = senior?.functionCfg?.emptyDataStrategy
|
||||
|
||||
@ -69,6 +69,10 @@ export abstract class S2ChartView<P extends SpreadSheet> extends AntVAbstractCha
|
||||
let field
|
||||
switch (cell.cellType) {
|
||||
case 'dataCell':
|
||||
if (meta.valueField === SERIES_NUMBER_FIELD) {
|
||||
content = meta.fieldValue
|
||||
break
|
||||
}
|
||||
field = find(metaConfig, item => item.field === meta.valueField)
|
||||
if (meta.fieldValue === 0) {
|
||||
content = '0'
|
||||
|
||||
@ -4,7 +4,7 @@ import { equalsAny, includesAny } from '../editor/util/StringUtils'
|
||||
import { FeatureCollection } from '@antv/l7plot/dist/esm/plots/choropleth/types'
|
||||
import { useMapStoreWithOut } from '@/store/modules/map'
|
||||
import { getGeoJson } from '@/api/map'
|
||||
import { toRaw } from 'vue'
|
||||
import { computed, toRaw } from 'vue'
|
||||
import { Options } from '@antv/g2plot/esm'
|
||||
import { PickOptions } from '@antv/g2plot/esm/core/plot'
|
||||
import { innerExportDetails } from '@/api/chart'
|
||||
@ -12,6 +12,8 @@ import { ElMessage } from 'element-plus-secondary'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useLinkStoreWithOut } from '@/store/modules/link'
|
||||
|
||||
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
|
||||
|
||||
const { t } = useI18n()
|
||||
// 同时支持将hex和rgb,转换成rgba
|
||||
export function hexColorToRGBA(hex, alpha) {
|
||||
@ -511,7 +513,7 @@ export const exportExcelDownload = (chart, callBack?) => {
|
||||
|
||||
innerExportDetails(request)
|
||||
.then(res => {
|
||||
if (linkStore.getLinkToken) {
|
||||
if (linkStore.getLinkToken || isDataEaseBi.value) {
|
||||
const blob = new Blob([res.data], { type: 'application/vnd.ms-excel' })
|
||||
const link = document.createElement('a')
|
||||
link.style.display = 'none'
|
||||
|
||||
@ -116,7 +116,6 @@ const calcData = (view: Chart, callback, resetPageInfo = true) => {
|
||||
} else {
|
||||
delete view.chartExtRequest.pageSize
|
||||
}
|
||||
console.log(view)
|
||||
if (view.tableId || view['dataFrom'] === 'template') {
|
||||
isError.value = false
|
||||
const v = JSON.parse(JSON.stringify(view))
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
package io.dataease.api.copilot;
|
||||
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
|
||||
import io.dataease.api.copilot.dto.MsgDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author Junjun
|
||||
*/
|
||||
@Tag(name = "Copilot")
|
||||
@ApiSupport(order = 999)
|
||||
public interface CopilotApi {
|
||||
@Operation(summary = "发起一次对话")
|
||||
@PostMapping("chat")
|
||||
MsgDTO chat(@RequestBody MsgDTO msgDTO) throws Exception;
|
||||
|
||||
@Operation(summary = "获取对话记录")
|
||||
@PostMapping("getList")
|
||||
List<MsgDTO> getList() throws Exception;
|
||||
|
||||
@Operation(summary = "清空对话")
|
||||
@PostMapping("clearAll")
|
||||
void clearAll() throws Exception;
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package io.dataease.api.copilot.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author Junjun
|
||||
*/
|
||||
@Data
|
||||
public class AxisDTO {
|
||||
private String x;
|
||||
private String y;
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package io.dataease.api.copilot.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author Junjun
|
||||
*/
|
||||
@Data
|
||||
public class ChartDTO {
|
||||
private String type;
|
||||
private AxisDTO axis;
|
||||
private String title;
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package io.dataease.api.copilot.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author Junjun
|
||||
*/
|
||||
@Data
|
||||
public class DEReceiveDTO extends ReceiveDTO {
|
||||
private Map<String, Object> chartData;
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package io.dataease.api.copilot.dto;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author Junjun
|
||||
*/
|
||||
@Data
|
||||
public class DESendDTO extends SendDTO {
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long datasetGroupId;
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package io.dataease.api.copilot.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author Junjun
|
||||
*/
|
||||
@Data
|
||||
public class HistoryDTO {
|
||||
private String type;
|
||||
private String message;
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package io.dataease.api.copilot.dto;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author Junjun
|
||||
*/
|
||||
@Data
|
||||
public class MsgDTO {
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long userId;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long datasetGroupId;
|
||||
|
||||
private String msgType;
|
||||
|
||||
private String engineType;
|
||||
|
||||
private String schemaSql;
|
||||
|
||||
private String question;
|
||||
|
||||
private List<HistoryDTO> history;
|
||||
|
||||
private String copilotSql;
|
||||
|
||||
private String apiMsg;
|
||||
|
||||
private Integer sqlOk;
|
||||
|
||||
private Integer chartOk;
|
||||
|
||||
private ChartDTO chart;
|
||||
|
||||
private Long createTime;
|
||||
|
||||
private Map<String, Object> chartData;
|
||||
|
||||
private String execSql;
|
||||
|
||||
private Integer msgStatus;
|
||||
|
||||
private String errMsg;
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package io.dataease.api.copilot.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author Junjun
|
||||
*/
|
||||
@Data
|
||||
public class ReceiveDTO {
|
||||
private String sql;
|
||||
private List<HistoryDTO> history;
|
||||
private String apiMessage;
|
||||
private Boolean sqlOk;
|
||||
private Boolean chartOk;
|
||||
private ChartDTO chart;
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package io.dataease.api.copilot.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author Junjun
|
||||
*/
|
||||
@Data
|
||||
public class SendDTO {
|
||||
private String engine;
|
||||
private String schema;
|
||||
private String question;
|
||||
private List<HistoryDTO> history;
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package io.dataease.api.copilot.dto;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author Junjun
|
||||
*/
|
||||
@Data
|
||||
public class TokenDTO {
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
private String token;
|
||||
private Long updateTime;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user