# Conflicts:
#	ruoyi-modules/Sis/src/main/java/org/dromara/sis/rocketmq/RocketMqConstants.java
#	ruoyi-modules/Sis/src/main/java/org/dromara/sis/rocketmq/consumer/FaceCaptureConsumer.java
#	ruoyi-modules/Sis/src/main/java/org/dromara/sis/rocketmq/consumer/FaceCompareConsumer.java
This commit is contained in:
lxj
2025-08-27 18:01:31 +08:00
95 changed files with 1694 additions and 491 deletions

View File

@@ -140,6 +140,12 @@
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.drewnoakes</groupId>
<artifactId>metadata-extractor</artifactId>
<version>2.18.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>

View File

@@ -0,0 +1,75 @@
package org.dromara.sis.config;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author lsm
* @apiNote RocketMQClusterConfig
* @since 2025/8/26
*/
@Configuration
public class RocketMQClusterConfig {
// 从配置文件中读取 cluster 的配置
@Value("${rocketmq.cluster1.name-server}")
private String nameServer1;
@Value("${rocketmq.cluster1.producer.group}")
private String producerGroup1;
// 为第一个集群创建生产者实例
@Bean({"clusterProducerOne"})
public DefaultMQProducer clusterProducerOne() throws Exception {
DefaultMQProducer producer = new DefaultMQProducer(producerGroup1);
producer.setNamesrvAddr(nameServer1);
// 设置发送超时时间
producer.setSendMsgTimeout(5000);
// 设置重试次数
producer.setRetryTimesWhenSendFailed(2);
producer.setRetryTimesWhenSendAsyncFailed(2);
return producer;
}
// 使用上面的生产者实例创建 RocketMQTemplate
@Bean("rocketMQTemplateClusterOne")
public RocketMQTemplate rocketMQTemplateClusterOne(@Qualifier("clusterProducerOne") DefaultMQProducer producer) {
RocketMQTemplate template = new RocketMQTemplate();
template.setProducer(producer);
return template;
}
// 从配置文件中读取 cluster 的配置
// @Value("${rocketmq.cluster2.name-server}")
// private String nameServer2;
//
// @Value("${rocketmq.cluster2.producer.group}")
// private String producerGroup2;
//
// // 为第二个集群创建生产者实例
// @Bean({"clusterProducerTwo"})
// public DefaultMQProducer clusterProducerTwo() throws Exception {
// DefaultMQProducer producer = new DefaultMQProducer(producerGroup2);
// producer.setNamesrvAddr(nameServer2);
// // 设置发送超时时间
// producer.setSendMsgTimeout(5000);
// // 设置重试次数
// producer.setRetryTimesWhenSendFailed(2);
// producer.setRetryTimesWhenSendAsyncFailed(2);
// return producer;
// }
//
// // 使用上面的生产者实例创建 RocketMQTemplate
// @Bean("rocketMQTemplateClusterTwo")
// public RocketMQTemplate rocketMQTemplateClusterTwo(@Qualifier("clusterProducerTwo") DefaultMQProducer producer) {
// RocketMQTemplate template = new RocketMQTemplate();
// template.setProducer(producer);
// return template;
// }
}

View File

@@ -0,0 +1,85 @@
package org.dromara.sis.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import lombok.RequiredArgsConstructor;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.sis.domain.bo.SisPersonLibImgBo;
import org.dromara.sis.domain.vo.SisPersonLibImgVo;
import org.dromara.sis.sdk.e8.E8PlatformApi;
import org.dromara.sis.sdk.e8.domain.QueryDto;
import org.dromara.sis.sdk.e8.domain.accessControl.req.AccessRecordFindReq;
import org.dromara.sis.sdk.e8.domain.accessControl.res.AccessRecordFindRes;
import org.dromara.sis.service.ISisPersonLibImgService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @author yuyongle
* @version 1.0
* @description:
* @date 2025/8/27 15:57
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/visitor")
public class SisVisitorController {
private final E8PlatformApi e8PlatformApi;
/**
* 查询人员通行记录
*/
@GetMapping("/list")
public TableDataInfo<AccessRecordFindRes> list(QueryDto dto) {
TableDataInfo tableDataInfo = new TableDataInfo();
List<AccessRecordFindRes> accessRecordFindResList = new ArrayList<>();
AccessRecordFindRes accessRecordFindRes = new AccessRecordFindRes();
accessRecordFindRes.setDeviceName("4#岗亭09");
accessRecordFindRes.setDoorName("4#岗亭09");
accessRecordFindRes.setDeviceType(1102);
accessRecordFindRes.setReaderName("");
accessRecordFindRes.setGatewayType(1);
accessRecordFindRes.setCustomerName("德隆吴鹏");
accessRecordFindRes.setOrganFullPath("主楼11楼");
accessRecordFindRes.setPictureUrl("https://bpic.588ku.com/back_list_pic/23/04/21/ef5e2a3dd5cfc336fdcf2fd000474f0f.jpg");
accessRecordFindRes.setCardType(34);
accessRecordFindRes.setRecordType(2);
accessRecordFindRes.setActionTime(new Date());
accessRecordFindResList.add(accessRecordFindRes);
tableDataInfo.setRows(accessRecordFindResList);
tableDataInfo.setTotal(1);
tableDataInfo.setCode(200);
return tableDataInfo;
// dto.setPageIndex(1);
// dto.setMaxResultCount(20);
//
// // 10秒内
// String starTime = DateUtil.format(DateUtil.offset(new Date(), DateField.SECOND, -10), "yyyy-MM-dd HH:mm:ss");
// String endTime = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
//
// AccessRecordFindReq lift = new AccessRecordFindReq();
// lift.setStartTime(starTime);
// lift.setEndTime(endTime);
// lift.setRecordType(2);
// // 9号电梯
// lift.setDeviceId(550757939925061L);
// dto.setQueryDto(lift);
//// TableDataInfo<AccessRecordFindRes> nineLiftList = e8PlatformApi.getPageAccessRecordList(dto);
// TableDataInfo<AccessRecordFindRes> pageAccessRecordList = new TableDataInfo();
//
// return e8PlatformApi.getPageAccessRecordList(dto);
//如果pageAccessRecordList报错就捕获并封装
}
}

View File

@@ -0,0 +1,44 @@
package org.dromara.sis.rocketmq.consumer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.dromara.sis.rocketmq.RocketMqConstants;
import org.dromara.sis.rocketmq.producer.ProducerService;
import org.springframework.stereotype.Component;
/**
* @author lsm
* @apiNote MeterRecordConsumer
* @since 2025/8/25
*/
@Slf4j
@Component
@RequiredArgsConstructor
@RocketMQMessageListener(
topic = RocketMqConstants.TOPIC,
consumerGroup = RocketMqConstants.METER_GROUP,
selectorExpression = RocketMqConstants.METER_RECORD,
nameServer = "${rocketmq.cluster1.name-server}"
)
public class MeterRecordConsumer implements RocketMQListener<MessageExt> {
private final ProducerService producerService;
@Override
public void onMessage(MessageExt ext) {
try {
if (ext.getBody() == null) {
log.info("仪表上报消息数据,不转发!");
} else {
producerService.defaultSend(RocketMqConstants.TOPIC, RocketMqConstants.METER_RECORD, new String(ext.getBody()));
log.info("转发仪表上报数据处理成功");
}
} catch (Exception e) {
log.error("转发仪表上报数据处理失败,", e);
}
}
}

View File

@@ -0,0 +1,65 @@
package org.dromara.sis.rocketmq.producer;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
/**
* @author lsm
* @apiNote ProducerService
* @since 2025/8/26
*/
@Slf4j
@Component
public class ProducerService {
@Autowired
@Qualifier("rocketMQTemplateClusterOne")
private RocketMQTemplate rocketMQTemplateClusterOne;
// @Autowired
// @Qualifier("rocketMQTemplateClusterTwo")
// private RocketMQTemplate rocketMQTemplateClusterTwo;
/**
* 向mq写入消息
*
* @param topic 消息topic
* @param tag 消息tag
* @param msg 消息
*/
public void defaultSend(String topic, String tag, String msg) {
try {
String destination = topic + ":" + tag;
// 使用 RocketMQTemplate 的同步发送方法
rocketMQTemplateClusterOne.syncSend(destination, msg);
log.info("发送RocketMQOne消息成功, nameServer:{}", rocketMQTemplateClusterOne.getProducer().getNamesrvAddr());
} catch (Exception e) {
log.error("发送RocketMQOne消息失败", e);
}
}
/**
* 向mq写入消息
*
* @param topic 消息topic
* @param tag 消息tag
* @param msg 消息
*/
// public void clusterSend(String topic, String tag, String msg) {
// try {
// String destination = topic + ":" + tag;
// // 使用 RocketMQTemplate 的同步发送方法
// rocketMQTemplateClusterTwo.syncSend(destination, msg);
//
// log.info("发送RocketMQTwo消息成功, nameServer:{}", rocketMQTemplateClusterTwo.getProducer().getNamesrvAddr());
// } catch (Exception e) {
// log.error("发送RocketMQTwo消息失败", e);
// }
// }
}

View File

@@ -1,17 +1,21 @@
package org.dromara.sis.sdk.e8.utils;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Metadata;
import com.drew.metadata.MetadataException;
import com.drew.metadata.exif.ExifIFD0Directory;
import org.springframework.stereotype.Component;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.*;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Iterator;
/**
* @author lsm
@@ -22,7 +26,8 @@ import java.io.IOException;
public class ImageUtil {
public byte[] compressImageToRequirements(byte[] imageData) throws IOException {
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData));
// 读取图片并处理方向
BufferedImage image = readAndFixOrientation(imageData);
// 第一步调整分辨率不超过1000x1000
if (image.getWidth() > 1000 || image.getHeight() > 1000) {
@@ -91,6 +96,124 @@ public class ImageUtil {
return resizedImage;
}
/**
* 读取图片并处理 EXIF 方向信息
*/
private BufferedImage readAndFixOrientation(byte[] imageData) throws IOException {
try {
// 使用 metadata-extractor 读取 EXIF 方向信息
Metadata metadata = ImageMetadataReader.readMetadata(new ByteArrayInputStream(imageData));
ExifIFD0Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
int orientation = 1; // 默认正常方向
if (directory != null && directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
orientation = directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
}
// 读取图片
ImageInputStream input = ImageIO.createImageInputStream(new ByteArrayInputStream(imageData));
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
if (!readers.hasNext()) {
throw new IOException("No ImageReader found for image data");
}
ImageReader reader = readers.next();
reader.setInput(input);
BufferedImage image = reader.read(0);
reader.dispose();
// 根据方向信息旋转图片
return rotateImageAccordingToOrientation(image, orientation);
} catch (ImageProcessingException e) {
throw new IOException("Failed to process image metadata", e);
} catch (MetadataException e) {
throw new RuntimeException(e);
}
}
/**
* 根据方向信息旋转图片
*/
private BufferedImage rotateImageAccordingToOrientation(BufferedImage image, int orientation) {
int width = image.getWidth();
int height = image.getHeight();
return switch (orientation) {
case 1 -> // 正常
image;
case 2 -> // 水平翻转
flipImage(image, true, false);
case 3 -> // 旋转180度
rotateImage(image, 180);
case 4 -> // 垂直翻转
flipImage(image, false, true);
case 5 -> {
image = flipImage(image, true, false);
yield rotateImage(image, 270);
}
case 6 -> // 旋转90度
rotateImage(image, 90);
case 7 -> {
image = flipImage(image, true, false);
yield rotateImage(image, 90);
}
case 8 -> // 旋转270度
rotateImage(image, 270);
default -> image;
};
}
/**
* 旋转图片
*/
private BufferedImage rotateImage(BufferedImage image, double degrees) {
double radians = Math.toRadians(degrees);
double sin = Math.abs(Math.sin(radians));
double cos = Math.abs(Math.cos(radians));
int width = image.getWidth();
int height = image.getHeight();
int newWidth = (int) Math.floor(width * cos + height * sin);
int newHeight = (int) Math.floor(height * cos + width * sin);
BufferedImage rotated = new BufferedImage(newWidth, newHeight, image.getType());
Graphics2D g = rotated.createGraphics();
g.translate((newWidth - width) / 2, (newHeight - height) / 2);
g.rotate(Math.toRadians(degrees), width / 2.0, height / 2.0);
g.drawRenderedImage(image, null);
g.dispose();
return rotated;
}
/**
* 翻转图片
*/
private BufferedImage flipImage(BufferedImage image, boolean horizontal, boolean vertical) {
int width = image.getWidth();
int height = image.getHeight();
BufferedImage flipped = new BufferedImage(width, height, image.getType());
Graphics2D g = flipped.createGraphics();
if (horizontal && vertical) {
g.drawImage(image, width, height, -width, -height, null);
} else if (horizontal) {
g.drawImage(image, width, 0, -width, height, null);
} else if (vertical) {
g.drawImage(image, 0, height, width, -height, null);
} else {
g.drawImage(image, 0, 0, null);
}
g.dispose();
return flipped;
}
private byte[] compressWithQuality(BufferedImage image, float quality) throws IOException {
// 获取JPEG编码器
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();

View File

@@ -153,7 +153,7 @@ public class SisAlarmEventProcessServiceImpl implements ISisAlarmEventProcessSer
return null;
}
// 加载附件列表
List<SisAlarmEventAttachmentsVo> sisAlarmEventAttachmentsVos = sisAlarmEventAttachmentsService.queryListByAlarmId(alarmId);
List<SisAlarmEventAttachmentsVo> sisAlarmEventAttachmentsVos = sisAlarmEventAttachmentsService.queryListByAlarmId(sisAlarmEventProcessVo.getId());
sisAlarmEventProcessVo.setAttachments(sisAlarmEventAttachmentsVos);
return sisAlarmEventProcessVo;
}