Преглед изворни кода

Merge remote-tracking branch 'origin/master'

Zimo пре 1 недеља
родитељ
комит
f41ab36ac9

+ 11 - 3
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java

@@ -8,6 +8,8 @@ import java.math.RoundingMode;
 import java.sql.Timestamp;
 import java.time.*;
 import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.ChronoField;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -339,12 +341,18 @@ public class DateUtils {
     }
 
         public static boolean checkIfFullDayDifference(String timeStr) {
-            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+            DateTimeFormatter formatter = new DateTimeFormatterBuilder()
+                    .appendPattern("yyyy-MM-dd HH:mm:ss")
+                    .optionalStart()
+                    .appendLiteral('.')
+                    .appendFraction(ChronoField.MILLI_OF_SECOND, 1, 3, false)
+                    .optionalEnd()
+                    .toFormatter();
             LocalDateTime givenTime = LocalDateTime.parse(timeStr, formatter);
             LocalDateTime now = LocalDateTime.now();
 
-            long hoursDifference = ChronoUnit.MINUTES.between(givenTime, now);
-            return Math.abs(hoursDifference) >= 5;
+            long minutesDifference = ChronoUnit.MINUTES.between(givenTime, now);
+            return Math.abs(minutesDifference) >= 5;
         }
 
         public static LocalDateTime contactTime(LocalDateTime targetTime ) {

+ 9 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprodinventoryaging/IotProdInventoryAgingController.java

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotInventoryAgingStatisticsRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingSaveReqVO;
@@ -77,6 +78,14 @@ public class IotProdInventoryAgingController {
         return success(BeanUtils.toBean(pageResult, IotProdInventoryAgingRespVO.class));
     }
 
+    @GetMapping("/inventoryStatus")
+    @Operation(summary = "获得库存库龄存货情况")
+    @PreAuthorize("@ss.hasPermission('pms:iot-prod-inventory-aging:query')")
+    public CommonResult<IotInventoryAgingStatisticsRespVO> inventoryStatus(@Valid IotProdInventoryAgingPageReqVO pageReqVO) {
+        IotInventoryAgingStatisticsRespVO stockAgeStatistics = iotProdInventoryAgingService.inventoryStatus(pageReqVO);
+        return success(stockAgeStatistics);
+    }
+
     @GetMapping("/export-excel")
     @Operation(summary = "导出库存库龄表DWD Excel")
     @PreAuthorize("@ss.hasPermission('pms:iot-prod-inventory-aging:export')")

+ 33 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprodinventoryaging/vo/IotInventoryAgingStatisticsRespVO.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+@Schema(description = "管理后台 - 库存库龄 总库存 积压库存 VO")
+@Data
+@ExcelIgnoreUnannotated
+public class IotInventoryAgingStatisticsRespVO {
+
+    @Schema(description = "总库存")
+    private BigDecimal totalInventory;
+
+    @Schema(description = "总积压存")
+    private BigDecimal totalOverStock;
+
+    @Schema(description = "以工厂维度统计的总积压库存数据")
+    private Map<String, BigDecimal> factoryNameOverStockPair;
+
+    @Schema(description = "以工厂维度统计的总库存数据")
+    private Map<String, BigDecimal> factoryNameInventoryPair;
+
+    @Schema(description = "以工厂-库存地点为维度统计的总积压库存数据")
+    private Map<String, BigDecimal> storageNameOverStockPair;
+
+    @Schema(description = "以工厂-库存地点为维度统计的总库存数据")
+    private Map<String, BigDecimal> storageNameInventoryPair;
+
+}

+ 20 - 36
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/TDDeviceMapper.java

@@ -6,7 +6,6 @@ import cn.iocoder.yudao.module.pms.controller.admin.vo.DeviceTdVO;
 import cn.iocoder.yudao.module.pms.controller.admin.vo.DeviceVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.TDDeviceDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotZHBD.TDLogDO;
-import cn.iocoder.yudao.module.pms.job.DeviceLastTimeVO;
 import com.baomidou.dynamic.datasource.annotation.DS;
 import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
 import org.apache.ibatis.annotations.Insert;
@@ -37,16 +36,16 @@ public interface TDDeviceMapper extends BaseMapperX<TDDeviceDO> {
     List<DeviceVO> selectNew(@Param("deviceName") String tableName);
 
 
-//    @Select("SELECT LAST(ts) as ts FROM iot_log.device_${deviceName} where identity not in ('lng','lat','today_distance','distance','todayoil','totaloil','online','oil1','oil2','oil3'," +
+    //    @Select("SELECT LAST(ts) as ts FROM iot_log.device_${deviceName} where identity not in ('lng','lat','today_distance','distance','todayoil','totaloil','online','oil1','oil2','oil3'," +
 //            "'oil4','vehicle_name') ")
     @Select("SELECT ts FROM iot_log.device_${deviceName} " +
-        "where identity not in ('lng','lat','today_distance','distance','todayoil','totaloil','online','oil1','oil2','oil3','oil4','vehicle_name') " +
-        "ORDER BY ts DESC LIMIT 1")
+            "where identity not in ('lng','lat','today_distance','distance','todayoil','totaloil','online','oil1','oil2','oil3','oil4','vehicle_name') " +
+            "ORDER BY ts DESC LIMIT 1")
     @DS("tdengine")
     @TenantIgnore
     List<DeviceVO> selectLastTime(@Param("deviceName") String tableName);
 
-//    @Select("SELECT identity, LAST(log_value) as logValue FROM iot_log.device_${deviceName} where identity in ${params} group by identity")
+    //    @Select("SELECT identity, LAST(log_value) as logValue FROM iot_log.device_${deviceName} where identity in ${params} group by identity")
     @Select({
             "<script>",
             "SELECT",
@@ -167,15 +166,15 @@ public interface TDDeviceMapper extends BaseMapperX<TDDeviceDO> {
                     "        TAGS (${data.serialNumber}) VALUES ",
             "<foreach collection='data.list' item='device' separator=','>",
             "(now,"+
-            " #{device.logValue},"+
-            "#{device.isMonitor},"+
-            "#{device.logType},"+
-            "#{device.identity},"+
-            "#{device.mode},"+
-            "now,"+
-            "#{device.remark},"+
-            "#{device.batchId})"+
-            "</foreach>",
+                    " #{device.logValue},"+
+                    "#{device.isMonitor},"+
+                    "#{device.logType},"+
+                    "#{device.identity},"+
+                    "#{device.mode},"+
+                    "now,"+
+                    "#{device.remark},"+
+                    "#{device.batchId})"+
+                    "</foreach>",
             "</script>"
     })
     int batchInsert(@Param("data") TDLogDO data);
@@ -186,29 +185,14 @@ public interface TDDeviceMapper extends BaseMapperX<TDDeviceDO> {
     @TenantIgnore
     @InterceptorIgnore(tenantLine = "true")
     Integer selectRangeCount(@Param("deviceName") String tableName, @Param("identifier") String identifier,@Param("start") Timestamp start,@Param("end") Timestamp end,
-                                    @Param("max") String max,@Param("min") String min);
-
-    /**
-     * 批量获取 iot_log 库下所有 device_ 开头的表名
-     */
-    @Select("SELECT table_name FROM information_schema.ins_tables WHERE db_name = 'iot_log' AND table_name LIKE 'device_%'")
-    @DS("tdengine")
-    @TenantIgnore
-    List<String> selectAllDeviceTables();
+                             @Param("max") String max,@Param("min") String min);
 
-    /**
-     * 超级表批量查询所有子表的最新 ts(排除指定 identity)
-     * 注意:将 iot_log.你的超级表名 替换为实际超级表名
-     */
-
-    @Select("<script>" +
-            "SELECT tbname, LAST(ts) AS ts FROM iot_log.device_log " +
-            "WHERE tbname IN " +
-            "<foreach item='item' collection='tables' open='(' separator=',' close=')'>#{item}</foreach> " +
-            "AND identity NOT IN ('lng','lat','today_distance','distance','todayoil','totaloil','online','oil1','oil2','oil3','oil4','vehicle_name') " +
-            "GROUP BY tbname" +
-            "</script>")
+    @Select("SELECT table_name, create_time " +
+            "FROM information_schema.ins_tables " +
+            "WHERE db_name = 'iot_log' " +
+            "ORDER BY table_name;")
     @DS("tdengine")
     @TenantIgnore
-    List<DeviceLastTimeVO> selectLastTimeBatch(@Param("tables") List<String> tables);
+    @InterceptorIgnore(tenantLine = "true")
+    List<String> getAllTableNames();
 }

+ 35 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotprodinventoryaging/IotProdInventoryAgingMapper.java

@@ -9,6 +9,8 @@ import cn.iocoder.yudao.module.pms.dal.dataobject.iotprodinventoryaging.IotProdI
 import com.baomidou.dynamic.datasource.annotation.DS;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 /**
  * 库存库龄表DWD Mapper
  *
@@ -50,4 +52,37 @@ public interface IotProdInventoryAgingMapper extends BaseMapperX<IotProdInventor
                 .eqIfPresent(IotProdInventoryAgingDO::getAmtn, reqVO.getAmtn()));
     }
 
+    @DS("fanruan")
+    @TenantIgnore
+    default List<IotProdInventoryAgingDO> InventoryAges(IotProdInventoryAgingPageReqVO reqVO) {
+        return selectList(new LambdaQueryWrapperX<IotProdInventoryAgingDO>()
+                .eqIfPresent(IotProdInventoryAgingDO::getFyear, reqVO.getFyear())
+                .eqIfPresent(IotProdInventoryAgingDO::getFmonth, reqVO.getFmonth())
+                .eqIfPresent(IotProdInventoryAgingDO::getFdate, reqVO.getFdate())
+                .eqIfPresent(IotProdInventoryAgingDO::getWerks, reqVO.getWerks())
+                .eqIfPresent(IotProdInventoryAgingDO::getDept, reqVO.getDept())
+                .eqIfPresent(IotProdInventoryAgingDO::getMatnr, reqVO.getMatnr())
+                .eqIfPresent(IotProdInventoryAgingDO::getLgort, reqVO.getLgort())
+                .eqIfPresent(IotProdInventoryAgingDO::getBklas, reqVO.getBklas())
+                .eqIfPresent(IotProdInventoryAgingDO::getMatkl, reqVO.getMatkl())
+                .eqIfPresent(IotProdInventoryAgingDO::getMtart, reqVO.getMtart())
+                .eqIfPresent(IotProdInventoryAgingDO::getMaktx, reqVO.getMaktx())
+                .eqIfPresent(IotProdInventoryAgingDO::getVbeln, reqVO.getVbeln())
+                .eqIfPresent(IotProdInventoryAgingDO::getPosnr, reqVO.getPosnr())
+                .eqIfPresent(IotProdInventoryAgingDO::getBezei, reqVO.getBezei())
+                .eqIfPresent(IotProdInventoryAgingDO::getTotal, reqVO.getTotal())
+                .eqIfPresent(IotProdInventoryAgingDO::getStprs, reqVO.getStprs())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmttotal, reqVO.getAmttotal())
+                .eqIfPresent(IotProdInventoryAgingDO::getPeinh, reqVO.getPeinh())
+                .eqIfPresent(IotProdInventoryAgingDO::getQty1, reqVO.getQty1())
+                .eqIfPresent(IotProdInventoryAgingDO::getQty2, reqVO.getQty2())
+                .eqIfPresent(IotProdInventoryAgingDO::getQty3, reqVO.getQty3())
+                .eqIfPresent(IotProdInventoryAgingDO::getQty4, reqVO.getQty4())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmt1, reqVO.getAmt1())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmt2, reqVO.getAmt2())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmt3, reqVO.getAmt3())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmt4, reqVO.getAmt4())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmtn, reqVO.getAmtn()));
+    }
+
 }

+ 103 - 150
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/job/TdCronJob.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.module.pms.job;
 
-import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
 import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
@@ -10,6 +9,9 @@ import cn.iocoder.yudao.module.pms.dal.mysql.IotDeviceMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.TDDeviceMapper;
 import cn.iocoder.yudao.module.pms.service.yanfan.YfDeviceService;
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -17,11 +19,7 @@ import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Component;
 
 import javax.annotation.Resource;
-import java.sql.Timestamp;
-import java.text.SimpleDateFormat;
 import java.util.*;
-import java.util.function.Function;
-import java.util.stream.Collectors;
 
 @Component
 @Slf4j
@@ -32,114 +30,68 @@ public class TdCronJob implements JobHandler {
     private IotDeviceMapper iotDeviceMapper;
     @Autowired
     private TDDeviceMapper deviceMapper;
-    @Autowired
-    private StringRedisTemplate redisTemplate;
+    @Resource
+    private StringRedisTemplate stringRedisTemplate;
 
     @Override
     @TenantIgnore
     public String execute(String param) throws Exception {
+        List<String> codes = new ArrayList<>();
+        ObjectMapper objectMapper = new ObjectMapper();
         List<YfDeviceDO> allDevice = yfDeviceService.getAllDevice();
-
-        // ① serialNumber(小写) -> YfDeviceDO,替代循环内 stream filter
-        Map<String, YfDeviceDO> yfDeviceMap = allDevice.stream()
-                .collect(Collectors.toMap(
-                        d -> d.getSerialNumber().toLowerCase(),
-                        Function.identity(),
-                        (a, b) -> a   // 重复时保留前者
-                ));
-
-        List<String> codes = new ArrayList<>(yfDeviceMap.keySet());
+        allDevice.forEach(d -> {
+            codes.add(d.getSerialNumber());
+        });
         List<IotDeviceDO> devices = iotDeviceMapper.selectByCodeIn(codes);
 
-        // ② 预取所有存在的 device 表名(一次查询,放入 Set)
-        Set<String> existTables = deviceMapper.selectAllDeviceTables().stream()
-                .map(String::toLowerCase)
-                .collect(Collectors.toSet());
-
-        // ③ 筛选出表存在的设备,组装子表名列表
-        List<String> targetTableNames = allDevice.stream()
-                .map(d -> "device_" + d.getSerialNumber().toLowerCase())
-                .filter(existTables::contains)
-                .collect(Collectors.toList());
-
-        // ④ 超级表一次查询全部设备的最新 ts → Map<serialNumber小写, Timestamp>
-        Map<String, Timestamp> lastTimeMap = new HashMap<>();
-        if (CollUtil.isNotEmpty(targetTableNames)) {
-            List<DeviceLastTimeVO> lastTimeList = deviceMapper.selectLastTimeBatch(targetTableNames);
-            for (DeviceLastTimeVO vo : lastTimeList) {
-                // tbname 格式为 device_xxx,去掉前缀得到 serialNumber
-                String serialNumber = vo.getTbname().toLowerCase().replace("device_", "");
-                lastTimeMap.put(serialNumber, vo.getTs());
-            }
-        }
-
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         List<IotDeviceDO> deviceDOS = new ArrayList<>();
-
-        // ⑤ 循环内纯内存操作,零数据库查询
+        List<String> allTableNames = deviceMapper.getAllTableNames();
         for (IotDeviceDO device : devices) {
             String deviceCode = device.getDeviceCode();
-            YfDeviceDO e = yfDeviceMap.get(deviceCode.toLowerCase());
-            if (e == null) {
-                continue;
+            if ("YF6660388".equals(deviceCode)) {
+                System.out.println("************************");
             }
+            allDevice.stream().filter(e -> e.getSerialNumber().equals(deviceCode)).findFirst().ifPresent(e -> {
+                device.setYfDeviceId(e.getDeviceId());
+//                Integer i = deviceMapper.tableIfExist(e.getSerialNumber().toLowerCase());
+                if (allTableNames.contains("device_"+e.getSerialNumber().toLowerCase())) {//如果存在表
+                    Map<Object, Object> hashAll = stringRedisTemplate.opsForHash().entries("TSLV:"+e.getProductId()+"_"+e.getSerialNumber());
+
+                    Map<String, String> result = new HashMap<>();
+                    String timestamp = "";
+                    for (Map.Entry<Object, Object> entry : hashAll.entrySet()) {
+                        String paramId = entry.getKey().toString();      // "1003"
+                        String jsonStr = entry.getValue().toString();     // "{\"id\":\"1003\",\"ts\":\"...\"}"
+
+                        try {
+                            JsonNode node = objectMapper.readTree(jsonStr);
+                            if (node.isTextual()) {
+                                node = objectMapper.readTree(node.asText()); // 处理双重转义
+                            }
+
+                            if (node.has("ts")) {
+                                String ts = node.get("ts").asText();
+                                timestamp = ts;
+                                break;
+                            }
+                        } catch (Exception f) {
+                            f.printStackTrace();
+                        }
+                    }
+
+
+                    if (StringUtils.isNotBlank(timestamp)) {
+                        device.setLastInlineTime(timestamp);
+                    }
 
-            device.setYfDeviceId(e.getDeviceId());
-
-            // 从 Map 取最新时间,替代循环内 selectLastTime
-            Timestamp ts = lastTimeMap.get(e.getSerialNumber().toLowerCase());
-            if (ts != null) {
-                device.setLastInlineTime(sdf.format(ts));
-            }
-
-            if (e.getStatus() == 1) { // 未激活状态
-                if (StringUtils.isNotBlank(device.getLastInlineTime())) {
-                    device.setIfInline(DateUtils.checkIfFullDayDifference(device.getLastInlineTime()) ? 4 : 3);
-                } else {
-                    device.setIfInline(4);
-                }
-
-                // Redis 一次 hgetAll 替代三次 hget
-                Map<Object, Object> tslv = redisTemplate.opsForHash().entries("TSLV:" + device.getDeviceCode());
-                Object lat = tslv.get("lat");
-                Object lng = tslv.get("lng");
-                Object online = tslv.get("online");
-
-                if (Objects.nonNull(lat)) {
-                    device.setLat(Double.valueOf((String) JSON.parseObject(lat.toString()).get("value")));
-                }
-                if (Objects.nonNull(lng)) {
-                    device.setLng(Double.valueOf((String) JSON.parseObject(lng.toString()).get("value")));
-                }
-                if (Objects.nonNull(online)) {
-                    String value = String.valueOf(JSON.parseObject(online.toString()).get("value"));
-                    device.setIfInline("true".equals(value) ? 3 : 4);
-                }
-            } else {
-                if (StringUtils.isNotBlank(device.getLastInlineTime())) {
-                    device.setIfInline(DateUtils.checkIfFullDayDifference(device.getLastInlineTime()) ? 4 : e.getStatus());
-                } else {
-                    device.setIfInline(4);
+//                    List<DeviceVO> deviceVOS = deviceMapper.selectLastTime(e.getSerialNumber());
+//                    if (CollUtil.isNotEmpty(deviceVOS)) {
+//                        Timestamp ts = deviceVOS.get(0).getTs();
+//                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+//                        String format = sdf.format(ts);
+//                        device.setLastInlineTime(format);
+//                    }
                 }
-            }
-
-            deviceDOS.add(device);
-        }
-
-        iotDeviceMapper.updateBatch(deviceDOS);
-        return "";
-//        List<String> codes = new ArrayList<>();
-//        List<YfDeviceDO> allDevice = yfDeviceService.getAllDevice();
-//        allDevice.forEach(d -> {
-//            codes.add(d.getSerialNumber());
-//        });
-//        List<IotDeviceDO> devices = iotDeviceMapper.selectByCodeIn(codes);
-//        List<IotDeviceDO> deviceDOS = new ArrayList<>();
-//        for (IotDeviceDO device : devices) {
-//            String deviceCode = device.getDeviceCode();
-//            allDevice.stream().filter(e -> e.getSerialNumber().equals(deviceCode)).findFirst().ifPresent(e -> {
-//                device.setYfDeviceId(e.getDeviceId());
-//                Integer i = deviceMapper.tableIfExist(e.getSerialNumber().toLowerCase());
 //                if (i==1) {
 //                    List<DeviceVO> deviceVOS = deviceMapper.selectLastTime(e.getSerialNumber());
 //                    if (CollUtil.isNotEmpty(deviceVOS)) {
@@ -149,60 +101,61 @@ public class TdCronJob implements JobHandler {
 //                        device.setLastInlineTime(format);
 //                    }
 //                }
-//                if (e.getStatus()==1) {//未激活状态
-//
-//                    if (StringUtils.isNotBlank(device.getLastInlineTime())) {
-//                        try {
-//                            if (DateUtils.checkIfFullDayDifference(device.getLastInlineTime())) {
-//                                device.setIfInline(4);
-//                            } else {
-//                                device.setIfInline(3);
-//                            }
-//                        } catch (Exception ex) {
-//                            throw new RuntimeException(ex.getMessage());
-//                        }
-//                    } else {
-//                        device.setIfInline(4);
-//                    }
-//
-//                    Object lat = redisTemplate.opsForHash().get("TSLV:" + device.getDeviceCode(), "lat");
-//                    if (Objects.nonNull(lat)) {
-//                        JSONObject jsonObject = JSON.parseObject(lat.toString());
-//                        device.setLat(Double.valueOf((String) jsonObject.get("value")));
-//                    }
-//                    Object lng = redisTemplate.opsForHash().get("TSLV:" + device.getDeviceCode(), "lng");
-//                    if (Objects.nonNull(lng)) {
-//                        JSONObject jsonObject = JSON.parseObject(lng.toString());
-//                        device.setLng(Double.valueOf((String) jsonObject.get("value")));
-//                    }
-//                    Object online = redisTemplate.opsForHash().get("TSLV:" + device.getDeviceCode(), "online");
-//                    if (Objects.nonNull(online)) {
-//                        JSONObject jsonObject = JSON.parseObject(online.toString());
-//                        String value = String.valueOf(jsonObject.get("value"));
-//                        device.setIfInline("true".equals(value)?3:4);
-//                    }
-//                } else {
-//                        if (StringUtils.isNotBlank(device.getLastInlineTime())) {
-//                            if (DateUtils.checkIfFullDayDifference(device.getLastInlineTime())) {
-//                                device.setIfInline(4);
-//                            } else {
-//                                device.setIfInline(e.getStatus());
-//                            }
-//                        } else {
-//                            device.setIfInline(4);
-//                        }
-//                    }
-////                iotDeviceMapper.updateTdCron(device);
-//                deviceDOS.add(device);
-//            });
-//        }
-//        iotDeviceMapper.updateBatch(deviceDOS);
-//        return "";
+                if (e.getStatus()==1) {//未激活状态
+
+                    if (StringUtils.isNotBlank(device.getLastInlineTime())) {
+                        try {
+                            if (DateUtils.checkIfFullDayDifference(device.getLastInlineTime())) {
+                                device.setIfInline(4);
+                            } else {
+                                device.setIfInline(3);
+                            }
+                        } catch (Exception ex) {
+                            throw new RuntimeException(ex.getMessage());
+                        }
+                    } else {
+                        device.setIfInline(4);
+                    }
+
+                    Object lat = stringRedisTemplate.opsForHash().get("TSLV:" + device.getDeviceCode(), "lat");
+                    if (Objects.nonNull(lat)) {
+                        JSONObject jsonObject = JSON.parseObject(lat.toString());
+                        device.setLat(Double.valueOf((String) jsonObject.get("value")));
+                    }
+                    Object lng = stringRedisTemplate.opsForHash().get("TSLV:" + device.getDeviceCode(), "lng");
+                    if (Objects.nonNull(lng)) {
+                        JSONObject jsonObject = JSON.parseObject(lng.toString());
+                        device.setLng(Double.valueOf((String) jsonObject.get("value")));
+                    }
+                    Object online = stringRedisTemplate.opsForHash().get("TSLV:" + device.getDeviceCode(), "online");
+                    if (Objects.nonNull(online)) {
+                        JSONObject jsonObject = JSON.parseObject(online.toString());
+                        String value = String.valueOf(jsonObject.get("value"));
+                        device.setIfInline("true".equals(value)?3:4);
+                    }
+                } else {
+                    if (StringUtils.isNotBlank(device.getLastInlineTime())) {
+                        if (DateUtils.checkIfFullDayDifference(device.getLastInlineTime())) {
+                            device.setIfInline(4);
+                        } else {
+                            device.setIfInline(e.getStatus());
+                        }
+                    } else {
+                        device.setIfInline(4);
+                    }
+                }
+//                iotDeviceMapper.updateTdCron(device);
+                deviceDOS.add(device);
+            });
+        }
+        iotDeviceMapper.updateBatch(deviceDOS);
+        return "";
     }
 
 
     public static void main(String[] args) {
-        String abc = "2026-01-21 10:25:40";
+        System.out.println(new Date().toString());
+        String abc = "2026-09-03 18:48:38.42";
         System.out.println(DateUtils.checkIfFullDayDifference(abc));
     }
 }

+ 8 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotprodinventoryaging/IotProdInventoryAgingService.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.pms.service.iotprodinventoryaging;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotInventoryAgingStatisticsRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingSaveReqVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprodinventoryaging.IotProdInventoryAgingDO;
@@ -52,4 +53,11 @@ public interface IotProdInventoryAgingService {
      */
     PageResult<IotProdInventoryAgingDO> getIotProdInventoryAgingPage(IotProdInventoryAgingPageReqVO pageReqVO);
 
+    /**
+     * 获得各公司的存货情况
+     *
+     * @param pageReqVO 分页查询
+     * @return 库存库龄表DWD分页
+     */
+    IotInventoryAgingStatisticsRespVO inventoryStatus(IotProdInventoryAgingPageReqVO pageReqVO);
 }

+ 196 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotprodinventoryaging/IotProdInventoryAgingServiceImpl.java

@@ -1,16 +1,33 @@
 package cn.iocoder.yudao.module.pms.service.iotprodinventoryaging;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotInventoryAgingStatisticsRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingSaveReqVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprodinventoryaging.IotProdInventoryAgingDO;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotprodinventoryaging.IotProdInventoryAgingMapper;
+import cn.iocoder.yudao.module.system.api.saporg.SapOrgApi;
+import cn.iocoder.yudao.module.system.api.saporg.dto.SapOrgRespDTO;
+import cn.iocoder.yudao.module.system.controller.admin.saporg.vo.SapOrgSimpleReqVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.saporg.SapOrgDO;
+import cn.iocoder.yudao.module.system.service.dept.DeptService;
+import cn.iocoder.yudao.module.system.service.saporg.SapOrgService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
 
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_PROD_INVENTORY_AGING_NOT_EXISTS;
 
@@ -25,6 +42,12 @@ public class IotProdInventoryAgingServiceImpl implements IotProdInventoryAgingSe
 
     @Resource
     private IotProdInventoryAgingMapper iotProdInventoryAgingMapper;
+    @Autowired
+    private SapOrgApi sapOrgApi;
+    @Autowired
+    private DeptService deptService;
+    @Autowired
+    private SapOrgService sapOrgService;
 
     @Override
     public Long createIotProdInventoryAging(IotProdInventoryAgingSaveReqVO createReqVO) {
@@ -68,4 +91,177 @@ public class IotProdInventoryAgingServiceImpl implements IotProdInventoryAgingSe
         return iotProdInventoryAgingMapper.selectPage(pageReqVO);
     }
 
+    @Override
+    public IotInventoryAgingStatisticsRespVO inventoryStatus(IotProdInventoryAgingPageReqVO pageReqVO) {
+        // 库龄数据量大 必须选择日期
+        if (StrUtil.isBlank(pageReqVO.getFdate())) {
+            throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
+        }
+        String deptIdStr = pageReqVO.getDept();
+        if (StrUtil.isBlank(deptIdStr)) {
+            throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
+        }
+        IotInventoryAgingStatisticsRespVO result = new IotInventoryAgingStatisticsRespVO();
+        // 查询当前公司对应的 库存地点 标识 名称 按照各库存地点统计 存货及积压金额
+        Long deptId = Long.valueOf(deptIdStr);
+        DeptDO dept = deptService.getDept(deptId);
+        Set<Long> factoryIds = dept.getFactoryIds();
+        List<SapOrgRespDTO> sapOrganizations = sapOrgApi.getSapOrgList(factoryIds);
+        if (CollUtil.isEmpty(sapOrganizations)) {
+            throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
+        }
+        // key工厂编码   value工厂名称
+        Map<String, String> factoryPair = new HashMap<>();
+        sapOrganizations.forEach(org -> {
+            factoryPair.put(org.getFactoryCode(), org.getFactoryName());
+        });
+        // 需要查询库龄的工厂
+        List<String> factoryCodes = sapOrganizations.stream()
+                .filter(dto -> "age".equals(dto.getCreator()))
+                .map(SapOrgRespDTO::getFactoryCode)
+                .collect(Collectors.toList());
+        // 查询工厂下所有库存地点信息
+        // key工厂编辑-库存地点编码   value库存地点名称
+        Map<String, String> storageLocationPair = new HashMap<>();
+        SapOrgSimpleReqVO reqVO = new SapOrgSimpleReqVO();
+        reqVO.setType(3);
+        reqVO.setFactoryCodes(factoryCodes);
+        List<SapOrgDO> storageLocations = sapOrgService.getSelectedList(reqVO);
+        if (CollUtil.isNotEmpty(storageLocations)) {
+            storageLocations.forEach(location -> {
+                String factoryCode = location.getFactoryCode();
+                String storageLocationCode = location.getStorageLocationCode();
+                String uniqueKey = StrUtil.join("-", factoryCode, storageLocationCode);
+                storageLocationPair.put(uniqueKey, location.getStorageLocationName());
+            });
+        }
+        // 查询帆软数仓中的库龄数据
+        List<IotProdInventoryAgingDO> stockAges = iotProdInventoryAgingMapper.InventoryAges(pageReqVO);
+        if (CollUtil.isEmpty(stockAges)) {
+            throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
+        }
+        // 总库存
+        BigDecimal amtSummary = stockAges.stream()
+                .map(IotProdInventoryAgingDO::getAmttotal)
+                // null值转为0
+                .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        // 1年以内的库存总额
+        BigDecimal amtSummaryOneYear = stockAges.stream()
+                .map(IotProdInventoryAgingDO::getAmt1)
+                // null值转为0
+                .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        // 按工厂‑库存地点分组
+        Map<String, List<IotProdInventoryAgingDO>> groupMap = stockAges.stream()
+                .collect(Collectors.groupingBy(age -> StrUtil.join("-", age.getWerks(), age.getLgort())));
+        // 按工厂‑库存地点分组
+        Map<String, List<IotProdInventoryAgingDO>> facotryGroupMap = stockAges.stream()
+                .collect(Collectors.groupingBy(age -> age.getWerks()));
+
+        // 积压库存 = 总库存 - 1年以内的库存总额
+        BigDecimal overstockInventory = amtSummary.subtract(amtSummaryOneYear);
+
+        // 各工厂的总库存 key工厂编码   value
+        Map<String, BigDecimal> factoryInventoryPair = new HashMap<>();
+        // 各工厂的积压库存 key工厂编码
+        Map<String, BigDecimal> factoryOverStockPair = new HashMap<>();
+        // 各工厂的总库存 key工厂编码   value
+        Map<String, BigDecimal> factoryNameInventoryPair = new HashMap<>();
+        // 各工厂的积压库存 key工厂编码
+        Map<String, BigDecimal> factoryNameOverStockPair = new HashMap<>();
+
+        // 循环分组统计总库存、积压库存 按照库存地点统计
+        facotryGroupMap.forEach((factoryCode, ages) -> {
+            BigDecimal sumTotal = ages.stream()
+                    .map(IotProdInventoryAgingDO::getAmttotal)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+            BigDecimal sumOneYear = ages.stream()
+                    .map(IotProdInventoryAgingDO::getAmt1)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+            BigDecimal sumOverStock = sumTotal.subtract(sumOneYear);
+
+            factoryInventoryPair.put(factoryCode, sumTotal);
+            factoryOverStockPair.put(factoryCode, sumOverStock);
+        });
+
+        // 生成 storageNameInventoryPair
+        factoryNameInventoryPair = factoryInventoryPair.entrySet().stream()
+                .collect(Collectors.toMap(
+                        e -> factoryPair.getOrDefault(e.getKey(), "未知"),
+                        Map.Entry::getValue,
+                        (v1, v2) -> v1   // 若键冲突(例如多个“未知”),保留第一个值
+                ));
+
+        // 生成 storageNameOverStockPair
+        factoryNameOverStockPair = factoryOverStockPair.entrySet().stream()
+                .collect(Collectors.toMap(
+                        e -> factoryPair.getOrDefault(e.getKey(), "未知"),
+                        Map.Entry::getValue,
+                        (v1, v2) -> v1
+                ));
+
+        // 各库存地点的总库存 key工厂编码-库存地点编码   value
+        Map<String, BigDecimal> storageInventoryPair = new HashMap<>();
+        // 各库存地点的积压库存 key工厂编码-库存地点编码
+        Map<String, BigDecimal> storageOverStockPair = new HashMap<>();
+        // 循环分组统计总库存、积压库存 按照库存地点统计
+        groupMap.forEach((uniqueKey, list) -> {
+            BigDecimal sumTotal = list.stream()
+                    .map(IotProdInventoryAgingDO::getAmttotal)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+            BigDecimal sumOneYear = list.stream()
+                    .map(IotProdInventoryAgingDO::getAmt1)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+            BigDecimal sumOverStock = sumTotal.subtract(sumOneYear);
+
+            storageInventoryPair.put(uniqueKey, sumTotal);
+            storageOverStockPair.put(uniqueKey, sumOverStock);
+        });
+        // 各库存地点的总库存 key工厂编码-库存地点名称   value
+        Map<String, BigDecimal> storageNameInventoryPair = new HashMap<>();
+        // 各库存地点的积压库存 key工厂编辑-库存地点编码
+        Map<String, BigDecimal> storageNameOverStockPair = new HashMap<>();
+        Function<String, String> keyMapper = uniqueKey -> {
+            String locationName = storageLocationPair.get(uniqueKey);
+            if (StrUtil.isBlank(locationName)) {
+                return "未知";
+            }
+            String factoryCode = StrUtil.subBefore(uniqueKey, "-", false);
+            return StrUtil.join("-", factoryCode, locationName);
+        };
+        // 生成 storageNameInventoryPair
+        storageNameInventoryPair = storageInventoryPair.entrySet().stream()
+                .collect(Collectors.toMap(
+                        e -> keyMapper.apply(e.getKey()),
+                        Map.Entry::getValue,
+                        (v1, v2) -> v1   // 若键冲突(例如多个“未知”),保留第一个值
+                ));
+
+        // 生成 storageNameOverStockPair
+        storageNameOverStockPair = storageOverStockPair.entrySet().stream()
+                .collect(Collectors.toMap(
+                        e -> keyMapper.apply(e.getKey()),
+                        Map.Entry::getValue,
+                        (v1, v2) -> v1
+                ));
+        result.setTotalInventory(amtSummary);
+        result.setTotalOverStock(overstockInventory);
+
+        result.setFactoryNameInventoryPair(factoryNameInventoryPair);
+        result.setFactoryNameOverStockPair(factoryNameOverStockPair);
+
+        result.setStorageNameInventoryPair(storageInventoryPair);
+        result.setStorageNameOverStockPair(storageOverStockPair);
+        return result;
+    }
+
 }

+ 61 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/util/DeptUtils.java

@@ -0,0 +1,61 @@
+package cn.iocoder.yudao.module.pms.util;
+
+import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
+import cn.iocoder.yudao.module.system.service.dept.DeptService;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Set;
+
+/**
+ * 部门工具类
+ */
+@Service
+public class DeptUtils {
+
+    @Resource
+    private DeptService deptService;
+
+    /**
+     *  检测是否已经维护了生产时效
+     * @param deptId
+     * @return
+     */
+    public DeptDO loginUserCompany(Long deptId){
+        Long companyId = null;
+        DeptDO company = null;
+        // 查询各专业公司下的所有 部门id集合
+        // 如果集合包含 当前登录人的 deptId 则确定当前登录人所属的专业公司
+        Set<Long> rhChildDeptIds = deptService.getChildDeptIdListFromCache(157l);
+        rhChildDeptIds.add(157l);
+        Set<Long> ryChildDeptIds = deptService.getChildDeptIdListFromCache(158l);
+        ryChildDeptIds.add(158l);
+        Set<Long> rdChildDeptIds = deptService.getChildDeptIdListFromCache(163l);
+        rdChildDeptIds.add(163l);
+        Set<Long> fiveChildDeptIds = deptService.getChildDeptIdListFromCache(388l);
+        fiveChildDeptIds.add(388l);
+        if (rhChildDeptIds.contains(deptId)) {
+            // 当前登录所属公司为 瑞恒
+            companyId = 157l;
+        }
+        if (ryChildDeptIds.contains(deptId)) {
+            // 当前登录所属公司为 瑞鹰
+            companyId = 158l;
+        }
+        if (rdChildDeptIds.contains(deptId)) {
+            // 当前登录所属公司为 瑞都
+            companyId = 163l;
+        }
+        if (fiveChildDeptIds.contains(deptId)) {
+            // 当前登录所属公司为 5#公司
+            companyId = 388l;
+        }
+        if (ObjUtil.isNotEmpty(companyId)) {
+            company = deptService.getDept(companyId);
+        }
+        return company;
+    }
+
+
+}