FrameTour-BE/src/main/java/com/ycwl/basic/facebody/adapter/AliFaceBodyAdapter.java

334 lines
14 KiB
Java

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.Collections;
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<String, IRateLimiter> addEntityLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> addFaceLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> addDbLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> listDbLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> listFaceLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> searchFaceLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> deleteDbLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> deleteEntityLimiters = new ConcurrentHashMap<>();
private AliFaceBodyConfig config;
public boolean setConfig(AliFaceBodyConfig config) {
this.config = config;
return true;
}
@Override
public boolean loadConfig(Map<String, String> _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<String> 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 Collections.emptyList();
}
}
@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<String> 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<SearchFaceResponse.Data.MatchListItem> 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,
}
}