feat(face): 增加人脸识别详情与人工调整功能

- 新增人脸识别详情接口,返回系统采纳与被过滤的样本信息
- 新增人工调整识别结果接口,支持用户手动选择或排除样本
- 引入样本过滤原因枚举,用于记录和展示过滤原因
- 重构样本过滤逻辑,增加过滤轨迹追踪功能
- 优化时间范围与设备照片数量限制的过滤实现
- 在搜索结果中增加过滤轨迹信息,便于前端展示
- 添加人脸识别详情VO和样本VO,丰富返回数据结构
- 完善人脸识别相关的请求与响应模型定义
This commit is contained in:
2025-10-21 21:35:06 +08:00
parent 37033f1b16
commit 1b312313b2
11 changed files with 696 additions and 169 deletions

View File

@@ -0,0 +1,22 @@
package com.ycwl.basic.model.pc.face.enums;
import lombok.Getter;
/**
* 标识系统在过滤识别样本时的原因,便于前端渲染提示。
*/
@Getter
public enum FaceRecognitionFilterReason {
SCORE_BELOW_THRESHOLD("score_below_threshold", "置信度低于阈值"),
OUT_OF_TIME_RANGE("out_of_time_range", "超出景区时间范围限制"),
DEVICE_PHOTO_LIMIT("device_photo_limit", "超过设备照片数量限制"),
MANUAL_REJECTED("manual_rejected", "已被手动排除");
private final String code;
private final String description;
FaceRecognitionFilterReason(String code, String description) {
this.code = code;
this.description = description;
}
}

View File

@@ -0,0 +1,36 @@
package com.ycwl.basic.model.pc.face.req;
import lombok.Data;
import java.util.List;
/**
* 人工调整人脸识别结果的请求体。
*/
@Data
public class FaceRecognitionUpdateReq {
/**
* 指定需要操作的人脸ID。
*/
private Long faceId;
/**
* 用户人工选中希望保留的样本ID列表。
*/
private List<Long> manualAcceptedSampleIds;
/**
* 用户主动排除的样本ID列表。
*/
private List<Long> manualRejectedSampleIds;
/**
* 是否强制重新走一次识别流程。
*/
private Boolean forceRematch;
/**
* 前端传回的备注信息。
*/
private String remark;
}

View File

@@ -0,0 +1,35 @@
package com.ycwl.basic.model.pc.face.resp;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* 人脸识别详情,包含系统采纳及被过滤的样本。
*/
@Data
public class FaceRecognitionDetailVO {
private Long faceId;
private Long memberId;
private Long scenicId;
private String faceUrl;
private Float score;
private Float firstMatchRate;
private Boolean lowThreshold;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date lastMatchedAt;
/**
* 系统采纳的样本信息。
*/
private List<FaceRecognitionSampleVO> acceptedSamples;
/**
* 被系统过滤的样本信息。
*/
private List<FaceRecognitionSampleVO> filteredSamples;
}

View File

@@ -0,0 +1,42 @@
package com.ycwl.basic.model.pc.face.resp;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ycwl.basic.model.pc.face.enums.FaceRecognitionFilterReason;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* 单个人脸样本识别结果的信息描述。
*/
@Data
public class FaceRecognitionSampleVO {
private Long sampleId;
private Float score;
private Boolean accepted;
private Long sourceId;
private Integer sourceType;
private String sourceUrl;
private String faceUrl;
private Long deviceId;
private String deviceName;
@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

@@ -0,0 +1,34 @@
package com.ycwl.basic.model.task.resp;
import com.ycwl.basic.model.pc.face.enums.FaceRecognitionFilterReason;
import lombok.Data;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 样本筛选的轨迹信息,记录最终样本集合及各过滤原因。
*/
@Data
public class SampleFilterTrace {
private List<Long> acceptedSampleIds;
private Map<Long, EnumSet<FaceRecognitionFilterReason>> filteredReasonMap = new HashMap<>();
public void addReason(Long sampleId, FaceRecognitionFilterReason reason) {
if (sampleId == null || reason == null) {
return;
}
filteredReasonMap.computeIfAbsent(sampleId, key -> EnumSet.noneOf(FaceRecognitionFilterReason.class))
.add(reason);
}
public Map<Long, EnumSet<FaceRecognitionFilterReason>> getFilteredReasonMap() {
if (filteredReasonMap == null) {
return Collections.emptyMap();
}
return filteredReasonMap;
}
}

View File

@@ -11,4 +11,5 @@ public class SearchFaceRespVo {
private String searchResultJson;
private Float firstMatchRate;
private boolean lowThreshold;
private SampleFilterTrace filterTrace;
}