From e75a9171160b7a7653d5167947a3630dfaee8720 Mon Sep 17 00:00:00 2001
From: cwchen <1048842385@qq.com>
Date: Fri, 9 Jan 2026 10:13:15 +0800
Subject: [PATCH] =?UTF-8?q?=E7=94=BB=E7=BA=BF=E7=AE=97=E6=B3=95=E8=B0=83?=
=?UTF-8?q?=E7=94=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
bonus-admin/pom.xml | 13 ++
.../data/DeviceIdentificationService.java | 124 +++++++++--
.../web/service/data/ExportExcelService.java | 78 +++++--
.../bonus/web/service/data/ExportService.java | 12 +-
.../bonus/web/service/data/LineService.java | 37 +++-
.../main/resources/application-algorithm.yml | 5 +
bonus-algorithm/pom.xml | 27 +++
.../algorithm/service/DrawLinesService.java | 205 ++++++++++++++++++
.../com/bonus/common/config/BonusConfig.java | 11 +
.../algorithm/dto/DrawLinesRequest.java | 34 +++
.../algorithm/vo/DrawLinesResponse.java | 33 +++
.../common/domain/data/vo/ImageRecord.java | 4 +
.../com/bonus/common/enums/ResultCode.java | 34 +++
.../mapper/DIDeviceIdentificationMapper.java | 28 +++
.../DIDeviceIdentificationService.java | 22 ++
.../DDeviceIdentificationServiceImpl.java | 12 +
.../mapper/DDeviceIdentificationMapper.xml | 46 +++-
bonus-framework/pom.xml | 4 -
.../bonus/quartz/task/FileCleanupTask.java | 4 +-
pom.xml | 1 +
20 files changed, 677 insertions(+), 57 deletions(-)
create mode 100644 bonus-admin/src/main/resources/application-algorithm.yml
create mode 100644 bonus-algorithm/pom.xml
create mode 100644 bonus-algorithm/src/main/java/com/bonus/algorithm/service/DrawLinesService.java
create mode 100644 bonus-common/src/main/java/com/bonus/common/domain/algorithm/dto/DrawLinesRequest.java
create mode 100644 bonus-common/src/main/java/com/bonus/common/domain/algorithm/vo/DrawLinesResponse.java
create mode 100644 bonus-common/src/main/java/com/bonus/common/enums/ResultCode.java
diff --git a/bonus-admin/pom.xml b/bonus-admin/pom.xml
index 20f9926..261b1c2 100644
--- a/bonus-admin/pom.xml
+++ b/bonus-admin/pom.xml
@@ -77,6 +77,19 @@
com.bonus
bonus-data
+
+
+
+ com.bonus
+ bonus-algorithm
+
+
+ com.bonus
+ bonus-algorithm
+ 3.9.0
+ compile
+
+
diff --git a/bonus-admin/src/main/java/com/bonus/web/service/data/DeviceIdentificationService.java b/bonus-admin/src/main/java/com/bonus/web/service/data/DeviceIdentificationService.java
index c1013d2..cdea817 100644
--- a/bonus-admin/src/main/java/com/bonus/web/service/data/DeviceIdentificationService.java
+++ b/bonus-admin/src/main/java/com/bonus/web/service/data/DeviceIdentificationService.java
@@ -2,16 +2,19 @@ package com.bonus.web.service.data;
import com.bonus.common.core.domain.AjaxResult;
import com.bonus.common.domain.data.dto.ParamsDto;
+import com.bonus.common.domain.data.vo.DataRecognitionExportVO;
import com.bonus.common.domain.data.vo.DeviceIdentificationDataCountVo;
import com.bonus.common.domain.data.vo.DeviceIdentificationDataVo;
import com.bonus.common.domain.data.vo.ImageRecord;
import com.bonus.data.service.DIDeviceIdentificationService;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
@@ -35,18 +38,7 @@ public class DeviceIdentificationService {
public List getDeviceIdentificationList(ParamsDto dto) {
try {
- if (dto.getStartTime() == null || dto.getEndTime() == null) {
- // 获取今天的日期
- LocalDateTime now = LocalDateTime.now();
- // 设置开始时间为 00:00:00
- LocalDateTime startOfDay = now.with(LocalTime.MIN);
- // 设置结束时间为 23:59:59 (也可以使用 LocalTime.MAX)
- LocalDateTime endOfDay = now.with(LocalTime.MAX);
- // 转换为 Date 赋值给 dto (如果你的字段类型是 Date)
- ZoneId zone = ZoneId.systemDefault();
- dto.setStartTime(Date.from(startOfDay.atZone(zone).toInstant()));
- dto.setEndTime(Date.from(endOfDay.atZone(zone).toInstant()));
- }
+ dto = setTime(dto);
List result = deviceIdentificationService.getDeviceIdentificationList(dto);
// 处理图片-待实现
return result != null ? result : Collections.emptyList();
@@ -67,6 +59,7 @@ public class DeviceIdentificationService {
public AjaxResult getDeviceIdentificationByDataDetail(ParamsDto dto) {
DeviceIdentificationDataCountVo vo = null;
try {
+ dto = setTime(dto);
List result = deviceIdentificationService.getDeviceIdentificationList(dto);
vo = calculateStatistics(result, dto.getStartTime(), dto.getEndTime());
@@ -117,6 +110,7 @@ public class DeviceIdentificationService {
/**
* 查询要下载的识别图片
+ *
* @param dto
* @return List
* @author cwchen
@@ -124,19 +118,101 @@ public class DeviceIdentificationService {
*/
public List getImageData(ParamsDto dto) {
try {
- if (dto.getStartTime() == null || dto.getEndTime() == null) {
- // 获取今天的日期
- LocalDateTime now = LocalDateTime.now();
- // 设置开始时间为 00:00:00
- LocalDateTime startOfDay = now.with(LocalTime.MIN);
- // 设置结束时间为 23:59:59 (也可以使用 LocalTime.MAX)
- LocalDateTime endOfDay = now.with(LocalTime.MAX);
- // 转换为 Date 赋值给 dto (如果你的字段类型是 Date)
- ZoneId zone = ZoneId.systemDefault();
- dto.setStartTime(Date.from(startOfDay.atZone(zone).toInstant()));
- dto.setEndTime(Date.from(endOfDay.atZone(zone).toInstant()));
- }
+ dto = setTime(dto);
List result = deviceIdentificationService.getImageData(dto);
+ if(CollectionUtils.isEmpty(result)){
+ return Collections.emptyList();
+ }else{
+ for (ImageRecord vo : result) {
+ // 1. 获取原始路径并提取最后一个 "_" 之后的部分
+ String photoPath = vo.getUrl();
+ String suffix = "";
+ if (photoPath != null && photoPath.contains("_")) {
+ suffix = photoPath.substring(photoPath.lastIndexOf("_") + 1);
+ }
+
+ // 2. 格式化日期(如果是 Date 类型需要格式化,如果是 String 则直接拼接)
+ // 假设 recognitionTime 是 Date 类型,建议格式化为 yyyyMMddHHmm
+ String timeStr = "";
+ if (vo.getCreateTime() != null) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm");
+ timeStr = sdf.format(vo.getCreateTime());
+ }
+
+ // 3. 拼接最终文件名:地点 + 时间 + 类型 + 路径后缀
+ // 使用 Optional 或 StringUtils 处理 null 以防出现 "null" 字符串
+ String fileName = String.format("%s_%s_%s_%s",
+ vo.getLocation() != null ? vo.getLocation() : "",
+ timeStr,
+ vo.getCarType() != null ? vo.getCarType() : "",
+ suffix);
+
+ vo.setFileName(fileName);
+ }
+ return result;
+ }
+
+ } catch (Exception e) {
+ log.error(e.toString(), e);
+ return Collections.emptyList();
+ }
+ }
+
+ /**
+ * 设置日期时间
+ *
+ * @param dto
+ * @return ParamsDto
+ * @author cwchen
+ * @date 2026/1/8 14:21
+ */
+ public ParamsDto setTime(ParamsDto dto) {
+ if (dto.getStartTime() == null || dto.getEndTime() == null) {
+ // 获取今天的日期
+ LocalDateTime now = LocalDateTime.now();
+ // 设置开始时间为 00:00:00
+ LocalDateTime startOfDay = now.with(LocalTime.MIN);
+ // 设置结束时间为 23:59:59 (也可以使用 LocalTime.MAX)
+ LocalDateTime endOfDay = now.with(LocalTime.MAX);
+ // 转换为 Date 赋值给 dto (如果你的字段类型是 Date)
+ ZoneId zone = ZoneId.systemDefault();
+ dto.setStartTime(Date.from(startOfDay.atZone(zone).toInstant()));
+ dto.setEndTime(Date.from(endOfDay.atZone(zone).toInstant()));
+ }
+ return dto;
+ }
+
+ /**
+ * 获取导出数据的数量
+ *
+ * @param date
+ * @param endDate
+ * @return long
+ * @author cwchen
+ * @date 2026/1/8 15:20
+ */
+ public long getDataCount(Date date, Date endDate) {
+ try {
+ Long result = deviceIdentificationService.getDataCount(date, endDate);
+ return result != null ? result : 0L;
+ } catch (Exception e) {
+ log.error(e.toString(), e);
+ return 0L;
+ }
+ }
+
+ /**
+ * 获取导出数据
+ *
+ * @param date
+ * @param endDate
+ * @return long
+ * @author cwchen
+ * @date 2026/1/8 15:20
+ */
+ public List getExportDataList(Date date, Date endDate, int pageNo, int pageSize) {
+ try {
+ List result = deviceIdentificationService.getExportDataList(date, endDate,pageNo,pageSize);
return result != null ? result : Collections.emptyList();
} catch (Exception e) {
log.error(e.toString(), e);
diff --git a/bonus-admin/src/main/java/com/bonus/web/service/data/ExportExcelService.java b/bonus-admin/src/main/java/com/bonus/web/service/data/ExportExcelService.java
index 9dd4831..e54ed52 100644
--- a/bonus-admin/src/main/java/com/bonus/web/service/data/ExportExcelService.java
+++ b/bonus-admin/src/main/java/com/bonus/web/service/data/ExportExcelService.java
@@ -6,6 +6,7 @@ import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
+import com.bonus.common.config.BonusConfig;
import com.bonus.common.domain.data.dto.ParamsDto;
import com.bonus.common.domain.data.vo.DataRecognitionExportVO;
import com.bonus.common.domain.data.vo.ExportTask;
@@ -19,12 +20,16 @@ import org.springframework.scheduling.annotation.Async;
import javax.annotation.Resource;
import java.io.File;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
- * 识别数据导出-业务逻辑层 (多线程+防OOM版本)
+ * 识别数据导出-业务逻辑层
*/
@Service(value = "ExportExcelService")
@Slf4j
@@ -33,6 +38,9 @@ public class ExportExcelService {
@Resource
private RedisTemplate redisTemplate;
+ @Resource(name = "DeviceIdentificationService")
+ private DeviceIdentificationService deviceIdentificationService;
+
// 自定义线程池:限制核心并发数为2,最大为3,防止大量图片IO导致内存溢出
private static final ExecutorService exportThreadPool = new ThreadPoolExecutor(
2, 3, 60L, TimeUnit.SECONDS,
@@ -41,7 +49,8 @@ public class ExportExcelService {
);
private static final String REDIS_KEY_PREFIX = "export:task:";
- private static final String TEMP_PATH = System.getProperty("java.io.tmpdir") + File.separator + "export_task" + File.separator;
+// private static final String TEMP_PATH = System.getProperty("java.io.tmpdir") + File.separator + "export_task" + File.separator;
+ private static final String TEMP_PATH = BonusConfig.getLsFile() + File.separator + "export_task" + File.separator;
public String createExportTask(ParamsDto dto) {
String taskId = UUID.randomUUID().toString();
@@ -59,6 +68,8 @@ public class ExportExcelService {
try {
Date start = dto.getStartTime();
Date end = dto.getEndTime();
+ LocalDate startLD = toLocalDate(start);
+ LocalDate endLD = toLocalDate(end);
long dayDiff = DateUtil.between(start, end, DateUnit.DAY);
File tempDir = new File(TEMP_PATH);
@@ -71,7 +82,7 @@ public class ExportExcelService {
// 单天任务:直接处理
downloadFileName = "数据识别_" + DateUtil.formatDate(start) + ".xlsx";
finalResultFile = new File(TEMP_PATH + taskId + "_" + downloadFileName);
- generateExcel(finalResultFile, start, taskId, 0, 100, true);
+ generateExcel(finalResultFile, start, end, taskId, 0, 100, true);
} else {
// 多天任务:多线程处理
List dateRange = getDatesBetween(start, end);
@@ -86,7 +97,26 @@ public class ExportExcelService {
String subFileName = taskId + "_数据识别_" + DateUtil.formatDate(currentDate) + ".xlsx";
File subFile = new File(TEMP_PATH + subFileName);
// 传入 false,表示子线程不直接更新 Redis 整体进度,由主线程负责更新
- generateExcel(subFile, currentDate, taskId, -1, -1, false);
+ LocalDate currentLD = toLocalDate(end);
+ // 进行逻辑判断
+ boolean isSameAsStart = currentLD.equals(startLD);
+ boolean isSameAsEnd = currentLD.equals(endLD);
+ if (isSameAsStart && !isSameAsEnd) {
+ // 情况 1:仅与开始日期相同
+ LocalDateTime endOfDay = currentLD.atTime(LocalTime.MAX);
+ Date endDate = Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
+ generateExcel(subFile, start, endDate, taskId, -1, -1, false);
+ } else if (!isSameAsStart && isSameAsEnd) {
+ // 情况 2:仅与结束日期相同
+ Date startDate = Date.from(currentLD.atStartOfDay(ZoneId.systemDefault()).toInstant());
+ generateExcel(subFile, startDate, end, taskId, -1, -1, false);
+ } else if (!isSameAsStart && !isSameAsEnd) {
+ // 情况 3:都不同
+ Date startDate = Date.from(currentLD.atStartOfDay(ZoneId.systemDefault()).toInstant());
+ LocalDateTime endOfDay = currentLD.atTime(LocalTime.MAX);
+ Date endDate = Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
+ generateExcel(subFile, startDate, endDate, taskId, -1, -1, false);
+ }
subFiles.add(subFile);
} catch (Exception e) {
log.error("子任务执行失败: {}", currentDate, e);
@@ -119,7 +149,7 @@ public class ExportExcelService {
}
}
- private void generateExcel(File file, Date date, String taskId, int basePct, int maxPct, boolean updateRedis) {
+ private void generateExcel(File file, Date date, Date endDate, String taskId, int basePct, int maxPct, boolean updateRedis) {
SheetWriteHandler freezePaneHandler = new SheetWriteHandler() {
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
@@ -138,7 +168,7 @@ public class ExportExcelService {
int pageNo = 1;
int pageSize = 150; // 多线程下进一步减小 pageSize 降低内存压力
- long totalCount = mockDbCount(date);
+ long totalCount = mockDbCount(date, endDate);
int totalPages = (int) Math.ceil((double) totalCount / pageSize);
if (totalCount == 0) {
@@ -147,7 +177,7 @@ public class ExportExcelService {
}
while (pageNo <= totalPages) {
- List dbDataList = mockDbQuery(date, pageNo, pageSize);
+ List dbDataList = mockDbQuery(date,endDate, pageNo, pageSize);
List voList = new ArrayList<>();
for (int i = 0; i < dbDataList.size(); i++) {
@@ -162,10 +192,11 @@ public class ExportExcelService {
try {
if (entity.getPhotoPath() != null) {
- File img = new File(entity.getPhotoPath());
+ File img = new File(BonusConfig.getProfile() + entity.getPhotoPath());
if (img.exists() && img.canRead()) vo.setPhotoFile(img);
}
- } catch (Exception ignored) {}
+ } catch (Exception ignored) {
+ }
voList.add(vo);
}
@@ -183,7 +214,11 @@ public class ExportExcelService {
}
} finally {
if (excelWriter != null) {
- try { excelWriter.finish(); } catch (Exception e) { log.error("IO Close Error", e); }
+ try {
+ excelWriter.finish();
+ } catch (Exception e) {
+ log.error("IO Close Error", e);
+ }
}
}
}
@@ -238,16 +273,12 @@ public class ExportExcelService {
return list;
}
- /*private long mockDbCount(Date date) { return 500; }
- private List mockDbQuery(Date date, int p, int s) {
- return new ArrayList<>(); // 替换为实际查询
- }*/
- private long mockDbCount(Date date) {
- return 1000; // 模拟少量数据测试
+ private long mockDbCount(Date date, Date endDate) {
+ return deviceIdentificationService.getDataCount(date,endDate);
}
- private List mockDbQuery(Date date, int pageNo, int pageSize) {
- List list = new ArrayList<>();
+ private List mockDbQuery(Date date,Date endDate, int pageNo, int pageSize) {
+ /* List list = new ArrayList<>();
for (int i = 0; i < pageSize; i++) {
DataRecognitionExportVO vo = new DataRecognitionExportVO();
vo.setIndex((pageNo - 1) * pageSize + i + 1);
@@ -258,7 +289,16 @@ public class ExportExcelService {
// 生产环境下确保此路径存在
vo.setPhotoPath("C:\\Users\\10488\\Desktop\\test_image.png");
list.add(vo);
+ }*/
+ List exportDataList = deviceIdentificationService.getExportDataList(date, endDate, pageNo, pageSize);
+ for (int i = 0; i < exportDataList.size(); i++) {
+ DataRecognitionExportVO dataRecognitionExportVO = exportDataList.get(i);
+ dataRecognitionExportVO.setIndex((pageNo - 1) * pageSize + i + 1);
}
- return list;
+ return exportDataList;
+ }
+
+ public static LocalDate toLocalDate(Date date) {
+ return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
}
\ No newline at end of file
diff --git a/bonus-admin/src/main/java/com/bonus/web/service/data/ExportService.java b/bonus-admin/src/main/java/com/bonus/web/service/data/ExportService.java
index 1a2acaa..4d530fd 100644
--- a/bonus-admin/src/main/java/com/bonus/web/service/data/ExportService.java
+++ b/bonus-admin/src/main/java/com/bonus/web/service/data/ExportService.java
@@ -1,5 +1,6 @@
package com.bonus.web.service.data;
+import com.bonus.common.config.BonusConfig;
import com.bonus.common.domain.data.dto.ParamsDto;
import com.bonus.common.domain.data.vo.ExportTask;
import com.bonus.common.domain.data.vo.ImageRecord;
@@ -40,7 +41,8 @@ public class ExportService {
private static final long EXPIRE_TIME = 24;
// 临时文件存放目录
- private static final String TEMP_DIR = System.getProperty("java.io.tmpdir") + "/export_task/";
+// private static final String TEMP_DIR = System.getProperty("java.io.tmpdir") + "/export_task/";
+ private static final String TEMP_DIR = BonusConfig.getLsFile() + "/export_task/";
/**
* 创建任务并立即返回 TaskID
@@ -211,7 +213,7 @@ public class ExportService {
for (ImageRecord img : images) {
// 这里假设你的 ImageRecord.getUrl() 存储的是本地绝对路径,例如 /home/data/images/1.jpg
// 如果字段名不同(比如叫 getFilePath),请自行替换
- String localPath = img.getUrl();
+ String localPath = BonusConfig.getProfile() + img.getUrl();
if (localPath == null || localPath.isEmpty()) {
continue;
@@ -229,8 +231,10 @@ public class ExportService {
try {
// 1. 确定在压缩包内的文件名 (防止重名)
- String originalFileName = imageFile.getName();
- String zipEntryName = UUID.randomUUID().toString().substring(0, 8) + "_" + originalFileName;
+// String originalFileName = imageFile.getName();
+ String originalFileName = img.getFileName();
+// String zipEntryName = UUID.randomUUID().toString().substring(0, 8) + "_" + originalFileName;
+ String zipEntryName = originalFileName;
// 2. 创建 Zip 条目
ZipEntry entry = new ZipEntry(zipEntryName);
diff --git a/bonus-admin/src/main/java/com/bonus/web/service/data/LineService.java b/bonus-admin/src/main/java/com/bonus/web/service/data/LineService.java
index d2afe9a..c17444e 100644
--- a/bonus-admin/src/main/java/com/bonus/web/service/data/LineService.java
+++ b/bonus-admin/src/main/java/com/bonus/web/service/data/LineService.java
@@ -1,10 +1,14 @@
package com.bonus.web.service.data;
+import com.bonus.algorithm.service.DrawLinesService;
import com.bonus.common.core.domain.AjaxResult;
+import com.bonus.common.domain.algorithm.dto.DrawLinesRequest;
+import com.bonus.common.domain.algorithm.vo.DrawLinesResponse;
import com.bonus.common.domain.data.dto.LineDto;
import com.bonus.common.domain.data.dto.ParamsDto;
import com.bonus.common.domain.data.vo.LineVo;
import com.bonus.common.domain.data.vo.StreamVo;
+import com.bonus.common.enums.ResultCode;
import com.bonus.common.utils.ValidatorsUtils;
import com.bonus.data.service.DILineService;
import lombok.extern.slf4j.Slf4j;
@@ -14,6 +18,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import javax.annotation.Resource;
+import java.util.Objects;
/**
* @className:LineService
@@ -32,6 +37,9 @@ public class LineService {
@Resource(name = "ValidatorsUtils")
private ValidatorsUtils validatorsUtils;
+ @Resource(name = "DrawLinesService")
+ private DrawLinesService drawLinesService;
+
/**
* 查询初始线的位置
* @param dto
@@ -43,6 +51,13 @@ public class LineService {
LineVo vo = null;
try {
vo = lineService.getLineDetail(dto);
+ DrawLinesRequest request = new DrawLinesRequest();
+ request.setX1(Double.parseDouble(vo.getStartX()));
+ request.setY1(Double.parseDouble(vo.getStartY()));
+ request.setX2(Double.parseDouble(vo.getEndX()));
+ request.setY2(Double.parseDouble(vo.getEndY()));
+ log.info("视频流初始加载时设置线的坐标:{}",request);
+ DrawLinesResponse drawLinesResponse = drawLinesService.callDrawLinesService(request);
} catch (Exception e) {
log.error(e.toString(),e);
}
@@ -64,14 +79,30 @@ public class LineService {
return AjaxResult.error(validResult);
}
try {
- // 1.添加线的位置
- lineService.addLineData(dto);
+ DrawLinesRequest request = new DrawLinesRequest();
+ request.setX1(Double.parseDouble(dto.getStartX()));
+ request.setY1(Double.parseDouble(dto.getStartY()));
+ request.setX2(Double.parseDouble(dto.getEndX()));
+ request.setY2(Double.parseDouble(dto.getEndY()));
+ log.info("设置线的坐标:{}",request);
+ DrawLinesResponse drawLinesResponse = drawLinesService.callDrawLinesService(request);
+ if (Objects.nonNull(drawLinesResponse)) {
+ if(drawLinesResponse.isSuccess()){
+ // 画线接口调用成功添加线的位置
+ lineService.addLineData(dto);
+ return AjaxResult.success();
+ }else{
+ return AjaxResult.error(drawLinesResponse.getMessage());
+ }
+ }else{
+ return AjaxResult.error(ResultCode.INTERFACE_CALL_FAILED.getMessage());
+ }
+
} catch (Exception e) {
log.error(e.toString(), e);
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return AjaxResult.error();
}
- return AjaxResult.success();
}
/**
diff --git a/bonus-admin/src/main/resources/application-algorithm.yml b/bonus-admin/src/main/resources/application-algorithm.yml
new file mode 100644
index 0000000..de8e10e
--- /dev/null
+++ b/bonus-admin/src/main/resources/application-algorithm.yml
@@ -0,0 +1,5 @@
+algorithm:
+ service:
+ url: http://192.168.0.108:8080/api/v1/video/setTripwire # 画线算法接口请求地址
+ timeout: 30000 # 画线算法请求超时时间
+ max-connections: 100
\ No newline at end of file
diff --git a/bonus-algorithm/pom.xml b/bonus-algorithm/pom.xml
new file mode 100644
index 0000000..7a37fc5
--- /dev/null
+++ b/bonus-algorithm/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+ com.bonus
+ bonus
+ 3.9.0
+
+
+ bonus-algorithm
+
+
+ 算法调用模块
+
+
+
+
+
+ com.bonus
+ bonus-common
+
+
+
+
+
\ No newline at end of file
diff --git a/bonus-algorithm/src/main/java/com/bonus/algorithm/service/DrawLinesService.java b/bonus-algorithm/src/main/java/com/bonus/algorithm/service/DrawLinesService.java
new file mode 100644
index 0000000..682c2d6
--- /dev/null
+++ b/bonus-algorithm/src/main/java/com/bonus/algorithm/service/DrawLinesService.java
@@ -0,0 +1,205 @@
+package com.bonus.algorithm.service;
+
+import com.bonus.common.domain.algorithm.dto.DrawLinesRequest;
+import com.bonus.common.domain.algorithm.vo.DrawLinesResponse;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+
+/**
+ * @className: DrawLinesService
+ * @author: cwchen
+ * @date: 2026-01-08-9:26
+ * @version: 1.0
+ * @description: 画线接口调用-业务逻辑层
+ */
+@Service(value = "DrawLinesService")
+@Slf4j
+public class DrawLinesService {
+
+ private static final String UTF_8 = "UTF-8";
+
+ @Value("${algorithm.service.url}")
+ private String algorithmServiceUrl;
+
+ @Value("${algorithm.service.timeout}")
+ private int timeout;
+
+ private final CloseableHttpClient httpClient;
+ private final ObjectMapper objectMapper;
+
+ public DrawLinesService() {
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectTimeout(timeout)
+ .setSocketTimeout(timeout)
+ .setConnectionRequestTimeout(timeout)
+ .build();
+
+ this.httpClient = HttpClients.custom()
+ .setDefaultRequestConfig(requestConfig)
+ .build();
+ this.objectMapper = new ObjectMapper();
+ }
+
+ /**
+ * 调用画线接口
+ *
+ * @param drawLinesRequest 画线请求参数
+ * @return 画线响应结果
+ * @throws IOException 当画线服务调用失败时抛出
+ */
+ public DrawLinesResponse callDrawLinesService(DrawLinesRequest drawLinesRequest) throws IOException {
+ validateDrawLinesRequest(drawLinesRequest);
+
+ HttpPost httpPost = null;
+ try {
+ httpPost = createHttpPost(drawLinesRequest);
+ return executeOcrRequest(httpPost);
+ } catch (IOException e) {
+ log.error("调用画线服务失败", e);
+ return null;
+ } finally {
+ cleanupResources(httpPost);
+ }
+ }
+
+ /**
+ * 验证画线请求参数
+ */
+ private void validateDrawLinesRequest(DrawLinesRequest drawLinesRequest) {
+ if (drawLinesRequest == null) {
+ throw new IllegalArgumentException("画线请求参数不能为空");
+ }
+ }
+
+ /**
+ * 创建HTTP POST请求
+ */
+ private HttpPost createHttpPost(DrawLinesRequest drawLinesRequest) {
+ HttpPost httpPost = new HttpPost(algorithmServiceUrl);
+
+ try {
+ // 将请求对象转换为JSON字符串
+ String jsonRequest = convertToJson(drawLinesRequest);
+
+ // 设置请求体为JSON
+ StringEntity entity = new StringEntity(jsonRequest, ContentType.APPLICATION_JSON);
+ httpPost.setEntity(entity);
+
+ // 设置请求头
+ httpPost.setHeader("Accept", "application/json");
+ httpPost.setHeader("Content-Type", "application/json; charset=UTF-8");
+
+ } catch (Exception e) {
+ log.error("创建HTTP POST请求失败,请求参数: {}", drawLinesRequest, e);
+ throw new RuntimeException("创建HTTP POST请求失败", e);
+ }
+
+ return httpPost;
+ }
+
+ /**
+ * 将请求对象转换为JSON字符串
+ */
+ private String convertToJson(DrawLinesRequest request) throws JsonProcessingException {
+ ObjectMapper objectMapper = new ObjectMapper();
+ return objectMapper.writeValueAsString(request);
+ }
+
+ /**
+ * 执行画线请求
+ */
+ private DrawLinesResponse executeOcrRequest(HttpPost httpPost) throws IOException {
+ log.info("开始调用画线服务识别");
+
+ try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
+ return processHttpResponse(response);
+ }
+ }
+
+ /**
+ * 处理HTTP响应
+ */
+ private DrawLinesResponse processHttpResponse(CloseableHttpResponse response) throws IOException {
+ log.info("响应内容:{}",response);
+ int statusCode = response.getStatusLine().getStatusCode();
+ String responseBody = getResponseBody(response);
+
+ log.info("画线服务响应状态: {}", statusCode);
+ log.debug("画线服务响应内容: {}", responseBody); // 改为debug级别,避免日志过大
+
+ // 检查HTTP状态码
+ if (statusCode != 200) {
+ log.error("画线服务HTTP请求失败,状态码: {}, 响应: {}", statusCode, responseBody);
+ return null;
+ }
+
+ DrawLinesResponse drawLinesResponse = parseResponseBody(responseBody);
+ return drawLinesResponse;
+ }
+
+ /**
+ * 获取响应体
+ */
+ private String getResponseBody(CloseableHttpResponse response) throws IOException {
+ HttpEntity entity = response.getEntity();
+ return EntityUtils.toString(entity, UTF_8);
+ }
+
+ /**
+ * 解析响应体
+ */
+ private DrawLinesResponse parseResponseBody(String responseBody) throws IOException {
+ try {
+ return objectMapper.readValue(responseBody, DrawLinesResponse.class);
+ } catch (IOException e) {
+ log.error("解析画线请求响应失败,响应内容: {}", responseBody, e);
+ return null;
+ }
+ }
+
+ /**
+ * 清理资源
+ */
+ private void cleanupResources(HttpPost httpPost) {
+ // 清理HTTP连接
+ if (httpPost != null) {
+ httpPost.releaseConnection();
+ }
+
+ }
+
+ /**
+ * 关闭HTTP客户端
+ */
+ public void close() {
+ try {
+ if (httpClient != null) {
+ httpClient.close();
+ log.info("画线服务HTTP客户端已关闭");
+ }
+ } catch (IOException e) {
+ log.error("关闭HTTP客户端失败", e);
+ }
+ }
+
+ /**
+ * 销毁方法,用于Spring容器关闭时调用
+ */
+ public void destroy() {
+ close();
+ }
+}
diff --git a/bonus-common/src/main/java/com/bonus/common/config/BonusConfig.java b/bonus-common/src/main/java/com/bonus/common/config/BonusConfig.java
index 3a31931..a6baf18 100644
--- a/bonus-common/src/main/java/com/bonus/common/config/BonusConfig.java
+++ b/bonus-common/src/main/java/com/bonus/common/config/BonusConfig.java
@@ -24,6 +24,9 @@ public class BonusConfig
/** 上传路径 */
private static String profile;
+ /** 临时文件路径 */
+ private static String lsFile;
+
/** 获取地址开关 */
private static boolean addressEnabled;
@@ -119,4 +122,12 @@ public class BonusConfig
{
return getProfile() + "/upload";
}
+
+ public static String getLsFile() {
+ return lsFile;
+ }
+
+ public static void setLsFile(String lsFile) {
+ BonusConfig.lsFile = lsFile;
+ }
}
diff --git a/bonus-common/src/main/java/com/bonus/common/domain/algorithm/dto/DrawLinesRequest.java b/bonus-common/src/main/java/com/bonus/common/domain/algorithm/dto/DrawLinesRequest.java
new file mode 100644
index 0000000..0673c01
--- /dev/null
+++ b/bonus-common/src/main/java/com/bonus/common/domain/algorithm/dto/DrawLinesRequest.java
@@ -0,0 +1,34 @@
+package com.bonus.common.domain.algorithm.dto;
+
+import lombok.Data;
+
+
+/**
+ * @className:DrawLinesRequest
+ * @author:cwchen
+ * @date:2026-01-08-9:35
+ * @version:1.0
+ * @description:画线调用请求-实体
+ */
+@Data
+public class DrawLinesRequest {
+
+ /**起点坐标x*/
+ private double x1;
+ /**起点坐标y*/
+ private double y1;
+ /**终点坐标x*/
+ private double x2;
+ /**终点坐标Y*/
+ private double y2;
+
+ public DrawLinesRequest(double x1, double y1, double x2, double y2) {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+ }
+
+ public DrawLinesRequest() {
+ }
+}
diff --git a/bonus-common/src/main/java/com/bonus/common/domain/algorithm/vo/DrawLinesResponse.java b/bonus-common/src/main/java/com/bonus/common/domain/algorithm/vo/DrawLinesResponse.java
new file mode 100644
index 0000000..c618a1b
--- /dev/null
+++ b/bonus-common/src/main/java/com/bonus/common/domain/algorithm/vo/DrawLinesResponse.java
@@ -0,0 +1,33 @@
+package com.bonus.common.domain.algorithm.vo;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+
+/**
+ * @className: DrawLinesResponse
+ * @author: cwchen
+ * @date: 2026-01-08-9:35
+ * @version: 1.0
+ * @description: 画线调用响应-实体
+ */
+@Data
+public class DrawLinesResponse {
+
+ @JsonProperty("code")
+ private Integer code; // 状态码
+
+ @JsonProperty("message")
+ private String message; // 消息
+
+ @JsonProperty("status")
+ private String status;
+
+ public boolean isSuccess() {
+ return Objects.equals(status, "success") && code != null && code == 200;
+ }
+}
diff --git a/bonus-common/src/main/java/com/bonus/common/domain/data/vo/ImageRecord.java b/bonus-common/src/main/java/com/bonus/common/domain/data/vo/ImageRecord.java
index 64c1e26..c33723d 100644
--- a/bonus-common/src/main/java/com/bonus/common/domain/data/vo/ImageRecord.java
+++ b/bonus-common/src/main/java/com/bonus/common/domain/data/vo/ImageRecord.java
@@ -17,6 +17,10 @@ import java.util.Date;
public class ImageRecord {
private String url;
+ private String location;
+ private String identificationTime;
+ private String carType;
+ private String fileName;
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date createTime;
diff --git a/bonus-common/src/main/java/com/bonus/common/enums/ResultCode.java b/bonus-common/src/main/java/com/bonus/common/enums/ResultCode.java
new file mode 100644
index 0000000..7949a75
--- /dev/null
+++ b/bonus-common/src/main/java/com/bonus/common/enums/ResultCode.java
@@ -0,0 +1,34 @@
+package com.bonus.common.enums;
+
+/**
+ * @className:ResultCode
+ * @author:cwchen
+ * @date:2026-01-08-9:59
+ * @version:1.0
+ * @description:异常枚举
+ */
+public enum ResultCode {
+
+ SUCCESS(200, "操作成功"),
+ INTERFACE_CALL_FAILED(10001, "接口调用失败"),
+ SYSTEM_ERROR(500, "系统繁忙,请稍后再试");
+
+ private final int code;
+ private final String message;
+
+ // 构造方法
+ ResultCode(int code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+ // 获取错误码
+ public int getCode() {
+ return code;
+ }
+
+ // 获取错误信息
+ public String getMessage() {
+ return message;
+ }
+}
\ No newline at end of file
diff --git a/bonus-data/src/main/java/com/bonus/data/mapper/DIDeviceIdentificationMapper.java b/bonus-data/src/main/java/com/bonus/data/mapper/DIDeviceIdentificationMapper.java
index 61a9fb7..e76a2e0 100644
--- a/bonus-data/src/main/java/com/bonus/data/mapper/DIDeviceIdentificationMapper.java
+++ b/bonus-data/src/main/java/com/bonus/data/mapper/DIDeviceIdentificationMapper.java
@@ -1,10 +1,13 @@
package com.bonus.data.mapper;
import com.bonus.common.domain.data.dto.ParamsDto;
+import com.bonus.common.domain.data.vo.DataRecognitionExportVO;
import com.bonus.common.domain.data.vo.DeviceIdentificationDataVo;
import com.bonus.common.domain.data.vo.ImageRecord;
+import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
+import java.util.Date;
import java.util.List;
/**
@@ -19,6 +22,7 @@ public interface DIDeviceIdentificationMapper {
/**
* 查询识别图片
+ *
* @param dto
* @return List
* @author cwchen
@@ -28,10 +32,34 @@ public interface DIDeviceIdentificationMapper {
/**
* 查询要下载的图片
+ *
* @param dto
* @return List
* @author cwchen
* @date 2025/12/26 10:41
*/
List getImageData(ParamsDto dto);
+
+ /**
+ * 获取导出数据的数量
+ *
+ * @param date
+ * @param endDate
+ * @return Long
+ * @author cwchen
+ * @date 2026/1/8 15:24
+ */
+ Long getDataCount(@Param("startDate") Date date,@Param("endDate") Date endDate);
+
+ /**
+ * 获取导出数据
+ * @param date
+ * @param endDate
+ * @param pageNo
+ * @param pageSize
+ * @return List
+ * @author cwchen
+ * @date 2026/1/8 15:24
+ */
+ List getExportDataList(@Param("startDate") Date date,@Param("endDate") Date endDate,@Param("pageNo") int pageNo,@Param("pageSize") int pageSize);
}
diff --git a/bonus-data/src/main/java/com/bonus/data/service/DIDeviceIdentificationService.java b/bonus-data/src/main/java/com/bonus/data/service/DIDeviceIdentificationService.java
index 4935a7d..c8b52ab 100644
--- a/bonus-data/src/main/java/com/bonus/data/service/DIDeviceIdentificationService.java
+++ b/bonus-data/src/main/java/com/bonus/data/service/DIDeviceIdentificationService.java
@@ -2,10 +2,12 @@ package com.bonus.data.service;
import com.bonus.common.domain.data.dto.ParamsDto;
import com.bonus.common.domain.data.vo.AlarmVo;
+import com.bonus.common.domain.data.vo.DataRecognitionExportVO;
import com.bonus.common.domain.data.vo.DeviceIdentificationDataVo;
import com.bonus.common.domain.data.vo.ImageRecord;
import org.springframework.stereotype.Service;
+import java.util.Date;
import java.util.List;
/**
@@ -34,4 +36,24 @@ public interface DIDeviceIdentificationService {
* @date 2025/12/26 10:38
*/
List getImageData(ParamsDto dto);
+
+ /**
+ * 获取导出数据的数量
+ * @param date
+ * @param endDate
+ * @return Long
+ * @author cwchen
+ * @date 2026/1/8 15:22
+ */
+ Long getDataCount(Date date, Date endDate);
+
+ /**
+ * 获取导出数据
+ * @param date
+ * @param endDate
+ * @return List
+ * @author cwchen
+ * @date 2026/1/8 15:22
+ */
+ List getExportDataList(Date date, Date endDate,int pageNo, int pageSize);
}
diff --git a/bonus-data/src/main/java/com/bonus/data/service/impl/DDeviceIdentificationServiceImpl.java b/bonus-data/src/main/java/com/bonus/data/service/impl/DDeviceIdentificationServiceImpl.java
index 6363cca..c3c739e 100644
--- a/bonus-data/src/main/java/com/bonus/data/service/impl/DDeviceIdentificationServiceImpl.java
+++ b/bonus-data/src/main/java/com/bonus/data/service/impl/DDeviceIdentificationServiceImpl.java
@@ -2,6 +2,7 @@ package com.bonus.data.service.impl;
import com.bonus.common.domain.data.dto.ParamsDto;
import com.bonus.common.domain.data.vo.AlarmVo;
+import com.bonus.common.domain.data.vo.DataRecognitionExportVO;
import com.bonus.common.domain.data.vo.DeviceIdentificationDataVo;
import com.bonus.common.domain.data.vo.ImageRecord;
import com.bonus.data.mapper.DIDeviceIdentificationMapper;
@@ -10,6 +11,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
/**
@@ -34,4 +36,14 @@ public class DDeviceIdentificationServiceImpl implements DIDeviceIdentificationS
public List getImageData(ParamsDto dto) {
return diDeviceIdentificationMapper.getImageData(dto);
}
+
+ @Override
+ public Long getDataCount(Date date, Date endDate) {
+ return diDeviceIdentificationMapper.getDataCount(date,endDate);
+ }
+
+ @Override
+ public List getExportDataList(Date date, Date endDate,int pageNo, int pageSize) {
+ return diDeviceIdentificationMapper.getExportDataList(date,endDate,pageNo,pageSize);
+ }
}
diff --git a/bonus-data/src/main/resources/mapper/DDeviceIdentificationMapper.xml b/bonus-data/src/main/resources/mapper/DDeviceIdentificationMapper.xml
index 3b3a668..54eb164 100644
--- a/bonus-data/src/main/resources/mapper/DDeviceIdentificationMapper.xml
+++ b/bonus-data/src/main/resources/mapper/DDeviceIdentificationMapper.xml
@@ -35,12 +35,14 @@
+
+
+
+
+
+
diff --git a/bonus-framework/pom.xml b/bonus-framework/pom.xml
index 4c0be4c..89c261e 100644
--- a/bonus-framework/pom.xml
+++ b/bonus-framework/pom.xml
@@ -59,10 +59,6 @@
bonus-system
-
- com.bonus
- bonus-file
-
diff --git a/bonus-quartz/src/main/java/com/bonus/quartz/task/FileCleanupTask.java b/bonus-quartz/src/main/java/com/bonus/quartz/task/FileCleanupTask.java
index 3774818..7ed4c90 100644
--- a/bonus-quartz/src/main/java/com/bonus/quartz/task/FileCleanupTask.java
+++ b/bonus-quartz/src/main/java/com/bonus/quartz/task/FileCleanupTask.java
@@ -1,4 +1,5 @@
package com.bonus.quartz.task;
+import com.bonus.common.config.BonusConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@@ -22,7 +23,8 @@ import java.util.concurrent.TimeUnit;
public class FileCleanupTask {
// 使用之前定义的临时目录路径
- private static final String TEMP_PATH = System.getProperty("java.io.tmpdir") + File.separator + "export_task" + File.separator;
+// private static final String TEMP_PATH = System.getProperty("java.io.tmpdir") + File.separator + "export_task" + File.separator;
+ private static final String TEMP_PATH = BonusConfig.getLsFile() + File.separator + "export_task" + File.separator;
/**
* 每天凌晨 2 点执行清理任务
diff --git a/pom.xml b/pom.xml
index 06b686c..baef741 100644
--- a/pom.xml
+++ b/pom.xml
@@ -251,6 +251,7 @@
bonus-common
bonus-ocr
bonus-data
+ bonus-algorithm
pom