iet-kpi-service/src/main/java/iet/ustb/sf/service/impl/TargetDataServiceImpl.java

712 lines
28 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.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<TargetDataMapper, TargetData>
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<String, String[]> 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<TargetData> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("cycle", cycle);
queryWrapper.eq("x_show", map.get(cycle)[0]);
targetDataMapper.delete(queryWrapper);
}
List<Target> targets = targetMapper.selectList(new QueryWrapper<Target>().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<String, Object> 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<String, String> 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<TargetData> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("target_id", targetId);
queryWrapper.ge("set_date", start);
queryWrapper.lt("set_date", end);
List<TargetData> targetDatas = targetDataMapper.selectList(queryWrapper);
targetDatas = getHistoryData(targetDatas, target, start, end);
List<String> xData = new ArrayList<>();
List<Double> data = new ArrayList<>();
List<Double> oriData = new ArrayList<>();
for (TargetData targetData : targetDatas) {
xData.add(targetData.getXShow());
data.add(targetData.getVal());
oriData.add(targetData.getVal());
}
Map<String, Object> 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<Monitor> 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<Target> 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<Target> targetList = targetMapper.selectList(queryWrapper);
List<String> 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<Monitor> monitorList = monitorMapper.getMbgkTargetMonitorsByTargetId(ids);
if (ids.size() == monitorList.size()) {
return monitorList;
}
List<String> addIds = new ArrayList<>(ids);
for (Monitor monitor : monitorList) {
addIds.remove(monitor.getTargetId());
}
Map<String, Target> targetMap = targetList.stream()
.collect(Collectors.toMap(Target::getId, vo -> vo));
Map<String, String[]> map = utilService.getCurrentCycleData(new Date());
for (String id : addIds) {
Target target1 = targetMap.get(id);
String[] strArr = map.get(target1.getCycle());
QueryWrapper<TargetData> 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<String, Object> 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<TableColumn> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("table_name", target.getTableName());
queryWrapper.in("column_name", Arrays.asList(columnNames));
List<TableColumn> columnList = tableColumnMapper.selectList(queryWrapper);
List<Map<String, String>> columns = columnList.stream()
.map(col -> {
Map<String, String> map = new HashMap<>();
map.put("prop", col.getColumnName());
map.put("label", col.getColumnNameCn() + " " + col.getColumnName());
return map;
})
.collect(Collectors.toList());
Map<String, Object> 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<Map<String, Object>> page = new Page<>(pageIndex, pageSize);
page.setTotal(total);
long offset = (long) (pageIndex - 1) * pageSize;
String sql = searchSql + " LIMIT " + pageSize + " OFFSET " + offset;
Page<Map<String, Object>> data = SqlRunner.db().selectPage(page, sql, start, end);
map.put("data", data);
map.put("cnt", data.getRecords().size());
} else {
List<Map<String, Object>> 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<Double> data) {
if (data.isEmpty()) {
return "-";
}
List<Double> 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<TargetData> getHistoryData(
List<TargetData> 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<String, String> getCycleData(String cycle, int n) {
LocalDate localDate = LocalDate.now(ZoneId.of("Asia/Shanghai"));
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<String, String> map = new HashMap<>(3);
map.put("start", start);
map.put("end", end);
return map;
}
@Override
public TargetData updateHistory(Target target, String[] strArr, Map<String, TargetData> map) {
String xShow = strArr[0];
QueryWrapper<TargetData> 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<Map<String, Object>> 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<String> opList = Arrays.asList("+", "-", "*", "/", "(", ")");
List<String> idList = new ArrayList<>();
for (String expression : expressionList) {
if (!opList.contains(expression) && !idList.contains(expression)) {
idList.add(expression);
}
}
List<Target> 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<Monitor> 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<Rule> ruleList = ruleMapper.selectList(new QueryWrapper<Rule>().eq("target_id", target.getId()));
if (ruleList == null || ruleList.isEmpty()) {
monitorMapper.insert(monitor);
return;
}
List<Monitor> 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);
}
}