自动化立体仓库 - WMS系统
zwl
7 小时以前 b2da7d9b244c5fb2de88803512f9350b21047721
数字孪生
18个文件已添加
1个文件已修改
2036 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/controller/DigitalTwinController.java 967 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/LocCount.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/digitaltwin/AllLocationsVo.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/digitaltwin/DtDetainMatVo.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/digitaltwin/DtEquipmentDocVo.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/digitaltwin/DtEquipmentVo.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/digitaltwin/DtInAndOutBoundVo.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/digitaltwin/DtLocDetailVo.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/digitaltwin/DtLocVo.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/digitaltwin/DtOrderVo.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/digitaltwin/DtOverviewVo.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/digitaltwin/LocPicDto.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/DigitalTwinMapper.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/LocCountMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/DigitalTwinService.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/DigitalTwinServiceImpl.java 482 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/LocCountMapper.xml 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/ViewDigitalTwinMapper.xml 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/DigitalTwinController.java
New file
@@ -0,0 +1,967 @@
package com.zy.asrs.controller;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.core.common.R;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zy.asrs.entity.LocChartPie;
import com.zy.asrs.entity.LocDetl;
import com.zy.asrs.entity.LocMast;
import com.zy.asrs.entity.digitaltwin.*;
import com.zy.asrs.mapper.LocDetlMapper;
import com.zy.asrs.mapper.ReportQueryMapper;
import com.zy.asrs.service.DigitalTwinService;
import com.zy.common.utils.HttpHandler;
import com.zy.common.web.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.core.type.TypeReference;
import javax.annotation.Resource;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RequestMapping("/digitalTwin")
@RestController
public class DigitalTwinController extends BaseController {
    @Value("${digitalTwins.jgUrl}")
    private String JG_URL;
    @Value("${digitalTwins.djUrl}")
    private String DJ_URL;
    @Value("${digitalTwins.ljqUrl}")
    private String LJQ_URL;
    @Resource
    private DigitalTwinService digitalTwinService;
    @Autowired
    ReportQueryMapper reportQueryMapper;
    @Autowired
    private LocDetlMapper locDetlMapper;
    /**
     * 数据总览
     *
     * @param areaId    库区编码
     * @return
     */
    @RequestMapping(value = "/overview")
//    @ManagerAuth
    public R overview(@RequestParam(required = false) String areaId) throws IOException {
        Map<String, Object> map = new HashMap<>();
        if (areaId != null) {
            map.put("areaId", areaId);
        }
        HttpHandler.Builder builder = new HttpHandler.Builder()
                .setPath("/digitalTwin/overview")
                .setParams(map);
        ObjectMapper objectMapper = new ObjectMapper();
        if (areaId != null) {
            switch (areaId) {
                case "A": {
                    String resA = builder.setUri(JG_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    // 如果只需要data部分
                    Map<String, Object> data = (Map<String, Object>) result.get("data");
                    return R.ok(data);
                }
                case "B": {
                    String resB = builder.setUri(DJ_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resB, Map.class);
                    // 如果只需要data部分
                    Map<String, Object> data = (Map<String, Object>) result.get("data");
                    return R.ok(data);
                }
                case "C": {
                    String resC = builder.setUri(LJQ_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resC, Map.class);
                    // 如果只需要data部分
                    Map<String, Object> data = (Map<String, Object>) result.get("data");
                    return R.ok(data);
                }
                case "D": {
                    return R.ok(digitalTwinService.overview(areaId));
                }
                default:
                    Map<String, Object> mergedResult = new HashMap<>();
                    Map<String, Object> mergedData = new HashMap<>();
                    String resA = builder.setUri(JG_URL).build().doGet();
                    Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
                    if (resultA.get("data") != null) {
                        mergedData.put("A", resultA.get("data"));
                    }
                    String resB = builder.setUri(DJ_URL).build().doGet();
                    Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
                    if (resultB.get("data") != null) {
                        mergedData.put("B", resultB.get("data"));
                    }
                    String resC = builder.setUri(LJQ_URL).build().doGet();
                    Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
                    if (resultC.get("data") != null) {
                        mergedData.put("C", resultC.get("data"));
                    }
                    Object resultD = digitalTwinService.overview(areaId);
                    if (resultD != null) {
                        mergedData.put("D", resultD);
                    }
                    return R.ok(mergedData);
            }
        } else {
            Map<String, Object> mergedResult = new HashMap<>();
            Map<String, Object> mergedData = new HashMap<>();
            String resA = builder.setUri(JG_URL).build().doGet();
            Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
            if (resultA.get("data") != null) {
                mergedData.put("A", resultA.get("data"));
            }
            String resB = builder.setUri(DJ_URL).build().doGet();
            Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
            if (resultB.get("data") != null) {
                mergedData.put("B", resultB.get("data"));
            }
            String resC = builder.setUri(LJQ_URL).build().doGet();
            Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
            if (resultC.get("data") != null) {
                mergedData.put("C", resultC.get("data"));
            }
            Object resultD = digitalTwinService.overview(areaId);
            if (resultD != null) {
                mergedData.put("D", resultD);
            }
            return R.ok(mergedData);
        }
    }
    /**
     * 近期订单(默认7天)
     *
     * @param areaId    库区编码
     * @param startDate 格式:yyyyMMdd,20251022
     * @param endDate   格式:yyyyMMdd,20251027
     * @return
     */
    @RequestMapping(value = "/recentOrder")
//    @ManagerAuth
    public R recentOrder(@RequestParam(required = false) String areaId,
                         @RequestParam(required = false) String startDate,
                         @RequestParam(required = false) String endDate) throws IOException {
        Map<String, Object> map = new HashMap<>();
        if (areaId != null) {
            map.put("areaId", areaId);
        }
        if (startDate != null) {
            map.put("startDate", startDate);
        }
        if (endDate != null) {
            map.put("endDate", endDate);
        }
        HttpHandler.Builder builder = new HttpHandler.Builder()
                .setPath("/digitalTwin/recentOrder")
                .setParams(map);
        ObjectMapper objectMapper = new ObjectMapper();
        if (areaId != null) {
            switch (areaId) {
                case "A": {
                    String resA = builder.setUri(JG_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    // 如果只需要data部分
                    List data = (List) result.get("data");
                    return R.ok(data);
                }
                case "B": {
                    String resB = builder.setUri(DJ_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resB, Map.class);
                    // 如果只需要data部分
                    List data = (List) result.get("data");
                    return R.ok(data);
                }
                case "C": {
                    String resC = builder.setUri(LJQ_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resC, Map.class);
                    // 如果只需要data部分
                    List data = (List) result.get("data");
                    return R.ok(data);
                }
                case "D": {
                    return R.ok(digitalTwinService.order(startDate, endDate));
                }
                default:
                    Map<String, Object> mergedResult = new HashMap<>();
                    Map<String, Object> mergedData = new HashMap<>();
                    String resA = builder.setUri(JG_URL).build().doGet();
                    Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
                    if (resultA.get("data") != null) {
                        mergedData.put("A", resultA.get("data"));
                    }
                    String resB = builder.setUri(DJ_URL).build().doGet();
                    Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
                    if (resultB.get("data") != null) {
                        mergedData.put("B", resultB.get("data"));
                    }
                    String resC = builder.setUri(LJQ_URL).build().doGet();
                    Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
                    if (resultC.get("data") != null) {
                        mergedData.put("C", resultC.get("data"));
                    }
                    Object resultD = digitalTwinService.order(startDate, endDate);
                    if (resultD != null) {
                        mergedData.put("D", resultD);
                    }
                    return R.ok(mergedData);
            }
        } else {
            Map<String, Object> mergedResult = new HashMap<>();
            Map<String, Object> mergedData = new HashMap<>();
            String resA = builder.setUri(JG_URL).build().doGet();
            Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
            if (resultA.get("data") != null) {
                mergedData.put("A", resultA.get("data"));
            }
            String resB = builder.setUri(DJ_URL).build().doGet();
            Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
            if (resultB.get("data") != null) {
                mergedData.put("B", resultB.get("data"));
            }
            String resC = builder.setUri(LJQ_URL).build().doGet();
            Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
            if (resultC.get("data") != null) {
                mergedData.put("C", resultC.get("data"));
            }
            Object resultD = digitalTwinService.order(startDate, endDate);
            if (resultD != null) {
                mergedData.put("D", resultD);
            }
            return R.ok(mergedData);
        }
    }
    /**
     * 近期剩余库位(默认7天)
     *
     * @param areaId    库区编码
     * @param startDate 格式:yyyyMMdd,20251022
     * @param endDate   格式:yyyyMMdd,20251027
     * @return
     */
    @RequestMapping(value = "/recentIdleLoc")
//    @ManagerAuth
    public R recentIdleLoc(@RequestParam(required = false) String areaId,
                           @RequestParam(required = false) String startDate,
                           @RequestParam(required = false) String endDate) throws IOException {
        Map<String, Object> map = new HashMap<>();
        if (areaId != null) {
            map.put("areaId", areaId);
        }
        if (startDate != null) {
            map.put("startDate", startDate);
        }
        if (endDate != null) {
            map.put("endDate", endDate);
        }
        HttpHandler.Builder builder = new HttpHandler.Builder()
                .setPath("/digitalTwin/recentIdleLoc")
                .setParams(map);
        ObjectMapper objectMapper = new ObjectMapper();
        if (areaId != null) {
            switch (areaId) {
                case "A": {
                    String resA = builder.setUri(JG_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    // 如果只需要data部分
                    List data = (List) result.get("data");
                    return R.ok(data);
                }
                case "B": {
                    String resA = builder.setUri(DJ_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    // 如果只需要data部分
                    List data = (List) result.get("data");
                    return R.ok(data);
                }
                case "C": {
                    String resA = builder.setUri(LJQ_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    // 如果只需要data部分
                    List data = (List) result.get("data");
                    return R.ok(data);
                }
                case "D": {
                    return R.ok(digitalTwinService.recentLoc(areaId, startDate, endDate));
                }
                default:
                    Map<String, Object> mergedResult = new HashMap<>();
                    Map<String, Object> mergedData = new HashMap<>();
                    String resA = builder.setUri(JG_URL).build().doGet();
                    Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
                    if (resultA.get("data") != null) {
                        mergedData.put("A", resultA.get("data"));
                    }
                    String resB = builder.setUri(DJ_URL).build().doGet();
                    Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
                    if (resultB.get("data") != null) {
                        mergedData.put("B", resultB.get("data"));
                    }
                    String resC = builder.setUri(LJQ_URL).build().doGet();
                    Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
                    if (resultC.get("data") != null) {
                        mergedData.put("C", resultC.get("data"));
                    }
                    Object resultD = digitalTwinService.recentLoc(areaId, startDate, endDate);
                    if (resultD != null) {
                        mergedData.put("D", resultD);
                    }
                    return R.ok(mergedData);
            }
        } else {
            Map<String, Object> mergedResult = new HashMap<>();
            Map<String, Object> mergedData = new HashMap<>();
            String resA = builder.setUri(JG_URL).build().doGet();
            Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
            if (resultA.get("data") != null) {
                mergedData.put("A", resultA.get("data"));
            }
            String resB = builder.setUri(DJ_URL).build().doGet();
            Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
            if (resultB.get("data") != null) {
                mergedData.put("B", resultB.get("data"));
            }
            String resC = builder.setUri(LJQ_URL).build().doGet();
            Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
            if (resultC.get("data") != null) {
                mergedData.put("C", resultC.get("data"));
            }
            Object resultD = digitalTwinService.recentLoc(areaId, startDate, endDate);
            if (resultD != null) {
                mergedData.put("D", resultD);
            }
            return R.ok(mergedData);
        }
    }
    /**
     * 近期出入库(默认7天)
     *
     * @param areaId    库区编码
     * @param startDate 格式:yyyyMMdd,20251022
     * @param endDate   格式:yyyyMMdd,20251027
     * @return
     */
    @RequestMapping(value = "/recentInAndOutBound")
//    @ManagerAuth
    public R recentInAndOutBound(@RequestParam(required = false) String areaId,
                                 @RequestParam(required = false) String startDate,
                                 @RequestParam(required = false) String endDate) throws ParseException, IOException {
        Map<String, Object> map = new HashMap<>();
        if (areaId != null) {
            map.put("areaId", areaId);
        }
        if (startDate != null) {
            map.put("startDate", startDate);
        }
        if (endDate != null) {
            map.put("endDate", endDate);
        }
        HttpHandler.Builder builder = new HttpHandler.Builder()
                .setPath("/digitalTwin/recentInAndOutBound")
                .setParams(map);
        ObjectMapper objectMapper = new ObjectMapper();
        if (areaId != null) {
            switch (areaId) {
                case "A": {
                    String resA = builder.setUri(JG_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    // 如果只需要data部分
                    List data = (List) result.get("data");
                    return R.ok(data);
                }
                case "B": {
                    String resA = builder.setUri(DJ_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    // 如果只需要data部分
                    List data = (List) result.get("data");
                    return R.ok(data);
                }
                case "C": {
                    String resA = builder.setUri(LJQ_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    // 如果只需要data部分
                    List data = (List) result.get("data");
                    return R.ok(data);
                }
                case "D": {
                    return R.ok(digitalTwinService.inAndOutBound(areaId, startDate, endDate));
                }
                default:
                    Map<String, Object> mergedResult = new HashMap<>();
                    Map<String, Object> mergedData = new HashMap<>();
                    String resA = builder.setUri(JG_URL).build().doGet();
                    Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
                    if (resultA.get("data") != null) {
                        mergedData.put("A", resultA.get("data"));
                    }
                    String resB = builder.setUri(DJ_URL).build().doGet();
                    Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
                    if (resultB.get("data") != null) {
                        mergedData.put("B", resultB.get("data"));
                    }
                    String resC = builder.setUri(LJQ_URL).build().doGet();
                    Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
                    if (resultC.get("data") != null) {
                        mergedData.put("C", resultC.get("data"));
                    }
                    Object resultD = digitalTwinService.inAndOutBound(areaId, startDate, endDate);
                    if (resultD != null) {
                        mergedData.put("D", resultD);
                    }
                    return R.ok(mergedData);
            }
        } else {
            // 如果没有提供areaId,默认调用本地服务
            Map<String, Object> mergedResult = new HashMap<>();
            Map<String, Object> mergedData = new HashMap<>();
            String resA = builder.setUri(JG_URL).build().doGet();
            Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
            if (resultA.get("data") != null) {
                mergedData.put("A", resultA.get("data"));
            }
            String resB = builder.setUri(DJ_URL).build().doGet();
            Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
            if (resultB.get("data") != null) {
                mergedData.put("B", resultB.get("data"));
            }
            String resC = builder.setUri(LJQ_URL).build().doGet();
            Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
            if (resultC.get("data") != null) {
                mergedData.put("C", resultC.get("data"));
            }
            Object resultD = digitalTwinService.inAndOutBound(areaId, startDate, endDate);
            if (resultD != null) {
                mergedData.put("D", resultD);
            }
            return R.ok(mergedData);
        }
    }
    /**
     * 近期呆滞品(默认超30天)
     *
     * @param areaId    库区编码
     * @param overDayNum 呆滞品天数,默认30天
     * @return
     */
    @RequestMapping(value = "/recentDetainMat")
//    @ManagerAuth
    public R recentDetainMat(@RequestParam(required = false) String areaId,
                             @RequestParam(required = false) Integer overDayNum,
                             @RequestParam(required = false) Integer pageIndex,
                             @RequestParam(required = false) Integer pageSize,
                             @RequestParam(required = false) String condition) throws IOException {
        Map<String, Object> map = new HashMap<>();
        if (areaId != null) {
            map.put("areaId", areaId);
        }
        if (overDayNum != null) {
            map.put("overDayNum", overDayNum);
        }
        if (pageIndex != null) {
            map.put("pageIndex", pageIndex);
        }
        if (pageSize != null) {
            map.put("pageSize", pageSize);
        }
        if (condition != null) {
            map.put("condition", condition);
        }
        HttpHandler.Builder builder = new HttpHandler.Builder()
                .setPath("/digitalTwin/recentDetainMat")
                .setParams(map);
        ObjectMapper objectMapper = new ObjectMapper();
        if (areaId != null) {
            switch (areaId) {
                case "A": {
                    String resA = builder.setUri(JG_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    // 如果只需要data部分
                    List data = (List) result.get("data");
                    return R.ok(data);
                }
                case "B": {
                    String resA = builder.setUri(DJ_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    // 如果只需要data部分
                    List data = (List) result.get("data");
                    return R.ok(data);
                }
                case "C": {
                    String resA = builder.setUri(LJQ_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    // 如果只需要data部分
                    List data = (List) result.get("data");
                    return R.ok(data);
                }
                case "D": {
                    return R.ok(digitalTwinService.recentDetainMat(areaId, overDayNum, pageIndex, pageSize, condition));
                }
                default:
                    Map<String, Object> mergedResult = new HashMap<>();
                    Map<String, Object> mergedData = new HashMap<>();
                    String resA = builder.setUri(JG_URL).build().doGet();
                    Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
                    if (resultA.get("data") != null) {
                        mergedData.put("A", resultA.get("data"));
                    }
                    String resB = builder.setUri(DJ_URL).build().doGet();
                    Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
                    if (resultB.get("data") != null) {
                        mergedData.put("B", resultB.get("data"));
                    }
                    String resC = builder.setUri(LJQ_URL).build().doGet();
                    Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
                    if (resultC.get("data") != null) {
                        mergedData.put("C", resultC.get("data"));
                    }
                    Object resultD = digitalTwinService.recentDetainMat(areaId, overDayNum, pageIndex, pageSize, condition);
                    if (resultD != null) {
                        mergedData.put("D", resultD);
                    }
                    return R.ok(mergedData);
            }
        } else {
            // 如果没有提供areaId,默认调用本地服务
            Map<String, Object> mergedResult = new HashMap<>();
            Map<String, Object> mergedData = new HashMap<>();
            String resA = builder.setUri(JG_URL).build().doGet();
            Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
            if (resultA.get("data") != null) {
                mergedData.put("A", resultA.get("data"));
            }
            String resB = builder.setUri(DJ_URL).build().doGet();
            Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
            if (resultB.get("data") != null) {
                mergedData.put("B", resultB.get("data"));
            }
            String resC = builder.setUri(LJQ_URL).build().doGet();
            Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
            if (resultC.get("data") != null) {
                mergedData.put("C", resultC.get("data"));
            }
            Object resultD = digitalTwinService.recentDetainMat(areaId, overDayNum, pageIndex, pageSize, condition);
            if (resultD != null) {
                mergedData.put("D", resultD);
            }
            return R.ok(mergedData);
        }
    }
    /**
     * 设备运行信息
     *
     * @param areaId
     * @return
     */
    @RequestMapping(value = "/equipment")
//    @ManagerAuth
    public R equipment(@RequestParam(required = false) String areaId) throws IOException {
        Map<String, Object> map = new HashMap<>();
        map.put("areaId", areaId);
        HttpHandler.Builder builder = new HttpHandler.Builder()
                .setPath("/digitalTwin/equipment")
                .setParams(map);
        ObjectMapper objectMapper = new ObjectMapper();
        if (areaId != null){
            switch (areaId) {
                case "A": {
                    String resA = builder.setUri(JG_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    return R.ok(result.get("data"));
                }
                case "B": {
                    String resA = builder.setUri(DJ_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    return R.ok(result.get("data"));
                }
                case "C": {
                    String resA = builder.setUri(LJQ_URL).build().doGet();
                    Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                    return R.ok(result.get("data"));
                }
                case "D": {
                    return R.ok(digitalTwinService.equipment(areaId));
                }
                default:
                    Map<String, Object> mergedResult = new HashMap<>();
                    Map<String, Object> mergedData = new HashMap<>();
                    String resA = builder.setUri(JG_URL).build().doGet();
                    Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
                    if (resultA.get("data") != null) {
                        mergedData.put("A", resultA.get("data"));
                    }
                    String resB = builder.setUri(DJ_URL).build().doGet();
                    Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
                    if (resultB.get("data") != null) {
                        mergedData.put("B", resultB.get("data"));
                    }
                    String resC = builder.setUri(LJQ_URL).build().doGet();
                    Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
                    if (resultC.get("data") != null) {
                        mergedData.put("C", resultC.get("data"));
                    }
                    Object resultD = digitalTwinService.equipment(areaId);
                    if (resultD != null) {
                        mergedData.put("D", resultD);
                    }
                    return R.ok(mergedData);
            }
        }else {
            Map<String, Object> mergedResult = new HashMap<>();
            Map<String, Object> mergedData = new HashMap<>();
            String resA = builder.setUri(JG_URL).build().doGet();
            Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
            if (resultA.get("data") != null) {
                mergedData.put("A", resultA.get("data"));
            }
            String resB = builder.setUri(DJ_URL).build().doGet();
            Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
            if (resultB.get("data") != null) {
                mergedData.put("B", resultB.get("data"));
            }
            String resC = builder.setUri(LJQ_URL).build().doGet();
            Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
            if (resultC.get("data") != null) {
                mergedData.put("C", resultC.get("data"));
            }
            Object resultD = digitalTwinService.equipment(areaId);
            if (resultD != null) {
                mergedData.put("D", resultD);
            }
            return R.ok(mergedData);
        }
    }
    /**
     * 库位和库存详情
     *
     * @param areaId
     * @return
     */
    @RequestMapping(value = "/warehouseDetail")
//    @ManagerAuth
    public R warehouseDetail(@RequestParam(required = false) String areaId) throws IOException {
        Map<String, Object> map = new HashMap<>();
        if (areaId != null) {
            map.put("areaId", areaId);
        }
        HttpHandler.Builder builder = new HttpHandler.Builder()
                .setPath("/digitalTwin/warehouseDetail")
                .setParams(map);
        ObjectMapper objectMapper = new ObjectMapper();
        if (areaId != null) {
            switch (areaId) {
            case "A": {
                String resA = builder.setUri(JG_URL).build().doGet();
                Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                // 如果只需要data部分
                List data = (List) result.get("data");
                return R.ok(data);
            }
            case "B": {
                builder.setPath("/digitalTwin/warehouseDetail");
                String resA = builder.setUri(DJ_URL).build().doGet();
                Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                // 如果只需要data部分
                List data = (List) result.get("data");
                return R.ok(data);
            }
            case "C": {
                String resA = builder.setUri(LJQ_URL).build().doGet();
                Map<String, Object> result = objectMapper.readValue(resA, Map.class);
                // 如果只需要data部分
                List data = (List) result.get("data");
                return R.ok(data);
            }
            case "D": {
                return R.ok(digitalTwinService.warehouseDetail(areaId));
            }
            default:
                Map<String, Object> mergedResult = new HashMap<>();
                Map<String, Object> mergedData = new HashMap<>();
                String resA = builder.setUri(JG_URL).build().doGet();
                Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
                if (resultA.get("data") != null) {
                    mergedData.put("A", resultA.get("data"));
                }
                String resB = builder.setUri(DJ_URL).build().doGet();
                Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
                if (resultB.get("data") != null) {
                    mergedData.put("B", resultB.get("data"));
                }
                String resC = builder.setUri(LJQ_URL).build().doGet();
                Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
                if (resultC.get("data") != null) {
                    mergedData.put("C", resultC.get("data"));
                }
                Object resultD = digitalTwinService.warehouseDetail(areaId);
                if (resultD != null) {
                    mergedData.put("D", resultD);
                }
                return R.ok(mergedData);
        }
        } else {
            // 如果没有提供areaId,默认调用本地服务
            Map<String, Object> mergedResult = new HashMap<>();
            Map<String, Object> mergedData = new HashMap<>();
            String resA = builder.setUri(JG_URL).build().doGet();
            Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
            if (resultA.get("data") != null) {
                mergedData.put("A", resultA.get("data"));
            }
            String resB = builder.setUri(DJ_URL).build().doGet();
            Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
            if (resultB.get("data") != null) {
                mergedData.put("B", resultB.get("data"));
            }
            String resC = builder.setUri(LJQ_URL).build().doGet();
            Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
            if (resultC.get("data") != null) {
                mergedData.put("C", resultC.get("data"));
            }
            Object resultD = digitalTwinService.warehouseDetail(areaId);
            if (resultD != null) {
                mergedData.put("D", resultD);
            }
            return R.ok(mergedData);
        }
    }
    /**
     * 查询所有库位状态和物料-二机床信息化数字孪生用
     */
    @RequestMapping(value = "/getAllLocations")
    public R getAllLocations() throws IOException {
        Map<String, Object> mergedData = new HashMap<>();
        HttpHandler.Builder builder = new HttpHandler.Builder()
                .setPath("/digitalTwin/getAllLocations");
        ObjectMapper objectMapper = new ObjectMapper();
        String resA = builder.setUri(JG_URL).build().doGet();
        Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
        if (resultA.get("data") != null) {
            mergedData.put("A", resultA.get("data"));
        }
        String resB = builder.setUri(DJ_URL).build().doGet();
        Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
        if (resultB.get("data") != null) {
            mergedData.put("B", resultB.get("data"));
        }
        String resC = builder.setUri(LJQ_URL).build().doGet();
        Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
        if (resultC.get("data") != null) {
            mergedData.put("C", resultC.get("data"));
        }
        mergedData.put("D",digitalTwinService.getAllLocations());
        return R.ok(mergedData);
    }
    /**
     * 查询所有库的库位状态总数量
     */
    @RequestMapping(value = "/getLocalInfo")
    public R getLocalInfo() throws IOException {
        Map<String, Object> mergedData = new HashMap<>();
        HttpHandler.Builder builder = new HttpHandler.Builder()
                .setPath("/digitalTwin/getLocalInfo");
        ObjectMapper objectMapper = new ObjectMapper();
        String resA = builder.setUri(JG_URL).build().doGet();
        Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
        mergedData.put("A", resultA);
        String resB = builder.setUri(DJ_URL).build().doGet();
        Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
        mergedData.put("B", resultB);
        String resC = builder.setUri(LJQ_URL).build().doGet();
        Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
        mergedData.put("C", resultC);
        Map<String, Object> locInfo = digitalTwinService.getLocInfo();
        mergedData.put("D", locInfo);
        return R.ok(mergedData);
    }
    /**
     * 查询所有库的库存明细
     */
    @RequestMapping(value = "/getLocalDetal")
    public R getLocalDetal() throws IOException {
        Map<String, Object> mergedData = new HashMap<>();
        HttpHandler.Builder builder = new HttpHandler.Builder()
                .setPath("/digitalTwin/getLocalDetal");
        ObjectMapper objectMapper = new ObjectMapper();
        String resA = builder.setUri(JG_URL).build().doGet();
        Map<String, Object> resultA = objectMapper.readValue(resA, Map.class);
        if (resultA.get("data") != null) {
            mergedData.put("A", resultA.get("data"));
        }
        String resB = builder.setUri(DJ_URL).build().doGet();
        Map<String, Object> resultB = objectMapper.readValue(resB, Map.class);
        if (resultB.get("data") != null) {
            mergedData.put("B", resultB.get("data"));
        }
        String resC = builder.setUri(LJQ_URL).build().doGet();
        Map<String, Object> resultC = objectMapper.readValue(resC, Map.class);
        if (resultC.get("data") != null) {
            mergedData.put("C", resultC.get("data"));
        }
        List<Map<String, Object>> detalList = digitalTwinService.getLocalDetal();
        mergedData.put("D", detalList);
        return R.ok(mergedData);
    }
}
src/main/java/com/zy/asrs/entity/LocCount.java
New file
@@ -0,0 +1,41 @@
package com.zy.asrs.entity;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableName;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@TableName("asr_loc_count")
public class LocCount {
    private static final long serialVersionUID = 1L;
    /**
     * 日期
     */
    @ApiModelProperty(value= "日期,格式:20250101")
    @TableField("date")
    private Integer date;
    /**
     * 库区号
     */
    @ApiModelProperty(value= "库区号")
    @TableField("area_id")
    private String areaId;
    /**
     * 库位数量
     */
    @ApiModelProperty(value= "库位数量")
    @TableField("loc_num")
    private Integer locNum;
    /**
     * 剩余库位数量
     */
    @ApiModelProperty(value= "剩余库位数量")
    @TableField("remain_num")
    private Integer remainNum;
}
src/main/java/com/zy/asrs/entity/digitaltwin/AllLocationsVo.java
New file
@@ -0,0 +1,36 @@
package com.zy.asrs.entity.digitaltwin;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
 * @author pang.jiabao
 * @description 查询所有库位状态和物料-二机床信息化数字孪生用
 * @createDate 2026/1/26 11:13
 */
@Data
public class AllLocationsVo {
    // 库位号
    private String locNo;
    // 库位状态,O空库位(英文不是数字);F 在库;D 空板;P 出库中;R 出库预约;S 入库预约;其他 其他;
    private String locSts;
    private List<LocDetl> locDetls  = new ArrayList<>();
    @Data
    public static class LocDetl {
        @ApiModelProperty(value = "商品编号")
        private String matnr;
        @ApiModelProperty(value = "商品名称")
        private String maktx;
        private Double anfme;
    }
}
src/main/java/com/zy/asrs/entity/digitaltwin/DtDetainMatVo.java
New file
@@ -0,0 +1,27 @@
package com.zy.asrs.entity.digitaltwin;
import lombok.Builder;
import lombok.Data;
// 数字孪生:呆滞品信息
@Data
public class DtDetainMatVo {
    // 归属库区ID
    private String belongAreaId;
    // 归属库区名称
    private String belongAreaName;
    // 物料ID
    private String matId;
    // 物料名称
    private String matName;
    // 所属库位ID
    private String lokId;
    // 所属库位名称
    private String lokName;
    // 呆滞时间,计算单位:分钟
    private Integer detainTime;
    // 入库时间,格式:2025-10-11T11:15:16,预留
    private String inBoundTime;
}
src/main/java/com/zy/asrs/entity/digitaltwin/DtEquipmentDocVo.java
New file
@@ -0,0 +1,36 @@
package com.zy.asrs.entity.digitaltwin;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
// 数字孪生:设备监控(对接文档)
@Data
public class DtEquipmentDocVo {
    // 设备类型:1 堆垛机;2 输送线
    private Integer equipmentType;
    // 堆垛机号
    private Integer crnNo;
    // 任务号
    private String taskNo;
    // 列
    private Integer bay1;
    // 层
    @JsonProperty("Lev1")
    private Integer lev1;
    // 垂直速度,单位:M/min
    private Integer verticalSpeed;
    // 水平速度,单位:M/min
    private Integer horizontalSpeed;
    // 电压,单位:v
    private Integer voltage;
    // 设备状态:1 正常;2 待机;3 故障;
    private Integer status;
    // 操作方式:1 自动;2 半自动;3 手动;
    private Integer operateMethod;
    // 输送线号
    private Integer devpNo;
    // 有物
    @JsonProperty("Loading")
    private String loading;
}
src/main/java/com/zy/asrs/entity/digitaltwin/DtEquipmentVo.java
New file
@@ -0,0 +1,35 @@
package com.zy.asrs.entity.digitaltwin;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class DtEquipmentVo {
    // 设备ID
    private String equipmentId;
    // 设备名称
    private String equipmentName;
    // 设备类型:1 堆垛机;2 CTU;3 AGV;
    private Integer equipmentType;
    // 所属库ID
    private String belongAreaId;
    // 所属库名
    private String belongAreaName;
    // 垂直速度,单位:M/min
    private Integer verticalSpeed;
    // 水平速度,单位:M/min
    private Integer horizontalSpeed;
    // 电压,单位:v
    private Integer voltage;
    // 设备状态:1 正常;2 待机;3 故障;
    private Integer status;
    // 操作方式:1 自动;2 半自动;3 手动;
    private Integer operateMethod;
}
src/main/java/com/zy/asrs/entity/digitaltwin/DtInAndOutBoundVo.java
New file
@@ -0,0 +1,17 @@
package com.zy.asrs.entity.digitaltwin;
import lombok.Builder;
import lombok.Data;
// 数字孪生:按天出入库数量
@Data
@Builder
public class DtInAndOutBoundVo {
    // 日期
    private String boundDate;
    // 入库数量
    private Integer inBoundNum;
    // 出库数量
    private Integer outBoundNum;
}
src/main/java/com/zy/asrs/entity/digitaltwin/DtLocDetailVo.java
New file
@@ -0,0 +1,30 @@
package com.zy.asrs.entity.digitaltwin;
import com.zy.asrs.entity.LocDetl;
import com.zy.asrs.entity.LocMast;
import lombok.Data;
@Data
public class DtLocDetailVo  {
    // 库位号
    private String locNo;
    // 库位状态,O空库位(英文不是数字);F 在库;D 空板;P 出库中;R 出库预约;S 入库预约;其他 其他;
    private String locSts;
    // 库区id
    private Long areaId;
    // 库区名称
    private String areaName;
    // 排
    private Integer row1;
    // 列
    private Integer bay1;
    // 层
    private Integer lev1;
    // 库位信息
    private LocMast locMast;
    // 库存信息
    private LocDetl locDetl;
}
src/main/java/com/zy/asrs/entity/digitaltwin/DtLocVo.java
New file
@@ -0,0 +1,15 @@
package com.zy.asrs.entity.digitaltwin;
import lombok.Builder;
import lombok.Data;
// 数字孪生:按天剩余库位数量
@Data
@Builder
public class DtLocVo {
    // 日期
    private String locDate;
    // 库位剩余数量
    private Integer idleNum;
}
src/main/java/com/zy/asrs/entity/digitaltwin/DtOrderVo.java
New file
@@ -0,0 +1,19 @@
package com.zy.asrs.entity.digitaltwin;
import lombok.Builder;
import lombok.Data;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
// 数字孪生:按天订单数量
@Data
@Builder
// DtOrderVo.java
public class DtOrderVo {
    private String orderDate;  // 或 LocalDate
    private Integer orderNum;
}
src/main/java/com/zy/asrs/entity/digitaltwin/DtOverviewVo.java
New file
@@ -0,0 +1,24 @@
package com.zy.asrs.entity.digitaltwin;
import lombok.Builder;
import lombok.Data;
// 数字孪生:按天出库、入库数量
@Data
@Builder
public class DtOverviewVo {
    // 总库位
    private Integer totalLoc;
    // 已用库位
    private Integer useLoc;
    // 剩余库位
    private Integer idleLoc;
    // 今日出库
    private Integer todayOutbound;
    // 今日入库
    private Integer todayWarehousing;
    // 剩余库存
    private Integer remainingStock;
}
src/main/java/com/zy/asrs/entity/digitaltwin/LocPicDto.java
New file
@@ -0,0 +1,13 @@
package com.zy.asrs.entity.digitaltwin;
import lombok.Data;
import java.util.List;
@Data
public class LocPicDto {
    private String locNo;
    private List<String> pics;
}
src/main/java/com/zy/asrs/mapper/DigitalTwinMapper.java
New file
@@ -0,0 +1,27 @@
package com.zy.asrs.mapper;
import com.zy.asrs.entity.digitaltwin.DtDetainMatVo;
import com.zy.asrs.entity.digitaltwin.DtInAndOutBoundVo;
import com.zy.asrs.entity.digitaltwin.DtOrderVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.Date;
import java.util.List;
@Mapper
@Repository
public interface DigitalTwinMapper {
    List<Double> overview(@Param("areaId")String areaId);
    List<DtOrderVo> recentOrder(@Param("startTime")Date startTime, @Param("endTime")Date endTime);
    List<DtInAndOutBoundVo> recentInBound(@Param("areaId")String areaId, @Param("startTime") Date startTime, @Param("endTime")Date endTime);
    List<DtInAndOutBoundVo> recentOutBound(@Param("areaId")String areaId, @Param("startTime") Date startTime, @Param("endTime")Date endTime);
    List<DtDetainMatVo> recentDetainMat(@Param("areaId")String areaId, @Param("startTime")Date startTime,
                                        @Param("pageIndex")Integer pageIndex, @Param("pageSize")Integer pageSize);
}
src/main/java/com/zy/asrs/mapper/LocCountMapper.java
New file
@@ -0,0 +1,24 @@
package com.zy.asrs.mapper;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.zy.asrs.entity.LocCount;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface LocCountMapper extends BaseMapper<LocCount> {
    List<LocCount> getByAreaAndDate(@Param("areaId")String areaId, @Param("startDate")int startDate, @Param("endDate")int endDate);
    List<LocCount> getByDate(@Param("startDate")int startDate, @Param("endDate")int endDate);
    Integer insertOrUpdate(@Param("model")LocCount model);
    List<LocCount> totalLoc();
    List<LocCount> useLoc();
}
src/main/java/com/zy/asrs/service/DigitalTwinService.java
New file
@@ -0,0 +1,83 @@
package com.zy.asrs.service;
import com.zy.asrs.entity.digitaltwin.*;
import java.util.List;
import java.util.Map;
public interface DigitalTwinService {
    /**
     * 总览:总库位、已用库位、剩余库位、今日出库、今日入库、剩余库位
     *
     * @param areaId
     * @return
     */
    DtOverviewVo overview(String areaId);
    /**
     * 近期订单,默认7天
     *
     * @param startDate
     * @param endDate
     * @return
     */
    List<DtOrderVo> order(String startDate, String endDate);
    /**
     * 近期出库、入库数量,默认7天
     *
     * @param areaId
     * @param startDate
     * @param endDate
     * @return
     */
    List<DtInAndOutBoundVo> inAndOutBound(String areaId, String startDate, String endDate);
    /**
     * 近期近期呆滞品信息,默认超过30天为呆滞品
     *
     * @param areaId
     * @param overDayNum
     * @param pageIndex
     * @param pageSize
     * @param condition 搜索条件
     * @return
     */
    List<DtDetainMatVo> recentDetainMat(String areaId, Integer overDayNum, Integer pageIndex, Integer pageSize, String condition);
    /**
     * 查询库存和库位详细信息
     *
     * @param areaId
     * @return
     */
    List<DtLocDetailVo> warehouseDetail(String areaId);
    /**
     * 近期剩余库位数量,默认7天
     *
     * @param areaId
     * @param startDate
     * @param endDate
     * @return
     */
    List<DtLocVo> recentLoc(String areaId, String startDate, String endDate);
    /**
     * 定期统计剩余库存
     *
     */
    void locNumCount();
    List<DtEquipmentDocVo> equipment(String areaId);
    /**
     * 查询所有库位状态和物料-二机床信息化数字孪生用
     */
    List<AllLocationsVo> getAllLocations();
    List<Map<String, Object>> getLocalDetal();
    Map<String, Object> getLocInfo();
}
src/main/java/com/zy/asrs/service/impl/DigitalTwinServiceImpl.java
New file
@@ -0,0 +1,482 @@
package com.zy.asrs.service.impl;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.core.common.Cools;
import com.zy.asrs.entity.BasCrnp;
import com.zy.asrs.entity.BasDevp;
import com.zy.asrs.entity.LocCount;
import com.zy.asrs.entity.LocDetl;
import com.zy.asrs.entity.LocMast;
import com.zy.asrs.entity.digitaltwin.*;
import com.zy.asrs.mapper.DigitalTwinMapper;
import com.zy.asrs.mapper.LocCountMapper;
import com.zy.asrs.mapper.LocDetlMapper;
import com.zy.asrs.mapper.LocMastMapper;
import com.zy.asrs.service.BasCrnpService;
import com.zy.asrs.service.BasDevpService;
import com.zy.asrs.service.DigitalTwinService;
import com.zy.asrs.service.LocDetlService;
import com.zy.asrs.service.LocMastService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class DigitalTwinServiceImpl implements DigitalTwinService {
    @Resource
    private DigitalTwinMapper digitalTwinMapper;
    @Resource
    private LocCountMapper locCountMapper;
    @Resource
    private LocMastMapper locMastMapper;
    @Resource
    private LocDetlMapper locDetlMapper;
    @Resource
    private LocMastService locMastService;
    /**
     * 总览:总库位、已用库位、剩余库位、今日出库、今日入库、剩余库位
     *
     * @param areaId
     * @return
     */
    public DtOverviewVo overview(String areaId) {
        List<Double> dbOverview = digitalTwinMapper.overview(areaId);
        if (dbOverview != null && !dbOverview.isEmpty()){
            DtOverviewVo dtOverviewVo = DtOverviewVo.builder()
                .totalLoc((int)Math.round(dbOverview.get(0)))
                    .useLoc((int)Math.round(dbOverview.get(1)))
                    .remainingStock((int)Math.round(dbOverview.get(2)))
                    .todayWarehousing((int)Math.round(dbOverview.get(3)))
                    .todayOutbound((int)Math.round(dbOverview.get(4)))
                    .build();
            dtOverviewVo.setIdleLoc(dtOverviewVo.getTotalLoc() - dtOverviewVo.getUseLoc());
            return dtOverviewVo;
        }
        return DtOverviewVo.builder().build();
    }
    /**
     * 近期订单,默认7天
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public List<DtOrderVo> order(String startDate, String endDate) {
        Date startTime = new Date();
        Date endTime = new Date();
        if (startDate == null || endDate == null || startDate.isEmpty() || endDate.isEmpty()){
            Date now = new Date();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(now);
            calendar.add(Calendar.DAY_OF_MONTH, -7);
            startTime = calendar.getTime();
            endTime = now;
        } else {
            SimpleDateFormat sdf;
            try {
                // 尝试解析yyyyMMdd格式
                if (startDate.length() == 8 && endDate.length() == 8) {
                    sdf = new SimpleDateFormat("yyyyMMdd");
                    startTime = sdf.parse(startDate);
                    endTime = sdf.parse(endDate);
                    // 设置结束时间为当天的23:59:59.999
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(endTime);
                    calendar.set(Calendar.HOUR_OF_DAY, 23);
                    calendar.set(Calendar.MINUTE, 59);
                    calendar.set(Calendar.SECOND, 59);
                    calendar.set(Calendar.MILLISECOND, 999);
                    endTime = calendar.getTime();
                } else {
                    // 尝试解析yyyy-MM-dd HH:mm:ss.SSS格式
                    sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
                    startTime = sdf.parse(startDate);
                    endTime = sdf.parse(endDate);
                }
            } catch (ParseException e) {
                e.printStackTrace();
                // 解析失败时使用默认的7天时间范围
                Date now = new Date();
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(now);
                calendar.add(Calendar.DAY_OF_MONTH, -7);
                startTime = calendar.getTime();
                endTime = now;
            }
        }
        List<DtOrderVo> dbOrder = digitalTwinMapper.recentOrder(startTime, endTime);
        // 空日期补全
        for (DtOrderVo dtOrderVo : dbOrder) {
            dtOrderVo.setOrderDate(dtOrderVo.getOrderDate());
        }
        return dbOrder;
    }
    /**
     * 近期出库、入库数量,默认7天
     *
     * @param areaId
     * @param startDate
     * @param endDate
     * @return
     */
    public List<DtInAndOutBoundVo> inAndOutBound(String areaId, String startDate, String endDate) {
        Date startTime = new Date();
        Date endTime = new Date();
        if (startDate == null || endDate == null || startDate.isEmpty() || endDate.isEmpty()){
            Date now = new Date();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(now);
            calendar.add(Calendar.DAY_OF_MONTH, -7);
            startTime = calendar.getTime();
            endTime = now;
        } else {
            SimpleDateFormat sdf;
            try {
                // 尝试解析yyyyMMdd格式
                if (startDate.length() == 8 && endDate.length() == 8) {
                    sdf = new SimpleDateFormat("yyyyMMdd");
                    startTime = sdf.parse(startDate);
                    endTime = sdf.parse(endDate);
                    // 设置结束时间为当天的23:59:59.999
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(endTime);
                    calendar.set(Calendar.HOUR_OF_DAY, 23);
                    calendar.set(Calendar.MINUTE, 59);
                    calendar.set(Calendar.SECOND, 59);
                    calendar.set(Calendar.MILLISECOND, 999);
                    endTime = calendar.getTime();
                } else {
                    // 尝试解析yyyy-MM-dd HH:mm:ss.SSS格式
                    sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
                    startTime = sdf.parse(startDate);
                    endTime = sdf.parse(endDate);
                }
            } catch (ParseException e) {
                e.printStackTrace();
                // 解析失败时使用默认的7天时间范围
                Date now = new Date();
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(now);
                calendar.add(Calendar.DAY_OF_MONTH, -7);
                startTime = calendar.getTime();
                endTime = now;
            }
        }
        List<DtInAndOutBoundVo> dtInBoundVos = digitalTwinMapper.recentInBound(areaId, startTime, endTime);
        List<DtInAndOutBoundVo> dtOutBoundVos = digitalTwinMapper.recentOutBound(areaId, startTime, endTime);
        // 格式整理
        List<DtInAndOutBoundVo> dtInAndOutBoundVos = new ArrayList<>(dtInBoundVos);
        dtInAndOutBoundVos.addAll(dtOutBoundVos);
        return dtInAndOutBoundVos;
    }
    /**
     * 近期近期呆滞品信息,默认超过30天为呆滞品
     *
     * @param areaId
     * @param overDayNum
     * @param pageIndex
     * @param pageSize
     * @param condition 搜索条件
     * @return
     */
    public List<DtDetainMatVo> recentDetainMat(String areaId, Integer overDayNum, Integer pageIndex, Integer pageSize, String condition) {
        overDayNum = overDayNum == null ? 30 : overDayNum;
        pageIndex = pageIndex == null ? 1 : pageIndex;
        pageSize = pageSize == null ? 1000000 : pageSize;
        Date now = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(now);
        calendar.add(Calendar.DAY_OF_MONTH, -overDayNum);
        Date start = calendar.getTime();
    /*    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String startTime = sdf.format(start);*/
        List<DtDetainMatVo> dbDetainMats = digitalTwinMapper.recentDetainMat(areaId, start, pageIndex, pageSize);
        return dbDetainMats;
    }
    /**
     * 查询库存和库位详细信息
     *
     * @param areaId
     * @return
     */
    public List<DtLocDetailVo> warehouseDetail(String areaId) {
        List<LocMast> locMastList = locMastService.selectList(new EntityWrapper<>());
        return locMastList.stream()
                .map(loc -> {
                    DtLocDetailVo vo = new DtLocDetailVo();
                    BeanUtils.copyProperties(loc, vo);
                    vo.setLocMast(loc);
                    LocDetl query = new LocDetl();
                    query.setLocNo(loc.getLocNo());
                    LocDetl locDetl = locDetlMapper.selectOne(query);
                    vo.setLocDetl(locDetl);
                    return vo;
                })
                .collect(Collectors.toList());
    }
    /**
     * 近期剩余库位数量,默认7天
     *
     * @param areaId
     * @param startDate
     * @param endDate
     * @return
     */
    public List<DtLocVo> recentLoc(String areaId, String startDate, String endDate) {
        List<DtLocVo> locVos = new ArrayList<>();
        if (startDate == null || endDate == null || startDate.isEmpty() || endDate.isEmpty()){
            Date now = new Date();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(now);
            calendar.add(Calendar.DAY_OF_MONTH, -7);
            Date start = calendar.getTime();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            endDate = sdf.format(now);
            startDate = sdf.format(start);
        }
        List<LocCount> locCounts;
        if (areaId != null && !areaId.isEmpty()) {
            locCounts = locCountMapper.getByAreaAndDate(areaId, Integer.parseInt(startDate), Integer.parseInt(endDate));
        } else {
            locCounts = locCountMapper.getByDate(Integer.parseInt(startDate), Integer.parseInt(endDate));
        }
        for (LocCount locCount : locCounts) {
            String date = locCount.getDate().toString();
            String locDate = date.substring(0, 4) + "-" + date.substring(4, 6) + "-" + date.substring(6, 8);
            DtLocVo dtLocVo = DtLocVo.builder()
                    .locDate(locDate)
                    .idleNum(locCount.getRemainNum())
                    .build();
            locVos.add(dtLocVo);
        }
        return locVos;
    }
    /**
     * 定期统计剩余库存
     *
     */
    @Transactional
    public void locNumCount() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String date = sdf.format(new Date());
        List<LocCount> totalLoc = locCountMapper.totalLoc();
        List<LocCount> useLoc = locCountMapper.useLoc();
        LocCount locCount1 = new LocCount();
        locCount1.setDate(Integer.valueOf(date));
        locCount1.setLocNum(totalLoc.get(0).getLocNum());
        locCount1.setRemainNum(locCount1.getLocNum() - useLoc.get(0).getLocNum());
        locCountMapper.insertOrUpdate(locCount1);
    }
    @Resource
    private BasCrnpService basCrnpService;
    @Resource
    private BasDevpService basDevpService;
    @Override
    public List<DtEquipmentDocVo> equipment(String areaId) {
        List<DtEquipmentDocVo> equipmentVos = new ArrayList<>();
        List<BasCrnp> crnps = basCrnpService.selectList(new EntityWrapper<>());
        for (BasCrnp crnp : crnps) {
            DtEquipmentDocVo vo = new DtEquipmentDocVo();
            vo.setEquipmentType(1);
            vo.setCrnNo(crnp.getCrnNo());
            Integer wrkNo = crnp.getWrkNo();
            vo.setTaskNo(wrkNo == null ? null : String.valueOf(wrkNo));
            String locNo = !Cools.isEmpty(crnp.getToLocno()) ? crnp.getToLocno() : crnp.getFrmLocno();
            if (!Cools.isEmpty(locNo)) {
                LocMast locMast = locMastService.selectById(locNo);
                if (locMast != null) {
                    vo.setBay1(locMast.getBay1());
                    vo.setLev1(locMast.getLev1());
                }
            }
            vo.setVerticalSpeed(parseInteger(crnp.getCtlHp()));
            vo.setHorizontalSpeed(parseInteger(crnp.getCtlRest()));
            vo.setVoltage(220);
            vo.setStatus(crnp.getCrnErr() != null && crnp.getCrnErr() == 0 ? 1 : 3);
            vo.setOperateMethod(crnp.getCrnSts() != null && crnp.getCrnSts() == 3 ? 1 : 3);
            equipmentVos.add(vo);
        }
        List<BasDevp> devps = basDevpService.selectList(new EntityWrapper<>());
        for (BasDevp devp : devps) {
            DtEquipmentDocVo vo = new DtEquipmentDocVo();
            vo.setEquipmentType(2);
            vo.setDevpNo(devp.getDevNo());
            Integer wrkNo = devp.getWrkNo();
            Integer wrkNo1 = devp.getWrkNo1();
            Integer task = wrkNo != null ? wrkNo : wrkNo1;
            vo.setTaskNo(task == null ? null : String.valueOf(task));
            vo.setLoading(devp.getLoading());
            equipmentVos.add(vo);
        }
        return equipmentVos;
    }
    private Integer parseInteger(String value) {
        if (Cools.isEmpty(value)) {
            return 0;
        }
        try {
            return Integer.valueOf(value);
        } catch (NumberFormatException ex) {
            return 0;
        }
    }
    // region 数字孪生
    // 数字孪生整合
    // 机器状态整合
    // endregion
    // region 立库调度
    // 堆垛机存取
    // 输送线拍照、称重
    // endregion
    @Resource
    private LocDetlService locDetlService;
    @Override
    public List<AllLocationsVo> getAllLocations() {
        List<AllLocationsVo> allLocationsVos = new ArrayList<>();
        List<LocMast> locMastList = locMastService.selectList(new EntityWrapper<>());
        locMastList.forEach(locMast -> {
            AllLocationsVo allLocationsVo = new AllLocationsVo();
            allLocationsVo.setLocNo(locMast.getLocNo());
            String locSts = locMast.getLocSts();
            allLocationsVo.setLocSts(locSts);
            // 有库存
            if (locSts.equals("F") || locSts.equals("P") || locSts.equals("Q") || locSts.equals("R")) {
                List<LocDetl> locDetls = locDetlService.selectList(new EntityWrapper<LocDetl>().eq("loc_no", locMast.getLocNo()));
                if (!locDetls.isEmpty()) {
                    List<AllLocationsVo.LocDetl> locDetlList = locDetls.stream().map(locDetl -> {
                                AllLocationsVo.LocDetl locDetl1 = new AllLocationsVo.LocDetl();
                                BeanUtils.copyProperties(locDetl, locDetl1);
                                return locDetl1;
                            }
                    ).collect(Collectors.toList());
                    allLocationsVo.setLocDetls(locDetlList);
                }
            }
            allLocationsVos.add(allLocationsVo);
        });
        return allLocationsVos;
    }
    public List<Map<String, Object>> getLocalDetal() {
        List<LocDetl> locDetls = locDetlMapper.selectList(new EntityWrapper<>());
        List<Map<String, Object>> result = new ArrayList<>();
        for (LocDetl locDetl : locDetls) {
            Map<String, Object> item = new HashMap<>();
            item.put("zpallet", locDetl.getZpallet());
            item.put("anfme", locDetl.getAnfme());
            item.put("matnr", locDetl.getMatnr());
            item.put("maktx", locDetl.getMaktx());
            result.add(item);
        }
        return result;
    }
    public Map<String, Object> getLocInfo() {
        List<LocMast> LocMasts = locMastMapper.selectList(new EntityWrapper<>());
        Map<String, Object> result = new HashMap<>();
        // 初始化计数器
        int emptyLocCount = 0;
        int fullZpalletCount = 0;
        int emptyZpalletCount = 0;
        int pickOutCount = 0;
        int pickInCount = 0;
        int outboundCount = 0;
        int inboundCount = 0;
        int disableCount = 0;
        int mergeCount = 0;
        // 统计每种状态的库位数量
        for (LocMast locMast : LocMasts) {
            String locSts = locMast.getLocSts();
            if (locSts.equals("O")) {
                emptyLocCount++;
            } else if (locSts.equals("F")) {
                fullZpalletCount++;
            } else if (locSts.equals("D")) {
                emptyZpalletCount++;
            } else if (locSts.equals("P")) {
                pickOutCount++;
            } else if (locSts.equals("Q")) {
                pickInCount++;
            } else if (locSts.equals("R")) {
                outboundCount++;
            } else if (locSts.equals("S")) {
                inboundCount++;
            } else if (locSts.equals("X")) {
                disableCount++;
            } else if (locSts.equals("Y")) {
                mergeCount++;
            }
        }
        // 将统计结果放入Map中
        result.put("EmptyLoc", emptyLocCount);
        result.put("FullZpallet", fullZpalletCount);
        result.put("EmptyZpallet", emptyZpalletCount);
        result.put("PickOut", pickOutCount);
        result.put("PickIn", pickInCount);
        result.put("Outbound", outboundCount);
        result.put("Inbound", inboundCount);
        result.put("Disable", disableCount);
        result.put("Merge", mergeCount);
        return result;
    }
}
src/main/resources/application.yml
@@ -53,6 +53,11 @@
  api-key: app-mP0O6aY5WpbfaHs7BNnjVkli
  model: deepseek-ai/DeepSeek-V3.2
digitalTwins:
  jgUrl: http://172.26.11.88:8089/jgwms
  djUrl: http://172.26.11.88:8088/djwms
  ljqUrl: http://172.26.11.80:8083/ljqwms
# 下位机配置
wcs-slave:
  # 双深
src/main/resources/mapper/LocCountMapper.xml
New file
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zy.asrs.mapper.LocCountMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.zy.asrs.entity.LocCount">
        <result column="date" property="date" />
        <result column="area_id" property="areaId" />
        <result column="loc_num" property="locNum" />
        <result column="remain_num" property="remainNum" />
    </resultMap>
    <select id="getByAreaAndDate" resultType="com.zy.asrs.entity.LocCount">
        SELECT * FROM asr_loc_count
        WHERE area_id = #{areaId} AND date BETWEEN #{startDate} AND #{endDate}
    </select>
    <select id="getByDate" resultType="com.zy.asrs.entity.LocCount">
        SELECT date, SUM(ISNULL(loc_num, 0)) AS loc_num, SUM(ISNULL(remain_num, 0)) AS remain_num
        FROM asr_loc_count
        WHERE date BETWEEN #{startDate} AND #{endDate}
        GROUP BY date
    </select>
    <insert id="insertOrUpdate" parameterType="com.zy.asrs.entity.LocCount">
        IF EXISTS (SELECT 1 FROM asr_loc_count WHERE date = #{model.date})
            BEGIN
                UPDATE asr_loc_count
                SET loc_num = #{model.locNum},
                    remain_num = #{model.remainNum}
                WHERE date = #{model.date}
            END
        ELSE
            BEGIN
                INSERT INTO asr_loc_count(date, area_id, loc_num, remain_num)
                VALUES (#{model.date}, #{model.areaId}, #{model.locNum}, #{model.remainNum})
            END
    </insert>
    <select id="totalLoc" resultType="com.zy.asrs.entity.LocCount">
        SELECT area_id, COUNT(*) AS loc_num FROM asr_loc_mast WHERE loc_sts != 'Z' GROUP BY area_id
    </select>
    <select id="useLoc" resultType="com.zy.asrs.entity.LocCount">
        SELECT area_id, COUNT(*) AS loc_num FROM asr_loc_mast
        WHERE loc_sts = 'F' or loc_sts = 'P' or loc_sts = 'Q' or loc_sts = 'R' or loc_sts = 'S' or loc_sts = 'X'
        GROUP BY area_id
    </select>
</mapper>
src/main/resources/mapper/ViewDigitalTwinMapper.xml
New file
@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zy.asrs.mapper.DigitalTwinMapper">
    <resultMap id="dtOrderMap" type="com.zy.asrs.entity.digitaltwin.DtOrderVo">
        <constructor>
            <arg column="orderDate" javaType="java.util.Date"/>
            <arg column="orderNum" javaType="java.lang.Integer"/>
        </constructor>
    </resultMap>
    <!--总览:总库位、已用库位、今日库存、今日出库、今日入库-->
    <select id="overview" resultType="Double">
        SELECT COUNT(*) FROM asr_loc_mast WHERE loc_sts != 'Z'
<!--            <if test="areaId != null">-->
<!--                and area_id = #{areaId}-->
<!--            </if>-->
        UNION ALL
        SELECT COUNT(*) FROM asr_loc_mast WHERE loc_sts = 'F' or loc_sts = 'P' or loc_sts = 'Q' or loc_sts = 'R' or loc_sts = 'S' or loc_sts = 'X'
<!--        <if test="areaId != null">-->
<!--            and area_id = #{areaId}-->
<!--        </if>-->
        UNION ALL
        SELECT ISNULL(SUM(anfme), 0) FROM asr_loc_detl
<!--        <if test="areaId != null">-->
<!--            WHERE area_id = #{areaId}-->
<!--        </if>-->
        UNION ALL
        SELECT ISNULL(SUM(anfme), 0) FROM asr_wrkin_view WHERE CONVERT(VARCHAR, io_time, 23) = CONVERT(VARCHAR, GETDATE(), 23)
<!--        <if test="areaId != null">-->
<!--            and area_id = #{areaId}-->
<!--        </if>-->
        UNION ALL
        SELECT ISNULL(SUM(anfme), 0) FROM asr_wrkout_view WHERE CONVERT(VARCHAR, io_time, 23) = CONVERT(VARCHAR, GETDATE(), 23)
<!--        <if test="areaId != null">-->
<!--            and area_id = #{areaId}-->
<!--        </if>-->
    </select>
    <select id="recentOrder" resultType="com.zy.asrs.entity.digitaltwin.DtOrderVo">
        SELECT
        FORMAT(orderDate, 'yyyy-MM-dd') as orderDate,
        COUNT(*) as orderNum
        FROM (
        SELECT
        CAST(order_time AS DATE) as orderDate
        FROM man_order_pakin
        WHERE order_time BETWEEN #{startTime} AND #{endTime}
        UNION ALL
        SELECT
        CAST(order_time AS DATE) as orderDate
        FROM man_order_pakout
        WHERE order_time BETWEEN #{startTime} AND #{endTime}
        ) combined
        GROUP BY orderDate
        ORDER BY orderDate
    </select>
    <select id="recentInBound" resultType="com.zy.asrs.entity.digitaltwin.DtInAndOutBoundVo">
        SELECT CONVERT(VARCHAR, io_time, 23) AS boundDate, SUM(anfme) AS inBoundNum
        FROM asr_wrkin_view
        WHERE io_time BETWEEN #{startTime} AND #{endTime}
<!--        <if test="areaId != null">-->
<!--            and area_id = #{areaId}-->
<!--        </if>-->
        GROUP BY CONVERT(VARCHAR, io_time, 23)
    </select>
    <select id="recentOutBound" resultType="com.zy.asrs.entity.digitaltwin.DtInAndOutBoundVo">
        SELECT CONVERT(VARCHAR, io_time, 23) AS boundDate, SUM(anfme) AS outBoundNum
        FROM asr_wrkout_view
        WHERE io_time BETWEEN #{startTime} AND #{endTime}
<!--        <if test="areaId != null">-->
<!--            and area_id = #{areaId}-->
<!--        </if>-->
        GROUP BY CONVERT(VARCHAR, io_time, 23)
    </select>
    <select id="recentDetainMat" resultType="com.zy.asrs.entity.digitaltwin.DtDetainMatVo">
        SELECT *
        FROM (
                 SELECT
                     ROW_NUMBER() OVER(ORDER BY t.inBoundTime DESC) AS rownum,
                     t.*
                 FROM (
                          SELECT
                              matnr AS matId,
                              maktx AS matName,
                              loc_no AS lokId,
                              '' AS lokName,
                              DATEDIFF(MINUTE, appe_time, GETDATE()) AS detainTime,
                              CONVERT(VARCHAR, appe_time, 126) AS inBoundTime
                          FROM asr_loc_detl
                          WHERE appe_time &lt;= #{startTime}
                      ) t
             ) a
        WHERE a.rownum BETWEEN ((#{pageIndex}-1)*#{pageSize}+1) AND (#{pageIndex}*#{pageSize})
    </select>
</mapper>