diff --git a/yudao-module-camera/yudao-module-camera-biz/pom.xml b/yudao-module-camera/yudao-module-camera-biz/pom.xml index a45d24c..91368d9 100644 --- a/yudao-module-camera/yudao-module-camera-biz/pom.xml +++ b/yudao-module-camera/yudao-module-camera-biz/pom.xml @@ -30,11 +30,14 @@ - com.sun.jna + net.java.dev.jna jna - 1.0 - system - ${project.basedir}/src/main/resources/libs/jna.jar + 5.13.0 + + + net.java.dev.jna + jna-platform + 5.13.0 diff --git a/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/conf/MyInitializer.java b/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/conf/MyInitializer.java index 00a3e60..6deb5b7 100644 --- a/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/conf/MyInitializer.java +++ b/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/conf/MyInitializer.java @@ -27,8 +27,8 @@ import java.util.Objects; public class MyInitializer { - - private final OkHttpClient client = new OkHttpClient(); + @Resource + private OkHttpClient client ; @Resource @@ -43,6 +43,8 @@ public class MyInitializer { myInitializer.checkAndAddRtspProxies(); } + + @Scheduled(fixedRate = 30000) // 每分钟执行一次 public void checkAndAddRtspProxies() { diff --git a/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/cameraio/CameraIoController.java b/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/cameraio/CameraIoController.java index 2299788..e69de29 100644 --- a/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/cameraio/CameraIoController.java +++ b/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/cameraio/CameraIoController.java @@ -1,144 +0,0 @@ -package cn.iocoder.yudao.module.camera.controller.admin.cameraio; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.camera.controller.admin.cameraio.vo.CameraIoPageReqVO; -import cn.iocoder.yudao.module.camera.controller.admin.cameraio.vo.CameraIoRespVO; -import cn.iocoder.yudao.module.camera.controller.admin.cameraio.vo.CameraIoSaveReqVO; -import cn.iocoder.yudao.module.camera.dal.dataobject.camera.CameraDO; -import cn.iocoder.yudao.module.camera.dal.dataobject.cameraio.CameraIoDO; -import cn.iocoder.yudao.module.camera.lib.camera.CameraModFactory; -import cn.iocoder.yudao.module.camera.service.camera.CameraService; -import cn.iocoder.yudao.module.camera.service.cameraio.CameraIoService; -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 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.List; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 相机预置点位") -@RestController -@RequestMapping("/admin-api/camera/camera-io") -@Validated -public class CameraIoController { - - @Resource - private CameraIoService cameraIoService; - - @Resource - private CameraModFactory cameraModFactory; - - - @Resource - private CameraService cameraService; - - @PostMapping("/create") - @Operation(summary = "创建相机预置点位") - @PreAuthorize("@ss.hasPermission('camera:camera-io:create')") - public CommonResult createCameraIo(@Valid @RequestBody CameraIoSaveReqVO createReqVO) { - List list = cameraIoService.list(new QueryWrapper() - .eq("camera_id", createReqVO.getCameraId())); - Integer ptzId = list.stream().map(CameraIoDO::getPtzId).max(Integer::compareTo).orElse(0)+1; - createReqVO.setPtzId(ptzId); - int i= cameraIoService.createCameraIo(createReqVO); - CameraDO cameraIoDO = cameraService.getById(createReqVO.getCameraId()); - cameraModFactory.get(cameraIoDO.getType()).ptz(createReqVO.getPtzId(),createReqVO.getName(),createReqVO.getCameraId()); - - return success(i); - } - - @PostMapping("/callPtz") - @Operation(summary = "调用预置点位") - @PreAuthorize("@ss.hasPermission('camera:camera-io:callPtz')") - public CommonResult callPtz(@Valid @RequestBody CameraIoSaveReqVO createReqVO) { - - CameraDO cameraIoDO = cameraService.getById(createReqVO.getCameraId()); - cameraModFactory.get(cameraIoDO.getType()).toPtz(createReqVO.getPtzId(),createReqVO.getCameraId()); - return success(cameraIoService.createCameraIo(createReqVO)); - } - - /** - * 创建或覆盖相机预置点位 必须穿ptzId - * @param createReqVO - * @return - */ - @PostMapping("/overwritePtz") - @Operation(summary = "创建或覆盖相机预置点位") - @PreAuthorize("@ss.hasPermission('camera:camera-io:overwritePtz')") - public CommonResult overwritePtz(@Valid @RequestBody CameraIoSaveReqVO createReqVO) { - CameraDO cameraIoDO = cameraService.getById(createReqVO.getCameraId()); - cameraModFactory.get(cameraIoDO.getType()).ptz(createReqVO.getPtzId(),createReqVO.getName(),createReqVO.getCameraId()); - return success(cameraIoService.createCameraIo(createReqVO)); - } - - @GetMapping("/list") - @Operation(summary = "获得相机预置点位分页") - @PreAuthorize("@ss.hasPermission('camera:camera-io:query')") - public CommonResult> list(@Valid CameraIoPageReqVO pageReqVO) { - List pageResult = cameraIoService.list(new QueryWrapper().eq("camera_id", pageReqVO.getCameraId()).orderByAsc("id")); - return success(BeanUtils.toBean(pageResult, CameraIoRespVO.class)); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除相机预置点位") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('camera:camera-io:delete')") - public CommonResult deleteCameraIo(@RequestParam("id") Integer id) { - cameraIoService.deleteCameraIo(id); - return success(true); - } - @PutMapping("/update") - @Operation(summary = "更新相机预置点位") - @PreAuthorize("@ss.hasPermission('camera:camera-io:update')") - public CommonResult updateCameraIo(@Valid @RequestBody CameraIoSaveReqVO updateReqVO) { - cameraIoService.updateCameraIo(updateReqVO); - return success(true); - } - - - @GetMapping("/get") - @Operation(summary = "获得相机预置点位") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('camera:camera-io:query')") - public CommonResult getCameraIo(@RequestParam("id") Integer id) { - CameraIoDO cameraIo = cameraIoService.getCameraIo(id); - return success(BeanUtils.toBean(cameraIo, CameraIoRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得相机预置点位分页") - @PreAuthorize("@ss.hasPermission('camera:camera-io:query')") - public CommonResult> getCameraIoPage(@Valid CameraIoPageReqVO pageReqVO) { - PageResult pageResult = cameraIoService.getCameraIoPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, CameraIoRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出相机预置点位 Excel") - @PreAuthorize("@ss.hasPermission('camera:camera-io:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportCameraIoExcel(@Valid CameraIoPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = cameraIoService.getCameraIoPage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "相机预置点位.xls", "数据", CameraIoRespVO.class, - BeanUtils.toBean(list, CameraIoRespVO.class)); - } - -} \ No newline at end of file diff --git a/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/lib/camera/CameraModFactory.java b/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/lib/camera/CameraModFactory.java index 8ccbc35..9a80fca 100644 --- a/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/lib/camera/CameraModFactory.java +++ b/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/lib/camera/CameraModFactory.java @@ -24,6 +24,8 @@ public class CameraModFactory { return hikControlModuleImpl; // 获取 HikControlModuleImpl 单例实例 } else if (type == 0) { return dhControlModuleImpl; // 获取 DhControlModuleImpl 单例实例 + }else if (type == 2) { + return dhControlModuleImpl; // 获取 DhControlModuleImpl 单例实例 } // 默认返回 HikControlModuleImpl return hikControlModuleImpl; diff --git a/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/service/channel/CameraChannel.java b/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/service/channel/CameraChannel.java index 7579dac..278f4b7 100644 --- a/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/service/channel/CameraChannel.java +++ b/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/service/channel/CameraChannel.java @@ -7,9 +7,9 @@ public class CameraChannel { public static String generateHikvisionRTSPUrl(String ip, int port, String channel, String username, String password) { return String.format("rtsp://%s:%s@%s:%d/Streaming/channels/%s", username, password, ip, port, channel); } - +//默认主码流 public static String generateDahuaRTSPUrl(String ip, int port, String channel, String username, String password) { - return String.format("rtsp://%s:%s@%s:%d/cam/realmonitor?channel=%s", username, password, ip, port, channel); + return String.format("rtsp://%s:%s@%s:%d/cam/realmonitor?channel=%s&subtype=0", username, password, ip, port, channel); } public static String getRtspUrl(String ip, int port, String channel, String username, String password, int type) { String rtspUrl = ""; diff --git a/yudao-module-camera/yudao-module-camera-biz/src/test/java/cn/iocoder/yudao/module/camera/service/camera/CameraServiceImplTest.java b/yudao-module-camera/yudao-module-camera-biz/src/test/java/cn/iocoder/yudao/module/camera/service/camera/CameraServiceImplTest.java index 2ce8164..e69de29 100644 --- a/yudao-module-camera/yudao-module-camera-biz/src/test/java/cn/iocoder/yudao/module/camera/service/camera/CameraServiceImplTest.java +++ b/yudao-module-camera/yudao-module-camera-biz/src/test/java/cn/iocoder/yudao/module/camera/service/camera/CameraServiceImplTest.java @@ -1,18 +0,0 @@ -package cn.iocoder.yudao.module.camera.service.camera; - -import cn.iocoder.yudao.module.camera.framework.netty.NettyClient; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -/** - * {@link CameraServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(NettyClient.class) -public class CameraServiceImplTest{ - @Test - public void testCreateCamera_success() { - - } -} \ No newline at end of file diff --git a/yudao-module-logistics/yudao-module-logistics-biz/pom.xml b/yudao-module-logistics/yudao-module-logistics-biz/pom.xml index 4fc80e3..4abe238 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/pom.xml +++ b/yudao-module-logistics/yudao-module-logistics-biz/pom.xml @@ -35,11 +35,14 @@ - com.sun.jna + net.java.dev.jna jna - 1.0 - system - ${project.basedir}/src/main/resources/libs/jna.jar + 5.13.0 + + + net.java.dev.jna + jna-platform + 5.13.0 diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/config/OkHttpClientConfig.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/config/OkHttpClientConfig.java new file mode 100644 index 0000000..e36112e --- /dev/null +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/config/OkHttpClientConfig.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.camera.config; + +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.TimeUnit; + +@Configuration +public class OkHttpClientConfig { + + @Bean + public OkHttpClient okHttpClient() { + return new OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .connectionPool(new ConnectionPool(10, 3, TimeUnit.MINUTES)) + // 可添加拦截器,如日志、Token 添加等 +// .addInterceptor(chain -> { +// Request request = chain.request().newBuilder() +// .addHeader("Authorization", "Bearer your_token") +// .build(); +// return chain.proceed(request); +// }) + .build(); + } +} diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/Hik3D/Hik3DController.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/Hik3D/Hik3DController.java new file mode 100644 index 0000000..a934031 --- /dev/null +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/Hik3D/Hik3DController.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.camera.controller.admin.Hik3D; + +import cn.iocoder.yudao.module.camera.dal.entity.HikPythonEntity; +import cn.iocoder.yudao.module.camera.service.Hik3D.HikFlaskApiService; +import cn.iocoder.yudao.module.camera.service.checklog.CheckLogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; + +@Tag(name = "管理后台 - hik3D识别") +@RestController +@RequestMapping("/logistics/Hik3DController") +@Validated +public class Hik3DController { + @Resource + HikFlaskApiService hikFlaskApiService; + + @Resource + private CheckLogService checkLogService; + + @PostMapping("/updateConfig") + @Operation(summary = "updateConfig") + @ResponseBody + @PermitAll + public HikPythonEntity updateConfig(){ + String baseUrl = "http://127.0.0.1:5000"; + try { + return hikFlaskApiService.updateConfig(baseUrl); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + + @PostMapping("/addTemplate") + @Operation(summary = "updateConfig") + @ResponseBody + @PermitAll + public HikPythonEntity addTemplate(@RequestBody HikPythonEntity hikPythonEntity){ + String baseUrl = "http://127.0.0.1:5000"; + try { + return hikFlaskApiService.addTemplate(hikPythonEntity.getType(), hikPythonEntity,baseUrl); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @PostMapping("/picComputeAll") + @Operation(summary = "picComputeAll") + @ResponseBody + @PermitAll + public HikPythonEntity picComputeAll(@RequestBody HikPythonEntity hikPythonEntity){ + String baseUrl = "http://127.0.0.1:5000"; + try { + return hikFlaskApiService.picComputeAll(hikPythonEntity.getType(),baseUrl); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + + +} \ No newline at end of file diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/cameraio/CameraIoController.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/cameraio/CameraIoController.java index bfaf778..3130037 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/cameraio/CameraIoController.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/cameraio/CameraIoController.java @@ -69,7 +69,7 @@ public class CameraIoController { CameraDO cameraIoDO = cameraService.getById(createReqVO.getCameraId()); cameraModFactory.get(cameraIoDO.getType()).toPtz(createReqVO.getPtzId(),createReqVO.getCameraId()); - return success(cameraIoService.createCameraIo(createReqVO)); + return success(cameraIoDO.getPtzId()); } /** diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/sensorgun/vo/SensorGunPageReqVO.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/sensorgun/vo/SensorGunPageReqVO.java index 9142eb9..ac1dbb1 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/sensorgun/vo/SensorGunPageReqVO.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/sensorgun/vo/SensorGunPageReqVO.java @@ -19,6 +19,9 @@ public class SensorGunPageReqVO extends PageParam { @Schema(description = "ip") private String ip; + @Schema(description = "type") + private Integer type; + @Schema(description = "端口") private Integer port; diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/sensorgun/vo/SensorGunSaveReqVO.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/sensorgun/vo/SensorGunSaveReqVO.java index d7823ec..5f22cec 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/sensorgun/vo/SensorGunSaveReqVO.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/controller/admin/sensorgun/vo/SensorGunSaveReqVO.java @@ -19,6 +19,9 @@ public class SensorGunSaveReqVO { @Schema(description = "ip") private String ip; + @Schema(description = "type") + private Integer type; + @Schema(description = "端口") private Integer port; diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/dataobject/street/SensorGunDO.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/dataobject/street/SensorGunDO.java index 55dac76..a4fd435 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/dataobject/street/SensorGunDO.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/dataobject/street/SensorGunDO.java @@ -1,8 +1,11 @@ package cn.iocoder.yudao.module.camera.dal.dataobject.sensorgun; -import lombok.*; -import com.baomidou.mybatisplus.annotation.*; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; +import org.glassfish.jaxb.core.v2.TODO; /** * 扫码枪 DO @@ -17,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; @Builder @NoArgsConstructor @AllArgsConstructor -public class SensorGunDO extends BaseDO { +public class SensorGunDO extends BaseDO { /** * id @@ -28,6 +31,11 @@ public class SensorGunDO extends BaseDO { * 巷道id */ private Integer streetId; + + /** + * type + */ + private Integer type; /** * 左右 * diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/entity/HikPythonEntity.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/entity/HikPythonEntity.java new file mode 100644 index 0000000..7f1c2bd --- /dev/null +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/dal/entity/HikPythonEntity.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.camera.dal.entity; + +import lombok.Data; + +import java.util.List; + +@Data +public class HikPythonEntity { + private String type; + private String sn; + private Integer direction; + private String width; + private String length; + private Integer height; + private Integer min_area; + private Boolean lack; +// 是否有缺 + private Integer tolerance; + private String status; + private List colors; + +} 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 new file mode 100644 index 0000000..0cee629 --- /dev/null +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/Hik3D/HikFlaskApiService.java @@ -0,0 +1,171 @@ +package cn.iocoder.yudao.module.camera.service.Hik3D; + +import cn.iocoder.yudao.module.camera.dal.dataobject.resources.URLResourcesDo; +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.HikPythonEntity; +import cn.iocoder.yudao.module.camera.dal.entity.ScanData; +import cn.iocoder.yudao.module.camera.framework.netty.ksec.KsecDataInfo; +import cn.iocoder.yudao.module.camera.service.resources.URLResourcesService; +import cn.iocoder.yudao.module.camera.service.scan.ScanService; +import cn.iocoder.yudao.module.system.service.dict.DictDataService; +import com.google.gson.Gson; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.Objects; + +@Slf4j +@Service("HikFlaskApiService") +public class HikFlaskApiService implements ScanService { + + private static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); + @Resource + private OkHttpClient client; + private final Gson gson = new Gson(); + + + @Resource + private DictDataService dictDataService; + // ================== /config/update ================== + + /** + * 调用 /config/update 接口,触发配置更新 + */ + public HikPythonEntity updateConfig(String baseUrl) throws IOException { + Request request = new Request.Builder() + .url(baseUrl + "/config/update") + .get() + .build(); + + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); + + String responseBody = Objects.requireNonNull(response.body()).string(); + return gson.fromJson(responseBody, HikPythonEntity.class); + } + } + + // ================== /api/addTemplate ================== + + /** + * 调用 /api/addTemplate 接口,保存模板配置 + * + * @param templateType 模板类型(type 字段) + * @param data 模板数据(JSON 格式 Map) + */ + public HikPythonEntity addTemplate(String templateType, HikPythonEntity data,String baseUrl) throws IOException { + + if (templateType == null || templateType.isEmpty()) { + throw new IllegalArgumentException("Missing 'type' field"); + } + + String json = gson.toJson(data); + RequestBody body = RequestBody.create(json, JSON); + + Request request = new Request.Builder() + .url(baseUrl + "/api/addTemplate") + .post(body) + .build(); + + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); + + String responseBody = Objects.requireNonNull(response.body()).string(); + return gson.fromJson(responseBody, HikPythonEntity.class); + } + } + + // ================== /api/picComputeAll ================== + + /** + * 调用 /api/picComputeAll 接口,批量处理所有设备图片 + */ + public HikPythonEntity picComputeAll(String type,String baseUrl) throws IOException { + HikPythonEntity requestData = new HikPythonEntity(); + requestData.setType(type); + + String json = gson.toJson(requestData); + RequestBody body = RequestBody.create(json, JSON); + + Request request = new Request.Builder() + .url(baseUrl + "/api/picComputeAll") + .post(body) + .build(); + + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); + + String responseBody = Objects.requireNonNull(response.body()).string(); + return gson.fromJson(responseBody, HikPythonEntity.class); + } + } + + // ================== /api/picCompute ================== + + /** + * 调用 /api/picCompute 接口,处理单个设备图片 + */ + public HikPythonEntity picCompute(String sn, String type,String baseUrl) throws IOException { + HikPythonEntity requestData =new HikPythonEntity(); + requestData.setSn(sn); + requestData.setType(type); + + String json = gson.toJson(requestData); + RequestBody body = RequestBody.create(json, JSON); + + Request request = new Request.Builder() + .url(baseUrl + "/api/picCompute") + .post(body) + .build(); + + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); + + String responseBody = Objects.requireNonNull(response.body()).string(); + return gson.fromJson(responseBody, HikPythonEntity.class); + } + } + + @Resource + URLResourcesService urlResourcesService; +// 识别结果是正常或缺件 + @Override + public ScanData scan(StreetDO streetDO, KsecDataInfo dataInfo, StockDO stockDO) { + ScanData scanData = new ScanData(); + + + String urlPath ="http://"+streetDO.getPlcIp() + ":" + dictDataService.parseDictData("Hik3DApi", "port").getValue(); + String picPath ="http://"+streetDO.getPlcIp() + ":" + dictDataService.parseDictData("Hik3DApi", "picPath").getValue(); + try { + + scanData.setCode("缺件"); + HikPythonEntity hikPythonEntity = picComputeAll(dataInfo.getCategory(),urlPath); + if (hikPythonEntity != null){ + if (hikPythonEntity.getLack()!= null && hikPythonEntity.getLack()) { + }else { + scanData.setCode("正常"); + } + if (hikPythonEntity.getColors() != null && hikPythonEntity.getColors().size() > 0) { + for (String color:hikPythonEntity.getColors()){ + if (color.startsWith(".")) { + color = color.substring(1); + } + color = picPath + color ; + urlResourcesService.save(URLResourcesDo.builder().url(color).uuid( stockDO.getCheckPic()).type("1").little("缺件拍照").build()); + } + } + } + return scanData; + } catch (IOException e) { + throw new RuntimeException(e); + } +// return null; + } + + // ================== 测试方法 ================== + +} diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/order/OrderService.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/order/OrderService.java index 3f8e096..c1f6f3c 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/order/OrderService.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/order/OrderService.java @@ -13,7 +13,6 @@ import jakarta.validation.Valid; * @author 芋道源码 */ public interface OrderService { - void check(OrderDO orderDO); /** * 创建随行记录 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 eb7a47c..54efb5a 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 @@ -2,22 +2,19 @@ package cn.iocoder.yudao.module.camera.service.order; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.camera.controller.admin.checklog.vo.CheckLogSaveReqVO; import cn.iocoder.yudao.module.camera.controller.admin.order.vo.OrderPageReqVO; import cn.iocoder.yudao.module.camera.controller.admin.order.vo.OrderSaveReqVO; -import cn.iocoder.yudao.module.camera.controller.admin.stock.vo.StockSaveReqVO; -import cn.iocoder.yudao.module.camera.dal.dataobject.checklog.CheckLogDO; import cn.iocoder.yudao.module.camera.dal.dataobject.order.OrderDO; -import cn.iocoder.yudao.module.camera.dal.dataobject.sensorgun.SensorGunDO; -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.Legend; +import cn.iocoder.yudao.module.camera.dal.entity.echarts.Series; import cn.iocoder.yudao.module.camera.dal.mysql.order.OrderMapper; import cn.iocoder.yudao.module.camera.dal.mysql.sensorgun.SensorGunMapper; import cn.iocoder.yudao.module.camera.service.checklog.CheckLogService; import cn.iocoder.yudao.module.camera.service.stock.StockService; import cn.iocoder.yudao.module.camera.service.street.StreetService; -import cn.iocoder.yudao.module.camera.service.threeInOneCode.ScanningGun; import cn.iocoder.yudao.module.camera.service.threeInOneCode.SpecificationsAndOCR; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import de.danielbechler.util.Strings; @@ -64,58 +61,6 @@ public class OrderServiceImpl implements OrderService { StockService stockService; - @Override - public void check(OrderDO orderDO) { - StreetDO streetDo = streetService.getStreetByPlcId(orderDO.getSrmNumber()); - if(streetDo==null){ - return; - } - CheckLogSaveReqVO checkLogSaveReqVO = new CheckLogSaveReqVO(); - List sensorGunDOS = sensorGunMapper.selectListByStreetId(streetDo.getId()); - //扫码枪校验 - String sensorGunCode = ""; - for (SensorGunDO sensorGunDO : sensorGunDOS){ - sensorGunCode = ScanningGun.readOCR(sensorGunDO.getIp(), sensorGunDO.getPort(),"start",3); - } - - //灵闪校验 - Map readOCR = SpecificationsAndOCR.readOCR(streetDo.getPlcIp(), streetDo.getPlcPort()); - String code = readOCR.get("ORC"); - StockDO stockDoc =stockService.getOne(new QueryWrapper() - .eq("street_id",streetDo.getId()) - .eq("side",orderDO.getFromSide()) - .eq("direction",orderDO.getFromDirection()) - .eq("`column`",orderDO.getFromColumn()) - .eq("`row`",orderDO.getFromRow()) - .eq("separation",orderDO.getFromSeparation()) - ); - if(stockDoc==null){ - stockDoc.setCode(sensorGunCode); - stockDoc.setStorageCode(code); - stockService.updateById(stockDoc); - }else { - - //图片保存和记录 - StockSaveReqVO stockSaveReqVO = new StockSaveReqVO(); - stockSaveReqVO.setOrderNum(orderDO.getTaskId()); - stockSaveReqVO.setCode(sensorGunCode); - stockSaveReqVO.setStreetId(streetDo.getId()); - stockSaveReqVO.setSide(orderDO.getFromSide()); - stockSaveReqVO.setDirection(orderDO.getFromDirection()); - stockSaveReqVO.setColumn(orderDO.getFromColumn()); - stockSaveReqVO.setRow(orderDO.getFromRow()); - stockSaveReqVO.setStorageCode(code); - stockService.createStock(stockSaveReqVO); - } - - - - CheckLogDO checkLogDoc = new CheckLogDO(); - checkLogService.save(checkLogDoc); - - - } - @Override public Long createOrder(OrderSaveReqVO createReqVO) { // 插入 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 c28aec4..0716b33 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 @@ -26,6 +26,7 @@ import cn.iocoder.yudao.module.camera.service.checklog.CheckLogService; import cn.iocoder.yudao.module.camera.service.resources.URLResourcesService; import cn.iocoder.yudao.module.camera.service.scan.ScanServiceFactory; import cn.iocoder.yudao.module.camera.service.stock.StockService; +import cn.iocoder.yudao.module.camera.service.streamingMedia.ZLMediaKitService; import cn.iocoder.yudao.module.camera.service.street.StreetService; import cn.iocoder.yudao.module.camera.service.threeInOneCode.ScanningGun; import cn.iocoder.yudao.module.camera.util.PathUtil; @@ -502,7 +503,8 @@ public class PLCServiceImpl implements PLCService { //OrderRealtime.startOrder(street.getId(), plcCmdInfo.getOrderNum()); } } - + @Resource + ZLMediaKitService zLMediaKitService; public void orderStop(KsecDataInfo ksecDataInfo) { Map dictDataList = dictDataService.getDictDataList("camera_conf"); LocalDateTime endTime = LocalDateTime.now(); @@ -535,7 +537,8 @@ public class PLCServiceImpl implements PLCService { if (street.getCamera1Id() != null) { String pathSrc = PathUtil.createFileName(ksecDataInfo, street, "B2-1", ".mp4"); - String path = cameraVideo(street.getCamera1Id(), pathSrc, order.getCreateTime(), endDownLoadTime, dictDataList); + String path = zLMediaKitService.startRecord("live", street.getCamera1Id().toString()); +// String path = cameraVideo(street.getCamera1Id(), pathSrc, order.getCreateTime(), endDownLoadTime, dictDataList); update.setVideoPath1(path); } if (street.getCamera2Id() != null) { diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/scan/ScanGunScanServiceImpl.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/scan/ScanGunScanServiceImpl.java index 1acd0d8..5d53e38 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/scan/ScanGunScanServiceImpl.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/scan/ScanGunScanServiceImpl.java @@ -5,7 +5,9 @@ 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.ScanData; import cn.iocoder.yudao.module.camera.framework.netty.ksec.KsecDataInfo; +import cn.iocoder.yudao.module.camera.service.sensorgun.CognexSocket; import cn.iocoder.yudao.module.camera.service.sensorgun.SensorGunService; +import cn.iocoder.yudao.module.camera.service.sensorgun.SickSocket; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -87,7 +89,13 @@ public class ScanGunScanServiceImpl implements ScanService{ if (sensorGun == null) { log.error("no sensor gun config in database ,street id:{},direction:{}", streetDO.getId(), dataInfo.getFromDirection()); } else { - trayCode = readOCR(sensorGun.getIp(), sensorGun.getPort()); +// sick扫码 + if (sensorGun.getType() == 1){ + trayCode = SickSocket.readOCR(sensorGun.getIp(), sensorGun.getPort()); + }else { +// 康耐视扫码 + trayCode = CognexSocket.readOCR(sensorGun.getIp(), sensorGun.getPort()); + } } scanData.setCode(trayCode); return scanData; diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/scan/ScanServiceFactory.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/scan/ScanServiceFactory.java index 5e44166..b7e3e24 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/scan/ScanServiceFactory.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/scan/ScanServiceFactory.java @@ -4,6 +4,7 @@ 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.ScanData; import cn.iocoder.yudao.module.camera.framework.netty.ksec.KsecDataInfo; +import cn.iocoder.yudao.module.camera.service.Hik3D.HikFlaskApiService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -21,8 +22,13 @@ public class ScanServiceFactory { private YoloServiceImpl yoloService; @Resource private OpencvServiceImpl opencvService; + @Resource + private HikFlaskApiService hikFlaskApiService; + +// 返回的ScanData的code会将作为扫描结果,如果和wms的一致,则记录为成功 +// 图片保存将在scan方法中实现 public ScanData scan(String type, StreetDO streetDO, KsecDataInfo dataInfo, StockDO stockDO) { @@ -47,6 +53,9 @@ public class ScanServiceFactory { case "5": return opencvService.scan(streetDO, dataInfo, stockDO); + // hik3d识别扫码 + case "6": + return hikFlaskApiService.scan(streetDO, dataInfo, stockDO); // 默认情况处理 default: return null; diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/sensorgun/CognexSocket.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/sensorgun/CognexSocket.java new file mode 100644 index 0000000..b6e9290 --- /dev/null +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/sensorgun/CognexSocket.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.camera.service.sensorgun; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.charset.StandardCharsets; + +@Slf4j +public class CognexSocket { + + private static final Logger tcpLogger = LoggerFactory.getLogger("sick"); + public static String readOCR(String ip,int port){ + Socket socket = new Socket(); + String code = "NOREAD"; + OutputStream os = null; + InputStream is = null; + try { + socket.connect(new InetSocketAddress(ip,port),3000); + os = socket.getOutputStream(); + is = socket.getInputStream(); + int i = 0; + while ("NOREAD".equals(code) && i <= 4){ + writeCmd(os); + code = read(is); + tcpLogger.info("count:{},ip:{},code:{}",i,ip,code); + if(code!= null){ + code = code.replace("\\n",""); + } + i++; + } + } catch (IOException e) { + tcpLogger.error("sick time out,ip:{},info:{}",ip,e); + log.error("sick time out,ip:{},info:{}",ip,e); + }finally { + if(os != null){ + try { + os.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if(is != null){ + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + try { + socket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + return code; + } + } + + private static void writeCmd(OutputStream os) throws IOException { + String startCmd = "+"; + byte[]bytes = startCmd.getBytes(StandardCharsets.UTF_8); + os.write(bytes); + try { + Thread.sleep(300); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + startCmd = "-"; + bytes = startCmd.getBytes(StandardCharsets.UTF_8); + os.write(bytes); + } + + private static String read(InputStream inStream) throws IOException { + BufferedReader bd = new BufferedReader(new InputStreamReader(inStream)); + return bd.readLine(); + } +} diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/sensorgun/SensorGunService.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/sensorgun/SensorGunService.java index c6e37e6..ecd8c89 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/sensorgun/SensorGunService.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/sensorgun/SensorGunService.java @@ -52,4 +52,5 @@ public interface SensorGunService extends IService { */ PageResult getSensorGunPage(SensorGunPageReqVO pageReqVO); + String scan(Integer id); } \ 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/sensorgun/SensorGunServiceImpl.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/sensorgun/SensorGunServiceImpl.java index ae611fb..7a6ac3d 100644 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/sensorgun/SensorGunServiceImpl.java +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/sensorgun/SensorGunServiceImpl.java @@ -68,4 +68,20 @@ public class SensorGunServiceImpl extends ServiceImpl list); + void deleteRecordDirectory(String app, String stream, String period); +} 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 new file mode 100644 index 0000000..6867dcc --- /dev/null +++ b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/service/streamingMedia/ZLMediaKitServiceImpl.java @@ -0,0 +1,218 @@ +package cn.iocoder.yudao.module.camera.service.streamingMedia; + +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.camera.dal.dataobject.camera.CameraDO; +import cn.iocoder.yudao.module.camera.dal.zlm.RtspSessionResponse; +import cn.iocoder.yudao.module.camera.service.channel.CameraChannel; +import cn.iocoder.yudao.module.system.service.dict.DictDataService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Service +@Validated +@Slf4j +public class ZLMediaKitServiceImpl implements ZLMediaKitService{ + + @Resource + private OkHttpClient client ; + @Resource + private DictDataService dictDataService; + + @Override + public String startRecord(String app, String cameraId) { + + String zlmApiSecret = dictDataService.parseDictData("ZLMediaKit_conf", "secret").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("stream","camera"+ cameraId); +// addParams.put("url", CameraChannel.getRtspUrl(camera.getIp(), camera.getRtspPort(), camera.getChannel(), camera.getUser(), camera.getPassword(),camera.getType())); + sendHttp(addParams, "startRecord"); + log.info("startRecord camera:"+cameraId); + String path = checkHiddenFilesInDirectory(app,cameraId); + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + + Runnable task = () -> { + stopRecord(app,cameraId); + + }; + + // 延迟5分钟后执行停止任务,不会将录像文件录5分钟 + long delay =5; + scheduler.schedule(task, delay, TimeUnit.MINUTES); + return path; + } + + // 新增方法:检查指定文件夹下是否有以.为开头的文件 + public String checkHiddenFilesInDirectory(String app, String cameraId) { + + String mp4SavePath = dictDataService.parseDictData("ZLMediaKit_conf", "mp4SavePath").getValue(); + // 获取当前日期 + LocalDate currentDate = LocalDate.now(); + + // 定义日期格式 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + // 格式化日期 + String formattedDate = currentDate.format(formatter); + + String directoryPath = mp4SavePath+"record/"+app+"/camera"+cameraId+"/"+formattedDate; + + + Path directory = Paths.get(directoryPath); + List hiddenFiles = new ArrayList<>(); + + try (Stream paths = Files.walk(directory, 1)) { + hiddenFiles= paths + .filter(Files::isRegularFile) + .map(Path::getFileName) + .map(Path::toString) + .filter(name -> name.startsWith(".")) + .collect(Collectors.toList()); + return hiddenFiles.isEmpty() ? "" : "record/"+app+"/camera"+cameraId+"/"+formattedDate+"/"+removeLeadingDot(hiddenFiles.get(0)); + } catch (IOException e) { + e.printStackTrace(); + hiddenFiles= List.of(); // 返回空列表 + } + return ""; + } + + public static String removeLeadingDot(String input) { + if (input == null || input.isEmpty()) { + return input; + } + if (input.startsWith(".")) { + return input.substring(1); + } + return input; + } + @Override + public void stopRecord(String app, String stream) { + + String zlmApiSecret = dictDataService.parseDictData("ZLMediaKit_conf", "secret").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("stream","camera"+ stream); +// addParams.put("url", CameraChannel.getRtspUrl(camera.getIp(), camera.getRtspPort(), camera.getChannel(), camera.getUser(), camera.getPassword(),camera.getType())); + sendHttp(addParams, "stopRecord"); + + } + + @Override + public void zlmConf( List list) { + + String zlmApiUrl = dictDataService.parseDictData("ZLMediaKit_conf", "Apiurl").getValue(); + String zlmApiSecret = dictDataService.parseDictData("ZLMediaKit_conf", "secret").getValue(); + try { + // 查询当前的RTSP拉流代理 + Map queryParams = new HashMap<>(); + queryParams.put("secret", zlmApiSecret); + String queryUrl = buildUrl(zlmApiUrl +"getMediaList",null , queryParams); + String response = get(queryUrl); + RtspSessionResponse rtspSessionResponse = JsonUtils.parseObject(response, RtspSessionResponse.class); + // 检查并添加缺失的RTSP代理 + for (CameraDO entry : list) { + boolean isRtspProxyExists = false; + if (rtspSessionResponse != null && rtspSessionResponse.getData()!=null && rtspSessionResponse.getData().size()>0) { + for (RtspSessionResponse.RtspSession rtspSession : rtspSessionResponse.getData()){ + if (rtspSession.getApp().equals("live")&&rtspSession.getStream().equals("camera"+entry.getId())){ + isRtspProxyExists = true; + break; + } + } + } + //不存在则重新注入 + if (!isRtspProxyExists){ + try { + + addRtspProxy(entry, zlmApiUrl, zlmApiSecret); + }catch (Exception e){ + e.printStackTrace(); + } + } + + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + + public void addRtspProxy(CameraDO camera, String zlmApiUrl, String zlmApiSecret) { + Map addParams = new HashMap<>(); + addParams.put("secret", zlmApiSecret); + addParams.put("vhost", "__defaultVhost__"); + addParams.put("app", "live"); + addParams.put("stream","camera"+ camera.getId()); + addParams.put("url", CameraChannel.getRtspUrl(camera.getIp(), camera.getRtspPort(), camera.getChannel(), camera.getUser(), camera.getPassword(),camera.getType())); + String addUrl = buildUrl(zlmApiUrl+"addStreamProxy",null , addParams); + String response = null; + try { + response = get(addUrl); + } catch (IOException e) { + throw new RuntimeException(e); + } + System.out.println("Add RTSP Proxy Response: " + response); + } + @Override + public void deleteRecordDirectory(String app, String stream, String period) { + + } + public void sendHttp(Map queryParams ,String url){ + + String zlmApiUrl = dictDataService.parseDictData("ZLMediaKit_conf", "Apiurl").getValue(); + try { + // 查询当前的RTSP拉流代理 + String queryUrl = buildUrl(zlmApiUrl +url,null , queryParams); + String response = get(queryUrl); + RtspSessionResponse rtspSessionResponse = JsonUtils.parseObject(response, RtspSessionResponse.class); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private String buildUrl(String baseUrl, String action, Map params) { + HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl).newBuilder(); + + for (Map.Entry entry : params.entrySet()) { + urlBuilder.addQueryParameter(entry.getKey(), entry.getValue().toString()); + } + return urlBuilder.build().toString(); + } + + private String get(String url) throws IOException { + Request request = new Request.Builder() + .url(url) + .build(); + try (Response response = client.newCall(request).execute()) { + return response.body().string(); + } + } +} diff --git a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/util/RedisUtil.java b/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/util/RedisUtil.java deleted file mode 100644 index 93a6030..0000000 --- a/yudao-module-logistics/yudao-module-logistics-biz/src/main/java/cn/iocoder/yudao/module/camera/util/RedisUtil.java +++ /dev/null @@ -1,747 +0,0 @@ -package cn.iocoder.yudao.module.camera.util; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -/** - * - * @author 王赛超 基于spring和redis的redisTemplate工具类 针对所有的hash 都是以h开头的方法 针对所有的Set 都是以s开头的方法 不含通用方法 针对所有的List 都是以l开头的方法 - */ -@Component -@Slf4j -public class RedisUtil { - @Autowired - private RedisTemplate redisTemplate; - - // =============================common============================ - /** - * 指定缓存失效时间 - * - * @param key - * 键 - * @param time - * 时间(秒) - * @return - */ - public boolean expire(String key, long time) { - try { - if (time > 0) { - redisTemplate.expire(key, time, TimeUnit.SECONDS); - } - return true; - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * 根据key 获取过期时间 - * - * @param key - * 键 不能为null - * @return 时间(秒) 返回0代表为永久有效 - */ - public long getExpire(String key) { - return redisTemplate.getExpire(key, TimeUnit.SECONDS); - } - - /** - * 判断key是否存在 - * - * @param key - * 键 - * @return true 存在 false不存在 - */ - public boolean hasKey(String key) { - try { - return redisTemplate.hasKey(key); - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * 删除缓存 - * - * @param key - * 可以传一个值 或多个 - */ - @SuppressWarnings("unchecked") - public void del(String... key) { - if (key != null && key.length > 0) { - if (key.length == 1) { - redisTemplate.delete(key[0]); - } else { - redisTemplate.delete((Collection) CollectionUtils.arrayToList(key)); - } - } - } - - // ============================String============================= - /** - * 普通缓存获取 - * - * @param key - * 键 - * @return 值 - */ - public Object get(String key) { - return key == null ? null : redisTemplate.opsForValue().get(key); - } - - /** - * 普通缓存放入 - * - * @param key - * 键 - * @param value - * 值 - * @return true成功 false失败 - */ - public boolean set(String key, Object value) { - try { - redisTemplate.opsForValue().set(key, value); - return true; - } catch (Exception e) { - log.error(key, e); - return false; - } - - } - - /** - * 普通缓存放入并设置时间 - * - * @param key - * 键 - * @param value - * 值 - * @param time - * 时间(秒) time要大于0 如果time小于等于0 将设置无限期 - * @return true成功 false 失败 - */ - public boolean set(String key, Object value, long time) { - try { - if (time > 0) { - redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); - } else { - set(key, value); - } - return true; - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * 递增 适用场景: https://blog.csdn.net/y_y_y_k_k_k_k/article/details/79218254 高并发生成订单号,秒杀类的业务逻辑等。。 - * - * @param key - * 键 - * 要增加几(大于0) - * @return - */ - public long incr(String key, long delta) { - if (delta < 0) { - throw new RuntimeException("递增因子必须大于0"); - } - return redisTemplate.opsForValue().increment(key, delta); - } - - /** - * 递减 - * - * @param key - * 键 - * 要减少几(小于0) - * @return - */ - public long decr(String key, long delta) { - if (delta < 0) { - throw new RuntimeException("递减因子必须大于0"); - } - return redisTemplate.opsForValue().increment(key, -delta); - } - - // ================================Map================================= - /** - * HashGet - * - * @param key - * 键 不能为null - * @param item - * 项 不能为null - * @return 值 - */ - public Object hget(String key, String item) { - return redisTemplate.opsForHash().get(key, item); - } - - /** - * 获取hashKey对应的所有键值 - * - * @param key - * 键 - * @return 对应的多个键值 - */ - public Map hmget(String key) { - return redisTemplate.opsForHash().entries(key); - } - - /** - * HashSet - * - * @param key - * 键 - * @param map - * 对应多个键值 - * @return true 成功 false 失败 - */ - public boolean hmset(String key, Map map) { - try { - redisTemplate.opsForHash().putAll(key, map); - return true; - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * HashSet 并设置时间 - * - * @param key - * 键 - * @param map - * 对应多个键值 - * @param time - * 时间(秒) - * @return true成功 false失败 - */ - public boolean hmset(String key, Map map, long time) { - try { - redisTemplate.opsForHash().putAll(key, map); - if (time > 0) { - expire(key, time); - } - return true; - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * 向一张hash表中放入数据,如果不存在将创建 - * - * @param key - * 键 - * @param item - * 项 - * @param value - * 值 - * @return true 成功 false失败 - */ - public boolean hset(String key, String item, Object value) { - try { - redisTemplate.opsForHash().put(key, item, value); - return true; - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * 向一张hash表中放入数据,如果不存在将创建 - * - * @param key - * 键 - * @param item - * 项 - * @param value - * 值 - * @param time - * 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 - * @return true 成功 false失败 - */ - public boolean hset(String key, String item, Object value, long time) { - try { - redisTemplate.opsForHash().put(key, item, value); - if (time > 0) { - expire(key, time); - } - return true; - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * 删除hash表中的值 - * - * @param key - * 键 不能为null - * @param item - * 项 可以使多个 不能为null - */ - public void hdel(String key, Object... item) { - redisTemplate.opsForHash().delete(key, item); - } - - /** - * 判断hash表中是否有该项的值 - * - * @param key - * 键 不能为null - * @param item - * 项 不能为null - * @return true 存在 false不存在 - */ - public boolean hHasKey(String key, String item) { - return redisTemplate.opsForHash().hasKey(key, item); - } - - /** - * hash递增 如果不存在,就会创建一个 并把新增后的值返回 - * - * @param key - * 键 - * @param item - * 项 - * @param by - * 要增加几(大于0) - * @return - */ - public double hincr(String key, String item, double by) { - return redisTemplate.opsForHash().increment(key, item, by); - } - - /** - * hash递减 - * - * @param key - * 键 - * @param item - * 项 - * @param by - * 要减少记(小于0) - * @return - */ - public double hdecr(String key, String item, double by) { - return redisTemplate.opsForHash().increment(key, item, -by); - } - - // ============================set============================= - /** - * 根据key获取Set中的所有值 - * - * @param key - * 键 - * @return - */ - public Set sGet(String key) { - try { - return redisTemplate.opsForSet().members(key); - } catch (Exception e) { - log.error(key, e); - return null; - } - } - - /** - * 根据value从一个set中查询,是否存在 - * - * @param key - * 键 - * @param value - * 值 - * @return true 存在 false不存在 - */ - public boolean sHasKey(String key, Object value) { - try { - return redisTemplate.opsForSet().isMember(key, value); - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * 将数据放入set缓存 - * - * @param key - * 键 - * @param values - * 值 可以是多个 - * @return 成功个数 - */ - public long sSet(String key, Object... values) { - try { - return redisTemplate.opsForSet().add(key, values); - } catch (Exception e) { - log.error(key, e); - return 0; - } - } - - /** - * 将set数据放入缓存 - * - * @param key - * 键 - * @param time - * 时间(秒) - * @param values - * 值 可以是多个 - * @return 成功个数 - */ - public long sSetAndTime(String key, long time, Object... values) { - try { - Long count = redisTemplate.opsForSet().add(key, values); - if (time > 0) - expire(key, time); - return count; - } catch (Exception e) { - log.error(key, e); - return 0; - } - } - - /** - * 获取set缓存的长度 - * - * @param key - * 键 - * @return - */ - public long sGetSetSize(String key) { - try { - return redisTemplate.opsForSet().size(key); - } catch (Exception e) { - log.error(key, e); - return 0; - } - } - - /** - * 移除值为value的 - * - * @param key - * 键 - * @param values - * 值 可以是多个 - * @return 移除的个数 - */ - public long setRemove(String key, Object... values) { - try { - Long count = redisTemplate.opsForSet().remove(key, values); - return count; - } catch (Exception e) { - log.error(key, e); - return 0; - } - } - - // ============================zset============================= - /** - * 根据key获取Set中的所有值 - * - * @param key - * 键 - * @return - */ - public Set zSGet(String key) { - try { - return redisTemplate.opsForSet().members(key); - } catch (Exception e) { - log.error(key, e); - return null; - } - } - - /** - * 根据value从一个set中查询,是否存在 - * - * @param key - * 键 - * @param value - * 值 - * @return true 存在 false不存在 - */ - public boolean zSHasKey(String key, Object value) { - try { - return redisTemplate.opsForSet().isMember(key, value); - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - public Boolean zSSet(String key, Object value, double score) { - try { - return redisTemplate.opsForZSet().add(key, value, 2); - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * 将set数据放入缓存 - * - * @param key - * 键 - * @param time - * 时间(秒) - * @param values - * 值 可以是多个 - * @return 成功个数 - */ - public long zSSetAndTime(String key, long time, Object... values) { - try { - Long count = redisTemplate.opsForSet().add(key, values); - if (time > 0) - expire(key, time); - return count; - } catch (Exception e) { - log.error(key, e); - return 0; - } - } - - /** - * 获取set缓存的长度 - * - * @param key - * 键 - * @return - */ - public long zSGetSetSize(String key) { - try { - return redisTemplate.opsForSet().size(key); - } catch (Exception e) { - log.error(key, e); - return 0; - } - } - - /** - * 移除值为value的 - * - * @param key - * 键 - * @param values - * 值 可以是多个 - * @return 移除的个数 - */ - public long zSetRemove(String key, Object... values) { - try { - Long count = redisTemplate.opsForSet().remove(key, values); - return count; - } catch (Exception e) { - log.error(key, e); - return 0; - } - } - // ===============================list================================= - - /** - * 获取list缓存的内容 - * - * @取出来的元素 总数 end-start+1 - * - * @param key - * 键 - * @param start - * 开始 0 是第一个元素 - * @param end - * 结束 -1代表所有值 - * @return - */ - public List lGet(String key, long start, long end) { - try { - return redisTemplate.opsForList().range(key, start, end); - } catch (Exception e) { - log.error(key, e); - return null; - } - } - - /** - * 获取list缓存的长度 - * - * @param key - * 键 - * @return - */ - public long lGetListSize(String key) { - try { - return redisTemplate.opsForList().size(key); - } catch (Exception e) { - log.error(key, e); - return 0; - } - } - - /** - * 通过索引 获取list中的值 - * - * @param key - * 键 - * @param index - * 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 - * @return - */ - public Object lGetIndex(String key, long index) { - try { - return redisTemplate.opsForList().index(key, index); - } catch (Exception e) { - log.error(key, e); - return null; - } - } - - /** - * 将list放入缓存 - * - * @param key - * 键 - * @param value - * 值 - * 时间(秒) - * @return - */ - public boolean lSet(String key, Object value) { - try { - redisTemplate.opsForList().rightPush(key, value); - return true; - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * 将list放入缓存 - * - * @param key - * 键 - * @param value - * 值 - * @param time - * 时间(秒) - * @return - */ - public boolean lSet(String key, Object value, long time) { - try { - redisTemplate.opsForList().rightPush(key, value); - if (time > 0) - expire(key, time); - return true; - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * 将list放入缓存 - * - * @param key - * 键 - * @param value - * 值 - * 时间(秒) - * @return - */ - public boolean lSet(String key, List value) { - try { - redisTemplate.opsForList().rightPushAll(key, value); - return true; - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * 将list放入缓存 - * - * @param key - * 键 - * @param value - * 值 - * @param time - * 时间(秒) - * @return - */ - public boolean lSet(String key, List value, long time) { - try { - redisTemplate.opsForList().rightPushAll(key, value); - if (time > 0) - expire(key, time); - return true; - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * 根据索引修改list中的某条数据 - * - * @param key - * 键 - * @param index - * 索引 - * @param value - * 值 - * @return - */ - public boolean lUpdateIndex(String key, long index, Object value) { - try { - redisTemplate.opsForList().set(key, index, value); - return true; - } catch (Exception e) { - log.error(key, e); - return false; - } - } - - /** - * 移除N个值为value - * - * @param key - * 键 - * @param count - * 移除多少个 - * @param value - * 值 - * @return 移除的个数 - */ - public long lRemove(String key, long count, Object value) { - try { - Long remove = redisTemplate.opsForList().remove(key, count, value); - return remove; - } catch (Exception e) { - log.error(key, e); - return 0; - } - } - -} - diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/sse/SseController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/sse/SseController.java index 7e06bb4..98c738f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/sse/SseController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/sse/SseController.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.system.controller.app.sse; import cn.hutool.core.util.IdUtil; +import cn.iocoder.yudao.module.system.controller.app.sse.vo.SseVo; import cn.iocoder.yudao.module.system.service.sse.SseClient; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.security.PermitAll; @@ -42,6 +43,16 @@ public class SseController { } return "ok"; } + @CrossOrigin + @GetMapping("/sendMsgAll") + @ResponseBody + @PermitAll + public String sseChatAll() { + SseVo sseVo = new SseVo(); + sseVo.setMessage("hello world"); + sseClient.sendMessage(sseVo); + return "ok"; + } /** * 关闭连接 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java index e10d9d2..1eda869 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java @@ -10,17 +10,16 @@ import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSave import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; import cn.iocoder.yudao.module.system.dal.mysql.dict.DictDataMapper; +import cn.iocoder.yudao.module.system.util.RedisUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import java.util.Collection; -import java.util.Comparator; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -34,6 +33,8 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @Service @Slf4j public class DictDataServiceImpl extends ServiceImpl implements DictDataService { + @Resource + private RedisUtil redisUtil; /** * 排序 dictType > sort @@ -48,8 +49,19 @@ public class DictDataServiceImpl extends ServiceImpl @Resource private DictDataMapper dictDataMapper; + @PostConstruct + void init(){ +// log.info("init DictDataServiceImpl"); + List list = dictDataMapper.selectList(); + Map> map = list.stream().collect(Collectors.groupingBy(DictDataDO::getDictType)); + map.forEach((k,v)->{ + redisUtil.hsetList("dict:"+k, v.stream().collect(Collectors.toMap(DictDataDO::getLabel,DictDataDO::getValue))); + }); + } + @Override public List getDictDataList(Integer status, String dictType) { + List list = dictDataMapper.selectListByStatusAndDictType(status, dictType); list.sort(COMPARATOR_TYPE_AND_SORT); return list; @@ -66,6 +78,18 @@ public class DictDataServiceImpl extends ServiceImpl } @Override public Map getDictDataList( String dictType) { +// redis + if(redisUtil.hasKey("dict:"+dictType)){ + Map map = new HashMap<>(); + Map redisMap = redisUtil.hmget("dict:"+dictType); + map = redisMap.entrySet().stream().map(entry -> { + DictDataDO dictDataDO = new DictDataDO(); + dictDataDO.setLabel(entry.getKey()); + dictDataDO.setValue(entry.getValue()); + return dictDataDO; + }).collect(Collectors.toMap(DictDataDO::getLabel, v->v)); + return map; + } List list = dictDataMapper.selectList( new QueryWrapper().eq("dict_type", dictType)); Map map = list.stream().collect( Collectors.toMap(DictDataDO::getLabel, v->v)); @@ -84,6 +108,7 @@ public class DictDataServiceImpl extends ServiceImpl @Override public Long createDictData(DictDataSaveReqVO createReqVO) { + // 校验字典类型有效 validateDictTypeExists(createReqVO.getDictType()); // 校验字典数据的值的唯一性 @@ -92,6 +117,9 @@ public class DictDataServiceImpl extends ServiceImpl // 插入字典类型 DictDataDO dictData = BeanUtils.toBean(createReqVO, DictDataDO.class); dictDataMapper.insert(dictData); + +// redis创建 + redisUtil.hset("dict:"+createReqVO.getDictType(), createReqVO.getLabel(), createReqVO.getValue()); return dictData.getId(); } @@ -107,6 +135,8 @@ public class DictDataServiceImpl extends ServiceImpl // 更新字典类型 DictDataDO updateObj = BeanUtils.toBean(updateReqVO, DictDataDO.class); dictDataMapper.updateById(updateObj); +// redis + redisUtil.hset("dict:"+updateReqVO.getDictType(), updateReqVO.getLabel(), updateReqVO.getValue()); } @Override @@ -116,6 +146,11 @@ public class DictDataServiceImpl extends ServiceImpl // 删除字典数据 dictDataMapper.deleteById(id); + + + DictDataDO updateReqVO = dictDataMapper.selectById(id); +// redis + redisUtil.hdel("dict:"+updateReqVO.getDictType(), updateReqVO.getLabel()); } @Override @@ -181,11 +216,21 @@ public class DictDataServiceImpl extends ServiceImpl @Override public DictDataDO getDictData(String dictType, String value) { + return dictDataMapper.selectByDictTypeAndValue(dictType, value); } @Override public DictDataDO parseDictData(String dictType, String label) { + // redis + if(redisUtil.hasKey("dict:"+dictType)) { + String value = redisUtil.hgetString("dict:" + dictType,label); + + DictDataDO dictDataDO = new DictDataDO(); + dictDataDO.setLabel(label); + dictDataDO.setValue(value); + return dictDataDO; + } return dictDataMapper.selectByDictTypeAndLabel(dictType, label); } diff --git a/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/util/RedisUtil.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/RedisUtil.java similarity index 93% rename from yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/util/RedisUtil.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/RedisUtil.java index 93a6030..d11c26b 100644 --- a/yudao-module-camera/yudao-module-camera-biz/src/main/java/cn/iocoder/yudao/module/camera/util/RedisUtil.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/RedisUtil.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.camera.util; +package cn.iocoder.yudao.module.system.util; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -6,10 +6,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.TimeUnit; /** @@ -43,6 +40,24 @@ public class RedisUtil { return false; } } + /** + * 指定缓存失效时间 + * + * @param key + * 键= + * 时间(秒) + * @return + */ + public Set keys(String key) { + try { + + Set list = redisTemplate.keys(key); + return list ; + } catch (Exception e) { + log.error(key, e); + return new HashSet(); + } + } /** * 根据key 获取过期时间 @@ -189,6 +204,10 @@ public class RedisUtil { return redisTemplate.opsForHash().get(key, item); } + public String hgetString(String key, String item) { + return (String) redisTemplate.opsForHash().get(key, item); + } + /** * 获取hashKey对应的所有键值 * @@ -196,8 +215,13 @@ public class RedisUtil { * 键 * @return 对应的多个键值 */ - public Map hmget(String key) { - return redisTemplate.opsForHash().entries(key); + public Map hmget(String key) { + Map map = redisTemplate.opsForHash().entries(key); + Map mapString = new HashMap<>(); + map.forEach((k, v) -> { + mapString.put(k.toString(), v.toString()); + }); + return mapString; } /** @@ -264,6 +288,18 @@ public class RedisUtil { } } + public boolean hsetList(String key, Map itemMap) { + try { + for(String item:itemMap.keySet()) { + redisTemplate.opsForHash().put(key, item, itemMap.get(item)); + } + return true; + } catch (Exception e) { + log.error(key, e); + return false; + } + } + /** * 向一张hash表中放入数据,如果不存在将创建 * diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImplTest.java index 999570e..e69de29 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImplTest.java @@ -1,352 +0,0 @@ -package cn.iocoder.yudao.module.system.service.dict; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; -import cn.iocoder.yudao.module.system.dal.mysql.dict.DictDataMapper; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.List; -import java.util.function.Consumer; - -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static java.util.Collections.singletonList; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -@Import(DictDataServiceImpl.class) -public class DictDataServiceImplTest extends BaseDbUnitTest { - - @Resource - private DictDataServiceImpl dictDataService; - - @Resource - private DictDataMapper dictDataMapper; - @MockBean - private DictTypeService dictTypeService; - - @Test - public void testGetDictDataList() { - // mock 数据 - DictDataDO dictDataDO01 = randomDictDataDO().setDictType("yunai").setSort(2) - .setStatus(CommonStatusEnum.ENABLE.getStatus()); - dictDataMapper.insert(dictDataDO01); - DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setSort(1) - .setStatus(CommonStatusEnum.ENABLE.getStatus()); - dictDataMapper.insert(dictDataDO02); - DictDataDO dictDataDO03 = randomDictDataDO().setDictType("yunai").setSort(3) - .setStatus(CommonStatusEnum.DISABLE.getStatus()); - dictDataMapper.insert(dictDataDO03); - DictDataDO dictDataDO04 = randomDictDataDO().setDictType("yunai2").setSort(3) - .setStatus(CommonStatusEnum.DISABLE.getStatus()); - dictDataMapper.insert(dictDataDO04); - // 准备参数 - Integer status = CommonStatusEnum.ENABLE.getStatus(); - String dictType = "yunai"; - - // 调用 - List dictDataDOList = dictDataService.getDictDataList(status, dictType); - // 断言 - assertEquals(2, dictDataDOList.size()); - assertPojoEquals(dictDataDO02, dictDataDOList.get(0)); - assertPojoEquals(dictDataDO01, dictDataDOList.get(1)); - } - - @Test - public void testGetDictDataPage() { - // mock 数据 - DictDataDO dbDictData = randomPojo(DictDataDO.class, o -> { // 等会查询到 - o.setLabel("芋艿"); - o.setDictType("yunai"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - dictDataMapper.insert(dbDictData); - // 测试 label 不匹配 - dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setLabel("艿"))); - // 测试 dictType 不匹配 - dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setDictType("nai"))); - // 测试 status 不匹配 - dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 准备参数 - DictDataPageReqVO reqVO = new DictDataPageReqVO(); - reqVO.setLabel("芋"); - reqVO.setDictType("yunai"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - - // 调用 - PageResult pageResult = dictDataService.getDictDataPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbDictData, pageResult.getList().get(0)); - } - - @Test - public void testGetDictData() { - // mock 数据 - DictDataDO dbDictData = randomDictDataDO(); - dictDataMapper.insert(dbDictData); - // 准备参数 - Long id = dbDictData.getId(); - - // 调用 - DictDataDO dictData = dictDataService.getDictData(id); - // 断言 - assertPojoEquals(dbDictData, dictData); - } - - @Test - public void testCreateDictData_success() { - // 准备参数 - DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class, - o -> o.setStatus(randomCommonStatus())) - .setId(null); // 防止 id 被赋值 - // mock 方法 - when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); - - // 调用 - Long dictDataId = dictDataService.createDictData(reqVO); - // 断言 - assertNotNull(dictDataId); - // 校验记录的属性是否正确 - DictDataDO dictData = dictDataMapper.selectById(dictDataId); - assertPojoEquals(reqVO, dictData, "id"); - } - - @Test - public void testUpdateDictData_success() { - // mock 数据 - DictDataDO dbDictData = randomDictDataDO(); - dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 - // 准备参数 - DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class, o -> { - o.setId(dbDictData.getId()); // 设置更新的 ID - o.setStatus(randomCommonStatus()); - }); - // mock 方法,字典类型 - when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); - - // 调用 - dictDataService.updateDictData(reqVO); - // 校验是否更新正确 - DictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, dictData); - } - - @Test - public void testDeleteDictData_success() { - // mock 数据 - DictDataDO dbDictData = randomDictDataDO(); - dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbDictData.getId(); - - // 调用 - dictDataService.deleteDictData(id); - // 校验数据不存在了 - assertNull(dictDataMapper.selectById(id)); - } - - @Test - public void testValidateDictDataExists_success() { - // mock 数据 - DictDataDO dbDictData = randomDictDataDO(); - dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 - - // 调用成功 - dictDataService.validateDictDataExists(dbDictData.getId()); - } - - @Test - public void testValidateDictDataExists_notExists() { - assertServiceException(() -> dictDataService.validateDictDataExists(randomLongId()), DICT_DATA_NOT_EXISTS); - } - - @Test - public void testValidateDictTypeExists_success() { - // mock 方法,数据类型被禁用 - String type = randomString(); - when(dictTypeService.getDictType(eq(type))).thenReturn(randomDictTypeDO(type)); - - // 调用, 成功 - dictDataService.validateDictTypeExists(type); - } - - @Test - public void testValidateDictTypeExists_notExists() { - assertServiceException(() -> dictDataService.validateDictTypeExists(randomString()), DICT_TYPE_NOT_EXISTS); - } - - @Test - public void testValidateDictTypeExists_notEnable() { - // mock 方法,数据类型被禁用 - String dictType = randomString(); - when(dictTypeService.getDictType(eq(dictType))).thenReturn( - randomPojo(DictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - - // 调用, 并断言异常 - assertServiceException(() -> dictDataService.validateDictTypeExists(dictType), DICT_TYPE_NOT_ENABLE); - } - - @Test - public void testValidateDictDataValueUnique_success() { - // 调用,成功 - dictDataService.validateDictDataValueUnique(randomLongId(), randomString(), randomString()); - } - - @Test - public void testValidateDictDataValueUnique_valueDuplicateForCreate() { - // 准备参数 - String dictType = randomString(); - String value = randomString(); - // mock 数据 - dictDataMapper.insert(randomDictDataDO(o -> { - o.setDictType(dictType); - o.setValue(value); - })); - - // 调用,校验异常 - assertServiceException(() -> dictDataService.validateDictDataValueUnique(null, dictType, value), - DICT_DATA_VALUE_DUPLICATE); - } - - @Test - public void testValidateDictDataValueUnique_valueDuplicateForUpdate() { - // 准备参数 - Long id = randomLongId(); - String dictType = randomString(); - String value = randomString(); - // mock 数据 - dictDataMapper.insert(randomDictDataDO(o -> { - o.setDictType(dictType); - o.setValue(value); - })); - - // 调用,校验异常 - assertServiceException(() -> dictDataService.validateDictDataValueUnique(id, dictType, value), - DICT_DATA_VALUE_DUPLICATE); - } - - @Test - public void testGetDictDataCountByDictType() { - // mock 数据 - dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai"))); - dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("tudou"))); - dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai"))); - // 准备参数 - String dictType = "yunai"; - - // 调用 - long count = dictDataService.getDictDataCountByDictType(dictType); - // 校验 - assertEquals(2L, count); - } - - @Test - public void testValidateDictDataList_success() { - // mock 数据 - DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); - dictDataMapper.insert(dictDataDO); - // 准备参数 - String dictType = dictDataDO.getDictType(); - List values = singletonList(dictDataDO.getValue()); - - // 调用,无需断言 - dictDataService.validateDictDataList(dictType, values); - } - - @Test - public void testValidateDictDataList_notFound() { - // 准备参数 - String dictType = randomString(); - List values = singletonList(randomString()); - - // 调用, 并断言异常 - assertServiceException(() -> dictDataService.validateDictDataList(dictType, values), DICT_DATA_NOT_EXISTS); - } - - @Test - public void testValidateDictDataList_notEnable() { - // mock 数据 - DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); - dictDataMapper.insert(dictDataDO); - // 准备参数 - String dictType = dictDataDO.getDictType(); - List values = singletonList(dictDataDO.getValue()); - - // 调用, 并断言异常 - assertServiceException(() -> dictDataService.validateDictDataList(dictType, values), - DICT_DATA_NOT_ENABLE, dictDataDO.getLabel()); - } - - @Test - public void testGetDictData_dictType() { - // mock 数据 - DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setValue("1"); - dictDataMapper.insert(dictDataDO); - DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setValue("2"); - dictDataMapper.insert(dictDataDO02); - // 准备参数 - String dictType = "yunai"; - String value = "1"; - - // 调用 - DictDataDO dbDictData = dictDataService.getDictData(dictType, value); - // 断言 - assertEquals(dictDataDO, dbDictData); - } - - @Test - public void testParseDictData() { - // mock 数据 - DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setLabel("1"); - dictDataMapper.insert(dictDataDO); - DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setLabel("2"); - dictDataMapper.insert(dictDataDO02); - // 准备参数 - String dictType = "yunai"; - String label = "1"; - - // 调用 - DictDataDO dbDictData = dictDataService.parseDictData(dictType, label); - // 断言 - assertEquals(dictDataDO, dbDictData); - } - - // ========== 随机对象 ========== - - @SafeVarargs - private static DictDataDO randomDictDataDO(Consumer... consumers) { - Consumer consumer = (o) -> { - o.setStatus(randomCommonStatus()); // 保证 status 的范围 - }; - return randomPojo(DictDataDO.class, ArrayUtils.append(consumer, consumers)); - } - - /** - * 生成一个有效的字典类型 - * - * @param type 字典类型 - * @return DictTypeDO 对象 - */ - private static DictTypeDO randomDictTypeDO(String type) { - return randomPojo(DictTypeDO.class, o -> { - o.setType(type); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 是开启 - }); - } - -} diff --git a/yudao-server/src/main/resources/application-dev.yaml b/yudao-server/src/main/resources/application-dev.yaml index c10cdcb..e2038fc 100644 --- a/yudao-server/src/main/resources/application-dev.yaml +++ b/yudao-server/src/main/resources/application-dev.yaml @@ -55,7 +55,7 @@ spring: # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: redis: - host: 400-infra.server.iocoder.cn # 地址 + host: 127.0.0.1 # 地址 port: 6379 # 端口 database: 1 # 数据库索引 # password: 123456 # 密码,建议生产环境开启