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/SpcChartServiceImpl.java |  267 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 267 insertions(+), 0 deletions(-)

diff --git a/report-server/src/main/java/com/ruoyi/report/service/impl/SpcChartServiceImpl.java b/report-server/src/main/java/com/ruoyi/report/service/impl/SpcChartServiceImpl.java
new file mode 100644
index 0000000..f7e16f3
--- /dev/null
+++ b/report-server/src/main/java/com/ruoyi/report/service/impl/SpcChartServiceImpl.java
@@ -0,0 +1,267 @@
+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.SpcChartDto;
+import com.ruoyi.report.mapper.SpcChartMapper;
+import com.ruoyi.report.service.SpcChartService;
+import com.ruoyi.report.vo.SpcResultVo;
+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;
+
+/**
+ * SPC鎺у埗鍥炬湇鍔″疄鐜�
+ */
+@Service
+@AllArgsConstructor
+public class SpcChartServiceImpl implements SpcChartService {
+
+    private SpcChartMapper spcChartMapper;
+
+    @Override
+    public SpcResultVo analyze(SpcChartDto dto) {
+        // 鏌ヨ鏁版嵁
+        List<Map<String, Object>> itemData = spcChartMapper.getItemData(dto);
+
+        if (CollectionUtil.isEmpty(itemData)) {
+            return null;
+        }
+
+        // 鑾峰彇鏁板�煎垪琛�
+        List<BigDecimal> values = itemData.stream()
+                .map(m -> new BigDecimal((String) m.get("lastValue")))
+                .collect(Collectors.toList());
+
+        // 瀛愮粍澶у皬锛岄粯璁や负5
+        int subgroupSize = dto.getSubgroupSize() != null ? dto.getSubgroupSize() : 5;
+
+        // 鍒嗙粍
+        List<List<BigDecimal>> subgroups = new ArrayList<>();
+        for (int i = 0; i < values.size(); i += subgroupSize) {
+            int end = Math.min(i + subgroupSize, values.size());
+            if (end - i >= 2) { // 鑷冲皯闇�瑕�2涓暟鎹偣
+                subgroups.add(values.subList(i, end));
+            }
+        }
+
+        if (subgroups.isEmpty()) {
+            throw new ErrorException("鏁版嵁涓嶈冻浠ヨ繘琛孲PC鍒嗘瀽");
+        }
+
+        // 璁$畻X-bar鍜孯
+        List<BigDecimal> xBarData = new ArrayList<>();
+        List<BigDecimal> rData = new ArrayList<>();
+        List<String> sampleLabels = new ArrayList<>();
+
+        for (int i = 0; i < subgroups.size(); i++) {
+            List<BigDecimal> subgroup = subgroups.get(i);
+            BigDecimal sum = subgroup.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
+            BigDecimal mean = sum.divide(new BigDecimal(subgroup.size()), 10, RoundingMode.HALF_UP);
+            BigDecimal max = subgroup.stream().max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
+            BigDecimal min = subgroup.stream().min(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
+            BigDecimal range = max.subtract(min);
+
+            xBarData.add(mean.setScale(4, RoundingMode.HALF_UP));
+            rData.add(range.setScale(4, RoundingMode.HALF_UP));
+            sampleLabels.add("缁�" + (i + 1));
+        }
+
+        // 璁$畻鎺у埗闄�
+        BigDecimal xBarMean = xBarData.stream().reduce(BigDecimal.ZERO, BigDecimal::add)
+                .divide(new BigDecimal(xBarData.size()), 10, RoundingMode.HALF_UP);
+        BigDecimal rMean = rData.stream().reduce(BigDecimal.ZERO, BigDecimal::add)
+                .divide(new BigDecimal(rData.size()), 10, RoundingMode.HALF_UP);
+
+        // A2, D3, D4 甯告暟 (閽堝瀛愮粍澶у皬)
+        BigDecimal A2 = getA2(subgroupSize);
+        BigDecimal D3 = getD3(subgroupSize);
+        BigDecimal D4 = getD4(subgroupSize);
+
+        // X-bar 鎺у埗闄�
+        BigDecimal xBarUcl = dto.getUcl() != null ? dto.getUcl() :
+                xBarMean.add(A2.multiply(rMean)).setScale(4, RoundingMode.HALF_UP);
+        BigDecimal xBarLcl = dto.getLcl() != null ? dto.getLcl() :
+                xBarMean.subtract(A2.multiply(rMean)).setScale(4, RoundingMode.HALF_UP);
+
+        // R 鎺у埗闄�
+        BigDecimal rUcl = D4.multiply(rMean).setScale(4, RoundingMode.HALF_UP);
+        BigDecimal rLcl = D3.multiply(rMean).setScale(4, RoundingMode.HALF_UP);
+
+        // 鏋勫缓缁撴灉
+        SpcResultVo result = new SpcResultVo();
+
+        SpcResultVo.ChartData xBarChart = new SpcResultVo.ChartData();
+        xBarChart.setData(xBarData);
+        xBarChart.setUcl(xBarUcl);
+        xBarChart.setLcl(xBarLcl);
+        xBarChart.setCl(xBarMean.setScale(4, RoundingMode.HALF_UP));
+        xBarChart.setSampleLabels(sampleLabels);
+        result.setXBar(xBarChart);
+
+        SpcResultVo.ChartData rChart = new SpcResultVo.ChartData();
+        rChart.setData(rData);
+        rChart.setUcl(rUcl);
+        rChart.setLcl(rLcl);
+        rChart.setCl(rMean.setScale(4, RoundingMode.HALF_UP));
+        rChart.setSampleLabels(sampleLabels);
+        result.setRChart(rChart);
+
+        // 鍒剁▼鑳藉姏
+        SpcResultVo.Capability capability = calculateCapability(values, dto.getUcl(), dto.getLcl());
+        result.setCapability(capability);
+
+        return result;
+    }
+
+    @Override
+    public SpcResultVo.Capability getCapability(SpcChartDto dto) {
+        List<Map<String, Object>> itemData = spcChartMapper.getItemData(dto);
+
+        if (CollectionUtil.isEmpty(itemData)) {
+            return null;
+        }
+
+        List<BigDecimal> values = itemData.stream()
+                .map(m -> new BigDecimal((String) m.get("lastValue")))
+                .collect(Collectors.toList());
+
+        return calculateCapability(values, dto.getUcl(), dto.getLcl());
+    }
+
+    @Override
+    public void export(SpcChartDto dto, HttpServletResponse response) {
+        SpcResultVo result = analyze(dto);
+
+        if (result == null) {
+            return;
+        }
+
+        try {
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+            String fileName = "SPC鍒嗘瀽鏁版嵁_" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
+            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+
+            // 鏋勫缓瀵煎嚭鏁版嵁
+            List<Map<String, Object>> exportData = new ArrayList<>();
+            List<String> labels = result.getXBar().getSampleLabels();
+            List<BigDecimal> xBarData = result.getXBar().getData();
+            List<BigDecimal> rData = result.getRChart().getData();
+
+            for (int i = 0; i < labels.size(); i++) {
+                Map<String, Object> row = new HashMap<>();
+                row.put("sampleLabel", labels.get(i));
+                row.put("xBar", xBarData.get(i));
+                row.put("r", rData.get(i));
+                exportData.add(row);
+            }
+
+            EasyExcel.write(response.getOutputStream())
+                    .sheet("SPC鍒嗘瀽鏁版嵁")
+                    .doWrite(exportData);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public List<String> getItemNames(SpcChartDto dto) {
+        return spcChartMapper.getItemNames(dto);
+    }
+
+    @Override
+    public List<String> getSampleNames(SpcChartDto dto) {
+        return spcChartMapper.getSampleNames(dto);
+    }
+
+    /**
+     * 璁$畻鍒剁▼鑳藉姏
+     */
+    private SpcResultVo.Capability calculateCapability(List<BigDecimal> values, BigDecimal ucl, BigDecimal lcl) {
+        if (ucl == null || lcl == null) {
+            return null;
+        }
+
+        SpcResultVo.Capability capability = new SpcResultVo.Capability();
+
+        // 璁$畻鍧囧�煎拰鏍囧噯宸�
+        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);
+
+        // 璁$畻Cp, Cpk
+        BigDecimal USL = ucl;
+        BigDecimal LSL = lcl;
+        BigDecimal tolerance = USL.subtract(LSL);
+
+        BigDecimal cp = tolerance.divide(stdDev.multiply(new BigDecimal(6)), 4, RoundingMode.HALF_UP);
+        BigDecimal cpu = USL.subtract(mean).divide(stdDev.multiply(new BigDecimal(3)), 4, RoundingMode.HALF_UP);
+        BigDecimal cpl = mean.subtract(LSL).divide(stdDev.multiply(new BigDecimal(3)), 4, RoundingMode.HALF_UP);
+        BigDecimal cpk = cpu.min(cpl);
+
+        capability.setCp(cp);
+        capability.setCpk(cpk);
+        capability.setPp(cp); // 绠�鍖栧鐞�
+        capability.setPpk(cpk);
+
+        return capability;
+    }
+
+    /**
+     * 鑾峰彇A2甯告暟
+     */
+    private BigDecimal getA2(int n) {
+        double[] a2Values = {0, 1.880, 1.023, 0.729, 0.577, 0.483, 0.419, 0.373, 0.337, 0.308};
+        return new BigDecimal(a2Values[Math.min(n, 9)]);
+    }
+
+    /**
+     * 鑾峰彇D3甯告暟
+     */
+    private BigDecimal getD3(int n) {
+        double[] d3Values = {0, 0, 0, 0, 0, 0, 0.076, 0.136, 0.184, 0.223};
+        return new BigDecimal(d3Values[Math.min(n, 9)]);
+    }
+
+    /**
+     * 鑾峰彇D4甯告暟
+     */
+    private BigDecimal getD4(int n) {
+        double[] d4Values = {0, 3.267, 2.574, 2.282, 2.114, 2.004, 1.924, 1.864, 1.816, 1.777};
+        return new BigDecimal(d4Values[Math.min(n, 9)]);
+    }
+
+    /**
+     * 骞虫柟鏍硅绠�
+     */
+    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