refactor(face):优化人脸识别更新接口及样本展示逻辑

- 修改 updateRecognition 接口返回类型为 void,简化响应内容
- 移除 FaceRecognitionSampleVO 中冗余的字段(sourceType、faceUrl 等)- 删除与过滤原因相关的属性和处理逻辑
- 简化 buildSampleVO 方法参数及内部实现- 调整 resolveSourceUrl 方法中 URL 获取优先级
- 优化样本列表构建逻辑,提升性能与可读性
This commit is contained in:
2025-10-29 15:21:15 +08:00
parent 07ebccad3c
commit b6bde4ad62
4 changed files with 25 additions and 110 deletions

View File

@@ -111,10 +111,11 @@ public class AppFaceController {
}
@PutMapping("/{faceId}/recognition")
public ApiResponse<FaceRecognitionDetailVO> updateRecognition(@PathVariable Long faceId,
public ApiResponse<?> updateRecognition(@PathVariable Long faceId,
@RequestBody FaceRecognitionUpdateReq req) {
req.setFaceId(faceId);
return ApiResponse.success(faceService.updateRecognition(req));
faceService.updateRecognition(req);
return ApiResponse.success("OK");
}
@GetMapping("/{faceId}/recognition/detail")

View File

@@ -17,9 +17,7 @@ public class FaceRecognitionSampleVO {
private Boolean accepted;
private Long sourceId;
private Integer sourceType;
private String sourceUrl;
private String faceUrl;
private Long deviceId;
private String deviceName;
@@ -27,16 +25,4 @@ public class FaceRecognitionSampleVO {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date shotAt;
private Integer isFree;
private Integer isBuy;
/**
* 过滤原因列表,用于提示用户样本被过滤的原因。
*/
private List<FaceRecognitionFilterReason> filterReasons;
/**
* 过滤原因的描述集合,方便前端直接展示。
*/
private List<String> filterReasonTexts;
}

View File

@@ -53,7 +53,7 @@ public interface FaceService {
void matchCustomFaceId(Long faceId, List<Long> faceSampleIds);
FaceRecognitionDetailVO updateRecognition(FaceRecognitionUpdateReq req);
void updateRecognition(FaceRecognitionUpdateReq req);
FaceRecognitionDetailVO getRecognitionDetail(Long faceId);
}

View File

@@ -27,7 +27,6 @@ import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
import com.ycwl.basic.model.mobile.statistic.req.StatisticsRecordAddReq;
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.face.enums.FaceRecognitionFilterReason;
import com.ycwl.basic.model.mobile.face.FaceRecognitionUpdateReq;
import com.ycwl.basic.model.pc.face.req.FaceReqQuery;
import com.ycwl.basic.model.mobile.face.FaceRecognitionDetailVO;
@@ -80,7 +79,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -1264,7 +1262,7 @@ public class FaceServiceImpl implements FaceService {
}
@Override
public FaceRecognitionDetailVO updateRecognition(FaceRecognitionUpdateReq req) {
public void updateRecognition(FaceRecognitionUpdateReq req) {
if (req == null || req.getFaceId() == null) {
throw new IllegalArgumentException("faceId 不能为空");
}
@@ -1308,8 +1306,6 @@ public class FaceServiceImpl implements FaceService {
if (Strings.isNotBlank(req.getRemark())) {
log.info("人脸识别人工调整备注:faceId={}, remark={}", faceId, req.getRemark());
}
return getRecognitionDetail(faceId);
}
@Override
@@ -1363,52 +1359,12 @@ public class FaceServiceImpl implements FaceService {
return detail;
}
List<FaceSampleEntity> allSamples = faceSampleMapper.listByIds(allSampleIds);
Map<Long, FaceSampleEntity> sampleEntityMap = allSamples.stream()
.collect(Collectors.toMap(FaceSampleEntity::getId, Function.identity(), (a, b) -> a, LinkedHashMap::new));
List<SourceEntity> sourceEntities = sourceMapper.listBySampleIds(allSampleIds);
Map<Long, SourceEntity> sourceBySampleId = sourceEntities.stream()
.collect(Collectors.toMap(SourceEntity::getFaceSampleId, Function.identity(), (a, b) -> a, LinkedHashMap::new));
Map<Long, SourceEntity> sourceById = sourceEntities.stream()
.collect(Collectors.toMap(SourceEntity::getId, Function.identity(), (a, b) -> a));
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(face.getScenicId());
Float thresholdConfig = scenicConfig != null ? scenicConfig.getFloat("face_score_threshold") : null;
float threshold = thresholdConfig != null ? thresholdConfig / 100F : 0F;
List<Long> initialAcceptedIds = allSampleIds.stream()
.filter(id -> {
SearchFaceResultItem item = itemBySampleId.get(id);
return item != null && item.getScore() != null && item.getScore() > threshold;
})
.collect(Collectors.toList());
List<FaceSampleEntity> orderedSampleList = allSampleIds.stream()
.map(sampleEntityMap::get)
.filter(Objects::nonNull)
.collect(Collectors.toList());
SampleFilterTrace filterTrace = faceService.applySampleFiltersWithTrace(initialAcceptedIds, orderedSampleList, scenicConfig);
List<Long> systemAcceptedIds = filterTrace.getAcceptedSampleIds() == null
? Collections.emptyList()
: filterTrace.getAcceptedSampleIds();
Set<Long> initialAcceptedSet = new HashSet<>(initialAcceptedIds);
for (Long sampleId : allSampleIds) {
if (!initialAcceptedSet.contains(sampleId) && !systemAcceptedIds.contains(sampleId)) {
filterTrace.addReason(sampleId, FaceRecognitionFilterReason.SCORE_BELOW_THRESHOLD);
}
}
Set<Long> systemAcceptedSet = new HashSet<>(systemAcceptedIds);
Set<Long> persistedAcceptedSet = new HashSet<>(persistedAcceptedIds);
for (Long sampleId : systemAcceptedSet) {
if (!persistedAcceptedSet.contains(sampleId)) {
filterTrace.addReason(sampleId, FaceRecognitionFilterReason.MANUAL_REJECTED);
}
}
List<MemberSourceEntity> relations = new ArrayList<>();
List<MemberSourceEntity> videoRelations = memberRelationRepository.listSourceByFaceRelation(faceId, 1);
if (videoRelations != null) {
@@ -1426,9 +1382,9 @@ public class FaceServiceImpl implements FaceService {
}
}
Map<Long, EnumSet<FaceRecognitionFilterReason>> reasonMap = filterTrace.getFilteredReasonMap();
Map<Long, DeviceEntity> deviceCache = new HashMap<>();
Set<Long> persistedAcceptedSet = new LinkedHashSet<>(persistedAcceptedIds);
List<Long> acceptedOrdered = new ArrayList<>();
for (Long sampleId : allSampleIds) {
if (persistedAcceptedSet.contains(sampleId)) {
@@ -1446,32 +1402,25 @@ public class FaceServiceImpl implements FaceService {
sampleId,
true,
itemBySampleId.get(sampleId),
sampleEntityMap.get(sampleId),
sourceBySampleId.get(sampleId),
relationBySampleId.get(sampleId),
deviceCache,
Collections.emptyList()))
deviceCache))
.collect(Collectors.toList());
Set<Long> acceptedSet = new HashSet<>(acceptedOrdered);
Set<Long> acceptedSet = new LinkedHashSet<>(acceptedOrdered);
List<FaceRecognitionSampleVO> filteredSamples = new ArrayList<>();
for (Long sampleId : allSampleIds) {
if (acceptedSet.contains(sampleId)) {
continue;
}
EnumSet<FaceRecognitionFilterReason> reasons = reasonMap.get(sampleId);
List<FaceRecognitionFilterReason> reasonList = reasons == null
? Collections.emptyList()
: new ArrayList<>(reasons);
filteredSamples.add(buildSampleVO(
FaceRecognitionSampleVO sampleVO = buildSampleVO(
sampleId,
false,
itemBySampleId.get(sampleId),
sampleEntityMap.get(sampleId),
sourceBySampleId.get(sampleId),
relationBySampleId.get(sampleId),
deviceCache,
reasonList));
deviceCache);
if (sampleVO.getSourceId() != null) {
filteredSamples.add(sampleVO);
}
}
detail.setAcceptedSamples(acceptedSamples);
@@ -1508,44 +1457,26 @@ public class FaceServiceImpl implements FaceService {
private FaceRecognitionSampleVO buildSampleVO(Long sampleId,
boolean accepted,
SearchFaceResultItem resultItem,
FaceSampleEntity sampleEntity,
SourceEntity sourceEntity,
MemberSourceEntity relation,
Map<Long, DeviceEntity> deviceCache,
List<FaceRecognitionFilterReason> reasons) {
Map<Long, DeviceEntity> deviceCache) {
FaceRecognitionSampleVO vo = new FaceRecognitionSampleVO();
vo.setSampleId(sampleId);
vo.setAccepted(accepted);
if (resultItem != null) {
vo.setScore(resultItem.getScore());
}
if (sampleEntity != null) {
vo.setFaceUrl(sampleEntity.getFaceUrl());
vo.setDeviceId(sampleEntity.getDeviceId());
vo.setShotAt(sampleEntity.getCreateAt());
DeviceEntity device = getDeviceCached(sampleEntity.getDeviceId(), deviceCache);
if (sourceEntity != null) {
if (sourceEntity.getDeviceId() != null) {
vo.setDeviceId(sourceEntity.getDeviceId());
vo.setShotAt(sourceEntity.getCreateTime());
DeviceEntity device = getDeviceCached(sourceEntity.getDeviceId(), deviceCache);
if (device != null) {
vo.setDeviceName(device.getName());
}
}
if (sourceEntity != null) {
vo.setSourceId(sourceEntity.getId());
vo.setSourceType(sourceEntity.getType());
vo.setSourceUrl(resolveSourceUrl(sourceEntity));
}
if (relation != null) {
vo.setIsFree(relation.getIsFree());
vo.setIsBuy(relation.getIsBuy());
}
if (reasons != null && !reasons.isEmpty()) {
vo.setFilterReasons(new ArrayList<>(reasons));
vo.setFilterReasonTexts(reasons.stream()
.map(FaceRecognitionFilterReason::getDescription)
.collect(Collectors.toList()));
} else {
vo.setFilterReasons(Collections.emptyList());
vo.setFilterReasonTexts(Collections.emptyList());
}
return vo;
}
@@ -1565,15 +1496,12 @@ public class FaceServiceImpl implements FaceService {
if (sourceEntity == null) {
return null;
}
if (!Strings.isBlank(sourceEntity.getUrl())) {
return sourceEntity.getUrl();
}
if (!Strings.isBlank(sourceEntity.getVideoUrl())) {
return sourceEntity.getVideoUrl();
}
if (!Strings.isBlank(sourceEntity.getThumbUrl())) {
return sourceEntity.getThumbUrl();
}
if (!Strings.isBlank(sourceEntity.getUrl())) {
return sourceEntity.getUrl();
}
return null;
}