refactor(face): 重构人脸识别服务逻辑

- 优化了 faceId 参数校验和日志记录
- 重构了人脸识别主流程,增加了异常处理和日志记录
- 新增了人脸识别补救逻辑方法
- 优化了源文件关联、免费逻辑、购买状态处理等方法
- 重构了视频重切逻辑,使其更加清晰- 优化了时间范围筛选逻辑
This commit is contained in:
2025-09-10 17:00:09 +08:00
parent 7839082352
commit eaf959e1b8
2 changed files with 450 additions and 147 deletions

View File

@@ -2,6 +2,7 @@ package com.ycwl.basic.service.task.impl;
import cn.hutool.core.date.DateUtil;
import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
import com.ycwl.basic.utils.JacksonUtil;
import com.aliyuncs.facebody.model.v20191230.SearchFaceRequest;
import com.ycwl.basic.biz.OrderBiz;
@@ -168,26 +169,22 @@ public class TaskFaceServiceImpl implements TaskFaceService {
request.setDbName(dbName);
request.setImageUrl(faceUrl);
request.setLimit(200);
// request.setQualityScoreThreshold(60f);
FaceDetectLog logEntity = FaceDetectLog.quickCreate(reason, request);
float threshold = 0;
int tourMinutes = -1;
ScenicConfigManager scenicConfig = null;
if (StringUtils.isNumeric(dbName)) {
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(Long.valueOf(dbName));
scenicConfig = scenicRepository.getScenicConfigManager(Long.valueOf(dbName));
if (scenicConfig != null) {
if (scenicConfig.getFaceScoreThreshold() != null) {
threshold = scenicConfig.getFaceScoreThreshold() / 100F;
}
if (scenicConfig.getTourTime() != null) {
tourMinutes = scenicConfig.getTourTime();
if (scenicConfig.getFloat("face_score_threshold") != null) {
threshold = scenicConfig.getFloat("face_score_threshold") / 100F;
}
}
} else if (StringUtils.isNumeric(dbName.replace(USER_FACE_DB_NAME, ""))) {
Long scenicId = Long.valueOf(dbName.replace(USER_FACE_DB_NAME, ""));
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
scenicConfig = scenicRepository.getScenicConfigManager(scenicId);
if (scenicConfig != null) {
if (scenicConfig.getFaceScoreThreshold() != null) {
threshold = scenicConfig.getFaceScoreThreshold() / 100F;
if (scenicConfig.getFloat("face_score_threshold") != null) {
threshold = scenicConfig.getFloat("face_score_threshold") / 100F;
}
}
}
@@ -219,35 +216,8 @@ public class TaskFaceServiceImpl implements TaskFaceService {
.filter(StringUtils::isNumeric)
.map(Long::valueOf)
.collect(Collectors.toList());
List<FaceSampleEntity> allFaceSampleList = new ArrayList<>();
if (StringUtils.isNumeric(dbName)) { // 景区
allFaceSampleList = faceSampleMapper.listByIds(allFaceSampleIds);
if (!acceptFaceSampleIds.isEmpty()) {
List<Long> finalAcceptFaceSampleIds = acceptFaceSampleIds;
Optional<FaceSampleEntity> firstFaceSample = allFaceSampleList.stream()
.filter(faceSample -> finalAcceptFaceSampleIds.contains(faceSample.getId()))
.sorted(Comparator.comparing(FaceSampleEntity::getCreateAt).reversed())
.findAny();
// Long firstFaceSampleId = acceptFaceSampleIds.getFirst();
// Optional<FaceSampleEntity> firstFaceSample = allFaceSampleList.stream().filter(faceSample -> faceSample.getId().equals(firstFaceSampleId)).findAny();
if (firstFaceSample.isPresent()) {
if (tourMinutes > 0) {
List<FaceSampleEntity> acceptFaceSampleList = faceSampleMapper.listByIds(acceptFaceSampleIds);
Date startDate = DateUtil.offsetMinute(firstFaceSample.get().getCreateAt(), -tourMinutes);
Date endDate = DateUtil.offsetMinute(firstFaceSample.get().getCreateAt(), 1);
acceptFaceSampleIds = acceptFaceSampleList.stream()
.filter(faceSample -> faceSample.getCreateAt().after(startDate) && faceSample.getCreateAt().before(endDate))
.map(FaceSampleEntity::getId)
.collect(Collectors.toList());
log.info("时间范围逻辑:最高匹配:{},时间范围需要在:{}~{}间", firstFaceSample, startDate, endDate);
} else {
log.info("时间范围逻辑:景区未限制");
}
} else {
log.info("时间范围逻辑:最高匹配ID:{},未找到", "--");
}
}
}
List<FaceSampleEntity> allFaceSampleList = faceSampleMapper.listByIds(allFaceSampleIds);
acceptFaceSampleIds = applySampleFilters(acceptFaceSampleIds, allFaceSampleList, scenicConfig);
List<MatchLocalRecord> collect = new ArrayList<>();
for (SearchFaceResultItem item : records) {
MatchLocalRecord record = new MatchLocalRecord();
@@ -360,4 +330,97 @@ public class TaskFaceServiceImpl implements TaskFaceService {
}
redisTemplate.opsForValue().set(FaceConstant.FACE_USER_URL_PFX + faceId, faceUrl, 3, TimeUnit.DAYS);
}
/**
* 应用样本筛选逻辑
* 包含时间范围筛选和未来可能的其他筛选策略
*
* @param acceptedSampleIds 已接受的样本ID列表
* @param allFaceSampleList 所有人脸样本实体列表
* @param scenicConfig 景区配置,包含各种筛选策略的参数
* @return 筛选后的样本ID列表
*/
private List<Long> applySampleFilters(List<Long> acceptedSampleIds,
List<FaceSampleEntity> allFaceSampleList,
ScenicConfigManager scenicConfig) {
if (acceptedSampleIds == null || acceptedSampleIds.isEmpty()) {
return acceptedSampleIds;
}
if (scenicConfig == null) {
// 没有配置,不管
return acceptedSampleIds;
}
// 1. 找到最高匹配的样本(按创建时间倒序排列的第一个)
Optional<FaceSampleEntity> firstFaceSample = allFaceSampleList.stream()
.filter(faceSample -> acceptedSampleIds.contains(faceSample.getId())).max(Comparator.comparing(FaceSampleEntity::getCreateAt));
if (firstFaceSample.isEmpty()) {
log.warn("样本筛选逻辑:未找到匹配的人脸样本,acceptedIds: {}", acceptedSampleIds);
return acceptedSampleIds;
}
FaceSampleEntity topMatchSample = firstFaceSample.get();
log.debug("样本筛选逻辑:找到最高匹配样本 ID={}, 创建时间={}",
topMatchSample.getId(), topMatchSample.getCreateAt());
List<Long> filteredIds = acceptedSampleIds;
// 2. 应用时间范围筛选(基于景区配置)
if (scenicConfig.getInteger("tour_time", 0) > 0) {
filteredIds = filterSampleIdsByTimeRange(filteredIds, topMatchSample, scenicConfig.getInteger("tour_time"));
log.debug("应用时间范围筛选:游览时间限制={}分钟", scenicConfig.getInteger("tour_time"));
} else {
log.debug("时间范围逻辑:景区未设置游览时间限制");
}
// 3. TODO: 基于景区配置的其他筛选策略
// 可以根据 scenicConfig 中的配置来决定是否启用特定筛选
// 示例:未来可能的筛选策略
// if (scenicConfig.getEnableLocationFilter() != null && scenicConfig.getEnableLocationFilter()) {
// filteredIds = applyLocationFilter(filteredIds, allFaceSampleList, scenicConfig);
// }
// if (scenicConfig.getEnableQualityFilter() != null && scenicConfig.getEnableQualityFilter()) {
// filteredIds = applyQualityFilter(filteredIds, allFaceSampleList, scenicConfig);
// }
// if (scenicConfig.getMaxSampleCount() != null) {
// filteredIds = applySampleCountLimit(filteredIds, scenicConfig.getMaxSampleCount());
// }
log.debug("样本筛选完成:原始数量={}, 最终数量={}",
acceptedSampleIds.size(), filteredIds.size());
return filteredIds;
}
/**
* 根据时间范围过滤人脸样本ID
* 基于最高匹配样本的时间,过滤出指定时间范围内的样本ID
*/
private List<Long> filterSampleIdsByTimeRange(List<Long> acceptedSampleIds,
FaceSampleEntity firstMatch,
int tourMinutes) {
if (acceptedSampleIds == null || acceptedSampleIds.isEmpty() ||
firstMatch == null || tourMinutes <= 0) {
return acceptedSampleIds;
}
List<FaceSampleEntity> acceptFaceSampleList = faceSampleMapper.listByIds(acceptedSampleIds);
if (acceptFaceSampleList.isEmpty()) {
return acceptedSampleIds;
}
Date startDate = DateUtil.offsetMinute(firstMatch.getCreateAt(), -tourMinutes);
Date endDate = DateUtil.offsetMinute(firstMatch.getCreateAt(), 1);
List<Long> filteredIds = acceptFaceSampleList.stream()
.filter(faceSample -> faceSample.getCreateAt().after(startDate) &&
faceSample.getCreateAt().before(endDate))
.map(FaceSampleEntity::getId)
.collect(Collectors.toList());
log.info("时间范围逻辑:最高匹配:{},时间范围:{}~{},原样本数:{},过滤后样本数:{}",
firstMatch.getId(), startDate, endDate, acceptedSampleIds.size(), filteredIds.size());
return filteredIds;
}
}