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.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.toolkit.SqlRunner; import iet.ustb.sf.domain.*; import iet.ustb.sf.exception.ErrorCode; import iet.ustb.sf.exception.MyException; import iet.ustb.sf.exception.ThrowUtils; import iet.ustb.sf.mapper.*; import iet.ustb.sf.service.TargetDataService; import iet.ustb.sf.service.UtilService; import iet.ustb.sf.util.CheckUtils; import jakarta.annotation.Resource; import org.apache.commons.lang3.StringUtils; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.io.PrintWriter; import java.io.StringWriter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAdjusters; import java.util.*; import java.util.stream.Collectors; /** * @author hyy * @description 针对表【mbgk_target_data】的数据库操作Service实现 * @createDate 2025-08-05 15:40:32 */ @Service public class TargetDataServiceImpl extends ServiceImpl implements TargetDataService { @Resource private TargetDataMapper targetDataMapper; @Resource private TargetMapper targetMapper; @Resource private UtilService utilService; @Resource private MonitorMapper monitorMapper; @Resource private RuleMapper ruleMapper; @Resource private JobLogMapper jobLogMapper; @Resource private TableColumnMapper tableColumnMapper; @Override @Scheduled(cron = "0 45 6 * * ?") public void saveTargetDate(Date date) { String startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date now = new Date(); Map map = utilService.getCurrentCycleData(date); LocalDate day = date.toInstant().atZone(ZoneId.of("Asia/Shanghai")).toLocalDate(); for (String cycle : map.keySet()) { if (day.getDayOfWeek().getValue() > 1 && "lastWeek".equals(cycle)) { continue; } else if ("lastMonth".equals(cycle) && day.getDayOfMonth() > 1) { continue; } else if ("lastProduct".equals(cycle) && day.getDayOfMonth() != 26) { continue; } QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("cycle", cycle); queryWrapper.eq("x_show", map.get(cycle)[0]); targetDataMapper.delete(queryWrapper); } List targets = targetMapper.selectList(new QueryWrapper().orderByAsc("type")); for (Target target : targets) { if (target.getIsKey() == 0) { continue; } String[] strArr = map.get(target.getCycle()); if (strArr == null) { continue; } if (Arrays.asList("lastWeek", "lastMonth").contains(target.getCycle())) { continue; } String info = "OK"; int status = 0; try { String result; if (target.getType() < 2) { result = SqlRunner.db().selectOne(target.getResultSql(), strArr[3], strArr[4]).get("result").toString(); } else { result = utilService.getTogethResult(date, target.getResultSql()).get("res"); } TargetData targetData = TargetData.builder() .id(UUID.randomUUID().toString()) .targetId(target.getId()) .setDate(sdf.parse(strArr[2])) .xShow(strArr[0]) .cycle(strArr[1]) .createTime(now) .updateTime(now) .val(CheckUtils.roundToDecimalPlaces(result, 6)) .targetCycle(target.getCycle()) .build(); try { targetDataMapper.insert(targetData); } catch (Exception e) { throw new RuntimeException(e); } saveMonitor(targetData); } catch (Exception e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); info = sw.toString(); status = 1; } finally { JobLog jobLog = JobLog.builder() .id(UUID.randomUUID().toString()) .targetId(target.getId()) .info(info) .status(status) .createTime(new Date()) .build(); jobLogMapper.insert(jobLog); } } String endTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); System.out.println("定时保存指标数据 start:" + startTime); System.out.println("定时保存指标数据 end:" + endTime); } @Override public Map getData(JSONObject params, int type) { String targetId = params.getString("id"); String start = params.getString("start"); String end = params.getString("end"); Integer n = params.getInteger("n"); CheckUtils.checkEmpty(targetId, "指标ID"); Target target = targetMapper.selectById(targetId); if (n == null) { n = 7; } if (StringUtils.isEmpty(start) && StringUtils.isEmpty(end)) { Map cycleDate = getCycleData(target.getCycle(), n); start = cycleDate.get("start"); end = cycleDate.get("end"); } else if (!StringUtils.isEmpty(start) && !StringUtils.isEmpty(end)) { try { String[] strArrStart = getDataBySetDateAndCycle(LocalDate.parse(start), target.getCycle()); start = strArrStart[3]; } catch (DateTimeParseException e) { throw new MyException(ErrorCode.PARAMS_ERROR, "开始日期格式错误或日期非法!"); } try { String[] strArrEnd = getDataBySetDateAndCycle(LocalDate.parse(end), target.getCycle()); if (LocalDate.parse(strArrEnd[4]).isBefore(LocalDate.parse(end))) { end = strArrEnd[4]; } } catch (DateTimeParseException e) { throw new MyException(ErrorCode.PARAMS_ERROR, "结束日期格式错误或日期非法!"); } } else { throw new MyException(ErrorCode.PARAMS_ERROR, "开始日期和结束日期不能一个有值一个没有值!"); } QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("target_id", targetId); queryWrapper.ge("set_date", start); queryWrapper.lt("set_date", end); List targetDatas = targetDataMapper.selectList(queryWrapper); targetDatas = getHistoryData(targetDatas, target, start, end); List xData = new ArrayList<>(); List data = new ArrayList<>(); List oriData = new ArrayList<>(); for (TargetData targetData : targetDatas) { xData.add(targetData.getXShow()); data.add(targetData.getVal()); oriData.add(targetData.getVal()); } Map map = new HashMap<>(); map.put("start", start); map.put("end", end); map.put("max", Optional.of(data) .filter(list -> !list.isEmpty()) .map(Collections::max) .map(value -> "" + CheckUtils.roundToDecimalPlaces(value, 4)) .orElse("-")); map.put("avg", Optional.of(data) .filter(list -> !list.isEmpty()) .map(list -> list.stream().mapToDouble(Double::doubleValue).average().orElse(Double.NaN)) .map(value -> "" + CheckUtils.roundToDecimalPlaces(value, 4)) .orElse("-")); map.put("medium", CheckUtils.roundToDecimalPlaces(getMedian(data), 4)); map.put("min", Optional.of(data) .filter(list -> !list.isEmpty()) .map(Collections::min) .map(value -> "" + CheckUtils.roundToDecimalPlaces(value, 4)) .orElse("-")); map.put("xData", xData); map.put("data", data); map.put("cycle", utilService.getCycle(target)); map.put("oriData", oriData); return map; } @Override public List getTargetMonitor(JSONObject params) { Target target = JSON.toJavaObject(params, Target.class); String topic = target.getTopic(); String organization = target.getOrganization(); ThrowUtils.throwIf( StringUtils.isEmpty(topic) && StringUtils.isEmpty(organization), ErrorCode.PARAMS_ERROR, "主题和组织至少传入一个参数!" ); String categoryId = target.getCategoryId(); ThrowUtils.throwIf( !StringUtils.isEmpty(categoryId) && StringUtils.isEmpty(topic), ErrorCode.PARAMS_ERROR, "传入分类ID时主题必须传入!" ); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("is_key", 1); if (!StringUtils.isEmpty(topic)) { queryWrapper.eq("topic", topic); } if (!StringUtils.isEmpty(organization)) { queryWrapper.like("organization", "%" + organization + "%"); } if (!StringUtils.isEmpty(categoryId)) { queryWrapper.eq("category_id", categoryId); } if (!StringUtils.isEmpty(target.getName())) { queryWrapper.like("name", "%" + target.getName() + "%"); } List targetList = targetMapper.selectList(queryWrapper); List ids = new ArrayList<>(); if (!StringUtils.isEmpty(target.getId())) { for (Target targetItem : targetList) { if (targetItem.getId().equals(target.getId()) || (!StringUtils.isEmpty(targetItem.getParent()) && targetItem.getParent().equals(target.getId())) ) { ids.add(targetItem.getId()); } } } else { ids = targetList.stream().map(Target::getId).collect(Collectors.toList()); } if (ids.isEmpty()) { return Collections.emptyList(); } List monitorList = monitorMapper.getMbgkTargetMonitorsByTargetId(ids); if (ids.size() == monitorList.size()) { return monitorList; } List addIds = new ArrayList<>(ids); for (Monitor monitor : monitorList) { addIds.remove(monitor.getTargetId()); } Map targetMap = targetList.stream() .collect(Collectors.toMap(Target::getId, vo -> vo)); Map map = utilService.getCurrentCycleData(new Date()); for (String id : addIds) { Target target1 = targetMap.get(id); String[] strArr = map.get(target1.getCycle()); QueryWrapper qw = new QueryWrapper<>(); qw.eq("target_id", id); qw.eq("x_show", strArr[0]); TargetData targetData = targetDataMapper.selectOne(qw); saveMonitor(targetData); } return monitorMapper.getMbgkTargetMonitorsByTargetId(ids); } @Override public Map exportPreview(JSONObject params) { String targetId = params.getString("targetId"); CheckUtils.checkEmpty(targetId, "指标ID"); Target target = targetMapper.selectById(targetId); ThrowUtils.throwIf(target == null, ErrorCode.PARAMS_ERROR, "指标不存在!"); ThrowUtils.throwIf(target.getType() == 2, ErrorCode.OPERATION_ERROR, "聚合指标无法导出数据!"); String type = params.getString("type"); CheckUtils.checkEmpty(type, "类型"); ThrowUtils.throwIf( !"total".equals(type) && !"page".equals(type), ErrorCode.PARAMS_ERROR, "类型传参异常!" ); String start = params.getString("start"); String end = params.getString("end"); String[] strArr = utilService.getCurrentCycleDataByCycle(new Date(), target.getCycle()); try { if (!StringUtils.isEmpty(start)) { LocalDate.parse(start); } else { start = strArr[3]; } } catch (DateTimeParseException e) { throw new MyException(ErrorCode.PARAMS_ERROR, "开始日期格式不对!"); } try { if (!StringUtils.isEmpty(end)) { end = LocalDate.parse(end).plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); } else { end = strArr[4]; } } catch (DateTimeParseException e) { throw new MyException(ErrorCode.PARAMS_ERROR, "结束日期格式不对!"); } String searchSql = target.getSearchSql(); String[] columnNames = searchSql.substring(7, searchSql.indexOf("FROM") - 1).split(","); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("table_name", target.getTableName()); queryWrapper.in("column_name", Arrays.asList(columnNames)); List columnList = tableColumnMapper.selectList(queryWrapper); List> columns = columnList.stream() .map(col -> { Map map = new HashMap<>(); map.put("prop", col.getColumnName()); map.put("label", col.getColumnNameCn()); return map; }) .collect(Collectors.toList()); Map map = new HashMap<>(5); map.put("columns", columns); if ("page".equals(type)) { String countSql = "SELECT COUNT(1) " + searchSql.substring(searchSql.indexOf("FROM")); long total = SqlRunner.db().selectCount(countSql, start, end); int pageIndex = Optional.ofNullable(params.getInteger("pageIndex")).orElse(1); int pageSize = Optional.ofNullable(params.getInteger("pageSize")).orElse(10); Page> page = new Page<>(pageIndex, pageSize); page.setTotal(total); long offset = (long) (pageIndex - 1) * pageSize; String sql = searchSql + " LIMIT " + pageSize + " OFFSET " + offset; Page> data = SqlRunner.db().selectPage(page, sql, start, end); map.put("data", data); map.put("cnt", data.getRecords().size()); } else { List> data = SqlRunner.db().selectList(searchSql, start, end); map.put("data", data); map.put("cnt", data.size()); } map.put("start", start.substring(0, 10)); map.put("end", end.substring(0, 10)); return map; } /** * 获取列表中位数 * * @param data 列表数据 * @return 中位数 */ private String getMedian(List data) { if (data.isEmpty()) { return "-"; } List sortedData = data.stream() .sorted() .toList(); int size = sortedData.size(); double median; if (size % 2 == 0) { // 偶数个元素,返回中间两个元素的平均值 median = (sortedData.get(size / 2 - 1) + sortedData.get(size / 2)) / 2.0; } else { // 奇数个元素,返回中间的元素 median = sortedData.get(size / 2); } return String.valueOf(median); } /** * 获取指定日期所在周期的 xShow, cycle, setDate, start, end * * @param localDate 日期 * @param cycle 周期 * @return 结果 */ private String[] getDataBySetDateAndCycle(LocalDate localDate, String cycle) { DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); String setDate = localDate.format(dtf); String xShow, showCycle, start, end; switch (cycle) { case "yesterday": case "nearlyWeek": case "nearlyMonth": showCycle = "day"; xShow = localDate.format(dtf); end = localDate.plusDays(1).format(dtf); if ("yesterday".equals(cycle)) { start = localDate.format(dtf); } else if ("nearlyWeek".equals(cycle)) { start = localDate.minusDays(7).format(dtf); } else { start = localDate.minusDays(30).format(dtf); } break; case "week": case "lastWeek": showCycle = "week"; xShow = utilService.getYearWeek(LocalDate.parse(setDate)); start = localDate.format(dtf); end = localDate.plusDays(7).format(dtf); break; case "month": case "lastMonth": showCycle = "month"; DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM"); xShow = localDate.format(formatter); start = localDate.format(dtf); end = localDate.plusMonths(1).format(dtf); break; case "product": case "lastProduct": showCycle = "product"; DateTimeFormatter productDtf = DateTimeFormatter.ofPattern("yyyy-MM"); xShow = localDate.plusMonths(1).format(productDtf); start = localDate.format(dtf); end = localDate.plusMonths(1).format(dtf); break; default: xShow = null; showCycle = null; setDate = null; start = null; end = null; break; } return new String[]{xShow, showCycle, setDate, start, end}; } /** * 补充时间范围内缺失的历史数据 * * @param targetDataList 现有历史数据 * @param target 指标 * @param start 开始日期 * @param end 结束日期 * @return 补全的历史数据 */ public List getHistoryData( List targetDataList, Target target, String start, String end) { LocalDate compareDate = LocalDate.parse(start); for (int i = 0; i < targetDataList.size(); i++) { LocalDate setDate = targetDataList.get(i).getSetDate() .toInstant().atZone(ZoneId.of("Asia/Shanghai")).toLocalDate(); while (compareDate.isBefore(setDate)) { String[] strArr = getDataBySetDateAndCycle(compareDate, target.getCycle()); targetDataList.add(i, updateHistory(target, strArr, new HashMap<>())); compareDate = getCompareDate(target, compareDate); i++; setDate = targetDataList.get(i).getSetDate() .toInstant().atZone(ZoneId.of("Asia/Shanghai")).toLocalDate(); } compareDate = getCompareDate(target, compareDate); } while (compareDate.isBefore(LocalDate.parse(end))) { String[] strArr = getDataBySetDateAndCycle(compareDate, target.getCycle()); if (LocalDate.parse(end).isBefore(LocalDate.parse(strArr[4]))) { strArr[4] = end; } targetDataList.add(updateHistory(target, strArr, new HashMap<>())); compareDate = getCompareDate(target, compareDate); } return targetDataList; } /** * 获取下一个对比日期 * * @param target 指标 * @param compareDate 对比日期 * @return 对比日期 */ private LocalDate getCompareDate(Target target, LocalDate compareDate) { switch (target.getCycle()) { case "yesterday": case "nearlyMonth": case "nearlyWeek": compareDate = compareDate.plusDays(1); break; case "week": case "lastWeek": compareDate = compareDate.plusWeeks(1); break; case "month": case "lastMonth": case "product": case "lastProduct": compareDate = compareDate.plusMonths(1); break; } return compareDate; } /** * 获取前n个周期的开始结束日期 * * @param cycle 周期 * @return 结果map */ private Map getCycleData(String cycle, int n) { LocalDate localDate = LocalDate.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); String end = localDate.format(formatter); if (Arrays.asList("yesterday", "nearlyWeek", "nearlyMonth").contains(cycle)) { localDate = localDate.minusDays(n); } else if (Arrays.asList("week", "lastWeek").contains(cycle)) { if (localDate.getDayOfWeek() == DayOfWeek.MONDAY) { localDate = localDate.minusWeeks(1); } localDate = localDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); if ("lastWeek".equals(cycle)) { end = localDate.format(formatter); localDate = localDate.minusWeeks(1); } localDate = localDate.minusWeeks(n - 1); } else if (Arrays.asList("month", "lastMonth").contains(cycle)) { if (localDate.getDayOfMonth() == 1) { localDate = localDate.minusMonths(1); } localDate = localDate.withDayOfMonth(1); if ("lastMonth".equals(cycle)) { end = localDate.format(formatter); localDate = localDate.minusMonths(1); } localDate = localDate.minusMonths(n - 1); } else { if (localDate.getDayOfMonth() == 26) { localDate = localDate.minusMonths(1); } if (localDate.getDayOfMonth() >= 26) { localDate = localDate.withDayOfMonth(26); } if ("lastProduct".equals(cycle)) { end = localDate.format(formatter); localDate = localDate.minusMonths(1); } localDate = localDate.minusMonths(n - 1); } String start = localDate.format(formatter); Map map = new HashMap<>(3); map.put("start", start); map.put("end", end); return map; } @Override public TargetData updateHistory(Target target, String[] strArr, Map map) { String xShow = strArr[0]; QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("target_id", target.getId()); queryWrapper.eq("x_show", xShow); TargetData targetData = targetDataMapper.selectOne(queryWrapper); if (targetData != null && targetData.getId() != null) { return targetData; } double result; if (target.getType() < 2) { List> list = SqlRunner.db().selectList(target.getResultSql(), strArr[3], strArr[4]); if (list.isEmpty() || list.get(0) == null || list.get(0).isEmpty()) { result = 0f; } else { result = Double.parseDouble(list.get(0).get("result").toString()); } } else { String exprStr = target.getResultSql(); String[] expressionList = exprStr.split(","); List opList = Arrays.asList("+", "-", "*", "/", "(", ")"); List idList = new ArrayList<>(); for (String expression : expressionList) { if (!opList.contains(expression) && !idList.contains(expression)) { idList.add(expression); } } List targetList = targetMapper.selectByIds(idList); exprStr = exprStr.replaceAll(",", ""); if (map == null) { map = new HashMap<>(); } for (Target child : targetList) { double val; if (map.containsKey(child.getId())) { val = map.get(child.getId()).getVal(); } else { TargetData tmp = updateHistory(child, strArr, map); map.put(child.getId(), tmp); val = tmp.getVal(); } exprStr = exprStr.replaceAll(child.getId(), val + ""); } result = utilService.getEvalResult(exprStr); } Date now = new Date(); try { targetData = TargetData.builder() .id(UUID.randomUUID().toString()) .targetId(target.getId()) .setDate(new SimpleDateFormat("yyyy-MM-dd").parse(strArr[2])) .xShow(xShow) .cycle(strArr[1]) .createTime(now) .updateTime(now) .val(CheckUtils.roundToDecimalPlaces(result, 6)) .targetCycle(target.getCycle()) .build(); targetDataMapper.insert(targetData); } catch (Exception e) { throw new RuntimeException(e); } saveMonitor(targetData); return targetData; } /** * 新的指标数据添加监控数据 * * @param targetData 指标数据 */ public void saveMonitor(TargetData targetData) { double val = targetData.getVal(); String targetId = targetData.getTargetId(); Target target = targetMapper.selectById(targetId); Date now = new Date(); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("target_id", target.getId()); queryWrapper.eq("x_show", targetData.getXShow()); monitorMapper.delete(queryWrapper); Monitor monitor = Monitor.builder() .id(UUID.randomUUID().toString()) .targetId(targetId) .name(target.getName()) .type(target.getType()) .topic(target.getTopic()) .organization(target.getOrganization()) .level(target.getLevel()) .unit(target.getUnit()) .val(val) .warnLevel(999) .setDate(targetData.getSetDate()) .xShow(targetData.getXShow()) .createTime(now) .updateTime(now) .build(); List ruleList = ruleMapper.selectList(new QueryWrapper().eq("target_id", target.getId())); if (ruleList == null || ruleList.isEmpty()) { monitorMapper.insert(monitor); return; } List monitorList = new ArrayList<>(); monitorList.add(monitor); boolean bl = false; Rule oneRule = null; for (Rule rule : ruleList) { if (oneRule == null || oneRule.getWarnLevel() > rule.getWarnLevel()) { oneRule = rule; } bl = utilService.isWarn(rule, target, targetData); if (bl) { monitorList.add(utilService.getInitMonitor(targetData, target, rule)); } } if (!bl) { Monitor mbgkMonitor = utilService.getInitMonitor(targetData, target, oneRule); mbgkMonitor.setWarnLevel(998); monitorList.add(mbgkMonitor); } monitorMapper.insert(monitorList); } }