diff --git a/src/main/java/iet/ustb/sf/controller/TargetController.java b/src/main/java/iet/ustb/sf/controller/TargetController.java index 32532a5..f1ce5e9 100644 --- a/src/main/java/iet/ustb/sf/controller/TargetController.java +++ b/src/main/java/iet/ustb/sf/controller/TargetController.java @@ -46,4 +46,11 @@ public class TargetController { List result = targetService.searchColumn(params); return R.ok(result); } + + @Operation(summary = "指标输出查询") + @PostMapping("/getResult") + public R getResult(@RequestBody JSONObject params) { + String result = targetService.getResult(params); + return R.ok(result); + } } diff --git a/src/main/java/iet/ustb/sf/service/TargetService.java b/src/main/java/iet/ustb/sf/service/TargetService.java index 12122b9..c1ae6fd 100644 --- a/src/main/java/iet/ustb/sf/service/TargetService.java +++ b/src/main/java/iet/ustb/sf/service/TargetService.java @@ -17,4 +17,6 @@ public interface TargetService extends IService { IPage getTargetsByCategory(JSONObject params); List searchColumn(JSONObject params); + + String getResult(JSONObject params); } diff --git a/src/main/java/iet/ustb/sf/service/impl/TargetServiceImpl.java b/src/main/java/iet/ustb/sf/service/impl/TargetServiceImpl.java index 25de975..5fd0d21 100644 --- a/src/main/java/iet/ustb/sf/service/impl/TargetServiceImpl.java +++ b/src/main/java/iet/ustb/sf/service/impl/TargetServiceImpl.java @@ -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 List> 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> 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 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 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(); + } } diff --git a/src/main/java/iet/ustb/sf/util/CheckUtils.java b/src/main/java/iet/ustb/sf/util/CheckUtils.java index ad42c38..6035109 100644 --- a/src/main/java/iet/ustb/sf/util/CheckUtils.java +++ b/src/main/java/iet/ustb/sf/util/CheckUtils.java @@ -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 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 typeList = Arrays.asList(1, 2, 3); + ThrowUtils.throwIf( + !typeList.contains(option.getType()), + ErrorCode.PARAMS_ERROR, + columns + ":指标中的类型不对!" + ); + List 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 funList = Arrays.asList("SUM", "AVG", "MAX", "MIN", "MEDIAN", "COUNT"); + ThrowUtils.throwIf(!funList.contains(fun), ErrorCode.PARAMS_ERROR, "方法错误!"); + checkColumns(option.getColumnName(), tableName); + } }