指标库:原子指标编辑页:输出查询

This commit is contained in:
huangge1199 2025-08-07 14:10:21 +08:00
parent b095d6926c
commit bec249ced3
4 changed files with 264 additions and 2 deletions

View File

@ -46,4 +46,11 @@ public class TargetController {
List<String> result = targetService.searchColumn(params);
return R.ok(result);
}
@Operation(summary = "指标输出查询")
@PostMapping("/getResult")
public R<String> getResult(@RequestBody JSONObject params) {
String result = targetService.getResult(params);
return R.ok(result);
}
}

View File

@ -17,4 +17,6 @@ public interface TargetService extends IService<Target> {
IPage<Target> getTargetsByCategory(JSONObject params);
List<String> searchColumn(JSONObject params);
String getResult(JSONObject params);
}

View File

@ -1,5 +1,6 @@
package iet.ustb.sf.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -8,6 +9,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import iet.ustb.sf.domain.Category;
import iet.ustb.sf.domain.Target;
import iet.ustb.sf.domain.TargetOption;
import iet.ustb.sf.exception.ErrorCode;
import iet.ustb.sf.exception.ThrowUtils;
import iet.ustb.sf.mapper.CategoryMapper;
@ -19,6 +21,9 @@ import lombok.val;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@ -80,6 +85,154 @@ public class TargetServiceImpl extends ServiceImpl<TargetMapper, Target>
List<Map<String, Object>> list = SqlRunner.db().selectList(sql);
return list.stream().map(target -> target.get(columnName).toString()).collect(Collectors.toList());
}
@Override
public String getResult(JSONObject params) {
String sql = getResultSql(params);
List<Map<String, Object>> list = SqlRunner.db().selectList(sql);
TargetOption result = params.getJSONObject("result").toJavaObject(TargetOption.class);
return list.get(0).get(result.getColumnName()).toString();
}
/**
* 拼接结果sql
*
* @param params 传入参数
* @return 结果sql
*/
private String getResultSql(JSONObject params) {
String tableName = params.getString("tableName");
CheckUtils.checkTableName(tableName);
List<TargetOption> searchList = params.getJSONArray("search").toJavaList(TargetOption.class);
CheckUtils.checkSearchOption(searchList, tableName);
TargetOption result = params.getJSONObject("result").toJavaObject(TargetOption.class);
CheckUtils.checkResultOption(result, tableName);
return "SELECT ROUND(" + result.getFun() + "(CAST(" + result.getColumnName() + " AS DOUBLE)), 6) AS " +
result.getColumnName() + " FROM " + tableName + " WHERE " + getWhereSql(searchList);
}
/**
* 拼接条件sql
*
* @param searchList 条件列表
* @return 条件sql
*/
private String getWhereSql(List<TargetOption> searchList) {
StringBuilder sql = new StringBuilder();
for (TargetOption option : searchList) {
int type = option.getType();
if (type == 1) {
String start, end;
LocalDate day = LocalDate.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
switch (option.getFun()) {
case "yesterday":
end = day.format(dtf);
start = day.minusDays(1).format(dtf);
break;
case "week":
end = day.format(dtf);
start = day.minusDays(1).with(DayOfWeek.MONDAY).format(dtf);
break;
case "lastWeek":
day = day.minusDays(1).with(DayOfWeek.MONDAY);
start = day.minusDays(7).format(dtf);
end = day.format(dtf);
break;
case "nearlyWeek":
end = day.format(dtf);
start = day.minusDays(7).format(dtf);
break;
case "month":
end = day.format(dtf);
start = day.minusDays(1).withDayOfMonth(1).format(dtf);
break;
case "lastMonth":
day = day.withDayOfMonth(1);
end = day.format(dtf);
start = day.minusMonths(1).format(dtf);
break;
case "nearlyMonth":
end = day.format(dtf);
start = day.minusDays(30).format(dtf);
break;
case "product":
end = day.format(dtf);
day = day.minusDays(1);
if (day.getDayOfMonth() < 26) {
start = day.minusMonths(1).withDayOfMonth(26).format(dtf);
} else {
start = day.withDayOfMonth(26).format(dtf);
}
break;
case "lastProduct":
day = day.minusDays(1);
if (day.getDayOfMonth() < 26) {
day = day.minusMonths(1).withDayOfMonth(26);
} else {
day = day.withDayOfMonth(26);
}
end = day.format(dtf);
start = day.minusMonths(1).format(dtf);
break;
default:
start = "";
end = "";
break;
}
sql.append(option.getColumnName()).append(" >= '").append(start).append("' and ")
.append(option.getColumnName()).append(" < '").append(end).append("' and ");
} else if (type == 2) {
sql.append(getIsNullSql(option));
} else {
sql.append("CAST(").append(option.getColumnName()).append(" AS DOUBLE)");
if ("between".equals(option.getFun())) {
double[] vals = Arrays.stream(option.getVal().split(",")).mapToDouble(Double::parseDouble).toArray();
sql.append(("between ")).append(vals[0]).append(" and ").append(vals[1]).append(" ");
} else {
sql.append(option.getFun()).append(" ").append(option.getVal()).append(" ");
}
sql.append("and ");
}
}
sql = new StringBuilder(sql.substring(0, sql.length() - 4));
return sql.toString();
}
/**
* 处理条件中可能包含NULL的情况
*
* @param option 单个条件
* @return 结果
*/
private String getIsNullSql(TargetOption option) {
boolean isNull = false;
StringBuilder tmpSql = new StringBuilder();
if (StringUtils.isEmpty(option.getVal())) {
return "( " + option.getColumnName() + " IS NULL OR " + option.getColumnName() + "='' ) AND ";
}
if (option.getVal().startsWith(",") || option.getVal().endsWith(",")) {
isNull = true;
}
String[] vals = option.getVal().split(",");
tmpSql.append(option.getColumnName()).append(" in(");
for (String val : vals) {
if (StringUtils.isEmpty(val)) {
isNull = true;
} else {
tmpSql.append("'").append(val).append("',");
}
}
tmpSql = new StringBuilder(tmpSql.substring(0, tmpSql.length() - 1));
tmpSql.append(")");
if (isNull) {
tmpSql = new StringBuilder("(" + tmpSql + " OR "
+ option.getColumnName() + " IS NULL OR "
+ option.getColumnName() + " ='' )");
}
tmpSql.append(" and ");
return tmpSql.toString();
}
}

View File

@ -1,13 +1,16 @@
package iet.ustb.sf.util;
import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import iet.ustb.sf.domain.TargetOption;
import iet.ustb.sf.exception.ErrorCode;
import iet.ustb.sf.exception.MyException;
import iet.ustb.sf.exception.ThrowUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* CheckUtils
@ -35,7 +38,8 @@ public class CheckUtils {
*/
public static void checkTableName(String tableName) {
checkEmpty(tableName, "表名");
String sql = "SELECT COUNT(1) FROM information_schema.tables WHERE table_schema = 'dwops_extension' AND table_name = '" + tableName + "'";
String sql = "SELECT COUNT(1) FROM information_schema.tables " +
"WHERE table_schema = 'dwops_extension' AND table_name = '" + tableName + "'";
long cnt = SqlRunner.db().selectCount(sql);
ThrowUtils.throwIf(cnt <= 0, new RuntimeException("" + tableName + "不存在!"));
}
@ -51,4 +55,100 @@ public class CheckUtils {
String sql = "SELECT " + columns + " FROM " + tableName + " limit 5";
SqlRunner.db().selectList(sql);
}
/**
* 条件指标验证
*
* @param optionList 条件指标集合
* @param tableName 表名
*/
public static void checkSearchOption(List<TargetOption> optionList, String tableName) {
String columns = optionList.stream().map(TargetOption::getColumnName).collect(Collectors.joining(","));
checkColumns(columns, tableName);
for (TargetOption option : optionList) {
checkSingleOption(option);
}
}
/**
* 单个条件指标
*
* @param option 指标
*/
public static void checkSingleOption(TargetOption option) {
String columns = option.getColumnName();
checkEmpty(columns, "列名");
List<Integer> typeList = Arrays.asList(1, 2, 3);
ThrowUtils.throwIf(
!typeList.contains(option.getType()),
ErrorCode.PARAMS_ERROR,
columns + ":指标中的类型不对!"
);
List<String> funList;
switch (option.getType()) {
case 1:
ThrowUtils.throwIf(
StringUtils.isEmpty(option.getFun()),
ErrorCode.PARAMS_ERROR,
columns + ":日期类型的指标,日期选项不能为空!"
);
funList = Arrays.asList("yesterday", "week", "nearlyWeek", "lastWeek", "month", "lastMonth",
"nearlyMonth", "product", "lastProduct");
ThrowUtils.throwIf(
!funList.contains(option.getFun()),
ErrorCode.PARAMS_ERROR,
columns + ":日期类型的指标,日期选项不对!"
);
break;
case 2:
break;
case 3:
ThrowUtils.throwIf(
StringUtils.isEmpty(option.getFun()),
ErrorCode.PARAMS_ERROR,
columns + ":数值类型的指标,符号不能为空!"
);
funList = Arrays.asList(">", ">=", "<", "<=", "=", "!=", "between");
ThrowUtils.throwIf(
!funList.contains(option.getFun()),
ErrorCode.PARAMS_ERROR,
columns + ":数值类型的指标,符号不对!"
);
ThrowUtils.throwIf(
StringUtils.isEmpty(option.getVal()),
ErrorCode.PARAMS_ERROR,
columns + ":数值类型的指标,值不能为空!"
);
if ("between".equals(option.getFun())) {
String[] nums = option.getVal().split(",");
ThrowUtils.throwIf(
nums.length != 2,
ErrorCode.PARAMS_ERROR,
columns + "范围条件请输入两个数值!"
);
try {
if (Double.parseDouble(nums[0]) > Double.parseDouble(nums[1])) {
throw new MyException(ErrorCode.PARAMS_ERROR, columns + ":范围条件请确保前面的值小于等于后面的值!");
}
} catch (Exception e) {
throw new MyException(ErrorCode.PARAMS_ERROR, columns + ":请传入两个数值!");
}
}
break;
}
}
/**
* 结果指标验证
*
* @param option 结果指标
* @param tableName 表名
*/
public static void checkResultOption(TargetOption option, String tableName) {
String fun = option.getFun();
checkEmpty(fun, "函数");
List<String> funList = Arrays.asList("SUM", "AVG", "MAX", "MIN", "MEDIAN", "COUNT");
ThrowUtils.throwIf(!funList.contains(fun), ErrorCode.PARAMS_ERROR, "方法错误!");
checkColumns(option.getColumnName(), tableName);
}
}