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