[add] 违规记录

This commit is contained in:
lcj
2025-07-23 11:16:37 +08:00
parent e89c99d2ca
commit 71a9cfafa4
35 changed files with 1259 additions and 1230 deletions

View File

@ -1,591 +0,0 @@
package org.dromara.test;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.manager.spartamanager.SpartaManager;
import org.dromara.manager.spartamanager.enums.SpartaRecTypeEnum;
import org.dromara.manager.spartamanager.vo.ImageStreamResult;
import org.dromara.manager.spartamanager.vo.SpartaRecognizeVo;
import org.dromara.manager.spartamanager.vo.SpartaTargetVo;
import org.dromara.other.domain.dto.ys7deviceimg.OthYs7DeviceImgCreateByCapture;
import org.dromara.other.service.IOthYs7DeviceImgService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Date;
import java.util.List;
/**
* @author lcj
* @date 2025/6/20 19:40
*/
@Slf4j
@SpringBootTest
public class SpartaTest {
@Resource
private SpartaManager spartaManager;
@Resource
private IOthYs7DeviceImgService othYs7DeviceImgService;
@Test
public void test() {
String token = spartaManager.getToken();
log.info("token: {}", token);
}
@Test
public void test2() throws URISyntaxException, IOException {
String url = "http://58.17.134.85:9000/xinnengyuan-dev/ys7/device/img/sxt_1750583731809_227.jpg";
// List<SpartaRecTypeEnum> hat = List.of(SpartaRecTypeEnum.HAT, SpartaRecTypeEnum.HEAD);
// SpartaRecognizeVo recognize = spartaManager.recognize(
// url,
// hat,
// null,
// null,
// null
// );
// log.info("识别结果: {}", recognize);
SpartaRecognizeVo vo = new SpartaRecognizeVo();
vo.setHasTarget(1);
vo.setOriginalImgSize(List.of(2560, 1440));
SpartaTargetVo targets = new SpartaTargetVo();
targets.setType("hat");
targets.setSize(List.of(59, 78));
targets.setLeftTopPoint(List.of(880, 597));
targets.setScore(0.41687846183776855);
vo.setTargets(List.of(targets));
// 1. 读取图片
URI uri = new URI(url);
BufferedImage image = ImageIO.read(uri.toURL());
// 2. 创建画布
Graphics2D g = image.createGraphics();
g.setColor(Color.RED);
g.setStroke(new BasicStroke(5));
g.setFont(new Font("SansSerif", Font.BOLD, 18));
// 3. 遍历目标并画框
for (SpartaTargetVo target : vo.getTargets()) {
List<Integer> size = target.getSize(); // 宽高
List<Integer> leftTop = target.getLeftTopPoint(); // x y
int x = leftTop.get(0);
int y = leftTop.get(1);
int width = size.get(0);
int height = size.get(1);
// 画矩形框
g.drawRect(x, y, width, height);
// 写文字(类型 + 置信度)
String label = SpartaRecTypeEnum.fromValue(target.getType()).getText() + " (" + String.format("%.2f", target.getScore()) + ")";
g.drawString(label, x, y - 5);
}
g.dispose();
// 4. 保存标记后的图片
File outFile = new File("marked_output.jpg");
ImageIO.write(image, "jpg", outFile);
System.out.println("标记完成,保存路径: " + "marked_output.jpg");
}
@Test
public void test3() {
String url = "http://58.17.134.85:9000/xinnengyuan-dev/ys7/device/img/sxt_1750583731809_227.jpg";
OthYs7DeviceImgCreateByCapture img = new OthYs7DeviceImgCreateByCapture();
img.setCreateTime(new Date());
img.setUrl(url);
img.setDeviceName("摄像头1");
img.setDeviceSerial("sxt_1750583731809_227");
log.info("识别结果: {}", img);
othYs7DeviceImgService.saveCapturePic(List.of(img));
}
@Test
public void test4() throws IOException, URISyntaxException {
String url = "http://xny.yj-3d.com:7363/file/tif/20250625160218orthophoto.png";
String targetStr = "[\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 344,\n" +
" 149\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 3787,\n" +
" 3241\n" +
" ],\n" +
" \"score\": 0.9511\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 334,\n" +
" 147\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4276,\n" +
" 3590\n" +
" ],\n" +
" \"score\": 0.9498\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 330,\n" +
" 149\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 3459,\n" +
" 3183\n" +
" ],\n" +
" \"score\": 0.9496\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 353,\n" +
" 148\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4226,\n" +
" 3714\n" +
" ],\n" +
" \"score\": 0.9495\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 339,\n" +
" 132\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4276,\n" +
" 2678\n" +
" ],\n" +
" \"score\": 0.9486\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 340,\n" +
" 145\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4565,\n" +
" 2982\n" +
" ],\n" +
" \"score\": 0.9486\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 335,\n" +
" 142\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 3695,\n" +
" 3360\n" +
" ],\n" +
" \"score\": 0.9475\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 336,\n" +
" 145\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4446,\n" +
" 3357\n" +
" ],\n" +
" \"score\": 0.9473\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 338,\n" +
" 140\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4248,\n" +
" 2803\n" +
" ],\n" +
" \"score\": 0.9466\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 343,\n" +
" 155\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4565,\n" +
" 3769\n" +
" ],\n" +
" \"score\": 0.9464\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 346,\n" +
" 148\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4233,\n" +
" 2925\n" +
" ],\n" +
" \"score\": 0.9462\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 327,\n" +
" 159\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4000,\n" +
" 2484\n" +
" ],\n" +
" \"score\": 0.946\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 336,\n" +
" 145\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4906,\n" +
" 2908\n" +
" ],\n" +
" \"score\": 0.9457\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 339,\n" +
" 150\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 3618,\n" +
" 3477\n" +
" ],\n" +
" \"score\": 0.9456\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 318,\n" +
" 147\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 5241,\n" +
" 3883\n" +
" ],\n" +
" \"score\": 0.9448\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 328,\n" +
" 147\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4934,\n" +
" 3702\n" +
" ],\n" +
" \"score\": 0.943\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 312,\n" +
" 142\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 3633,\n" +
" 2557\n" +
" ],\n" +
" \"score\": 0.9428\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 340,\n" +
" 137\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4576,\n" +
" 2856\n" +
" ],\n" +
" \"score\": 0.9406\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 335,\n" +
" 138\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4316,\n" +
" 2548\n" +
" ],\n" +
" \"score\": 0.9398\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 345,\n" +
" 146\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4890,\n" +
" 3829\n" +
" ],\n" +
" \"score\": 0.9378\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 332,\n" +
" 153\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4126,\n" +
" 3304\n" +
" ],\n" +
" \"score\": 0.9371\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 325,\n" +
" 137\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 3581,\n" +
" 2944\n" +
" ],\n" +
" \"score\": 0.9318\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 320,\n" +
" 145\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 3879,\n" +
" 3123\n" +
" ],\n" +
" \"score\": 0.9304\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 343,\n" +
" 143\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4601,\n" +
" 3645\n" +
" ],\n" +
" \"score\": 0.9258\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 335,\n" +
" 142\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4673,\n" +
" 3528\n" +
" ],\n" +
" \"score\": 0.9255\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 312,\n" +
" 144\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4800,\n" +
" 3411\n" +
" ],\n" +
" \"score\": 0.923\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 337,\n" +
" 139\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4016,\n" +
" 3412\n" +
" ],\n" +
" \"score\": 0.9225\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 340,\n" +
" 141\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4605,\n" +
" 2729\n" +
" ],\n" +
" \"score\": 0.9191\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 298,\n" +
" 325\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 3249,\n" +
" 2576\n" +
" ],\n" +
" \"score\": 0.9185\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 338,\n" +
" 156\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4537,\n" +
" 2451\n" +
" ],\n" +
" \"score\": 0.9118\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 334,\n" +
" 143\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4933,\n" +
" 2783\n" +
" ],\n" +
" \"score\": 0.9114\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 339,\n" +
" 141\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4464,\n" +
" 2308\n" +
" ],\n" +
" \"score\": 0.9107\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 343,\n" +
" 143\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 3890,\n" +
" 3000\n" +
" ],\n" +
" \"score\": 0.9064\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 313,\n" +
" 139\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 3593,\n" +
" 2810\n" +
" ],\n" +
" \"score\": 0.902\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 259,\n" +
" 131\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 4000,\n" +
" 2755\n" +
" ],\n" +
" \"score\": 0.8869\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 345,\n" +
" 149\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 3901,\n" +
" 2864\n" +
" ],\n" +
" \"score\": 0.8841\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 345,\n" +
" 150\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 3945,\n" +
" 2610\n" +
" ],\n" +
" \"score\": 0.8632\n" +
" },\n" +
" {\n" +
" \"type\": \"pho\",\n" +
" \"size\": [\n" +
" 199,\n" +
" 110\n" +
" ],\n" +
" \"leftTopPoint\": [\n" +
" 5166,\n" +
" 3661\n" +
" ],\n" +
" \"score\": 0.8225\n" +
" }\n" +
" ]";
List<SpartaTargetVo> list = JSONUtil.toList(targetStr, SpartaTargetVo.class);
log.info("list:{}", list);
ImageStreamResult imageStreamResult = spartaManager.drawImageToStream(url, list);
log.info("imageStreamResult:{}", imageStreamResult);
try (OutputStream outputStream = new FileOutputStream("D:/images/save.png")) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = imageStreamResult.getInputStream().read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
}
}

View File

@ -0,0 +1,58 @@
package org.dromara.test;
import jakarta.annotation.Resource;
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordCreateDto;
import org.dromara.safety.service.IHseViolationRecordService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @author lcj
* @date 2025/7/22 15:36
*/
@SpringBootTest
public class ViolationRecordTest {
@Resource
private IHseViolationRecordService violationRecordService;
@Test
void test() {
List<HseViolationRecordCreateDto> list = new ArrayList<>();
HseViolationRecordCreateDto dto1 = new HseViolationRecordCreateDto();
dto1.setProjectId(1897160897167638529L);
dto1.setRecognizeId(1937451365735419905L);
dto1.setViolationType("3");
dto1.setViolationTime(new Date());
list.add(dto1);
HseViolationRecordCreateDto dto2 = new HseViolationRecordCreateDto();
dto2.setProjectId(1897160897167638529L);
dto2.setRecognizeId(1937451365735419906L);
dto2.setViolationType("1,2,3");
dto2.setViolationTime(new Date());
list.add(dto2);
HseViolationRecordCreateDto dto3 = new HseViolationRecordCreateDto();
dto3.setProjectId(1897160897167638529L);
dto3.setRecognizeId(1937451365735419907L);
dto3.setViolationType("2,7,8");
dto3.setViolationTime(new Date());
list.add(dto3);
HseViolationRecordCreateDto dto4 = new HseViolationRecordCreateDto();
dto4.setProjectId(1897160897167638529L);
dto4.setRecognizeId(1937451365735419908L);
dto4.setViolationType("5,8");
dto4.setViolationTime(new Date());
list.add(dto4);
HseViolationRecordCreateDto dto5 = new HseViolationRecordCreateDto();
dto5.setProjectId(1897160897167638529L);
dto5.setRecognizeId(1937451365735419909L);
dto5.setViolationType("3,9");
dto5.setViolationTime(new Date());
list.add(dto5);
violationRecordService.insertByMonitor(list);
}
}

View File

@ -26,7 +26,7 @@ import java.util.stream.Collectors;
* @date 2025/6/18 15:59
*/
@Slf4j
//@Component
@Component
public class IncSyncYs7DeviceCapturePicData {
@Resource

View File

@ -29,12 +29,17 @@ public class FullSyncYs7DeviceData implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
log.info("开始同步摄像头设备数据");
List<Ys7QueryDeviceResponseVo> ys7QueryDeviceList = ys7Manager.queryAllDeviceList();
Boolean result = ys7DeviceService.saveOrUpdateByDeviceList(ys7QueryDeviceList);
if (result) {
log.info("摄像头设备数据同步成功");
} else {
log.info("没有需要同步的设备");
try {
List<Ys7QueryDeviceResponseVo> ys7QueryDeviceList = ys7Manager.queryAllDeviceList();
Boolean result = ys7DeviceService.saveOrUpdateByDeviceList(ys7QueryDeviceList);
if (result) {
log.info("摄像头设备数据同步成功");
} else {
log.info("没有需要同步的设备");
}
} catch (Exception e) {
log.info("摄像头设备数据同步失败", e);
}
}
}

View File

@ -12,10 +12,10 @@ import java.util.stream.Collectors;
@Getter
public enum RecognizerTypeEnum {
HARDHAT("安全帽识别", "hardhat", ""),
VEST("反光背心", "vest", "1"),
HARDHAT("安全帽识别", "nohelmet", "1"),
VEST("反光背心识别", "novest", ""),
SMOKING("吸烟识别", "smoking", "3"),
FIRE("火焰识别", "fire", "2"),
FIRE("火焰识别", "fire", "16"),
PHO("光伏板识别", "pho", ""),
SHELVES("光伏板支架识别", "shelves", ""),
PILE("光伏板立柱识别", "pile", ""),

View File

@ -1,24 +0,0 @@
package org.dromara.manager.spartamanager;
/**
* @author lcj
* @date 2025/6/20 19:05
*/
public interface SpartaConstant {
/**
* token redis 缓存 key 前缀
*/
String TOKEN_REDIS_KEY = "sparta:token";
/**
* 获取 token api 路径
*/
String GET_TOKEN_API_PATH_GET = "/token";
/**
* 识别 api 路径
*/
String RECOGNIZE_API_PATH_GET = "/api/recognize";
}

View File

@ -1,170 +0,0 @@
package org.dromara.manager.spartamanager;
import cn.hutool.core.io.FileUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.manager.spartamanager.enums.SpartaRecTypeEnum;
import org.dromara.manager.spartamanager.vo.ImageStreamResult;
import org.dromara.manager.spartamanager.vo.SpartaRecognizeVo;
import org.dromara.manager.spartamanager.vo.SpartaTargetVo;
import org.dromara.manager.spartamanager.vo.SpartaTokenVo;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.FileNameMap;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLConnection;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author lcj
* @date 2025/6/20 19:27
*/
@Slf4j
@Component
public class SpartaManager {
@Resource
private SpartaProperties spartaProperties;
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* 获取token
*
* @return token
*/
public String getToken() {
String token = stringRedisTemplate.opsForValue().get(SpartaConstant.TOKEN_REDIS_KEY);
if (token != null) {
return token;
}
SpartaTokenVo tokenVo = SpartaRequestUtils.getToken(
spartaProperties.getUrl(),
spartaProperties.getClientId(),
spartaProperties.getClientSecret()
);
token = tokenVo.getToken();
Long expiresAt = tokenVo.getExpiresAt();
// 当前时间戳(单位:秒)
long now = System.currentTimeMillis() / 1000;
// 相差秒数
long diff = expiresAt - now;
if (diff <= 0) {
throw new ServiceException("token已过期");
}
stringRedisTemplate.opsForValue().set(SpartaConstant.TOKEN_REDIS_KEY, token, (diff - 3600), TimeUnit.SECONDS);
return token;
}
/**
* 识别图片
*
* @param capUrl 图片地址
* @param recTypeList 识别类型
* @param async 是否异步
* @param callBackUrl 回调地址
* @param areaHigh 区域高
* @return 识别结果
*/
public SpartaRecognizeVo recognize(String capUrl, List<SpartaRecTypeEnum> recTypeList, Boolean async, String callBackUrl, String areaHigh) {
String token = getToken();
String recType = SpartaRecTypeEnum.joinRecTypes(recTypeList);
return SpartaRequestUtils.recognize(
spartaProperties.getUrl(),
token,
capUrl,
recType,
async,
callBackUrl,
areaHigh
);
}
/**
* 识别图片
*
* @param capUrl 图片地址
* @param recTypeList 识别类型
* @return 识别结果
*/
public SpartaRecognizeVo recognize(String capUrl, List<SpartaRecTypeEnum> recTypeList) {
return recognize(capUrl, recTypeList, false, null, null);
}
/**
* 绘制图片
*
* @param imgUrl 图片地址
* @param targets 识别结果
* @return 绘制后的图片
*/
public ImageStreamResult drawImageToStream(String imgUrl, List<SpartaTargetVo> targets) throws IOException, URISyntaxException {
// 1. 加载图片
URI uri = new URI(imgUrl);
BufferedImage image = ImageIO.read(uri.toURL());
// 2. 开始绘图
Graphics2D g = image.createGraphics();
g.setColor(Color.RED);
g.setStroke(new BasicStroke(5));
// 设置中文兼容字体(或使用指定字体)
g.setFont(new Font("SansSerif", Font.BOLD, 18));
for (SpartaTargetVo target : targets) {
int x = target.getLeftTopPoint().get(0);
int y = target.getLeftTopPoint().get(1);
int w = target.getSize().get(0);
int h = target.getSize().get(1);
// 画矩形框
g.drawRect(x, y, w, h);
// 写文字(类型 + 置信度)
String label = target.getType() + " (" + String.format("%.2f", target.getScore()) + ")";
g.drawString(label, x, y - 5);
}
g.dispose();
// 3. 输出为 InputStream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String filename = extractFilename(imgUrl);
String suffix = FileUtil.getSuffix(filename);
ImageIO.write(image, suffix == null ? "jpg" : suffix, baos);
return new ImageStreamResult(new ByteArrayInputStream(baos.toByteArray()), baos.size(), getContentTypeByFilename(filename));
}
/**
* 提取文件名
*
* @param url 文件路径
* @return 文件名
*/
private static String extractFilename(String url) {
int start = url.lastIndexOf("/") + 1;
int end = url.indexOf("?", start);
if (end == -1) {
return url.substring(start);
}
if (start > 0 && end > start) {
return url.substring(start, end);
}
return null;
}
/**
* 根据文件名获取文件类型
*
* @param filename 文件名
* @return 文件类型
*/
private static String getContentTypeByFilename(String filename) {
FileNameMap fileNameMap = URLConnection.getFileNameMap();
return fileNameMap.getContentTypeFor(filename);
}
}

View File

@ -1,30 +0,0 @@
package org.dromara.manager.spartamanager;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author lcj
* @date 2025/6/20 19:01
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "sparta")
public class SpartaProperties {
/**
* 请求地址
*/
private String url;
/**
* 客户端id
*/
private String clientId;
/**
* 客户端密钥
*/
private String clientSecret;
}

View File

@ -1,110 +0,0 @@
package org.dromara.manager.spartamanager;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.manager.spartamanager.enums.SpartaHasTargetEnum;
import org.dromara.manager.spartamanager.vo.SpartaRecognizeVo;
import org.dromara.manager.spartamanager.vo.SpartaTargetVo;
import org.dromara.manager.spartamanager.vo.SpartaTokenVo;
import java.util.HashMap;
import java.util.List;
/**
* @author lcj
* @date 2025/6/20 19:06
*/
@Slf4j
public class SpartaRequestUtils {
/**
* 获取斯巴达 Token
*
* @param host 域名
* @param clientId 客户端id
* @param clientSecret 客户端密钥
* @return token
*/
public static SpartaTokenVo getToken(String host, String clientId, String clientSecret) {
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("clientId", clientId);
paramMap.put("clientSecret", clientSecret);
String errorMsg = "斯巴达 Token 请求失败";
String url = host + SpartaConstant.GET_TOKEN_API_PATH_GET;
try (HttpResponse response = HttpRequest.get(url)
.form(paramMap)
.execute()) {
if (!response.isOk()) {
log.error("{}{}", errorMsg, response.getStatus());
throw new ServiceException(errorMsg + response.getStatus());
}
String body = response.body();
if (body == null) {
log.error("{}{}", errorMsg, "返回参数为空");
}
log.info("斯巴达 Token 请求成功:{}", body);
JSONObject jsonObject = JSONUtil.parseObj(body);
SpartaTokenVo tokenVo = new SpartaTokenVo();
tokenVo.setToken(jsonObject.get("token", String.class));
tokenVo.setExpiresAt(jsonObject.get("expires_at", Long.class));
return tokenVo;
}
}
/**
* 识别图片
*
* @param host 域名
* @param token token
* @param capUrl 在线图片地址
* @param recType 识别算法模型,多选模式每个参数之间用空格隔开例
* @param async 是否异步处理
* @param callBackUrl 回调地址Post
* @param areaHigh 高空识别项目格式为勾选范围的左上角xy坐标以及宽中间用空格隔开
* @return 识别结果
*/
public static SpartaRecognizeVo recognize(String host, String token, String capUrl,
String recType, Boolean async, String callBackUrl, String areaHigh) {
if (StringUtils.isAnyBlank(host, token, capUrl)) {
throw new ServiceException("斯巴达识别图片参数为空", HttpStatus.HTTP_BAD_REQUEST);
}
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("capUrl", capUrl);
paramMap.put("recType", recType);
paramMap.put("async", async != null && async ? "True" : "False");
paramMap.put("callBackUrl", callBackUrl);
paramMap.put("area_high", areaHigh);
String errorMsg = "斯巴达识别图片请求失败";
String url = host + SpartaConstant.RECOGNIZE_API_PATH_GET;
try (HttpResponse response = HttpRequest.get(url)
.header("Authorization", "Basic " + token)
.form(paramMap)
.execute()) {
if (!response.isOk()) {
log.error("{}{}", errorMsg, response.getStatus());
throw new ServiceException(errorMsg + response.getStatus());
}
String body = response.body();
if (body == null) {
log.error("{}{}", errorMsg, "返回参数为空");
}
JSONObject result = JSONUtil.parseObj(response.body());
log.info("斯巴达识别图片请求成功:{}", body);
SpartaRecognizeVo spartaRecognizeVo = new SpartaRecognizeVo();
Integer hasTarget = result.getInt("hasTarget");
spartaRecognizeVo.setHasTarget(hasTarget);
if (hasTarget.equals(SpartaHasTargetEnum.YES.getValue())) {
spartaRecognizeVo.setOriginalImgSize(result.getJSONArray("originalImgSize").toList(Integer.class));
List<SpartaTargetVo> targetList = JSONUtil.toList(result.getJSONArray("targets"), SpartaTargetVo.class);
spartaRecognizeVo.setTargets(targetList);
}
return spartaRecognizeVo;
}
}
}

View File

@ -1,22 +0,0 @@
package org.dromara.manager.spartamanager.enums;
import lombok.Getter;
/**
* @author lcj
* @date 2025/6/23 9:51
*/
@Getter
public enum SpartaHasTargetEnum {
YES("", 1),
NO("", 0);
private final String text;
private final int value;
SpartaHasTargetEnum(String text, int value) {
this.text = text;
this.value = value;
}
}

View File

@ -1,69 +0,0 @@
package org.dromara.manager.spartamanager.enums;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author lcj
* @date 2025/6/23 9:42
*/
@Getter
public enum SpartaRecTypeEnum {
HAT("安全帽识别", "hat", ""),
HEAD("不戴安全帽识别", "head", "1"),
SMOKE("吸烟识别", "smoke", "3"),
BELT("安全带识别", "belt", "2"),
WASTE("工程垃圾识别(暂无)", "waste", ""),
EXCAVATOR("挖掘机", "excavator", ""),
ROLLER("压路机", "Roller", ""),
TRUCK_CRANE("汽车吊", "Truck_crane", ""),
LOADER("装载机", "Loader", ""),
SUBMERSIBLE_DRILLING_RIG("潜挖钻机", "Submersible_drilling_rig", ""),
SPRINKLER("洒水车", "Sprinkler", ""),
TRUCK_MOUNTED_CRANE("随车吊", "Truck_mounted_crane", ""),
TRUCK("货车", "Truck", ""),
PHO("光伏板", "pho", ""),
HOLE("", "hole", ""),
SHELVES("架子", "shelves", ""),
PILE("", "pile", "");
private final String text;
private final String value;
/**
* 描述对应字典violation_level_type
*/
private final String code;
SpartaRecTypeEnum(String text, String value, String code) {
this.text = text;
this.value = value;
this.code = code;
}
public static SpartaRecTypeEnum fromValue(String value) {
for (SpartaRecTypeEnum type : SpartaRecTypeEnum.values()) {
if (type.getValue().equals(value)) {
return type;
}
}
return null;
}
/**
* 将多个 SpartaRecTypeEnum 拼接为接口识别参数字符串(空格分隔)
* 示例hat belt Truck
*/
public static String joinRecTypes(List<SpartaRecTypeEnum> types) {
if (types == null || types.isEmpty()) {
return "";
}
return types.stream()
.map(SpartaRecTypeEnum::getValue)
.collect(Collectors.joining(" "));
}
}

View File

@ -1,31 +0,0 @@
package org.dromara.manager.spartamanager.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.InputStream;
/**
* @author lcj
* @date 2025/6/23 14:44
*/
@Data
@AllArgsConstructor
public class ImageStreamResult {
/**
* 图片输入流
*/
private InputStream inputStream;
/**
* 图片长度
*/
private long length;
/**
* 图片类型
*/
private String contentType;
}

View File

@ -1,29 +0,0 @@
package org.dromara.manager.spartamanager.vo;
import lombok.Data;
import java.util.List;
/**
* @author lcj
* @date 2025/6/20 19:25
*/
@Data
public class SpartaRecognizeVo {
/**
* 是否监测到目标10
*/
private Integer hasTarget;
/**
* 原始图片尺寸([宽,高]ex[1920,1080]
*/
private List<Integer> originalImgSize;
/**
* 目标信息
*/
private List<SpartaTargetVo> targets;
}

View File

@ -1,37 +0,0 @@
package org.dromara.manager.spartamanager.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author lcj
* @date 2025/6/23 11:22
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SpartaTargetVo {
/**
* 目标类型
*/
private String type;
/**
* 目标外接矩形像素
*/
private List<Integer> size;
/**
* 目标在画面中左上角位置信息
*/
private List<Integer> leftTopPoint;
/**
* 置信度得分0~1
*/
private Double score;
}

View File

@ -1,21 +0,0 @@
package org.dromara.manager.spartamanager.vo;
import lombok.Data;
/**
* @author lcj
* @date 2025/6/20 19:36
*/
@Data
public class SpartaTokenVo {
/**
* token
*/
private String token;
/**
* 过期时间
*/
private Long expiresAt;
}

View File

@ -20,8 +20,6 @@ import org.dromara.manager.recognizermanager.enums.RecognizerTypeEnum;
import org.dromara.manager.recognizermanager.vo.RecognizeImageStreamResult;
import org.dromara.manager.recognizermanager.vo.RecognizeTargetVo;
import org.dromara.manager.recognizermanager.vo.RecognizeVo;
import org.dromara.manager.spartamanager.enums.SpartaHasTargetEnum;
import org.dromara.manager.spartamanager.enums.SpartaRecTypeEnum;
import org.dromara.other.constant.Ys7DeviceImgConstant;
import org.dromara.other.domain.OthYs7DeviceImg;
import org.dromara.other.domain.dto.ys7deviceimg.OthYs7DeviceImgCreateByCapture;
@ -180,7 +178,7 @@ public class OthYs7DeviceImgServiceImpl extends ServiceImpl<OthYs7DeviceImgMappe
vo.setUrl(deviceImg.getRecognizeUrl());
}
List<String> recTypeList = JSONUtil.toList(deviceImg.getRecType(), String.class);
List<String> list = recTypeList.stream().map(recType -> Objects.requireNonNull(SpartaRecTypeEnum.fromValue(recType)).getText()).toList();
List<String> list = recTypeList.stream().map(recType -> Objects.requireNonNull(RecognizerTypeEnum.fromValue(recType)).getText()).toList();
vo.setRecTypeList(list);
return vo;
}).toList();
@ -214,7 +212,7 @@ public class OthYs7DeviceImgServiceImpl extends ServiceImpl<OthYs7DeviceImgMappe
// 将抓取的图片进行识别
List<RecognizerTypeEnum> recTypes = List.of(RecognizerTypeEnum.HARDHAT, RecognizerTypeEnum.SMOKING, RecognizerTypeEnum.FIRE);
RecognizeVo recognizeVo = recognizerManager.recognize(ossUrl, recTypes);
if (recognizeVo != null && recognizeVo.getHasTarget().equals(SpartaHasTargetEnum.YES.getValue())) {
if (recognizeVo != null && recognizeVo.getHasTarget().equals(RecognizerHasTargetEnum.YES.getValue())) {
// 记录识别信息
HseRecognizeRecordCreateDto record = new HseRecognizeRecordCreateDto();
record.setCreateTime(new Date());

View File

@ -31,4 +31,6 @@ public interface HseSafetyConstant {
return String.format("安全生产监督检查通知书(%s.docx", createDate);
}
String VIOLATION_LEVEL_TYPE = "violation_level_type";
}

View File

@ -0,0 +1,81 @@
package org.dromara.safety.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.dromara.common.core.domain.R;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordCreateHandlerReq;
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordQueryReq;
import org.dromara.safety.domain.vo.violationrecord.HseViolationRecordVo;
import org.dromara.safety.service.IHseViolationRecordService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 违规记录
*
* @author lcj
* @date 2025-07-22
*/
@Validated
@RestController
@RequestMapping("/safety/violationRecord")
public class HseViolationRecordController extends BaseController {
@Resource
private IHseViolationRecordService hseViolationRecordService;
/**
* 查询违规记录列表
*/
@SaCheckPermission("safety:violationRecord:list")
@GetMapping("/list")
public TableDataInfo<HseViolationRecordVo> list(HseViolationRecordQueryReq req, PageQuery pageQuery) {
return hseViolationRecordService.queryPageList(req, pageQuery);
}
/**
* 获取违规记录详细信息
*
* @param id 主键
*/
@SaCheckPermission("safety:violationRecord:query")
@GetMapping("/{id}")
public R<HseViolationRecordVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(hseViolationRecordService.queryById(id));
}
/**
* 新增违规记录处理人
*/
@SaCheckPermission("safety:violationRecord:insertHandler")
@Log(title = "违规记录", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/handler")
public R<Void> insertHandler(@Validated @RequestBody HseViolationRecordCreateHandlerReq req) {
return toAjax(hseViolationRecordService.insertHandler(req));
}
/**
* 删除违规记录
*
* @param ids 主键串
*/
@SaCheckPermission("safety:violationRecord:remove")
@Log(title = "违规记录", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(hseViolationRecordService.deleteWithValidByIds(List.of(ids)));
}
}

View File

@ -0,0 +1,112 @@
package org.dromara.safety.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.io.Serial;
import java.util.Date;
/**
* 违规记录对象 hse_violation_record
*
* @author lcj
* @date 2025-07-22
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("hse_violation_record")
public class HseViolationRecord extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@TableId(value = "id")
private Long id;
/**
* 项目id
*/
private Long projectId;
/**
* 违章等级id
*/
private Long levelId;
/**
* 识别记录id
*/
private Long recognizeId;
/**
* 违章类型
*/
private String violationType;
/**
* 违章时间
*/
private Date violationTime;
/**
* 违章处理人id
*/
private Long handlerId;
/**
* 处理期限
*/
private Date disposeDeadline;
/**
* 处理时间
*/
private Date disposeTime;
/**
* 整改措施
*/
private String measure;
/**
* 整改时间
*/
private Date rectificationTime;
/**
* 复查情况
*/
private String review;
/**
* 复查状态1通过 2未通过
*/
private String reviewType;
/**
* 复查时间
*/
private Date reviewTime;
/**
* 处理流程类型(0仅通知 1通知整改复查)
*/
private String processType;
/**
* 工单状态1通知 2整改 3复查
*/
private String status;
/**
* 备注
*/
private String remark;
}

View File

@ -48,6 +48,7 @@ public class HseViolationLevelCreateReq implements Serializable {
/**
* 违章类型(多个逗号分隔)
*/
@NotNull(message = "违章类型不能为空")
private String violationType;
}

View File

@ -0,0 +1,44 @@
package org.dromara.safety.domain.dto.violationrecord;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* @author lcj
* @date 2025/7/22 14:28
*/
@Data
public class HseViolationRecordCreateDto implements Serializable {
@Serial
private static final long serialVersionUID = 5895177891570353747L;
/**
* 项目id
*/
private Long projectId;
/**
* 识别记录id
*/
private Long recognizeId;
/**
* 违章类型
*/
private String violationType;
/**
* 违章时间
*/
private Date violationTime;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,49 @@
package org.dromara.safety.domain.dto.violationrecord;
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* @author lcj
* @date 2025/7/22 11:07
*/
@Data
public class HseViolationRecordCreateHandlerReq implements Serializable {
@Serial
private static final long serialVersionUID = -5274907523390625095L;
/**
* 主键id
*/
@NotNull(message = "id不能为空")
private Long id;
/**
* 违章处理人id
*/
@NotNull(message = "处理人不能为空")
private Long handlerId;
/**
* 处理期限
*/
@NotNull(message = "处理期限不能为空")
@Future(message = "处理期限不能小于当前时间")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date disposeDeadline;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,41 @@
package org.dromara.safety.domain.dto.violationrecord;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* @author lcj
* @date 2025/7/22 11:06
*/
@Data
public class HseViolationRecordQueryReq implements Serializable {
@Serial
private static final long serialVersionUID = 1084576574388146447L;
/**
* 主键id
*/
private Long id;
/**
* 项目id
*/
private Long projectId;
/**
* 违章类型
*/
private String violationType;
/**
* 违章时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date violationTime;
}

View File

@ -0,0 +1,145 @@
package org.dromara.safety.domain.vo.violationrecord;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.safety.domain.HseViolationRecord;
import org.dromara.safety.domain.vo.recognizerecord.HseRecognizeRecordVo;
import org.dromara.safety.domain.vo.violationlevel.HseViolationLevelVo;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 违规记录视图对象 hse_violation_record
*
* @author lcj
* @date 2025-07-22
*/
@Data
@AutoMapper(target = HseViolationRecord.class)
public class HseViolationRecordVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
private Long id;
/**
* 项目id
*/
private Long projectId;
/**
* 违章等级id
*/
private Long levelId;
/**
* 违章等级
*/
private HseViolationLevelVo levelVo;
/**
* 识别记录id
*/
private Long recognizeId;
/**
* 识别记录
*/
private HseRecognizeRecordVo recognizeVo;
/**
* 违章类型
*/
private String violationType;
/**
* 违章时间
*/
private Date violationTime;
/**
* 违章处理人id
*/
private Long handlerId;
/**
* 违章处理人名称
*/
private String handlerName;
/**
* 处理期限
*/
private Date disposeDeadline;
/**
* 处理时间
*/
private Date disposeTime;
/**
* 整改措施
*/
private String measure;
/**
* 整改时间
*/
private Date rectificationTime;
/**
* 复查情况
*/
private String review;
/**
* 复查状态1通过 2未通过
*/
private String reviewType;
/**
* 复查时间
*/
private Date reviewTime;
/**
* 处理流程类型(0仅通知 1通知整改复查)
*/
private String processType;
/**
* 工单状态1通知 2整改 3复查
*/
private String status;
/**
* 创建人
*/
private Long createBy;
/**
* 创建人名字
*/
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "createBy")
private String createByName;
/**
* 创建时间
*/
private Date createTime;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,15 @@
package org.dromara.safety.mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.safety.domain.HseViolationRecord;
import org.dromara.safety.domain.vo.violationrecord.HseViolationRecordVo;
/**
* 违规记录Mapper接口
*
* @author lcj
* @date 2025-07-22
*/
public interface HseViolationRecordMapper extends BaseMapperPlus<HseViolationRecord, HseViolationRecordVo> {
}

View File

@ -79,7 +79,15 @@ public interface IHseViolationLevelService extends IService<HseViolationLevel> {
* @param violationLevel 违章等级对象
* @return 违章等级视图对象
*/
HseViolationLevelByPostVo getVo(HseViolationLevel violationLevel);
HseViolationLevelByPostVo getByPostVo(HseViolationLevel violationLevel);
/**
* 获取违章等级视图对象
*
* @param violationLevel 违章等级对象
* @return 违章等级视图对象
*/
HseViolationLevelVo getVo(HseViolationLevel violationLevel);
/**
* 获取违章等级查询条件封装

View File

@ -0,0 +1,97 @@
package org.dromara.safety.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.safety.domain.HseViolationRecord;
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordCreateDto;
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordCreateHandlerReq;
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordQueryReq;
import org.dromara.safety.domain.vo.violationrecord.HseViolationRecordVo;
import java.util.Collection;
import java.util.List;
/**
* 违规记录Service接口
*
* @author lcj
* @date 2025-07-22
*/
public interface IHseViolationRecordService extends IService<HseViolationRecord> {
/**
* 查询违规记录
*
* @param id 主键
* @return 违规记录
*/
HseViolationRecordVo queryById(Long id);
/**
* 分页查询违规记录列表
*
* @param req 查询条件
* @param pageQuery 分页参数
* @return 违规记录分页列表
*/
TableDataInfo<HseViolationRecordVo> queryPageList(HseViolationRecordQueryReq req, PageQuery pageQuery);
/**
* 查询符合条件的违规记录列表
*
* @param req 查询条件
* @return 违规记录列表
*/
List<HseViolationRecordVo> queryList(HseViolationRecordQueryReq req);
/**
* 新增违规记录信息
*
* @param dto 违规记录信息
*/
void insertByMonitor(List<HseViolationRecordCreateDto> dto);
/**
* 新增违章处理人
*
* @param req 违章处理人
* @return 是否新增成功
*/
Boolean insertHandler(HseViolationRecordCreateHandlerReq req);
/**
* 批量删除违规记录信息
*
* @param ids 待删除的主键集合
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids);
/**
* 获取违规记录视图对象
*
* @param violationRecord 违规记录对象
* @return 违规记录视图对象
*/
HseViolationRecordVo getVo(HseViolationRecord violationRecord);
/**
* 获取违规记录查询条件封装
*
* @param req 违规记录查询条件
* @return 违规记录查询条件封装
*/
LambdaQueryWrapper<HseViolationRecord> buildQueryWrapper(HseViolationRecordQueryReq req);
/**
* 获取违规记录分页对象视图
*
* @param violationRecordPage 违规记录分页对象
* @return 违规记录分页对象视图
*/
Page<HseViolationRecordVo> getVoPage(Page<HseViolationRecord> violationRecordPage);
}

View File

@ -12,15 +12,17 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.manager.recognizermanager.enums.RecognizerTypeEnum;
import org.dromara.manager.recognizermanager.vo.RecognizeTargetVo;
import org.dromara.manager.spartamanager.enums.SpartaRecTypeEnum;
import org.dromara.project.service.IBusProjectService;
import org.dromara.safety.domain.HseRecognizeRecord;
import org.dromara.safety.domain.dto.recognizerecord.HseRecognizeRecordCreateDto;
import org.dromara.safety.domain.dto.recognizerecord.HseRecognizeRecordQueryReq;
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordCreateDto;
import org.dromara.safety.domain.vo.recognizerecord.HseRecognizeRecordVo;
import org.dromara.safety.mapper.HseRecognizeRecordMapper;
import org.dromara.safety.service.IHseRecognizeRecordService;
import org.dromara.safety.service.IHseViolationRecordService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -46,6 +48,9 @@ public class HseRecognizeRecordServiceImpl extends ServiceImpl<HseRecognizeRecor
@Resource
private IBusProjectService projectService;
@Resource
private IHseViolationRecordService violationRecordService;
/**
* 查询识别记录
*
@ -110,8 +115,8 @@ public class HseRecognizeRecordServiceImpl extends ServiceImpl<HseRecognizeRecor
List<RecognizeTargetVo> targets = record.getTargets();
List<String> codeList = targets.stream()
.map(RecognizeTargetVo::getType).distinct()
.map(SpartaRecTypeEnum::fromValue).filter(Objects::nonNull)
.map(SpartaRecTypeEnum::getCode).filter(Objects::nonNull)
.map(RecognizerTypeEnum::fromValue).filter(Objects::nonNull)
.map(RecognizerTypeEnum::getCode).filter(Objects::nonNull)
.toList();
String codeStr = String.join(",", codeList);
entity.setViolationType(codeStr);
@ -123,6 +128,20 @@ public class HseRecognizeRecordServiceImpl extends ServiceImpl<HseRecognizeRecor
if (!result) {
throw new ServiceException("保存识别记录失败");
}
List<HseViolationRecordCreateDto> violationRecordList = new ArrayList<>();
for (HseRecognizeRecord entity : entityList) {
HseViolationRecordCreateDto violationRecord = new HseViolationRecordCreateDto();
if (entity.getProjectId() != null) {
violationRecord.setProjectId(entity.getProjectId());
violationRecord.setRecognizeId(entity.getId());
violationRecord.setViolationType(entity.getViolationType());
violationRecord.setViolationTime(entity.getCreateTime());
violationRecordList.add(violationRecord);
}
}
if (CollUtil.isNotEmpty(violationRecordList)) {
violationRecordService.insertByMonitor(violationRecordList);
}
}
}

View File

@ -13,6 +13,7 @@ import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.project.service.IBusProjectService;
import org.dromara.safety.constant.HseSafetyConstant;
import org.dromara.safety.domain.HseViolationLevel;
import org.dromara.safety.domain.HseViolationLevelPost;
import org.dromara.safety.domain.dto.violationlevel.HseViolationLevelCreateReq;
@ -24,15 +25,14 @@ import org.dromara.safety.mapper.HseViolationLevelMapper;
import org.dromara.safety.service.IHseViolationLevelPostService;
import org.dromara.safety.service.IHseViolationLevelService;
import org.dromara.system.domain.vo.SysPostVo;
import org.dromara.system.service.ISysPostService;
import org.dromara.system.mapper.SysPostMapper;
import org.dromara.system.service.ISysDictDataService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
/**
* 违章等级Service业务层处理
@ -51,7 +51,10 @@ public class HseViolationLevelServiceImpl extends ServiceImpl<HseViolationLevelM
private IHseViolationLevelPostService violationLevelPostService;
@Resource
private ISysPostService postService;
private SysPostMapper postMapper;
@Resource
private ISysDictDataService dictDataService;
/**
* 查询违章等级
@ -65,7 +68,7 @@ public class HseViolationLevelServiceImpl extends ServiceImpl<HseViolationLevelM
if (violationLevel == null) {
throw new ServiceException("违章等级信息不存在", HttpStatus.NOT_FOUND);
}
return this.getVo(violationLevel);
return this.getByPostVo(violationLevel);
}
/**
@ -107,6 +110,10 @@ public class HseViolationLevelServiceImpl extends ServiceImpl<HseViolationLevelM
@Override
@Transactional(rollbackFor = Exception.class)
public Long insertByBo(HseViolationLevelCreateReq req) {
Long projectId = req.getProjectId();
if (projectService.getById(projectId) == null) {
throw new ServiceException("项目不存在", HttpStatus.NOT_FOUND);
}
HseViolationLevel violationLevel = new HseViolationLevel();
BeanUtils.copyProperties(req, violationLevel);
// 校验参数
@ -115,12 +122,46 @@ public class HseViolationLevelServiceImpl extends ServiceImpl<HseViolationLevelM
if (CollUtil.isEmpty(postIdList)) {
throw new ServiceException("岗位不能为空", HttpStatus.BAD_REQUEST);
}
// 判断违章等级是否存在
Long count = this.lambdaQuery()
.eq(HseViolationLevel::getViolationLevel, violationLevel.getViolationLevel())
.count();
if (count > 0) {
throw new ServiceException("违章等级已存在", HttpStatus.BAD_REQUEST);
// 判断违章等级或风险等级是否存在
List<HseViolationLevel> existList = this.lambdaQuery()
.eq(HseViolationLevel::getProjectId, projectId)
.and(wrapper -> wrapper
.eq(HseViolationLevel::getViolationLevel, violationLevel.getViolationLevel())
.or()
.eq(HseViolationLevel::getRiskType, violationLevel.getRiskType())
)
.list();
for (HseViolationLevel exist : existList) {
if (exist.getViolationLevel().equals(violationLevel.getViolationLevel())) {
throw new ServiceException("违章等级已存在", HttpStatus.BAD_REQUEST);
}
if (exist.getRiskType().equals(violationLevel.getRiskType())) {
throw new ServiceException("风险等级已存在", HttpStatus.BAD_REQUEST);
}
}
// 拆分 violationType
Set<String> violationTypeList = Arrays.stream(req.getViolationType().split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toSet());
if (CollUtil.isEmpty(violationTypeList)) {
throw new ServiceException("违章类型不能为空", HttpStatus.BAD_REQUEST);
}
// 判断违章类型是否存在
List<String> allViolationTypeList = this.lambdaQuery()
.select(HseViolationLevel::getViolationType)
.eq(HseViolationLevel::getProjectId, projectId)
.list().stream()
.flatMap(v -> Arrays.stream(v.getViolationType().split(",")))
.map(String::trim)
.distinct()
.toList();
// 判断是否存在交集(只要一个存在就抛异常)
for (String type : violationTypeList) {
if (allViolationTypeList.contains(type)) {
String dictLabel = dictDataService.selectDictLabel(HseSafetyConstant.VIOLATION_LEVEL_TYPE, type);
throw new ServiceException("违章类型 [" + dictLabel + "] 已存在,不能重复添加", HttpStatus.BAD_REQUEST);
}
}
boolean result = this.save(violationLevel);
if (!result) {
@ -158,6 +199,58 @@ public class HseViolationLevelServiceImpl extends ServiceImpl<HseViolationLevelM
if (oldViolationLevel == null) {
throw new ServiceException("修改的违章等级信息不存在", HttpStatus.NOT_FOUND);
}
// 判断违章等级是否存在
if (!oldViolationLevel.getViolationLevel().equals(violationLevel.getViolationLevel())) {
Long count = this.lambdaQuery()
.eq(HseViolationLevel::getProjectId, oldViolationLevel.getProjectId())
.eq(HseViolationLevel::getViolationLevel, violationLevel.getViolationLevel())
.count();
if (count > 0) {
throw new ServiceException("该项目已存在同名违章等级", HttpStatus.CONFLICT);
}
}
// 判断风险等级是否存在
if (!oldViolationLevel.getRiskType().equals(violationLevel.getRiskType())) {
Long count = this.lambdaQuery()
.eq(HseViolationLevel::getProjectId, oldViolationLevel.getProjectId())
.eq(HseViolationLevel::getRiskType, violationLevel.getRiskType())
.count();
if (count > 0) {
throw new ServiceException("该项目已存在同名风险等级", HttpStatus.CONFLICT);
}
}
Set<String> newTypeSet = Arrays.stream(req.getViolationType().split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toSet());
if (CollUtil.isEmpty(newTypeSet)) {
throw new ServiceException("违章类型不能为空", HttpStatus.BAD_REQUEST);
}
Set<String> oldTypeSet = Arrays.stream(oldViolationLevel.getViolationType().split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toSet());
// 如果新旧类型完全相同,则不需要校验
if (!newTypeSet.equals(oldTypeSet)) {
// 查询其他记录的 violationType排除当前这条记录
List<String> otherViolationTypes = this.lambdaQuery()
.select(HseViolationLevel::getViolationType)
.eq(HseViolationLevel::getProjectId, oldViolationLevel.getProjectId())
.ne(HseViolationLevel::getId, id)
.list().stream()
.flatMap(v -> Arrays.stream(v.getViolationType().split(",")))
.map(String::trim)
.filter(s -> !s.isEmpty())
.distinct()
.toList();
// 检查是否有交集
for (String type : newTypeSet) {
if (otherViolationTypes.contains(type)) {
String dictLabel = dictDataService.selectDictLabel(HseSafetyConstant.VIOLATION_LEVEL_TYPE, type);
throw new ServiceException("违章类型 [" + dictLabel + "] 已存在,不能重复添加", HttpStatus.BAD_REQUEST);
}
}
}
List<Long> postIdList = req.getPostIdList();
if (CollUtil.isNotEmpty(postIdList)) {
List<Long> oldPostIdList = violationLevelPostService.lambdaQuery()
@ -251,7 +344,7 @@ public class HseViolationLevelServiceImpl extends ServiceImpl<HseViolationLevelM
* @return 违章等级视图对象
*/
@Override
public HseViolationLevelByPostVo getVo(HseViolationLevel violationLevel) {
public HseViolationLevelByPostVo getByPostVo(HseViolationLevel violationLevel) {
HseViolationLevelByPostVo vo = new HseViolationLevelByPostVo();
if (violationLevel == null) {
return vo;
@ -262,12 +355,28 @@ public class HseViolationLevelServiceImpl extends ServiceImpl<HseViolationLevelM
.list();
if (CollUtil.isNotEmpty(levelPostList)) {
List<Long> postIdList = levelPostList.stream().map(HseViolationLevelPost::getPost).toList();
List<SysPostVo> postList = postService.selectPostByIds(postIdList);
List<SysPostVo> postList = postMapper.selectVoByIds(postIdList);
vo.setPostList(postList);
}
return vo;
}
/**
* 获取违章等级视图对象
*
* @param violationLevel 违章等级对象
* @return 违章等级视图对象
*/
@Override
public HseViolationLevelVo getVo(HseViolationLevel violationLevel) {
HseViolationLevelVo vo = new HseViolationLevelVo();
if (violationLevel == null) {
return vo;
}
BeanUtils.copyProperties(violationLevel, vo);
return vo;
}
/**
* 获取违章等级查询条件封装
*

View File

@ -0,0 +1,361 @@
package org.dromara.safety.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.sse.dto.SseMessageDto;
import org.dromara.common.sse.utils.SseMessageUtils;
import org.dromara.safety.domain.HseRecognizeRecord;
import org.dromara.safety.domain.HseViolationLevel;
import org.dromara.safety.domain.HseViolationLevelPost;
import org.dromara.safety.domain.HseViolationRecord;
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordCreateDto;
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordCreateHandlerReq;
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordQueryReq;
import org.dromara.safety.domain.vo.violationrecord.HseViolationRecordVo;
import org.dromara.safety.mapper.HseViolationRecordMapper;
import org.dromara.safety.service.IHseRecognizeRecordService;
import org.dromara.safety.service.IHseViolationLevelPostService;
import org.dromara.safety.service.IHseViolationLevelService;
import org.dromara.safety.service.IHseViolationRecordService;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
/**
* 违规记录Service业务层处理
*
* @author lcj
* @date 2025-07-22
*/
@Slf4j
@Service
public class HseViolationRecordServiceImpl extends ServiceImpl<HseViolationRecordMapper, HseViolationRecord>
implements IHseViolationRecordService {
@Resource
private ISysUserService userService;
@Resource
private IHseViolationLevelService violationLevelService;
@Lazy
@Resource
private IHseRecognizeRecordService recognizeRecordService;
@Resource
private IHseViolationLevelPostService violationLevelPostService;
/**
* 查询违规记录
*
* @param id 主键
* @return 违规记录
*/
@Override
public HseViolationRecordVo queryById(Long id) {
HseViolationRecord violationRecord = this.getById(id);
if (violationRecord == null) {
throw new ServiceException("违规记录信息不存在", HttpStatus.NOT_FOUND);
}
return this.getVo(violationRecord);
}
/**
* 分页查询违规记录列表
*
* @param req 查询条件
* @param pageQuery 分页参数
* @return 违规记录分页列表
*/
@Override
public TableDataInfo<HseViolationRecordVo> queryPageList(HseViolationRecordQueryReq req, PageQuery pageQuery) {
Page<HseViolationRecord> violationRecordPage = this.page(pageQuery.build(), this.buildQueryWrapper(req));
return TableDataInfo.build(this.getVoPage(violationRecordPage));
}
/**
* 查询符合条件的违规记录列表
*
* @param req 查询条件
* @return 违规记录列表
*/
@Override
public List<HseViolationRecordVo> queryList(HseViolationRecordQueryReq req) {
LambdaQueryWrapper<HseViolationRecord> lqw = buildQueryWrapper(req);
return this.list(lqw).stream().map(this::getVo).toList();
}
/**
* 新增违规记录信息
*
* @param dto 违规记录信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void insertByMonitor(List<HseViolationRecordCreateDto> dto) {
if (CollUtil.isEmpty(dto)) {
return;
}
List<HseViolationRecord> violationRecordList = new ArrayList<>();
// 1. 获取所有相关项目的风险等级配置
Set<Long> projectIds = dto.stream()
.map(HseViolationRecordCreateDto::getProjectId)
.collect(Collectors.toSet());
List<HseViolationLevel> violationLevelList = violationLevelService.lambdaQuery()
.in(HseViolationLevel::getProjectId, projectIds)
.list();
// 2. 构建 "项目 -> (违规类型 -> 等级ID)" 的映射
Map<Long, Map<String, Long>> projectTypeToLevelIdMap = new HashMap<>();
for (HseViolationLevel level : violationLevelList) {
Long projectId = level.getProjectId();
String[] types = level.getViolationType().split(",");
Map<String, Long> typeMap = projectTypeToLevelIdMap.computeIfAbsent(projectId, k -> new HashMap<>());
for (String type : types) {
typeMap.put(type.trim(), level.getId());
}
}
// 3. 遍历DTO按LevelId对ViolationType分组然后创建记录
for (HseViolationRecordCreateDto dtoItem : dto) {
if (dtoItem.getProjectId() == null) {
continue;
}
Map<String, Long> typeToLevelIdMap = projectTypeToLevelIdMap.get(dtoItem.getProjectId());
if (typeToLevelIdMap == null) {
log.warn("项目[{}]中找不到任何违章等级定义", dtoItem.getProjectId());
continue;
}
// 创建一个临时Map用于根据LevelId对本次DTO中的types进行分组
Map<Long, List<String>> levelIdToTypesMap = new HashMap<>();
String[] typesFromDto = dtoItem.getViolationType().split(",");
for (String type : typesFromDto) {
type = type.trim();
Long levelId = typeToLevelIdMap.get(type);
if (levelId == null) {
log.warn("项目[{}]中找不到违章类型[{}]对应的违章等级", dtoItem.getProjectId(), type);
continue;
}
// 将当前type添加到对应levelId的列表中
levelIdToTypesMap.computeIfAbsent(levelId, k -> new ArrayList<>()).add(type);
}
// 4. 为每个分组即每个LevelId创建一条合并后的记录
for (Map.Entry<Long, List<String>> entry : levelIdToTypesMap.entrySet()) {
Long levelId = entry.getKey();
List<String> groupedTypesList = entry.getValue();
HseViolationRecord record = new HseViolationRecord();
record.setProjectId(dtoItem.getProjectId());
record.setRecognizeId(dtoItem.getRecognizeId());
record.setViolationTime(dtoItem.getViolationTime());
record.setRemark(dtoItem.getRemark());
record.setLevelId(levelId); // 设置分组的LevelId
// 将分组后的types用逗号拼接设置为合并后的ViolationType
record.setViolationType(String.join(",", groupedTypesList));
violationRecordList.add(record);
}
}
// 批量保存
if (CollUtil.isNotEmpty(violationRecordList)) {
violationRecordList.forEach(violationRecord -> violationRecord.setCreateBy(SystemConstants.SUPER_ADMIN_ID));
boolean result = this.saveBatch(violationRecordList);
if (!result) {
throw new ServiceException("批量新增违规记录失败,数据库异常", HttpStatus.ERROR);
}
// 通知对应人员
Set<Long> levelIds = violationRecordList.stream().map(HseViolationRecord::getLevelId).collect(Collectors.toSet());
List<HseViolationLevelPost> levelPostList = violationLevelPostService.lambdaQuery()
.in(HseViolationLevelPost::getLevel, levelIds)
.list();
List<Long> postIdList = levelPostList.stream().map(HseViolationLevelPost::getPost).distinct().toList();
List<SysUserVo> userVoList = userService.selectUserListByPostList(postIdList);
if (CollUtil.isNotEmpty(userVoList)) {
// 通过 sse 向指定用户发送通知
Set<Long> userIds = userVoList.stream().map(SysUserVo::getUserId).collect(Collectors.toSet());
SseMessageDto messageDto = new SseMessageDto();
messageDto.setUserIds(new ArrayList<>(userIds));
for (HseViolationRecord violationRecord : violationRecordList) {
messageDto.setMessage("您有一份重要的违章需要处理!工单号为:" + violationRecord.getId());
SseMessageUtils.publishMessage(messageDto);
}
}
}
}
/**
* 新增违章处理人
*
* @param req 违章处理人
* @return 是否新增成功
*/
@Override
public Boolean insertHandler(HseViolationRecordCreateHandlerReq req) {
Long id = req.getId();
HseViolationRecord oldViolationRecord = this.getById(id);
if (oldViolationRecord == null) {
throw new ServiceException("违规记录信息不存在", HttpStatus.NOT_FOUND);
}
Long handlerId = req.getHandlerId();
SysUserVo user = userService.selectUserById(handlerId);
if (user == null) {
throw new ServiceException("处理人信息不存在", HttpStatus.NOT_FOUND);
}
HseViolationRecord violationRecord = new HseViolationRecord();
violationRecord.setId(id);
violationRecord.setHandlerId(handlerId);
violationRecord.setDisposeTime(req.getDisposeDeadline());
violationRecord.setRemark(req.getRemark());
boolean result = this.updateById(violationRecord);
if (!result) {
throw new ServiceException("更新处理人操作失败", HttpStatus.ERROR);
}
return true;
}
/**
* 批量删除违规记录信息
*
* @param ids 待删除的主键集合
* @return 是否删除成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids) {
return this.removeBatchByIds(ids);
}
/**
* 获取违规记录视图对象
*
* @param violationRecord 违规记录对象
* @return 违规记录视图对象
*/
@Override
public HseViolationRecordVo getVo(HseViolationRecord violationRecord) {
HseViolationRecordVo violationRecordVo = new HseViolationRecordVo();
if (violationRecord == null) {
return violationRecordVo;
}
BeanUtils.copyProperties(violationRecord, violationRecordVo);
// 关联等级
Long levelId = violationRecord.getLevelId();
if (ObjectUtils.isNotEmpty(levelId)) {
HseViolationLevel level = violationLevelService.getById(levelId);
violationRecordVo.setLevelVo(violationLevelService.getVo(level));
}
// 关联识别记录
Long recognizeId = violationRecord.getRecognizeId();
if (ObjectUtils.isNotEmpty(recognizeId)) {
HseRecognizeRecord recognizeRecord = recognizeRecordService.getById(recognizeId);
violationRecordVo.setRecognizeVo(recognizeRecordService.getVo(recognizeRecord));
}
// 关联违章人信息
Long handlerId = violationRecord.getHandlerId();
if (ObjectUtils.isNotEmpty(handlerId)) {
SysUserVo handler = userService.selectUserById(handlerId);
violationRecordVo.setHandlerName(handler.getNickName());
}
return violationRecordVo;
}
/**
* 获取违规记录查询条件封装
*
* @param req 违规记录查询条件
* @return 违规记录查询条件封装
*/
@Override
public LambdaQueryWrapper<HseViolationRecord> buildQueryWrapper(HseViolationRecordQueryReq req) {
LambdaQueryWrapper<HseViolationRecord> lqw = new LambdaQueryWrapper<>();
if (req == null) {
return lqw;
}
Long id = req.getId();
Long projectId = req.getProjectId();
String violationType = req.getViolationType();
Date violationTime = req.getViolationTime();
lqw.eq(ObjectUtils.isNotEmpty(id), HseViolationRecord::getId, id);
lqw.eq(ObjectUtils.isNotEmpty(projectId), HseViolationRecord::getProjectId, projectId);
lqw.eq(StringUtils.isNotBlank(violationType), HseViolationRecord::getViolationType, violationType);
if (violationTime != null) {
// 构造当天的起始和结束时间
LocalDate localDate = violationTime.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
LocalDateTime startOfDay = localDate.atStartOfDay();
LocalDateTime startOfNextDay = localDate.plusDays(1).atStartOfDay();
Date start = Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant());
Date end = Date.from(startOfNextDay.atZone(ZoneId.systemDefault()).toInstant());
lqw.ge(HseViolationRecord::getViolationTime, start)
.lt(HseViolationRecord::getViolationTime, end);
}
return lqw;
}
/**
* 获取违规记录分页对象视图
*
* @param violationRecordPage 违规记录分页对象
* @return 违规记录分页对象视图
*/
@Override
public Page<HseViolationRecordVo> getVoPage(Page<HseViolationRecord> violationRecordPage) {
List<HseViolationRecord> violationRecordList = violationRecordPage.getRecords();
Page<HseViolationRecordVo> violationRecordVoPage = new Page<>(
violationRecordPage.getCurrent(),
violationRecordPage.getSize(),
violationRecordPage.getTotal());
if (CollUtil.isEmpty(violationRecordList)) {
return violationRecordVoPage;
}
// 关联等级
Set<Long> levelIds = violationRecordList.stream().map(HseViolationRecord::getLevelId).collect(Collectors.toSet());
Map<Long, HseViolationLevel> levelIdLevelMap = violationLevelService.listByIds(levelIds).stream()
.collect(Collectors.toMap(HseViolationLevel::getId, level -> level));
// 关联识别记录
Set<Long> recognizeIds = violationRecordList.stream().map(HseViolationRecord::getRecognizeId).collect(Collectors.toSet());
Map<Long, HseRecognizeRecord> recognizeIdRecognizeMap = recognizeRecordService.listByIds(recognizeIds).stream()
.collect(Collectors.toMap(HseRecognizeRecord::getId, recognize -> recognize));
// 关联违章人信息
List<Long> handlerIds = violationRecordList.stream().map(HseViolationRecord::getHandlerId).distinct().toList();
Map<Long, SysUserVo> handlerIdUserMap = userService.selectUserByIds(handlerIds, null).stream()
.collect(Collectors.toMap(SysUserVo::getUserId, user -> user));
List<HseViolationRecordVo> violationRecordVoList = violationRecordList.stream().map(violationLevel -> {
HseViolationRecordVo vo = new HseViolationRecordVo();
BeanUtils.copyProperties(violationLevel, vo);
// 关联等级
if (CollUtil.isNotEmpty(levelIdLevelMap) && levelIdLevelMap.containsKey(violationLevel.getLevelId())) {
vo.setLevelVo(violationLevelService.getVo(levelIdLevelMap.get(violationLevel.getLevelId())));
}
// 关联识别记录
if (CollUtil.isNotEmpty(recognizeIdRecognizeMap) && recognizeIdRecognizeMap.containsKey(violationLevel.getRecognizeId())) {
vo.setRecognizeVo(recognizeRecordService.getVo(recognizeIdRecognizeMap.get(violationLevel.getRecognizeId())));
}
// 违章人信息
if (CollUtil.isNotEmpty(handlerIdUserMap) && handlerIdUserMap.containsKey(violationLevel.getHandlerId())) {
vo.setHandlerName(handlerIdUserMap.get(violationLevel.getHandlerId()).getNickName());
}
return vo;
}).toList();
violationRecordVoPage.setRecords(violationRecordVoList);
return violationRecordVoPage;
}
}

View File

@ -219,4 +219,12 @@ public interface ISysUserService {
* @return 结果
*/
List<SysUserVo> selectUserListByDept(Long deptId);
/**
* 通过岗位id列表查询岗位所有用户
*
* @param postIds 岗位id列表
* @return 结果
*/
List<SysUserVo> selectUserListByPostList(List<Long> postIds);
}

View File

@ -614,6 +614,24 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
return baseMapper.selectVoList(lqw);
}
/**
* 通过岗位id列表查询岗位所有用户
*
* @param postIds 岗位id列表
* @return 结果
*/
@Override
public List<SysUserVo> selectUserListByPostList(List<Long> postIds) {
LambdaQueryWrapper<SysUserPost> lqw = Wrappers.lambdaQuery();
lqw.in(SysUserPost::getPostId, postIds);
List<SysUserPost> userPostList = userPostMapper.selectList(lqw);
if (CollUtil.isNotEmpty(userPostList)){
List<Long> userIds = userPostList.stream().map(SysUserPost::getUserId).distinct().toList();
return baseMapper.selectVoByIds(userIds);
}
return List.of();
}
/**
* 通过用户ID查询用户账户
*

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.safety.mapper.HseViolationRecordMapper">
</mapper>

View File

@ -1743,44 +1743,34 @@ insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component,
values (1942399191372275718, '无人机配置导出', 1942399191372275713, '5', '#', '', 1, 0, 'F', '0', '0',
'drone:droneConfig:export', '#', 103, 1, sysdate(), null, null, '');
-- 菜单 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible,
status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values (1947491501058625537, '违规记录', '1935597155897143297', '1', 'violationRecord', 'safety/violationRecord/index',
1, 0, 'C', '0', '0', 'safety:violationRecord:list', '#', 103, 1, sysdate(), null, null, '违规记录菜单');
CREATE TABLE `bus_violation_record`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`pid` bigint DEFAULT '0' COMMENT '父级主键ID',
`project_id` bigint DEFAULT NULL COMMENT '项目id',
`level_id` bigint DEFAULT NULL COMMENT '违章等级主键ID',
`level` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '违章等级(字符串)',
`tour_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '违章类型(字典)',
`tour_id` bigint DEFAULT NULL COMMENT '违章详情关联bus_tour',
`data_source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '数据来源violation_record_data_source',
`select_people` bigint DEFAULT NULL COMMENT '选择人后台用户ID',
`openid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'openid施工人员',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '工单状态1通知 2整改 3复查',
`processing_period` varchar(22) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '处理期限(要求)',
`processing_period_practical` varchar(22) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '处理期限(实际)',
`measure` varchar(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '整改措施',
`review` varchar(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '复查情况',
`review_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '复查状态1通过 2未通过',
`rectification_time` datetime DEFAULT NULL COMMENT '整改时间',
`review_time` datetime DEFAULT NULL COMMENT '复查时间',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建者',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者',
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
`deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
`remark` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
`wx_or_pc` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '来源1pc 2小程序',
`picture` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '图片',
`processing_time` date DEFAULT NULL COMMENT '处理日期',
`is_dispose` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1' COMMENT '是否处理1待处理 2已处理',
`labor_date` varchar(22) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '人工日期(数据来源人工才会有此日期)',
`sxt_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '摄像头名称',
PRIMARY KEY (`id`) USING BTREE,
KEY `select_people` (`select_people`) USING BTREE,
KEY `openid` (`openid`) USING BTREE
) ENGINE = InnoDB
AUTO_INCREMENT = 262
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci
ROW_FORMAT = DYNAMIC COMMENT ='工单管理/违规记录';
-- 按钮 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible,
status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values (1947491501058625538, '违规记录查询', 1947491501058625537, '1', '#', '', 1, 0, 'F', '0', '0',
'safety:violationRecord:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible,
status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values (1947491501058625539, '违规记录新增', 1947491501058625537, '2', '#', '', 1, 0, 'F', '0', '0',
'safety:violationRecord:add', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible,
status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values (1947491501058625540, '违规记录修改', 1947491501058625537, '3', '#', '', 1, 0, 'F', '0', '0',
'safety:violationRecord:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible,
status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values (1947491501058625541, '违规记录删除', 1947491501058625537, '4', '#', '', 1, 0, 'F', '0', '0',
'safety:violationRecord:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible,
status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values (1947491501058625542, '违规记录导出', 1947491501058625537, '5', '#', '', 1, 0, 'F', '0', '0',
'safety:violationRecord:export', '#', 103, 1, sysdate(), null, null, '');

View File

@ -1564,31 +1564,26 @@ CREATE TABLE `dro_drone_config`
DROP TABLE IF EXISTS `hse_violation_record`;
CREATE TABLE `hse_violation_record`
(
`id` bigint not null auto_increment comment '主键id',
`project_id` bigint not null comment '项目id',
`level_id` bigint DEFAULT NULL COMMENT '违章等级主键ID',
`level` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '违章等级(字符串)',
`tour_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '违章类型(字典)',
`tour_id` bigint DEFAULT NULL COMMENT '违章详情关联bus_tour',
`data_source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '数据来源violation_record_data_source',
`select_people` bigint DEFAULT NULL COMMENT '选择人后台用户ID',
`openid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'openid施工人员',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '工单状态1通知 2整改 3复查',
`processing_period` varchar(22) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '处理期限(要求)',
`processing_period_practical` varchar(22) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '处理期限(实际)',
`measure` varchar(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '整改措施',
`review` varchar(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '复查情况',
`review_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '复查状态1通过 2未通过',
`rectification_time` datetime DEFAULT NULL COMMENT '整改时间',
`review_time` datetime DEFAULT NULL COMMENT '复查时间',
`picture` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '图片',
`processing_time` date DEFAULT NULL COMMENT '处理日期',
`is_dispose` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1' COMMENT '是否处理1待处理 2已处理',
`labor_date` varchar(22) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '人工日期(数据来源人工才会有此日期)',
`sxt_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '摄像头名称',
`remark` text null comment '备注',
`id` bigint not null auto_increment comment '主键id',
`project_id` bigint not null comment '项目id',
`level_id` bigint not null comment '违章等级id',
`recognize_id` bigint not null comment '识别记录id',
`violation_type` varchar(32) null comment '违章类型',
`violation_time` datetime null comment '违章时间',
`handler_id` bigint null comment '违章处理人id',
`dispose_deadline` date null comment '处理期限',
`dispose_time` datetime null comment '处理时间',
`measure` varchar(300) null comment '整改措施',
`rectification_time` datetime null comment '整改时间',
`review` varchar(255) null comment '复查情况',
`review_type` char(1) null comment '复查状态1通过 2未通过',
`review_time` datetime null comment '复查时间',
`process_type` char(1) default '1' not null comment '处理流程类型(0仅通知 1通知整改复查)',
`status` char(1) default '1' not null comment '工单状态1通知 2整改 3复查',
`remark` varchar(255) null comment '备注',
`create_by` bigint null comment '创建者',
`update_by` bigint null comment '更新者',
`create_dept` bigint null comment '创建部门',
`create_time` datetime default CURRENT_TIMESTAMP null comment '创建时间',
`update_time` datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',
primary key (`id`) using btree,