This commit is contained in:
2025-07-01 17:54:58 +08:00
commit 57dcd609e2
5136 changed files with 346184 additions and 0 deletions

24
zhwl-face/pom.xml Normal file
View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>zhwl</artifactId>
<groupId>com.zhwl</groupId>
<version>3.8.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>zhwl-face</artifactId>
<description>
人脸服务
</description>
<dependencies>
<dependency>
<groupId>com.zhwl</groupId>
<artifactId>zhwl-system</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,163 @@
package com.zhwl.face.config;
import com.zhwl.common.core.text.Convert;
import com.zhwl.common.utils.spring.SpringUtils;
import com.zhwl.system.service.ISysConfigService;
import lombok.extern.slf4j.Slf4j;
/**
* @author duanyashu
* @description 安居乐人脸配置
* @date 2024/11/6 13:36
*/
@Slf4j
public class AjlFaceConfig {
/**
* 系统配置参数token
*/
private final static String CONFIG_KEY_TOKEN = "ajl.face.token";
/**
* 请求服务URL地址
*/
public static final String CONFIG_KEY_SERVICE_BASE_URL = "ajl.face.url";
/**
* 人脸库id
*/
public final static String CONFIG_KEY_GROUP_ID = "ajl.face.groupId";
/**
* 人员库名称
*/
public final static String GROUP_NAME = "安居链游客人脸库";
/**
* 添加公司
*/
public final static String ADD_COMPONY = "/faceAPI/AddCompany";
/**
* 修改公司
*/
public final static String UPD_COMPONY = "/faceAPI/UpdateCompany";
/**
* 删除公司
*/
public final static String DEL_COMPONY = "/faceAPI/DeleteCompany";
/**
* 查询公司
*/
public final static String QUERY_COMPONY = "/faceAPI/GetCompanyDetailed";
/**
* 创建人脸组
*/
public final static String CREATE_GROUP = "/faceAPI/CreateFaceGroup";
/**
* 删除人脸组
*/
public final static String DEL_GROUP = "/faceAPI/DeleteFaceGroup";
/**
* 编辑人脸组
*/
public final static String UPD_GROUP = "/faceAPI/EditFaceGroup";
/**
* 获取人脸组列表
*/
public final static String GET_GROUP_LIST = "/faceAPI/GetAllFaceGroup";
/**
* 获取人脸组详情
*/
public final static String GET_GROUP_INFO = "/faceAPI/GetFaceGroupById";
/**
* 添加人脸
*/
public final static String ADD_FACE = "/faceAPI/AddFaceFeature";
/**
* 删除人脸
*/
public final static String DEL_FACE = "/faceAPI/DeleteFaceFeature";
/**
* 编辑人脸
*/
public final static String UPD_FACE = "/faceAPI/EditFaceFeature";
/**
* 获取指定特征的人脸图片
*/
public final static String GET_FACE_IMAGE = "/faceAPI/GetFaceImage";
/**
* 下载人脸特征列表
*/
public final static String DOWNLOAD_FACE_LIST = "/faceAPI/DownloadFaceFeature";
/**
* 下载 单个人脸数据
*/
public final static String DOWNLOAD_FACE_INFO = "/faceAPI/GetFeatureByID";
/**
* 人脸搜索
*/
public final static String SEARCH_FACE = "/faceAPI/SearchFace";
/**
* 匹配两个数据(图片\特征)
*/
public final static String MATCH_FACE = "/faceAPI/MatchFeatureImage";
/**
* 图片属性检测
*/
public final static String GET_FACE_ATTR = "/faceAPI/GetFaceAttr";
/**
* 小1比N
*/
public final static String COMPARISON_FACE = " /faceAPI/ComparisonFace";
/**
* 特征提取检测
*/
public final static String EXTRACT_FEATURE = "/faceAPI/ExtractFeature";
static ISysConfigService configService;
static {
configService = SpringUtils.getBean(ISysConfigService.class);
}
/**
* 获取安居乐人脸token
* @return
*/
public static String getAjlFaceToken() {
return configService.selectConfigByKey(CONFIG_KEY_TOKEN);
}
public static Long getFaceGroupId() {
return Convert.toLong(configService.selectConfigByKey(CONFIG_KEY_GROUP_ID));
}
public static String getBaseUrl() {
return configService.selectConfigByKey(CONFIG_KEY_SERVICE_BASE_URL);
}
}

View File

@@ -0,0 +1,98 @@
package com.zhwl.face.entity;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* 人脸请求参数
*
* @author duanyashu
* @date 2025/5/28 9:55
*/
@Data
public class FaceAddRequest implements Serializable {
/**
* //特征ID填0即可
*/
@JSONField(name = "ID")
private Long id = 0L;
/**
* 算法等级 1-5 默认等级1可为空
*/
@JSONField(name = "FaceLibLevel")
private String faceLibLevel;
/**
* //特征组ID必填
*/
@JSONField(name = "GroupID")
private Long groupId;
/**
* 特征名称 // 卡号
*/
@JSONField(name = "FeatureName")
private String featureName;
/**
* 特征自编号
*/
@JSONField(name = "FeatureNumber")
private String featureNumber;
/**
* 有效开始时间
*/
@JSONField(name = "ValidStartTime")
private Date validStartTime;
/**
* 有效结束时间
*/
@JSONField(name = "ValidEndTime")
private Date validEndTime;
/**
* 备注
*/
@JSONField(name = "Remarks")
private String remarks;
/**
* // 景区ID: 多个景区ID用英文逗号分开 (注意格式) ,可为空
*/
@JSONField(name = "ParkIds")
private String parkIds;
/**
* 创建时间
*/
@JSONField(name = "CreateDate")
private Date createDate;
@JSONField(name = "Data")
private FaceImage data;
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public static class FaceImage {
/**
* 图像数据
*/
@JSONField(name = "Image")
private String image;
}
}

View File

@@ -0,0 +1,41 @@
package com.zhwl.face.entity;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.io.Serializable;
/**
* 人脸组实体
*
* @author duanyashu
* @date 2025/5/28 8:56
*/
@Data
public class FaceGroup implements Serializable {
/**
* 人脸组ID
*/
@JSONField(name = "ID")
private Long id;
/**
* 人脸组名称
*/
@JSONField(name = "Name")
private String name;
/**
* 人脸组备注
*/
@JSONField(name = "Remarks")
private String remarks;
/**
* 创建时间
*/
@JSONField(name = "CreateDate")
private String createDate;
}

View File

@@ -0,0 +1,83 @@
package com.zhwl.face.entity;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 人脸请求参数
*
* @author duanyashu
* @date 2025/5/28 9:55
*/
@Data
public class FaceResponse implements Serializable {
/**
* //特征ID
*/
@JSONField(name = "ID")
private Long id;
/**
* //人脸组
*/
@JSONField(name = "GroupID")
private Long groupId;
/**
* 特征名称 // 卡号
*/
@JSONField(name = "FeatureName")
private String featureName;
/**
* 特征自编号
*/
@JSONField(name = "FeatureNumber")
private String featureNumber;
/**
* 备注
*/
@JSONField(name = "Remarks")
private String remarks;
/**
* 特征信息
*/
@JSONField(name = "FeatureInfo")
private FeatureInfo featureInfo;
/**
* 创建时间
*/
@JSONField(name = "CreateDate")
private Date createDate;
@JSONField(name = "Data")
private FaceImage data;
@Data
public static class FeatureInfo{
/**
* 特征信息
*/
@JSONField(name = "Info")
private String info;
}
@Data
public static class FaceImage {
/**
* 图像数据
*/
@JSONField(name = "Image")
private String image;
}
}

View File

@@ -0,0 +1,83 @@
package com.zhwl.face.entity;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 搜索人脸请求参数
*
* @author duanyashu
* @date 2025/5/28 10:39
*/
@Data
public class FaceSearchRequest implements Serializable {
/**
* 人脸组id 决定从哪个人脸组中搜索如果填0则从该开发者帐号下所有人脸组搜索
*/
@JSONField(name = "GroupID")
private Long groupId;
/**
* 算法等级 1-5默认等级1可为空
*/
@JSONField(name = "FaceLibLevel")
private String faceLibLevel;
/**
* 匹配度最低阈值 建议值80
*/
@JSONField(name = "Value")
private Integer value;
/**
* 要求返回多个特征,满足匹配度的数量
*/
@JSONField(name = "NeedCount")
private Integer needCount;
/**
* 是否返回匹配的原始图片
*/
@JSONField(name = "NeedImage")
private Boolean needImage;
/**
* 景区ID,可为空
*/
@JSONField(name = "ParkId")
private String parkId;
/**
* 搜索模式 0=只要超过阀值的结果最大值,1=只要超过阀值的第一个结果,2=要求超过阀值后的最高值N个结果。
*/
@JSONField(name = "Mode")
private Integer mode;
@JSONField(name = "Data")
private DataInfo data;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Data
public static class DataInfo {
/**
* 数据类型DataType:0特征1图片
*/
@JSONField(name = "DataType")
private Integer dataType;
/**
* Base64编码的特征或图片数据
*/
@JSONField(name = "Data")
private String data;
}
}

View File

@@ -0,0 +1,91 @@
package com.zhwl.face.entity;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.io.Serializable;
/**
* 搜索人脸结果实体
*
* @author duanyashu
* @date 2025/5/28 10:46
*/
@Data
public class FaceSearchResponse implements Serializable {
/**
* 目标与本条特征的相似度(0-100)
*/
@JSONField(name = "Value")
private Long value;
/**
* 特征ID
*/
@JSONField(name = "ID")
private Long id;
/**
* 特征所属组ID
*/
@JSONField(name = "GroupID")
private Long groupId;
/**
* 特征名称
*/
@JSONField(name = "FeatureName")
private String featureName;
/**
* 特征自编码(编号)
*/
@JSONField(name = "FeatureNumber")
private String featureNumber;
/**
* 特征备注
*/
@JSONField(name = "Remarks")
private String remarks;
/**
* 特征信息包含Base64数据
*/
@JSONField(name = "FeatureInfo")
private FeatureInfo featureInfo;
/**
* 创建时间
*/
@JSONField(name = "CreateDate",format = "yyyy-MM-dd HH:mm:ss")
private String createDate;
/**
* 图像数据对象
*/
@JSONField(name = "Data")
private ImageData data;
/**
* 图片URL名称
*/
@JSONField(name = "ImageName")
private String imageName;
// 内部类FeatureInfo
@Data
public static class FeatureInfo {
@JSONField(name = "Info")
private String info; // Base64数据
}
// 内部类ImageData
@Data
public static class ImageData {
@JSONField(name = "Image")
private String image; // Base64编码图片
}
}

View File

@@ -0,0 +1,24 @@
package com.zhwl.face.entity;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
/**
* 人脸请求参数
*
* @author duanyashu
* @date 2025/5/28 9:55
*/
@Data
public class FaceUpdRequest extends FaceAddRequest {
/**
* //需要修改的字段列表 空为全部修改
*/
@JSONField(name = "UpdateField")
private List<String> updateField;
}

View File

@@ -0,0 +1,26 @@
package com.zhwl.face.entity;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.io.Serializable;
/**
* 响应基类
*
* @author duanyashu
* @date 2025/5/28 9:03
*/
@Data
public class ResponseBase<T> implements Serializable {
@JSONField(name = "State")
private Boolean state;
@JSONField(name = "Message")
private String message;
@JSONField(name = "Data")
private T data;
}

View File

@@ -0,0 +1,125 @@
package com.zhwl.face.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.http.ContentType;
import com.alibaba.fastjson2.JSON;
import com.zhwl.common.exception.GlobalException;
import com.zhwl.common.utils.ListUtils;
import com.zhwl.common.utils.spring.SpringUtils;
import com.zhwl.face.config.AjlFaceConfig;
import com.zhwl.face.entity.FaceGroup;
import com.zhwl.face.entity.ResponseBase;
import com.zhwl.face.util.FaceHttpUtil;
import com.zhwl.system.domain.SysConfig;
import com.zhwl.system.service.ISysConfigService;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
/**
* 安居链人脸组服务类
*
* @author duanyashu
* @date 2025/5/28 8:50
*/
@Slf4j
public class FaceGroupService {
/**
* 创建人脸组
* @return
*/
public static FaceGroup createFaceGroup(){
FaceGroup faceGroupInfo = getFaceGroupInfo();
if (faceGroupInfo != null){
return faceGroupInfo;
}
FaceGroup faceGroup = new FaceGroup();
faceGroup.setName(AjlFaceConfig.GROUP_NAME);
String body = FaceHttpUtil.runRequest(AjlFaceConfig.CREATE_GROUP, faceGroup);
log.info("创建人脸库响应数据:{}",body);
ResponseBase<FaceGroup> responseBase = JSON.parseObject(body, ResponseBase.class);
if (!responseBase.getState()){
throw new GlobalException(responseBase.getMessage());
}
//存入数据库
SysConfig sysConfig = new SysConfig();
sysConfig.setConfigKey(AjlFaceConfig.CONFIG_KEY_GROUP_ID);
ISysConfigService configService = SpringUtils.getBean(ISysConfigService.class);
List<SysConfig> sysConfigs = configService.selectConfigList(sysConfig);
if (CollUtil.isEmpty(sysConfigs)){
sysConfig.setConfigValue(responseBase.getData().getId()+"");
configService.insertConfig(sysConfig);
}else {
SysConfig sysConfig1 = sysConfigs.get(0);
sysConfig1.setConfigValue(responseBase.getData().getId()+"");
configService.updateConfig(sysConfig1);
}
return responseBase.getData();
}
/**
* 修改人脸库
*/
public static boolean updateFaceGroup(String name,String remarks){
FaceGroup faceGroup = new FaceGroup();
faceGroup.setId(AjlFaceConfig.getFaceGroupId());
faceGroup.setName(name);
faceGroup.setRemarks(remarks);
String body = FaceHttpUtil.runRequest(AjlFaceConfig.UPD_GROUP, faceGroup);
log.info("修改人脸库响应数据:{}",body);
ResponseBase<FaceGroup> responseBase = JSON.parseObject(body, ResponseBase.class);
return responseBase.getState();
}
/**
* 删除人脸库
*/
public static boolean delFaceGroup(){
Long faceGroupId = AjlFaceConfig.getFaceGroupId();
if (faceGroupId == null){
return true;
}
FaceGroup faceGroup = new FaceGroup();
faceGroup.setId(faceGroupId);
String body = FaceHttpUtil.runRequest(AjlFaceConfig.DEL_GROUP, faceGroup);
ResponseBase<FaceGroup> responseBase = JSON.parseObject(body, ResponseBase.class);
return responseBase.getState();
}
/**
* 获取人脸库列表
* @return
*/
public static List<FaceGroup> getFaceGroupList(){
String body = FaceHttpUtil.runRequest(AjlFaceConfig.GET_GROUP_LIST, null, ContentType.FORM_URLENCODED);
log.info("获取人脸库列表响应数据:{}",body);
ResponseBase<List<FaceGroup>> responseBase = JSON.parseObject(body, ResponseBase.class);
if (responseBase.getState()){
return ListUtils.empty();
}
return responseBase.getData();
}
/**
* 获取人脸库详情
* @return
*/
public static FaceGroup getFaceGroupInfo(){
Long faceGroupId = AjlFaceConfig.getFaceGroupId();
if (faceGroupId == null){
return null;
}
FaceGroup faceGroup = new FaceGroup();
faceGroup.setId(faceGroupId);
String body = FaceHttpUtil.runRequest(AjlFaceConfig.GET_GROUP_INFO, faceGroup);
log.info("获取人脸库响应数据:{}",body);
ResponseBase<FaceGroup> responseBase = JSON.parseObject(body, ResponseBase.class);
if (!responseBase.getState()){
throw new GlobalException(responseBase.getMessage());
}
return responseBase.getData();
}
}

View File

@@ -0,0 +1,140 @@
package com.zhwl.face.service;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.zhwl.common.exception.GlobalException;
import com.zhwl.common.utils.DateUtils;
import com.zhwl.common.utils.MapUtils;
import com.zhwl.face.config.AjlFaceConfig;
import com.zhwl.face.entity.*;
import com.zhwl.face.util.FaceHttpUtil;
import lombok.extern.slf4j.Slf4j;
/**
* 安居链人脸服务类
*
* @author duanyashu
* @date 2025/5/28 8:50
*/
@Slf4j
public class FaceService {
/**
* 添加人脸特征
* @param featureNumber 特征自编号
* @param featureName 本条特征名称
* @param image 图片
* @return
*/
public static Long addFaceFeature(String featureNumber,String featureName,String image) {
if (StrUtil.isBlank(image)){
throw new GlobalException("图片不能为空");
}
FaceAddRequest faceAddRequest = new FaceAddRequest();
faceAddRequest.setGroupId(AjlFaceConfig.getFaceGroupId());
faceAddRequest.setFeatureName(featureName);
faceAddRequest.setFeatureNumber(featureNumber);
faceAddRequest.setValidStartTime(DateUtils.getNowDate());
//设置有效期 不知道是否必填及不过去设置 暂设置2050年
faceAddRequest.setValidEndTime(DateUtils.parseDate("2050-01-01"));
faceAddRequest.setData(FaceAddRequest.FaceImage.builder().image(image).build());
String body = FaceHttpUtil.runRequest(AjlFaceConfig.ADD_FACE, faceAddRequest);
log.info("添加人脸特征返回结果:{}",body);
ResponseBase<FaceResponse> responseBase = JSON.parseObject(body, ResponseBase.class);
if (!responseBase.getState()){
throw new GlobalException(responseBase.getMessage());
}
return responseBase.getData().getId();
}
/**
* 删除人脸特征
* @param faceId 人脸特征id
* @return
*/
public static ResponseBase delFaceFeature(Long faceId) {
if (faceId == null){
throw new GlobalException("请选择要删除的人脸特征");
}
FaceAddRequest faceAddRequest = new FaceAddRequest();
faceAddRequest.setId(faceId);
String body = FaceHttpUtil.runRequest(AjlFaceConfig.DEL_FACE, faceAddRequest);
log.info("删除人脸特征返回结果:{}",body);
ResponseBase<FaceResponse> responseBase = JSON.parseObject(body, ResponseBase.class);
return responseBase;
}
/**
* 修改人脸
* @param faceId
* @param featureNumber
* @param featureName
* @param image 图片
* @param groupId 人脸库id
* @return
*/
public static ResponseBase updateFaceFeature(Long faceId,String featureNumber,String featureName,String image,Long groupId) {
if (!ObjUtil.isAllNotEmpty(faceId,image)){
throw new GlobalException("id或者图片为空");
}
FaceUpdRequest faceUpdRequest = new FaceUpdRequest();
faceUpdRequest.setId(faceId);
faceUpdRequest.setGroupId(groupId);
faceUpdRequest.setFeatureName(featureName);
faceUpdRequest.setFeatureNumber(featureNumber);
faceUpdRequest.setValidStartTime(DateUtils.getNowDate());
//设置有效期 不知道是否必填及不过去设置 暂设置2050年
faceUpdRequest.setValidEndTime(DateUtils.parseDate("2050-01-01"));
faceUpdRequest.setData(FaceAddRequest.FaceImage.builder().image(image).build());
String body = FaceHttpUtil.runRequest(AjlFaceConfig.UPD_FACE, faceUpdRequest);
log.info("添加人脸特征返回结果:{}",body);
ResponseBase<FaceResponse> responseBase = JSON.parseObject(body, ResponseBase.class);
return responseBase;
}
/**
* 获取人脸图片
* @param faceId
* @return
*/
public static String getFaceImage(Long faceId) {
if (faceId == null){
throw new GlobalException("人脸id不能为空");
}
String body = FaceHttpUtil.runRequest(AjlFaceConfig.GET_FACE_IMAGE, MapUtils.of("GroupID", AjlFaceConfig.getFaceGroupId(),"FeatureID",faceId));
log.info("获取指定特征的人脸图片结果:{}",body);
ResponseBase<JSONObject> responseBase = JSON.parseObject(body,ResponseBase.class);
if (!responseBase.getState()){
throw new GlobalException(responseBase.getMessage());
}
return responseBase.getData().getString("Image");
}
/**
* 搜索人脸
* @param image
* @return
*/
public static FaceSearchResponse searchFace(String image) {
if (StrUtil.isBlank(image)){
throw new GlobalException("图片不能为空");
}
FaceSearchRequest faceSearchRequest = new FaceSearchRequest();
faceSearchRequest.setGroupId(AjlFaceConfig.getFaceGroupId());
faceSearchRequest.setValue(80);
faceSearchRequest.setNeedCount(1);
faceSearchRequest.setMode(1);
faceSearchRequest.setNeedImage(true);
faceSearchRequest.setData(FaceSearchRequest.DataInfo.builder().dataType(0).data(image).build());
String body = FaceHttpUtil.runRequest(AjlFaceConfig.SEARCH_FACE, faceSearchRequest);
log.info("搜索人脸结果:{}",body);
ResponseBase<FaceSearchResponse> responseBase = JSON.parseObject(body, ResponseBase.class);
if (!responseBase.getState()){
throw new GlobalException(responseBase.getMessage());
}
return responseBase.getData();
}
}

View File

@@ -0,0 +1,39 @@
package com.zhwl.face.util;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.zhwl.face.config.AjlFaceConfig;
/**
* 人脸库请求工具类
*
* @author duanyashu
* @date 2025/5/28 8:54
*/
public class FaceHttpUtil {
/**
* 运行请求
* @param url
* @param body
* @return
*/
public static String runRequest(String url, Object body) {
return runRequest(url, body, ContentType.JSON);
}
public static String runRequest(String url, Object body, ContentType contentType) {
HttpRequest httprequest = HttpUtil.createPost(AjlFaceConfig.getBaseUrl() + url);
httprequest.header("Content-Type", contentType.getValue());
httprequest.header("TOKEN", AjlFaceConfig.getAjlFaceToken());
if (ObjUtil.isNotEmpty(body)){
httprequest.body(JSON.toJSONString(body));
}
HttpResponse response = httprequest.execute();
return response.body();
}
}