From 4f45f29e6b53f4c01b414409c5000ff4e212b3d9 Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期五, 05 六月 2026 13:36:54 +0800
Subject: [PATCH] 增加eip

---
 report-server/src/main/java/com/ruoyi/report/service/impl/NormalDistributionServiceImpl.java |  193 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 193 insertions(+), 0 deletions(-)

diff --git a/report-server/src/main/java/com/ruoyi/report/service/impl/NormalDistributionServiceImpl.java b/report-server/src/main/java/com/ruoyi/report/service/impl/NormalDistributionServiceImpl.java
new file mode 100644
index 0000000..7f82fcb
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/impl/NormalDistributionServiceImpl.java
@@ -0,0 +1,193 @@
+package com.ruoyi.report.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.excel.EasyExcel;
+import com.ruoyi.framework.exception.ErrorException;
+import com.ruoyi.report.dto.NormalDistributionDto;
+import com.ruoyi.report.mapper.NormalDistributionMapper;
+import com.ruoyi.report.service.NormalDistributionService;
+import com.ruoyi.report.vo.NormalDistributionVo;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 姝f�佸垎甯冨浘鏈嶅姟瀹炵幇
+ */
+@Service
+@AllArgsConstructor
+public class NormalDistributionServiceImpl implements NormalDistributionService {
+
+    private NormalDistributionMapper normalDistributionMapper;
+
+    @Override
+    public NormalDistributionVo analyze(NormalDistributionDto dto) {
+        // 鏌ヨ鏁版嵁
+        List<Map<String, Object>> itemData = normalDistributionMapper.getItemData(dto);
+
+        if (CollectionUtil.isEmpty(itemData)) {
+            return null;
+        }
+
+        if (itemData.size() < 10) {
+            throw new ErrorException("鏁版嵁閲忎笉瓒筹紝鑷冲皯闇�瑕�10涓暟鎹偣");
+        }
+
+        // 鑾峰彇鏁板�煎垪琛�
+        List<BigDecimal> values = itemData.stream()
+                .map(m -> new BigDecimal((String) m.get("lastValue")))
+                .sorted()
+                .collect(Collectors.toList());
+
+        // 璁$畻缁熻閲�
+        BigDecimal min = values.get(0);
+        BigDecimal max = values.get(values.size() - 1);
+        BigDecimal sum = values.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
+        BigDecimal mean = sum.divide(new BigDecimal(values.size()), 10, RoundingMode.HALF_UP);
+
+        // 璁$畻鏍囧噯宸�
+        BigDecimal variance = BigDecimal.ZERO;
+        for (BigDecimal v : values) {
+            BigDecimal diff = v.subtract(mean);
+            variance = variance.add(diff.multiply(diff));
+        }
+        variance = variance.divide(new BigDecimal(values.size() - 1), 10, RoundingMode.HALF_UP);
+        BigDecimal stdDev = sqrt(variance).setScale(6, RoundingMode.HALF_UP);
+
+        // 鐩存柟鍥惧垎缁�
+        int binCount = dto.getBinCount() != null ? dto.getBinCount() : 10;
+        BigDecimal range = max.subtract(min);
+        BigDecimal binWidth = range.divide(new BigDecimal(binCount), 10, RoundingMode.HALF_UP);
+
+        // 璁$畻鍒嗙粍杈圭晫
+        List<BigDecimal> binEdges = new ArrayList<>();
+        BigDecimal edge = min;
+        for (int i = 0; i <= binCount; i++) {
+            binEdges.add(edge.setScale(4, RoundingMode.HALF_UP));
+            edge = edge.add(binWidth);
+        }
+
+        // 璁$畻棰戞暟
+        List<Integer> frequencies = new ArrayList<>();
+        for (int i = 0; i < binCount; i++) {
+            BigDecimal lower = binEdges.get(i);
+            BigDecimal upper = binEdges.get(i + 1);
+            int count = 0;
+            for (BigDecimal v : values) {
+                if (v.compareTo(lower) >= 0 && (i == binCount - 1 ? v.compareTo(upper) <= 0 : v.compareTo(upper) < 0)) {
+                    count++;
+                }
+            }
+            frequencies.add(count);
+        }
+
+        // 姝f�佸垎甯冩洸绾�
+        List<BigDecimal> normalX = new ArrayList<>();
+        List<BigDecimal> normalY = new ArrayList<>();
+
+        // 鐢熸垚鏇茬嚎鐐�
+        BigDecimal step = range.divide(new BigDecimal(100), 10, RoundingMode.HALF_UP);
+        BigDecimal x = min;
+        for (int i = 0; i <= 100; i++) {
+            normalX.add(x.setScale(4, RoundingMode.HALF_UP));
+
+            // 姝f�佸垎甯冨叕寮�: f(x) = (1/(蟽鈭�(2蟺))) * e^(-(x-渭)^2/(2蟽^2))
+            BigDecimal exponent = x.subtract(mean).pow(2)
+                    .divide(stdDev.pow(2).multiply(new BigDecimal(2)), 10, RoundingMode.HALF_UP);
+            BigDecimal expValue = BigDecimal.valueOf(Math.exp(-exponent.doubleValue()));
+            BigDecimal coefficient = BigDecimal.ONE.divide(
+                    stdDev.multiply(BigDecimal.valueOf(Math.sqrt(2 * Math.PI))),
+                    10, RoundingMode.HALF_UP);
+            BigDecimal y = coefficient.multiply(expValue)
+                    .multiply(new BigDecimal(values.size()))
+                    .multiply(binWidth)
+                    .setScale(4, RoundingMode.HALF_UP);
+            normalY.add(y);
+
+            x = x.add(step);
+        }
+
+        // 鏋勫缓缁撴灉
+        NormalDistributionVo vo = new NormalDistributionVo();
+        vo.setBinEdges(binEdges);
+        vo.setFrequencies(frequencies);
+        vo.setNormalX(normalX);
+        vo.setNormalY(normalY);
+        vo.setMean(mean.setScale(4, RoundingMode.HALF_UP));
+        vo.setStdDev(stdDev.setScale(4, RoundingMode.HALF_UP));
+        vo.setMin(min.setScale(4, RoundingMode.HALF_UP));
+        vo.setMax(max.setScale(4, RoundingMode.HALF_UP));
+        vo.setSampleSize(values.size());
+
+        return vo;
+    }
+
+    @Override
+    public void export(NormalDistributionDto dto, HttpServletResponse response) {
+        List<Map<String, Object>> itemData = normalDistributionMapper.getItemData(dto);
+
+        if (CollectionUtil.isEmpty(itemData)) {
+            return;
+        }
+
+        try {
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+            String fileName = "姝f�佸垎甯冨垎鏋愭暟鎹甠" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
+            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+
+            // 鏋勫缓瀵煎嚭鏁版嵁
+            List<Map<String, Object>> exportData = new ArrayList<>();
+            for (Map<String, Object> item : itemData) {
+                Map<String, Object> row = new HashMap<>();
+                row.put("sampleCode", item.get("sampleCode"));
+                row.put("sampleName", item.get("sampleName"));
+                row.put("itemName", item.get("itemName"));
+                row.put("lastValue", item.get("lastValue"));
+                row.put("insTime", item.get("insTime"));
+                exportData.add(row);
+            }
+
+            EasyExcel.write(response.getOutputStream())
+                    .sheet("姝f�佸垎甯冨垎鏋愭暟鎹�")
+                    .doWrite(exportData);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public List<String> getItemNames(NormalDistributionDto dto) {
+        return normalDistributionMapper.getItemNames(dto);
+    }
+
+    @Override
+    public List<String> getSampleNames(NormalDistributionDto dto) {
+        return normalDistributionMapper.getSampleNames(dto);
+    }
+
+    /**
+     * 骞虫柟鏍硅绠�
+     */
+    private BigDecimal sqrt(BigDecimal value) {
+        BigDecimal x = value;
+        BigDecimal tolerance = new BigDecimal("1E-10");
+        BigDecimal guess = value.divide(BigDecimal.valueOf(2), MathContext.DECIMAL128);
+
+        while (x.subtract(guess).abs().compareTo(tolerance) > 0) {
+            x = guess;
+            guess = x.add(value.divide(x, MathContext.DECIMAL128)).divide(new BigDecimal("2"), MathContext.DECIMAL128);
+        }
+
+        return guess;
+    }
+
+}
\ No newline at end of file

--
Gitblit v1.9.3