You've already forked FrameTour-BE
feat(service): 实现自定义人脸匹配功能
- 新增 matchCustomFaceId 方法,实现自定义人脸匹配逻辑 - 优化 mergeSearchResults 方法,合并多个搜索结果 - 在 TaskFaceService 接口中添加 applySampleFilters 方法 - 在 TaskFaceServiceImpl 中实现 applySampleFilters 方法
This commit is contained in:
@@ -69,9 +69,11 @@ import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -948,7 +950,151 @@ public class FaceServiceImpl implements FaceService {
|
||||
|
||||
@Override
|
||||
public void matchCustomFaceId(Long faceId, List<Long> faceSampleIds) {
|
||||
// 参数验证
|
||||
if (faceId == null) {
|
||||
throw new IllegalArgumentException("faceId 不能为空");
|
||||
}
|
||||
if (faceSampleIds == null || faceSampleIds.isEmpty()) {
|
||||
throw new IllegalArgumentException("faceSampleIds 不能为空");
|
||||
}
|
||||
|
||||
log.debug("开始自定义人脸匹配:faceId={}, faceSampleIds={}", faceId, faceSampleIds);
|
||||
|
||||
try {
|
||||
// 1. 获取基础数据
|
||||
FaceEntity face = faceRepository.getFace(faceId);
|
||||
if (face == null) {
|
||||
log.warn("人脸不存在,faceId: {}", faceId);
|
||||
throw new BaseException("人脸不存在");
|
||||
}
|
||||
|
||||
List<FaceSampleEntity> faceSamples = faceSampleMapper.listByIds(faceSampleIds);
|
||||
if (faceSamples.isEmpty()) {
|
||||
log.warn("未找到指定的人脸样本,faceSampleIds: {}", faceSampleIds);
|
||||
throw new BaseException("未找到指定的人脸样本");
|
||||
}
|
||||
|
||||
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(face.getScenicId());
|
||||
IFaceBodyAdapter faceBodyAdapter = scenicService.getScenicFaceBodyAdapter(face.getScenicId());
|
||||
|
||||
if (faceBodyAdapter == null) {
|
||||
log.error("无法获取人脸识别适配器,scenicId: {}", face.getScenicId());
|
||||
throw new BaseException("人脸识别服务不可用,请稍后再试");
|
||||
}
|
||||
|
||||
// 2. 对每个faceSample进行人脸搜索
|
||||
List<SearchFaceRespVo> searchResults = new ArrayList<>();
|
||||
for (FaceSampleEntity faceSample : faceSamples) {
|
||||
try {
|
||||
SearchFaceRespVo result = faceService.searchFace(faceBodyAdapter,
|
||||
String.valueOf(face.getScenicId()),
|
||||
faceSample.getFaceUrl(),
|
||||
"自定义人脸匹配");
|
||||
if (result != null) {
|
||||
searchResults.add(result);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("人脸样本搜索失败,faceSampleId={}, faceUrl={}",
|
||||
faceSample.getId(), faceSample.getFaceUrl(), e);
|
||||
// 继续处理其他样本,不中断整个流程
|
||||
}
|
||||
}
|
||||
|
||||
if (searchResults.isEmpty()) {
|
||||
log.warn("所有人脸样本搜索都失败,faceId={}, faceSampleIds={}", faceId, faceSampleIds);
|
||||
throw new BaseException("人脸识别失败,请重试");
|
||||
}
|
||||
|
||||
// 3. 整合多个搜索结果
|
||||
SearchFaceRespVo mergedResult = mergeSearchResults(searchResults);
|
||||
|
||||
// 4. 应用后置筛选逻辑
|
||||
if (mergedResult.getSampleListIds() != null && !mergedResult.getSampleListIds().isEmpty()) {
|
||||
List<FaceSampleEntity> allFaceSampleList = faceSampleMapper.listByIds(mergedResult.getSampleListIds());
|
||||
List<Long> filteredSampleIds = faceService.applySampleFilters(mergedResult.getSampleListIds(), allFaceSampleList, scenicConfig);
|
||||
mergedResult.setSampleListIds(filteredSampleIds);
|
||||
log.debug("应用后置筛选:原始样本数={}, 筛选后样本数={}", allFaceSampleList.size(), filteredSampleIds.size());
|
||||
}
|
||||
|
||||
// 5. 更新人脸实体结果
|
||||
updateFaceEntityResult(face, mergedResult, faceId);
|
||||
|
||||
// 6. 执行后续业务逻辑
|
||||
List<Long> sampleListIds = mergedResult.getSampleListIds();
|
||||
if (sampleListIds != null && !sampleListIds.isEmpty()) {
|
||||
try {
|
||||
List<MemberSourceEntity> memberSourceEntityList = processMemberSources(sampleListIds, face);
|
||||
|
||||
if (!memberSourceEntityList.isEmpty()) {
|
||||
List<Long> freeSourceIds = processFreeSourceLogic(memberSourceEntityList, scenicConfig, false);
|
||||
processBuyStatus(memberSourceEntityList, freeSourceIds, face.getMemberId(),
|
||||
face.getScenicId(), faceId);
|
||||
|
||||
handleVideoRecreation(scenicConfig, memberSourceEntityList, faceId,
|
||||
face.getMemberId(), sampleListIds, false);
|
||||
|
||||
sourceMapper.addRelations(memberSourceEntityList);
|
||||
taskTaskService.autoCreateTaskByFaceId(face.getId());
|
||||
|
||||
log.info("自定义人脸匹配完成:faceId={}, 匹配样本数={}, 关联源文件数={}, 免费数={}",
|
||||
faceId, sampleListIds.size(), memberSourceEntityList.size(), freeSourceIds.size());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理源文件关联失败,faceId={}", faceId, e);
|
||||
// 源文件关联失败不影响主流程
|
||||
}
|
||||
} else {
|
||||
log.warn("自定义人脸匹配无结果:faceId={}, faceSampleIds={}", faceId, faceSampleIds);
|
||||
}
|
||||
|
||||
} catch (BaseException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("自定义人脸匹配处理异常,faceId={}, faceSampleIds={}", faceId, faceSampleIds, e);
|
||||
throw new BaseException("自定义人脸匹配处理失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并多个搜索结果
|
||||
*/
|
||||
private SearchFaceRespVo mergeSearchResults(List<SearchFaceRespVo> searchResults) {
|
||||
SearchFaceRespVo mergedResult = new SearchFaceRespVo();
|
||||
|
||||
// 收集所有样本ID并去重
|
||||
Set<Long> allSampleIds = new LinkedHashSet<>();
|
||||
List<String> allSearchJsons = new ArrayList<>();
|
||||
float maxScore = 0f;
|
||||
float maxFirstMatchRate = 0f;
|
||||
boolean hasLowThreshold = false;
|
||||
|
||||
for (SearchFaceRespVo result : searchResults) {
|
||||
if (result.getSampleListIds() != null) {
|
||||
allSampleIds.addAll(result.getSampleListIds());
|
||||
}
|
||||
if (result.getSearchResultJson() != null) {
|
||||
allSearchJsons.add(result.getSearchResultJson());
|
||||
}
|
||||
if (result.getScore() > maxScore) {
|
||||
maxScore = result.getScore();
|
||||
}
|
||||
if (result.getFirstMatchRate() > maxFirstMatchRate) {
|
||||
maxFirstMatchRate = result.getFirstMatchRate();
|
||||
}
|
||||
if (result.isLowThreshold()) {
|
||||
hasLowThreshold = true;
|
||||
}
|
||||
}
|
||||
|
||||
mergedResult.setSampleListIds(new ArrayList<>(allSampleIds));
|
||||
mergedResult.setSearchResultJson(String.join("|", allSearchJsons));
|
||||
mergedResult.setScore(maxScore);
|
||||
mergedResult.setFirstMatchRate(maxFirstMatchRate);
|
||||
mergedResult.setLowThreshold(hasLowThreshold);
|
||||
|
||||
log.debug("合并搜索结果完成,总样本数: {}", allSampleIds.size());
|
||||
|
||||
return mergedResult;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,9 +1,13 @@
|
||||
package com.ycwl.basic.service.task;
|
||||
|
||||
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
|
||||
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
|
||||
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
|
||||
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TaskFaceService {
|
||||
|
||||
SearchFaceRespVo searchFace(Long faceId);
|
||||
@@ -13,4 +17,15 @@ public interface TaskFaceService {
|
||||
boolean deleteFaceSample(Long scenicId, String dbName, String entityId);
|
||||
|
||||
boolean assureFaceDb(IFaceBodyAdapter faceBodyAdapter, String dbName);
|
||||
|
||||
/**
|
||||
* 应用样本筛选逻辑
|
||||
* @param acceptedSampleIds 已接受的样本ID列表
|
||||
* @param allFaceSampleList 所有人脸样本实体列表
|
||||
* @param scenicConfig 景区配置管理器
|
||||
* @return 筛选后的样本ID列表
|
||||
*/
|
||||
List<Long> applySampleFilters(List<Long> acceptedSampleIds,
|
||||
List<FaceSampleEntity> allFaceSampleList,
|
||||
ScenicConfigManager scenicConfig);
|
||||
}
|
||||
|
@@ -352,7 +352,8 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
* @param scenicConfig 景区配置,包含各种筛选策略的参数
|
||||
* @return 筛选后的样本ID列表
|
||||
*/
|
||||
private List<Long> applySampleFilters(List<Long> acceptedSampleIds,
|
||||
@Override
|
||||
public List<Long> applySampleFilters(List<Long> acceptedSampleIds,
|
||||
List<FaceSampleEntity> allFaceSampleList,
|
||||
ScenicConfigManager scenicConfig) {
|
||||
if (acceptedSampleIds == null || acceptedSampleIds.isEmpty()) {
|
||||
|
Reference in New Issue
Block a user