feat(face): 引入人脸识别指标记录与搜索结果合并功能

- 新增 FaceMetricsRecorder 类用于记录人脸识别、自定义匹配及低阈值检测次数
- 新增 SearchResultMerger 类用于合并多个人脸搜索结果,支持并集与交集模式- 在 FaceServiceImpl 中引入 metricsRecorder 和 resultMerger 辅助类
- 替换原有的 Redis 操作代码为 FaceMetricsRecorder 的方法调用- 将搜索结果合并逻辑从 FaceServiceImpl 提取至 SearchResultMerger- 新增策略模式相关类:RematchContext、RematchModeStrategy 接口及四种实现
- 使用策略工厂 Rematch
This commit is contained in:
2025-10-31 17:11:02 +08:00
parent 12cd9bd275
commit bf014db7ff
10 changed files with 632 additions and 251 deletions

View File

@@ -0,0 +1,47 @@
package com.ycwl.basic.service.pc.strategy;
import lombok.Builder;
import lombok.Data;
import java.util.Date;
/**
* 重匹配上下文
* 包含判断是否需要重匹配的所有必要信息
*
* @author longbinbin
* @date 2025-01-31
*/
@Data
@Builder
public class RematchContext {
/**
* 人脸识别次数
*/
private long recognitionCount;
/**
* 是否触发低阈值检测
*/
private boolean hasLowThreshold;
/**
* 是否符合游览时间匹配
*/
private boolean tourMatch;
/**
* 是否符合项目时间匹配
*/
private boolean projectMatch;
/**
* 规则匹配数量
*/
private int ruleMatched;
/**
* 人脸创建时间
*/
private Date faceCreateAt;
}

View File

@@ -0,0 +1,26 @@
package com.ycwl.basic.service.pc.strategy;
/**
* 重匹配模式策略接口
* 用于判断是否需要进行人脸重新匹配
*
* @author longbinbin
* @date 2025-01-31
*/
public interface RematchModeStrategy {
/**
* 判断是否应该重新匹配
*
* @param context 重匹配上下文
* @return true-需要重匹配, false-不需要重匹配
*/
boolean shouldRematch(RematchContext context);
/**
* 获取策略对应的模式值
*
* @return 模式值
*/
int getMode();
}

View File

@@ -0,0 +1,60 @@
package com.ycwl.basic.service.pc.strategy;
import com.ycwl.basic.service.pc.strategy.impl.DefaultRematchStrategy;
import com.ycwl.basic.service.pc.strategy.impl.RematchMode1Strategy;
import com.ycwl.basic.service.pc.strategy.impl.RematchMode5Strategy;
import com.ycwl.basic.service.pc.strategy.impl.RematchMode9Strategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 重匹配策略工厂
* 根据模式值获取对应的策略实例
*
* @author longbinbin
* @date 2025-01-31
*/
@Slf4j
@Component
public class RematchStrategyFactory {
@Autowired
private List<RematchModeStrategy> strategies;
private final Map<Integer, RematchModeStrategy> strategyMap = new HashMap<>();
@PostConstruct
public void init() {
for (RematchModeStrategy strategy : strategies) {
strategyMap.put(strategy.getMode(), strategy);
log.debug("注册重匹配策略: mode={}, class={}",
strategy.getMode(), strategy.getClass().getSimpleName());
}
}
/**
* 根据模式值获取对应的策略
*
* @param mode 模式值(0-默认, 1-模式1, 5-模式5, 9-模式9)
* @return 对应的策略实例,如果没有找到则返回默认策略
*/
public RematchModeStrategy getStrategy(Integer mode) {
if (mode == null) {
return strategyMap.getOrDefault(0, new DefaultRematchStrategy());
}
RematchModeStrategy strategy = strategyMap.get(mode);
if (strategy == null) {
log.warn("未找到重匹配模式{}对应的策略,使用默认策略", mode);
return strategyMap.getOrDefault(0, new DefaultRematchStrategy());
}
return strategy;
}
}

View File

@@ -0,0 +1,29 @@
package com.ycwl.basic.service.pc.strategy.impl;
import com.ycwl.basic.service.pc.strategy.RematchContext;
import com.ycwl.basic.service.pc.strategy.RematchModeStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 默认重匹配策略(模式0或其他未定义模式)
* 条件: 不触发重匹配
*
* @author longbinbin
* @date 2025-01-31
*/
@Slf4j
@Component
public class DefaultRematchStrategy implements RematchModeStrategy {
@Override
public boolean shouldRematch(RematchContext context) {
log.debug("DefaultRematchStrategy判断: 默认不重匹配");
return false;
}
@Override
public int getMode() {
return 0;
}
}

View File

@@ -0,0 +1,37 @@
package com.ycwl.basic.service.pc.strategy.impl;
import com.ycwl.basic.service.pc.strategy.RematchContext;
import com.ycwl.basic.service.pc.strategy.RematchModeStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 重匹配模式1策略
* 条件: tourMatch || recognitionCount > 1 || hasLowThreshold
* 满足任一条件即可重匹配
*
* @author longbinbin
* @date 2025-01-31
*/
@Slf4j
@Component
public class RematchMode1Strategy implements RematchModeStrategy {
@Override
public boolean shouldRematch(RematchContext context) {
boolean result = context.isTourMatch()
|| context.getRecognitionCount() > 1
|| context.isHasLowThreshold();
log.debug("RematchMode1Strategy判断: tourMatch={}, recognitionCount={}, hasLowThreshold={}, result={}",
context.isTourMatch(), context.getRecognitionCount(),
context.isHasLowThreshold(), result);
return result;
}
@Override
public int getMode() {
return 1;
}
}

View File

@@ -0,0 +1,35 @@
package com.ycwl.basic.service.pc.strategy.impl;
import com.ycwl.basic.service.pc.strategy.RematchContext;
import com.ycwl.basic.service.pc.strategy.RematchModeStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 重匹配模式5策略
* 条件: hasLowThreshold || (ruleMatched >= 2)
* 触发低阈值或匹配2个及以上规则即可重匹配
*
* @author longbinbin
* @date 2025-01-31
*/
@Slf4j
@Component
public class RematchMode5Strategy implements RematchModeStrategy {
@Override
public boolean shouldRematch(RematchContext context) {
boolean result = context.isHasLowThreshold()
|| context.getRuleMatched() >= 2;
log.debug("RematchMode5Strategy判断: hasLowThreshold={}, ruleMatched={}, result={}",
context.isHasLowThreshold(), context.getRuleMatched(), result);
return result;
}
@Override
public int getMode() {
return 5;
}
}

View File

@@ -0,0 +1,35 @@
package com.ycwl.basic.service.pc.strategy.impl;
import com.ycwl.basic.service.pc.strategy.RematchContext;
import com.ycwl.basic.service.pc.strategy.RematchModeStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 重匹配模式9策略
* 条件: hasLowThreshold && (ruleMatched >= 2)
* 必须同时触发低阈值且匹配2个及以上规则才可重匹配
*
* @author longbinbin
* @date 2025-01-31
*/
@Slf4j
@Component
public class RematchMode9Strategy implements RematchModeStrategy {
@Override
public boolean shouldRematch(RematchContext context) {
boolean result = context.isHasLowThreshold()
&& context.getRuleMatched() >= 2;
log.debug("RematchMode9Strategy判断: hasLowThreshold={}, ruleMatched={}, result={}",
context.isHasLowThreshold(), context.getRuleMatched(), result);
return result;
}
@Override
public int getMode() {
return 9;
}
}