From a1941a375b8b9fa2aa313bd99c8f8058754e8038 Mon Sep 17 00:00:00 2001 From: caozehui <2427765068@qq.com> Date: Tue, 26 May 2026 09:23:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A3=80=E6=B5=8B=E8=AE=A1=E5=88=92=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plan/controller/AdPlanController.java | 12 + .../gather/plan/pojo/param/AdPlanParam.java | 7 + .../plan/pojo/vo/PlanStatisticsItemVO.java | 42 ++++ .../gather/plan/pojo/vo/PlanStatisticsVO.java | 83 +++++++ .../gather/plan/service/IAdPlanService.java | 9 + .../plan/service/impl/AdPlanServiceImpl.java | 234 ++++++++++++++++++ 6 files changed, 387 insertions(+) create mode 100644 detection/src/main/java/com/njcn/gather/plan/pojo/vo/PlanStatisticsItemVO.java create mode 100644 detection/src/main/java/com/njcn/gather/plan/pojo/vo/PlanStatisticsVO.java diff --git a/detection/src/main/java/com/njcn/gather/plan/controller/AdPlanController.java b/detection/src/main/java/com/njcn/gather/plan/controller/AdPlanController.java index a9311121..03239003 100644 --- a/detection/src/main/java/com/njcn/gather/plan/controller/AdPlanController.java +++ b/detection/src/main/java/com/njcn/gather/plan/controller/AdPlanController.java @@ -22,6 +22,7 @@ import com.njcn.gather.device.service.IPqDevService; import com.njcn.gather.plan.pojo.param.AdPlanParam; import com.njcn.gather.plan.pojo.po.AdPlan; import com.njcn.gather.plan.pojo.vo.AdPlanVO; +import com.njcn.gather.plan.pojo.vo.PlanStatisticsVO; import com.njcn.gather.plan.service.AsyncPlanHandler; import com.njcn.gather.plan.service.IAdPlanService; import com.njcn.gather.type.pojo.po.DevType; @@ -198,6 +199,17 @@ public class AdPlanController extends BaseController { adPlanService.analyse(ids); } + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/statistics") + @ApiOperation("检测计划统计") + @ApiImplicitParam(name = "param", value = "检测计划统计参数", required = true) + public HttpResult statistics(@RequestBody @Validated AdPlanParam.StatisticsParam param) { + String methodDescribe = getMethodDescribe("statistics"); + LogUtil.njcnDebug(log, "{},查询数据为:{}", methodDescribe, param); + PlanStatisticsVO result = adPlanService.statistics(param.getPlanId()); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + @OperateInfo(info = LogEnum.BUSINESS_COMMON) @PostMapping("/listByPlanId") @ApiOperation("查询出所有已绑定的设备") diff --git a/detection/src/main/java/com/njcn/gather/plan/pojo/param/AdPlanParam.java b/detection/src/main/java/com/njcn/gather/plan/pojo/param/AdPlanParam.java index 491bb4dd..dab28f30 100644 --- a/detection/src/main/java/com/njcn/gather/plan/pojo/param/AdPlanParam.java +++ b/detection/src/main/java/com/njcn/gather/plan/pojo/param/AdPlanParam.java @@ -130,4 +130,11 @@ public class AdPlanParam { private String patternId; private String scriptType; } + + @Data + public static class StatisticsParam { + @ApiModelProperty(value = "检测计划ID", required = true) + @NotBlank(message = DetectionValidMessage.PLAN_ID_NOT_BLANK) + private String planId; + } } diff --git a/detection/src/main/java/com/njcn/gather/plan/pojo/vo/PlanStatisticsItemVO.java b/detection/src/main/java/com/njcn/gather/plan/pojo/vo/PlanStatisticsItemVO.java new file mode 100644 index 00000000..4c455527 --- /dev/null +++ b/detection/src/main/java/com/njcn/gather/plan/pojo/vo/PlanStatisticsItemVO.java @@ -0,0 +1,42 @@ +package com.njcn.gather.plan.pojo.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 检测计划单个检测项统计结果。 + */ +@Data +public class PlanStatisticsItemVO { + + /** + * 检测项ID。 + */ + private String itemId; + + /** + * 检测项名称。 + */ + private String itemName; + + /** + * 执行次数。 + */ + private Integer totalCount; + + /** + * 合格次数。 + */ + private Integer qualifiedCount; + + /** + * 不合格次数。 + */ + private Integer unqualifiedCount; + + /** + * 合格率,百分制。 + */ + private BigDecimal passRate; +} diff --git a/detection/src/main/java/com/njcn/gather/plan/pojo/vo/PlanStatisticsVO.java b/detection/src/main/java/com/njcn/gather/plan/pojo/vo/PlanStatisticsVO.java new file mode 100644 index 00000000..018be326 --- /dev/null +++ b/detection/src/main/java/com/njcn/gather/plan/pojo/vo/PlanStatisticsVO.java @@ -0,0 +1,83 @@ +package com.njcn.gather.plan.pojo.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 检测计划统计结果。 + */ +@Data +public class PlanStatisticsVO { + + /** + * 检测计划ID。 + */ + private String planId; + + /** + * 检测计划名称。 + */ + private String planName; + + /** + * 所有设备检测次数之和。 + */ + private Integer totalCheckCount; + + /** + * 已检设备总数。 + */ + private Integer checkedDeviceCount; + + /** + * 第一次检测合格的设备数量。 + */ + private Integer firstQualifiedDeviceCount; + + /** + * 第二次检测后合格的设备数量。 + */ + private Integer secondQualifiedDeviceCount; + + /** + * 第三次及以上检测后合格的设备数量。 + */ + private Integer thirdOrMoreQualifiedDeviceCount; + + /** + * 最终不合格的设备数量。 + */ + private Integer unqualifiedDeviceCount; + + /** + * 存在不合格结果的检测项数量。 + */ + private Integer unqualifiedItemCount; + + /** + * 一次合格率,百分制。 + */ + private BigDecimal firstPassRate; + + /** + * 二次合格率,百分制。 + */ + private BigDecimal secondPassRate; + + /** + * 三次及以上合格率,百分制。 + */ + private BigDecimal thirdOrMorePassRate; + + /** + * 不合格率,百分制。 + */ + private BigDecimal unqualifiedRate; + + /** + * 检测项分布。 + */ + private List itemDistributions; +} diff --git a/detection/src/main/java/com/njcn/gather/plan/service/IAdPlanService.java b/detection/src/main/java/com/njcn/gather/plan/service/IAdPlanService.java index b6fdde4d..574d87aa 100644 --- a/detection/src/main/java/com/njcn/gather/plan/service/IAdPlanService.java +++ b/detection/src/main/java/com/njcn/gather/plan/service/IAdPlanService.java @@ -6,6 +6,7 @@ import com.njcn.gather.device.pojo.po.PqStandardDev; import com.njcn.gather.plan.pojo.param.AdPlanParam; import com.njcn.gather.plan.pojo.po.AdPlan; import com.njcn.gather.plan.pojo.vo.AdPlanVO; +import com.njcn.gather.plan.pojo.vo.PlanStatisticsVO; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; @@ -105,6 +106,14 @@ public interface IAdPlanService extends IService { */ void analyse(List ids); + /** + * 统计检测计划结果。 + * + * @param planId 检测计划ID + * @return 检测计划统计结果 + */ + PlanStatisticsVO statistics(String planId); + /** * 导出检测计划数据 * diff --git a/detection/src/main/java/com/njcn/gather/plan/service/impl/AdPlanServiceImpl.java b/detection/src/main/java/com/njcn/gather/plan/service/impl/AdPlanServiceImpl.java index c77f80de..6f4a25d7 100644 --- a/detection/src/main/java/com/njcn/gather/plan/service/impl/AdPlanServiceImpl.java +++ b/detection/src/main/java/com/njcn/gather/plan/service/impl/AdPlanServiceImpl.java @@ -69,7 +69,9 @@ import com.njcn.gather.script.service.IPqScriptService; import com.njcn.gather.source.pojo.po.PqSource; import com.njcn.gather.source.service.IPqSourceService; import com.njcn.gather.storage.pojo.param.StorageParam; +import com.njcn.gather.storage.pojo.po.SimAndDigBaseResult; import com.njcn.gather.storage.service.SimAndDigHarmonicService; +import com.njcn.gather.storage.service.SimAndDigNonHarmonicService; import com.njcn.gather.storage.service.TableGenService; import com.njcn.gather.system.cfg.pojo.enums.SceneEnum; import com.njcn.gather.system.cfg.pojo.po.SysTestConfig; @@ -107,6 +109,8 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.net.URLEncoder; import java.util.*; import java.util.stream.Collectors; @@ -133,6 +137,7 @@ public class AdPlanServiceImpl extends ServiceImpl impleme private final IDevTypeService devTypeService; private final IDictTypeService dictTypeService; private final SimAndDigHarmonicService adHarmonicService; + private final SimAndDigNonHarmonicService adNonHarmonicService; private final PqDevMapper pqDevMapper; private final IPqDevSubService pqDevSubService; private final IAdPlanStandardDevService adPlanStandardDevService; @@ -555,6 +560,7 @@ public class AdPlanServiceImpl extends ServiceImpl impleme child.put("name", adPlan.getName()); child.put("timeCheck", adPlan.getTimeCheck()); child.put("dataRule", adPlan.getDataRule()); + child.put("testState", adPlan.getTestState()); List pqStandardDevs = adPlanStandardDevMapper.listByPlanId(Collections.singletonList(adPlan.getId())); List devTypeIdList = pqStandardDevs.stream().map(PqStandardDev::getDevType).collect(Collectors.toList()); @@ -809,6 +815,234 @@ public class AdPlanServiceImpl extends ServiceImpl impleme } } + @Override + public PlanStatisticsVO statistics(String planId) { + AdPlan plan = this.getById(planId); + if (ObjectUtil.isNull(plan)) { + throw new BusinessException(DetectionResponseEnum.PLAN_NOT_EXIST); + } + if (!CheckStateEnum.CHECKED.getValue().equals(plan.getTestState())) { + throw new BusinessException(DetectionResponseEnum.NOT_CHECKED_PLAN_CANNOT_ANALYSE); + } + + List checkedDevices = listCheckedDevices(plan.getId()); + PlanStatisticsVO statistics = new PlanStatisticsVO(); + statistics.setPlanId(plan.getId()); + statistics.setPlanName(plan.getName()); + statistics.setCheckedDeviceCount(checkedDevices.size()); + statistics.setTotalCheckCount(checkedDevices.stream().mapToInt(dev -> defaultZero(dev.getRecheckNum())).sum()); + + int firstQualifiedCount = (int) checkedDevices.stream() + .filter(dev -> Objects.equals(dev.getRecheckNum(), 1)) + .filter(dev -> Objects.equals(dev.getCheckResult(), CheckResultEnum.ACCORD.getValue())) + .count(); + int secondQualifiedCount = (int) checkedDevices.stream() + .filter(dev -> Objects.equals(dev.getRecheckNum(), 2)) + .filter(dev -> Objects.equals(dev.getCheckResult(), CheckResultEnum.ACCORD.getValue())) + .count(); + int thirdOrMoreQualifiedCount = (int) checkedDevices.stream() + .filter(dev -> defaultZero(dev.getRecheckNum()) >= 3) + .filter(dev -> Objects.equals(dev.getCheckResult(), CheckResultEnum.ACCORD.getValue())) + .count(); + int unqualifiedDeviceCount = (int) checkedDevices.stream() + .filter(dev -> Objects.equals(dev.getCheckResult(), CheckResultEnum.NOT_ACCORD.getValue())) + .count(); + + statistics.setFirstQualifiedDeviceCount(firstQualifiedCount); + statistics.setSecondQualifiedDeviceCount(secondQualifiedCount); + statistics.setThirdOrMoreQualifiedDeviceCount(thirdOrMoreQualifiedCount); + statistics.setUnqualifiedDeviceCount(unqualifiedDeviceCount); + statistics.setFirstPassRate(rate(firstQualifiedCount, checkedDevices.size())); + statistics.setSecondPassRate(rate(secondQualifiedCount, checkedDevices.size())); + statistics.setThirdOrMorePassRate(rate(thirdOrMoreQualifiedCount, checkedDevices.size())); + statistics.setUnqualifiedRate(rate(unqualifiedDeviceCount, checkedDevices.size())); + + List itemDistributions = buildItemDistributions(plan, checkedDevices); + statistics.setItemDistributions(itemDistributions); + statistics.setUnqualifiedItemCount(itemDistributions.stream() + .mapToInt(item -> defaultZero(item.getUnqualifiedCount())) + .sum()); + return statistics; + } + + private List listCheckedDevices(String planId) { + PqDevParam.QueryParam queryParam = new PqDevParam.QueryParam(); + queryParam.setPlanIdList(Collections.singletonList(planId)); + List pqDevVOList = pqDevMapper.selectByQueryParam(queryParam); + if (CollUtil.isEmpty(pqDevVOList)) { + return Collections.emptyList(); + } + return pqDevVOList.stream() + .filter(dev -> !Objects.equals(dev.getCheckResult(), CheckResultEnum.UNCHECKED.getValue())) + .collect(Collectors.toList()); + } + + private List buildItemDistributions(AdPlan plan, List checkedDevices) { + if (CollUtil.isEmpty(checkedDevices) || StrUtil.isBlank(plan.getScriptId()) || ObjectUtil.isNull(plan.getCode())) { + return Collections.emptyList(); + } + + Map scriptItemInfoMap = getScriptItemInfoMap(plan.getScriptId()); + if (CollUtil.isEmpty(scriptItemInfoMap)) { + return Collections.emptyList(); + } + + Map itemAccumulatorMap = new TreeMap<>(); + for (PqDevVO device : checkedDevices) { + List deviceResultList = new ArrayList<>(); + deviceResultList.addAll(adNonHarmonicService.listSimAndDigBaseResult(plan.getScriptId(), plan.getCode() + "", device.getId())); + deviceResultList.addAll(adHarmonicService.listAllResultData(plan.getScriptId(), plan.getCode() + "", device.getId())); + if (CollUtil.isEmpty(deviceResultList)) { + continue; + } + + Map> deviceItemResultMap = deviceResultList.stream() + .filter(result -> ObjectUtil.isNotNull(result.getSort())) + .map(result -> new AbstractMap.SimpleEntry<>(scriptItemInfoMap.get(result.getSort()), result)) + .filter(entry -> ObjectUtil.isNotNull(entry.getKey())) + .collect(Collectors.groupingBy( + entry -> entry.getKey().getItemId(), + TreeMap::new, + Collectors.mapping(Map.Entry::getValue, Collectors.toList()) + )); + + deviceItemResultMap.forEach((itemId, results) -> { + boolean hasUnqualified = results.stream().anyMatch(result -> isUnqualifiedResultFlag(result.getResultFlag())); + boolean hasQualified = results.stream().anyMatch(result -> Objects.equals(result.getResultFlag(), 1)); + if (!hasUnqualified && !hasQualified) { + return; + } + + ScriptItemInfo itemInfo = scriptItemInfoMap.get(results.get(0).getSort()); + PlanStatisticsItemAccumulator accumulator = itemAccumulatorMap.computeIfAbsent( + itemId, + key -> new PlanStatisticsItemAccumulator(itemId, itemInfo.getItemName()) + ); + accumulator.totalCount++; + if (hasUnqualified) { + accumulator.unqualifiedCount++; + } else { + accumulator.qualifiedCount++; + } + }); + } + + return itemAccumulatorMap.values().stream() + .map(PlanStatisticsItemAccumulator::toVO) + .collect(Collectors.toList()); + } + + private Map getScriptItemInfoMap(String scriptId) { + List scriptDtlsList = pqScriptDtlsService.list(new LambdaQueryWrapper() + .eq(PqScriptDtls::getScriptId, scriptId) + .ne(PqScriptDtls::getScriptIndex, -1) + .eq(PqScriptDtls::getEnable, DataStateEnum.ENABLE.getCode()) + ); + if (CollUtil.isEmpty(scriptDtlsList)) { + return Collections.emptyMap(); + } + + List scriptTypeIds = scriptDtlsList.stream() + .map(PqScriptDtls::getScriptType) + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.toList()); + Map dictTreeMap = Collections.emptyMap(); + if (CollUtil.isNotEmpty(scriptTypeIds)) { + dictTreeMap = dictTreeService.getDictTreeById(scriptTypeIds).stream() + .collect(Collectors.toMap(DictTree::getId, dictTree -> dictTree, (left, right) -> left)); + } + + Map finalDictTreeMap = dictTreeMap; + return scriptDtlsList.stream() + .collect(Collectors.groupingBy(PqScriptDtls::getScriptIndex, TreeMap::new, Collectors.toList())) + .entrySet() + .stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> resolveScriptItemInfo(entry.getKey(), entry.getValue(), finalDictTreeMap), + (left, right) -> left, + TreeMap::new + )); + } + + private ScriptItemInfo resolveScriptItemInfo(Integer sort, List dtlsList, Map dictTreeMap) { + if (CollUtil.isEmpty(dtlsList)) { + return new ScriptItemInfo(String.valueOf(sort), "检测项" + sort); + } + PqScriptDtls first = dtlsList.get(0); + DictTree dictTree = dictTreeMap.get(first.getScriptType()); + if (ObjectUtil.isNotNull(dictTree) && StrUtil.isNotBlank(dictTree.getName())) { + return new ScriptItemInfo(dictTree.getId(), dictTree.getName()); + } + if (StrUtil.isNotBlank(first.getScriptType())) { + return new ScriptItemInfo(first.getScriptType(), "检测项" + sort); + } + return new ScriptItemInfo(String.valueOf(sort), "检测项" + sort); + } + + private boolean isUnqualifiedResultFlag(Integer resultFlag) { + return ObjectUtil.isNotNull(resultFlag) + && !Objects.equals(resultFlag, 1) + && !Objects.equals(resultFlag, 4) + && !Objects.equals(resultFlag, 5); + } + + private Integer defaultZero(Integer value) { + return ObjectUtil.isNull(value) ? 0 : value; + } + + private BigDecimal rate(Integer numerator, Integer denominator) { + if (ObjectUtil.isNull(denominator) || denominator == 0) { + return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); + } + return BigDecimal.valueOf(defaultZero(numerator)) + .multiply(BigDecimal.valueOf(100)) + .divide(BigDecimal.valueOf(denominator), 2, RoundingMode.HALF_UP); + } + + private class PlanStatisticsItemAccumulator { + private final String itemId; + private final String itemName; + private int totalCount; + private int qualifiedCount; + private int unqualifiedCount; + + private PlanStatisticsItemAccumulator(String itemId, String itemName) { + this.itemId = itemId; + this.itemName = itemName; + } + + private PlanStatisticsItemVO toVO() { + PlanStatisticsItemVO itemVO = new PlanStatisticsItemVO(); + itemVO.setItemId(itemId); + itemVO.setItemName(itemName); + itemVO.setTotalCount(totalCount); + itemVO.setQualifiedCount(qualifiedCount); + itemVO.setUnqualifiedCount(unqualifiedCount); + itemVO.setPassRate(rate(qualifiedCount, totalCount)); + return itemVO; + } + } + + private static class ScriptItemInfo { + private final String itemId; + private final String itemName; + + private ScriptItemInfo(String itemId, String itemName) { + this.itemId = itemId; + this.itemName = itemName; + } + + private String getItemId() { + return itemId; + } + + private String getItemName() { + return itemName; + } + } + @Override public void exportPlan(AdPlanParam.QueryParam queryParam) { DictData dictData = dictDataService.getDictDataById(queryParam.getPatternId());