From 5ca9c9112aba3274ae5527197078cc20a59df06e Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Wed, 6 May 2026 15:41:23 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E6=B7=BB=E5=8A=A0=E6=8A=A5=E5=B7=A5?= =?UTF-8?q?=E6=95=B0=E3=80=81=E6=95=B0=E6=8D=AE=E9=87=87=E9=9B=86=E7=9A=84?= =?UTF-8?q?=E4=BA=A7=E9=87=8F=E6=8E=92=E4=BA=A7=E7=9A=84=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/service/device/TDengineService.java | 43 ++++- .../module/mes/enums/ErrorCodeConstants.java | 2 + .../deviceledger/enums/CapacityTypeEnum.java | 6 +- .../admin/task/vo/CapacityContext.java | 30 +++ .../task/vo/TaskOneClickScheduleReqVO.java | 2 +- .../task/vo/TaskOneClickScheduleRespVO.java | 6 +- .../admin/task/vo/TaskPageReqVO.java | 4 +- .../baogongrecord/BaogongRecordMapper.java | 5 + .../mes/service/task/TaskServiceImpl.java | 180 +++++++++++++++++- .../baogongrecord/BaogongRecordMapper.xml | 23 +++ 10 files changed, 287 insertions(+), 14 deletions(-) create mode 100644 yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/CapacityContext.java 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 1461c7f34..d6e991f6e 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 @@ -2599,6 +2599,47 @@ public class TDengineService { } } + @DS("tdengine") + public Map> queryDailyLatestCapacityValues(Collection deviceIds, + LocalDateTime beginTime, + LocalDateTime endTime) { + Map> result = new HashMap<>(); + if (deviceIds == null || deviceIds.isEmpty() || beginTime == null || endTime == null || !endTime.isAfter(beginTime)) { + return result; + } + + try { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT _wstart AS day_key, device_id, LAST(capacity_value) AS capacityvalue ") + .append("FROM besure_server.iot_device_capacity_record ") + .append("WHERE ts >= ? AND ts < ? AND device_id IN (") + .append(deviceIds.stream().map(id -> "?").collect(Collectors.joining(","))) + .append(") PARTITION BY device_id INTERVAL(1d)"); + + List params = new ArrayList<>(); + params.add(Timestamp.valueOf(beginTime)); + params.add(Timestamp.valueOf(endTime)); + params.addAll(deviceIds); + + jdbcTemplate.query(sql.toString(), params.toArray(), rs -> { + Long deviceId = rs.getLong("device_id"); + Timestamp dayTs = rs.getTimestamp("day_key"); + Double capacityValue = rs.getDouble("capacityvalue"); + if (rs.wasNull() || dayTs == null) { + return; + } + LocalDate day = dayTs.toLocalDateTime().toLocalDate(); + result.computeIfAbsent(deviceId, key -> new HashMap<>()) + .put(day, capacityValue); + }); + } catch (Exception e) { + log.error("TDengine查询每日最新容量值失败, deviceIds: {}, beginTime: {}, endTime: {}", + deviceIds, beginTime, endTime, e); + } + return result; + } + + // ========================= app报表相关接口 =================== @DS("tdengine") @@ -2704,4 +2745,4 @@ public class TDengineService { -} \ No newline at end of file +} diff --git a/yudao-module-mes/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java b/yudao-module-mes/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java index d188a0123..07b20e9b0 100644 --- a/yudao-module-mes/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java +++ b/yudao-module-mes/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java @@ -194,6 +194,8 @@ public interface ErrorCodeConstants { ErrorCode SCHEDULE_WORK_HOURS_INVALID = new ErrorCode(100_301_0014, "排产工时非法,start={}, end={},可用工时必须大于0"); ErrorCode WAREHOUSE_NOT_EXISTS= new ErrorCode(100_301_0014, "仓库Id不能为空"); ErrorCode PLAN_RECORD_NOT_EXISTS = new ErrorCode(100_301_0015, "生产计划操作记录不存在"); + ErrorCode SCHEDULE_PRODUCT_DAILY_AVERAGE_ZERO = new ErrorCode(100_301_0016, "产品最近半年平均报工值为0,productId={},productCode={},productName={}"); + ErrorCode SCHEDULE_DEVICE_COLLECTION_CAPACITY_ZERO = new ErrorCode(100_301_0017, "设备最近半年数据采集产能为0,deviceId={},deviceName={}"); diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/enums/CapacityTypeEnum.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/enums/CapacityTypeEnum.java index 66240b285..e79000abd 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/enums/CapacityTypeEnum.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/enums/CapacityTypeEnum.java @@ -8,19 +8,19 @@ import lombok.Getter; @AllArgsConstructor public enum CapacityTypeEnum { - RATED(1, "额定产能") { + RATED(1, "计划产能") { @Override public Integer getCapacity(DeviceLedgerDO device) { return device.getRatedCapacity(); } }, - DAILY_AVERAGE(2, "每日报工平均值") { + DAILY_AVERAGE(2, "报工产能") { @Override public Integer getCapacity(DeviceLedgerDO device) { return device.getDailyAverageValue(); } }, - DATA_COLLECTION(3, "数据采集产能") { + DATA_COLLECTION(3, "实际产能") { @Override public Integer getCapacity(DeviceLedgerDO device) { return device.getDataCollectionCapacity(); diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/CapacityContext.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/CapacityContext.java new file mode 100644 index 000000000..60ed0290c --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/CapacityContext.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.mes.controller.admin.task.vo; + +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.deviceledger.DeviceLedgerDO; +import lombok.Data; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Data +public class CapacityContext { + private List statDays = new ArrayList<>(); + private Integer statDayCount = 0; + private Map productDailyAverageMap = new HashMap<>(); + private Map deviceCollectionAverageMap = new HashMap<>(); + private Map productCache = new HashMap<>(); + private Map deviceCache = new HashMap<>(); + + public Integer getProductDailyAverageCapacity(Long productId) { + return productDailyAverageMap.get(productId); + } + + public Integer getDeviceCollectionAverageCapacity(Long deviceId) { + return deviceCollectionAverageMap.get(deviceId); + } +} diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/TaskOneClickScheduleReqVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/TaskOneClickScheduleReqVO.java index b40e2596b..bdf4f63e2 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/TaskOneClickScheduleReqVO.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/TaskOneClickScheduleReqVO.java @@ -29,7 +29,7 @@ public class TaskOneClickScheduleReqVO { @NotNull(message = "排序规则不能为空") private Integer sortRule; - @Schema(description = "产能来源:1-额定产能 2-每日报工平均值 3-数据采集产能", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "产能来源:1-计划产能 2-报工产能 3-实际产能", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull(message = "产能来源不能为空") private Integer capacityType; diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/TaskOneClickScheduleRespVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/TaskOneClickScheduleRespVO.java index 1a1401692..630509085 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/TaskOneClickScheduleRespVO.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/TaskOneClickScheduleRespVO.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mes.controller.admin.task.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import javax.validation.constraints.NotNull; import java.time.LocalDateTime; import java.util.List; @@ -16,9 +17,12 @@ public class TaskOneClickScheduleRespVO { @Schema(description = "设备名称") private String deviceName; - @Schema(description = "设备额定产能") + @Schema(description = "产能") private Integer ratedCapacity; + @Schema(description = "产能来源:1-计划产能 2-报工产能 3-实际产能") + private Integer capacityType; + @Schema(description = "该设备排产计划") private List plans; diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/TaskPageReqVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/TaskPageReqVO.java index 849be1ac6..2e4abb863 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/TaskPageReqVO.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/vo/TaskPageReqVO.java @@ -22,11 +22,11 @@ public class TaskPageReqVO extends PageParam { private String code; @Schema(description = "下达日期") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY) + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] orderDate; @Schema(description = "交货日期") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY) + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] deliveryDate; @Schema(description = "状态", example = "2") diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/baogongrecord/BaogongRecordMapper.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/baogongrecord/BaogongRecordMapper.java index ce66ce18a..a49d58886 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/baogongrecord/BaogongRecordMapper.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/baogongrecord/BaogongRecordMapper.java @@ -11,6 +11,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.apache.ibatis.annotations.Mapper; import cn.iocoder.yudao.module.mes.controller.admin.baogongrecord.vo.*; import org.apache.ibatis.annotations.Param; +import java.time.LocalDateTime; /** * 报工 Mapper @@ -43,4 +44,8 @@ public interface BaogongRecordMapper extends BaseMapperX { List selectTrendByHour(@Param("reqVO") BaogongRecordTrendReqVO reqVO); + List> selectProductDailyReportTotals(@Param("productIds") Collection productIds, + @Param("beginTime") LocalDateTime beginTime, + @Param("endTime") LocalDateTime endTime); + } diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/task/TaskServiceImpl.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/task/TaskServiceImpl.java index 76c324142..7f7086a45 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/task/TaskServiceImpl.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/task/TaskServiceImpl.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.mes.service.task; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; @@ -15,16 +14,18 @@ import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductMapper; import cn.iocoder.yudao.module.erp.dal.mysql.productdevicerel.ProductDeviceRelMapper; import cn.iocoder.yudao.module.erp.service.product.ErpProductService; import cn.iocoder.yudao.module.erp.service.product.ErpProductUnitService; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO; +import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceMapper; import cn.iocoder.yudao.module.mes.controller.admin.deviceledger.enums.CapacityTypeEnum; import cn.iocoder.yudao.module.mes.controller.admin.plan.vo.PlanSaveReqVO; import cn.iocoder.yudao.module.mes.controller.admin.plan.vo.PlanStatusEnum; import cn.iocoder.yudao.module.mes.controller.admin.task.vo.*; -import cn.iocoder.yudao.module.mes.dal.dataobject.calholiday.CalHolidayDO; import cn.iocoder.yudao.module.mes.dal.dataobject.deviceledger.DeviceLedgerDO; import cn.iocoder.yudao.module.mes.dal.dataobject.plan.PlanDO; import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDO; import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDetailDO; import cn.iocoder.yudao.module.mes.dal.dataobject.task.ViewTaskProductSummary; +import cn.iocoder.yudao.module.mes.dal.mysql.baogongrecord.BaogongRecordMapper; import cn.iocoder.yudao.module.mes.dal.mysql.calholiday.CalHolidayMapper; import cn.iocoder.yudao.module.mes.dal.mysql.deviceledger.DeviceLedgerMapper; import cn.iocoder.yudao.module.mes.dal.mysql.plan.PlanMapper; @@ -33,6 +34,7 @@ import cn.iocoder.yudao.module.mes.dal.mysql.task.TaskMapper; import cn.iocoder.yudao.module.mes.dal.mysql.task.ViewTaskProductSummaryMapper; import cn.iocoder.yudao.module.mes.dal.redis.no.MesNoRedisDAO; import cn.iocoder.yudao.module.mes.enums.BaogongTrendTypeEnum; +import cn.iocoder.yudao.module.iot.service.device.TDengineService; import cn.iocoder.yudao.module.mes.service.deviceledger.DeviceLedgerService; import cn.iocoder.yudao.module.mes.strategy.task.ScheduleSortStrategyFactory; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -84,6 +86,10 @@ public class TaskServiceImpl implements TaskService { @Resource private DeviceLedgerMapper deviceLedgerMapper; + @Resource + @Lazy + private DeviceMapper deviceMapper; + @Resource private ProductDeviceRelMapper productDeviceRelMapper; @@ -93,6 +99,13 @@ public class TaskServiceImpl implements TaskService { @Resource private CalHolidayMapper calHolidayMapper; + @Resource + private BaogongRecordMapper baogongRecordMapper; + + @Resource + private TDengineService tdengineService; + + @Resource private AutoCodeUtil autoCodeUtil; @Resource @@ -450,7 +463,6 @@ public class TaskServiceImpl implements TaskService { } @Override - @Transactional(rollbackFor = Exception.class) public List oneClickSchedule(TaskOneClickScheduleReqVO reqVO) { if (reqVO == null || CollUtil.isEmpty(reqVO.getCreateReqVO())) { return Collections.emptyList(); @@ -515,6 +527,8 @@ public class TaskServiceImpl implements TaskService { throw exception(UNSUPPORTED_CAPACITY_TYPE, reqVO.getCapacityType()); } + CapacityContext capacityContext = buildCapacityContext(sortedPlans, skipHoliday, capacityType); + DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd"); @@ -538,7 +552,7 @@ public class TaskServiceImpl implements TaskService { List candidates = new ArrayList<>(); for (ProductRelationRespVO rel : deviceRels) { DeviceLedgerDO device = deviceLedgerMapper.selectById(rel.getId()); - Integer dailyCapacity = capacityType.getCapacity(device); // 每日产能 + Integer dailyCapacity = resolveCapacityValue(capacityType, device, item.getProductId(), rel.getId(), capacityContext); if (dailyCapacity == null || dailyCapacity <= 0) { continue; } @@ -554,7 +568,8 @@ public class TaskServiceImpl implements TaskService { c.setDeviceName(rel.getName()); c.setCapacityValue(dailyCapacity); c.setNextAvailableTime(nextAvailable); - candidates.add(c); } + candidates.add(c); + } if (CollUtil.isEmpty(candidates)) { throw exception(SCHEDULE_PRODUCT_DEVICE_UNAVAILABLE, item.getProductId()); @@ -592,6 +607,7 @@ public class TaskServiceImpl implements TaskService { vo.setDeviceId(chosen.getDeviceId()); vo.setDeviceName(chosen.getDeviceName()); vo.setRatedCapacity(chosen.getCapacityValue()); + vo.setCapacityType(reqVO.getCapacityType()); vo.setPlans(new ArrayList<>()); return vo; }); @@ -633,7 +649,8 @@ public class TaskServiceImpl implements TaskService { TaskOneClickScheduleRespVO vo = new TaskOneClickScheduleRespVO(); vo.setDeviceId(deviceId); vo.setDeviceName(device == null ? null : device.getDeviceName()); - vo.setRatedCapacity(capacityType.getCapacity(device)); // device为空时注意判空 +// vo.setRatedCapacity(resolveCapacityValue(capacityType, device, null, deviceId, capacityContext)); +// vo.setCapacityType(reqVO.getCapacityType()); vo.setPlans(new ArrayList<>()); return vo; }); @@ -854,6 +871,157 @@ public class TaskServiceImpl implements TaskService { return d; } + private Integer resolveCapacityValue(CapacityTypeEnum capacityType, DeviceLedgerDO device, + Long productId, Long deviceId, CapacityContext capacityContext) { + if (capacityType == CapacityTypeEnum.RATED) { + return device == null ? null : capacityType.getCapacity(device); + } + if (capacityContext == null) { + return null; + } + if (capacityType == CapacityTypeEnum.DAILY_AVERAGE) { + if (productId == null) { + return null; + } + Integer value = capacityContext.getProductDailyAverageCapacity(productId); + if (value == null || value <= 0) { + ErpProductDO product = capacityContext.getProductCache().get(productId); + throw exception(SCHEDULE_PRODUCT_DAILY_AVERAGE_ZERO, productId, + product == null ? null : product.getBarCode(), + product == null ? null : product.getName()); + } + return value; + } + if (capacityType == CapacityTypeEnum.DATA_COLLECTION) { + if (deviceId == null) { + return null; + } + Integer value = capacityContext.getDeviceCollectionAverageCapacity(deviceId); + if (value == null || value <= 0) { + DeviceDO deviceInfo = capacityContext.getDeviceCache().get(deviceId); + throw exception(SCHEDULE_DEVICE_COLLECTION_CAPACITY_ZERO, deviceId, + deviceInfo == null ? null : deviceInfo.getDeviceName()); + } + return value; + } + return device == null ? null : capacityType.getCapacity(device); + } + + private CapacityContext buildCapacityContext(List sortedPlans, boolean skipHoliday, + CapacityTypeEnum capacityType) { + CapacityContext context = new CapacityContext(); + if (CollUtil.isEmpty(sortedPlans)) { + return context; + } + + LocalDate endDate = LocalDate.now(); + LocalDate startDate = endDate.minusMonths(6).plusDays(1); + List statDays = listStatDays(startDate, endDate, skipHoliday); + context.setStatDays(statDays); + context.setStatDayCount(statDays.size()); + if (CollUtil.isEmpty(statDays)) { + return context; + } + + LocalDateTime beginTime = statDays.get(0).atStartOfDay(); + LocalDateTime endTime = statDays.get(statDays.size() - 1).plusDays(1).atStartOfDay(); + + if (capacityType == CapacityTypeEnum.DAILY_AVERAGE) { + Set productIds = sortedPlans.stream().map(PlanSaveReqVO::getProductId) + .filter(Objects::nonNull).collect(Collectors.toSet()); + if (CollUtil.isNotEmpty(productIds)) { + context.setProductCache(productIds.stream().collect(Collectors.toMap( + id -> id, + erpProductMapper::selectById, + (a, b) -> a + ))); + } + context.setProductDailyAverageMap(queryProductDailyAverageMap(productIds, statDays, beginTime, endTime)); + } else if (capacityType == CapacityTypeEnum.DATA_COLLECTION) { + + Set deviceIds = deviceMapper.selectList(new LambdaQueryWrapper() + .select(DeviceDO::getId)) + .stream() + .map(DeviceDO::getId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + + if (CollUtil.isNotEmpty(deviceIds)) { + context.setDeviceCache(deviceMapper.selectBatchIds(deviceIds).stream() + .filter(Objects::nonNull) + .collect(Collectors.toMap( + DeviceDO::getId, + device -> device, + (a, b) -> a + ))); + } + context.setDeviceCollectionAverageMap(queryDeviceCollectionAverageMap(deviceIds, statDays, beginTime, endTime)); + } + return context; + } + + private Map queryProductDailyAverageMap(Set productIds, List statDays, + LocalDateTime beginTime, LocalDateTime endTime) { + Map result = new HashMap<>(); + if (CollUtil.isEmpty(productIds) || CollUtil.isEmpty(statDays)) { + return result; + } + Set validDays = new HashSet<>(statDays); + List> rows = baogongRecordMapper.selectProductDailyReportTotals(productIds, beginTime, endTime); + Map productTotalMap = new HashMap<>(); + for (Map row : rows) { + Long productId = row.get("productId") == null ? null : Long.valueOf(String.valueOf(row.get("productId"))); + LocalDate day = row.get("day") == null ? null : LocalDate.parse(String.valueOf(row.get("day"))); + if (productId == null || day == null || !validDays.contains(day)) { + continue; + } + BigDecimal totalNum = new BigDecimal(String.valueOf(row.get("totalNum"))); + productTotalMap.merge(productId, totalNum, BigDecimal::add); + } + BigDecimal divisor = BigDecimal.valueOf(statDays.size()); + for (Long productId : productIds) { + BigDecimal total = productTotalMap.getOrDefault(productId, BigDecimal.ZERO); + result.put(productId, total.divide(divisor, 0, RoundingMode.HALF_UP).intValue()); + } + return result; + } + + private Map queryDeviceCollectionAverageMap(Set deviceIds, List statDays, + LocalDateTime beginTime, LocalDateTime endTime) { + Map result = new HashMap<>(); + if (CollUtil.isEmpty(deviceIds) || CollUtil.isEmpty(statDays)) { + 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, boolean skipHoliday) { + List days = new ArrayList<>(); + LocalDate cursor = startDate; + while (!cursor.isAfter(endDate)) { + if (!skipHoliday || isWorkingDay(cursor)) { + days.add(cursor); + } + cursor = cursor.plusDays(1); + } + return days; + } + private Set queryDeviceIdsFromCurrentMonth() { LocalDateTime monthStart = LocalDate.now().withDayOfMonth(1).atStartOfDay(); List plans = planMapper.selectList(new LambdaQueryWrapper() diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/resources/mapper/baogongrecord/BaogongRecordMapper.xml b/yudao-module-mes/yudao-module-mes-biz/src/main/resources/mapper/baogongrecord/BaogongRecordMapper.xml index 26f0b1a83..91d4f1692 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/resources/mapper/baogongrecord/BaogongRecordMapper.xml +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/resources/mapper/baogongrecord/BaogongRecordMapper.xml @@ -195,4 +195,27 @@ ORDER BY h.day ASC + +