package com.meritdata.cloud.multianalysis.customizesearch.handler;
import com.meritdata.cloud.base.entity.QOrganization;
import com.meritdata.cloud.multianalysis.common.enums.*;
import com.meritdata.cloud.multianalysis.common.exception.HandleServiceException;
import com.meritdata.cloud.multianalysis.customizesearch.dto.CustomizeSearchParamDto;
import com.meritdata.cloud.multianalysis.entity.DataSet;
import com.meritdata.cloud.multianalysis.entity.QDataSet;
import com.meritdata.cloud.multianalysis.properties.MultianalyProperties;
import com.meritdata.cloud.utils.MD5;
import com.meritdata.cloud.utils.SessionUtils;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.*;
/**
* @Author sunyt
* @Date 2022/6/24 20:00
* @Description
*/
@Service
@Slf4j
public class CheckSqlHandler extends SqlHandler {
private final Integer orderId = 1;
@Autowired
private JPAQueryFactory jpaQueryFactory;
@Autowired
private SessionUtils sessionUtils;
@Autowired
private MultianalyProperties multianalyProperties;
@Override
public void draw(StringBuilder sqlBuilder, CustomizeSearchParamDto.Table table) {
if(StringUtils.isBlank(sqlBuilder)) {
List<CustomizeSearchParamDto.Code> codes = table.getCodes();
String tableName = "";
sqlBuilder.append("SELECT ");
sqlBuilder.append(String.join(",", getColumn(table)));
sqlBuilder.append(" FROM ");
if (table.getCode().contains(",")) {
//拼接多个join on 关联关系
StringBuilder sb = getJoinSql(table, sqlBuilder);
sqlBuilder.append(sb);
} else {
tableName = getTableName(codes, table.getCode());
sqlBuilder.append(tableName);
}
sqlBuilder.append(this.getFilters(table));
}else{
getNext().draw(sqlBuilder,table);
}
}
@Override
public Integer getOrder() {
return orderId;
}
/**
* 获取多个关联查询sql
*
* @param table
* @param sqlBuilder
* @return
*/
private StringBuilder getJoinSql(CustomizeSearchParamDto.Table table, StringBuilder sqlBuilder) {
String tableName = null;
List<CustomizeSearchParamDto.Code> codes = table.getCodes();
CustomizeSearchParamDto.Join join = table.getJoin();
String leftTable = join.getLeftTable();
tableName = getTableName(codes, leftTable);
sqlBuilder.append(tableName);
StringBuilder sb = new StringBuilder();
for (CustomizeSearchParamDto.Relations relation : join.getRelations()) {
CustomizeSearchParamDto.On on = relation.getOn();
String relationKey = RelationsEnum.getByName(relation.getRelation()).getValue();
tableName = getTableName(codes, relation.getRightTable());
sb.append(relationKey).append(tableName).append(" on ");
List<CustomizeSearchParamDto.RelationOfOn> relationOfOn = on.getRelation();
//拼接单个join on 后面可能存在的多个条件
getConditionOfOn(sb, relationOfOn);
}
return sb;
}
private String getTableName(List<CustomizeSearchParamDto.Code> codes, String name) {
String tableName = name;
if (!CollectionUtils.isEmpty(codes)) {
QDataSet qDataSet = QDataSet.dataSet;
Optional<CustomizeSearchParamDto.Code> first = codes.stream().filter(s -> s.getTableName().equals(name)).findFirst();
CustomizeSearchParamDto.Code code = new CustomizeSearchParamDto.Code();
if (first.isPresent()) {
code = first.get();
}
if ("SQL".equals(code.getType())) {
DataSet dataSet = jpaQueryFactory.selectFrom(qDataSet).where(qDataSet.id.eq(code.getModelId())).fetchFirst();
tableName = " (" + dataSet.getCheckSql() + ") " + code.getTableName();
}
}
return tableName;
}
/**
* 获取单个关联查询的多个查询条件
*
* @param sb
* @param relationOfOn
*/
private void getConditionOfOn(StringBuilder sb, List<CustomizeSearchParamDto.RelationOfOn> relationOfOn) {
List<String> relationOfOnList = new ArrayList<>();
for (CustomizeSearchParamDto.RelationOfOn ofOn : relationOfOn) {
StringBuilder sbOfOn = new StringBuilder(" ");
CustomizeSearchParamDto.Column left = ofOn.getLeft();
String leftFinallyName = getConcat(left.getTableName(), left.getCode());
CustomizeSearchParamDto.Column right = ofOn.getRight();
String rightFinallyName = getConcat(right.getTableName(), right.getCode());
String relationOFOn = ofOn.getRelation();
SearchLogicalTypeEnum logicalTypeEnum = SearchLogicalTypeEnum.getByCode(relationOFOn);
String format = String.format(logicalTypeEnum.getSymbol(), rightFinallyName);
sbOfOn.append(leftFinallyName).append(format.replace(":", ""));
relationOfOnList.add(sbOfOn.toString());
}
sb.append(String.join(" and ", relationOfOnList));
}
/**
* @return
*/
private StringBuilder getAuth(Map<String, Object> sqlParams, StringBuilder sqlBuilder) {
if (StringUtils.isBlank(multianalyProperties.getColumn())) {
return null;
}
Boolean rule = multianalyProperties.getRule();
if (Boolean.TRUE.equals(rule)) {
sqlParams.put("dLevelCode", sessionUtils.getOrgDlevelCode());
sqlBuilder.append(" WHERE ").append(multianalyProperties.getColumn()).append(" LIKE :dLevelCode '%'");
} else {
QOrganization qOrganization = QOrganization.organization;
List<String> ids = jpaQueryFactory.select(qOrganization.id).from(qOrganization).where(qOrganization.parentId.eq(sessionUtils.getDeptId())).fetch();
List<String> deptIds = new ArrayList<>();
if (ids != null && ids.size() > 0) {
deptIds.addAll(ids);
getAuthDeptId(ids, deptIds);
}
deptIds.add(sessionUtils.getDeptId());
sqlParams.put("deptIds", deptIds);
sqlBuilder.append(" WHERE ").append(multianalyProperties.getColumn()).append(" IN (:deptIds)");
}
return sqlBuilder;
}
/**
* 查询该部门下的所有部门
*
* @param id
* @param deptIds
*/
private void getAuthDeptId(List<String> id, List<String> deptIds) {
QOrganization qOrganization = QOrganization.organization;
List<String> ids = jpaQueryFactory.select(qOrganization.id).from(qOrganization).where(qOrganization.parentId.in(id)).fetch();
if (ids != null && ids.isEmpty()) {
deptIds.addAll(ids);
getAuthDeptId(ids, deptIds);
}
}
/**
* @param table
* @return
*/
private List<String> getColumn(CustomizeSearchParamDto.Table table) {
if (CollectionUtils.isEmpty(table.getColumns())) {
throw new HandleServiceException("参数错误: 没有选择列");
}
List<String> columnList = new ArrayList<>();
for (CustomizeSearchParamDto.Column column : table.getColumns()) {
String columnName = null;
if (StringUtils.isNotBlank(column.getType())) {
AggregationType type = AggregationType.getByCode(column.getType());
columnName = AggregationType.getSql(type, getConcat(column.getTableName(), column.getCode()), column.getAlias());
} else if (StringUtils.isNotBlank(column.getCode())) {
columnName = getConcat(column.getTableName(), column.getCode()).toUpperCase();
}
// 列名不为空 且 没有重复
if (StringUtils.isNotBlank(columnName) && !columnList.contains(columnName)) {
if (StringUtils.isNotEmpty(column.getAlias())) {
//聚合字段处理
if (!columnName.contains("AS")) {
columnName = columnName.concat(" as ").concat(column.getAlias());
}
}
columnList.add(columnName);
}
}
// 如果列为空,则可能是 指标数据,此处查询指标数据
if (CollectionUtils.isEmpty(columnList) || StringUtils.isNotBlank(table.getCode()) && table.getCode().contains(".")) {
String tableCode = table.getCode();
columnList = this.getIndicatorTableAndColumn(tableCode, table);
}
if (CollectionUtils.isEmpty(columnList)) {
throw new HandleServiceException("参数错误: 列为空或者列无效");
}
return columnList;
}
private String getConcat(String tableName, String column) {
return tableName.concat(".").concat(column);
}
/**
* @param table
* @return
*/
private List<String> getGroupBy(CustomizeSearchParamDto.Table table) {
List<String> groupbyList = new ArrayList<>();
if (CollectionUtils.isEmpty(table.getColumns())) {
return groupbyList;
}
for (CustomizeSearchParamDto.Column column : table.getColumns()) {
if (StringUtils.isBlank(column.getType()) && StringUtils.isNotBlank((column.getCode()))) {
groupbyList.add(getConcat(column.getTableName(), column.getCode()));
}
}
return groupbyList;
}
private List<String> getOrderBy(CustomizeSearchParamDto.Table table) {
List<String> orderByList = new ArrayList<>();
if (CollectionUtils.isEmpty(table.getColumns())) {
return orderByList;
}
for (CustomizeSearchParamDto.Column column : table.getColumns()) {
if (StringUtils.isNotBlank(column.getOrder())) {
OrderTypeEnum orderTypeEnum = OrderTypeEnum.getByName(column.getOrder());
if (DataApplyTypeEnum.CROSS.getCode().equals(table.getType())) {
String orderCol = AggregationType.getAlias(column.getType(), column.getAlias()).concat(orderTypeEnum.getSql());
orderByList.add(orderCol.toUpperCase());
} else {
orderByList.add(getConcat(column.getTableName(), column.getCode()).concat(orderTypeEnum.getSql()));
}
}
}
return orderByList;
}
/**
* @param indName
*/
private List<String> getIndicatorTableAndColumn(String indName, CustomizeSearchParamDto.Table table) {
// List<Indicator> indicatorList = indicatorService.findIndicatorByName(this.getSuffix(indName, "\\."));
// if (CollectionUtils.isEmpty(indicatorList)) {
// return Collections.emptyList();
// }
//
// // 只取出第一条
// Indicator indicator = indicatorList.get(0);
//
// if (StringUtils.isNotBlank(indicator.getTabId())) {
// MetaTable metaTable = metaTableService.getMetaTableById(indicator.getTabId());
// table.setOriginalCode(metaTable.getTableName());
// }
//
// List<String> columnList = new ArrayList<>();
// if (StringUtils.isNotBlank(indicator.getColId())) {
// MetaColumn metaColumn = metaColumnService.getMetaColumnById(indicator.getColId());
// columnList.add(metaColumn.getColName());
// }
// return columnList;
return null;
}
private String getSuffix(String str, String character) {
if (StringUtils.isBlank(str)) {
return str;
}
String[] strArray = str.split(character);
return strArray[strArray.length - 1];
}
/**
* 封装查询条件
*
* @param table
* @return
*/
private StringBuilder getFilters(CustomizeSearchParamDto.Table table) {
StringBuilder sqlBuilder = new StringBuilder();
Map<String, Object> sqlParams = new LinkedHashMap<>();
StringBuilder auth = this.getAuth(sqlParams, sqlBuilder);
table.setSqlParams(sqlParams);
if (CollectionUtils.isEmpty(table.getFilter())) {
return sqlBuilder;
}
String sql;
for (CustomizeSearchParamDto.Filter filter : table.getFilter()) {
sql = this.getFilter(filter, sqlParams);
if (StringUtils.isNotBlank(sql)) {
if (sqlBuilder.length() > 0) {
sqlBuilder.append(" AND ");
}
sqlBuilder.append(sql);
}
}
table.setSqlParams(sqlParams);
// 将sql执行的参数保存到查询参数对象中
if (auth == null) {
sqlBuilder.insert(0, " WHERE ");
}
return sqlBuilder;
}
/**
* 解析一个条件
*
* @param filter
* @return
*/
private String getFilter(CustomizeSearchParamDto.Filter filter, Map<String, Object> sqlParams) {
if (filter == null) {
return null;
}
// 获取字段
String field = filter.getField();
// 获取逻辑关系符
String logical = filter.getLogicalType();
// 获取值类型
String valType = filter.getValType();
// 获取值
String keywords = filter.getKeywords();
//
// 获取值
String tableName = filter.getTableName();
SearchLogicalTypeEnum logicalTypeEnum = SearchLogicalTypeEnum.getByCode(logical);
SearchValTypeEnum valTypeEnum = SearchValTypeEnum.getByCode(valType);
// 转换参数 以及封装 sql片段
Map<String, Object> condition = this.getCondition(field, logicalTypeEnum, valTypeEnum, keywords, tableName);
// 获取参数类型 STRING ARRAY RANGE
SearchValTypeEnum typeEnum = SearchValTypeEnum.getByCode(String.valueOf(condition.get("type")));
Object value = condition.get("value");
switch (typeEnum) {
case ARRAY:
List<Object> arrayValueList = (List<Object>) value;
sqlParams.put(getNamed(field), arrayValueList);
break;
case RANGE:
List<Object> rangeValueList = (List<Object>) value;
if (CollectionUtils.isEmpty(rangeValueList)) {
throw new RuntimeException("Sql params[RANG] error, Need 2 parameters, found 0.");
} else if (rangeValueList.size() == 1) {
sqlParams.put(getNamed(field.concat("_START")), rangeValueList.get(0));
sqlParams.put(getNamed(field.concat("_END")), rangeValueList.get(0));
} else if (rangeValueList.size() >= 2) {
sqlParams.put(getNamed(field.concat("_START")), rangeValueList.get(0));
sqlParams.put(getNamed(field.concat("_END")), rangeValueList.get(1));
}
break;
case STRING:
default:
sqlParams.put(getNamed(field), value);
}
return String.valueOf(condition.get("sql"));
}
/**
* 获取其中一个condition条件
*
* @param field
* @param logicType
* @param valType
* @param keywords
* @return 返回的Map中包含:<br>
* 1) sql: 有:field 占位符的sql条件
* 2) type: STRING, ARRAY, RANGE
* 3)value: 值 包含 单个值, N个值的数组, 两个值的数组
*/
private Map<String, Object> getCondition(String field, SearchLogicalTypeEnum logicType, SearchValTypeEnum valType, String keywords, String tableName) {
// 返回的结果集
Map<String, Object> result = new HashMap<>();
StringBuilder conditionBuilder = new StringBuilder();
conditionBuilder.append(getConcat(tableName, field));
conditionBuilder.append(" ");
Object value = this.getValue(valType, keywords);
String type = SearchValTypeEnum.STRING.getCode();
Object[] params;
switch (logicType) {
case ISNULL:
conditionBuilder.append(SearchLogicalTypeEnum.ISNULL.getSymbol());
break;
case ISNOTNULL:
conditionBuilder.append(SearchLogicalTypeEnum.ISNOTNULL.getSymbol());
break;
case NOTEQUALS:
conditionBuilder.append(String.format(SearchLogicalTypeEnum.NOTEQUALS.getSymbol(), getNamed(field)));
break;
case LIKE:
value = "%".concat(String.valueOf(value)).concat("%");
conditionBuilder.append(String.format(SearchLogicalTypeEnum.LIKE.getSymbol(), getNamed(field)));
break;
case NOTLIKE:
value = "%".concat(String.valueOf(value)).concat("%");
conditionBuilder.append(String.format(SearchLogicalTypeEnum.NOTLIKE.getSymbol(), getNamed(field)));
break;
case IN:
type = SearchValTypeEnum.ARRAY.getCode();
conditionBuilder.append(String.format(SearchLogicalTypeEnum.IN.getSymbol(), getNamed(field)));
break;
case NOTIN:
type = SearchValTypeEnum.ARRAY.getCode();
conditionBuilder.append(String.format(SearchLogicalTypeEnum.NOTIN.getSymbol(), getNamed(field)));
break;
case BETWEEN:
type = SearchValTypeEnum.RANGE.getCode();
params = new Object[]{getNamed(field.concat("_START")), getNamed(field.concat("_END"))};
conditionBuilder.append(String.format(SearchLogicalTypeEnum.BETWEEN.getSymbol(), params));
break;
case GT:
conditionBuilder.append(String.format(SearchLogicalTypeEnum.GT.getSymbol(), getNamed(field)));
break;
case GTE:
conditionBuilder.append(String.format(SearchLogicalTypeEnum.GTE.getSymbol(), getNamed(field)));
break;
case LT:
conditionBuilder.append(String.format(SearchLogicalTypeEnum.LT.getSymbol(), getNamed(field)));
break;
case LTE:
conditionBuilder.append(String.format(SearchLogicalTypeEnum.LTE.getSymbol(), getNamed(field)));
break;
case EQUALS:
default:
conditionBuilder.append(String.format(SearchLogicalTypeEnum.EQUALS.getSymbol(), getNamed(field)));
}
result.put("sql", conditionBuilder.toString());
result.put("type", type);
result.put("value", value);
return result;
}
/**
* 值转换
*
* @param valType
* @param keywords
* @return
*/
private Object getValue(SearchValTypeEnum valType, String keywords) {
Object result;
if (keywords == null) {
return null;
}
switch (valType) {
case NUMBER:
case DOUBLE:
result = getDouble(keywords);
break;
case FLOAT:
result = getFloat(keywords);
break;
case INTEGER:
result = getInteger(keywords);
break;
case DATE:
case ARRAY:
case RANGE:
result = Arrays.asList(keywords.split(","));
break;
case STRING:
default:
result = keywords;
}
return result;
}
private static Object getInteger(String keywords) {
try {
if (StringUtils.isNotBlank(keywords) && keywords.contains(",")) {
return Arrays.asList(keywords.split(","));
} else {
return Integer.parseInt(keywords);
}
} catch (Exception e) {
log.error("Parse error.", e);
throw new HandleServiceException("参数格式不正确, 需要整数, 实际: " + keywords);
}
}
private static Object getFloat(String keywords) {
try {
if (StringUtils.isNotBlank(keywords) && keywords.contains(",")) {
return Arrays.asList(keywords.split(","));
} else {
return Float.parseFloat(keywords);
}
} catch (Exception e) {
log.error("Parse error.", e);
throw new HandleServiceException("参数格式不正确, 需要小数, 实际: " + keywords);
}
}
private static Object getDouble(String keywords) {
try {
if (StringUtils.isNotBlank(keywords) && keywords.contains(",")) {
return Arrays.asList(keywords.split(","));
} else {
return Double.parseDouble(keywords);
}
} catch (Exception e) {
log.error("Parse error.", e);
throw new HandleServiceException("参数格式不正确, 需要数字, 实际: " + keywords);
}
}
/**
* 使用MD5 处理占位符,避免中文字段
*
* @param str
* @return
*/
private static String getNamed(String str) {
return "P_".concat(MD5.get(str));
}
/**
* 获取查询 sql
*
* @param table
* @return
*/
private String getSql(CustomizeSearchParamDto.Table table) {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append(table.getSql());
if (StringUtils.isNotBlank(table.getPageSql())) {
sqlBuilder.append(table.getPageSql());
}
return sqlBuilder.toString();
}
/**
* 获取count sql
*
* @param table
* @return
*/
private String getCountSql(CustomizeSearchParamDto.Table table) {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("SELECT COUNT(1) CNT FROM (");
sqlBuilder.append(table.getSql());
sqlBuilder.append(") AS CONT");
return sqlBuilder.toString();
}
/**
* 获取左侧维度的数据集合 单行数据
*
* @param row
* @param dimSize
* @return
*/
private static List<String> getDimDataList(Object[] row, int dimSize) {
List<String> dimData = new ArrayList<>();
for (int i = 0; i < dimSize; i++) {
dimData.add(Objects.isNull(row[i]) ? null : String.valueOf(row[i]));
}
return dimData;
}
}
package com.meritdata.cloud.multianalysis.customizesearch.handler;
import com.meritdata.cloud.multianalysis.common.enums.DataApplyTypeEnum;
import com.meritdata.cloud.multianalysis.customizesearch.dto.CustomizeSearchParamDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
// 分组
@Slf4j
@Service
public class GroupBySqlHandler extends SqlHandler {
private final Integer orderId = 2;
@Override
public void draw(StringBuilder sqlBuilder, CustomizeSearchParamDto.Table table) {
if (DataApplyTypeEnum.CROSS.getCode().equals(table.getType())) {
// // 获取group sql片段
List<String> groupby = this.getGroupBy(table);
if (!CollectionUtils.isEmpty(groupby)) {
sqlBuilder.append(" GROUP BY ");
sqlBuilder.append(String.join(",", groupby));
}
}else{
getNext().draw(sqlBuilder,table);
}
}
@Override
public Integer getOrder() {
return orderId;
}
/**
* @param table
* @return
*/
private List<String> getGroupBy(CustomizeSearchParamDto.Table table) {
List<String> groupbyList = new ArrayList<>();
if (CollectionUtils.isEmpty(table.getColumns())) {
return groupbyList;
}
for (CustomizeSearchParamDto.Column column : table.getColumns()) {
if (StringUtils.isBlank(column.getType()) && StringUtils.isNotBlank((column.getCode()))) {
groupbyList.add(getConcat(column.getTableName(), column.getCode()));
}
}
return groupbyList;
}
private String getConcat(String tableName, String column) {
return tableName.concat(".").concat(column);
}
}
package com.meritdata.cloud.multianalysis.customizesearch.handler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @Author sunyt
* @Date 2022/6/24 19:41
* @Description
*/
@Component
public class HandlerChain {
@Autowired
private ApplicationContext applicationContext;
private SqlHandler sqlHandler;
public SqlHandler getHandler() {
return sqlHandler;
}
@PostConstruct
public void afterPropertiesSet() {
Map<String, SqlHandler> beansOfTypeMap = applicationContext.getBeansOfType(SqlHandler.class);
if (beansOfTypeMap == null || beansOfTypeMap.size() == 0) {
return;
}
List<SqlHandler> sqlHandlers = beansOfTypeMap.values().stream()
.sorted((s1, s2) -> s1.getOrder().compareTo(s2.getOrder()))
.collect(Collectors.toList());
for (int i = 1; i < sqlHandlers.size(); i++) {
//当前处理器
SqlHandler sqlHandler = sqlHandlers.get(i - 1);
//下一个处理器
SqlHandler nextHandler = sqlHandlers.get(i);
//将处理器串成链表
sqlHandler.setNext(nextHandler);
}
this.sqlHandler = sqlHandlers.get(0);
}
}
package com.meritdata.cloud.multianalysis.customizesearch.handler;
import com.meritdata.cloud.multianalysis.customizesearch.dto.CustomizeSearchParamDto;
/**
* Handler: 抽象处理者
*/
public abstract class SqlHandler {
// 持有后继责任对象
protected SqlHandler next;
public abstract void draw(StringBuilder sqlBuilder,CustomizeSearchParamDto.Table table);
public SqlHandler getNext() {
return next;
}
public void setNext(SqlHandler next) {
this.next = next;
}
public abstract Integer getOrder();
}
orderId是为了排序 或者文件时候用了几种方法都没法处理成我们自己想要的加载顺序,故此加载完毕后根据orderId 再排序
// 调用如下
StringBuilder sqlBuilder = new StringBuilder();
SqlHandler handler = handlerChain.getHandler();
handler.draw(sqlBuilder,table);