From c2ec3f0dbf07c434357df3b4a18f81e9ede4d106 Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Thu, 23 Apr 2026 17:39:04 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E4=BC=98=E5=8C=96app=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E6=8A=A5=E8=A1=A8=E6=9F=A5=E8=AF=A2=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/erp/service/mold/MoldService.java | 4 + .../erp/service/mold/MoldServiceImpl.java | 7 + .../admin/device/DeviceController.java | 6 +- .../enums/DeviceRateTrendPeriodEnum.java | 4 + .../DeviceOperationRecordController.java | 7 +- .../vo/DeviceTotalTimeRecordReqVO.java | 3 + .../iot/dal/mysql/device/DeviceMapper.java | 5 +- .../iot/service/device/DeviceService.java | 2 + .../iot/service/device/DeviceServiceImpl.java | 307 +++++++++++++++--- .../iot/service/device/TDengineService.java | 105 ++++++ .../DeviceOperationRecordService.java | 2 +- .../DeviceOperationRecordServiceImpl.java | 192 +++++++++-- .../resources/mapper/device/DeviceMapper.xml | 8 + .../admin/dashboard/DashboardController.java | 137 ++++---- .../deviceledger/DeviceLedgerService.java | 2 + .../deviceledger/DeviceLedgerServiceImpl.java | 7 + 16 files changed, 667 insertions(+), 131 deletions(-) diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/mold/MoldService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/mold/MoldService.java index 77d2d84c2..0e3598627 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/mold/MoldService.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/mold/MoldService.java @@ -102,4 +102,8 @@ public interface MoldService { List getInTransitMoldAllList(); List getMoldListByStatus(Long status); + + List getMoldListByIds(Collection ids); + + } \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/mold/MoldServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/mold/MoldServiceImpl.java index 97a1e4c1e..d8363e364 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/mold/MoldServiceImpl.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/mold/MoldServiceImpl.java @@ -136,4 +136,11 @@ public class MoldServiceImpl implements MoldService { .eqIfPresent(MoldDO::getStatus, status); return moldMapper.selectList(queryWrapper); } + + @Override + public List getMoldListByIds(Collection ids) { + if (ids == null || ids.isEmpty()) { + return Collections.emptyList(); + } + return moldMapper.selectBatchIds(ids); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java index edf0fe2b0..ef0681e90 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java @@ -137,9 +137,9 @@ public class DeviceController { } @GetMapping("/deviceList") // @PreAuthorize("@ss.hasPermission('iot:device:query')") - public CommonResult> deviceList(@Valid DevicePageReqVO pageReqVO) { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = deviceService.getDevicePage(pageReqVO).getList(); + public CommonResult> deviceList(@Valid DevicePageReqVO pageReqVO) { +// List list = deviceService.getDevicePage(pageReqVO).getList(); + List list = deviceService.deviceList(pageReqVO); return success(list); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/DeviceRateTrendPeriodEnum.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/DeviceRateTrendPeriodEnum.java index ece8cfba2..d94145624 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/DeviceRateTrendPeriodEnum.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/enums/DeviceRateTrendPeriodEnum.java @@ -9,6 +9,10 @@ import java.time.temporal.TemporalAdjusters; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_RATE_TREND_PERIOD_INVALID; + +/** + 不含今天日期枚举 + */ @Getter @AllArgsConstructor public enum DeviceRateTrendPeriodEnum { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/DeviceOperationRecordController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/DeviceOperationRecordController.java index cce051eee..f74ee3a5a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/DeviceOperationRecordController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/DeviceOperationRecordController.java @@ -111,10 +111,11 @@ public class DeviceOperationRecordController { } @GetMapping("/deviceRateTrendByDeviceId") - @Operation(summary = "根据设备ID查询某个设备近7日开机率和稼动率") + @Operation(summary = "根据设备ID查询某个设备开机率和稼动率") @PreAuthorize("@ss.hasPermission('iot:device-operation-record:query')") - public CommonResult> getDeviceRateTrendByDeviceId(@RequestParam("deviceId") Long deviceId) { - return success(deviceOperationRecordService.getDeviceRateTrendByDeviceId(deviceId)); + public CommonResult> getDeviceRateTrendByDeviceId(@RequestParam("deviceId") Long deviceId, + @RequestParam(value = "period", required = false) String period) { + return success(deviceOperationRecordService.getDeviceRateTrendByDeviceId(deviceId, period)); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceTotalTimeRecordReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceTotalTimeRecordReqVO.java index 0032afc7d..ab33724cd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceTotalTimeRecordReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/deviceoperationrecord/vo/DeviceTotalTimeRecordReqVO.java @@ -14,6 +14,9 @@ import lombok.ToString; @ToString(callSuper = true) public class DeviceTotalTimeRecordReqVO extends PageParam{ + @Schema(description = "时间区间:LAST_WEEK/THIS_WEEK/LAST_7_DAYS/LAST_MONTH/THIS_MONTH") + private String period; + @Schema(description = "设备编码") private String deviceCode; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/DeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/DeviceMapper.java index 6b6cc8169..d8f8f4295 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/DeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/DeviceMapper.java @@ -140,6 +140,9 @@ public interface DeviceMapper extends BaseMapperX { List selectHolidayDays(@Param("startDay") Date startDay, @Param("endDay") Date endDay); - List getAvailableDevicePage(@Param("page") Page page,@Param("param") DevicePageReqVO pageReqVO); + + List selectHolidayDaysInRange(@Param("startDay") Date startDay, @Param("endDayExclusive") Date endDayExclusive); + + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java index d53c62e34..4d399004d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java @@ -157,4 +157,6 @@ public interface DeviceService { PageResult getAvailableDevicePage(DevicePageReqVO pageReqVO); + + List deviceList(@Valid DevicePageReqVO pageReqVO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java index b435468d8..c950d5f07 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java @@ -15,6 +15,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.scheduler.T import cn.iocoder.yudao.module.iot.controller.admin.device.utils.DataTypeParseUtil; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.*; import cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo.DeviceContactModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.utils.TimeConverterUtil; import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo.DeviceTotalTimeRecordReqVO; import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.vo.DeviceTotalTimeRecordRespVO; import cn.iocoder.yudao.module.iot.controller.admin.mqttdatarecord.vo.MqttDataRecordPageReqVO; @@ -64,10 +65,7 @@ import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import java.sql.Timestamp; import java.text.SimpleDateFormat; -import java.time.Duration; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; +import java.time.*; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.function.Function; @@ -1746,40 +1744,97 @@ public class DeviceServiceImpl implements DeviceService { return new PageResult<>(availableDeviceList, page.getTotal()); } + @Override + public List deviceList(DevicePageReqVO pageReqVO) { + return deviceMapper.selectList(pageReqVO); + } + /** - * 按时间区间查询设备整体开机率、稼动率日趋势。 - * 按天复用 deviceOperationPage 统计单设备数据后求平均。 + * 按时间区间查询设备整体开机率、稼动率日趋势 + * 一次性查询时间范围内按天、按设备的统计数据,避免按天循环重复查库 */ @Override public List getDeviceRateTrend(DeviceRateTrendReqVO reqVO) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND); - // 根据 period 解析查询日期范围,不包含当天 - DeviceRateTrendPeriodEnum.DateRange dateRange = DeviceRateTrendPeriodEnum.valueOfCode(reqVO.getPeriod()).resolve(LocalDate.now()); - LocalDateTime startDateTime = dateRange.getStart().atStartOfDay(); - LocalDateTime endDateTime = dateRange.getEnd().atTime(LocalTime.MAX).withNano(0); - // 默认只统计排产设备,这里先得到最终参与统计的设备集合 + + DeviceRateTrendPeriodEnum.DateRange dateRange = + DeviceRateTrendPeriodEnum.valueOfCode(reqVO.getPeriod()).resolve(LocalDate.now()); + + LocalDate startDate = dateRange.getStart(); + LocalDate endDate = dateRange.getEnd(); + LocalDateTime startDateTime = startDate.atStartOfDay(); + LocalDateTime endDateTime = endDate.atTime(LocalTime.MAX).withNano(0); + String filteredIds = buildFilteredDeviceIds(reqVO); + if (StringUtils.isBlank(filteredIds)) { + return Collections.emptyList(); + } + + List deviceIds = Arrays.stream(filteredIds.split(",")) + .filter(StringUtils::isNotBlank) + .map(Long::valueOf) + .distinct() + .collect(Collectors.toList()); + + + List> statsList = tdengineService.selectDeviceStatsTrendFromTD( + deviceIds, + startDateTime.format(formatter), + endDateTime.format(formatter) + ); + // day -> (deviceId -> record) + Map> dayDeviceRecordMap = convertTrendStatsToMap(statsList); + + // skipHoliday 时,区间一次查出所有节假日,避免按天查库 + Set holidaySet = Collections.emptySet(); + if (Boolean.TRUE.equals(reqVO.getSkipHoliday())) { + List holidays = deviceMapper.selectHolidayDaysInRange( + java.sql.Timestamp.valueOf(startDate.atStartOfDay()), + java.sql.Timestamp.valueOf(endDate.plusDays(1).atStartOfDay()) + ); + holidaySet = holidays == null ? Collections.emptySet() : holidays.stream() + .filter(Objects::nonNull) + .map(this::toLocalDateSafe) + .collect(Collectors.toSet()); + } + + int deviceCount = deviceIds.size(); List result = new ArrayList<>(); - for (LocalDate day = startDateTime.toLocalDate(); !day.isAfter(endDateTime.toLocalDate()); day = day.plusDays(1)) { - // 跳过节假日时,这一天不查询,也不返回 - if (Boolean.TRUE.equals(reqVO.getSkipHoliday()) && isHoliday(day)) { + + for (LocalDate day = startDate; !day.isAfter(endDate); day = day.plusDays(1)) { + if (Boolean.TRUE.equals(reqVO.getSkipHoliday()) && holidaySet.contains(day)) { continue; } - LocalDateTime dayStart = day.atStartOfDay(); - LocalDateTime dayEnd = day.atTime(LocalTime.MAX).withNano(0); - // 复用现有 deviceOperationPage,查出当天每台设备的开机率、稼动率基础数据 - List deviceDayList = queryDeviceDayList(reqVO, filteredIds, dayStart.format(formatter), dayEnd.format(formatter)); + + Map dayMap = + dayDeviceRecordMap.getOrDefault(day, Collections.emptyMap()); double powerOnRateSum = 0D; double utilizationRateSum = 0D; - int deviceCount = deviceDayList.size(); - for (DeviceTotalTimeRecordRespVO record : deviceDayList) { - powerOnRateSum += parsePercentValue(record.getPowerOnRate()); - utilizationRateSum += parsePercentValue(record.getUtilizationRate()); - } + for (Long deviceId : deviceIds) { + DeviceTotalTimeRecordRespVO record = dayMap.get(deviceId); + if (record == null) { + // 无数据按 0 + continue; + } + + // 直接按秒计算,不走字符串百分比解析 + double offlineSec = record.getTotalOfflineTime(); + double runningSec = record.getTotalRunningTime(); + double standbySec = record.getTotalStandbyTime(); + double faultSec = record.getTotalFaultTime(); + double onlineSec = runningSec + standbySec + faultSec; + double totalSec = offlineSec + onlineSec; + + double powerOnRate = totalSec > 0 ? (onlineSec / totalSec) : 0D; + double utilizationRate = onlineSec > 0 ? (runningSec / onlineSec) : 0D; + + powerOnRateSum += powerOnRate * 100D; + utilizationRateSum += utilizationRate * 100D; + } DeviceRateTrendPointRespVO point = new DeviceRateTrendPointRespVO(); point.setDay(day.toString()); @@ -1787,11 +1842,101 @@ public class DeviceServiceImpl implements DeviceService { point.setPowerOnRate(formatPercentValue(deviceCount > 0 ? powerOnRateSum / deviceCount : 0D)); point.setUtilizationRate(formatPercentValue(deviceCount > 0 ? utilizationRateSum / deviceCount : 0D)); result.add(point); + } + + return result; + } + + + private Map> convertTrendStatsToMap(List> statsList) { + Map> result = new LinkedHashMap<>(); + if (statsList == null || statsList.isEmpty()) { + return result; + } + + for (Map row : statsList) { + Object statDayObj = row.get("statDay"); + if (statDayObj == null) { + statDayObj = row.get("statday"); + } + if (statDayObj == null || row.get("deviceId") == null) { + continue; + } + + LocalDate statDay; + if (statDayObj instanceof Timestamp) { + statDay = ((Timestamp) statDayObj).toLocalDateTime().toLocalDate(); + } else if (statDayObj instanceof LocalDateTime) { + statDay = ((LocalDateTime) statDayObj).toLocalDate(); + } else { + statDay = LocalDateTime.parse(statDayObj.toString().replace(" ", "T")).toLocalDate(); + } + + Long deviceId = ((Number) row.get("deviceId")).longValue(); + + DeviceTotalTimeRecordRespVO vo = new DeviceTotalTimeRecordRespVO(); + vo.setId(deviceId); + vo.setTotalOfflineTime(getDoubleValue(row, "totalOfflineTime")); + vo.setTotalRunningTime(getDoubleValue(row, "totalRunningTime")); + vo.setTotalStandbyTime(getDoubleValue(row, "totalStandbyTime")); + vo.setTotalFaultTime(getDoubleValue(row, "totalFaultTime")); + + result.computeIfAbsent(statDay, key -> new HashMap<>()).put(deviceId, vo); + } + + return result; + } + + + /** + * 将按天按设备统计结果转换为 day -> device stats + */ + private Map> convertTrendStats(List> statsList) { + Map> result = new LinkedHashMap<>(); + if (statsList == null || statsList.isEmpty()) { + return result; + } + for (Map row : statsList) { + Object statDayObj = row.get("statDay"); + if (statDayObj == null) { + statDayObj = row.get("statday"); + } + if (statDayObj == null) { + continue; + } + + LocalDate statDay; + if (statDayObj instanceof Timestamp) { + statDay = ((Timestamp) statDayObj).toLocalDateTime().toLocalDate(); + } else if (statDayObj instanceof LocalDateTime) { + statDay = ((LocalDateTime) statDayObj).toLocalDate(); + } else { + statDay = LocalDateTime.parse(statDayObj.toString().replace(" ", "T")).toLocalDate(); + } + + DeviceTotalTimeRecordRespVO vo = new DeviceTotalTimeRecordRespVO(); + vo.setId(((Number) row.get("deviceId")).longValue()); + vo.setTotalOfflineTime(getDoubleValue(row, "totalOfflineTime")); + vo.setTotalRunningTime(getDoubleValue(row, "totalRunningTime")); + vo.setTotalStandbyTime(getDoubleValue(row, "totalStandbyTime")); + vo.setTotalFaultTime(getDoubleValue(row, "totalFaultTime")); + + result.computeIfAbsent(statDay, key -> new ArrayList<>()).add(vo); } + return result; } + + private DeviceTotalTimeRecordReqVO buildTrendReqVO(LocalDate day) { + DeviceTotalTimeRecordReqVO reqVO = new DeviceTotalTimeRecordReqVO(); + reqVO.setStartTime(day.atStartOfDay().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + reqVO.setEndTime(day.atTime(LocalTime.MAX).withNano(0).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + return reqVO; + } + + private double parsePercentValue(String percent) { if (StringUtils.isBlank(percent)) { return 0D; @@ -1810,37 +1955,57 @@ public class DeviceServiceImpl implements DeviceService { return String.format("%.2f%%", value); } + private LocalDate toLocalDateSafe(Date date) { + if (date instanceof java.sql.Date) { + return ((java.sql.Date) date).toLocalDate(); + } + if (date instanceof java.sql.Timestamp) { + return ((java.sql.Timestamp) date).toLocalDateTime().toLocalDate(); + } + return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } - private List queryDeviceDayList(DeviceRateTrendReqVO reqVO, String ids, String startTime, String endTime) { - DeviceTotalTimeRecordReqVO pageReqVO = - new DeviceTotalTimeRecordReqVO(); - pageReqVO.setDeviceCode(reqVO.getDeviceCode()); - pageReqVO.setDeviceName(reqVO.getDeviceName()); - pageReqVO.setIds(ids); - pageReqVO.setStartTime(startTime); - pageReqVO.setEndTime(endTime); - return deviceOperationRecordService.deviceOperationPageList(pageReqVO); - } private String buildFilteredDeviceIds(DeviceRateTrendReqVO reqVO) { boolean onlyScheduled = reqVO.getOnlyScheduled() == null || reqVO.getOnlyScheduled(); + + // 不只看排产设备时: + // 1. 如果前端传了 ids,就按传入 ids 查询 + // 2. 如果前端没传 ids,就查询全部设备 id if (!onlyScheduled) { - return reqVO.getIds(); + if (StringUtils.isNotBlank(reqVO.getIds())) { + return reqVO.getIds(); + } + + List allDeviceIds = deviceMapper.getAllDeviceIds(); + if (CollectionUtils.isEmpty(allDeviceIds)) { + return "-1"; + } + return allDeviceIds.stream() + .map(String::valueOf) + .collect(Collectors.joining(",")); } + // 只统计排产设备 Collection requestIds = parseIds(reqVO.getIds()); List filteredDvIds = deviceMapper.selectScheduledDvIds(requestIds); + // 如果前端传了 ids,则在排产设备中再次过滤 if (StringUtils.isNotBlank(reqVO.getIds())) { Set requestIdSet = new HashSet<>(requestIds); - filteredDvIds = filteredDvIds.stream().filter(requestIdSet::contains).collect(Collectors.toList()); + filteredDvIds = filteredDvIds.stream() + .filter(requestIdSet::contains) + .collect(Collectors.toList()); } if (filteredDvIds.isEmpty()) { return "-1"; } - return filteredDvIds.stream().map(String::valueOf).collect(Collectors.joining(",")); + + return filteredDvIds.stream() + .map(String::valueOf) + .collect(Collectors.joining(",")); } private boolean isHoliday(LocalDate day) { @@ -1848,10 +2013,6 @@ public class DeviceServiceImpl implements DeviceService { return !CollectionUtils.isEmpty(holidays); } - private String toPercent(double value) { - return String.format("%.2f%%", value * 100); - } - private Collection parseIds(String ids) { if (StringUtils.isBlank(ids)) { return Collections.emptyList(); @@ -1865,5 +2026,71 @@ public class DeviceServiceImpl implements DeviceService { + /** + * 根据设备单条运行时长统计,计算开机率和稼动率 + * 计算逻辑与 calculateAndSetConvertedValues 保持一致: + * 开机率 = 在线时间 / 总时间 + * 稼动率 = 运行时间 / 在线时间 + */ + private void calculateRate(DeviceTotalTimeRecordRespVO record) { + if (record == null) { + return; + } + + double offlineSec = record.getTotalOfflineTime(); + double runningSec = record.getTotalRunningTime(); + double standbySec = record.getTotalStandbyTime(); + double faultSec = record.getTotalFaultTime(); + + // 在线时间 = 运行 + 待机 + 故障 + double onlineSec = runningSec + standbySec + faultSec; + double totalSec = offlineSec + onlineSec; + + // 防止异常值 + if (totalSec < 0) { + totalSec = 0; + } + + // 开机率 = 在线时间 / 总时间 + double powerOnRate = 0D; + if (totalSec > 0) { + powerOnRate = onlineSec / totalSec; + } + + // 稼动率 = 运行时间 / 在线时间 + double utilizationRate = 0D; + if (onlineSec > 0) { + utilizationRate = runningSec / onlineSec; + } + + record.setPowerOnRate(TimeConverterUtil.getPercentString(powerOnRate)); + record.setUtilizationRate(TimeConverterUtil.getPercentString(utilizationRate)); + } + + + private Double getDoubleValue(Map row, String key) { + if (row == null || key == null) { + return 0D; + } + + Object value = row.get(key); + if (value == null) { + value = row.get(key.toLowerCase()); + } + if (value == null) { + return 0D; + } + + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + + try { + return Double.parseDouble(value.toString()); + } catch (Exception e) { + return 0D; + } + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java index caa958bc9..5b20c424b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java @@ -2535,4 +2535,109 @@ public class TDengineService { } } + + // ========================= app报表相关接口 =================== + @DS("tdengine") + public List> selectDeviceStatsTrendFromTD(List deviceIds, String startTime, String endTime) { + StringBuilder sql = new StringBuilder(); + List params = new ArrayList<>(); + + sql.append("SELECT ") + .append("_wstart AS statDay, ") + .append("device_id AS deviceId, ") + .append("SUM(CASE WHEN rule = '0' THEN 1 ELSE 0 END) * 60 AS totalOfflineTime, ") + .append("SUM(CASE WHEN rule = '1' THEN 1 ELSE 0 END) * 60 AS totalRunningTime, ") + .append("SUM(CASE WHEN rule = '2' THEN 1 ELSE 0 END) * 60 AS totalStandbyTime, ") + .append("SUM(CASE WHEN rule = '3' THEN 1 ELSE 0 END) * 60 AS totalFaultTime ") + .append("FROM besure_server.iot_device_operation_record ") + .append("WHERE deleted = 0 "); + + if (startTime != null && !startTime.trim().isEmpty()) { + sql.append("AND create_time >= ? "); + params.add(startTime); + } + + if (endTime != null && !endTime.trim().isEmpty()) { + sql.append("AND create_time <= ? "); + params.add(endTime); + } + + // deviceIds 不为空时才加过滤 + if (deviceIds != null && !deviceIds.isEmpty()) { + sql.append("AND device_id IN ("); + for (int i = 0; i < deviceIds.size(); i++) { + if (i > 0) { + sql.append(", "); + } + sql.append("?"); + params.add(deviceIds.get(i)); + } + sql.append(") "); + } + + sql.append("PARTITION BY device_id ") + .append("INTERVAL(1d) ") + .append("ORDER BY statDay ASC"); + + try { + return jdbcTemplate.queryForList(sql.toString(), params.toArray()); + } catch (Exception e) { + log.error("TDengine 查询设备日趋势统计失败,sql={}, params={}", sql, params, e); + return Collections.emptyList(); + } + } + + @DS("tdengine") + public List> queryDeviceDayTrend(Long deviceId, String startTime, String endTime) { + StringBuilder sql = new StringBuilder(); + List params = new ArrayList<>(); + + sql.append("SELECT ") + .append("_wstart AS statDay, ") + .append("device_id AS deviceId, ") + .append("SUM(CASE WHEN rule = '0' THEN 1 ELSE 0 END) * 60 AS totalOfflineTime, ") + .append("SUM(CASE WHEN rule = '1' THEN 1 ELSE 0 END) * 60 AS totalRunningTime, ") + .append("SUM(CASE WHEN rule = '2' THEN 1 ELSE 0 END) * 60 AS totalStandbyTime, ") + .append("SUM(CASE WHEN rule = '3' THEN 1 ELSE 0 END) * 60 AS totalFaultTime ") + .append("FROM besure_server.iot_device_operation_record ") + .append("WHERE deleted = 0 ") + .append("AND device_id = ? "); + params.add(deviceId); + + if (startTime != null && !startTime.trim().isEmpty()) { + sql.append("AND create_time >= ? "); + params.add(startTime); + } + if (endTime != null && !endTime.trim().isEmpty()) { + sql.append("AND create_time <= ? "); + params.add(endTime); + } + + sql.append("PARTITION BY device_id ") + .append("INTERVAL(1d) ") + .append("ORDER BY statDay ASC"); + + try { + List> list = jdbcTemplate.queryForList(sql.toString(), params.toArray()); + for (Map row : list) { + Object statDay = row.get("statDay"); + if (statDay instanceof java.sql.Timestamp) { + row.put("day", ((java.sql.Timestamp) statDay).toLocalDateTime().toLocalDate().toString()); + } else if (statDay != null) { + String text = String.valueOf(statDay); + row.put("day", text.length() >= 10 ? text.substring(0, 10) : text); + } else { + row.put("day", null); + } + } + return list; + } catch (Exception e) { + log.error("TDengine 查询单设备日趋势失败,sql={}, params={}", sql, params, e); + return Collections.emptyList(); + } + } + + + + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordService.java index 92ed6f461..154c9c91b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordService.java @@ -56,7 +56,7 @@ public interface DeviceOperationRecordService { List deviceOperationPageList(@Valid DeviceTotalTimeRecordReqVO pageReqVO); - List getDeviceRateTrendByDeviceId(Long deviceId); + List getDeviceRateTrendByDeviceId(Long deviceId, String period); List deviceOperationList(@Valid DeviceTotalTimeRecordReqVO pageReqVO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordServiceImpl.java index cc6c475b9..25d5ea992 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordServiceImpl.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.iot.service.deviceoperationrecord; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import cn.iocoder.yudao.module.iot.controller.admin.device.enums.DeviceRateTrendPeriodEnum; import cn.iocoder.yudao.module.iot.controller.admin.deviceoperationrecord.utils.TimeConverterUtil; import cn.iocoder.yudao.module.iot.service.device.TDengineService; import com.baomidou.mybatisplus.core.metadata.IPage; @@ -12,11 +14,9 @@ import javax.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.transaction.annotation.Transactional; -import java.time.Duration; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; +import java.time.*; import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAdjusters; import java.util.*; import java.util.stream.Collectors; @@ -92,6 +92,8 @@ public class DeviceOperationRecordServiceImpl implements DeviceOperationRecordSe @Override public PageResult deviceOperationPage(DeviceTotalTimeRecordReqVO pageReqVO) { + applyPeriodIfNecessary(pageReqVO); + // 1. 先从 MySQL 查询设备分页信息 Page page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()); IPage mysqlDevicePage = @@ -106,6 +108,9 @@ public class DeviceOperationRecordServiceImpl implements DeviceOperationRecordSe .map(DeviceTotalTimeRecordRespVO::getId) .collect(Collectors.toList()); + log.info("deviceOperationPage mysql设备ID列表 pageReqVO.startTime={}, pageReqVO.endTime={}, deviceIds={}", + pageReqVO.getStartTime(), pageReqVO.getEndTime(), deviceIds); + // 3. 从 TDengine 批量查询统计数据 List> statsList = deviceOperationRecordMapper.selectDeviceStatsFromTD( @@ -114,6 +119,9 @@ public class DeviceOperationRecordServiceImpl implements DeviceOperationRecordSe pageReqVO.getEndTime() ); + log.info("deviceOperationPage TD原始统计 startTime={}, endTime={}, statsList={}", + pageReqVO.getStartTime(), pageReqVO.getEndTime(), statsList); + // 4. 转换为 Map<设备ID, 统计数据> Map tdStatsMap = convertStatsListToMap(statsList); @@ -122,6 +130,10 @@ public class DeviceOperationRecordServiceImpl implements DeviceOperationRecordSe List result = new ArrayList<>(); for (DeviceTotalTimeRecordRespVO device : mysqlDevicePage.getRecords()) { DeviceTotalTimeRecordRespVO stats = tdStatsMap.get(device.getId()); + + log.info("deviceOperationPage 合并前 deviceId={}, deviceCode={}, deviceName={}, stats={}", + device.getId(), device.getDeviceCode(), device.getDeviceName(), stats); + if (stats != null) { // 设置统计数据 device.setTotalOfflineTime(stats.getTotalOfflineTime()); @@ -137,6 +149,7 @@ public class DeviceOperationRecordServiceImpl implements DeviceOperationRecordSe } result.add(device); } + log.info("deviceOperationPage mysql设备ID列表: {}", deviceIds); // 5. 计算和转换 calculateAndSetConvertedValues(result, pageReqVO); @@ -148,6 +161,8 @@ public class DeviceOperationRecordServiceImpl implements DeviceOperationRecordSe @Override public List deviceOperationPageList(DeviceTotalTimeRecordReqVO pageReqVO) { + applyPeriodIfNecessary(pageReqVO); + List deviceList = deviceOperationRecordMapper.selectDeviceListFromMySQL(pageReqVO); @@ -191,33 +206,153 @@ public class DeviceOperationRecordServiceImpl implements DeviceOperationRecordSe } @Override - public List getDeviceRateTrendByDeviceId(Long deviceId) { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - List result = new ArrayList<>(); + public List getDeviceRateTrendByDeviceId(Long deviceId, String period) { + DeviceRateTrendPeriodEnum.DateRange dateRange = resolvePeriodRange(period); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND); - for (int offset = 6; offset >= 0; offset--) { - LocalDate day = LocalDate.now().minusDays(offset); - LocalDateTime dayStart = day.atStartOfDay(); - LocalDateTime dayEnd = day.atTime(LocalTime.MAX).withNano(0); + String startTime = dateRange.getStart().atStartOfDay().format(formatter); + String endTime = dateRange.getEnd().atTime(LocalTime.MAX).withNano(0).format(formatter); - DeviceTotalTimeRecordReqVO reqVO = new DeviceTotalTimeRecordReqVO(); - reqVO.setIds(String.valueOf(deviceId)); - reqVO.setStartTime(dayStart.format(formatter)); - reqVO.setEndTime(dayEnd.format(formatter)); + log.info("getDeviceRateTrendByDeviceId 开始 deviceId={}, period={}, startTime={}, endTime={}", + deviceId, period, startTime, endTime); + + // queryDeviceDayTrend 改成“按 create_time 的日期分组”, + // 则这里每一天的结果就等价于 deviceOperationPage 单独查当天 + List> dayRows = tDengineService.queryDeviceDayTrend(deviceId, startTime, endTime); + + log.info("getDeviceRateTrendByDeviceId TD原始结果 deviceId={}, dayRows={}", deviceId, dayRows); + + Map> dayMap = new HashMap<>(); + if (CollectionUtils.isNotEmpty(dayRows)) { + for (Map row : dayRows) { + Object dayObj = row.get("day"); + if (dayObj == null) { + continue; + } + try { + dayMap.put(LocalDate.parse(String.valueOf(dayObj)), row); + } catch (Exception e) { + log.warn("解析设备日趋势日期失败,deviceId={}, day={}", deviceId, dayObj); + } + } + } - List dayList = deviceOperationPageList(reqVO); - DeviceTotalTimeRecordRespVO record = CollectionUtils.isNotEmpty(dayList) ? dayList.get(0) : null; + List result = new ArrayList<>(); + for (LocalDate day = dateRange.getStart(); !day.isAfter(dateRange.getEnd()); day = day.plusDays(1)) { + Map row = dayMap.get(day); DeviceOperationRateTrendRespVO trend = new DeviceOperationRateTrendRespVO(); trend.setDay(day.toString()); - trend.setPowerOnRate(record != null ? record.getPowerOnRate() : "0%"); - trend.setUtilizationRate(record != null ? record.getUtilizationRate() : "0%"); + + if (row == null) { + trend.setPowerOnRate("0%"); + trend.setUtilizationRate("0%"); + result.add(trend); + continue; + } + + try { + // 与 calculateAndSetConvertedValues 完全一致 + double offlineSec = toDouble(row, "totalOfflineTime"); + double runningSec = toDouble(row, "totalRunningTime"); + double standbySec = toDouble(row, "totalStandbyTime"); + double faultSec = toDouble(row, "totalFaultTime"); + + double onlineSec = runningSec + standbySec + faultSec; + double totalSec = offlineSec + onlineSec; + + if (totalSec < 0) { + totalSec = 0; + } + + double powerOnRate = 0D; + if (totalSec > 0D) { + powerOnRate = onlineSec / totalSec; + } + + double utilizationRate = 0D; + if (onlineSec > 0D) { + utilizationRate = runningSec / onlineSec; + } + + trend.setPowerOnRate(TimeConverterUtil.getPercentString(powerOnRate)); + trend.setUtilizationRate(TimeConverterUtil.getPercentString(utilizationRate)); + + log.info("getDeviceRateTrendByDeviceId 每日计算 deviceId={}, day={}, row={}, " + + "offlineSec={}, runningSec={}, standbySec={}, faultSec={}, onlineSec={}, totalSec={}, " + + "powerOnRateRaw={}, utilizationRateRaw={}, powerOnRateStr={}, utilizationRateStr={}", + deviceId, + day, + row, + offlineSec, + runningSec, + standbySec, + faultSec, + onlineSec, + totalSec, + powerOnRate, + utilizationRate, + TimeConverterUtil.getPercentString(powerOnRate), + TimeConverterUtil.getPercentString(utilizationRate) + ); + + + } catch (Exception e) { + log.error("计算设备日趋势失败,deviceId={}, day={}", deviceId, day, e); + trend.setPowerOnRate("0%"); + trend.setUtilizationRate("0%"); + } + result.add(trend); + + } return result; } + private static double toDouble(Map row, String key) { + if (row == null || row.get(key) == null) { + return 0D; + } + Object value = row.get(key); + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + try { + return Double.parseDouble(String.valueOf(value)); + } catch (Exception e) { + return 0D; + } + } + + private void applyPeriodIfNecessary(DeviceTotalTimeRecordReqVO reqVO) { + if (StringUtils.isNotBlank(reqVO.getStartTime()) && StringUtils.isNotBlank(reqVO.getEndTime())) { + return; + } + + String period = StringUtils.defaultIfBlank( + reqVO.getPeriod(), + DeviceRateTrendPeriodEnum.LAST_7_DAYS.getCode() + ); + + DeviceRateTrendPeriodEnum.DateRange dateRange = DeviceRateTrendPeriodEnum + .valueOfCode(period) + .resolve(LocalDate.now()); + + LocalDateTime startDateTime = dateRange.getStart().atStartOfDay(); + LocalDateTime endDateTime = dateRange.getEnd().atTime(LocalTime.MAX).withNano(0); + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND); + reqVO.setStartTime(startDateTime.format(formatter)); + reqVO.setEndTime(endDateTime.format(formatter)); + } + + private DeviceRateTrendPeriodEnum.DateRange resolvePeriodRange(String period) { + String actualPeriod = StringUtils.defaultIfBlank(period, DeviceRateTrendPeriodEnum.LAST_7_DAYS.getCode()); + return DeviceRateTrendPeriodEnum.valueOfCode(actualPeriod).resolve(LocalDate.now()); + } + @Override public List deviceOperationList(DeviceTotalTimeRecordReqVO deviceTotalTimeRecordReqVO) { @@ -391,6 +526,25 @@ public class DeviceOperationRecordServiceImpl implements DeviceOperationRecordSe record.setPowerOnRate(TimeConverterUtil.getPercentString(powerOnRate)); record.setUtilizationRate(TimeConverterUtil.getPercentString(utilizationRate)); + log.info("calculateAndSetConvertedValues deviceId={}, deviceCode={}, startTime={}, endTime={}, " + + "offlineSec={}, runningSec={}, standbySec={}, faultSec={}, onlineSec={}, totalSec={}, " + + "powerOnRateRaw={}, utilizationRateRaw={}, powerOnRateStr={}, utilizationRateStr={}", + record.getId(), + record.getDeviceCode(), + startTimeStr, + endTimeStr, + offlineSec, + runningSec, + standbySec, + faultSec, + onlineSec, + totalSec, + powerOnRate, + utilizationRate, + TimeConverterUtil.getPercentString(powerOnRate), + TimeConverterUtil.getPercentString(utilizationRate) + ); + } catch (Exception e) { log.error("计算设备{}时间统计出错: {}", record.getDeviceCode(), e.getMessage()); setDefaultValues(record); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/DeviceMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/DeviceMapper.xml index b1733a8b1..a99775ea3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/DeviceMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/DeviceMapper.xml @@ -299,5 +299,13 @@ ORDER BY d.id DESC + diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dashboard/DashboardController.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dashboard/DashboardController.java index 42ec39534..981fb9350 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dashboard/DashboardController.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dashboard/DashboardController.java @@ -139,39 +139,6 @@ public class DashboardController { item.setValue(taskMapper.selectCount(new LambdaQueryWrapperX() .betweenIfPresent(TaskDO::getCreateTime, taskReqVO.getStartTime()))); // 创建时间 taskItems.add(item); -// // 未开工任务数 -// item = new TaskRespVO.Item(); -// item.setKey("2"); -// item.setLabel("未开工任务数"); -// Collection count1 = new ArrayList<>(); -// count1.add(1L); -// count1.add(2L); -// count1.add(3L); -// item.setValue(taskMapper.selectCount(new LambdaQueryWrapperX() -// .in(TaskDO::getStatus, count1) -// .betweenIfPresent(TaskDO::getOrderDate, taskReqVO.getStartTime()))); // 下达时间 -// taskItems.add(item); -// // 生产中任务数 -// item = new TaskRespVO.Item(); -// item.setKey("3"); -// item.setLabel("生产中任务数"); -// Collection count2 = new ArrayList<>(); -// count2.add(4L); -// item.setValue(taskMapper.selectCount(new LambdaQueryWrapperX() -// .in(TaskDO::getStatus, count2) -// .betweenIfPresent(TaskDO::getOrderDate, taskReqVO.getStartTime()))); -// taskItems.add(item); -// // 完工任务数 -// item = new TaskRespVO.Item(); -// item.setKey("4"); -// item.setLabel("完工任务数"); -// Collection count3 = new ArrayList<>(); -// count3.add(5L); -// count3.add(6L); -// item.setValue(taskMapper.selectCount(new LambdaQueryWrapperX() -// .in(TaskDO::getStatus, count3) -// .betweenIfPresent(TaskDO::getOrderDate, taskReqVO.getStartTime()))); -// taskItems.add(item); // 生产计划总数 @@ -347,66 +314,108 @@ public class DashboardController { @GetMapping("/getTodoList") @Operation(summary = "获得待办任务") @Parameter(name = "id", description = "编号", required = true, example = "1024") -// @PreAuthorize("@ss.hasPermission('mes:bom:query')") +// @PreAuthorize("@ss.hasPermission('mes:bom:query')") public CommonResult> getTodoList() { + // 待办结果集 List todoRespVOList = new ArrayList<>(); - // 设备维修 + + // 1. 查询各类待办数据 List dvRepairDOList = dvRepairService.getDvRepairDOListByStatus(); + List ticketManagementDOList = ticketManagementService.getListByJobStatus(); + List moldRepairDOList = moldRepairService.getMoldRepairDOListByStatus(); + List moldTicketManagementDOList = moldTicketManagementService.getListByJobStatus(); + + // 2. 批量查询设备名称,避免循环中逐条查库 + Map deviceNameMap = new HashMap<>(); + if (dvRepairDOList != null && !dvRepairDOList.isEmpty()) { + List deviceIds = dvRepairDOList.stream() + .map(DvRepairDO::getDeviceId) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + + if (!deviceIds.isEmpty()) { + List deviceLedgerList = deviceLedgerService.getDeviceLedgerListByIds(deviceIds); + deviceNameMap = deviceLedgerList.stream() + .collect(Collectors.toMap( + DeviceLedgerDO::getId, + DeviceLedgerDO::getDeviceName, + (a, b) -> a + )); + } + } + + // 3. 批量查询模具名称,避免循环中逐条查库 + Map moldNameMap = new HashMap<>(); + if (moldRepairDOList != null && !moldRepairDOList.isEmpty()) { + List moldIds = moldRepairDOList.stream() + .map(MoldRepairDO::getMoldId) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + + if (!moldIds.isEmpty()) { + List moldList = moldService.getMoldListByIds(moldIds); + moldNameMap = moldList.stream() + .collect(Collectors.toMap( + MoldDO::getId, + MoldDO::getName, + (a, b) -> a + )); + } + } + + // 4. 组装设备维修待办 for (DvRepairDO dvRepairDO : dvRepairDOList) { - TodoRespVO todoRespVO = new TodoRespVO(); + TodoRespVO todoRespVO = new TodoRespVO(); todoRespVO.setCode(dvRepairDO.getRepairCode()); todoRespVO.setName(dvRepairDO.getRepairName()); todoRespVO.setType("设备维修"); - todoRespVO.setDeviceName(deviceLedgerService.getDeviceLedger(dvRepairDO.getDeviceId()).getDeviceName()); + todoRespVO.setDeviceName(deviceNameMap.getOrDefault(dvRepairDO.getDeviceId(), "未知设备")); todoRespVO.setCreateTime(dvRepairDO.getCreateTime()); todoRespVOList.add(todoRespVO); } - // 设备保养 点检 - List ticketManagementDOList = ticketManagementService.getListByJobStatus(); - for (TicketManagementDO ticketManagementDO : ticketManagementDOList) { - TodoRespVO todoRespVO = new TodoRespVO(); + // 5. 组装设备保养 / 点检待办 + for (TicketManagementDO ticketManagementDO : ticketManagementDOList) { + TodoRespVO todoRespVO = new TodoRespVO(); todoRespVO.setCode(ticketManagementDO.getPlanNo()); todoRespVO.setName(ticketManagementDO.getConfigName()); - if (ticketManagementDO.getPlanType() == 2) { - todoRespVO.setType("设备保养"); - } else { - todoRespVO.setType("设备点检"); - } + todoRespVO.setType(ticketManagementDO.getPlanType() == 2 ? "设备保养" : "设备点检"); todoRespVO.setDeviceName(ticketManagementDO.getDeviceName()); todoRespVO.setCreateTime(ticketManagementDO.getCreateTime()); todoRespVOList.add(todoRespVO); } - // 模具维修 - List moldRepairDOList = moldRepairService.getMoldRepairDOListByStatus(); - for (MoldRepairDO moldRepairDO : moldRepairDOList) { - TodoRespVO todoRespVO = new TodoRespVO(); + + // 6. 组装模具维修待办 + for (MoldRepairDO moldRepairDO : moldRepairDOList) { + TodoRespVO todoRespVO = new TodoRespVO(); todoRespVO.setCode(moldRepairDO.getRepairCode()); todoRespVO.setName(moldRepairDO.getRepairName()); todoRespVO.setType("模具维修"); - todoRespVO.setDeviceName( moldRepairDO.getMoldId() != null && moldService.getMold(moldRepairDO.getMoldId()) != null - ? moldService.getMold(moldRepairDO.getMoldId()).getName() - : "未知设备"); + todoRespVO.setDeviceName(moldNameMap.getOrDefault(moldRepairDO.getMoldId(), "未知设备")); todoRespVO.setCreateTime(moldRepairDO.getCreateTime()); todoRespVOList.add(todoRespVO); - } - // 模具保养 点检 - List moldTicketManagementDOList = moldTicketManagementService.getListByJobStatus(); - for (MoldTicketManagementDO moldTicketManagementDO : moldTicketManagementDOList) { - TodoRespVO todoRespVO = new TodoRespVO(); + + // 7. 组装模具保养 / 点检待办 + for (MoldTicketManagementDO moldTicketManagementDO : moldTicketManagementDOList) { + TodoRespVO todoRespVO = new TodoRespVO(); todoRespVO.setCode(moldTicketManagementDO.getPlanNo()); todoRespVO.setName(moldTicketManagementDO.getConfigName()); - if (moldTicketManagementDO.getPlanType() == 2) { - todoRespVO.setType("模具保养"); - } else { - todoRespVO.setType("模具点检"); - } + todoRespVO.setType(moldTicketManagementDO.getPlanType() == 2 ? "模具保养" : "模具点检"); todoRespVO.setDeviceName(moldTicketManagementDO.getMoldName()); todoRespVO.setCreateTime(moldTicketManagementDO.getCreateTime()); todoRespVOList.add(todoRespVO); } - return success(todoRespVOList); + + // 8. 按创建时间倒序排序,最近的待办排前面 + todoRespVOList.sort(Comparator.comparing( + TodoRespVO::getCreateTime, + Comparator.nullsLast(Comparator.reverseOrder()) + )); + + return success(todoRespVOList); } @GetMapping("/getDeviceRepairLineOptions") 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 4adb90ceb..450c1e185 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 @@ -83,5 +83,7 @@ public interface DeviceLedgerService { Map getDeviceMap(Set longs); + List getDeviceLedgerListByIds(Collection ids); + } \ No newline at end of file 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 9fa05db06..55029500b 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 @@ -431,4 +431,11 @@ public class DeviceLedgerServiceImpl implements DeviceLedgerService { return deviceLedgerMapper.selectListByIds(ids).stream() .collect(Collectors.toMap(DeviceLedgerDO::getId, Function.identity(), (a, b) -> a)); } + + @Override + public List getDeviceLedgerListByIds(Collection ids) { + if (ids == null || ids.isEmpty()) { + return Collections.emptyList(); + } + return deviceLedgerMapper.selectBatchIds(ids); } } \ No newline at end of file