You've already forked FrameTour-BE
Compare commits
10 Commits
32b5b39ea3
...
2f88699bb0
Author | SHA1 | Date | |
---|---|---|---|
2f88699bb0 | |||
39bd18497c | |||
1d666c076e | |||
88974d7e9e | |||
a956c54500 | |||
a7e5c8cd95 | |||
eaf959e1b8 | |||
0c56a7fa67 | |||
7839082352 | |||
c1d61f4ed5 |
@@ -1,100 +0,0 @@
|
||||
package com.ycwl.basic.controller.pc;
|
||||
|
||||
import com.ycwl.basic.integration.scenic.dto.config.DefaultConfigDTO;
|
||||
import com.ycwl.basic.integration.scenic.service.ScenicDefaultConfigIntegrationService;
|
||||
import com.ycwl.basic.utils.ApiConst;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 默认配置管理控制器
|
||||
* 提供默认配置的增删查改功能
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/default-config")
|
||||
@RequiredArgsConstructor
|
||||
public class DefaultConfigController {
|
||||
|
||||
private final ScenicDefaultConfigIntegrationService scenicDefaultConfigIntegrationService;
|
||||
|
||||
/**
|
||||
* 获取默认配置列表
|
||||
*/
|
||||
@GetMapping("/")
|
||||
public ApiResponse<List<DefaultConfigDTO>> listDefaultConfigs() {
|
||||
log.info("获取默认配置列表");
|
||||
try {
|
||||
List<DefaultConfigDTO> configs = scenicDefaultConfigIntegrationService.listDefaultConfigs();
|
||||
return ApiResponse.success(configs);
|
||||
} catch (Exception e) {
|
||||
log.error("获取默认配置列表失败", e);
|
||||
return ApiResponse.fail("获取默认配置列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置键获取默认配置
|
||||
*/
|
||||
@GetMapping("/{configKey}")
|
||||
public ApiResponse<DefaultConfigDTO> getDefaultConfig(@PathVariable String configKey) {
|
||||
log.info("获取默认配置, configKey: {}", configKey);
|
||||
try {
|
||||
DefaultConfigDTO config = scenicDefaultConfigIntegrationService.getDefaultConfig(configKey);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("获取默认配置失败, configKey: {}", configKey, e);
|
||||
return ApiResponse.fail("获取默认配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建默认配置
|
||||
*/
|
||||
@PostMapping("/")
|
||||
public ApiResponse<DefaultConfigDTO> createDefaultConfig(@RequestBody DefaultConfigDTO request) {
|
||||
log.info("创建默认配置, configKey: {}", request.getConfigKey());
|
||||
try {
|
||||
DefaultConfigDTO config = scenicDefaultConfigIntegrationService.createDefaultConfig(request);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("创建默认配置失败, configKey: {}", request.getConfigKey(), e);
|
||||
return ApiResponse.fail("创建默认配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新默认配置
|
||||
*/
|
||||
@PutMapping("/{configKey}")
|
||||
public ApiResponse<DefaultConfigDTO> updateDefaultConfig(@PathVariable String configKey,
|
||||
@RequestBody DefaultConfigDTO request) {
|
||||
log.info("更新默认配置, configKey: {}", configKey);
|
||||
try {
|
||||
DefaultConfigDTO config = scenicDefaultConfigIntegrationService.updateDefaultConfig(configKey, request);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("更新默认配置失败, configKey: {}", configKey, e);
|
||||
return ApiResponse.fail("更新默认配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除默认配置
|
||||
*/
|
||||
@DeleteMapping("/{configKey}")
|
||||
public ApiResponse<Void> deleteDefaultConfig(@PathVariable String configKey) {
|
||||
log.info("删除默认配置, configKey: {}", configKey);
|
||||
try {
|
||||
scenicDefaultConfigIntegrationService.deleteDefaultConfig(configKey);
|
||||
return ApiResponse.buildResponse(ApiConst.Code.CODE_SUCCESS.code(), null, "删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除默认配置失败, configKey: {}", configKey, e);
|
||||
return ApiResponse.fail("删除默认配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@@ -24,14 +24,28 @@ public class ScenicConfigIntegrationService {
|
||||
|
||||
public List<ScenicConfigV2DTO> listConfigs(Long scenicId) {
|
||||
log.debug("获取景区配置列表, scenicId: {}", scenicId);
|
||||
CommonResponse<List<ScenicConfigV2DTO>> response = scenicConfigV2Client.listConfigs(scenicId);
|
||||
return handleResponse(response, "获取景区配置列表失败");
|
||||
return fallbackService.executeWithFallback(
|
||||
SERVICE_NAME,
|
||||
"scenic:configs:" + scenicId,
|
||||
() -> {
|
||||
CommonResponse<List<ScenicConfigV2DTO>> response = scenicConfigV2Client.listConfigs(scenicId);
|
||||
return handleResponse(response, "获取景区配置列表失败");
|
||||
},
|
||||
List.class
|
||||
);
|
||||
}
|
||||
|
||||
public ScenicConfigV2DTO getConfigByKey(Long scenicId, String configKey) {
|
||||
log.debug("根据键获取景区配置, scenicId: {}, configKey: {}", scenicId, configKey);
|
||||
CommonResponse<ScenicConfigV2DTO> response = scenicConfigV2Client.getConfigByKey(scenicId, configKey);
|
||||
return handleResponse(response, "根据键获取景区配置失败");
|
||||
return fallbackService.executeWithFallback(
|
||||
SERVICE_NAME,
|
||||
"scenic:config:" + scenicId + ":" + configKey,
|
||||
() -> {
|
||||
CommonResponse<ScenicConfigV2DTO> response = scenicConfigV2Client.getConfigByKey(scenicId, configKey);
|
||||
return handleResponse(response, "根据键获取景区配置失败");
|
||||
},
|
||||
ScenicConfigV2DTO.class
|
||||
);
|
||||
}
|
||||
|
||||
public Map<String, Object> getFlatConfigs(Long scenicId) {
|
||||
|
@@ -24,10 +24,11 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Objects;
|
||||
|
||||
@Component
|
||||
public class ScenicRepository {
|
||||
@@ -335,10 +336,7 @@ public class ScenicRepository {
|
||||
public ScenicConfigManager getScenicConfigManager(Long scenicId) {
|
||||
try {
|
||||
List<ScenicConfigV2DTO> configList = scenicConfigIntegrationService.listConfigs(scenicId);
|
||||
if (configList != null) {
|
||||
return new ScenicConfigManager(configList);
|
||||
}
|
||||
return null;
|
||||
return new ScenicConfigManager(Objects.requireNonNullElse(configList, Collections.emptyList()));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
@@ -371,7 +369,7 @@ public class ScenicRepository {
|
||||
if (scenicIds == null || scenicIds.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
|
||||
Map<Long, String> result = new HashMap<>();
|
||||
for (Long scenicId : scenicIds) {
|
||||
try {
|
||||
@@ -389,14 +387,14 @@ public class ScenicRepository {
|
||||
|
||||
/**
|
||||
* 批量获取景区完整信息
|
||||
* @param scenicIds 景区ID列表
|
||||
* @param scenicIds 景区ID列表
|
||||
* @return 景区ID到景区实体的映射
|
||||
*/
|
||||
public Map<Long, ScenicEntity> batchGetScenics(List<Long> scenicIds) {
|
||||
if (scenicIds == null || scenicIds.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
|
||||
Map<Long, ScenicEntity> result = new HashMap<>();
|
||||
for (Long scenicId : scenicIds) {
|
||||
try {
|
||||
@@ -421,7 +419,7 @@ public class ScenicRepository {
|
||||
if (scenicIds == null || scenicIds.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
|
||||
Map<Long, ScenicV2DTO> result = new HashMap<>();
|
||||
for (Long scenicId : scenicIds) {
|
||||
try {
|
||||
|
@@ -26,7 +26,7 @@ import com.ycwl.basic.model.pc.face.req.FaceReqQuery;
|
||||
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
|
||||
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
|
||||
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
|
||||
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
|
||||
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
|
||||
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
|
||||
import com.ycwl.basic.model.pc.source.req.SourceReqQuery;
|
||||
@@ -245,121 +245,361 @@ public class FaceServiceImpl implements FaceService {
|
||||
|
||||
@Override
|
||||
public SearchFaceRespVo matchFaceId(Long faceId, boolean isNew) {
|
||||
FaceEntity face = faceRepository.getFace(faceId);
|
||||
if (face == null) {
|
||||
return null;
|
||||
if (faceId == null) {
|
||||
throw new IllegalArgumentException("faceId 不能为空");
|
||||
}
|
||||
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(face.getScenicId());
|
||||
IFaceBodyAdapter faceBodyAdapter = scenicService.getScenicFaceBodyAdapter(face.getScenicId());
|
||||
SearchFaceRespVo scenicDbSearchResult = faceService.searchFace(faceBodyAdapter, String.valueOf(face.getScenicId()), face.getFaceUrl(), "人脸识别");
|
||||
if (scenicDbSearchResult == null) {
|
||||
throw new BaseException("人脸识别失败,请换一张试试把~");
|
||||
}
|
||||
if (scenicDbSearchResult.getSampleListIds() != null && scenicDbSearchResult.getFirstMatchRate() != null && !scenicDbSearchResult.getSampleListIds().isEmpty()) {
|
||||
if (scenicConfig != null && scenicConfig.getFaceDetectHelperThreshold() != null && scenicConfig.getFaceDetectHelperThreshold() > 0) {
|
||||
if (scenicDbSearchResult.getSampleListIds().size() < scenicConfig.getFaceDetectHelperThreshold()) {
|
||||
// 补救逻辑
|
||||
Long resultItem = scenicDbSearchResult.getSampleListIds().getFirst();
|
||||
FaceSampleEntity faceSample = faceRepository.getFaceSample(resultItem);
|
||||
if (faceSample != null) {
|
||||
// 以这个结果为人脸库的匹配结果
|
||||
SearchFaceRespVo tmpResult = faceService.searchFace(faceBodyAdapter, String.valueOf(face.getScenicId()), faceSample.getFaceUrl(), "人脸补救措施1");
|
||||
if (tmpResult != null && tmpResult.getSampleListIds() != null && !tmpResult.getSampleListIds().isEmpty()) {
|
||||
scenicDbSearchResult = tmpResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("开始人脸匹配:faceId={}, isNew={}", faceId, isNew);
|
||||
|
||||
try {
|
||||
// 1. 数据准备:获取人脸信息、景区配置、适配器等
|
||||
FaceEntity face = faceRepository.getFace(faceId);
|
||||
if (face == null) {
|
||||
log.warn("人脸不存在,faceId: {}", faceId);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
FaceEntity faceEntity = new FaceEntity();
|
||||
faceEntity.setId(faceId);
|
||||
faceEntity.setScore(scenicDbSearchResult.getScore());
|
||||
faceEntity.setMatchResult(scenicDbSearchResult.getSearchResultJson());
|
||||
if (scenicDbSearchResult.getFirstMatchRate() != null) {
|
||||
faceEntity.setFirstMatchRate(BigDecimal.valueOf(scenicDbSearchResult.getFirstMatchRate()));
|
||||
}
|
||||
if (scenicDbSearchResult.getSampleListIds() != null) {
|
||||
faceEntity.setMatchSampleIds(scenicDbSearchResult.getSampleListIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||
}
|
||||
faceEntity.setCreateAt(new Date());
|
||||
faceEntity.setScenicId(face.getScenicId());
|
||||
faceEntity.setMemberId(face.getMemberId());
|
||||
faceEntity.setFaceUrl(face.getFaceUrl());
|
||||
List<Long> sampleListIds = scenicDbSearchResult.getSampleListIds();
|
||||
faceMapper.update(faceEntity);
|
||||
faceRepository.clearFaceCache(faceEntity.getId());
|
||||
if (sampleListIds != null && !sampleListIds.isEmpty()) {// 匹配原片:照片
|
||||
List<SourceEntity> sourceEntities = sourceMapper.listBySampleIds(sampleListIds);
|
||||
List<MemberSourceEntity> memberSourceEntityList = sourceEntities.stream().map(sourceEntity -> {
|
||||
DeviceConfigManager deviceConfig = deviceRepository.getDeviceConfigManager(sourceEntity.getDeviceId());
|
||||
MemberSourceEntity memberSourceEntity = new MemberSourceEntity();
|
||||
memberSourceEntity.setScenicId(face.getScenicId());
|
||||
memberSourceEntity.setFaceId(face.getId());
|
||||
memberSourceEntity.setMemberId(face.getMemberId());
|
||||
memberSourceEntity.setSourceId(sourceEntity.getId());
|
||||
memberSourceEntity.setType(sourceEntity.getType());
|
||||
memberSourceEntity.setIsFree(0);
|
||||
if (deviceConfig != null) {
|
||||
if (sourceEntity.getType() == 1) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getInteger("video_free"))) {
|
||||
memberSourceEntity.setIsFree(1);
|
||||
}
|
||||
} else if (sourceEntity.getType() == 2) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getInteger("image_free"))) {
|
||||
memberSourceEntity.setIsFree(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return memberSourceEntity;
|
||||
}).collect(Collectors.toList());
|
||||
List<Long> freeSourceIds = new ArrayList<>();
|
||||
List<MemberSourceEntity> photoSource = memberSourceEntityList.stream().filter(item -> item.getIsFree() == 0).filter(item -> Integer.valueOf(2).equals(item.getType())).toList();
|
||||
if (isNew) {
|
||||
// 送照片逻辑
|
||||
if (scenicConfig != null && scenicConfig.getPhotoFreeNum() != null && scenicConfig.getPhotoFreeNum() > 0) {
|
||||
if (scenicConfig.getPhotoFreeNum() > photoSource.size()) {
|
||||
freeSourceIds.addAll(photoSource.stream().map(MemberSourceEntity::getSourceId).toList());
|
||||
} else {
|
||||
freeSourceIds.addAll(photoSource.stream().limit(scenicConfig.getPhotoFreeNum()).map(MemberSourceEntity::getSourceId).toList());
|
||||
|
||||
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(face.getScenicId());
|
||||
IFaceBodyAdapter faceBodyAdapter = scenicService.getScenicFaceBodyAdapter(face.getScenicId());
|
||||
|
||||
if (faceBodyAdapter == null) {
|
||||
log.error("无法获取人脸识别适配器,scenicId: {}", face.getScenicId());
|
||||
throw new BaseException("人脸识别服务不可用,请稍后再试");
|
||||
}
|
||||
|
||||
// 2. 人脸识别:执行人脸搜索和补救逻辑
|
||||
SearchFaceRespVo scenicDbSearchResult;
|
||||
try {
|
||||
scenicDbSearchResult = faceService.searchFace(faceBodyAdapter,
|
||||
String.valueOf(face.getScenicId()),
|
||||
face.getFaceUrl(),
|
||||
"人脸识别");
|
||||
} catch (Exception e) {
|
||||
log.error("人脸识别服务调用失败,faceId={}, scenicId={}", faceId, face.getScenicId(), e);
|
||||
throw new BaseException("人脸识别失败,请换一张试试把~");
|
||||
}
|
||||
|
||||
if (scenicDbSearchResult == null) {
|
||||
log.warn("人脸识别返回结果为空,faceId={}", faceId);
|
||||
throw new BaseException("人脸识别失败,请换一张试试把~");
|
||||
}
|
||||
|
||||
// 执行补救逻辑(如需要)
|
||||
scenicDbSearchResult = executeFaceRecoveryLogic(scenicDbSearchResult, scenicConfig,
|
||||
faceBodyAdapter, face.getScenicId());
|
||||
|
||||
// 3. 结果处理:更新人脸实体信息
|
||||
try {
|
||||
updateFaceEntityResult(face, scenicDbSearchResult, faceId);
|
||||
} catch (Exception e) {
|
||||
log.error("更新人脸结果失败,faceId={}", faceId, e);
|
||||
throw new BaseException("保存人脸识别结果失败");
|
||||
}
|
||||
|
||||
List<Long> sampleListIds = scenicDbSearchResult.getSampleListIds();
|
||||
if (sampleListIds != null && !sampleListIds.isEmpty()) {
|
||||
try {
|
||||
// 4. 源文件关联:处理匹配到的源文件
|
||||
List<MemberSourceEntity> memberSourceEntityList = processMemberSources(sampleListIds, face);
|
||||
|
||||
if (!memberSourceEntityList.isEmpty()) {
|
||||
// 5. 业务逻辑处理:免费逻辑、购买状态、任务创建
|
||||
List<Long> freeSourceIds = processFreeSourceLogic(memberSourceEntityList, scenicConfig, isNew);
|
||||
processBuyStatus(memberSourceEntityList, freeSourceIds, face.getMemberId(),
|
||||
face.getScenicId(), faceId);
|
||||
|
||||
// 处理视频重切逻辑
|
||||
handleVideoRecreation(scenicConfig, memberSourceEntityList, faceId,
|
||||
face.getMemberId(), sampleListIds, isNew);
|
||||
|
||||
// 保存关联关系并创建任务
|
||||
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 {
|
||||
// 重新切视频逻辑
|
||||
if (scenicConfig != null && !Boolean.TRUE.equals(scenicConfig.getDisableSourceVideo())) {
|
||||
long videoCount = memberSourceEntityList.stream().filter(item -> item.getType().equals(1)).count();
|
||||
long photoCount = memberSourceEntityList.stream().filter(item -> item.getType().equals(2)).count();
|
||||
if (photoCount > videoCount) {
|
||||
VideoPieceGetter.Task task = new VideoPieceGetter.Task();
|
||||
task.faceId = faceId;
|
||||
task.faceSampleIds = sampleListIds;
|
||||
task.templateId = null;
|
||||
task.memberId = face.getMemberId();
|
||||
task.callback = () -> {
|
||||
log.info("task callback: {}", task);
|
||||
};
|
||||
VideoPieceGetter.addTask(task);
|
||||
}
|
||||
}
|
||||
log.warn("人脸匹配无结果:faceId={}", faceId);
|
||||
}
|
||||
if (!memberSourceEntityList.isEmpty()) {
|
||||
IsBuyRespVO isBuy = orderBiz.isBuy(face.getMemberId(), face.getScenicId(), memberSourceEntityList.getFirst().getType(), faceEntity.getId());
|
||||
for (MemberSourceEntity memberSourceEntity : memberSourceEntityList) {
|
||||
if (isBuy.isBuy()) { // 如果用户买过
|
||||
memberSourceEntity.setIsBuy(1);
|
||||
} else if (isBuy.isFree()) { // 全免费逻辑
|
||||
memberSourceEntity.setIsBuy(1);
|
||||
} else {
|
||||
memberSourceEntity.setIsBuy(0);
|
||||
|
||||
return scenicDbSearchResult;
|
||||
|
||||
} catch (BaseException e) {
|
||||
// 业务异常直接抛出
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("人脸匹配处理异常,faceId={}, isNew={}", faceId, isNew, e);
|
||||
throw new BaseException("人脸匹配处理失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新人脸实体结果信息
|
||||
*/
|
||||
private void updateFaceEntityResult(FaceEntity originalFace, SearchFaceRespVo searchResult, Long faceId) {
|
||||
FaceEntity faceEntity = new FaceEntity();
|
||||
faceEntity.setId(faceId);
|
||||
faceEntity.setScore(searchResult.getScore());
|
||||
faceEntity.setMatchResult(searchResult.getSearchResultJson());
|
||||
|
||||
if (searchResult.getFirstMatchRate() != null) {
|
||||
faceEntity.setFirstMatchRate(BigDecimal.valueOf(searchResult.getFirstMatchRate()));
|
||||
}
|
||||
|
||||
if (searchResult.getSampleListIds() != null) {
|
||||
faceEntity.setMatchSampleIds(searchResult.getSampleListIds().stream()
|
||||
.map(String::valueOf)
|
||||
.collect(Collectors.joining(",")));
|
||||
}
|
||||
|
||||
faceEntity.setCreateAt(new Date());
|
||||
faceEntity.setScenicId(originalFace.getScenicId());
|
||||
faceEntity.setMemberId(originalFace.getMemberId());
|
||||
faceEntity.setFaceUrl(originalFace.getFaceUrl());
|
||||
|
||||
faceMapper.update(faceEntity);
|
||||
faceRepository.clearFaceCache(faceEntity.getId());
|
||||
|
||||
log.debug("人脸结果更新完成:faceId={}, score={}, 匹配数={}",
|
||||
faceId, searchResult.getScore(),
|
||||
searchResult.getSampleListIds() != null ? searchResult.getSampleListIds().size() : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行人脸识别补救逻辑
|
||||
* 当匹配结果数量少于阈值时,使用第一个匹配结果重新进行人脸搜索
|
||||
*/
|
||||
private SearchFaceRespVo executeFaceRecoveryLogic(SearchFaceRespVo originalResult,
|
||||
ScenicConfigManager scenicConfig,
|
||||
IFaceBodyAdapter faceBodyAdapter,
|
||||
Long scenicId) {
|
||||
if (originalResult == null || originalResult.getSampleListIds() == null ||
|
||||
originalResult.getFirstMatchRate() == null || originalResult.getSampleListIds().isEmpty()) {
|
||||
return originalResult;
|
||||
}
|
||||
|
||||
if (scenicConfig == null) {
|
||||
return originalResult;
|
||||
}
|
||||
|
||||
// 检查是否需要执行补救逻辑
|
||||
Integer helperThreshold = scenicConfig.getInteger("face_detect_helper_threshold", 0);
|
||||
if (helperThreshold == null || helperThreshold <= 0) {
|
||||
return originalResult;
|
||||
}
|
||||
|
||||
// 检查匹配结果数量是否少于阈值
|
||||
if (originalResult.getSampleListIds().size() >= helperThreshold) {
|
||||
return originalResult;
|
||||
}
|
||||
|
||||
log.info("执行人脸识别补救逻辑,原匹配数量: {}, 阈值: {}",
|
||||
originalResult.getSampleListIds().size(), helperThreshold);
|
||||
|
||||
// 获取第一个匹配结果
|
||||
Long firstResultId = originalResult.getSampleListIds().getFirst();
|
||||
FaceSampleEntity faceSample = faceRepository.getFaceSample(firstResultId);
|
||||
|
||||
if (faceSample == null) {
|
||||
log.warn("补救逻辑失败:无法找到人脸样本, sampleId: {}", firstResultId);
|
||||
return originalResult;
|
||||
}
|
||||
|
||||
// 使用人脸样本重新进行搜索
|
||||
try {
|
||||
SearchFaceRespVo recoveryResult = faceService.searchFace(faceBodyAdapter,
|
||||
String.valueOf(scenicId),
|
||||
faceSample.getFaceUrl(),
|
||||
"人脸补救措施1");
|
||||
|
||||
if (recoveryResult != null && recoveryResult.getSampleListIds() != null &&
|
||||
!recoveryResult.getSampleListIds().isEmpty()) {
|
||||
log.info("补救逻辑成功,新匹配数量: {}", recoveryResult.getSampleListIds().size());
|
||||
return recoveryResult;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("补救逻辑执行失败", e);
|
||||
}
|
||||
|
||||
return originalResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理源文件关联逻辑
|
||||
* 根据匹配的样本ID创建MemberSourceEntity列表
|
||||
*/
|
||||
private List<MemberSourceEntity> processMemberSources(List<Long> sampleListIds, FaceEntity face) {
|
||||
if (sampleListIds == null || sampleListIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<SourceEntity> sourceEntities = sourceMapper.listBySampleIds(sampleListIds);
|
||||
if (sourceEntities.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return sourceEntities.stream().map(sourceEntity -> {
|
||||
DeviceConfigManager deviceConfig = deviceRepository.getDeviceConfigManager(sourceEntity.getDeviceId());
|
||||
MemberSourceEntity memberSourceEntity = new MemberSourceEntity();
|
||||
memberSourceEntity.setScenicId(face.getScenicId());
|
||||
memberSourceEntity.setFaceId(face.getId());
|
||||
memberSourceEntity.setMemberId(face.getMemberId());
|
||||
memberSourceEntity.setSourceId(sourceEntity.getId());
|
||||
memberSourceEntity.setType(sourceEntity.getType());
|
||||
|
||||
// 设置免费状态 - 默认收费
|
||||
memberSourceEntity.setIsFree(0);
|
||||
|
||||
if (deviceConfig != null) {
|
||||
// 视频类型检查
|
||||
if (sourceEntity.getType() == 1) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getInteger("video_free"))) {
|
||||
memberSourceEntity.setIsFree(1);
|
||||
}
|
||||
if (freeSourceIds.contains(memberSourceEntity.getSourceId())) {
|
||||
}
|
||||
// 图片类型检查
|
||||
else if (sourceEntity.getType() == 2) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getInteger("image_free"))) {
|
||||
memberSourceEntity.setIsFree(1);
|
||||
}
|
||||
}
|
||||
sourceMapper.addRelations(memberSourceEntityList);
|
||||
taskTaskService.autoCreateTaskByFaceId(face.getId());
|
||||
}
|
||||
|
||||
return memberSourceEntity;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理免费源文件逻辑
|
||||
* 根据景区配置和是否新用户决定哪些照片可以免费
|
||||
*/
|
||||
private List<Long> processFreeSourceLogic(List<MemberSourceEntity> memberSourceEntityList,
|
||||
ScenicConfigManager scenicConfig,
|
||||
boolean isNew) {
|
||||
List<Long> freeSourceIds = new ArrayList<>();
|
||||
|
||||
if (memberSourceEntityList.isEmpty()) {
|
||||
return freeSourceIds;
|
||||
}
|
||||
|
||||
if (isNew) {
|
||||
// 新用户送照片逻辑
|
||||
List<MemberSourceEntity> photoSource = memberSourceEntityList.stream()
|
||||
.filter(item -> item.getIsFree() == 0) // 只考虑收费的
|
||||
.filter(item -> Integer.valueOf(2).equals(item.getType())) // 只考虑照片类型
|
||||
.toList();
|
||||
|
||||
Integer photoFreeNum = scenicConfig != null ? scenicConfig.getInteger("photo_free_num") : null;
|
||||
if (scenicConfig != null && photoFreeNum != null && photoFreeNum > 0) {
|
||||
|
||||
int freePhotoCount = Math.min(photoFreeNum, photoSource.size());
|
||||
freeSourceIds.addAll(photoSource.stream()
|
||||
.limit(freePhotoCount)
|
||||
.map(MemberSourceEntity::getSourceId)
|
||||
.toList());
|
||||
|
||||
log.debug("新用户免费照片逻辑:配置免费数量 {}, 实际可用 {}, 赠送 {} 张",
|
||||
photoFreeNum, photoSource.size(), freePhotoCount);
|
||||
}
|
||||
}
|
||||
return scenicDbSearchResult;
|
||||
|
||||
return freeSourceIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理购买状态逻辑
|
||||
* 设置每个源文件的购买状态和免费状态
|
||||
*/
|
||||
private void processBuyStatus(List<MemberSourceEntity> memberSourceEntityList,
|
||||
List<Long> freeSourceIds,
|
||||
Long memberId,
|
||||
Long scenicId,
|
||||
Long faceId) {
|
||||
if (memberSourceEntityList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取用户购买状态
|
||||
IsBuyRespVO isBuy = orderBiz.isBuy(memberId, scenicId,
|
||||
memberSourceEntityList.getFirst().getType(),
|
||||
faceId);
|
||||
|
||||
for (MemberSourceEntity memberSourceEntity : memberSourceEntityList) {
|
||||
// 设置购买状态
|
||||
if (isBuy.isBuy()) {
|
||||
// 如果用户买过
|
||||
memberSourceEntity.setIsBuy(1);
|
||||
} else if (isBuy.isFree()) {
|
||||
// 全免费逻辑
|
||||
memberSourceEntity.setIsBuy(1);
|
||||
} else {
|
||||
memberSourceEntity.setIsBuy(0);
|
||||
}
|
||||
|
||||
// 设置免费状态
|
||||
if (freeSourceIds.contains(memberSourceEntity.getSourceId())) {
|
||||
memberSourceEntity.setIsFree(1);
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("购买状态处理完成:用户购买状态 isBuy={}, isFree={}, 免费源文件数量={}",
|
||||
isBuy.isBuy(), isBuy.isFree(), freeSourceIds.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理视频重切逻辑
|
||||
* 当非新用户且照片数量大于视频数量时,创建视频重切任务
|
||||
*/
|
||||
private void handleVideoRecreation(ScenicConfigManager scenicConfig,
|
||||
List<MemberSourceEntity> memberSourceEntityList,
|
||||
Long faceId,
|
||||
Long memberId,
|
||||
List<Long> sampleListIds,
|
||||
boolean isNew) {
|
||||
// 新用户不执行视频重切逻辑
|
||||
if (isNew) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查景区是否禁用源视频功能
|
||||
Boolean disableSourceVideo = scenicConfig != null ? scenicConfig.getBoolean("disable_source_video") : null;
|
||||
if (scenicConfig == null || Boolean.TRUE.equals(disableSourceVideo)) {
|
||||
log.debug("视频重切逻辑跳过:景区禁用了源视频功能");
|
||||
return;
|
||||
}
|
||||
|
||||
// 统计视频和照片数量
|
||||
long videoCount = memberSourceEntityList.stream()
|
||||
.filter(item -> Integer.valueOf(1).equals(item.getType()))
|
||||
.count();
|
||||
long photoCount = memberSourceEntityList.stream()
|
||||
.filter(item -> Integer.valueOf(2).equals(item.getType()))
|
||||
.count();
|
||||
|
||||
log.debug("视频重切逻辑:视频数量 {}, 照片数量 {}", videoCount, photoCount);
|
||||
|
||||
// 只有照片数量大于视频数量时才创建重切任务
|
||||
if (photoCount > videoCount) {
|
||||
VideoPieceGetter.Task task = new VideoPieceGetter.Task();
|
||||
task.faceId = faceId;
|
||||
task.faceSampleIds = sampleListIds;
|
||||
task.templateId = null;
|
||||
task.memberId = memberId;
|
||||
task.callback = () -> {
|
||||
log.info("视频重切任务回调: {}", task);
|
||||
};
|
||||
|
||||
VideoPieceGetter.addTask(task);
|
||||
log.debug("视频重切任务已创建:faceId={}, memberId={}, sampleIds={}",
|
||||
faceId, memberId, sampleListIds.size());
|
||||
} else {
|
||||
log.debug("视频重切逻辑跳过:照片数量({})未超过视频数量({})", photoCount, videoCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -443,8 +683,8 @@ public class FaceServiceImpl implements FaceService {
|
||||
sourceImageContent.setContentType(2);
|
||||
sourceVideoContent.setLockType(-1);
|
||||
sourceImageContent.setLockType(-1);
|
||||
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(faceRespVO.getScenicId());
|
||||
if (!Boolean.TRUE.equals(scenicConfig.getDisableSourceImage())) {
|
||||
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(faceRespVO.getScenicId());
|
||||
if (!Boolean.TRUE.equals(scenicConfig.getBoolean("disable_source_image"))) {
|
||||
IsBuyRespVO isBuyRespVO = orderBiz.isBuy(userId, faceRespVO.getScenicId(), 2, faceId);
|
||||
sourceImageContent.setSourceType(isBuyRespVO.getGoodsType());
|
||||
sourceImageContent.setContentId(isBuyRespVO.getGoodsId());
|
||||
@@ -463,7 +703,7 @@ public class FaceServiceImpl implements FaceService {
|
||||
sourceImageContent.setFreeCount((int) freeCount);
|
||||
contentList.add(sourceImageContent);
|
||||
}
|
||||
if (!Boolean.TRUE.equals(scenicConfig.getDisableSourceVideo())) {
|
||||
if (!Boolean.TRUE.equals(scenicConfig.getBoolean("disable_source_video"))) {
|
||||
IsBuyRespVO isBuyRespVO = orderBiz.isBuy(userId, faceRespVO.getScenicId(), 1, faceId);
|
||||
sourceVideoContent.setSourceType(isBuyRespVO.getGoodsType());
|
||||
sourceVideoContent.setContentId(isBuyRespVO.getGoodsId());
|
||||
|
@@ -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,170 @@ 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. 应用设备照片数量限制筛选
|
||||
filteredIds = applyDevicePhotoLimit(filteredIds, allFaceSampleList);
|
||||
log.debug("应用设备照片数量限制筛选完成");
|
||||
|
||||
// 4. 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设备配置的limit_photo值限制每个设备的照片数量
|
||||
*
|
||||
* @param acceptedSampleIds 已接受的样本ID列表
|
||||
* @param allFaceSampleList 所有人脸样本实体列表
|
||||
* @return 应用设备照片数量限制后的样本ID列表
|
||||
*/
|
||||
private List<Long> applyDevicePhotoLimit(List<Long> acceptedSampleIds,
|
||||
List<FaceSampleEntity> allFaceSampleList) {
|
||||
if (acceptedSampleIds == null || acceptedSampleIds.isEmpty()) {
|
||||
return acceptedSampleIds;
|
||||
}
|
||||
|
||||
// 获取过滤后的样本列表
|
||||
List<FaceSampleEntity> filteredSamples = allFaceSampleList.stream()
|
||||
.filter(sample -> acceptedSampleIds.contains(sample.getId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 按设备ID分组
|
||||
Map<Long, List<FaceSampleEntity>> samplesByDevice = filteredSamples.stream()
|
||||
.collect(Collectors.groupingBy(FaceSampleEntity::getDeviceId));
|
||||
|
||||
List<Long> resultIds = new ArrayList<>();
|
||||
|
||||
// 处理每个设备的样本
|
||||
for (Map.Entry<Long, List<FaceSampleEntity>> entry : samplesByDevice.entrySet()) {
|
||||
Long deviceId = entry.getKey();
|
||||
List<FaceSampleEntity> deviceSamples = entry.getValue();
|
||||
|
||||
// 获取设备配置
|
||||
DeviceConfigManager deviceConfig = deviceRepository.getDeviceConfigManager(deviceId);
|
||||
Integer limitPhoto = null;
|
||||
if (deviceConfig != null) {
|
||||
limitPhoto = deviceConfig.getInteger("limit_photo");
|
||||
}
|
||||
|
||||
// 如果没有配置或配置为0,不限制
|
||||
if (limitPhoto == null || limitPhoto <= 0) {
|
||||
List<Long> deviceSampleIds = deviceSamples.stream()
|
||||
.map(FaceSampleEntity::getId)
|
||||
.collect(Collectors.toList());
|
||||
resultIds.addAll(deviceSampleIds);
|
||||
log.debug("设备照片限制:设备ID={}, 无限制,保留{}张照片",
|
||||
deviceId, deviceSampleIds.size());
|
||||
} else {
|
||||
// 按创建时间倒序排序,取前N张
|
||||
List<FaceSampleEntity> limitedSamples = deviceSamples.stream()
|
||||
.sorted(Comparator.comparing(FaceSampleEntity::getCreateAt).reversed())
|
||||
.limit(limitPhoto)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Long> limitedIds = limitedSamples.stream()
|
||||
.map(FaceSampleEntity::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
resultIds.addAll(limitedIds);
|
||||
log.debug("设备照片限制:设备ID={}, 限制={}张, 原始{}张,最终{}张",
|
||||
deviceId, limitPhoto, deviceSamples.size(), limitedIds.size());
|
||||
}
|
||||
}
|
||||
|
||||
log.info("设备照片数量限制筛选:原始样本数量={}, 筛选后数量={}",
|
||||
acceptedSampleIds.size(), resultIds.size());
|
||||
|
||||
return resultIds;
|
||||
}
|
||||
}
|
||||
|
@@ -42,12 +42,17 @@ public class N9eSyncTask {
|
||||
requestBody.put("queries", queries);
|
||||
|
||||
// 发送POST请求
|
||||
HttpResponse response = HttpUtil.createPost("https://n9e.jerryyan.top/v1/n9e/target/list")
|
||||
Map<String, Object> respData;
|
||||
try (HttpResponse response = HttpUtil.createPost("https://n9e.jerryyan.top/v1/n9e/target/list")
|
||||
.header("Authorization", auth)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(JacksonUtil.toJSONString(requestBody))
|
||||
.execute();
|
||||
Map<String, Object> respData = JacksonUtil.parseObject(response.body(), Map.class);
|
||||
.execute()) {
|
||||
respData = JacksonUtil.parseObject(response.body(), Map.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("N9E信息获取失败");
|
||||
return;
|
||||
}
|
||||
String err = (String) respData.get("err");
|
||||
if (StringUtils.isNotBlank(err)) {
|
||||
log.warn("N9E信息获取失败");
|
||||
@@ -65,8 +70,8 @@ public class N9eSyncTask {
|
||||
list.forEach(item -> {
|
||||
String ident = (String) item.get("ident");
|
||||
Number updateAtNum = (Number) item.get("update_at");
|
||||
Long updateAt = updateAtNum != null ? updateAtNum.longValue() : 0L;
|
||||
redisTemplate.opsForValue().set("ext_device:online:" + ident, updateAt.toString(), 1, TimeUnit.DAYS);
|
||||
long updateAt = updateAtNum != null ? updateAtNum.longValue() : 0L;
|
||||
redisTemplate.opsForValue().set("ext_device:online:" + ident, Long.toString(updateAt), 1, TimeUnit.DAYS);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user