Case 12: Production line QC grading
This case corresponds to Scenario 12 in the JQuick-Java repository’s USE-CASES.md, targeting batch quality inspection of discrete manufacturing in the MES domain. For each sampling record, the system deducts points by counts of critical / major / minor defects, stacks additional deductions for appearance, critical-dimension tolerance, and equipment status, combines them with the defect rate computed in per-mille integer arithmetic, and determines a four-level conclusion: accept (ACCEPT), rework (REWORK), scrap (SCRAP), or hold (HOLD). It further decides the disposition destination, whether the batch may be released, and whether to trigger a CAPA corrective/preventive action. All judgment criteria are driven by JQuick-Java XML rule proxies.
Industry scenario
Section titled “Industry scenario”Discrete manufacturing workshops organize production and sampling by batch. After a batch of precision shaft sleeves comes off the line, the QC station records the batch number, product information, sample size, and the counts of critical defects (safety / functional failure), major defects, and minor defects found in sampling. It also records the measured value, nominal value, and allowed deviation of the critical dimension, an appearance score from 0-100, the production equipment status (NORMAL / WARNING), and whether this is a first-article inspection.
The system first computes two base metrics: the defect rate uses integer arithmetic “defect count × 1000 / sample size”, outputting a per-mille value to avoid floating-point division precision issues; the critical dimension is checked with a two-sided tolerance against the nominal value — outside nominal ± tolerance means non-conforming. The quality score uses a deduction system starting from 100: each critical defect -40, major -15, minor -5 (with a floor of 0 inside the script); appearance below 95/85 deducts another 10/20; out-of-tolerance dimension deducts 30; equipment warning deducts 5. The Service aggregates these and then clamps to the 0-100 range.
Level grading follows the “one critical defect scraps the batch” principle: any critical defect or defect rate ≥200‰ means direct SCRAP; a quality score below 40 or defect rate ≥100‰ means REWORK; below 60 points or defect rate ≥30‰ means HOLD; otherwise ACCEPT. The four levels map to disposition destinations: scrap bin (SCRAP_BIN), rework line (REWORK_LINE), QA hold (QA_HOLD), and next process (NEXT_PROCESS).
Two parallel decisions are also produced: a CAPA corrective/preventive process is triggered when there is a critical defect, ≥3 major defects, or an equipment warning; only ACCEPT batches with a conforming critical dimension may be released. The decision’s notes record item by item: conforming flow-through, critical-defect isolation, dimension out-of-tolerance to rework, hold for tightened re-inspection, first-article confirmation, equipment check, and CAPA hints — forming a complete QC judgment explanation.
Business pain points
Section titled “Business pain points”- Defect deductions, extra deductions, and level thresholds scattered in hard-coded logic: critical -40 / major -15 / minor -5, the 200‰/100‰/30‰ defect-rate red lines, and the 40/60 score boundaries are spread across MES code, making adjustments difficult when switching production lines or materials.
- Quality standards vary by customer/material and require frequent releases: different product families may have different tolerances, appearance tiers, and deduction values; traditionally every adjustment means modifying Java and redeploying the MES system.
- Deduction systems can easily go negative or above 100: after stacking multiple deductions, the raw score can go negative (e.g., the mass-scrap case has a raw score of -55); without clamping on the Java side, illegal quality scores are produced.
- Floating-point division in defect rate has precision risks: computing the ratio with
doubleand comparing against thresholds can misjudge at boundaries like 30‰ and 100‰ due to rounding error; misjudging borderline batches is very costly on the production line. - Level, disposition, CAPA, and release are four coupled decisions: critical defects must scrap the batch regardless of score and block release; out-of-tolerance dimensions must route to the rework line. Getting these orderings and linkages wrong lets problem batches flow into the next process.
- Legacy production-line systems are mostly JDK 8 and IT-innovation environments: workshop servers have limited resources and cannot host standalone rule platforms or database-backed middleware; an embeddable lightweight rule solution is needed, and results must be auditable for quality accountability.
Solution architecture
Section titled “Solution architecture”This case adopts the six-piece structure of “input model + decision result model + rule interface + XML rule script + Service orchestration + JUnit tests”. Tolerances, deduction criteria, level thresholds, and disposition mappings are all externalized in XML; the Service handles defect-rate/deduction aggregation, score clamping, and explanation assembly:
| Layer | Responsibility | Files |
|---|---|---|
| Domain model | Batch sampling record, QC decision (score / defect rate / level / disposition / CAPA / release / notes) | InspectionBatch.java, InspectionDecision.java |
| Rule set & XML | Defect rate / tolerance / deductions + level / disposition / CAPA / release | QualityInspectionRules.java, quality-inspection.xml |
| Orchestration service | Defect-rate and deduction aggregation, 0-100 clamp, level assembly, judgment note assembly | QualityInspectionService.java |
| Tests | 6 batch profiles (release, scrap, rework, hold, CAPA, high defect rate) | QualityInspectionServiceTest.java |
Rule method breakdown
Section titled “Rule method breakdown”The rule interface has 10 methods in total; metric computation, deductions, level mapping, and parallel decisions are each independent:
| Rule method | Criterion | Score / output |
|---|---|---|
defectRatePermil |
Returns 0 when sample size ≤0; otherwise defect count × 1000 / sample size, integer arithmetic outputting per-mille | Per-mille integer |
dimensionPass |
Returns false when measured > nominal + tolerance or < nominal - tolerance; otherwise true |
boolean |
scoreDefects |
100 - critical×40 - major×15 - minor×5, floored at 0 | 0 ~ 100 |
scoreAppearance |
Appearance ≥95 no deduction; ≥85 deduct 10; otherwise deduct 20 | 0 / -10 / -20 |
scoreDimension |
Critical dimension out of tolerance deducts 30; conforming no deduction | 0 / -30 |
scoreEquipment |
Equipment status WARNING deducts 5; NORMAL no deduction | 0 / -5 |
qualityLevel |
Critical defects ≥1 or defect rate ≥200‰ → scrap; score <40 or ≥100‰ → rework; score <60 or ≥30‰ → hold; otherwise accept |
SCRAP/REWORK/HOLD/ACCEPT |
disposition |
SCRAP→SCRAP_BIN; REWORK→REWORK_LINE; HOLD→QA_HOLD; ACCEPT→NEXT_PROCESS | Disposition string |
needCapa |
Returns true when critical defects ≥1, major defects ≥3, or equipment warning hits | boolean |
releaseAllowed |
Returns true only when level is ACCEPT and the critical dimension conforms | boolean |
Code implementation (complete & runnable)
Section titled “Code implementation (complete & runnable)”Place the following six files into the JQuick-Java test project using the original package names from the source: the two models go into com.github.paohaijiao.demo.model, the rule interface and test class go into com.github.paohaijiao.demo, the orchestration service goes into com.github.paohaijiao.demo.service, and the XML script goes into src/test/resources/demo/. Keeping directories and package names unchanged, run mvn test -Dtest=QualityInspectionServiceTest at the project root.
InspectionBatch.java
Section titled “InspectionBatch.java”package com.github.paohaijiao.demo.model;
import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;
/** * 产线批次质检记录(抽检指标与实测数据快照) */@Data@NoArgsConstructor@AllArgsConstructorpublic class InspectionBatch {
/** * 批次号 */ private String batchNo;
/** * 产品编码 */ private String productCode;
/** * 产品名称 */ private String productName;
/** * 车间 */ private String workshop;
/** * 抽检数量 */ private int sampleSize;
/** * 致命缺陷数(安全/功能丧失) */ private int criticalDefects;
/** * 严重缺陷数 */ private int majorDefects;
/** * 轻微缺陷数 */ private int minorDefects;
/** * 关键尺寸实测值 */ private double measuredSize;
/** * 关键尺寸标称值 */ private double targetSize;
/** * 允许偏差(绝对值) */ private double tolerance;
/** * 外观评分(0-100) */ private int surfaceScore;
/** * 设备状态:NORMAL / WARNING */ private String equipmentStatus;
/** * 是否首件检验 */ private boolean firstArticle;}InspectionDecision.java
Section titled “InspectionDecision.java”package com.github.paohaijiao.demo.model;
import lombok.Data;
import java.util.ArrayList;import java.util.List;
/** * 批次质检决策结果 */@Datapublic class InspectionDecision {
/** * 批次号 */ private String batchNo;
/** * 综合质量分(0-100,扣分制) */ private int qualityScore;
/** * 不良率(千分比) */ private int defectRatePermil;
/** * 关键尺寸是否合格 */ private boolean dimensionPass;
/** * 质量等级:ACCEPT-接收 / REWORK-返工 / SCRAP-报废 / HOLD-挂起待检 */ private String qualityLevel;
/** * 处置去向:NEXT_PROCESS / REWORK_LINE / SCRAP_BIN / QA_HOLD */ private String disposition;
/** * 是否启动纠正预防措施(CAPA) */ private boolean needCapa;
/** * 是否允许批次放行 */ private boolean releaseAllowed;
/** * 判定说明 */ private List<String> notes = new ArrayList<String>();
public void addNote(String note) { this.notes.add(note); }}QualityInspectionRules.java
Section titled “QualityInspectionRules.java”package com.github.paohaijiao.demo;
import com.github.paohaijiao.xml.param.Param;
/** * 场景 12:产线质检多指标判定规则集 * <p> * 规则按「缺陷扣分 + 加扣项修正 → 等级判定 → 处置与 CAPA」拆分: * <ul> * <li>扣分类:致命/严重/轻微缺陷、外观、关键尺寸、设备状态</li> * <li>决策类:质量等级、处置去向、纠正措施、放行许可</li> * </ul> * 规则脚本见 {@code src/test/resources/demo/quality-inspection.xml}。 */public interface QualityInspectionRules {
int defectRatePermil(@Param("totalDefects") int totalDefects, @Param("sampleSize") int sampleSize);
boolean dimensionPass(@Param("measuredSize") double measuredSize, @Param("targetSize") double targetSize, @Param("tolerance") double tolerance);
int scoreDefects(@Param("criticalDefects") int criticalDefects, @Param("majorDefects") int majorDefects, @Param("minorDefects") int minorDefects);
int scoreAppearance(@Param("surfaceScore") int surfaceScore);
int scoreDimension(@Param("dimensionPass") boolean dimensionPass);
int scoreEquipment(@Param("equipmentStatus") String equipmentStatus);
String qualityLevel(@Param("qualityScore") int qualityScore, @Param("criticalDefects") int criticalDefects, @Param("defectRatePermil") int defectRatePermil);
String disposition(@Param("qualityLevel") String qualityLevel);
boolean needCapa(@Param("criticalDefects") int criticalDefects, @Param("majorDefects") int majorDefects, @Param("equipmentWarning") boolean equipmentWarning);
boolean releaseAllowed(@Param("qualityLevel") String qualityLevel, @Param("dimensionPass") boolean dimensionPass);}quality-inspection.xml
Section titled “quality-inspection.xml”<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE javas PUBLIC "-//PAOHAIJIAO//DTD API JAVA 1.0//EN" "classpath:paohaijiao/dtd/Jquick-java.dtd"><javas namespace="com.github.paohaijiao.demo.QualityInspectionRules">
<!-- 不良率(千分比,整数运算避免精度问题) --> <java name="defectRatePermil" returnClass="int"> <![CDATA[ int def defectRatePermil(int:totalDefects, int:sampleSize) { if (sampleSize <= 0) { return 0; } return totalDefects * 1000 / sampleSize; } ]]> </java>
<!-- 关键尺寸双向公差判定 --> <java name="dimensionPass" returnClass="boolean"> <![CDATA[ boolean def dimensionPass(double:measuredSize, double:targetSize, double:tolerance) { if (measuredSize > targetSize + tolerance) { return false; } else if (measuredSize < targetSize - tolerance) { return false; } else { return true; } } ]]> </java>
<!-- 缺陷扣分:致命 40 / 严重 15 / 轻微 5,下限 0 --> <java name="scoreDefects" returnClass="int"> <![CDATA[ int def scoreDefects(int:criticalDefects, int:majorDefects, int:minorDefects) { int score = 100 - criticalDefects * 40 - majorDefects * 15 - minorDefects * 5; if (score < 0) { score = 0; } return score; } ]]> </java>
<!-- 外观加扣项 --> <java name="scoreAppearance" returnClass="int"> <![CDATA[ int def scoreAppearance(int:surfaceScore) { if (surfaceScore >= 95) { return 0; } else if (surfaceScore >= 85) { return -10; } else { return -20; } } ]]> </java>
<!-- 尺寸加扣项 --> <java name="scoreDimension" returnClass="int"> <![CDATA[ int def scoreDimension(boolean:dimensionPass) { if (dimensionPass) { return 0; } else { return -30; } } ]]> </java>
<!-- 设备状态加扣项 --> <java name="scoreEquipment" returnClass="int"> <![CDATA[ int def scoreEquipment(str:equipmentStatus) { if (equipmentStatus == "WARNING") { return -5; } else { return 0; } } ]]> </java>
<!-- 质量等级:致命缺陷或高不良率直接报废,其次返工、挂起、接收 --> <java name="qualityLevel" returnClass="java.lang.String"> <![CDATA[ str def qualityLevel(int:qualityScore, int:criticalDefects, int:defectRatePermil) { if (criticalDefects >= 1 || defectRatePermil >= 200) { return "SCRAP"; } else if (qualityScore < 40 || defectRatePermil >= 100) { return "REWORK"; } else if (qualityScore < 60 || defectRatePermil >= 30) { return "HOLD"; } else { return "ACCEPT"; } } ]]> </java>
<!-- 处置去向 --> <java name="disposition" returnClass="java.lang.String"> <![CDATA[ str def disposition(str:qualityLevel) { if (qualityLevel == "SCRAP") { return "SCRAP_BIN"; } else if (qualityLevel == "REWORK") { return "REWORK_LINE"; } else if (qualityLevel == "HOLD") { return "QA_HOLD"; } else { return "NEXT_PROCESS"; } } ]]> </java>
<!-- 纠正预防措施:致命缺陷、严重缺陷成片、设备告警时触发 --> <java name="needCapa" returnClass="boolean"> <![CDATA[ boolean def needCapa(int:criticalDefects, int:majorDefects, boolean:equipmentWarning) { if (criticalDefects >= 1 || majorDefects >= 3 || equipmentWarning) { return true; } else { return false; } } ]]> </java>
<!-- 放行许可:必须接收且关键尺寸合格 --> <java name="releaseAllowed" returnClass="boolean"> <![CDATA[ boolean def releaseAllowed(str:qualityLevel, boolean:dimensionPass) { if (qualityLevel == "ACCEPT" && dimensionPass) { return true; } else { return false; } } ]]> </java>
</javas>QualityInspectionService.java
Section titled “QualityInspectionService.java”package com.github.paohaijiao.demo.service;
import com.github.paohaijiao.demo.QualityInspectionRules;import com.github.paohaijiao.demo.model.InspectionBatch;import com.github.paohaijiao.demo.model.InspectionDecision;
/** * 场景 12:产线批次质检分级服务 * <p> * 职责划分: * <ul> * <li><b>Java 侧</b>:不良率与扣分汇总、等级装配、处置说明与 CAPA 提示组装</li> * <li><b>规则侧</b>:公差判定、缺陷扣分口径、等级阈值与报废/返工界限,全部外置在 XML 中</li> * </ul> */public class QualityInspectionService {
private static final int SCORE_FLOOR = 0; private static final int SCORE_CEIL = 100;
private final QualityInspectionRules rules;
public QualityInspectionService(QualityInspectionRules rules) { this.rules = rules; }
/** * 执行批次质检判定 * * @param batch 批次抽检记录 * @return 质检决策 */ public InspectionDecision inspect(InspectionBatch batch) { InspectionDecision decision = new InspectionDecision(); decision.setBatchNo(batch.getBatchNo());
int totalDefects = batch.getCriticalDefects() + batch.getMajorDefects() + batch.getMinorDefects(); int ratePermil = rules.defectRatePermil(totalDefects, batch.getSampleSize()); boolean dimensionPass = rules.dimensionPass(batch.getMeasuredSize(), batch.getTargetSize(), batch.getTolerance());
int rawScore = rules.scoreDefects(batch.getCriticalDefects(), batch.getMajorDefects(), batch.getMinorDefects()) + rules.scoreAppearance(batch.getSurfaceScore()) + rules.scoreDimension(dimensionPass) + rules.scoreEquipment(batch.getEquipmentStatus()); int qualityScore = clamp(rawScore);
String level = rules.qualityLevel(qualityScore, batch.getCriticalDefects(), ratePermil); boolean equipmentWarning = "WARNING".equals(batch.getEquipmentStatus());
decision.setQualityScore(qualityScore); decision.setDefectRatePermil(ratePermil); decision.setDimensionPass(dimensionPass); decision.setQualityLevel(level); decision.setDisposition(rules.disposition(level)); decision.setNeedCapa(rules.needCapa(batch.getCriticalDefects(), batch.getMajorDefects(), equipmentWarning)); decision.setReleaseAllowed(rules.releaseAllowed(level, dimensionPass));
buildNotes(batch, decision, totalDefects); return decision; }
private int clamp(int score) { if (score < SCORE_FLOOR) { return SCORE_FLOOR; } if (score > SCORE_CEIL) { return SCORE_CEIL; } return score; }
private void buildNotes(InspectionBatch batch, InspectionDecision decision, int totalDefects) { if ("ACCEPT".equals(decision.getQualityLevel())) { decision.addNote("抽检 " + batch.getSampleSize() + " 件、不良 " + totalDefects + " 件,批次合格,流转下一工序"); } if (batch.getCriticalDefects() >= 1) { decision.addNote("发现致命缺陷 " + batch.getCriticalDefects() + " 件,整批判废并隔离"); } if (!decision.isDimensionPass()) { decision.addNote("关键尺寸实测 " + batch.getMeasuredSize() + " 超出公差 " + batch.getTolerance() + ",转返工线"); } if ("HOLD".equals(decision.getQualityLevel())) { decision.addNote("不良率或质量分处于临界区间,挂起等待加严复检"); } if (batch.isFirstArticle()) { decision.addNote("首件检验,需质量工程师确认后再批量投产"); } if ("WARNING".equals(batch.getEquipmentStatus())) { decision.addNote("生产设备处于告警状态,建议同步安排点检"); } if (decision.isNeedCapa()) { decision.addNote("已触发纠正预防措施(CAPA)流程"); } }}QualityInspectionServiceTest.java
Section titled “QualityInspectionServiceTest.java”package com.github.paohaijiao.demo;
import com.github.paohaijiao.demo.model.InspectionBatch;import com.github.paohaijiao.demo.model.InspectionDecision;import com.github.paohaijiao.demo.service.QualityInspectionService;import com.github.paohaijiao.xml.JQuickJava;import org.junit.Assert;import org.junit.Before;import org.junit.Test;
/** * 场景 12 生产级样板:产线质检多指标判定 * <p> * 覆盖 6 组批次形态:合格放行、致命缺陷报废、尺寸超差返工、 * 不良率临界挂起、设备告警成片缺陷、大批量报废。 */public class QualityInspectionServiceTest {
private QualityInspectionService service;
@Before public void setUp() { QualityInspectionRules rules = JQuickJava.create() .createApi(QualityInspectionRules.class, "demo/quality-inspection.xml"); this.service = new QualityInspectionService(rules); }
/** * 优质批次:200 抽 1 轻微不良、尺寸合格、外观 97 → 95 分接收放行 */ @Test public void shouldReleaseConformingBatch() { InspectionBatch batch = batch("BAT-1201", 200, 0, 0, 1, 10.01, 10.0, 0.05, 97, "NORMAL", false);
InspectionDecision decision = service.inspect(batch);
Assert.assertEquals(95, decision.getQualityScore()); Assert.assertEquals(5, decision.getDefectRatePermil()); Assert.assertTrue(decision.isDimensionPass()); Assert.assertEquals("ACCEPT", decision.getQualityLevel()); Assert.assertEquals("NEXT_PROCESS", decision.getDisposition()); Assert.assertTrue(decision.isReleaseAllowed()); Assert.assertFalse(decision.isNeedCapa()); Assert.assertTrue(decision.getNotes().contains("抽检 200 件、不良 1 件,批次合格,流转下一工序")); }
/** * 致命缺陷:1 件致命缺陷 → 不看分数直接整批报废 */ @Test public void shouldScrapBatchWithCriticalDefect() { InspectionBatch batch = batch("BAT-1202", 100, 1, 0, 2, 10.02, 10.0, 0.05, 80, "NORMAL", false);
InspectionDecision decision = service.inspect(batch);
Assert.assertEquals(30, decision.getQualityScore()); Assert.assertEquals("SCRAP", decision.getQualityLevel()); Assert.assertEquals("SCRAP_BIN", decision.getDisposition()); Assert.assertFalse(decision.isReleaseAllowed()); Assert.assertTrue(decision.isNeedCapa()); Assert.assertTrue(decision.getNotes().contains("发现致命缺陷 1 件,整批判废并隔离")); }
/** * 尺寸超差:2 严重 + 2 轻微、关键尺寸超差 → 20 分返工 */ @Test public void shouldReworkDimensionOutOfToleranceBatch() { InspectionBatch batch = batch("BAT-1203", 100, 0, 2, 2, 10.12, 10.0, 0.05, 90, "NORMAL", false);
InspectionDecision decision = service.inspect(batch);
Assert.assertEquals(20, decision.getQualityScore()); Assert.assertEquals(40, decision.getDefectRatePermil()); Assert.assertFalse(decision.isDimensionPass()); Assert.assertEquals("REWORK", decision.getQualityLevel()); Assert.assertEquals("REWORK_LINE", decision.getDisposition()); Assert.assertFalse(decision.isReleaseAllowed()); Assert.assertFalse(decision.isNeedCapa()); Assert.assertTrue(decision.getNotes().contains("关键尺寸实测 10.12 超出公差 0.05,转返工线")); }
/** * 不良率临界:4 件轻微不良、千分之 40 → 挂起待加严复检 */ @Test public void shouldHoldBorderlineDefectRateBatch() { InspectionBatch batch = batch("BAT-1204", 100, 0, 0, 4, 10.00, 10.0, 0.05, 97, "NORMAL", false);
InspectionDecision decision = service.inspect(batch);
Assert.assertEquals(80, decision.getQualityScore()); Assert.assertEquals(40, decision.getDefectRatePermil()); Assert.assertEquals("HOLD", decision.getQualityLevel()); Assert.assertEquals("QA_HOLD", decision.getDisposition()); Assert.assertFalse(decision.isReleaseAllowed()); Assert.assertFalse(decision.isNeedCapa()); Assert.assertTrue(decision.getNotes().contains("不良率或质量分处于临界区间,挂起等待加严复检")); }
/** * 设备告警 + 3 件严重缺陷 → 40 分挂起,触发 CAPA */ @Test public void shouldTriggerCapaForEquipmentWarningAndMajorDefects() { InspectionBatch batch = batch("BAT-1205", 200, 0, 3, 0, 10.01, 10.0, 0.05, 90, "WARNING", true);
InspectionDecision decision = service.inspect(batch);
Assert.assertEquals(40, decision.getQualityScore()); Assert.assertEquals(15, decision.getDefectRatePermil()); Assert.assertEquals("HOLD", decision.getQualityLevel()); Assert.assertTrue(decision.isNeedCapa()); Assert.assertTrue(decision.getNotes().contains("生产设备处于告警状态,建议同步安排点检")); Assert.assertTrue(decision.getNotes().contains("首件检验,需质量工程师确认后再批量投产")); Assert.assertTrue(decision.getNotes().contains("已触发纠正预防措施(CAPA)流程")); }
/** * 大批量报废:50 抽 16 件不良含 2 件致命、不良率千分之 320 */ @Test public void shouldScrapHighDefectRateBatch() { InspectionBatch batch = batch("BAT-1206", 50, 2, 4, 10, 10.20, 10.0, 0.05, 70, "WARNING", false);
InspectionDecision decision = service.inspect(batch);
Assert.assertEquals(0, decision.getQualityScore()); Assert.assertEquals(320, decision.getDefectRatePermil()); Assert.assertEquals("SCRAP", decision.getQualityLevel()); Assert.assertEquals("SCRAP_BIN", decision.getDisposition()); Assert.assertFalse(decision.isReleaseAllowed()); Assert.assertTrue(decision.isNeedCapa()); }
private InspectionBatch batch(String batchNo, int sampleSize, int critical, int major, int minor, double measured, double target, double tolerance, int surfaceScore, String equipmentStatus, boolean firstArticle) { InspectionBatch b = new InspectionBatch(); b.setBatchNo(batchNo); b.setProductCode("P-001"); b.setProductName("精密轴套"); b.setWorkshop("一号车间"); b.setSampleSize(sampleSize); b.setCriticalDefects(critical); b.setMajorDefects(major); b.setMinorDefects(minor); b.setMeasuredSize(measured); b.setTargetSize(target); b.setTolerance(tolerance); b.setSurfaceScore(surfaceScore); b.setEquipmentStatus(equipmentStatus); b.setFirstArticle(firstArticle); return b; }}Run & verified results
Section titled “Run & verified results”Run at the project root:
mvn test -Dtest=QualityInspectionServiceTestTest coverage
Section titled “Test coverage”| Test method | Constructed business profile | Key assertions / expected decision |
|---|---|---|
shouldReleaseConformingBatch |
200 sampled with 1 minor defect, dimension conforming, appearance 97, equipment normal (BAT-1201) | 95 points, defect rate 5‰, ACCEPT, NEXT_PROCESS, release true, no CAPA |
shouldScrapBatchWithCriticalDefect |
100 sampled with 1 critical + 2 minor, appearance 80 (BAT-1202) | Still SCRAP at 30 points, SCRAP_BIN, no release, CAPA true, contains the critical-defect isolation note |
shouldReworkDimensionOutOfToleranceBatch |
2 major + 2 minor, measured 10.12 exceeding tolerance 0.05 (BAT-1203) | 20 points, defect rate 40‰, dimension non-conforming, REWORK, REWORK_LINE, no release |
shouldHoldBorderlineDefectRateBatch |
100 sampled with 4 minor, dimension conforming, appearance 97 (BAT-1204) | 80 points but defect rate 40‰ → HOLD, QA_HOLD, no release, contains tightened re-inspection note |
shouldTriggerCapaForEquipmentWarningAndMajorDefects |
200 sampled with 3 major, equipment WARNING, first-article inspection (BAT-1205) | 40 points, 15‰, HOLD, CAPA true, with equipment-check / first-article-confirmation / CAPA notes |
shouldScrapHighDefectRateBatch |
50 sampled with 16 defects (2 critical + 4 major + 10 minor), dimension out of tolerance (BAT-1206) | Raw negative score clamped to 0, defect rate 320‰, SCRAP, SCRAP_BIN, CAPA true |
Verified output
Section titled “Verified output”High-quality batch (200 sampled with 1 minor defect, dimension conforming, appearance 97):
质量分: 95 不良率: 5‰ 等级: ACCEPT 处置: NEXT_PROCESS 放行: trueCritical-defect batch (1 critical defect, score ignored):
质量分: 30 等级: SCRAP 处置: SCRAP_BIN CAPA: true说明: 发现致命缺陷 1 件,整批判废并隔离Technical highlights
Section titled “Technical highlights”- Defect rate uses cross-multiplication in integer arithmetic:
defectRatePermilcomputes the per-mille value withtotalDefects * 1000 / sampleSize, staying in integers throughout — avoiding floating-point division rounding error and unifying comparison thresholds as integers, so the 30‰/100‰/200‰ boundary judgments are stable and reproducible. - Two-layer backstop for the deduction system: the
scoreDefectsscript internally floors defect deductions at 0, and the Service clamps the four-deduction aggregate to 0-100; the mass-scrap case with a raw score of -55 finally lands at 0, keeping illegal scores out of level grading. - One-critical-defect scrap priority hard-coded in rule order: the first branch of
qualityLeveliscriticalDefects >= 1 || defectRatePermil >= 200; a 30-point critical-defect batch is SCRAP just like a 0-point batch, without relying on incidental score outcomes. - All boolean judgments use positive returns, no negation:
dimensionPassreturnsfalsefor “above upper tolerance” and “below lower tolerance” separately, andtrueonly when conforming — matching the script’s lack of a!negation operator, intuitive and auditable branch by branch. - Parallel decisions are independent, assembled uniformly by the Service: level, disposition, CAPA, and release are four independent rule methods;
needCapacan be triggered by major defects ≥3 or equipment warning independently of the level, andreleaseAllowedconstrains both level and dimension — clear, testable linkages. - Thresholds externalized in XML + lightweight Java 8 embedding: deduction values, tolerance checks, and defect-rate red lines all live in
quality-inspection.xml; with the ASM call chain and JDK 8 compatibility, the solution embeds directly into legacy workshop MES and IT-innovation production-line systems without a standalone rule server.