From d85fb7522d10af5dd8447e91542d493de519a997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LAPTOP-S9HJSOEB=5C=E6=98=8A=E5=A4=A9?= Date: Thu, 21 Aug 2025 11:04:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=9A=E4=B8=AAbug=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/stock/StockController.java | 7 + .../admin/street/StreetController.java | 68 +++++ .../dal/dataobject/checklog/CheckLogDO.java | 7 +- .../camera/dal/dataobject/order/OrderDO.java | 1 + .../camera/dal/entity/echarts/Axis.java | 4 +- .../dal/mysql/checklog/CheckLogMapper.java | 4 +- .../camera/dal/mysql/order/OrderMapper.java | 2 +- .../service/Hik3D/HikFlaskApiService.java | 12 +- .../service/checklog/CheckLogServiceImpl.java | 6 + .../service/order/OrderServiceImpl.java | 4 + .../camera/service/plc/PLCServiceImpl.java | 56 +++- .../camera/service/rfid/RfidService.java | 3 +- .../camera/service/rfid/RfidServiceImpl.java | 4 +- .../camera/service/stock/StockService.java | 4 + .../service/stock/StockServiceImpl.java | 40 ++- .../streamingMedia/ZLMediaKitService.java | 4 +- .../streamingMedia/ZLMediaKitServiceImpl.java | 117 ++++++++- .../module/camera/util/DiskSpaceManager.java | 248 ++++++++++++++++++ .../yudao/module/camera/util/NetworkUtil.java | 99 +++++++ .../src/main/resources/application-dev.yaml | 8 + .../src/main/resources/application-local.yaml | 16 +- 21 files changed, 671 insertions(+), 43 deletions(-) create mode 100644 yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/util/DiskSpaceManager.java create mode 100644 yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/util/NetworkUtil.java diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/stock/StockController.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/stock/StockController.java index 8be0f6c..744808c 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/stock/StockController.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/stock/StockController.java @@ -103,6 +103,13 @@ public class StockController { EChartsOption bars = stockService.laneInventoryStatistics(); return success(bars); } + @PostMapping("/laneInventoryStatisticsPie") + @Operation(summary = "首页巷道盘点统计") + @PreAuthorize("@ss.hasPermission('logistics:order:laneInventoryStatistics')") + public CommonResult< List> > laneInventoryStatisticsPie() { + List> bars = stockService.laneInventoryStatisticsPie(); + return success(bars); + } @Resource private DictDataService dictDataService; diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/street/StreetController.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/street/StreetController.java index de8aded..c202a3c 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/street/StreetController.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/street/StreetController.java @@ -16,19 +16,26 @@ import cn.iocoder.yudao.module.camera.dal.dataobject.shelfCode.rfid.ShelfCodeDO; import cn.iocoder.yudao.module.camera.dal.dataobject.street.StreetDO; import cn.iocoder.yudao.module.camera.framework.light.LightFactory; import cn.iocoder.yudao.module.camera.service.lightsource.LightSourceService; +import cn.iocoder.yudao.module.camera.service.rfid.RfidService; +import cn.iocoder.yudao.module.camera.service.sensorgun.SensorGunService; import cn.iocoder.yudao.module.camera.service.street.StreetService; +import cn.iocoder.yudao.module.camera.util.NetworkUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.io.IOException; +import java.util.ArrayList; import java.util.List; +import java.util.concurrent.*; import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -37,6 +44,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @RestController @RequestMapping("/logistics/street") @Validated +@Slf4j public class StreetController { @Resource @@ -45,6 +53,7 @@ public class StreetController { private LightSourceService lightSourceService; + @PostMapping("/create") @Operation(summary = "创建巷道") @PreAuthorize("@ss.hasPermission('logistics:street:create')") @@ -79,6 +88,65 @@ public class StreetController { streetService.getCameraName(streetRespVO); return success(streetRespVO); } + @Resource + SensorGunService sensorGunService; + @Resource + RfidService rfidService; + + + @GetMapping("/linkCheck") + @Operation(summary = "巷道状态查询") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('logistics:street:linkCheck')") + public CommonResult > linkCheck(@RequestParam("id") Integer id) { + + // 使用try-with-resources确保线程池正确关闭 + try (ExecutorService executorService = Executors.newFixedThreadPool(10)) { + List> futures = new ArrayList<>(); + StreetDO street = streetService.getById(id); + List networkUtilList = new ArrayList<>(); + + // 异步检查PLC网络可达性 + futures.add(CompletableFuture.supplyAsync(() -> + new NetworkUtil().isReachable(street.getPlcIp(), street.getPlcPort(), "plc"), executorService)); + + // 异步检查扫码枪网络可达性 + List sensorGunDOList = sensorGunService.list(new QueryWrapper().eq("street_id", id)); + for (SensorGunDO sensorGunDO : sensorGunDOList) { + futures.add(CompletableFuture.supplyAsync(() -> + new NetworkUtil().isReachable(sensorGunDO.getIp(), sensorGunDO.getPort(), "扫码枪"), executorService)); + } + + // 异步检查RFID网络可达性 + List rfidDOList = rfidService.list(new QueryWrapper().eq("street_id", id)); + for (RfidDO rfidDO : rfidDOList) { + futures.add(CompletableFuture.supplyAsync(() -> + new NetworkUtil().isReachable(rfidDO.getIp(), rfidDO.getPort(), "RFID"), executorService)); + } + + // 异步检查光源网络可达性 + List lightSourceDOList = lightSourceService.list(new QueryWrapper().eq("street_id", id)); + for (LightSourceDO lightSourceDO : lightSourceDOList) { + futures.add(CompletableFuture.supplyAsync(() -> + new NetworkUtil().isReachable(lightSourceDO.getIp(), Integer.valueOf(lightSourceDO.getPort()), "光源"), executorService)); + } + + // 等待所有异步任务完成并收集结果 + for (CompletableFuture future : futures) { + try { + // 设置超时时间,避免长时间等待 + networkUtilList.add(future.get(5, TimeUnit.SECONDS)); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + log.error("网络可达性检查异常", e); + } + } + + return success(networkUtilList); + } catch (Exception e) { + log.error("执行网络检查时发生错误", e); + return success(new ArrayList<>()); + } + } @GetMapping("/page") @Operation(summary = "获得巷道分页") diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/dataobject/checklog/CheckLogDO.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/dataobject/checklog/CheckLogDO.java index 5c7b81b..db7a4c3 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/dataobject/checklog/CheckLogDO.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/dataobject/checklog/CheckLogDO.java @@ -2,10 +2,7 @@ package cn.iocoder.yudao.module.camera.dal.dataobject.checklog; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.camera.dal.dataobject.resources.URLResourcesDo; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.*; import lombok.*; import org.glassfish.jaxb.core.v2.TODO; @@ -83,6 +80,8 @@ public class CheckLogDO extends BaseDO { * 枚举 {@link TODO check_status 对应的类} */ private Integer status; + @TableField(exist = false) + private String statusName; /** * 检查编码 */ diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/dataobject/order/OrderDO.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/dataobject/order/OrderDO.java index f5614ed..0e289dc 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/dataobject/order/OrderDO.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/dataobject/order/OrderDO.java @@ -33,6 +33,7 @@ public class OrderDO extends BaseDO { private String cmdName ; private String taskId ; private String shelfCode; + private String uuid ; private int fromSide ; private int fromDirection ; private int fromColumn ; diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/entity/echarts/Axis.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/entity/echarts/Axis.java index 8aba8d3..c6b3c21 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/entity/echarts/Axis.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/entity/echarts/Axis.java @@ -9,7 +9,7 @@ import java.util.List; public class Axis { private String type = "value"; private boolean boundaryGap = true; - private int interval= 1; // 设置步长为 1 - private int splitNumber =5; + private int minInterval= 1; // 设置步长为 1 +// private int splitNumber =5; private List data = new ArrayList<>(); } \ No newline at end of file diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/mysql/checklog/CheckLogMapper.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/mysql/checklog/CheckLogMapper.java index 4651c09..c23d8d9 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/mysql/checklog/CheckLogMapper.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/mysql/checklog/CheckLogMapper.java @@ -16,12 +16,12 @@ import org.apache.ibatis.annotations.Mapper; public interface CheckLogMapper extends BaseMapperX { default PageResult selectPage(CheckLogPageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(CheckLogDO::getBatchNumber, reqVO.getBatchNumber()) + .likeIfPresent(CheckLogDO::getBatchNumber, reqVO.getBatchNumber()) .eqIfPresent(CheckLogDO::getRow, reqVO.getRow()) .eqIfPresent(CheckLogDO::getColumn, reqVO.getColumn()) .eqIfPresent(CheckLogDO::getStreetId, reqVO.getStreetId()) .betweenIfPresent(CheckLogDO::getExportTime, reqVO.getExportTime()) - .eqIfPresent(CheckLogDO::getTaskId, reqVO.getTaskId()) + .likeIfPresent(CheckLogDO::getTaskId, reqVO.getTaskId()) .eqIfPresent(CheckLogDO::getDirection, reqVO.getDirection()) .eqIfPresent(CheckLogDO::getSide, reqVO.getSide()) .eqIfPresent(CheckLogDO::getStatus, reqVO.getStatus()) diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/mysql/order/OrderMapper.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/mysql/order/OrderMapper.java index 4e6bdce..bae5968 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/mysql/order/OrderMapper.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/mysql/order/OrderMapper.java @@ -19,7 +19,7 @@ public interface OrderMapper extends BaseMapperX { .betweenIfPresent(OrderDO::getStartTime, reqVO.getStartTime()) .betweenIfPresent(OrderDO::getEndTime, reqVO.getEndTime()) .eqIfPresent(OrderDO::getSrmNumber, reqVO.getSrmNumber()) - .eqIfPresent(OrderDO::getTaskId, reqVO.getTaskId()) + .likeIfPresent(OrderDO::getTaskId, reqVO.getTaskId()) .eqIfPresent(OrderDO::getFromDirection, reqVO.getFromDirection()) .eqIfPresent(OrderDO::getFromColumn, reqVO.getFromColumn()) .eqIfPresent(OrderDO::getFromRow, reqVO.getFromRow()) diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/Hik3D/HikFlaskApiService.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/Hik3D/HikFlaskApiService.java index 666b873..1f487d5 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/Hik3D/HikFlaskApiService.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/Hik3D/HikFlaskApiService.java @@ -100,6 +100,7 @@ public class HikFlaskApiService implements ScanService { System.out.println("Unexpected code " + response); String responseBody = Objects.requireNonNull(response.body()).string(); + log.info("picComputeAll:{}",responseBody); return gson.fromJson(responseBody, HikPythonEntity.class); }catch (Exception e){ log.error("Error occurred while calling /api/picComputeAll", e); @@ -146,12 +147,19 @@ public class HikFlaskApiService implements ScanService { try { scanData.setCode("缺件"); + HikPythonEntity hikPythonEntity = picComputeAll(stockDO.getWmsItemCode(),urlPath); - System.out.println(hikPythonEntity); + for (int i = 0; i < 4; i++){ + if (hikPythonEntity.getColors()==null|| hikPythonEntity.getColors().size()==0){ + hikPythonEntity = picComputeAll(stockDO.getWmsItemCode(),urlPath); + } + } + log.info("hik3d拍照结果:"+hikPythonEntity.toString()); if (hikPythonEntity != null && !hikPythonEntity.getStatus().equals("ERROR")){ if (hikPythonEntity.getLack()!= null && hikPythonEntity.getLack()) { }else { - scanData.setCode("正常"); + scanData.setCode( "正常"); + log.info("taskId:"+stockDO.getTaskId()+" hik3d拍照结果:"+hikPythonEntity.toString()); } if (hikPythonEntity.getColors() != null && hikPythonEntity.getColors().size() > 0) { for (String color:hikPythonEntity.getColors()){ diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/checklog/CheckLogServiceImpl.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/checklog/CheckLogServiceImpl.java index f41491a..5807f36 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/checklog/CheckLogServiceImpl.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/checklog/CheckLogServiceImpl.java @@ -10,6 +10,8 @@ import cn.iocoder.yudao.module.camera.dal.dataobject.street.StreetDO; import cn.iocoder.yudao.module.camera.dal.mysql.checklog.CheckLogMapper; import cn.iocoder.yudao.module.camera.service.resources.URLResourcesService; import cn.iocoder.yudao.module.camera.service.street.StreetService; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; +import cn.iocoder.yudao.module.system.service.dict.DictDataService; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import de.danielbechler.util.Strings; @@ -81,11 +83,14 @@ public class CheckLogServiceImpl extends ServiceImpl .le(Strings.hasText(endTime),"create_time",endTime)); } + @Resource + DictDataService dictDataService; @Resource private URLResourcesService urlResourcesService; @Override public PageResult getCheckLogPage(CheckLogPageReqVO pageReqVO) { List streetDOS = streetService.list(); + Map dict = dictDataService.getDictValueMap("check_status"); PageResult checkLogDOPageResult = checkLogMapper.selectPage(pageReqVO); Map streetMap = streetDOS.stream().collect(Collectors.toMap(StreetDO::getId,StreetDO::getName)); @@ -93,6 +98,7 @@ public class CheckLogServiceImpl extends ServiceImpl for (CheckLogDO checkLogDO : checkLogDOPageResult.getList()){ List urlResources = urlResourcesService.list(new QueryWrapper().eq("uuid", checkLogDO.getPic())); checkLogDO.setUrlResources(urlResources); + checkLogDO.setStatusName(dict.get(checkLogDO.getStatus().toString()).getLabel()); checkLogDO.setStreetName(streetMap.get(checkLogDO.getStreetId())); } return checkLogDOPageResult; diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/order/OrderServiceImpl.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/order/OrderServiceImpl.java index f721d24..e43c661 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/order/OrderServiceImpl.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/order/OrderServiceImpl.java @@ -147,10 +147,12 @@ public class OrderServiceImpl implements OrderService { Map stocksMap = orderDOS.stream().collect(Collectors.groupingBy(OrderDO::getSrmNumber,Collectors.counting())); List data = new ArrayList<>(); xAxis.setData(new ArrayList<>()); + int max = 0; // 遍历街道列表,为X轴添加类别数据,并收集对应的数据到数据序列中 for (StreetDO street : streets) { xAxis.getData().add(street.getName()); data.add(stocksMap.get(street.getPlcId())); + } series.setData(data); seriesList.add(series); @@ -162,6 +164,7 @@ public class OrderServiceImpl implements OrderService { // 将数据序列添加到系列列表中 eChartsOption.setSeries(seriesList); +// eChartsOption.getYAxis().setSplitNumber(eChartsOption.setsSplitNumber()); return eChartsOption; } @@ -216,6 +219,7 @@ public class OrderServiceImpl implements OrderService { legend.setData(legendDate); eChartsOption.setLegend(legend); eChartsOption.setSeries(seriesList); +// yAxis.setSplitNumber(eChartsOption.setsSplitNumber()); return eChartsOption; } diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/plc/PLCServiceImpl.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/plc/PLCServiceImpl.java index b49cdda..0c3870e 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/plc/PLCServiceImpl.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/plc/PLCServiceImpl.java @@ -49,7 +49,6 @@ import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; import java.util.UUID; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -97,10 +96,15 @@ public class PLCServiceImpl implements PLCService { public void gyrateCameraByCode(CameraDO cameraDO, String code) { - Integer ptzId = cameraIoService.getOne(new QueryWrapper() + CameraIoDO cameraIoDO = cameraIoService.getOne(new QueryWrapper() .eq("camera_Id", cameraDO.getId()) .eq("code", code) - .last(" limit 1")).getPtzId(); + .last(" limit 1")); + if (cameraIoDO == null){ + log.error("ptz not found in sql,code:{},cameraId:{}", code, cameraDO.getId()); + return; + } + Integer ptzId = cameraIoDO.getPtzId(); if (ptzId != null && ptzId >= 0) { @@ -288,6 +292,7 @@ public class PLCServiceImpl implements PLCService { String pathSrc = PathUtil.createFileName(dataInfoData, street, picCmd, ".jpg"); pathSrc = cameraCapture(camera, true, pathSrc, dictDataService.getDictDataList("camera_conf")); + urlResourcesService.save(URLResourcesDo.builder().url(saveApiPath + pathSrc).uuid(order.getUuid()).type("1").little("球机拍照").build()); order.setPics(Strings.hasText(order.getPics()) ? order.getPics() + ";" + saveApiPath + pathSrc : saveApiPath + pathSrc); orderMapper.updateById(order); } @@ -301,7 +306,6 @@ public class PLCServiceImpl implements PLCService { ScanServiceFactory scanServiceFactory; - ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); // 设置线程最大执行时间,单位为毫秒 long timeout = 5000; @@ -367,8 +371,13 @@ public class PLCServiceImpl implements PLCService { stock.setCheckPic(uuid); //拍照记录 String pathSrc = PathUtil.createFileName(dataInfo, street, "E1", ".jpg"); - CameraDO camera = cameraService.getById(dataInfo.getFromDirection() == 1 ? street.getCamera1Id() : street.getCamera2Id()); - pathSrc = cameraCapture(camera, false, pathSrc, dictDataService.getDictDataList("camera_conf")); +// 旋转预置点位 + +// 判断巷道有几个相机 如果只有一个,则调用一个,如果是两个,先判断是否有对拍设置决定那个相机拍摄 + CameraDO camera = getCameraByLeftRight(street, stock.getDirection()); +// 调用预置点位 开始则调用E1 + gyrateCameraByCode(camera, "E1"); + pathSrc = cameraCapture(camera, true, pathSrc, dictDataService.getDictDataList("camera_conf")); urlResourcesService.save(URLResourcesDo.builder().url(saveApiPath + pathSrc).uuid(uuid).type("1").little("球机拍照").build()); // 先记录正常未盘点情况 @@ -397,8 +406,19 @@ public class PLCServiceImpl implements PLCService { StockDO stock = stockService.getOne( new QueryWrapper() .eq("task_id", taskId) + .last("limit 1") ); + if (stock == null) { + log.error("taskId:{}不存在", taskId); + return; + } StreetDO street = streetService.getById(stock.getStreetId()); +// 旋转预置点位 + +// 判断巷道有几个相机 如果只有一个,则调用一个,如果是两个,先判断是否有对拍设置决定那个相机拍摄 + CameraDO camera = getCameraByLeftRight(street, stock.getDirection()); +// 调用预置点位 + gyrateCameraByCode(camera, cmd); for (String scanType : scanTypes) { DictDataDO dictDataDO = dictDataList.get(scanType); ScanData scanData = scanServiceFactory.scan(dictDataDO.getValue(), street, stock); @@ -436,6 +456,8 @@ public class PLCServiceImpl implements PLCService { StockDO stock = stockService.getOne( new QueryWrapper() .eq("task_id", taskId) + .orderByDesc("export_time") + .last("limit 1") ); if (stock == null) { log.error("taskId:{}不存在", taskId); @@ -541,6 +563,9 @@ public class PLCServiceImpl implements PLCService { if (street != null) { OrderDO order = new OrderDO(); + + String uuid = UUID.randomUUID().toString(); + order.setUuid(uuid); order.setTaskId(kescEntity.getTaskId()); order.setCreateTime(LocalDateTime.now()); @@ -566,11 +591,9 @@ public class PLCServiceImpl implements PLCService { String path = zLMediaKitService.startRecord("live", street.getCamera1Id().toString()); // String path = cameraVideo(street.getCamera1Id(), pathSrc, order.getCreateTime(), endDownLoadTime, dictDataList); - order.setVideoPath1(path); } if (street.getCamera2Id() != null) { String path = zLMediaKitService.startRecord("live", street.getCamera2Id().toString()); - order.setVideoPath2(path); } orderMapper.insert(order); @@ -611,13 +634,26 @@ public class PLCServiceImpl implements PLCService { if (street.getCamera1Id() != null) { for (int i = 0; i < 5; i++) { - zLMediaKitService.stopRecord("live", street.getCamera1Id().toString()); + String path = zLMediaKitService.stopRecord("live", street.getCamera1Id().toString(),order.getStartTime()); + if (!path.equals("")){ + urlResourcesService.save(URLResourcesDo.builder().url(path).uuid(order.getUuid()).type("2").little("随行视频").build()); + order.setVideoPath1(path); + break; + } } // String path = cameraVideo(street.getCamera1Id(), pathSrc, order.getCreateTime(), endDownLoadTime, dictDataList); } if (street.getCamera2Id() != null) { - zLMediaKitService.stopRecord("live", street.getCamera2Id().toString()); + for (int i = 0; i < 5; i++) { + + String path = zLMediaKitService.stopRecord("live", street.getCamera2Id().toString(),order.getStartTime()); + if (!path.equals("")){ + urlResourcesService.save(URLResourcesDo.builder().url(path).uuid(order.getUuid()).type("2").little("随行视频").build()); + order.setVideoPath2(path); + break; + } + } } diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/rfid/RfidService.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/rfid/RfidService.java index 9694c01..d8493a8 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/rfid/RfidService.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/rfid/RfidService.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.camera.controller.admin.rfid.vo.RfidPageReqVO; import cn.iocoder.yudao.module.camera.controller.admin.rfid.vo.RfidSaveReqVO; import cn.iocoder.yudao.module.camera.dal.dataobject.rfid.RfidDO; +import com.baomidou.mybatisplus.extension.service.IService; import jakarta.validation.Valid; /** @@ -11,7 +12,7 @@ import jakarta.validation.Valid; * * @author 芋道源码 */ -public interface RfidService { +public interface RfidService extends IService< RfidDO> { /** * 创建RFID diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/rfid/RfidServiceImpl.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/rfid/RfidServiceImpl.java index a5dbf91..604e4cb 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/rfid/RfidServiceImpl.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/rfid/RfidServiceImpl.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.camera.controller.admin.rfid.vo.RfidPageReqVO; import cn.iocoder.yudao.module.camera.controller.admin.rfid.vo.RfidSaveReqVO; import cn.iocoder.yudao.module.camera.dal.dataobject.rfid.RfidDO; import cn.iocoder.yudao.module.camera.dal.mysql.rfid.RfidMapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -20,8 +21,7 @@ import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.RFID_NOT_EX */ @Service @Validated -public class RfidServiceImpl implements RfidService { - +public class RfidServiceImpl extends ServiceImpl implements RfidService { @Resource private RfidMapper rfidMapper; diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/stock/StockService.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/stock/StockService.java index eb9921f..badbacf 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/stock/StockService.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/stock/StockService.java @@ -9,6 +9,9 @@ import cn.iocoder.yudao.module.camera.dal.entity.echarts.EChartsOption; import com.baomidou.mybatisplus.extension.service.IService; import jakarta.validation.Valid; +import java.util.List; +import java.util.Map; + /** * 盘点状态 Service 接口 * @@ -57,4 +60,5 @@ public interface StockService extends IService< StockDO> { StockStreetList getStreetList(StockStreetList stockSaveReqVO); EChartsOption laneInventoryStatistics(); + List> laneInventoryStatisticsPie(); } \ No newline at end of file diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/stock/StockServiceImpl.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/stock/StockServiceImpl.java index 69ae7a8..cecd873 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/stock/StockServiceImpl.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/stock/StockServiceImpl.java @@ -5,10 +5,14 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.camera.controller.admin.stock.vo.StockPageReqVO; import cn.iocoder.yudao.module.camera.controller.admin.stock.vo.StockSaveReqVO; import cn.iocoder.yudao.module.camera.controller.admin.stock.vo.StockStreetList; +import cn.iocoder.yudao.module.camera.dal.dataobject.checklog.CheckLogDO; import cn.iocoder.yudao.module.camera.dal.dataobject.stock.StockDO; import cn.iocoder.yudao.module.camera.dal.dataobject.street.StreetDO; -import cn.iocoder.yudao.module.camera.dal.entity.echarts.*; +import cn.iocoder.yudao.module.camera.dal.entity.echarts.Axis; +import cn.iocoder.yudao.module.camera.dal.entity.echarts.EChartsOption; +import cn.iocoder.yudao.module.camera.dal.entity.echarts.Series; import cn.iocoder.yudao.module.camera.dal.mysql.stock.StockMapper; +import cn.iocoder.yudao.module.camera.service.checklog.CheckLogService; import cn.iocoder.yudao.module.camera.service.street.StreetService; import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; import cn.iocoder.yudao.module.system.service.dict.DictDataService; @@ -19,6 +23,7 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -98,6 +103,8 @@ public class StockServiceImpl extends ServiceImpl implement return stockStreetList; } + @Resource + CheckLogService checkLogService; @Resource private DictDataService dictDataService; @Override @@ -106,8 +113,9 @@ public class StockServiceImpl extends ServiceImpl implement eChartsOption.setXAxis(new Axis()); List bars = new ArrayList<>(); List streets = streetService.list(); - List stocks = list(); - Map> stocksMap = stocks.stream().collect(Collectors.groupingBy(StockDO::getStatus,Collectors.groupingBy(StockDO::getStreetId,Collectors.counting()))); + List stocks = checkLogService.list(); + Map> stocksMap = stocks.stream() + .collect(Collectors.groupingBy(k ->k.getStatus().toString(),Collectors.groupingBy(CheckLogDO::getStreetId,Collectors.counting()))); Map dictDataMap = dictDataService.getDictDataList("check_status"); Axis yAxis = new Axis(); yAxis.setType("category"); @@ -116,6 +124,7 @@ public class StockServiceImpl extends ServiceImpl implement yAxis.getData().add(street.getName()); } eChartsOption.setYAxis(yAxis); + int max = 0; for (String entry : dictDataMap.keySet()) { Series bar = new Series(); bar.setName(entry); @@ -127,10 +136,8 @@ public class StockServiceImpl extends ServiceImpl implement // 盘点异常 case 1 -> bar.setColor("#ec6464"); - // 盘点正确 case 2 -> bar.setColor("#94cc74"); - // 人工核对正确 case 3 -> bar.setColor("#5474c4"); default -> bar.setColor(""); @@ -140,14 +147,37 @@ public class StockServiceImpl extends ServiceImpl implement if (stocksMap.get(dictDataMap.get(entry).getValue())==null){ values.add(null); }else values.add(stocksMap.get(dictDataMap.get(entry).getValue()).get(street.getId())); + } bar.setData(values); bars.add(bar); } eChartsOption.setSeries(bars); +// eChartsOption.getXAxis().setSplitNumber(eChartsOption.setsSplitNumber()); + return eChartsOption; } + @Override + public List> laneInventoryStatisticsPie() { + EChartsOption eChartsOption = new EChartsOption(); + eChartsOption.setXAxis(new Axis()); + List bars = new ArrayList<>(); + List streets = streetService.list(); + List stocks = checkLogService.list(); + Map stocksMap = stocks.stream() + .collect(Collectors.groupingBy(k ->k.getStatus().toString(),(Collectors.counting()))); + Map dictDataMap = dictDataService.getDictValueMap("check_status"); + List> list = new ArrayList<>(); + for (String key :stocksMap.keySet()){ + Map map = new HashMap<>(); + map.put("name",dictDataMap.get(key).getLabel()); + map.put("value",stocksMap.get(key).toString()); + list.add(map); + } + + return list; + } } \ No newline at end of file diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/streamingMedia/ZLMediaKitService.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/streamingMedia/ZLMediaKitService.java index d792106..4ef9ff8 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/streamingMedia/ZLMediaKitService.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/streamingMedia/ZLMediaKitService.java @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.camera.service.streamingMedia; import cn.iocoder.yudao.module.camera.dal.dataobject.camera.CameraDO; -import cn.iocoder.yudao.module.camera.dal.zlm.RtspSessionResponse; +import java.time.LocalDateTime; import java.util.List; public interface ZLMediaKitService { String startRecord(String app, String stream); - RtspSessionResponse stopRecord(String app, String stream); + String stopRecord(String app, String stream, LocalDateTime path); void addRtspProxy(CameraDO camera, String zlmApiUrl, String zlmApiSecret); void pic(CameraDO camera, String zlmApiUrl); void zlmConf( List list); diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/streamingMedia/ZLMediaKitServiceImpl.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/streamingMedia/ZLMediaKitServiceImpl.java index 6b1e26e..80deabe 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/streamingMedia/ZLMediaKitServiceImpl.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/streamingMedia/ZLMediaKitServiceImpl.java @@ -19,7 +19,10 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.Instant; import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; @@ -43,15 +46,17 @@ public class ZLMediaKitServiceImpl implements ZLMediaKitService{ @Override - public String - startRecord(String app, String cameraId) { + public String startRecord(String app, String cameraId) { String zlmApiSecret = dictDataService.parseDictData("ZLMediaKit_conf", "secret").getValue(); + + String mp4SavePath = dictDataService.parseDictData("ZLMediaKit_conf", "mp4SavePath").getValue(); Map addParams = new HashMap<>(); addParams.put("secret", zlmApiSecret); addParams.put("type", "1");//0为hls,1为mp4 addParams.put("vhost", "__defaultVhost__"); addParams.put("app", "live"); + addParams.put("customized_path", mp4SavePath); addParams.put("stream","camera"+ cameraId); // addParams.put("url", CameraChannel.getRtspUrl(camera.getIp(), camera.getRtspPort(), camera.getChannel(), camera.getUser(), camera.getPassword(),camera.getType())); // sendHttp(addParams, "startRecord"); @@ -67,7 +72,7 @@ public class ZLMediaKitServiceImpl implements ZLMediaKitService{ ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); Runnable task = () -> { - stopRecord(app,cameraId); + stopRecord(app,cameraId,LocalDateTime.now()); }; @@ -77,6 +82,62 @@ public class ZLMediaKitServiceImpl implements ZLMediaKitService{ return path; } + // 检查指定文件夹中最新的文件名,且文件修改时间在指定时间之后 + public String getLatestFileNameAfterTime(String directoryPath, LocalDateTime afterTime) { + try { + Path directory = Paths.get(directoryPath); + + // 将LocalDateTime转换为Instant用于比较 + Instant afterInstant = afterTime.atZone(ZoneId.systemDefault()).toInstant(); + + // 获取目录下所有文件 + try (Stream paths = Files.list(directory)) { + return paths + .filter(Files::isRegularFile) // 只考虑文件 + .filter(path -> { + try { + // 获取文件最后修改时间 + Instant lastModified = Files.getLastModifiedTime(path).toInstant(); + // 筛选修改时间在指定时间之后的文件 + return lastModified.isAfter(afterInstant); + } catch (IOException e) { + log.error("获取文件修改时间失败: {}", e.getMessage()); + return false; + } + }) + .max((path1, path2) -> { + try { + // 按最后修改时间比较 + Instant time1 = Files.getLastModifiedTime(path1).toInstant(); + Instant time2 = Files.getLastModifiedTime(path2).toInstant(); + return time1.compareTo(time2); + } catch (IOException e) { + log.error("比较文件修改时间失败: {}", e.getMessage()); + return 0; + } + }) + .map(path -> path.getFileName().toString()) // 提取文件名 + .orElse(""); // 如果没有符合条件的文件,返回空字符串 + } + } catch (IOException e) { + log.error("读取目录失败: {}", e.getMessage()); + return ""; + } + } + // 检查摄像头目录中最新的文件名,且文件修改时间在指定时间之后 + public String getLatestCameraFileNameAfterTime(String app, String cameraId, LocalDateTime afterTime) { + String mp4SavePath = dictDataService.parseDictData("ZLMediaKit_conf", "mp4SavePath").getValue(); + + String mp4SaveApi = dictDataService.parseDictData("ZLMediaKit_conf", "mp4SaveApi").getValue(); + // 获取当前日期 + LocalDate currentDate = LocalDate.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + String formattedDate = currentDate.format(formatter); + + String directoryPath = mp4SavePath + "record/" + app + "/" + cameraId + "/" + formattedDate; + String mp4SaveApiPath = mp4SaveApi + "record/" + app + "/" + cameraId + "/" + formattedDate + "/"; + return mp4SaveApiPath + getLatestFileNameAfterTime(directoryPath, afterTime); + } // 新增方法:检查指定文件夹下是否有以.为开头的文件 public String checkHiddenFilesInDirectory(String app, String cameraId) { @@ -113,6 +174,43 @@ public class ZLMediaKitServiceImpl implements ZLMediaKitService{ return ""; } + // 新增方法:检查指定文件夹下是否有以.为开头的文件 + public String checkAndRenameFile(String app, String cameraId,String filePath) { + + String mp4SavePath = dictDataService.parseDictData("ZLMediaKit_conf", "mp4SavePath").getValue(); +// mp4SaveApi + String mp4SaveApi = dictDataService.parseDictData("ZLMediaKit_conf", "mp4SaveApi").getValue(); + // 获取当前日期 + LocalDate currentDate = LocalDate.now(); + + // 定义日期格式 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + // 格式化日期 + String formattedDate = currentDate.format(formatter); + // 构建完整路径 + String fullPath = mp4SavePath + "record/" + app + "/camera" + cameraId + "/" + formattedDate + "/." + filePath; + String renamedPath = mp4SavePath + "record/" + app + "/camera" + cameraId + "/" + formattedDate + "/" + filePath; + + // 构建访问路径 + String accessPath = mp4SaveApi + "record/" + app + "/camera" + cameraId + "/" + formattedDate + "/" + filePath; + + try { + Path originalFilePath = Paths.get(fullPath); + // 检查带点的文件是否存在 + if (Files.exists(originalFilePath)) { + // 重命名为去掉点的文件名 + Path newFilePath = Paths.get(renamedPath); + Files.move(originalFilePath, newFilePath); + return accessPath; + } + } catch (IOException e) { + log.error("文件重命名失败: {}", e.getMessage()); + } + + return ""; + } + /** * 调用ZLMediaKit的getSnap接口获取截图并保存 * @@ -155,9 +253,6 @@ public class ZLMediaKitServiceImpl implements ZLMediaKitService{ log.info("截图保存成功: {}", savePath); - // 返回访问路径(根据你的需求调整) - String mp4SaveApi = dictDataService.parseDictData("ZLMediaKit_conf", "mp4SaveApi").getValue(); - } else { log.error("获取截图失败,HTTP状态码: {}", response.code()); @@ -179,7 +274,7 @@ public class ZLMediaKitServiceImpl implements ZLMediaKitService{ return input; } @Override - public RtspSessionResponse stopRecord(String app, String stream) { + public String stopRecord(String app, String stream ,LocalDateTime time) { String zlmApiSecret = dictDataService.parseDictData("ZLMediaKit_conf", "secret").getValue(); Map addParams = new HashMap<>(); @@ -190,9 +285,15 @@ public class ZLMediaKitServiceImpl implements ZLMediaKitService{ addParams.put("stream","camera"+ stream); // addParams.put("url", CameraChannel.getRtspUrl(camera.getIp(), camera.getRtspPort(), camera.getChannel(), camera.getUser(), camera.getPassword(),camera.getType())); RtspSessionResponse rtspSessionResponse = sendHttp(addParams, "stopRecord"); - return rtspSessionResponse; + if (rtspSessionResponse!=null && rtspSessionResponse.isResult()){ + String fileName = getLatestCameraFileNameAfterTime(app, "camera"+stream, time.minusSeconds(10)); + return fileName; + }else { + return ""; + } } + @Override public void zlmConf( List list) { diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/util/DiskSpaceManager.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/util/DiskSpaceManager.java new file mode 100644 index 0000000..159dfff --- /dev/null +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/util/DiskSpaceManager.java @@ -0,0 +1,248 @@ +package cn.iocoder.yudao.module.camera.util; + +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; +import cn.iocoder.yudao.module.system.service.dict.DictDataService; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Comparator; +import java.util.Map; + +@Slf4j +@Component +public class DiskSpaceManager { + + // 日期格式化器 + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + + /** + * 检查磁盘空间 查看是否删除 + * @param driveLetter 驱动器字母 + * @param diskUsageThreshold 磁盘使用阈值 + * @param minFreeSpace 最小免费空间 + * @return 是否删除 + */ + public static boolean checkDiskSpace(String driveLetter,double diskUsageThreshold,long minFreeSpace) { + try { + // 方法2: 使用FileStore类(更精确) + Path path = Paths.get(driveLetter + "/"); + FileStore store = Files.getFileStore(path); + + long totalSpace2 = store.getTotalSpace(); + long usableSpace2 = store.getUsableSpace(); + long unallocatedSpace = store.getUnallocatedSpace(); + minFreeSpace = minFreeSpace * 1024 * 1024 * 1024; + + log.info("驱动器: " + driveLetter); + log.info("总空间: " + formatBytes(totalSpace2)); + log.info("可用空间: " + formatBytes(usableSpace2)); + log.info("未分配空间: " + formatBytes(unallocatedSpace)); + log.info("已用空间: " + formatBytes(totalSpace2 - usableSpace2)); + log.info("使用率: " + String.format("%.2f%%", (double)(totalSpace2 - usableSpace2) / totalSpace2 * 100)); + if ((double)(totalSpace2 - usableSpace2)/ totalSpace2> diskUsageThreshold || minFreeSpace>unallocatedSpace) { + return true; + }else return false; + + } catch (Exception e) { + System.err.println("查询磁盘空间时发生错误: " + e.getMessage()); + e.printStackTrace(); + } + return false; + } + + /** + * 格式化字节大小为人类可读的格式 + * @param bytes 字节数 + * @return 格式化后的字符串 + */ + private static String formatBytes(long bytes) { + if (bytes < 1024) return bytes + " B"; + if (bytes < 1024 * 1024) return String.format("%.2f KB", bytes / 1024.0); + if (bytes < 1024 * 1024 * 1024) return String.format("%.2f MB", bytes / (1024.0 * 1024)); + if (bytes < 1024L * 1024 * 1024 * 1024) return String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024)); + return String.format("%.2f TB", bytes / (1024.0 * 1024 * 1024 * 1024)); + } + + + @Resource + DictDataService dictDataService; + + /** + * 定时检查并清理磁盘空间 + */ + @Scheduled(cron = "0 3 0 * * ? " ) // 每5分钟检查一次 + @PostConstruct + public void checkAndCleanSpace() { + Map dictDataMap = dictDataService.getDictDataList("base_conf"); + // 检查是否需要清理空间 + for (int i = Integer.parseInt(dictDataMap.get("max_retention_days").getValue()); i > Integer.parseInt(dictDataMap.get("min_retention_days").getValue()); i--){ + + deleteFoldersByDaysAgo(i,dictDataMap); + if (!(checkDiskSpace(dictDataMap.get("base_path").getValue() + ,Double.parseDouble(dictDataMap.get("disk_usage_threshold").getValue()) + ,Long.parseLong(dictDataMap.get("min_free_space").getValue())))){ + break; + } + } + + }// ... existing code ... + + + /** + * 根据天数删除指定日期的文件夹 + * @param daysAgo 天数(例如:30表示删除30天前的文件夹) + * @return 删除的文件夹数量 + */ + public int deleteFoldersByDaysAgo(int daysAgo,Map dictDataMap) { + LocalDate targetDate = LocalDate.now().minusDays(daysAgo); + String targetDateStr = targetDate.format(DATE_FORMATTER); + String[] monitoredFolders = dictDataMap.get("monitored_folders").getValue().split(","); + + + log.info("开始删除 {} 天前的文件夹,日期: {}", daysAgo, targetDateStr); + + int deletedCount = 0; + + + for (String monitoredFolder : monitoredFolders) { + try { + File parentFolder = new File(monitoredFolder); + if (!parentFolder.exists() || !parentFolder.isDirectory()) { + continue; + } + + // 递归查找并删除所有匹配日期的文件夹 + deletedCount += deleteFoldersRecursively(parentFolder, targetDateStr); + + } catch (Exception e) { + log.error("处理监控文件夹时发生错误: {}", monitoredFolder, e); + } + } + + log.info("删除 {} 天前文件夹完成,共删除 {} 个文件夹", daysAgo, deletedCount); + return deletedCount; + } + + +// ... existing code ... + + /** + * 递归删除指定目录下所有匹配日期名称的文件夹 + * @param parentFolder 父目录 + * @param targetDateStr 目标日期字符串 + * @return 删除的文件夹数量 + */ + private int deleteFoldersRecursively(File parentFolder, String targetDateStr) { + int deletedCount = 0; + + File[] files = parentFolder.listFiles(); + if (files == null) { + return deletedCount; + } + + for (File file : files) { + if (file.isDirectory()) { + // 如果文件夹名称匹配目标日期,则删除 + if (file.getName().equals(targetDateStr)) { + try { + long folderSize = getFolderSize(file.getAbsolutePath()); + fastDeleteFolder(file.getAbsolutePath()); + deletedCount++; + log.info("快速删除文件夹: {}, 日期: {}, 释放空间: {} MB", + file.getAbsolutePath(), + targetDateStr, + folderSize / (1024.0 * 1024)); + } catch (IOException e) { + log.error("快速删除文件夹失败: {}", file.getAbsolutePath(), e); + } + } + // 递归处理子目录 + deletedCount += deleteFoldersRecursively(file, targetDateStr); + } + } + + return deletedCount; + } + /** + * 快速删除文件夹及其内容 + * @param folderPath 文件夹路径 + * @throws IOException IO异常 + */ + private void fastDeleteFolder(String folderPath) throws IOException { + Path folder = Paths.get(folderPath); + if (!Files.exists(folder)) { + return; + } + + // 使用并行流加速删除过程 + try (java.util.stream.Stream walk = Files.walk(folder)) { + walk.parallel() + .sorted(Comparator.reverseOrder()) // 先删除文件,再删除目录 + .forEach(path -> { + try { + Files.deleteIfExists(path); + } catch (IOException e) { + log.warn("删除文件/文件夹失败: {}", path, e); + } + }); + } + } + + /** + * 生成指定天数前的日期字符串 + * @param daysAgo 天数 + * @return yyyy-MM-dd格式的日期字符串 + */ + public String generateDateStr(int daysAgo) { + LocalDate targetDate = LocalDate.now().minusDays(daysAgo); + return targetDate.format(DATE_FORMATTER); + } + + /** + * 删除指定日期的文件夹 + * @param dateStr 日期字符串 (yyyy-MM-dd格式) + * @return 删除的文件夹数量 + */ + + /** + * 获取文件夹大小 + * @param folderPath 文件夹路径 + * @return 文件夹大小(字节) + * @throws IOException IO异常 + */ + private long getFolderSize(String folderPath) throws IOException { + Path folder = Paths.get(folderPath); + if (!Files.exists(folder)) { + return 0; + } + + final long[] size = {0}; + Files.walkFileTree(folder, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + size[0] += attrs.size(); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + }); + + return size[0]; + } + +// ... existing code ... + +} diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/util/NetworkUtil.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/util/NetworkUtil.java new file mode 100644 index 0000000..2d9acf5 --- /dev/null +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/util/NetworkUtil.java @@ -0,0 +1,99 @@ +package cn.iocoder.yudao.module.camera.util; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +@Data +@AllArgsConstructor +@NoArgsConstructor +public class NetworkUtil { + private String ip; + private Integer port; + private String type; + private boolean isConnect = false; + + /** + * 测试网络连接 + * @param ip IP地址 + * @param port 端口号,如果为null则只进行ping测试 + * @return 是否能通 + */ + public static NetworkUtil isReachable(String ip, Integer port,String type){ + NetworkUtil networkUtil = isReachable(ip, port,2000); + networkUtil.setType(type); + return networkUtil; + } + /** + * 测试网络连接 + * @param ip IP地址 + * @param port 端口号,如果为null则只进行ping测试 + * @param timeout 超时时间(毫秒) + * @return 是否能通 + */ + public static NetworkUtil isReachable(String ip, Integer port, int timeout) { + NetworkUtil networkUtil = new NetworkUtil(); + networkUtil.ip = ip; + networkUtil.port = port; + boolean isReachable = false; + if (ip == null || ip.isEmpty()) { + isReachable = false; + } + + try { + // 如果端口为空,只进行ping测试 + if (port == null) { + InetAddress address = InetAddress.getByName(ip); + isReachable = address.isReachable(timeout); + } else { + // 如果端口不为空,进行端口连通性测试 + try (Socket socket = new Socket()) { + socket.connect(new InetSocketAddress(ip, port), timeout); + isReachable = true; + } catch (Exception e) { + isReachable = false; + } + } + } catch (Exception e) { + isReachable = false; + } + networkUtil.setConnect(isReachable); + return networkUtil; + } + + + /** + * 测试IP是否能ping通 + * @param ip IP地址 + * @param timeout 超时时间(毫秒) + * @return 是否能ping通 + */ + public static boolean isPingable(String ip, int timeout) { + try { + InetAddress address = InetAddress.getByName(ip); + return address.isReachable(timeout); + } catch (IOException e) { + return false; + } + } + + /** + * 测试端口是否开放 + * @param ip IP地址 + * @param port 端口号 + * @param timeout 超时时间(毫秒) + * @return 端口是否开放 + */ + public static boolean isPortOpen(String ip, int port, int timeout) { + try (Socket socket = new Socket()) { + socket.connect(new InetSocketAddress(ip, port), timeout); + return true; + } catch (IOException e) { + return false; + } + } +} diff --git a/yudao-server/src/main/resources/application-dev.yaml b/yudao-server/src/main/resources/application-dev.yaml index e2038fc..989cad1 100644 --- a/yudao-server/src/main/resources/application-dev.yaml +++ b/yudao-server/src/main/resources/application-dev.yaml @@ -55,6 +55,14 @@ spring: # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: redis: + lettuce: + pool: + max-active: 100 # 增加最大连接数 + max-idle: 50 # 增加最大空闲连接数 + min-idle: 10 # 增加最小空闲连接数 + max-wait: 5000ms # 增加获取连接的最大等待时间 + shutdown-timeout: 1000ms # 增加关闭超时时间 + timeout: 5000ms # 增加连接超时时间 host: 127.0.0.1 # 地址 port: 6379 # 端口 database: 1 # 数据库索引 diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index afd0a0d..1b847eb 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -1,5 +1,5 @@ server: - port: 48080 + port: 48081 --- #################### 数据库相关配置 #################### @@ -46,8 +46,8 @@ spring: primary: master datasource: master: - name: sy-logistics - url: jdbc:mysql://192.168.2.162:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + name: sy + url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://192.168.2.162:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.master.name} # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 @@ -55,7 +55,7 @@ spring: # url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 username: root - password: Leaper@123 + password: root # username: sa # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # username: SYSDBA # DM 连接的示例 @@ -76,6 +76,14 @@ spring: # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: redis: + lettuce: + pool: + max-active: 100 # 增加最大连接数 + max-idle: 50 # 增加最大空闲连接数 + min-idle: 10 # 增加最小空闲连接数 + max-wait: 5000ms # 增加获取连接的最大等待时间 + shutdown-timeout: 1000ms # 增加关闭超时时间 + timeout: 5000ms # 增加连接超时时间 host: 127.0.0.1 # 地址 port: 6379 # 端口 database: 0 # 数据库索引