3 天以前 5e0640513226d9d9f2d766c075f79832c9d290ba
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiDecisionKpiServiceImpl.java
@@ -1,13 +1,18 @@
package cn.iocoder.yudao.module.bi.service.decision;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiKpiValueHistoryDO;
import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiKpiDefinitionMapper;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiKpiDefinitionDO;
import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiKpiValueHistoryMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
/**
@@ -23,6 +28,9 @@
    @Resource
    private BiKpiDefinitionMapper kpiMapper;
    @Resource
    private BiKpiValueHistoryMapper kpiValueHistoryMapper;
    @Resource
    private JdbcTemplate jdbcTemplate;
@@ -105,6 +113,105 @@
        return executeKpiSql(kpi.getQuerySql());
    }
    @Override
    public Map<String, Object> getKpiTrend(String kpiCode, LocalDateTime beginTime,
                                           LocalDateTime endTime, String periodType) {
        BiKpiDefinitionDO kpi = kpiMapper.selectOne(BiKpiDefinitionDO::getCode, kpiCode);
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("kpiCode", kpiCode);
        result.put("kpiName", kpi != null ? kpi.getName() : kpiCode);
        result.put("category", kpi != null ? kpi.getCategory() : null);
        result.put("unit", kpi != null ? kpi.getUnit() : null);
        result.put("periodType", periodType != null ? periodType : "hour");
        if (kpi == null) {
            result.put("points", Collections.emptyList());
            return result;
        }
        LocalDateTime begin = beginTime != null ? beginTime :
                LocalDateTime.now().minusDays(30);
        LocalDateTime end = endTime != null ? endTime : LocalDateTime.now();
        List<BiKpiValueHistoryDO> history = kpiValueHistoryMapper
                .selectByKpiCodeAndTimeRange(kpiCode, begin, end);
        List<Map<String, Object>> points = new ArrayList<>();
        for (BiKpiValueHistoryDO h : history) {
            Map<String, Object> p = new LinkedHashMap<>();
            p.put("time", h.getSnapshotTime());
            p.put("value", h.getKpiValue());
            points.add(p);
        }
        result.put("points", points);
        return result;
    }
    @Override
    public Map<String, Object> getKpiCompare(String kpiCode) {
        BiKpiDefinitionDO kpi = kpiMapper.selectOne(BiKpiDefinitionDO::getCode, kpiCode);
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("kpiCode", kpiCode);
        result.put("kpiName", kpi != null ? kpi.getName() : kpiCode);
        result.put("unit", kpi != null ? kpi.getUnit() : null);
        // 取最近 24 条快照(按时间倒序),最近一条为当前值
        List<BiKpiValueHistoryDO> recent = kpiValueHistoryMapper.selectRecentByKpiCode(kpiCode, 24);
        if (CollUtil.isEmpty(recent)) {
            result.put("currentValue", null);
            result.put("previousValue", null);
            result.put("changeRate", null);
            result.put("avgValue", null);
            result.put("maxValue", null);
            result.put("minValue", null);
            return result;
        }
        // recent 为倒序,第 0 条最新
        BiKpiValueHistoryDO current = recent.get(0);
        result.put("currentTime", current.getSnapshotTime());
        result.put("currentValue", current.getKpiValue());
        if (recent.size() > 1) {
            BiKpiValueHistoryDO previous = recent.get(1);
            result.put("previousTime", previous.getSnapshotTime());
            result.put("previousValue", previous.getKpiValue());
            result.put("changeRate", calcChangeRate(previous.getKpiValue(), current.getKpiValue()));
        } else {
            result.put("previousValue", null);
            result.put("changeRate", null);
        }
        // 近 24 期统计汇总
        BigDecimal sum = BigDecimal.ZERO;
        BigDecimal max = null;
        BigDecimal min = null;
        for (BiKpiValueHistoryDO h : recent) {
            if (h.getKpiValue() == null) {
                continue;
            }
            sum = sum.add(h.getKpiValue());
            max = max == null ? h.getKpiValue() : h.getKpiValue().max(max);
            min = min == null ? h.getKpiValue() : h.getKpiValue().min(min);
        }
        result.put("periodCount", recent.size());
        if (recent.size() > 0) {
            result.put("avgValue", sum.divide(BigDecimal.valueOf(recent.size()), 4, RoundingMode.HALF_UP));
        } else {
            result.put("avgValue", BigDecimal.ZERO);
        }
        result.put("maxValue", max);
        result.put("minValue", min);
        return result;
    }
    private BigDecimal calcChangeRate(BigDecimal previous, BigDecimal current) {
        if (previous == null || current == null || previous.compareTo(BigDecimal.ZERO) == 0) {
            return null;
        }
        return current.subtract(previous).divide(previous, 4, RoundingMode.HALF_UP)
                .multiply(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP);
    }
    /**
     * 执行 KPI 查询 SQL
     */