From 222f974ad578740a593df7dbb64249e25814b078 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Sat, 1 Nov 2025 19:58:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(face):=20=E6=B7=BB=E5=8A=A0=E4=BA=BA?= =?UTF-8?q?=E8=84=B8=E8=AF=86=E5=88=AB=E9=87=8D=E8=AF=95=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=E5=BE=97=E5=88=86=E7=AD=9B=E9=80=89?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在FaceSampleController中新增retryFaceRecognition接口用于手动重试失败的人脸识别任务- 集成人脸识别Kafka服务,支持异步处理重试请求- 在FaceServiceImpl中增加从景区配置读取人脸得分阈值的功能 - 根据配置的得分阈值对人脸识别结果进行筛选,过滤低分样本 - 添加详细的日志记录和异常处理机制- 优化线程池使用,确保重试任务能够正确提交和执行 --- .../controller/pc/FaceSampleController.java | 25 +++++++++- .../service/FaceProcessingKafkaService.java | 49 +++++++++++++++++++ .../service/pc/impl/FaceServiceImpl.java | 20 ++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ycwl/basic/controller/pc/FaceSampleController.java b/src/main/java/com/ycwl/basic/controller/pc/FaceSampleController.java index 2fceee35..2cdf684b 100644 --- a/src/main/java/com/ycwl/basic/controller/pc/FaceSampleController.java +++ b/src/main/java/com/ycwl/basic/controller/pc/FaceSampleController.java @@ -1,4 +1,5 @@ package com.ycwl.basic.controller.pc; +import com.ycwl.basic.integration.kafka.service.FaceProcessingKafkaService; import com.github.pagehelper.PageInfo; import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity; @@ -15,13 +16,14 @@ import java.util.List; * @Author:longbinbin * @Date:2024/12/2 16:33 */ -@Deprecated @RestController @RequestMapping("/api/faceSample/v1") // 人脸样本管理 public class FaceSampleController { @Autowired private FaceSampleService FaceSampleService; + @Autowired(required = false) + private FaceProcessingKafkaService faceProcessingKafkaService; // 分页查询人脸样本 @PostMapping("/page") @@ -39,4 +41,25 @@ public class FaceSampleController { return FaceSampleService.getById(id); } + /** + * 重试失败的人脸识别 + * 用于手动重试状态为-1的人脸样本 + * + * @param id 人脸样本ID + * @return 重试结果 + */ + @PostMapping("/retry/{id}") + public ApiResponse retryFaceRecognition(@PathVariable("id") Long id) { + if (faceProcessingKafkaService == null) { + return ApiResponse.fail("Kafka服务未启用,无法重试人脸识别"); + } + + boolean success = faceProcessingKafkaService.retryFaceRecognition(id); + if (success) { + return ApiResponse.success("人脸识别重试任务已提交"); + } else { + return ApiResponse.fail("提交重试任务失败,请检查人脸样本状态"); + } + } + } diff --git a/src/main/java/com/ycwl/basic/integration/kafka/service/FaceProcessingKafkaService.java b/src/main/java/com/ycwl/basic/integration/kafka/service/FaceProcessingKafkaService.java index 1d34258e..9bf01a39 100644 --- a/src/main/java/com/ycwl/basic/integration/kafka/service/FaceProcessingKafkaService.java +++ b/src/main/java/com/ycwl/basic/integration/kafka/service/FaceProcessingKafkaService.java @@ -191,4 +191,53 @@ public class FaceProcessingKafkaService { log.error("更新人脸样本状态失败, faceSampleId: {}", faceSampleId, e); } } + + /** + * 重试失败的人脸识别 + * 用于手动重试状态为-1的人脸样本 + * + * @param faceSampleId 人脸样本ID + * @return 是否成功提交重试任务 + */ + public boolean retryFaceRecognition(Long faceSampleId) { + try { + // 查询人脸样本信息 + FaceSampleEntity faceSample = faceSampleMapper.getEntity(faceSampleId); + if (faceSample == null) { + log.error("人脸样本不存在, faceSampleId: {}", faceSampleId); + return false; + } + + // 检查状态是否为失败状态(-1) + if (faceSample.getStatus() != -1) { + log.warn("人脸样本状态不是失败状态, 无需重试, faceSampleId: {}, status: {}", + faceSampleId, faceSample.getStatus()); + return false; + } + + // 构造人脸处理消息 + FaceProcessingMessage message = FaceProcessingMessage.builder() + .faceSampleId(faceSample.getId()) + .scenicId(faceSample.getScenicId()) + .deviceId(faceSample.getDeviceId()) + .faceUrl(faceSample.getFaceUrl()) + .shotTime(faceSample.getCreateAt()) + .createTime(new Date()) + .source("retry-manual") + .build(); + + // 提交到线程池进行异步处理 + faceRecognitionExecutor.execute(() -> processFaceRecognitionAsync(message)); + + log.info("人脸识别重试任务已提交, faceSampleId: {}, 活跃线程: {}, 队列大小: {}", + faceSampleId, faceRecognitionExecutor.getActiveCount(), + faceRecognitionExecutor.getQueue().size()); + + return true; + + } catch (Exception e) { + log.error("提交人脸识别重试任务失败, faceSampleId: {}", faceSampleId, e); + return false; + } + } } \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/service/pc/impl/FaceServiceImpl.java b/src/main/java/com/ycwl/basic/service/pc/impl/FaceServiceImpl.java index ad3e07ef..03639921 100644 --- a/src/main/java/com/ycwl/basic/service/pc/impl/FaceServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/pc/impl/FaceServiceImpl.java @@ -1341,12 +1341,32 @@ public class FaceServiceImpl implements FaceService { resultItems = Collections.emptyList(); } + // 获取景区配置的得分阈值 + Float scoreThreshold = null; + try { + ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(face.getScenicId()); + if (scenicConfig != null) { + BigDecimal thresholdConfig = scenicConfig.getBigDecimal("face_select_score_threshold"); + if (thresholdConfig != null) { + // 配置值是0~100的小数,需要转换为0~1的阈值 + scoreThreshold = thresholdConfig.floatValue() / 100.0f; + } + } + } catch (Exception e) { + log.warn("获取景区人脸得分阈值配置失败, scenicId: {}, 将不进行得分筛选", face.getScenicId(), e); + } + List persistedAcceptedIds = parseMatchSampleIds(face.getMatchSampleIds()); LinkedHashSet sampleUniverse = new LinkedHashSet<>(); Map itemBySampleId = new LinkedHashMap<>(); for (SearchFaceResultItem item : resultItems) { Long sampleId = parseLongSilently(item.getExtData()); if (sampleId != null) { + // 根据得分阈值筛选样本 + if (scoreThreshold != null && item.getScore() != null && item.getScore() < scoreThreshold) { + // 得分低于阈值,跳过此样本 + continue; + } sampleUniverse.add(sampleId); itemBySampleId.putIfAbsent(sampleId, item); }