diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/PlanController.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/PlanController.java index 92b9a02d7..046633081 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/PlanController.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/PlanController.java @@ -625,6 +625,12 @@ public class PlanController { return success(vo); } + @GetMapping("/quality-overview") + @Operation(summary = "质量概况") + public CommonResult getQualityOverview(@Valid PlanQualityOverviewReqVO reqVO) { + return success(planService.getQualityOverview(reqVO)); + } + @GetMapping("/getLastDaysRate") diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanQualityOverviewReqVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanQualityOverviewReqVO.java new file mode 100644 index 000000000..982ca9503 --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanQualityOverviewReqVO.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.mes.controller.admin.plan.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "管理后台 - 生产计划质量概况 Request VO") +public class PlanQualityOverviewReqVO { + + @Schema(description = "时间区间:LAST_WEEK/THIS_WEEK/LAST_7_DAYS/LAST_MONTH/THIS_MONTH") + private String period; +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanQualityOverviewRespVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanQualityOverviewRespVO.java new file mode 100644 index 000000000..d6bb547a7 --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/plan/vo/PlanQualityOverviewRespVO.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.mes.controller.admin.plan.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +@Schema(description = "管理后台 - 生产计划质量概况 Response VO") +public class PlanQualityOverviewRespVO { + + @Schema(description = "报工总数") + private Long totalWangongNumber; + + @Schema(description = "合格总数") + private Long totalPassNumber; + + @Schema(description = "不合格总数") + private Long totalNoPassNumber; + + @Schema(description = "总合格率") + private BigDecimal totalPassRate; + + @Schema(description = "按产品合格率排行") + private List productPassRateList; + + @Schema(description = "双折线图数据") + private List trendList; + + @Data + @Schema(description = "产品合格率项") + public static class ProductPassRateItem { + + @Schema(description = "产品ID") + private Long productId; + + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "报工总数") + private Long wangongNumber; + + @Schema(description = "合格数") + private Long passNumber; + + @Schema(description = "不合格数") + private Long noPassNumber; + + @Schema(description = "合格率") + private BigDecimal passRate; + } + + @Data + @Schema(description = "趋势项") + public static class TrendItem { + + @Schema(description = "日期") + private String day; + + @Schema(description = "合格数") + private Long passNumber; + + @Schema(description = "不合格数") + private Long noPassNumber; + } +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanService.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanService.java index 110b5307b..871d7ce23 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanService.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanService.java @@ -109,6 +109,8 @@ public interface PlanService { List> getLastSevenDaysCompletedCount(); + PlanQualityOverviewRespVO getQualityOverview(PlanQualityOverviewReqVO reqVO); + List getDevicePlanGantt(DevicePlanGanttReqVO reqVO); PageResult getPlanProductCapacityPage(PlanProductCapacityPageReqVO reqVO); diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanServiceImpl.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanServiceImpl.java index a8d076c33..c847388e8 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanServiceImpl.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/plan/PlanServiceImpl.java @@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpWarehouseMapper; import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.iot.controller.admin.device.enums.DeviceRateTrendPeriodEnum; import cn.iocoder.yudao.module.erp.service.stock.ErpStockInService; import cn.iocoder.yudao.module.erp.service.stock.ErpStockOutService; import cn.iocoder.yudao.module.erp.service.stock.ErpStockService; @@ -460,6 +461,103 @@ public class PlanServiceImpl implements PlanService { return result; } + @Override + public PlanQualityOverviewRespVO getQualityOverview(PlanQualityOverviewReqVO reqVO) { + DeviceRateTrendPeriodEnum.DateRange dateRange = DeviceRateTrendPeriodEnum + .valueOfCode(reqVO.getPeriod()) + .resolve(LocalDate.now()); + LocalDate startDate = dateRange.getStart(); + LocalDate endDate = dateRange.getEnd(); + if (endDate.isBefore(startDate)) { + endDate = startDate; + } + + LocalDateTime startTime = startDate.atStartOfDay(); + LocalDateTime endTime = endDate.plusDays(1).atStartOfDay(); + List planList = planMapper.selectList(new LambdaQueryWrapper() + .ge(PlanDO::getCreateTime, startTime) + .lt(PlanDO::getCreateTime, endTime)); + + PlanQualityOverviewRespVO respVO = new PlanQualityOverviewRespVO(); + long totalWangongNumber = 0L; + long totalPassNumber = 0L; + long totalNoPassNumber = 0L; + Map productMap = new HashMap<>(); + Map trendMap = new LinkedHashMap<>(); + + for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) { + PlanQualityOverviewRespVO.TrendItem trendItem = new PlanQualityOverviewRespVO.TrendItem(); + trendItem.setDay(date.toString()); + trendItem.setPassNumber(0L); + trendItem.setNoPassNumber(0L); + trendMap.put(date, trendItem); + } + + for (PlanDO plan : planList) { + long wangongNumber = Optional.ofNullable(plan.getWangongNumber()).orElse(0L); + long passNumber = Optional.ofNullable(plan.getPassNumber()).orElse(0L); + long noPassNumber = Optional.ofNullable(plan.getNoPassNumber()).orElse(0L); + + totalWangongNumber += wangongNumber; + totalPassNumber += passNumber; + totalNoPassNumber += noPassNumber; + + if (plan.getProductId() != null) { + PlanQualityOverviewRespVO.ProductPassRateItem item = productMap.computeIfAbsent(plan.getProductId(), key -> { + PlanQualityOverviewRespVO.ProductPassRateItem value = new PlanQualityOverviewRespVO.ProductPassRateItem(); + value.setProductId(key); + value.setWangongNumber(0L); + value.setPassNumber(0L); + value.setNoPassNumber(0L); + value.setPassRate(BigDecimal.ZERO); + return value; + }); + item.setWangongNumber(item.getWangongNumber() + wangongNumber); + item.setPassNumber(item.getPassNumber() + passNumber); + item.setNoPassNumber(item.getNoPassNumber() + noPassNumber); + } + + LocalDate trendDate = Optional.ofNullable(plan.getCreateTime()) + .map(LocalDateTime::toLocalDate) + .orElse(null); + if (trendDate != null && trendMap.containsKey(trendDate)) { + PlanQualityOverviewRespVO.TrendItem trendItem = trendMap.get(trendDate); + trendItem.setPassNumber(trendItem.getPassNumber() + passNumber); + trendItem.setNoPassNumber(trendItem.getNoPassNumber() + noPassNumber); + } + } + + Map productRespMap = erpProductService.getProductVOMap(productMap.keySet()); + List productPassRateList = new ArrayList<>(productMap.values()); + productPassRateList.forEach(item -> { + ErpProductRespVO product = productRespMap.get(item.getProductId()); + if (product != null) { + item.setProductName(product.getName()); + } + item.setPassRate(calculatePassRate(item.getPassNumber(), item.getWangongNumber())); + }); + productPassRateList.sort(Comparator.comparing(PlanQualityOverviewRespVO.ProductPassRateItem::getPassRate, + Comparator.reverseOrder()).thenComparing(PlanQualityOverviewRespVO.ProductPassRateItem::getProductId, + Comparator.nullsLast(Long::compareTo))); + + respVO.setTotalWangongNumber(totalWangongNumber); + respVO.setTotalPassNumber(totalPassNumber); + respVO.setTotalNoPassNumber(totalNoPassNumber); + respVO.setTotalPassRate(calculatePassRate(totalPassNumber, totalWangongNumber)); + respVO.setProductPassRateList(productPassRateList); + respVO.setTrendList(new ArrayList<>(trendMap.values())); + return respVO; + } + + private BigDecimal calculatePassRate(Long passNumber, Long totalNumber) { + if (totalNumber == null || totalNumber <= 0) { + return BigDecimal.ZERO; + } + return BigDecimal.valueOf(Optional.ofNullable(passNumber).orElse(0L)) + .multiply(BigDecimal.valueOf(100)) + .divide(BigDecimal.valueOf(totalNumber), 2, RoundingMode.HALF_UP); + } + @Override public PageResult getPlanPage(PlanPageReqVO pageReqVO) {