OEE添加合格品次品待判级内容

This commit is contained in:
2026-03-19 18:41:08 +08:00
parent a858abccea
commit 52b77991d5
3 changed files with 182 additions and 50 deletions

View File

@@ -3,7 +3,6 @@ package com.klp.pocket.acid.domain.vo;
import lombok.Data; import lombok.Data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date;
/** /**
* 酸轧线OEE日汇总视图对象 * 酸轧线OEE日汇总视图对象
@@ -45,18 +44,36 @@ public class AcidOeeDailySummaryVo {
/** 总产量(卷) */ /** 总产量(卷) */
private Long totalOutputCoil; private Long totalOutputCoil;
/** 良品量(吨) */ /** 良品量(A系列吨) */
private BigDecimal goodOutputTon; private BigDecimal goodOutputTon;
/** 良品量(卷) */ /** 良品量(A系列卷) */
private Long goodOutputCoil; private Long goodOutputCoil;
/** 不良量(吨)= total_output_ton - good_output_ton */ /** 合格品量B系列 */
private BigDecimal qualifiedOutputTon;
/** 合格品量B系列 */
private Long qualifiedOutputCoil;
/** 合格品合计A+B */
private BigDecimal abOutputTon;
/** 合格品合计A+B */
private Long abOutputCoil;
/** 次品量C/D系列 */
private BigDecimal defectOutputTon; private BigDecimal defectOutputTon;
/** 不良量(卷)= total_output_coil - good_output_coil */ /** 次品量C/D系列 */
private Long defectOutputCoil; private Long defectOutputCoil;
/** 待判级量O系列 */
private BigDecimal pendingOutputTon;
/** 待判级量O系列 */
private Long pendingOutputCoil;
/** 理论节拍min/吨;回归斜率) */ /** 理论节拍min/吨;回归斜率) */
private BigDecimal idealCycleTimeMinPerTon; private BigDecimal idealCycleTimeMinPerTon;
@@ -72,9 +89,18 @@ public class AcidOeeDailySummaryVo {
/** 派生指标:性能稼动率(卷维度) */ /** 派生指标:性能稼动率(卷维度) */
private BigDecimal performanceCoil; private BigDecimal performanceCoil;
/** 派生指标:良品率 */ /** 派生指标:良品率A/总量) */
private BigDecimal quality; private BigDecimal quality;
/** 派生指标合格品率A+B/总量) */
private BigDecimal qualifiedRate;
/** 派生指标次品率C/D系列/总量) */
private BigDecimal defectRate;
/** 派生指标待判级率O系列/总量) */
private BigDecimal pendingRate;
/** 派生指标OEE建议以吨维度为主 */ /** 派生指标OEE建议以吨维度为主 */
private BigDecimal oee; private BigDecimal oee;
} }

View File

@@ -41,8 +41,8 @@ import java.util.Set;
@Service @Service
public class AcidOeeServiceImpl implements IAcidOeeService { public class AcidOeeServiceImpl implements IAcidOeeService {
/** 次品判级集合:命中这些 quality_status 判为次品 */ /** 次品判级集合C/D系列 */
private static final Set<String> SCRAP_QUALITY_STATUS = new HashSet<>( private static final Set<String> CD_SERIES = new HashSet<>(
Arrays.asList("C+", "C", "C-", "D+", "D", "D-") Arrays.asList("C+", "C", "C-", "D+", "D", "D-")
); );
@@ -105,11 +105,17 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
List<CoilInfo> coilInfos = coilInfoByDate.get(statDate); List<CoilInfo> coilInfos = coilInfoByDate.get(statDate);
calculateQualityOutput(summary, coilInfos); calculateQualityOutput(summary, coilInfos);
} else { } else {
// 如果没有卷号,默认全部为良品(或根据业务规则处理) // 没有卷明细时,全部归待判级
summary.setGoodOutputTon(summary.getTotalOutputTon()); summary.setGoodOutputTon(BigDecimal.ZERO);
summary.setGoodOutputCoil(summary.getTotalOutputCoil()); summary.setGoodOutputCoil(0L);
summary.setQualifiedOutputTon(BigDecimal.ZERO);
summary.setQualifiedOutputCoil(0L);
summary.setAbOutputTon(BigDecimal.ZERO);
summary.setAbOutputCoil(0L);
summary.setDefectOutputTon(BigDecimal.ZERO); summary.setDefectOutputTon(BigDecimal.ZERO);
summary.setDefectOutputCoil(0L); summary.setDefectOutputCoil(0L);
summary.setPendingOutputTon(summary.getTotalOutputTon());
summary.setPendingOutputCoil(summary.getTotalOutputCoil());
} }
calculateDerivedMetrics(summary); calculateDerivedMetrics(summary);
@@ -378,51 +384,64 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
* 卷号信息内部类 * 卷号信息内部类
*/ */
private static class CoilInfo { private static class CoilInfo {
final String coilNo;
final BigDecimal weight; final BigDecimal weight;
final String qualityStatus; final String qualityStatus;
CoilInfo(String coilNo, BigDecimal weight, String qualityStatus) { CoilInfo(String coilNo, BigDecimal weight, String qualityStatus) {
this.coilNo = coilNo;
this.weight = weight; this.weight = weight;
this.qualityStatus = qualityStatus; this.qualityStatus = qualityStatus;
} }
} }
/** /**
* 计算良品/次品产量 * 计算质量细分产量A良品、B合格品、C/D次品、O待判级。
*/ */
private void calculateQualityOutput(AcidOeeDailySummaryVo summary, List<CoilInfo> coilInfos) { private void calculateQualityOutput(AcidOeeDailySummaryVo summary, List<CoilInfo> coilInfos) {
BigDecimal goodTon = BigDecimal.ZERO; BigDecimal aTon = BigDecimal.ZERO;
long goodCoil = 0L; long aCoil = 0L;
BigDecimal defectTon = BigDecimal.ZERO; BigDecimal bTon = BigDecimal.ZERO;
long defectCoil = 0L; long bCoil = 0L;
BigDecimal cdTon = BigDecimal.ZERO;
long cdCoil = 0L;
BigDecimal oTon = BigDecimal.ZERO;
long oCoil = 0L;
for (CoilInfo coilInfo : coilInfos) { for (CoilInfo coilInfo : coilInfos) {
BigDecimal coilWeight = coilInfo.weight != null ? coilInfo.weight : BigDecimal.ZERO; BigDecimal coilWeight = coilInfo.weight != null ? coilInfo.weight : BigDecimal.ZERO;
String qualityStatus = StringUtils.trim(coilInfo.qualityStatus); String qualityStatus = StringUtils.upperCase(StringUtils.trim(coilInfo.qualityStatus));
// 没有判级时按良品处理(避免再次跨库匹配导致错配) if (StringUtils.isBlank(qualityStatus) || "O".equals(qualityStatus)) {
if (StringUtils.isBlank(qualityStatus)) { oTon = oTon.add(coilWeight);
goodTon = goodTon.add(coilWeight); oCoil++;
goodCoil++;
continue; continue;
} }
if (SCRAP_QUALITY_STATUS.contains(qualityStatus)) { if (qualityStatus.startsWith("A")) {
// 次品 aTon = aTon.add(coilWeight);
defectTon = defectTon.add(coilWeight); aCoil++;
defectCoil++; } else if (qualityStatus.startsWith("B")) {
bTon = bTon.add(coilWeight);
bCoil++;
} else if (CD_SERIES.contains(qualityStatus)) {
cdTon = cdTon.add(coilWeight);
cdCoil++;
} else { } else {
// 良品 // 未识别等级统一归待判级
goodTon = goodTon.add(coilWeight); oTon = oTon.add(coilWeight);
goodCoil++; oCoil++;
} }
} }
summary.setGoodOutputTon(goodTon); summary.setGoodOutputTon(aTon);
summary.setGoodOutputCoil(goodCoil); summary.setGoodOutputCoil(aCoil);
summary.setDefectOutputTon(defectTon); summary.setQualifiedOutputTon(bTon);
summary.setDefectOutputCoil(defectCoil); summary.setQualifiedOutputCoil(bCoil);
summary.setAbOutputTon(aTon.add(bTon));
summary.setAbOutputCoil(aCoil + bCoil);
summary.setDefectOutputTon(cdTon);
summary.setDefectOutputCoil(cdCoil);
summary.setPendingOutputTon(oTon);
summary.setPendingOutputCoil(oCoil);
} }
/** /**
@@ -471,12 +490,29 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
summary.setPerformanceCoil(performanceCoil); summary.setPerformanceCoil(performanceCoil);
} }
// 良品 // 质量细分比
if (totalOutputTon != null && totalOutputTon.compareTo(BigDecimal.ZERO) > 0) { if (totalOutputTon != null && totalOutputTon.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal goodOutputTon = summary.getGoodOutputTon() != null ? summary.getGoodOutputTon() : BigDecimal.ZERO; BigDecimal aTon = summary.getGoodOutputTon() != null ? summary.getGoodOutputTon() : BigDecimal.ZERO;
BigDecimal quality = goodOutputTon.divide(totalOutputTon, 4, RoundingMode.HALF_UP) BigDecimal bTon = summary.getQualifiedOutputTon() != null ? summary.getQualifiedOutputTon() : BigDecimal.ZERO;
BigDecimal cdTon = summary.getDefectOutputTon() != null ? summary.getDefectOutputTon() : BigDecimal.ZERO;
BigDecimal oTon = summary.getPendingOutputTon() != null ? summary.getPendingOutputTon() : BigDecimal.ZERO;
BigDecimal quality = aTon.divide(totalOutputTon, 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100)); .multiply(BigDecimal.valueOf(100));
summary.setQuality(quality); summary.setQuality(quality);
BigDecimal qualifiedRate = aTon.add(bTon)
.divide(totalOutputTon, 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100));
summary.setQualifiedRate(qualifiedRate);
BigDecimal defectRate = cdTon.divide(totalOutputTon, 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100));
summary.setDefectRate(defectRate);
BigDecimal pendingRate = oTon.divide(totalOutputTon, 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100));
summary.setPendingRate(pendingRate);
} }
// OEE以吨维度为主 // OEE以吨维度为主

View File

@@ -79,11 +79,26 @@
<span>{{ formatPercent(scope.row.performanceTon) }}</span> <span>{{ formatPercent(scope.row.performanceTon) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="quality" label="良品率 Q (%)" align="center"> <el-table-column prop="quality" label="良品率(%)" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ formatPercent(scope.row.quality) }}</span> <span>{{ formatPercent(scope.row.quality) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="qualifiedRate" label="合格品率(%)" align="center">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.qualifiedRate) }}</span>
</template>
</el-table-column>
<el-table-column prop="defectRate" label="次品率 (%)" align="center">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.defectRate) }}</span>
</template>
</el-table-column>
<el-table-column prop="pendingRate" label="待判级率 (%)" align="center">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.pendingRate) }}</span>
</template>
</el-table-column>
<el-table-column prop="loadingTimeMin" label="负荷时间 (min)" align="center" /> <el-table-column prop="loadingTimeMin" label="负荷时间 (min)" align="center" />
<el-table-column prop="downtimeMin" label="停机时间 (min)" align="center" /> <el-table-column prop="downtimeMin" label="停机时间 (min)" align="center" />
<el-table-column prop="runTimeMin" label="运转时间 (min)" align="center" /> <el-table-column prop="runTimeMin" label="运转时间 (min)" align="center" />
@@ -93,16 +108,26 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="totalOutputCoil" label="总产量 (卷)" align="center" /> <el-table-column prop="totalOutputCoil" label="总产量 (卷)" align="center" />
<el-table-column prop="goodOutputTon" label="良品量 (吨)" align="center"> <el-table-column prop="goodOutputTon" label="良品(吨)" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ formatNumber(scope.row.goodOutputTon) }}</span> <span>{{ formatNumber(scope.row.goodOutputTon) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="defectOutputTon" label="次品量 (吨)" align="center"> <el-table-column prop="abOutputTon" label="合格(吨)" align="center">
<template slot-scope="scope">
<span>{{ formatNumber(scope.row.abOutputTon) }}</span>
</template>
</el-table-column>
<el-table-column prop="defectOutputTon" label="次品(吨)" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ formatNumber(scope.row.defectOutputTon) }}</span> <span>{{ formatNumber(scope.row.defectOutputTon) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="pendingOutputTon" label="待判(吨)" align="center">
<template slot-scope="scope">
<span>{{ formatNumber(scope.row.pendingOutputTon) }}</span>
</template>
</el-table-column>
</el-table> </el-table>
<!-- 日明细趋势表格风格方便导出 Word --> <!-- 日明细趋势表格风格方便导出 Word -->
@@ -133,11 +158,26 @@
{{ formatPercent(scope.row.performanceTon) }} {{ formatPercent(scope.row.performanceTon) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="quality" label="Q (%)"> <el-table-column prop="quality" label="良品率 (%)">
<template slot-scope="scope"> <template slot-scope="scope">
{{ formatPercent(scope.row.quality) }} {{ formatPercent(scope.row.quality) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="qualifiedRate" label="合格品率 (%)">
<template slot-scope="scope">
{{ formatPercent(scope.row.qualifiedRate) }}
</template>
</el-table-column>
<el-table-column prop="defectRate" label="次品率 (%)">
<template slot-scope="scope">
{{ formatPercent(scope.row.defectRate) }}
</template>
</el-table-column>
<el-table-column prop="pendingRate" label="待判级率 (%)">
<template slot-scope="scope">
{{ formatPercent(scope.row.pendingRate) }}
</template>
</el-table-column>
<el-table-column prop="loadingTimeMin" label="负荷 (min)"/> <el-table-column prop="loadingTimeMin" label="负荷 (min)"/>
<el-table-column prop="downtimeMin" label="停机 (min)"/> <el-table-column prop="downtimeMin" label="停机 (min)"/>
<el-table-column prop="runTimeMin" label="运转 (min)"/> <el-table-column prop="runTimeMin" label="运转 (min)"/>
@@ -147,16 +187,26 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="totalOutputCoil" label="总产量 (卷)" /> <el-table-column prop="totalOutputCoil" label="总产量 (卷)" />
<el-table-column prop="goodOutputTon" label="良品 (吨)"> <el-table-column prop="goodOutputTon" label="良品(吨)">
<template slot-scope="scope"> <template slot-scope="scope">
{{ formatNumber(scope.row.goodOutputTon) }} {{ formatNumber(scope.row.goodOutputTon) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="defectOutputTon" label="次品 (吨)"> <el-table-column prop="abOutputTon" label="合格(吨)">
<template slot-scope="scope">
{{ formatNumber(scope.row.abOutputTon) }}
</template>
</el-table-column>
<el-table-column prop="defectOutputTon" label="次品(吨)">
<template slot-scope="scope"> <template slot-scope="scope">
{{ formatNumber(scope.row.defectOutputTon) }} {{ formatNumber(scope.row.defectOutputTon) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="pendingOutputTon" label="待判(吨)">
<template slot-scope="scope">
{{ formatNumber(scope.row.pendingOutputTon) }}
</template>
</el-table-column>
</el-table> </el-table>
<!-- OEE/A/P/Q 趋势图 --> <!-- OEE/A/P/Q 趋势图 -->
@@ -263,7 +313,7 @@
<ul class="formula-list"> <ul class="formula-list">
<li>A时间稼动率 = (负荷时间 停机时间) / 负荷时间</li> <li>A时间稼动率 = (负荷时间 停机时间) / 负荷时间</li>
<li>P性能稼动率吨维度 = (理论节拍 × 产量吨) / 实际运转时间</li> <li>P性能稼动率吨维度 = (理论节拍 × 产量吨) / 实际运转时间</li>
<li>Q良品率 = 良品 / 总产量吨</li> <li>Q良品率 = A系列 / 总产量吨</li>
</ul> </ul>
</div> </div>
@@ -273,8 +323,9 @@
<li><b>负荷时间</b>计划生产时间扣除计划停机后的时间</li> <li><b>负荷时间</b>计划生产时间扣除计划停机后的时间</li>
<li><b>停机时间</b>所有停机/中断 stop_type 汇总的总时长</li> <li><b>停机时间</b>所有停机/中断 stop_type 汇总的总时长</li>
<li><b>实际运转时间</b>负荷时间 停机时间</li> <li><b>实际运转时间</b>负荷时间 停机时间</li>
<li><b>理论节拍</b>优良日统计口径得到的稳定节拍分钟/由理论节拍接口提供</li> <li><b>理论节拍</b>先按天计算(运转时间/)再取中位数并按业务口径乘以80%</li>
<li><b>良品/次品</b> WMS `quality_status` 判断C+/C/C-/D+/D/D- </li> <li><b>A系列</b>良品<b>B系列</b>合格品<b>C/D系列</b>次品<b>O系列</b>待判级</li>
<li><b>合格品率(AB)</b> = (A+B) / 总量<b>次品率(CD)</b> = CD / 总量<b>待判级率(O)</b> = O / 总量</li>
</ul> </ul>
</div> </div>
@@ -337,7 +388,7 @@ export default {
this.formatDate(today) this.formatDate(today)
], ],
pickerOptions: { pickerOptions: {
disabledDate(time) { disabledDate() {
// 不限制选择范围,保留扩展空间 // 不限制选择范围,保留扩展空间
return false return false
} }
@@ -365,13 +416,18 @@ export default {
availability: 0, availability: 0,
performanceTon: 0, performanceTon: 0,
quality: 0, quality: 0,
qualifiedRate: 0,
defectRate: 0,
pendingRate: 0,
loadingTimeMin: 0, loadingTimeMin: 0,
downtimeMin: 0, downtimeMin: 0,
runTimeMin: 0, runTimeMin: 0,
totalOutputTon: 0, totalOutputTon: 0,
totalOutputCoil: 0, totalOutputCoil: 0,
goodOutputTon: 0, goodOutputTon: 0,
defectOutputTon: 0 abOutputTon: 0,
defectOutputTon: 0,
pendingOutputTon: 0
} }
} }
let sumLoading = 0 let sumLoading = 0
@@ -379,12 +435,17 @@ export default {
let sumRun = 0 let sumRun = 0
let sumTotalTon = 0 let sumTotalTon = 0
let sumGoodTon = 0 let sumGoodTon = 0
let sumAbTon = 0
let sumDefectTon = 0 let sumDefectTon = 0
let sumPendingTon = 0
let sumCoil = 0 let sumCoil = 0
let sumOee = 0 let sumOee = 0
let sumA = 0 let sumA = 0
let sumP = 0 let sumP = 0
let sumQ = 0 let sumQ = 0
let sumQualifiedRate = 0
let sumDefectRate = 0
let sumPendingRate = 0
list.forEach(row => { list.forEach(row => {
sumLoading += row.loadingTimeMin || 0 sumLoading += row.loadingTimeMin || 0
@@ -392,28 +453,37 @@ export default {
sumRun += row.runTimeMin || 0 sumRun += row.runTimeMin || 0
sumTotalTon += Number(row.totalOutputTon || 0) sumTotalTon += Number(row.totalOutputTon || 0)
sumGoodTon += Number(row.goodOutputTon || 0) sumGoodTon += Number(row.goodOutputTon || 0)
sumAbTon += Number(row.abOutputTon || 0)
sumDefectTon += Number(row.defectOutputTon || 0) sumDefectTon += Number(row.defectOutputTon || 0)
sumPendingTon += Number(row.pendingOutputTon || 0)
sumCoil += row.totalOutputCoil || 0 sumCoil += row.totalOutputCoil || 0
sumOee += Number(row.oee || 0) sumOee += Number(row.oee || 0)
sumA += Number(row.availability || 0) sumA += Number(row.availability || 0)
sumP += Number(row.performanceTon || 0) sumP += Number(row.performanceTon || 0)
sumQ += Number(row.quality || 0) sumQ += Number(row.quality || 0)
sumQualifiedRate += Number(row.qualifiedRate || 0)
sumDefectRate += Number(row.defectRate || 0)
sumPendingRate += Number(row.pendingRate || 0)
}) })
const n = list.length const n = list.length
const defectAgg = Math.max(0, sumTotalTon - sumGoodTon)
return { return {
oee: n ? sumOee / n : 0, oee: n ? sumOee / n : 0,
availability: n ? sumA / n : 0, availability: n ? sumA / n : 0,
performanceTon: n ? sumP / n : 0, performanceTon: n ? sumP / n : 0,
quality: n ? sumQ / n : 0, quality: n ? sumQ / n : 0,
qualifiedRate: n ? sumQualifiedRate / n : 0,
defectRate: n ? sumDefectRate / n : 0,
pendingRate: n ? sumPendingRate / n : 0,
loadingTimeMin: sumLoading, loadingTimeMin: sumLoading,
downtimeMin: sumDowntime, downtimeMin: sumDowntime,
runTimeMin: sumRun, runTimeMin: sumRun,
totalOutputTon: sumTotalTon, totalOutputTon: sumTotalTon,
totalOutputCoil: sumCoil, totalOutputCoil: sumCoil,
goodOutputTon: sumGoodTon, goodOutputTon: sumGoodTon,
defectOutputTon: defectAgg || sumDefectTon abOutputTon: sumAbTon,
defectOutputTon: sumDefectTon,
pendingOutputTon: sumPendingTon
} }
} }
}, },