From a7863b39b5317085e29510a9b8502cf8d3068893 Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Sat, 9 May 2026 17:17:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=B7=BB=E5=8A=A0=E4=BA=A7=E8=83=BD?= =?UTF-8?q?=E6=8A=A5=E8=A1=A8=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../deviceledger/DeviceLedgerController.java | 17 ++ .../vo/DeviceCapacityReportPageReqVO.java | 32 +++ .../vo/DeviceCapacityReportRespVO.java | 52 +++++ .../deviceledger/DeviceLedgerService.java | 4 + .../deviceledger/DeviceLedgerServiceImpl.java | 210 ++++++++++++++++-- 5 files changed, 302 insertions(+), 13 deletions(-) create mode 100644 yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/vo/DeviceCapacityReportPageReqVO.java create mode 100644 yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/vo/DeviceCapacityReportRespVO.java diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/DeviceLedgerController.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/DeviceLedgerController.java index 49b7fd458..6f903c13e 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/DeviceLedgerController.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/DeviceLedgerController.java @@ -212,6 +212,23 @@ public class DeviceLedgerController { ExcelUtils.write(response, "设备台账.xls", "数据", DeviceLedgerRespVO.class,deviceLedgerRespVOS); } + @GetMapping("/capacity-report/page") + @Operation(summary = "获得设备产能报表分页") + @PreAuthorize("@ss.hasPermission('mes:device-ledger:query')") + public CommonResult> getDeviceCapacityReportPage(@Valid DeviceCapacityReportPageReqVO pageReqVO) { + return success(deviceLedgerService.getDeviceCapacityReportPage(pageReqVO)); + } + + @GetMapping("/capacity-report/export-excel") + @Operation(summary = "导出设备产能报表 Excel") + @PreAuthorize("@ss.hasPermission('mes:device-ledger:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDeviceCapacityReportExcel(@Valid DeviceCapacityReportPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + List list = deviceLedgerService.getDeviceCapacityReportList(pageReqVO); + ExcelUtils.write(response, "设备产能报表.xls", "数据", DeviceCapacityReportRespVO.class, list); + } + diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/vo/DeviceCapacityReportPageReqVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/vo/DeviceCapacityReportPageReqVO.java new file mode 100644 index 000000000..c36befa0c --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/vo/DeviceCapacityReportPageReqVO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.mes.controller.admin.deviceledger.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - 设备产能报表分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeviceCapacityReportPageReqVO extends PageParam { + + @Schema(description = "设备编码") + private String deviceCode; + + @Schema(description = "设备名称") + private String deviceName; + + @Schema(description = "设备类型") + private Long deviceType; + + @Schema(description = "设备状态") + private Integer deviceStatus; + + @Schema(description = "所属车间") + private String workshop; + + @Schema(description = "导出 ids,逗号分隔") + private String ids; +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/vo/DeviceCapacityReportRespVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/vo/DeviceCapacityReportRespVO.java new file mode 100644 index 000000000..dea2d6bea --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/vo/DeviceCapacityReportRespVO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.mes.controller.admin.deviceledger.vo; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - 设备产能报表 Response VO") +@Data +@ExcelIgnoreUnannotated +public class DeviceCapacityReportRespVO { + + @Schema(description = "设备 id") + private Long id; + + @Schema(description = "设备编码") + @ExcelProperty("编码") + private String deviceCode; + + @Schema(description = "设备名称") + @ExcelProperty("名称") + private String deviceName; + + @Schema(description = "设备类型名称") + @ExcelProperty("类型") + private String typeName; + + @Schema(description = "设备状态") +// @ExcelProperty(value = "状态", converter = DictConvert.class) +// @DictFormat("mes_tz_status") + private Integer deviceStatus; + + @Schema(description = "额定产能(计划产能)") + @ExcelProperty("额定产能(计划产能)") + private BigDecimal ratedCapacity; + + @Schema(description = "报工产能") + @ExcelProperty("报工产能") + private BigDecimal reportCapacity; + + @Schema(description = "实际产能") + @ExcelProperty("实际产能") + private BigDecimal actualCapacity; + + @Schema(description = "所属车间") + @ExcelProperty("所属车间") + private String workshopName; +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/deviceledger/DeviceLedgerService.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/deviceledger/DeviceLedgerService.java index c12da64c5..9859146dc 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/deviceledger/DeviceLedgerService.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/deviceledger/DeviceLedgerService.java @@ -57,6 +57,10 @@ public interface DeviceLedgerService { */ PageResult getDeviceLedgerPage(DeviceLedgerPageReqVO pageReqVO); + PageResult getDeviceCapacityReportPage(DeviceCapacityReportPageReqVO pageReqVO); + + List getDeviceCapacityReportList(DeviceCapacityReportPageReqVO pageReqVO); + List getDeviceLedgerList(); /** * 获得产品 VO 列表 diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/deviceledger/DeviceLedgerServiceImpl.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/deviceledger/DeviceLedgerServiceImpl.java index 15e5a2e32..8a3f38a46 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/deviceledger/DeviceLedgerServiceImpl.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/deviceledger/DeviceLedgerServiceImpl.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.mes.service.deviceledger; -import cn.hutool.core.collection.CollStreamUtil; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -8,36 +7,33 @@ import cn.iocoder.yudao.module.common.enums.CodeTypeEnum; import cn.iocoder.yudao.module.common.enums.QrcodeBizTypeEnum; import cn.iocoder.yudao.module.common.service.qrcordrecord.QrcodeRecordService; import cn.iocoder.yudao.module.erp.controller.admin.autocode.util.AutoCodeUtil; -import cn.iocoder.yudao.module.erp.controller.admin.mold.vo.MoldRespVO; -import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; -import cn.iocoder.yudao.module.common.dal.dataobject.mold.MoldBrandDO; import cn.iocoder.yudao.module.common.dal.dataobject.mold.MoldDO; import cn.iocoder.yudao.module.common.dal.mysql.mold.MoldMapper; import cn.iocoder.yudao.module.erp.dal.dataobject.productdevicerel.ProductDeviceRelDO; import cn.iocoder.yudao.module.erp.dal.mysql.productdevicerel.ProductDeviceRelMapper; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO; import cn.iocoder.yudao.module.iot.service.device.DeviceService; +import cn.iocoder.yudao.module.iot.service.device.TDengineService; import cn.iocoder.yudao.module.mes.dal.dataobject.devicetype.DeviceTypeDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.organization.OrganizationDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.plan.PlanDO; import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO; -import cn.iocoder.yudao.module.common.dal.mysql.mold.MoldMapper; import cn.iocoder.yudao.module.mes.dal.mysql.devicetype.DeviceTypeMapper; -import cn.iocoder.yudao.module.common.dal.mysql.mold.MoldMapper; import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductMapper; import cn.iocoder.yudao.module.erp.service.product.ErpProductUnitService; -import cn.iocoder.yudao.module.mes.controller.admin.ticketmanagement.enums.PlanTypeEnum; import cn.iocoder.yudao.module.mes.dal.dataobject.criticalcomponent.CriticalComponentDO; -import cn.iocoder.yudao.module.mes.dal.dataobject.dvrepair.DvRepairDO; import cn.iocoder.yudao.module.mes.dal.dataobject.dvrepair.DvRepairLineDO; -import cn.iocoder.yudao.module.mes.dal.dataobject.ticketresults.TicketResultsDO; import cn.iocoder.yudao.module.mes.dal.mysql.criticalcomponent.CriticalComponentMapper; +import cn.iocoder.yudao.module.mes.dal.mysql.organization.OrganizationMapper; +import cn.iocoder.yudao.module.mes.dal.mysql.plan.PlanMapper; +import cn.iocoder.yudao.module.mes.dal.mysql.producereport.ProduceReportDetailMapper; import cn.iocoder.yudao.module.mes.dal.mysql.dvrepair.DvRepairLineMapper; import cn.iocoder.yudao.module.mes.dal.mysql.dvrepair.DvRepairMapper; import cn.iocoder.yudao.module.mes.dal.mysql.ticketresults.TicketResultsMapper; +import cn.iocoder.yudao.module.mes.dal.mysql.workreportplan.ReportPlanSummaryMapper; +import cn.iocoder.yudao.module.mes.service.organization.OrganizationService; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.extern.slf4j.Slf4j; @@ -48,9 +44,15 @@ import javax.annotation.Resource; import org.springframework.validation.annotation.Validated; import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; +import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceMapper; + import cn.iocoder.yudao.module.mes.controller.admin.deviceledger.vo.*; import cn.iocoder.yudao.module.mes.dal.dataobject.deviceledger.DeviceLedgerDO; @@ -61,7 +63,6 @@ import cn.iocoder.yudao.module.mes.dal.mysql.deviceledger.DeviceLedgerMapper; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** @@ -99,6 +100,27 @@ public class DeviceLedgerServiceImpl implements DeviceLedgerService { @Resource private ProductDeviceRelMapper productDeviceRelMapper; + @Resource + @Lazy + private DeviceMapper deviceMapper; + + @Resource + private PlanMapper planMapper; + + @Resource + private ProduceReportDetailMapper produceReportDetailMapper; + + @Resource + private ReportPlanSummaryMapper reportPlanSummaryMapper; + + @Resource + @Lazy + private OrganizationService organizationService; + + @Resource + @Lazy + private OrganizationMapper organizationMapper; + @Resource private ErpProductUnitService productUnitService; @@ -114,6 +136,9 @@ public class DeviceLedgerServiceImpl implements DeviceLedgerService { @Autowired private AutoCodeUtil autoCodeUtil; + @Resource + private TDengineService tdengineService; + @Override public Long createDeviceLedger(DeviceLedgerSaveReqVO createReqVO) throws UnsupportedEncodingException { DeviceLedgerDO deviceLedger = BeanUtils.toBean(createReqVO, DeviceLedgerDO.class); @@ -347,6 +372,165 @@ public class DeviceLedgerServiceImpl implements DeviceLedgerService { return deviceLedgerDOPageResult; } + @Override + public PageResult getDeviceCapacityReportPage(DeviceCapacityReportPageReqVO pageReqVO) { + DeviceLedgerPageReqVO queryReqVO = buildDeviceCapacityQuery(pageReqVO); + PageResult pageResult = deviceLedgerMapper.selectPage(queryReqVO); + List reportList = buildDeviceCapacityReportList(pageResult.getList()); + return new PageResult<>(reportList, pageResult.getTotal()); + } + + @Override + public List getDeviceCapacityReportList(DeviceCapacityReportPageReqVO pageReqVO) { + DeviceLedgerPageReqVO queryReqVO = buildDeviceCapacityQuery(pageReqVO); + List list = deviceLedgerMapper.selectPage(queryReqVO).getList(); + return buildDeviceCapacityReportList(list); + } + + private DeviceLedgerPageReqVO buildDeviceCapacityQuery(DeviceCapacityReportPageReqVO pageReqVO) { + DeviceLedgerPageReqVO queryReqVO = new DeviceLedgerPageReqVO(); + queryReqVO.setPageNo(pageReqVO.getPageNo()); + queryReqVO.setPageSize(pageReqVO.getPageSize()); + queryReqVO.setDeviceCode(pageReqVO.getDeviceCode()); + queryReqVO.setDeviceName(pageReqVO.getDeviceName()); + queryReqVO.setDeviceType(pageReqVO.getDeviceType()); + queryReqVO.setDeviceStatus(pageReqVO.getDeviceStatus() == null ? null : String.valueOf(pageReqVO.getDeviceStatus())); + queryReqVO.setWorkshop(pageReqVO.getWorkshop()); + queryReqVO.setIds(pageReqVO.getIds()); + queryReqVO.setIsScheduled(1); + return queryReqVO; + } + + private List buildDeviceCapacityReportList(List deviceList) { + if (CollUtil.isEmpty(deviceList)) { + return Collections.emptyList(); + } + + Set deviceIds = deviceList.stream().map(DeviceLedgerDO::getId).filter(Objects::nonNull).collect(Collectors.toSet()); + Set deviceTypeIds = deviceList.stream().map(DeviceLedgerDO::getDeviceType).filter(Objects::nonNull).collect(Collectors.toSet()); + Set workshopIds = deviceList.stream() + .map(DeviceLedgerDO::getWorkshop) + .filter(StringUtils::isNotBlank) + .map(String::trim) + .filter(item -> item.matches("\\d+")) + .map(Long::valueOf) + .collect(Collectors.toSet()); + + Map deviceTypeMap = deviceTypeIds.isEmpty() ? Collections.emptyMap() + : deviceTypeMapper.selectBatchIds(deviceTypeIds).stream() + .collect(Collectors.toMap(DeviceTypeDO::getId, Function.identity(), (a, b) -> a)); + Map organizationMap = workshopIds.isEmpty() ? Collections.emptyMap() + : organizationService.getMapWithDeleted(workshopIds); + + List planList = planMapper.selectList(new LambdaQueryWrapperX() + .in(PlanDO::getDeviceId, deviceIds)); + + Map reportCapacityMap = planList.stream() + .filter(item -> item.getDeviceId() != null) + .collect(Collectors.groupingBy( + PlanDO::getDeviceId, + Collectors.mapping( + item -> BigDecimal.valueOf(Optional.ofNullable(item.getWangongNumber()).orElse(0L)), + Collectors.reducing(BigDecimal.ZERO, BigDecimal::add) + ) + )); + + Map machineDvMap = organizationMapper.selectList( + new LambdaQueryWrapper() + .in(OrganizationDO::getMachineId, deviceIds) + .select(OrganizationDO::getMachineId, OrganizationDO::getDvId)) + .stream() + .filter(item -> item.getMachineId() != null && item.getDvId() != null) + .collect(Collectors.toMap( + OrganizationDO::getMachineId, + OrganizationDO::getDvId, + (a, b) -> a + )); + + LocalDate endDate = LocalDate.now().minusDays(1); + LocalDate startDate = endDate.minusMonths(6); + List statDays = listStatDays(startDate, endDate); + Map actualCapacityMap = new HashMap<>(); + if (CollUtil.isNotEmpty(statDays)) { + LocalDateTime beginTime = statDays.get(0).atStartOfDay(); + LocalDateTime endTime = statDays.get(statDays.size() - 1).plusDays(1).atStartOfDay(); + + Set collectDeviceIds = deviceMapper.selectList(new LambdaQueryWrapper() + .select(DeviceDO::getId)) + .stream() + .map(DeviceDO::getId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + actualCapacityMap = queryDeviceCollectionAverageMap(collectDeviceIds, statDays, beginTime, endTime); + } + + List result = new ArrayList<>(); + for (DeviceLedgerDO device : deviceList) { + DeviceCapacityReportRespVO respVO = new DeviceCapacityReportRespVO(); + respVO.setId(device.getId()); + respVO.setDeviceCode(device.getDeviceCode()); + respVO.setDeviceName(device.getDeviceName()); + respVO.setDeviceStatus(device.getDeviceStatus()); + respVO.setRatedCapacity(BigDecimal.valueOf(Optional.ofNullable(device.getRatedCapacity()).orElse(0))); + + MapUtils.findAndThen(deviceTypeMap, device.getDeviceType(), item -> respVO.setTypeName(item.getName())); + if (StringUtils.isNotBlank(device.getWorkshop()) && device.getWorkshop().trim().matches("\\d+")) { + MapUtils.findAndThen(organizationMap, Long.valueOf(device.getWorkshop().trim()), item -> { + String workshopName = item.getName(); + if (Boolean.TRUE.equals(item.getDeleted())) { + workshopName += "(已被删除)"; + } + respVO.setWorkshopName(workshopName); + }); + } + + respVO.setReportCapacity(reportCapacityMap.getOrDefault(device.getId(), BigDecimal.ZERO)); + + Long collectDeviceId = machineDvMap.get(device.getId()); + respVO.setActualCapacity(BigDecimal.valueOf(actualCapacityMap.getOrDefault(collectDeviceId, 0))); + result.add(respVO); + } + return result; + } + + private Map queryDeviceCollectionAverageMap(Set deviceIds, List statDays, + LocalDateTime beginTime, LocalDateTime endTime) { + Map result = new HashMap<>(); + if (CollUtil.isEmpty(deviceIds) || CollUtil.isEmpty(statDays) || beginTime == null || endTime == null) { + return result; + } + Set validDays = new HashSet<>(statDays); + Map> rawMap = tdengineService.queryDailyLatestCapacityValues(deviceIds, beginTime, endTime); + BigDecimal divisor = BigDecimal.valueOf(statDays.size()); + for (Long deviceId : deviceIds) { + Map dayMap = rawMap.getOrDefault(deviceId, Collections.emptyMap()); + BigDecimal total = BigDecimal.ZERO; + for (Map.Entry entry : dayMap.entrySet()) { + if (entry.getKey() == null || entry.getValue() == null || !validDays.contains(entry.getKey())) { + continue; + } + total = total.add(BigDecimal.valueOf(entry.getValue())); + } + + result.put(deviceId, total.divide(divisor, 0, RoundingMode.HALF_UP).intValue()); + } + return result; + } + + private List listStatDays(LocalDate startDate, LocalDate endDate) { + List days = new ArrayList<>(); + if (startDate == null || endDate == null || startDate.isAfter(endDate)) { + return days; + } + LocalDate cursor = startDate; + while (!cursor.isAfter(endDate)) { + days.add(cursor); + cursor = cursor.plusDays(1); + } + return days; + } + @Override public List getDeviceLedgerList() { return deviceLedgerMapper.selectList();