package com.ycwl.basic.facebody.adapter; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.facebody.model.v20191230.AddFaceEntityRequest; import com.aliyuncs.facebody.model.v20191230.AddFaceRequest; import com.aliyuncs.facebody.model.v20191230.AddFaceResponse; import com.aliyuncs.facebody.model.v20191230.CreateFaceDbRequest; import com.aliyuncs.facebody.model.v20191230.DeleteFaceDbRequest; import com.aliyuncs.facebody.model.v20191230.DeleteFaceEntityRequest; import com.aliyuncs.facebody.model.v20191230.ListFaceDbsRequest; import com.aliyuncs.facebody.model.v20191230.ListFaceDbsResponse; import com.aliyuncs.facebody.model.v20191230.ListFaceEntitiesRequest; import com.aliyuncs.facebody.model.v20191230.ListFaceEntitiesResponse; import com.aliyuncs.facebody.model.v20191230.SearchFaceRequest; import com.aliyuncs.facebody.model.v20191230.SearchFaceResponse; import com.aliyuncs.profile.DefaultProfile; import com.ycwl.basic.facebody.entity.AddFaceResp; import com.ycwl.basic.facebody.entity.AliFaceBodyConfig; import com.ycwl.basic.facebody.entity.SearchFaceResp; import com.ycwl.basic.facebody.entity.SearchFaceResultItem; import com.ycwl.basic.utils.ratelimiter.FixedRateLimiter; import com.ycwl.basic.utils.ratelimiter.IRateLimiter; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Slf4j public class AliFaceBodyAdapter implements IFaceBodyAdapter { private static final Map addEntityLimiters = new ConcurrentHashMap<>(); private static final Map addFaceLimiters = new ConcurrentHashMap<>(); private static final Map addDbLimiters = new ConcurrentHashMap<>(); private static final Map listDbLimiters = new ConcurrentHashMap<>(); private static final Map listFaceLimiters = new ConcurrentHashMap<>(); private static final Map searchFaceLimiters = new ConcurrentHashMap<>(); private static final Map deleteDbLimiters = new ConcurrentHashMap<>(); private static final Map deleteEntityLimiters = new ConcurrentHashMap<>(); private AliFaceBodyConfig config; public boolean setConfig(AliFaceBodyConfig config) { this.config = config; return true; } @Override public boolean loadConfig(Map _config) { AliFaceBodyConfig config = new AliFaceBodyConfig(); config.setAccessKeyId(_config.get("accessKeyId")); config.setAccessKeySecret(_config.get("accessKeySecret")); config.setRegion(_config.get("region")); this.config = config; return true; } private IRateLimiter getLimiter(LOCK_TYPE type) { switch (type) { case ADD_DB: return addDbLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS)); case ADD_ENTITY: return addEntityLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS)); case ADD_FACE: return addFaceLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS)); case LIST_DB: return listDbLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(500, TimeUnit.MILLISECONDS)); case LIST_FACE: return listFaceLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(2)); case SEARCH_FACE: return searchFaceLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(200, TimeUnit.MILLISECONDS)); case DELETE_DB: return deleteDbLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS)); case DELETE_ENTITY: return deleteEntityLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS)); default: return new FixedRateLimiter(600, TimeUnit.MILLISECONDS); } } @Override public boolean addFaceDb(String dbName) { IRateLimiter addDbLimiter = getLimiter(LOCK_TYPE.ADD_DB); try (ClientWrapper clientWrapper = getClient()) { IAcsClient client = clientWrapper.getClient(); CreateFaceDbRequest request = new CreateFaceDbRequest(); request.setName(dbName); try { addDbLimiter.acquire(); } catch (InterruptedException ignored) { } client.getAcsResponse(request); return true; } catch (ClientException e) { log.error("阿里云添加人脸数据库失败!", e); return false; } } @Override public boolean deleteFaceDb(String dbName) { ListFaceEntitiesRequest request = new ListFaceEntitiesRequest(); IRateLimiter deleteEntityLimiter = getLimiter(LOCK_TYPE.DELETE_ENTITY); IRateLimiter deleteDbLimiter = getLimiter(LOCK_TYPE.DELETE_DB); request.setDbName(dbName); request.setLimit(200); try (ClientWrapper clientWrapper = getClient()) { IAcsClient client = clientWrapper.getClient(); while (true) { ListFaceEntitiesResponse response = client.getAcsResponse(request); if (response.getData().getTotalCount() == 0) { break; } response.getData().getEntities().forEach(entity -> { DeleteFaceEntityRequest deleteFaceEntityRequest = new DeleteFaceEntityRequest(); deleteFaceEntityRequest.setDbName(entity.getDbName()); deleteFaceEntityRequest.setEntityId(entity.getEntityId()); try { deleteEntityLimiter.acquire(); } catch (InterruptedException ignored) { } try { client.getAcsResponse(deleteFaceEntityRequest); } catch (ClientException e) { log.error("删除人脸数据失败!", e); } }); } DeleteFaceDbRequest deleteFaceDbRequest = new DeleteFaceDbRequest(); deleteFaceDbRequest.setName(dbName); try { deleteDbLimiter.acquire(); } catch (InterruptedException ignored) { } client.getAcsResponse(deleteFaceDbRequest); } catch (ClientException e) { log.error("删除人脸数据库失败!", e); return false; } return true; } @Override public List listFaceDb() { ListFaceDbsRequest request = new ListFaceDbsRequest(); try (ClientWrapper clientWrapper = getClient()) { IAcsClient client = clientWrapper.getClient(); ListFaceDbsResponse response = client.getAcsResponse(request); return response.getData().getDbList().stream().map(ListFaceDbsResponse.Data.DbListItem::getName).collect(Collectors.toList()); } catch (ClientException e) { log.error("获取人脸数据库失败!", e); return null; } } @Override public AddFaceResp addFace(String dbName, String entityId, String faceUrl, String extData) { IRateLimiter addEntityLimiter = getLimiter(LOCK_TYPE.ADD_ENTITY); IRateLimiter addFaceLimiter = getLimiter(LOCK_TYPE.ADD_FACE); AddFaceEntityRequest request = new AddFaceEntityRequest(); request.setDbName(dbName); request.setEntityId(entityId); try (ClientWrapper clientWrapper = getClient()) { IAcsClient client = clientWrapper.getClient(); try { addEntityLimiter.acquire(); } catch (InterruptedException ignored) { } try { client.getAcsResponse(request); } catch (ClientException e) { log.error("addFaceEntity, {}/{}", dbName, entityId, e); return null; } AddFaceRequest addFaceRequest = new AddFaceRequest(); addFaceRequest.setDbName(dbName); addFaceRequest.setEntityId(entityId); addFaceRequest.setImageUrl(faceUrl); addFaceRequest.setExtraData(extData); AddFaceResp respVo = new AddFaceResp(); try { addFaceLimiter.acquire(); } catch (InterruptedException ignored) { } try { AddFaceResponse acsResponse = client.getAcsResponse(addFaceRequest); respVo.setScore(acsResponse.getData().getQualitieScore()); return respVo; } catch (ClientException e) { log.error("addFace, {}/{}", dbName, entityId, e); return null; } } } @Override public boolean deleteFace(String dbName, String entityId) { IRateLimiter deleteEntityLimiter = getLimiter(LOCK_TYPE.DELETE_ENTITY); DeleteFaceEntityRequest request = new DeleteFaceEntityRequest(); request.setDbName(dbName); request.setEntityId(entityId); try (ClientWrapper clientWrapper = getClient()) { IAcsClient client = clientWrapper.getClient(); try { deleteEntityLimiter.acquire(); } catch (InterruptedException ignored) { } try { client.getAcsResponse(request); return true; } catch (ClientException e) { log.error("删除人脸数据失败!", e); return false; } } } @Override public List listFace(String dbName, String prefix, Integer offset, Integer size) { IRateLimiter listFaceLimiter = getLimiter(LOCK_TYPE.LIST_FACE); ListFaceEntitiesRequest listFaceEntitiesRequest = new ListFaceEntitiesRequest(); listFaceEntitiesRequest.setDbName(dbName); listFaceEntitiesRequest.setOrder("asc"); if (offset != null) { listFaceEntitiesRequest.setOffset(offset); } if (size != null) { listFaceEntitiesRequest.setLimit(size); } else { listFaceEntitiesRequest.setLimit(200); } if (StringUtils.isNotEmpty(prefix)) { listFaceEntitiesRequest.setEntityIdPrefix(prefix); } try (ClientWrapper clientWrapper = getClient()) { IAcsClient client = clientWrapper.getClient(); try { listFaceLimiter.acquire(); } catch (InterruptedException ignored) { } try { ListFaceEntitiesResponse response = client.getAcsResponse(listFaceEntitiesRequest); return response.getData().getEntities().stream().map(ListFaceEntitiesResponse.Data.Entity::getEntityId).collect(Collectors.toList()); } catch (ClientException e) { log.error("获取人脸数据失败!", e); return null; } } } @Override public SearchFaceResp searchFace(String dbName, String faceUrl) { SearchFaceResp resp = new SearchFaceResp(); IRateLimiter searchFaceLimiter = getLimiter(LOCK_TYPE.SEARCH_FACE); try (ClientWrapper clientWrapper = getClient()) { IAcsClient client = clientWrapper.getClient(); SearchFaceRequest request = new SearchFaceRequest(); request.setDbName(dbName); request.setImageUrl(faceUrl); request.setLimit(100); try { searchFaceLimiter.acquire(); } catch (InterruptedException ignored) { } try { SearchFaceResponse response = client.getAcsResponse(request); List matchList = response.getData().getMatchList(); if (matchList.isEmpty()) { resp.setOriginalFaceScore(0f); return resp; } SearchFaceResponse.Data.MatchListItem matchItem = matchList.get(0); resp.setOriginalFaceScore(matchItem.getQualitieScore()); resp.setResult(matchItem.getFaceItems().stream().map(item -> { SearchFaceResultItem resultItem = new SearchFaceResultItem(); resultItem.setDbName(dbName); resultItem.setFaceId(item.getFaceId()); resultItem.setExtData(item.getExtraData()); resultItem.setScore(item.getScore()); return resultItem; }).collect(Collectors.toList())); if (!resp.getResult().isEmpty()) { resp.setFirstMatchRate(resp.getResult().get(0).getScore()); } return resp; } catch (ClientException e) { log.error("搜索人脸失败!", e); return null; } } } public ClientWrapper getClient() { DefaultProfile profile = DefaultProfile.getProfile( config.getRegion(), config.getAccessKeyId(), config.getAccessKeySecret()); IAcsClient client = new DefaultAcsClient(profile); return new ClientWrapper(client); } @Getter public static class ClientWrapper implements AutoCloseable { private final IAcsClient client; public ClientWrapper(IAcsClient client) { this.client = client; } @Override public void close() { if (client == null) { return; } client.shutdown(); } } protected enum LOCK_TYPE { ADD_DB, ADD_ENTITY, ADD_FACE, LIST_DB, LIST_FACE, SEARCH_FACE, DELETE_DB, DELETE_ENTITY, } }